eeprom: at25: extend driver to plug into the NVMEM framework
authorAndrew Lunn <andrew@lunn.ch>
Fri, 26 Feb 2016 19:59:22 +0000 (20:59 +0100)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Wed, 2 Mar 2016 00:55:48 +0000 (16:55 -0800)
Add a regmap for accessing the EEPROM, and then use that with the
NVMEM framework. Enable backwards compatibility in the NVMEM config,
so that the 'eeprom' file in sys is provided by the framework.

Signed-off-by: Andrew Lunn <andrew@lunn.ch>
Acked-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/misc/eeprom/Kconfig
drivers/misc/eeprom/at25.c

index 2493547..8c43a22 100644 (file)
@@ -32,6 +32,8 @@ config EEPROM_AT24
 config EEPROM_AT25
        tristate "SPI EEPROMs from most vendors"
        depends on SPI && SYSFS
+       select REGMAP
+       select NVMEM
        help
          Enable this driver to get read/write support to most SPI EEPROMs,
          after you configure the board init code to know about each eeprom
index 00f859c..fa36a6e 100644 (file)
@@ -16,6 +16,8 @@
 #include <linux/device.h>
 #include <linux/sched.h>
 
+#include <linux/nvmem-provider.h>
+#include <linux/regmap.h>
 #include <linux/spi/spi.h>
 #include <linux/spi/eeprom.h>
 #include <linux/property.h>
@@ -31,8 +33,10 @@ struct at25_data {
        struct spi_device       *spi;
        struct mutex            lock;
        struct spi_eeprom       chip;
-       struct bin_attribute    bin;
        unsigned                addrlen;
+       struct regmap_config    regmap_config;
+       struct nvmem_config     nvmem_config;
+       struct nvmem_device     *nvmem;
 };
 
 #define        AT25_WREN       0x06            /* latch the write enable */
