Merge tag 'pm+acpi-4.6-rc1-2' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael...
[deliverable/linux.git] / drivers / misc / eeprom / eeprom_93xx46.c
index ff63f05edc763cac7bb8aefef565e20e1463a82b..426fe2fd5238c8f529c94f3c7ce0be8289e55def 100644 (file)
 
 #include <linux/delay.h>
 #include <linux/device.h>
+#include <linux/gpio/consumer.h>
 #include <linux/kernel.h>
 #include <linux/module.h>
 #include <linux/mutex.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/of_gpio.h>
 #include <linux/slab.h>
 #include <linux/spi/spi.h>
-#include <linux/sysfs.h>
+#include <linux/nvmem-provider.h>
+#include <linux/regmap.h>
 #include <linux/eeprom_93xx46.h>
 
 #define OP_START       0x4
 #define ADDR_ERAL      0x20
 #define ADDR_EWEN      0x30
 
+struct eeprom_93xx46_devtype_data {
+       unsigned int quirks;
+};
+
+static const struct eeprom_93xx46_devtype_data atmel_at93c46d_data = {
+       .quirks = EEPROM_93XX46_QUIRK_SINGLE_WORD_READ |
+                 EEPROM_93XX46_QUIRK_INSTRUCTION_LENGTH,
+};
+
 struct eeprom_93xx46_dev {
        struct spi_device *spi;
        struct eeprom_93xx46_platform_data *pdata;
-       struct bin_attribute bin;
        struct mutex lock;
+       struct regmap_config regmap_config;
+       struct nvmem_config nvmem_config;
+       struct nvmem_device *nvmem;
        int addrlen;
+       int size;
 };
 
+static inline bool has_quirk_single_word_read(struct eeprom_93xx46_dev *edev)
+{
+       return edev->pdata->quirks & EEPROM_93XX46_QUIRK_SINGLE_WORD_READ;
+}
+
+static inline bool has_quirk_instruction_length(struct eeprom_93xx46_dev *edev)
+{
+       return edev->pdata->quirks & EEPROM_93XX46_QUIRK_INSTRUCTION_LENGTH;
+}
+
 static ssize_t
