[PATCH v2 1/2] Create eeprom_dev hardware class for EEPROM devices

From: Curt Brune
Date: Thu Jan 23 2014 - 14:26:34 EST


Create a new hardware class under /sys/class/eeprom_dev

EEPROM drivers can register their devices with the eeprom_dev class
during instantiation.

The registered devices show up as:

/sys/class/eeprom_dev/eeprom0
/sys/class/eeprom_dev/eeprom1
...
/sys/class/eeprom_dev/eeprom[N]

Each member of the eeprom class exports a sysfs file called "label",
containing the label property from the corresponding device tree node.

Example:

/sys/class/eeprom_dev/eeprom0/label

If the device tree node property "label" does not exist the value
"unknown" is used.

Note: The class cannot be called 'eeprom' as that is the name of the
I/O file created by the driver. The class name appears as a
sub-directory within the main device directory. Hence the class name
'eeprom_dev'.

Userspace can use the label to identify what the EEPROM is for.

The real device is available from the class device via the "device"
link:

/sys/class/eeprom_dev/eeprom0/device

Signed-off-by: Curt Brune <curt@xxxxxxxxxxxxxxxxxxx>
---
Documentation/misc-devices/eeprom_hw_class.txt | 81 ++++++++++++
drivers/misc/eeprom/Kconfig | 11 ++
drivers/misc/eeprom/Makefile | 1 +
drivers/misc/eeprom/eeprom_class.c | 159 ++++++++++++++++++++++++
include/linux/eeprom_class.h | 35 ++++++
5 files changed, 287 insertions(+)
create mode 100644 Documentation/misc-devices/eeprom_hw_class.txt
create mode 100644 drivers/misc/eeprom/eeprom_class.c
create mode 100644 include/linux/eeprom_class.h

diff --git a/Documentation/misc-devices/eeprom_hw_class.txt b/Documentation/misc-devices/eeprom_hw_class.txt
new file mode 100644
index 0000000..b5cbc35
--- /dev/null
+++ b/Documentation/misc-devices/eeprom_hw_class.txt
@@ -0,0 +1,81 @@
+EEPROM Device Hardware Class
+============================
+
+This feature is enabled by CONFIG_EEPROM_CLASS.
+
+The original problem:
+
+We work on several different switching platforms, each of which has
+about 64 EEPROMs, one for each of the 10G SFP+ modules. In addition
+the systems typically have a board info EEPROM, SPD and power supply
+EEPROMs. It is difficult to map the device tree entries for the
+EEPROMs to the appropriate sysfs device needed for I/O in a generic
+way.
+
+Also mappings are further complicated by some systems using custom i2c
+buses implemented in FPGAs.
+
+The solution is two fold:
+
+1. Create an EEPROM class for all EEPROM devices. Each EEPROM driver,
+at24 for example, would register with the class during probe().
+
+2. Create a mapping in the .dts file by adding a property called
+'label' to each EEPROM entry. The EEPROM class will expose this label
+property for all EEPROMs.
+
+For example, for all the EEPROM devices in the system you would see
+directories in sysfs like:
+
+ /sys/class/eeprom_dev/eeprom0
+ /sys/class/eeprom_dev/eeprom1
+ /sys/class/eeprom_dev/eeprom2
+ ...
+ /sys/class/eeprom_dev/eeprom<N>
+
+Within each eepromN directory you would find:
+
+ root@switch:/sys/class/eeprom_dev# ls -l eeprom2/
+ total 0
+ lrwxrwxrwx 1 root root 0 Sep 3 22:08 device -> ../../../1-0050
+ -r--r--r-- 1 root root 4096 Sep 3 22:08 label
+ lrwxrwxrwx 1 root root 0 Sep 4 17:18 subsystem -> ../../../../../../../class/eeprom_dev
+
+device -- this is a symlink to the physical device. For example to
+dump the EEPROM data of eeprom2 you could do:
+
+ hexdump -C /sys/class/eeprom_dev/eeprom2/device/eeprom
+
+As an example the device tree entry corresponding to eeprom2 could
+look like:
+
+ sfp_eeprom@50 {
+ compatible = "at,24c04";
+ reg = <0x50>;
+ label = "port6";
+ };
+
+From the original problem, imagine 64 similar entries for all the
+other ports. Plus a few more entries for board EEPROM and power
+supply EEPROMs.
+
+From user space if I wanted to know the device corresponding to port6
+I could do something as simple as:
+
+root@switch:~# grep port6 /sys/class/eeprom_dev/eeprom*/label
+/sys/class/eeprom_dev/eeprom2/label:port6
+
+Then I could access the information via
+/sys/class/eeprom_dev/eeprom2/device/eeprom.
+
+It is nice that it keeps the mapping all in one place, in the .dts
+file. It is not spread around in the device tree and some other
+platform specific configuration file.
+
+Note: For devices without a 'label' property the label file is still
+created, however, its contents would be set to 'unknown'.
+
+Note2: The class cannot be called 'eeprom' as that is the name of the
+I/O file created by the driver. The class name appears as a
+sub-directory within the main device directory. Hence the class name
+'eeprom_dev'.
diff --git a/drivers/misc/eeprom/Kconfig b/drivers/misc/eeprom/Kconfig
index 9536852f..be6b727 100644
--- a/drivers/misc/eeprom/Kconfig
+++ b/drivers/misc/eeprom/Kconfig
@@ -1,7 +1,18 @@
menu "EEPROM support"

