aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSrinivas Kandagatla <srinivas.kandagatla@linaro.org>2015-03-19 11:19:14 +0000
committerSrinivas Kandagatla <srinivas.kandagatla@linaro.org>2015-03-19 11:19:14 +0000
commit5b1d3dec8f4dea54ea833bcbfba85d6b90a92f5f (patch)
tree9544bc2d106faa806e20ba6e59e1b7d6e90890fa
parent1093f256adf8d22491615087f12a1202c70039b0 (diff)
parent88c2c07d10ae1003a76231e489e54fe92e27c2f5 (diff)
Merge branch 'tracking-qcomlt-efuse' into integration-linux-qcomlttracking-integration-linux-qcomlt-ll-20150319.0
* tracking-qcomlt-efuse: eeprom: Add to MAINTAINERS for eeprom framework eeprom: qfprom: Add bindings for qfprom eeprom: qfprom: Add Qualcomm QFPROM support. eeprom: sunxi: Move the SID driver to the eeprom framework eeprom: Add bindings for simple eeprom framework eeprom: Add a simple EEPROM framework for eeprom consumers eeprom: Add a simple EEPROM framework for eeprom providers
-rw-r--r--Documentation/ABI/testing/sysfs-driver-sunxi-sid22
-rw-r--r--Documentation/devicetree/bindings/eeprom/allwinner,sunxi-sid.txt (renamed from Documentation/devicetree/bindings/misc/allwinner,sunxi-sid.txt)4
-rw-r--r--Documentation/devicetree/bindings/eeprom/eeprom.txt70
-rw-r--r--Documentation/devicetree/bindings/eeprom/qfprom.txt23
-rw-r--r--MAINTAINERS9
-rw-r--r--drivers/Kconfig2
-rw-r--r--drivers/Makefile1
-rw-r--r--drivers/eeprom/Kconfig33
-rw-r--r--drivers/eeprom/Makefile9
-rw-r--r--drivers/eeprom/core.c517
-rw-r--r--drivers/eeprom/eeprom-sunxi-sid.c136
-rw-r--r--drivers/eeprom/qfprom.c87
-rw-r--r--drivers/misc/eeprom/Kconfig13
-rw-r--r--drivers/misc/eeprom/Makefile1
-rw-r--r--drivers/misc/eeprom/sunxi_sid.c156
-rw-r--r--include/linux/eeprom-consumer.h67
-rw-r--r--include/linux/eeprom-provider.h47
17 files changed, 1005 insertions, 192 deletions
diff --git a/Documentation/ABI/testing/sysfs-driver-sunxi-sid b/Documentation/ABI/testing/sysfs-driver-sunxi-sid
deleted file mode 100644
index ffb9536f6ecc..000000000000
--- a/Documentation/ABI/testing/sysfs-driver-sunxi-sid
+++ /dev/null
@@ -1,22 +0,0 @@
-What: /sys/devices/*/<our-device>/eeprom
-Date: August 2013
-Contact: Oliver Schinagl <oliver@schinagl.nl>
-Description: read-only access to the SID (Security-ID) on current
- A-series SoC's from Allwinner. Currently supports A10, A10s, A13
- and A20 CPU's. The earlier A1x series of SoCs exports 16 bytes,
- whereas the newer A20 SoC exposes 512 bytes split into sections.
- Besides the 16 bytes of SID, there's also an SJTAG area,
- HDMI-HDCP key and some custom keys. Below a quick overview, for
- details see the user manual:
- 0x000 128 bit root-key (sun[457]i)
- 0x010 128 bit boot-key (sun7i)
- 0x020 64 bit security-jtag-key (sun7i)
- 0x028 16 bit key configuration (sun7i)
- 0x02b 16 bit custom-vendor-key (sun7i)
- 0x02c 320 bit low general key (sun7i)
- 0x040 32 bit read-control access (sun7i)
- 0x064 224 bit low general key (sun7i)
- 0x080 2304 bit HDCP-key (sun7i)
- 0x1a0 768 bit high general key (sun7i)
-Users: any user space application which wants to read the SID on
- Allwinner's A-series of CPU's.
diff --git a/Documentation/devicetree/bindings/misc/allwinner,sunxi-sid.txt b/Documentation/devicetree/bindings/eeprom/allwinner,sunxi-sid.txt
index fabdf64a5737..cceaaf6e45e3 100644
--- a/Documentation/devicetree/bindings/misc/allwinner,sunxi-sid.txt
+++ b/Documentation/devicetree/bindings/eeprom/allwinner,sunxi-sid.txt
@@ -4,6 +4,10 @@ Required properties:
- compatible: "allwinner,sun4i-a10-sid" or "allwinner,sun7i-a20-sid"
- reg: Should contain registers location and length
+= Data cells =
+Are child nodes of qfprom, bindings of which as described in
+bindings/eeprom/eeprom.txt
+
Example for sun4i:
sid@01c23800 {
compatible = "allwinner,sun4i-a10-sid";
diff --git a/Documentation/devicetree/bindings/eeprom/eeprom.txt b/Documentation/devicetree/bindings/eeprom/eeprom.txt
new file mode 100644
index 000000000000..8348d18a9e57
--- /dev/null
+++ b/Documentation/devicetree/bindings/eeprom/eeprom.txt
@@ -0,0 +1,70 @@
+= EEPROM Data Device Tree Bindings =
+
+This binding is intended to represent the location of hardware
+configuration data stored in EEPROMs.
+
+On a significant proportion of boards, the manufacturer has stored
+some data on an EEPROM-like device, for the OS to be able to retrieve
+these information and act upon it. Obviously, the OS has to know
+about where to retrieve these data from, and where they are stored on
+the storage device.
+
+This document is here to document this.
+
+= Data providers =
+Contains bindings specific to provider drivers and data cells as children
+to this node.
+
+= Data cells =
+These are the child nodes of the provider which contain data cell
+information like offset and size in eeprom provider.
+
+Required properties:
+reg: specifies the offset in byte within that storage device, and the length
+ in bytes of the data we care about.
+ There could be more then one offset-length pairs in this property.
+
+Optional properties:
+As required by specific data parsers/interpreters.
+
+For example:
+
+ /* Provider */
+ qfprom: qfprom@00700000 {
+ compatible = "qcom,qfprom";
+ reg = <0x00700000 0x1000>;
+ ...
+
+ /* Data cells */
+ tsens_calibration: calib@404 {
+ reg = <0x404 0x10>;
+ };
+
+ serial_number: sn {
+ reg = <0x104 0x4>, <0x204 0x4>, <0x30c 0x4>;
+
+ };
+ ...
+ };
+
+= Data consumers =
+Are device nodes which consume eeprom data cells.
+
+Required properties:
+
+eeproms: List of phandle and data cell the device might be interested in.
+
+Optional properties:
+
+eeprom-names: List of data cell name strings sorted in the same order
+ as the eeproms property. Consumers drivers will use
+ eeprom-names to differentiate between multiple cells,
+ and hence being able to know what these cells are for.
+
+For example:
+
+ tsens {
+ ...
+ eeproms = <&tsens_calibration>;
+ eeprom-names = "calibration";
+ };
diff --git a/Documentation/devicetree/bindings/eeprom/qfprom.txt b/Documentation/devicetree/bindings/eeprom/qfprom.txt
new file mode 100644
index 000000000000..d5baed6c92cc
--- /dev/null
+++ b/Documentation/devicetree/bindings/eeprom/qfprom.txt
@@ -0,0 +1,23 @@
+= Qualcomm QFPROM device tree bindings =
+
+This binding is intended to represent QFPROM which is found in most QCOM SOCs.
+
+Required properties:
+- compatible: should be "qcom,qfprom"
+- reg: Should contain registers location and length
+
+= Data cells =
+Are child nodes of qfprom, bindings of which as described in
+bindings/eeprom/eeprom.txt
+
+Example:
+
+ qfprom: qfprom@00700000 {
+ compatible = "qcom,qfprom";
+ reg = <0x00700000 0x1000>;
+ ...
+ /* Data cells */
+ tsens_calibration: calib@404 {
+ reg = <0x404 0x10>;
+ };
+ };
diff --git a/MAINTAINERS b/MAINTAINERS
index 0e1abe8cc684..fe881208bc43 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -3721,6 +3721,15 @@ T: git git://git.alsa-project.org/alsa-kernel.git
S: Maintained
F: sound/usb/misc/ua101.c
+EEPROM FRAMEWORK
+M: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
+M: Maxime Ripard <maxime.ripard@free-electrons.com>
+S: Maintained
+F: drivers/eeprom/
+F: Documentation/devicetree/bindings/eeprom/
+F: include/linux/eeprom-provider.h
+F: include/linux/eeprom-consumer.h
+
EXTENSIBLE FIRMWARE INTERFACE (EFI)
M: Matt Fleming <matt.fleming@intel.com>
L: linux-efi@vger.kernel.org
diff --git a/drivers/Kconfig b/drivers/Kconfig
index c0cc96bab9e7..1a0f99e474ea 100644
--- a/drivers/Kconfig
+++ b/drivers/Kconfig
@@ -182,4 +182,6 @@ source "drivers/thunderbolt/Kconfig"
source "drivers/android/Kconfig"
+source "drivers/eeprom/Kconfig"
+
endmenu
diff --git a/drivers/Makefile b/drivers/Makefile
index 527a6da8d539..57eb5b0ab9b8 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -165,3 +165,4 @@ obj-$(CONFIG_RAS) += ras/
obj-$(CONFIG_THUNDERBOLT) += thunderbolt/
obj-$(CONFIG_CORESIGHT) += coresight/
obj-$(CONFIG_ANDROID) += android/
+obj-$(CONFIG_EEPROM) += eeprom/
diff --git a/drivers/eeprom/Kconfig b/drivers/eeprom/Kconfig
new file mode 100644
index 000000000000..b77828b175e8
--- /dev/null
+++ b/drivers/eeprom/Kconfig
@@ -0,0 +1,33 @@
+menuconfig EEPROM
+ tristate "EEPROM Support"
+ depends on OF
+ select REGMAP
+ help
+ Support for EEPROM alike devices.
+
+ This framework is designed to provide a generic interface to EEPROM
+ from both the Linux Kernel and the userspace.
+
+ If unsure, say no.
+
+if EEPROM
+
+config EEPROM_SUNXI_SID
+ tristate "Allwinner SoCs SID support"
+ depends on ARCH_SUNXI
+ select REGMAP_MMIO
+ help
+ This is a driver for the 'security ID' available on various Allwinner
+ devices.
+
+ This driver can also be built as a module. If so, the module
+ will be called eprom-sunxi-sid.
+
+config QCOM_QFPROM
+ tristate "QCOM QFPROM Support"
+ depends on EEPROM
+ select REGMAP_MMIO
+ help
+ Say y here to enable QFPROM support. The QFPROM provides access
+ functions for QFPROM data to rest of the drivers via eeprom interface.
+endif
diff --git a/drivers/eeprom/Makefile b/drivers/eeprom/Makefile
new file mode 100644
index 000000000000..9c1bb03a4359
--- /dev/null
+++ b/drivers/eeprom/Makefile
@@ -0,0 +1,9 @@
+#
+# Makefile for eeprom drivers.
+#
+
+obj-$(CONFIG_EEPROM) += core.o
+
+# Devices
+obj-$(CONFIG_EEPROM_SUNXI_SID) += eeprom-sunxi-sid.o
+obj-$(CONFIG_QCOM_QFPROM) += qfprom.o
diff --git a/drivers/eeprom/core.c b/drivers/eeprom/core.c
new file mode 100644
index 000000000000..b87e136972c9
--- /dev/null
+++ b/drivers/eeprom/core.c
@@ -0,0 +1,517 @@
+/*
+ * EEPROM framework core.
+ *
+ * Copyright (C) 2015 Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
+ * Copyright (C) 2013 Maxime Ripard <maxime.ripard@free-electrons.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/device.h>
+#include <linux/eeprom-provider.h>
+#include <linux/eeprom-consumer.h>
+#include <linux/export.h>
+#include <linux/fs.h>
+#include <linux/idr.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+
+struct eeprom_device {
+ struct regmap *regmap;
+ int stride;
+ size_t size;
+
+ struct module *owner;
+ struct device dev;
+ int id;
+ int users;
+};
+
+struct eeprom_cell {
+ struct eeprom_device *eeprom;
+ int nblocks;
+ int size;
+ struct eeprom_block blocks[0];
+};
+
+static DEFINE_MUTEX(eeprom_mutex);
+static DEFINE_IDA(eeprom_ida);
+
+#define to_eeprom(d) container_of(d, struct eeprom_device, dev)
+
+static ssize_t bin_attr_eeprom_read(struct file *filp, struct kobject *kobj,
+ struct bin_attribute *attr,
+ char *buf, loff_t offset, size_t count)
+{
+ struct device *dev = container_of(kobj, struct device, kobj);
+ struct eeprom_device *eeprom = to_eeprom(dev);
+ int rc;
+
+ if (offset > eeprom->size)
+ return -EINVAL;
+
+ if (offset + count > eeprom->size)
+ count = eeprom->size - offset;
+
+ rc = regmap_bulk_read(eeprom->regmap, offset,
+ buf, count/eeprom->stride);
+
+ if (IS_ERR_VALUE(rc))
+ return rc;
+
+ return count - count % eeprom->stride;
+}
+
+static ssize_t bin_attr_eeprom_write(struct file *filp, struct kobject *kobj,
+ struct bin_attribute *attr,
+ char *buf, loff_t offset, size_t count)
+{
+ struct device *dev = container_of(kobj, struct device, kobj);
+ struct eeprom_device *eeprom = to_eeprom(dev);
+ int rc;
+
+ if (offset > eeprom->size)
+ return -EINVAL;
+
+ if (offset + count > eeprom->size)
+ count = eeprom->size - offset;
+
+ rc = regmap_bulk_write(eeprom->regmap, offset,
+ buf, count/eeprom->stride);
+
+ if (IS_ERR_VALUE(rc))
+ return rc;
+
+ return count - count % eeprom->stride;
+}
+
+static struct bin_attribute bin_attr_eeprom = {
+ .attr = {
+ .name = "eeprom",
+ .mode = S_IWUSR | S_IRUGO,
+ },
+ .read = bin_attr_eeprom_read,
+ .write = bin_attr_eeprom_write,
+};
+
+static struct bin_attribute *eeprom_bin_attributes[] = {
+ &bin_attr_eeprom,
+ NULL,
+};
+
+static const struct attribute_group eeprom_bin_group = {
+ .bin_attrs = eeprom_bin_attributes,
+};
+
+static const struct attribute_group *eeprom_dev_groups[] = {
+ &eeprom_bin_group,
+ NULL,
+};
+
+static void eeprom_release(struct device *dev)
+{
+ kfree(to_eeprom(dev));
+}
+
+static struct class eeprom_class = {
+ .name = "eeprom",
+ .dev_groups = eeprom_dev_groups,
+ .dev_release = eeprom_release,
+};
+
+static int of_eeprom_match(struct device *dev, const void *eeprom_np)
+{
+ return dev->of_node == eeprom_np;
+}
+
+static struct eeprom_device *of_eeprom_find(struct device_node *eeprom_np)
+{
+ struct device *d;
+
+ if (!eeprom_np)
+ return NULL;
+
+ d = class_find_device(&eeprom_class, NULL, eeprom_np, of_eeprom_match);
+
+ return d ? to_eeprom(d) : NULL;
+}
+
+static int eeprom_match(struct device *dev, const void *data)
+{
+ return !strcmp(dev_name(dev), (const char *)data);
+}
+
+static struct eeprom_device *eeprom_find(const char *name)
+{
+ struct device *d;
+
+ d = class_find_device(&eeprom_class, NULL, (void *)name, eeprom_match);
+
+ return d ? to_eeprom(d) : NULL;
+}
+
+/**
+ * eeprom_register(): Register a eeprom device for given eeprom.
+ * Also creates an binary entry in /sys/class/eeprom/name-id/eeprom
+ *
+ * @eeprom: eeprom device that needs to be created
+ *
+ * The return value will be an error code on error or a zero on success.
+ * The eeprom_device and sysfs entery will be freed by the eeprom_unregister().
+ */
+
+struct eeprom_device *eeprom_register(struct eeprom_config *config)
+{
+ struct eeprom_device *eeprom;
+ int rval;
+
+ if (!config->regmap || !config->size) {
+ dev_err(config->dev, "Regmap not found\n");
+ return ERR_PTR(-EINVAL);
+ }
+
+ eeprom = kzalloc(sizeof(*eeprom), GFP_KERNEL);
+ if (!eeprom)
+ return ERR_PTR(-ENOMEM);
+
+ eeprom->id = ida_simple_get(&eeprom_ida, 0, 0, GFP_KERNEL);
+ if (eeprom->id < 0)
+ return ERR_PTR(eeprom->id);
+
+ eeprom->owner = config->owner;
+ eeprom->regmap = config->regmap;
+ eeprom->stride = config->stride;
+ eeprom->size = config->size;
+ eeprom->dev.class = &eeprom_class;
+ eeprom->dev.parent = config->dev;
+ eeprom->dev.of_node = config->dev ? config->dev->of_node : NULL;
+ dev_set_name(&eeprom->dev, "%s%d",
+ config->name ? : "eeprom", config->id);
+
+ device_initialize(&eeprom->dev);
+
+ dev_dbg(&eeprom->dev, "Registering eeprom device %s\n",
+ dev_name(&eeprom->dev));
+
+ rval = device_add(&eeprom->dev);
+ if (rval)
+ return ERR_PTR(rval);
+
+ return eeprom;
+}
+EXPORT_SYMBOL_GPL(eeprom_register);
+
+/**
+ * eeprom_unregister(): Unregister previously registered eeprom device
+ *
+ * @eeprom: Pointer to previously registered eeprom device.
+ *
+ * The return value will be an non zero on error or a zero on success.
+ */
+int eeprom_unregister(struct eeprom_device *eeprom)
+{
+ mutex_lock(&eeprom_mutex);
+ if (eeprom->users) {
+ mutex_unlock(&eeprom_mutex);
+ return -EBUSY;
+ }
+ mutex_unlock(&eeprom_mutex);
+
+ device_del(&eeprom->dev);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(eeprom_unregister);
+
+static int eeprom_cell_sanity_check(struct eeprom_cell *cell)
+{
+ struct eeprom_device *eeprom = cell->eeprom;
+ int i;
+
+ /* byte aligned, no need to check for stride sanity */
+ if (eeprom->stride == 1)
+ return 0;
+
+ for (i = 0; i < cell->nblocks; i++) {
+ if (!IS_ALIGNED(cell->blocks[i].offset, eeprom->stride) ||
+ !IS_ALIGNED(cell->blocks[i].count, eeprom->stride)) {
+ dev_err(&eeprom->dev,
+ "cell unaligned to eeprom stride %d\n",
+ eeprom->stride);
+ return -EINVAL;
+ }
+ }
+
+ return 0;
+}
+
+static struct eeprom_cell *__eeprom_cell_get(struct device_node *cell_np,
+ const char *ename,
+ struct eeprom_block *blocks,
+ int nblocks)
+{
+ struct eeprom_cell *cell;
+ struct eeprom_device *eeprom = NULL;
+ struct property *prop;
+ const __be32 *vp;
+ u32 pv;
+ int i, rval;
+
+ mutex_lock(&eeprom_mutex);
+
+ eeprom = cell_np ? of_eeprom_find(cell_np->parent) : eeprom_find(ename);
+ if (!eeprom) {
+ mutex_unlock(&eeprom_mutex);
+ return ERR_PTR(-EPROBE_DEFER);
+ }
+
+ eeprom->users++;
+ mutex_unlock(&eeprom_mutex);
+
+ if (!try_module_get(eeprom->owner)) {
+ dev_err(&eeprom->dev,
+ "could not increase module refcount for cell %s\n",
+ ename);
+ rval = -EINVAL;
+ goto err_mod;
+ }
+
+ if (cell_np)
+ nblocks = of_property_count_u32_elems(cell_np, "reg") / 2;
+
+ cell = kzalloc(sizeof(*cell) + nblocks * sizeof(*blocks), GFP_KERNEL);
+ if (!cell) {
+ rval = -ENOMEM;
+ goto err_mem;
+ }
+
+ cell->nblocks = nblocks;
+ cell->eeprom = eeprom;
+ cell->size = 0;
+ i = 0;
+
+ if (cell_np) {
+ of_property_for_each_u32(cell_np, "reg", prop, vp, pv) {
+ cell->blocks[i].offset = pv;
+ vp = of_prop_next_u32(prop, vp, &pv);
+ cell->blocks[i].count = pv;
+ cell->size += pv;
+ i++;
+ }
+ } else {
+ memcpy(cell->blocks, blocks, nblocks * sizeof(*blocks));
+ for (; i < nblocks; i++)
+ cell->size += blocks[i].count;
+ }
+
+ if (IS_ERR_VALUE(eeprom_cell_sanity_check(cell))) {
+ rval = -EINVAL;
+ goto err_sanity;
+ }
+
+ return cell;
+
+err_sanity:
+ kfree(cell);
+
+err_mem:
+ module_put(eeprom->owner);
+
+err_mod:
+ mutex_lock(&eeprom_mutex);
+ eeprom->users--;
+ mutex_unlock(&eeprom_mutex);
+
+ return ERR_PTR(rval);
+
+}
+
+/**
+ * eeprom_cell_get(): Get eeprom cell of device form a given eeprom name
+ * and blocks.
+ *
+ * @ename: eeprom device name that needs to be looked-up.
+ * @blocks: eeprom blocks containing offset and length information.
+ * @nblocks: number of eeprom blocks.
+ *
+ * The return value will be an ERR_PTR() on error or a valid pointer
+ * to a struct eeprom_cell. The eeprom_cell will be freed by the
+ * eeprom_cell_put().
+ */
+struct eeprom_cell *eeprom_cell_get(const char *ename,
+ struct eeprom_block *blocks, int nblocks)
+{
+ return __eeprom_cell_get(NULL, ename, blocks, nblocks);
+}
+EXPORT_SYMBOL_GPL(eeprom_cell_get);
+
+/**
+ * of_eeprom_cell_get(): Get eeprom cell of device form a given index
+ *
+ * @dev: Device that will be interacted with
+ * @index: eeprom index in eeproms property.
+ *
+ * The return value will be an ERR_PTR() on error or a valid pointer
+ * to a struct eeprom_cell. The eeprom_cell will be freed by the
+ * eeprom_cell_put().
+ */
+struct eeprom_cell *of_eeprom_cell_get(struct device *dev, int index)
+{
+ struct device_node *cell_np;
+
+ if (!dev || !dev->of_node)
+ return ERR_PTR(-EINVAL);
+
+ cell_np = of_parse_phandle(dev->of_node, "eeproms", index);
+ if (!cell_np)
+ return ERR_PTR(-EPROBE_DEFER);
+
+ return __eeprom_cell_get(cell_np, NULL, NULL, 0);
+}
+EXPORT_SYMBOL_GPL(of_eeprom_cell_get);
+
+/**
+ * of_eeprom_cell_get_byname(): Get eeprom cell of device form a given name
+ *
+ * @dev: Device that will be interacted with
+ * @name: eeprom name in eeprom-names property.
+ *
+ * The return value will be an ERR_PTR() on error or a valid pointer
+ * to a struct eeprom_cell. The eeprom_cell will be freed by the
+ * eeprom_cell_put().
+ */
+struct eeprom_cell *of_eeprom_cell_get_byname(struct device *dev,
+ const char *id)
+{
+ int index = 0;
+
+ if (!dev || !dev->of_node)
+ return ERR_PTR(-EINVAL);
+
+ if (id)
+ index = of_property_match_string(dev->of_node,
+ "eeprom-names",
+ id);
+ return of_eeprom_cell_get(dev, index);
+
+}
+EXPORT_SYMBOL_GPL(of_eeprom_cell_get_byname);
+
+/**
+ * eeprom_cell_put(): Release previously allocated eeprom cell.
+ *
+ * @cell: Previously allocated eeprom cell by eeprom_cell_get()
+ * or of_eeprom_cell_get() or of_eeprom_cell_get_byname().
+ */
+void eeprom_cell_put(struct eeprom_cell *cell)
+{
+ struct eeprom_device *eeprom = cell->eeprom;
+
+ mutex_lock(&eeprom_mutex);
+ eeprom->users--;
+ mutex_unlock(&eeprom_mutex);
+ module_put(eeprom->owner);
+ kfree(cell);
+}
+EXPORT_SYMBOL_GPL(eeprom_cell_put);
+
+/**
+ * eeprom_cell_read(): Read a given eeprom cell
+ *
+ * @cell: eeprom cell to be read.
+ * @len: pointer to length of cell which will be populated on successful read.
+ *
+ * The return value will be an ERR_PTR() on error or a valid pointer
+ * to a char * bufffer. The buffer should be freed by the consumer with a
+ * kfree().
+ */
+char *eeprom_cell_read(struct eeprom_cell *cell, ssize_t *len)
+{
+ struct eeprom_device *eeprom = cell->eeprom;
+ char *buf;
+ int rc, i, offset = 0;
+
+ if (!eeprom || !eeprom->regmap)
+ return ERR_PTR(-EINVAL);
+
+ buf = kzalloc(cell->size, GFP_KERNEL);
+ if (!buf)
+ return ERR_PTR(-ENOMEM);
+
+ for (i = 0; i < cell->nblocks; i++) {
+ rc = regmap_bulk_read(eeprom->regmap, cell->blocks[i].offset,
+ buf + offset,
+ cell->blocks[i].count);
+
+ if (IS_ERR_VALUE(rc)) {
+ kfree(buf);
+ return ERR_PTR(rc);
+ }
+ offset += cell->blocks[i].count;
+ }
+
+ *len = cell->size;
+
+ return buf;
+}
+EXPORT_SYMBOL_GPL(eeprom_cell_read);
+
+/**
+ * eeprom_cell_write(): Write to a given eeprom cell
+ *
+ * @cell: eeprom cell to be written.
+ * @buf: Buffer to be written.
+ * @len: length of buffer to be written to eeprom cell.
+ *
+ * The return value will be an non zero on error or a zero on successful write.
+ */
+int eeprom_cell_write(struct eeprom_cell *cell, const char *buf, ssize_t len)
+{
+ struct eeprom_device *eeprom = cell->eeprom;
+ int i, rc, offset = 0;
+
+ if (!eeprom || !eeprom->regmap || len != cell->size)
+ return -EINVAL;
+
+ for (i = 0; i < cell->nblocks; i++) {
+ rc = regmap_bulk_write(eeprom->regmap, cell->blocks[i].offset,
+ buf + offset,
+ cell->blocks[i].count);
+
+ if (IS_ERR_VALUE(rc))
+ return rc;
+
+ offset += cell->blocks[i].count;
+ }
+
+ return len;
+}
+EXPORT_SYMBOL_GPL(eeprom_cell_write);
+
+static int eeprom_init(void)
+{
+ return class_register(&eeprom_class);
+}
+
+static void eeprom_exit(void)
+{
+ class_unregister(&eeprom_class);
+}
+
+subsys_initcall(eeprom_init);
+module_exit(eeprom_exit);
+
+MODULE_AUTHOR("Srinivas Kandagatla <srinivas.kandagatla@linaro.org");
+MODULE_AUTHOR("Maxime Ripard <maxime.ripard@free-electrons.com");
+MODULE_DESCRIPTION("EEPROM Driver Core");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/eeprom/eeprom-sunxi-sid.c b/drivers/eeprom/eeprom-sunxi-sid.c
new file mode 100644
index 000000000000..58f7d24c8386
--- /dev/null
+++ b/drivers/eeprom/eeprom-sunxi-sid.c
@@ -0,0 +1,136 @@
+/*
+ * Allwinner sunXi SoCs Security ID support.
+ *
+ * Copyright (c) 2013 Oliver Schinagl <oliver@schinagl.nl>
+ * Copyright (C) 2014 Maxime Ripard <maxime.ripard@free-electrons.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/device.h>
+#include <linux/eeprom-provider.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+
+struct eeprom_sid {
+ void __iomem *membase;
+ struct eeprom_device *eeprom;
+};
+
+static struct eeprom_config econfig = {
+ .stride = 1,
+ .name = "sunix-sid",
+};
+
+/* We read the entire key, due to a 32 bit read alignment requirement. Since we
+ * want to return the requested byte, this results in somewhat slower code and
+ * uses 4 times more reads as needed but keeps code simpler. Since the SID is
+ * only very rarely probed, this is not really an issue.
+ */
+static int sunxi_sid_reg_read(void *context,
+ unsigned int offset, unsigned int *val)
+{
+ struct eeprom_sid *sid = context;
+ u32 sid_key;
+
+ sid_key = ioread32be(sid->membase + round_down(offset, 4));
+ sid_key >>= (offset % 4) * 8;
+
+ *val = sid_key;
+
+ return 0;
+}
+
+static bool sunxi_sid_writeable_reg(struct device *dev, unsigned int reg)
+{
+ return false;
+}
+
+static const struct of_device_id sunxi_sid_of_match[] = {
+ { .compatible = "allwinner,sun4i-a10-sid", .data = (void *)16},
+ { .compatible = "allwinner,sun7i-a20-sid", .data = (void *)512},
+ {/* sentinel */},
+};
+MODULE_DEVICE_TABLE(of, sunxi_sid_of_match);
+
+static struct regmap_config sunxi_sid_regmap_config = {
+ .reg_bits = 32,
+ .val_bits = 8,
+ .reg_stride = 1,
+ .reg_read = sunxi_sid_reg_read,
+ .writeable_reg = sunxi_sid_writeable_reg,
+};
+
+static int sunxi_sid_probe(struct platform_device *pdev)
+{
+ const struct of_device_id *device;
+ struct eeprom_sid *sid;
+ struct resource *res;
+ struct eeprom_device *eeprom;
+ struct device *dev = &pdev->dev;
+
+ sid = devm_kzalloc(dev, sizeof(*sid), GFP_KERNEL);
+ if (!sid)
+ return -ENOMEM;
+
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ sid->membase = devm_ioremap_resource(dev, res);
+ if (IS_ERR(sid->membase))
+ return PTR_ERR(sid->membase);
+
+ device = of_match_device(sunxi_sid_of_match, dev);
+ if (!device)
+ return -ENODEV;
+
+ sunxi_sid_regmap_config.max_register = (unsigned int)device->data - 1;
+
+ econfig.regmap = devm_regmap_init(dev, NULL,
+ sid, &sunxi_sid_regmap_config);
+ if (IS_ERR(econfig.regmap))
+ return PTR_ERR(econfig.regmap);
+
+ econfig.dev = dev;
+ econfig.owner = THIS_MODULE;
+ econfig.size = sunxi_sid_regmap_config.max_register;
+
+ eeprom = eeprom_register(&econfig);
+ if (IS_ERR(eeprom))
+ return PTR_ERR(eeprom);
+
+ platform_set_drvdata(pdev, eeprom);
+
+ return 0;
+}
+
+static int sunxi_sid_remove(struct platform_device *pdev)
+{
+ struct eeprom_device *eeprom = platform_get_drvdata(pdev);
+
+ return eeprom_unregister(eeprom);
+}
+
+static struct platform_driver sunxi_sid_driver = {
+ .probe = sunxi_sid_probe,
+ .remove = sunxi_sid_remove,
+ .driver = {
+ .name = "eeprom-sunxi-sid",
+ .of_match_table = sunxi_sid_of_match,
+ },
+};
+module_platform_driver(sunxi_sid_driver);
+
+MODULE_AUTHOR("Oliver Schinagl <oliver@schinagl.nl>");
+MODULE_DESCRIPTION("Allwinner sunxi security id driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/eeprom/qfprom.c b/drivers/eeprom/qfprom.c
new file mode 100644
index 000000000000..7d9e5e9641b1
--- /dev/null
+++ b/drivers/eeprom/qfprom.c
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2015 Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/err.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/eeprom-provider.h>
+
+static struct regmap_config qfprom_regmap_config = {
+ .reg_bits = 32,
+ .val_bits = 8,
+ .reg_stride = 1,
+};
+
+static struct eeprom_config econfig = {
+ .stride = 1,
+ .name = "qfprom",
+};
+
+static int qfprom_remove(struct platform_device *pdev)
+{
+ struct eeprom_device *eeprom = platform_get_drvdata(pdev);
+
+ return eeprom_unregister(eeprom);
+}
+
+static int qfprom_probe(struct platform_device *pdev)
+{
+ struct resource *res;
+ void __iomem *base;
+ struct device *dev = &pdev->dev;
+ struct eeprom_device *eeprom;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ base = devm_ioremap_resource(dev, res);
+ if (IS_ERR(base))
+ return PTR_ERR(base);
+
+ qfprom_regmap_config.max_register = resource_size(res) - 1;
+
+ econfig.regmap = devm_regmap_init_mmio(dev, base,
+ &qfprom_regmap_config);
+ if (IS_ERR(econfig.regmap)) {
+ dev_err(dev, "regmap init failed\n");
+ return PTR_ERR(econfig.regmap);
+ }
+ econfig.owner = THIS_MODULE;
+ econfig.dev = dev;
+ econfig.size = resource_size(res) - 1;
+ eeprom = eeprom_register(&econfig);
+ if (IS_ERR(eeprom))
+ return PTR_ERR(eeprom);
+
+ platform_set_drvdata(pdev, eeprom);
+ return 0;
+}
+
+static const struct of_device_id qfprom_of_match[] = {
+ { .compatible = "qcom,qfprom"},
+ {/* sentinel */},
+};
+MODULE_DEVICE_TABLE(of, qfprom_of_match);
+
+static struct platform_driver qfprom_driver = {
+ .probe = qfprom_probe,
+ .remove = qfprom_remove,
+ .driver = {
+ .name = "qcom,qfprom",
+ .of_match_table = qfprom_of_match,
+ },
+};
+module_platform_driver(qfprom_driver);
+MODULE_AUTHOR("Srinivas Kandagatla <srinivas.kandagatla@linaro.org>");
+MODULE_DESCRIPTION("Qualcomm QFPROM driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/misc/eeprom/Kconfig b/drivers/misc/eeprom/Kconfig
index 9536852fd4c6..04f2e1fa9dd1 100644
--- a/drivers/misc/eeprom/Kconfig
+++ b/drivers/misc/eeprom/Kconfig
@@ -96,17 +96,4 @@ config EEPROM_DIGSY_MTC_CFG
If unsure, say N.
-config EEPROM_SUNXI_SID
- tristate "Allwinner sunxi security ID support"
- depends on ARCH_SUNXI && SYSFS
- help
- This is a driver for the 'security ID' available on various Allwinner
- devices.
-
- Due to the potential risks involved with changing e-fuses,
- this driver is read-only.
-
- This driver can also be built as a module. If so, the module
- will be called sunxi_sid.
-
endmenu
diff --git a/drivers/misc/eeprom/Makefile b/drivers/misc/eeprom/Makefile
index 9507aec95e94..fc1e81d29267 100644
--- a/drivers/misc/eeprom/Makefile
+++ b/drivers/misc/eeprom/Makefile
@@ -4,5 +4,4 @@ obj-$(CONFIG_EEPROM_LEGACY) += eeprom.o
obj-$(CONFIG_EEPROM_MAX6875) += max6875.o
obj-$(CONFIG_EEPROM_93CX6) += eeprom_93cx6.o
obj-$(CONFIG_EEPROM_93XX46) += eeprom_93xx46.o
-obj-$(CONFIG_EEPROM_SUNXI_SID) += sunxi_sid.o
obj-$(CONFIG_EEPROM_DIGSY_MTC_CFG) += digsy_mtc_eeprom.o
diff --git a/drivers/misc/eeprom/sunxi_sid.c b/drivers/misc/eeprom/sunxi_sid.c
deleted file mode 100644
index 8385177ff32b..000000000000
--- a/drivers/misc/eeprom/sunxi_sid.c
+++ /dev/null
@@ -1,156 +0,0 @@
-/*
- * Copyright (c) 2013 Oliver Schinagl <oliver@schinagl.nl>
- * http://www.linux-sunxi.org
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * This driver exposes the Allwinner security ID, efuses exported in byte-
- * sized chunks.
- */
-
-#include <linux/compiler.h>
-#include <linux/device.h>
-#include <linux/err.h>
-#include <linux/export.h>
-#include <linux/fs.h>
-#include <linux/io.h>
-#include <linux/kernel.h>
-#include <linux/kobject.h>
-#include <linux/module.h>
-#include <linux/of_device.h>
-#include <linux/platform_device.h>
-#include <linux/random.h>
-#include <linux/slab.h>
-#include <linux/stat.h>
-#include <linux/sysfs.h>
-#include <linux/types.h>
-
-#define DRV_NAME "sunxi-sid"
-
-struct sunxi_sid_data {
- void __iomem *reg_base;
- unsigned int keysize;
-};
-
-/* We read the entire key, due to a 32 bit read alignment requirement. Since we
- * want to return the requested byte, this results in somewhat slower code and
- * uses 4 times more reads as needed but keeps code simpler. Since the SID is
- * only very rarely probed, this is not really an issue.
- */
-static u8 sunxi_sid_read_byte(const struct sunxi_sid_data *sid_data,
- const unsigned int offset)
-{
- u32 sid_key;
-
- if (offset >= sid_data->keysize)
- return 0;
-
- sid_key = ioread32be(sid_data->reg_base + round_down(offset, 4));
- sid_key >>= (offset % 4) * 8;
-
- return sid_key; /* Only return the last byte */
-}
-
-static ssize_t sid_read(struct file *fd, struct kobject *kobj,
- struct bin_attribute *attr, char *buf,
- loff_t pos, size_t size)
-{
- struct platform_device *pdev;
- struct sunxi_sid_data *sid_data;
- int i;
-
- pdev = to_platform_device(kobj_to_dev(kobj));
- sid_data = platform_get_drvdata(pdev);
-
- if (pos < 0 || pos >= sid_data->keysize)
- return 0;
- if (size > sid_data->keysize - pos)
- size = sid_data->keysize - pos;
-
- for (i = 0; i < size; i++)
- buf[i] = sunxi_sid_read_byte(sid_data, pos + i);
-
- return i;
-}
-
-static struct bin_attribute sid_bin_attr = {
- .attr = { .name = "eeprom", .mode = S_IRUGO, },
- .read = sid_read,
-};
-
-static int sunxi_sid_remove(struct platform_device *pdev)
-{
- device_remove_bin_file(&pdev->dev, &sid_bin_attr);
- dev_dbg(&pdev->dev, "driver unloaded\n");
-
- return 0;
-}
-
-static const struct of_device_id sunxi_sid_of_match[] = {
- { .compatible = "allwinner,sun4i-a10-sid", .data = (void *)16},
- { .compatible = "allwinner,sun7i-a20-sid", .data = (void *)512},
- {/* sentinel */},
-};
-MODULE_DEVICE_TABLE(of, sunxi_sid_of_match);
-
-static int sunxi_sid_probe(struct platform_device *pdev)
-{
- struct sunxi_sid_data *sid_data;
- struct resource *res;
- const struct of_device_id *of_dev_id;
- u8 *entropy;
- unsigned int i;
-
- sid_data = devm_kzalloc(&pdev->dev, sizeof(struct sunxi_sid_data),
- GFP_KERNEL);
- if (!sid_data)
- return -ENOMEM;
-
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- sid_data->reg_base = devm_ioremap_resource(&pdev->dev, res);
- if (IS_ERR(sid_data->reg_base))
- return PTR_ERR(sid_data->reg_base);
-
- of_dev_id = of_match_device(sunxi_sid_of_match, &pdev->dev);
- if (!of_dev_id)
- return -ENODEV;
- sid_data->keysize = (int)of_dev_id->data;
-
- platform_set_drvdata(pdev, sid_data);
-
- sid_bin_attr.size = sid_data->keysize;
- if (device_create_bin_file(&pdev->dev, &sid_bin_attr))
- return -ENODEV;
-
- entropy = kzalloc(sizeof(u8) * sid_data->keysize, GFP_KERNEL);
- for (i = 0; i < sid_data->keysize; i++)
- entropy[i] = sunxi_sid_read_byte(sid_data, i);
- add_device_randomness(entropy, sid_data->keysize);
- kfree(entropy);
-
- dev_dbg(&pdev->dev, "loaded\n");
-
- return 0;
-}
-
-static struct platform_driver sunxi_sid_driver = {
- .probe = sunxi_sid_probe,
- .remove = sunxi_sid_remove,
- .driver = {
- .name = DRV_NAME,
- .of_match_table = sunxi_sid_of_match,
- },
-};
-module_platform_driver(sunxi_sid_driver);
-
-MODULE_AUTHOR("Oliver Schinagl <oliver@schinagl.nl>");
-MODULE_DESCRIPTION("Allwinner sunxi security id driver");
-MODULE_LICENSE("GPL");
diff --git a/include/linux/eeprom-consumer.h b/include/linux/eeprom-consumer.h
new file mode 100644
index 000000000000..6d9d075997ba
--- /dev/null
+++ b/include/linux/eeprom-consumer.h
@@ -0,0 +1,67 @@
+/*
+ * EEPROM framework consumer.
+ *
+ * Copyright (C) 2015 Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
+ * Copyright (C) 2013 Maxime Ripard <maxime.ripard@free-electrons.com>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#ifndef _LINUX_EEPROM_CONSUMER_H
+#define _LINUX_EEPROM_CONSUMER_H
+
+struct eeprom_cell;
+
+struct eeprom_block {
+ loff_t offset;
+ size_t count;
+};
+#if IS_ENABLED(CONFIG_EEPROM)
+struct eeprom_cell *eeprom_cell_get(const char *ename,
+ struct eeprom_block *blocks, int nblocks);
+void eeprom_cell_put(struct eeprom_cell *cell);
+char *eeprom_cell_read(struct eeprom_cell *cell, ssize_t *len);
+int eeprom_cell_write(struct eeprom_cell *cell, const char *buf, ssize_t len);
+#else
+
+static inline struct eeprom_cell *eeprom_cell_get(const char *ename,
+ struct eeprom_block *blocks, int nblocks)
+{
+ return NULL;
+}
+
+static inline void eeprom_cell_put(struct eeprom_cell *cell)
+{
+}
+
+static inline char *eeprom_cell_read(struct eeprom_cell *cell, ssize_t *len)
+{
+ return NULL;
+}
+
+static inline int eeprom_cell_write(struct eeprom_cell *cell,
+ const char *buf, ssize_t len)
+{
+ return -ENOSYS;
+}
+#endif /* CONFIG_EEPROM */
+
+#if IS_ENABLED(CONFIG_EEPROM) && IS_ENABLED(CONFIG_OF)
+struct eeprom_cell *of_eeprom_cell_get(struct device *dev, int index);
+struct eeprom_cell *of_eeprom_cell_get_byname(struct device *dev,
+ const char *name);
+#else
+static inline struct eeprom_cell *of_eeprom_cell_get(
+ struct device *dev, int index)
+{
+ return NULL;
+}
+static inline struct eeprom_cell *of_eeprom_cell_get_byname(struct device *dev,
+ const char *name)
+{
+ return NULL;
+}
+#endif
+#endif /* ifndef _LINUX_EEPROM_CONSUMER_H */
diff --git a/include/linux/eeprom-provider.h b/include/linux/eeprom-provider.h
new file mode 100644
index 000000000000..21afdaa8d7af
--- /dev/null
+++ b/include/linux/eeprom-provider.h
@@ -0,0 +1,47 @@
+/*
+ * EEPROM framework provider.
+ *
+ * Copyright (C) 2015 Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
+ * Copyright (C) 2013 Maxime Ripard <maxime.ripard@free-electrons.com>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#ifndef _LINUX_EEPROM_PROVIDER_H
+#define _LINUX_EEPROM_PROVIDER_H
+
+#include <linux/regmap.h>
+
+struct eeprom_device;
+
+struct eeprom_config {
+ struct device *dev;
+ struct regmap *regmap;
+ const char *name;
+ int id;
+ int stride;
+ size_t size;
+ struct module *owner;
+};
+
+#if IS_ENABLED(CONFIG_EEPROM)
+
+struct eeprom_device *eeprom_register(struct eeprom_config *cfg);
+int eeprom_unregister(struct eeprom_device *eeprom);
+
+#else
+
+static inline struct eeprom_device *eeprom_register(struct eeprom_config *cfg)
+{
+ return NULL;
+}
+static inline int eeprom_unregister(struct eeprom_device *eeprom)
+{
+ return -ENOSYS;
+}
+
+#endif /* CONFIG_EEPROM */
+
+#endif /* ifndef _LINUX_EEPROM_PROVIDER_H */