-eeprom_93xx46_bin_read(struct file *filp, struct kobject *kobj,
-                      struct bin_attribute *bin_attr,
-                      char *buf, loff_t off, size_t count)
+eeprom_93xx46_read(struct eeprom_93xx46_dev *edev, char *buf,
+                  unsigned off, size_t count)
 {
-       struct eeprom_93xx46_dev *edev;
-       struct device *dev;
-       struct spi_message m;
-       struct spi_transfer t[2];
-       int bits, ret;
-       u16 cmd_addr;
+       ssize_t ret = 0;
 
-       dev = container_of(kobj, struct device, kobj);
-       edev = dev_get_drvdata(dev);
+       if (unlikely(off >= edev->size))
+               return 0;
+       if ((off + count) > edev->size)
+               count = edev->size - off;
+       if (unlikely(!count))
+               return count;
 
-       cmd_addr = OP_READ << edev->addrlen;
+       mutex_lock(&edev->lock);
 
-       if (edev->addrlen == 7) {
-               cmd_addr |= off & 0x7f;
-               bits = 10;
-       } else {
-               cmd_addr |= off & 0x3f;
-               bits = 9;
-       }
+       if (edev->pdata->prepare)
+               edev->pdata->prepare(edev);
 
-       dev_dbg(&edev->spi->dev, "read cmd 0x%x, %d Hz\n",
-               cmd_addr, edev->spi->max_speed_hz);
+       while (count) {
+               struct spi_message m;
+               struct spi_transfer t[2] = { { 0 } };
+               u16 cmd_addr = OP_READ << edev->addrlen;
+               size_t nbytes = count;
+               int bits;
+               int err;
+
+               if (edev->addrlen == 7) {
+                       cmd_addr |= off & 0x7f;
+                       bits = 10;
+                       if (has_quirk_single_word_read(edev))
+                               nbytes = 1;
+               } else {
+                       cmd_addr |= (off >> 1) & 0x3f;
+                       bits = 9;
+                       if (has_quirk_single_word_read(edev))
+                               nbytes = 2;
+               }
 
-       spi_message_init(&m);
-       memset(t, 0, sizeof(t));
+               dev_dbg(&edev->spi->dev, "read cmd 0x%x, %d Hz\n",
+                       cmd_addr, edev->spi->max_speed_hz);
 
-       t[0].tx_buf = (char *)&cmd_addr;
-       t[0].len = 2;
-       t[0].bits_per_word = bits;
-       spi_message_add_tail(&t[0], &m);
+               spi_message_init(&m);
 
-       t[1].rx_buf = buf;
-       t[1].len = count;
-       t[1].bits_per_word = 8;
-       spi_message_add_tail(&t[1], &m);
+               t[0].tx_buf = (char *)&cmd_addr;
+               t[0].len = 2;
+               t[0].bits_per_word = bits;
+               spi_message_add_tail(&t[0], &m);
 
-       mutex_lock(&edev->lock);
+               t[1].rx_buf = buf;
+               t[1].len = count;
+               t[1].bits_per_word = 8;
+               spi_message_add_tail(&t[1], &m);
 
-       if (edev->pdata->prepare)
-               edev->pdata->prepare(edev);
+               err = spi_sync(edev->spi, &m);
+               /* have to wait at least Tcsl ns */
+               ndelay(250);
 
-       ret = spi_sync(edev->spi, &m);
-       /* have to wait at least Tcsl ns */
-       ndelay(250);
-       if (ret) {
-               dev_err(&edev->spi->dev, "read %zu bytes at %d: err. %d\n",
-                       count, (int)off, ret);
+               if (err) {
+                       dev_err(&edev->spi->dev, "read %zu bytes at %d: err. %d\n",
+                               nbytes, (int)off, err);
+                       ret = err;
+                       break;
+               }
+
+               buf += nbytes;
+               off += nbytes;
+               count -= nbytes;
+               ret += nbytes;
        }
 
        if (edev->pdata->finish)
                edev->pdata->finish(edev);
 
        mutex_unlock(&edev->lock);
-       return ret ? : count;
+       return ret;
 }
 
 static int eeprom_93xx46_ew(struct eeprom_93xx46_dev *edev, int is_on)
@@ -110,7 +153,13 @@ static int eeprom_93xx46_ew(struct eeprom_93xx46_dev *edev, int is_on)
                bits = 9;
        }
 
-       dev_dbg(&edev->spi->dev, "ew cmd 0x%04x\n", cmd_addr);
+       if (has_quirk_instruction_length(edev)) {
+               cmd_addr <<= 2;
+               bits += 2;
+       }
+
+       dev_dbg(&edev->spi->dev, "ew%s cmd 0x%04x, %d bits\n",
+                       is_on ? "en" : "ds", cmd_addr, bits);
 
        spi_message_init(&m);
        memset(&t, 0, sizeof(t));
@@ -155,7 +204,7 @@ eeprom_93xx46_write_word(struct eeprom_93xx46_dev *edev,
                bits = 10;
                data_len = 1;
        } else {
-               cmd_addr |= off & 0x3f;
+               cmd_addr |= (off >> 1) & 0x3f;
                bits = 9;
                data_len = 2;
        }
@@ -182,16 +231,17 @@ eeprom_93xx46_write_word(struct eeprom_93xx46_dev *edev,
 }
 
 static ssize_t