+config EEPROM_CLASS
+ tristate "EEPROM Hardware Class support"
+ depends on SYSFS
+ default y
+ help
+ Creates a hardware class in sysfs called "eeprom_dev",
+ providing a common place to register EEPROM devices.
+
+ This support can also be built as a module. If so, the module
+ will be called eeprom_class.
+
config EEPROM_AT24
tristate "I2C EEPROMs / RAMs / ROMs from most vendors"
depends on I2C && SYSFS
help
Enable this driver to get read/write support to most I2C EEPROMs
diff --git a/drivers/misc/eeprom/Makefile b/drivers/misc/eeprom/Makefile
index 9507aec..d2369ca 100644
--- a/drivers/misc/eeprom/Makefile
+++ b/drivers/misc/eeprom/Makefile
@@ -1,5 +1,6 @@
+obj-$(CONFIG_EEPROM_CLASS) += eeprom_class.o
obj-$(CONFIG_EEPROM_AT24) += at24.o
obj-$(CONFIG_EEPROM_AT25) += at25.o
obj-$(CONFIG_EEPROM_LEGACY) += eeprom.o
obj-$(CONFIG_EEPROM_MAX6875) += max6875.o
obj-$(CONFIG_EEPROM_93CX6) += eeprom_93cx6.o
diff --git a/drivers/misc/eeprom/eeprom_class.c b/drivers/misc/eeprom/eeprom_class.c
new file mode 100644
index 0000000..c934ba6e
--- /dev/null
+++ b/drivers/misc/eeprom/eeprom_class.c
@@ -0,0 +1,159 @@
+/*
+ * eeprom_class.c
+ *
+ * This file defines the sysfs class "eeprom", for use by EEPROM
+ * drivers.
+ *
+ * Copyright (C) 2013 Cumulus Networks, Inc.
+ * Author: Curt Brune <curt@xxxxxxxxxxxxxxxxxxx>
+ *
+ * Ideas and structure graciously borrowed from the hwmon class:
+ * Copyright (C) 2005 Mark M. Hoffman <mhoffman@xxxxxxxxxxxxx>
+ *
+ * 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; version 2 of the License.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/kdev_t.h>
+#include <linux/idr.h>
+#include <linux/eeprom_class.h>
+#include <linux/gfp.h>
+#include <linux/spinlock.h>
+#include <linux/pci.h>
+
+/* Root eeprom "class" object (corresponds to '/<sysfs>/class/eeprom_dev/') */
+static struct class *eeprom_class;
+
+#define EEPROM_CLASS_NAME "eeprom_dev"
+#define EEPROM_ID_PREFIX "eeprom"
+#define EEPROM_ID_FORMAT EEPROM_ID_PREFIX "%d"
+
+static DEFINE_IDA(eeprom_ida);
+
+/**
+ * eeprom_device_register - register w/ eeprom class
+ * @dev: the device to register
+ *
+ * eeprom_device_unregister() must be called when the device is no
+ * longer needed.
+ *
+ * Creates a new eeprom class device that is a child of @dev. Also
+ * creates a symlink in /<sysfs>/class/eeprom_dev/eeprom[N] pointing
+ * to the new device.
+ *
+ * Returns the pointer to the new device.
+ */
+struct device *eeprom_device_register(struct device *dev)
+{
+ struct device *eeprom_dev;
+ int id;
+
+ id = ida_simple_get(&eeprom_ida, 0, 0, GFP_KERNEL);
+ if (id < 0)
+ return ERR_PTR(id);
+
+ eeprom_dev = device_create(eeprom_class, dev, MKDEV(0, 0), NULL,
+ EEPROM_ID_FORMAT, id);
+
+ if (IS_ERR(eeprom_dev))
+ ida_simple_remove(&eeprom_ida, id);
+
+ return eeprom_dev;
+}
+
+/**
+ * eeprom_device_unregister - removes the previously registered class device
+ *
+ * @dev: the class device to destroy
+ */
+void eeprom_device_unregister(struct device *dev)
+{
+ int id;
+
+ if (likely(sscanf(dev_name(dev), EEPROM_ID_FORMAT, &id) == 1)) {
+ device_unregister(dev);
+ ida_simple_remove(&eeprom_ida, id);
+ } else
+ dev_dbg(dev->parent,
+ "eeprom_device_unregister() failed: bad class ID!\n");
+}
+
+/**
+ * Each member of the eeprom class exports a sysfs file called
+ * "label", containing the label property from the corresponding
+ * device tree node.
+ *
+ * Userspace can use the label to identify what the EEPROM is for.
+ */
+static ssize_t label_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ const char* cp = NULL;
+ int len = 0;
+
+ /*
+ * The class device is a child of the original device,
+ * i.e. dev->parent points to the original device.
+ */
+ if (dev->parent && dev->parent->of_node)
+ cp = of_get_property(dev->parent->of_node, "label", &len);
+
+ if ((cp == NULL) || (len == 0)) {
+ cp = "unknown";
+ len = strlen(cp) + 1;
+ }
+
+ strncpy(buf, cp, len - 1);
+ buf[len - 1] = '\n';
+ buf[len] = '\0';
+
+ return len;
+}
+
+struct device_attribute eeprom_class_dev_attrs[] = {
+ __ATTR_RO(label),
+ __ATTR_NULL,
+};
+
+static int __init eeprom_init(void)
+{
+ eeprom_class = class_create(THIS_MODULE, EEPROM_CLASS_NAME);
+ if (IS_ERR(eeprom_class)) {
+ pr_err("couldn't create sysfs class\n");
+ return PTR_ERR(eeprom_class);
+ }
+
+ eeprom_class->dev_attrs = eeprom_class_dev_attrs;
+
+ return 0;
+}
+
+static void __exit eeprom_exit(void)
+{
+ class_destroy(eeprom_class);
+}
+
+subsys_initcall(eeprom_init);
+module_exit(eeprom_exit);
+
+EXPORT_SYMBOL_GPL(eeprom_device_register);
+EXPORT_SYMBOL_GPL(eeprom_device_unregister);
+
+MODULE_AUTHOR("Curt Brune <curt@xxxxxxxxxxxxxxxxxxx>");
+MODULE_DESCRIPTION("eeprom sysfs/class support");
+MODULE_LICENSE("GPL v2");
diff --git a/include/linux/eeprom_class.h b/include/linux/eeprom_class.h
new file mode 100644
index 0000000..389ac16
--- /dev/null
+++ b/include/linux/eeprom_class.h
@@ -0,0 +1,35 @@
+/*
+ * eeprom_class.c
+ *
+ * This file exports interface functions for the sysfs class "eeprom",
+ * for use by EEPROM drivers.
+ *
+ * Copyright (C) 2013 Cumulus Networks, Inc.
+ * Author: Curt Brune <curt@xxxxxxxxxxxxxxxxxxx>
+ *
+ * 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; version 2 of the License.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#ifndef EEPROM_CLASS_H__
+#define EEPROM_CLASS_H__
+
+#include <linux/device.h>
+#include <linux/err.h>
+
+struct device *eeprom_device_register(struct device *dev);
+
+void eeprom_device_unregister(struct device *dev);
+
+#endif /* EEPROM_CLASS_H__ */
--
1.7.10.4

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/