@@ -76,10 +80,10 @@ at25_ee_read(
        struct spi_message      m;
        u8                      instr;
 
-       if (unlikely(offset >= at25->bin.size))
+       if (unlikely(offset >= at25->chip.byte_len))
                return 0;
-       if ((offset + count) > at25->bin.size)
-               count = at25->bin.size - offset;
+       if ((offset + count) > at25->chip.byte_len)
+               count = at25->chip.byte_len - offset;
        if (unlikely(!count))
                return count;
 
@@ -130,21 +134,19 @@ at25_ee_read(
        return status ? status : count;
 }
 
-static ssize_t
-at25_bin_read(struct file *filp, struct kobject *kobj,
-             struct bin_attribute *bin_attr,
-             char *buf, loff_t off, size_t count)
+static int at25_regmap_read(void *context, const void *reg, size_t reg_size,
+                           void *val, size_t val_size)
 {
-       struct device           *dev;
-       struct at25_data        *at25;
-
-       dev = kobj_to_dev(kobj);
-       at25 = dev_get_drvdata(dev);
+       struct at25_data *at25 = context;
+       off_t offset = *(u32 *)reg;
+       int err;
 
-       return at25_ee_read(at25, buf, off, count);
+       err = at25_ee_read(at25, val, offset, val_size);
+       if (err)
+               return err;
+       return 0;
 }
 
-
 static ssize_t
 at25_ee_write(struct at25_data *at25, const char *buf, loff_t off,
              size_t count)
@@ -154,10 +156,10 @@ at25_ee_write(struct at25_data *at25, const char *buf, loff_t off,
        unsigned                buf_size;
        u8                      *bounce;
 
-       if (unlikely(off >= at25->bin.size))
+       if (unlikely(off >= at25->chip.byte_len))
                return -EFBIG;
-       if ((off + count) > at25->bin.size)
-               count = at25->bin.size - off;
+       if ((off + count) > at25->chip.byte_len)
+               count = at25->chip.byte_len - off;
        if (unlikely(!count))
                return count;
 
@@ -264,20 +266,30 @@ at25_ee_write(struct at25_data *at25, const char *buf, loff_t off,
        return written ? written : status;
 }
 
-static ssize_t
-at25_bin_write(struct file *filp, struct kobject *kobj,
-              struct bin_attribute *bin_attr,
-              char *buf, loff_t off, size_t count)
+static int at25_regmap_write(void *context, const void *data, size_t count)
 {
-       struct device           *dev;
-       struct at25_data        *at25;
+       struct at25_data *at25 = context;
+       const char *buf;
+       u32 offset;
+       size_t len;
+       int err;
 
-       dev = kobj_to_dev(kobj);
-       at25 = dev_get_drvdata(dev);
+       memcpy(&offset, data, sizeof(offset));
+       buf = (const char *)data + sizeof(offset);
+       len = count - sizeof(offset);
 
-       return at25_ee_write(at25, buf, off, count);
+       err = at25_ee_write(at25, buf, offset, len);
+       if (err)
+               return err;
+       return 0;
 }
 
+static const struct regmap_bus at25_regmap_bus = {
+       .read = at25_regmap_read,
+       .write = at25_regmap_write,
+       .reg_format_endian_default = REGMAP_ENDIAN_NATIVE,
+};
+
 /*-------------------------------------------------------------------------*/
 
 static int at25_fw_to_chip(struct device *dev, struct spi_eeprom *chip)
@@ -337,6 +349,7 @@ static int at25_probe(struct spi_device *spi)
 {
        struct at25_data        *at25 = NULL;
        struct spi_eeprom       chip;
+       struct regmap           *regmap;
        int                     err;
        int                     sr;
        int                     addrlen;
@@ -381,35 +394,35 @@ static int at25_probe(struct spi_device *spi)
        spi_set_drvdata(spi, at25);
        at25->addrlen = addrlen;
 
-       /* Export the EEPROM bytes through sysfs, since that's convenient.
-        * And maybe to other kernel code; it might hold a board's Ethernet
-        * address, or board-specific calibration data generated on the
-        * manufacturing floor.
-        *
-        * Default to root-only access to the data; EEPROMs often hold data
-        * that's sensitive for read and/or write, like ethernet addresses,
-        * security codes, board-specific manufacturing calibrations, etc.
-        */
-       sysfs_bin_attr_init(&at25->bin);
-       at25->bin.attr.name = "eeprom";
-       at25->bin.attr.mode = S_IRUSR;
-       at25->bin.read = at25_bin_read;
-
-       at25->bin.size = at25->chip.byte_len;
-       if (!(chip.flags & EE_READONLY)) {
-               at25->bin.write = at25_bin_write;
-               at25->bin.attr.mode |= S_IWUSR;
-       }
+       at25->regmap_config.reg_bits = 32;
+       at25->regmap_config.val_bits = 8;
+       at25->regmap_config.reg_stride = 1;
+       at25->regmap_config.max_register = chip.byte_len - 1;
 
-       err = sysfs_create_bin_file(&spi->dev.kobj, &at25->bin);
-       if (err)
-               return err;
+       regmap = devm_regmap_init(&spi->dev, &at25_regmap_bus, at25,
+                                 &at25->regmap_config);
+       if (IS_ERR(regmap)) {
+               dev_err(&spi->dev, "regmap init failed\n");
+               return PTR_ERR(regmap);
+       }
 
-       dev_info(&spi->dev, "%Zd %s %s eeprom%s, pagesize %u\n",
-               (at25->bin.size < 1024)
-                       ? at25->bin.size
-                       : (at25->bin.size / 1024),
-               (at25->bin.size < 1024) ? "Byte" : "KByte",
+       at25->nvmem_config.name = dev_name(&spi->dev);
+       at25->nvmem_config.dev = &spi->dev;
+       at25->nvmem_config.read_only = chip.flags & EE_READONLY;
+       at25->nvmem_config.root_only = true;
+       at25->nvmem_config.owner = THIS_MODULE;
+       at25->nvmem_config.compat = true;
+       at25->nvmem_config.base_dev = &spi->dev;
+
+       at25->nvmem = nvmem_register(&at25->nvmem_config);
+       if (IS_ERR(at25->nvmem))
+               return PTR_ERR(at25->nvmem);
+
+       dev_info(&spi->dev, "%d %s %s eeprom%s, pagesize %u\n",
+               (chip.byte_len < 1024)
+                       ? chip.byte_len
+                       : (chip.byte_len / 1024),
+               (chip.byte_len < 1024) ? "Byte" : "KByte",
                at25->chip.name,
                (chip.flags & EE_READONLY) ? " (readonly)" : "",
                at25->chip.page_size);
@@ -421,7 +434,8 @@ static int at25_remove(struct spi_device *spi)
        struct at25_data        *at25;
 
        at25 = spi_get_drvdata(spi);
-       sysfs_remove_bin_file(&spi->dev.kobj, &at25->bin);
+       nvmem_unregister(at25->nvmem);
+
        return 0;
 }