-eeprom_93xx46_bin_write(struct file *filp, struct kobject *kobj,
-                       struct bin_attribute *bin_attr,
-                       char *buf, loff_t off, size_t count)
+eeprom_93xx46_write(struct eeprom_93xx46_dev *edev, const char *buf,
+                   loff_t off, size_t count)
 {
-       struct eeprom_93xx46_dev *edev;
-       struct device *dev;
        int i, ret, step = 1;
 
-       dev = container_of(kobj, struct device, kobj);
-       edev = dev_get_drvdata(dev);
+       if (unlikely(off >= edev->size))
+               return -EFBIG;
+       if ((off + count) > edev->size)
+               count = edev->size - off;
+       if (unlikely(!count))
+               return count;
 
        /* only write even number of bytes on 16-bit devices */
        if (edev->addrlen == 6) {
@@ -228,6 +278,49 @@ eeprom_93xx46_bin_write(struct file *filp, struct kobject *kobj,
        return ret ? : count;
 }
 
+/*
+ * Provide a regmap interface, which is registered with the NVMEM
+ * framework
+*/
+static int eeprom_93xx46_regmap_read(void *context, const void *reg,
+                                    size_t reg_size, void *val,
+                                    size_t val_size)
+{
+       struct eeprom_93xx46_dev *eeprom_93xx46 = context;
+       off_t offset = *(u32 *)reg;
+       int err;
+
+       err = eeprom_93xx46_read(eeprom_93xx46, val, offset, val_size);
+       if (err)
+               return err;
+       return 0;
+}
+
+static int eeprom_93xx46_regmap_write(void *context, const void *data,
+                                     size_t count)
+{
+       struct eeprom_93xx46_dev *eeprom_93xx46 = context;
+       const char *buf;
+       u32 offset;
+       size_t len;
+       int err;
+
+       memcpy(&offset, data, sizeof(offset));
+       buf = (const char *)data + sizeof(offset);
+       len = count - sizeof(offset);
+
+       err = eeprom_93xx46_write(eeprom_93xx46, buf, offset, len);
+       if (err)
+               return err;
+       return 0;
+}
+
+static const struct regmap_bus eeprom_93xx46_regmap_bus = {
+       .read = eeprom_93xx46_regmap_read,
+       .write = eeprom_93xx46_regmap_write,
+       .reg_format_endian_default = REGMAP_ENDIAN_NATIVE,
+};
+
 static int eeprom_93xx46_eral(struct eeprom_93xx46_dev *edev)
 {
        struct eeprom_93xx46_platform_data *pd = edev->pdata;
@@ -245,6 +338,13 @@ static int eeprom_93xx46_eral(struct eeprom_93xx46_dev *edev)
                bits = 9;
        }
 
+       if (has_quirk_instruction_length(edev)) {
+               cmd_addr <<= 2;
+               bits += 2;
+       }
+
+       dev_dbg(&edev->spi->dev, "eral cmd 0x%04x, %d bits\n", cmd_addr, bits);
+
        spi_message_init(&m);
        memset(&t, 0, sizeof(t));
 
@@ -294,12 +394,101 @@ static ssize_t eeprom_93xx46_store_erase(struct device *dev,
 }
 static DEVICE_ATTR(erase, S_IWUSR, NULL, eeprom_93xx46_store_erase);
 
+static void select_assert(void *context)
+{
+       struct eeprom_93xx46_dev *edev = context;
+
+       gpiod_set_value_cansleep(edev->pdata->select, 1);
+}
+
+static void select_deassert(void *context)
+{
+       struct eeprom_93xx46_dev *edev = context;
+
+       gpiod_set_value_cansleep(edev->pdata->select, 0);
+}
+
+static const struct of_device_id eeprom_93xx46_of_table[] = {
+       { .compatible = "eeprom-93xx46", },
+       { .compatible = "atmel,at93c46d", .data = &atmel_at93c46d_data, },
+       {}
+};
+MODULE_DEVICE_TABLE(of, eeprom_93xx46_of_table);
+
+static int eeprom_93xx46_probe_dt(struct spi_device *spi)
+{
+       const struct of_device_id *of_id =
+               of_match_device(eeprom_93xx46_of_table, &spi->dev);
+       struct device_node *np = spi->dev.of_node;
+       struct eeprom_93xx46_platform_data *pd;
+       u32 tmp;
+       int gpio;
+       enum of_gpio_flags of_flags;
+       int ret;
+
+       pd = devm_kzalloc(&spi->dev, sizeof(*pd), GFP_KERNEL);
+       if (!pd)
+               return -ENOMEM;
+
+       ret = of_property_read_u32(np, "data-size", &tmp);
+       if (ret < 0) {
+               dev_err(&spi->dev, "data-size property not found\n");
+               return ret;
+       }
+
+       if (tmp == 8) {
+               pd->flags |= EE_ADDR8;
+       } else if (tmp == 16) {
+               pd->flags |= EE_ADDR16;
+       } else {
+               dev_err(&spi->dev, "invalid data-size (%d)\n", tmp);
+               return -EINVAL;
+       }
+
+       if (of_property_read_bool(np, "read-only"))
+               pd->flags |= EE_READONLY;
+
+       gpio = of_get_named_gpio_flags(np, "select-gpios", 0, &of_flags);
+       if (gpio_is_valid(gpio)) {
+               unsigned long flags =
+                       of_flags == OF_GPIO_ACTIVE_LOW ? GPIOF_ACTIVE_LOW : 0;
+
+               ret = devm_gpio_request_one(&spi->dev, gpio, flags,
+                                           "eeprom_93xx46_select");
+               if (ret)
+                       return ret;
+
+               pd->select = gpio_to_desc(gpio);
+               pd->prepare = select_assert;
+               pd->finish = select_deassert;
+
+               gpiod_direction_output(pd->select, 0);
+       }
+
+       if (of_id->data) {
+               const struct eeprom_93xx46_devtype_data *data = of_id->data;
+
+               pd->quirks = data->quirks;
+       }
+
+       spi->dev.platform_data = pd;
+
+       return 0;
+}
+
 static int eeprom_93xx46_probe(struct spi_device *spi)
 {
        struct eeprom_93xx46_platform_data *pd;
        struct eeprom_93xx46_dev *edev;
+       struct regmap *regmap;
        int err;
 
+       if (spi->dev.of_node) {
+               err = eeprom_93xx46_probe_dt(spi);
+               if (err < 0)
+                       return err;
+       }
+
        pd = spi->dev.platform_data;
        if (!pd) {
                dev_err(&spi->dev, "missing platform data\n");
@@ -325,19 +514,34 @@ static int eeprom_93xx46_probe(struct spi_device *spi)
        edev->spi = spi_dev_get(spi);
        edev->pdata = pd;
 
-       sysfs_bin_attr_init(&edev->bin);
-       edev->bin.attr.name = "eeprom";
-       edev->bin.attr.mode = S_IRUSR;
-       edev->bin.read = eeprom_93xx46_bin_read;
-       edev->bin.size = 128;
-       if (!(pd->flags & EE_READONLY)) {
-               edev->bin.write = eeprom_93xx46_bin_write;
-               edev->bin.attr.mode |= S_IWUSR;
+       edev->size = 128;
+
+       edev->regmap_config.reg_bits = 32;
+       edev->regmap_config.val_bits = 8;
+       edev->regmap_config.reg_stride = 1;
+       edev->regmap_config.max_register = edev->size - 1;
+
+       regmap = devm_regmap_init(&spi->dev, &eeprom_93xx46_regmap_bus, edev,
+                                 &edev->regmap_config);
+       if (IS_ERR(regmap)) {
+               dev_err(&spi->dev, "regmap init failed\n");
+               err = PTR_ERR(regmap);
+               goto fail;
        }
 
-       err = sysfs_create_bin_file(&spi->dev.kobj, &edev->bin);
-       if (err)
+       edev->nvmem_config.name = dev_name(&spi->dev);
+       edev->nvmem_config.dev = &spi->dev;
+       edev->nvmem_config.read_only = pd->flags & EE_READONLY;
+       edev->nvmem_config.root_only = true;
+       edev->nvmem_config.owner = THIS_MODULE;
+       edev->nvmem_config.compat = true;
+       edev->nvmem_config.base_dev = &spi->dev;
+
+       edev->nvmem = nvmem_register(&edev->nvmem_config);
+       if (IS_ERR(edev->nvmem)) {
+               err = PTR_ERR(edev->nvmem);
                goto fail;
+       }
 
        dev_info(&spi->dev, "%d-bit eeprom %s\n",
                (pd->flags & EE_ADDR8) ? 8 : 16,
@@ -359,10 +563,11 @@ static int eeprom_93xx46_remove(struct spi_device *spi)
 {
        struct eeprom_93xx46_dev *edev = spi_get_drvdata(spi);
 
+       nvmem_unregister(edev->nvmem);
+
        if (!(edev->pdata->flags & EE_READONLY))
                device_remove_file(&spi->dev, &dev_attr_erase);
 
-       sysfs_remove_bin_file(&spi->dev.kobj, &edev->bin);
        kfree(edev);
        return 0;
 }
@@ -370,6 +575,7 @@ static int eeprom_93xx46_remove(struct spi_device *spi)
 static struct spi_driver eeprom_93xx46_driver = {
        .driver = {
                .name   = "93xx46",
+               .of_match_table = of_match_ptr(eeprom_93xx46_of_table),
        },
        .probe          = eeprom_93xx46_probe,
        .remove         = eeprom_93xx46_remove,
This page took 0.029276 seconds and 5 git commands to generate.