2011-05-24 16:03:04

by Anatolij Gustschin

[permalink] [raw]
Subject: [PATCH 1/2] misc/eeprom: add driver for 93xx46 EEPROMs over GPIO

93xx46 EEPROMs can be connected using GPIO lines. Add a generic
93xx46 EEPROM driver using common GPIO API for such configurations.
A platform is supposed to register appropriate 93xx46 gpio device
providing GPIO interface description and using this driver
read/write/erase access to the EEPROM chip can be easily done
over sysfs files.

Signed-off-by: Anatolij Gustschin <[email protected]>
---
drivers/misc/eeprom/Kconfig | 10 +
drivers/misc/eeprom/Makefile | 1 +
drivers/misc/eeprom/gpio-93xx46.c | 525 +++++++++++++++++++++++++++++++++++++
include/linux/gpio-93xx46.h | 19 ++
4 files changed, 555 insertions(+), 0 deletions(-)
create mode 100644 drivers/misc/eeprom/gpio-93xx46.c
create mode 100644 include/linux/gpio-93xx46.h

diff --git a/drivers/misc/eeprom/Kconfig b/drivers/misc/eeprom/Kconfig
index 9118613..fcceffd 100644
--- a/drivers/misc/eeprom/Kconfig
+++ b/drivers/misc/eeprom/Kconfig
@@ -70,4 +70,14 @@ config EEPROM_93CX6

If unsure, say N.

+config EEPROM_GPIO_93XX46
+ tristate "EEPROM 93XX46 over GPIO support"
+ depends on GPIOLIB && SYSFS
+ help
+ Driver for the EEPROM chipsets 93xx46x connected with GPIO.
+ The driver supports both read and write commands and also
+ the command to erase the whole EEPROM.
+
+ If unsure, say N.
+
endmenu
diff --git a/drivers/misc/eeprom/Makefile b/drivers/misc/eeprom/Makefile
index df3d68f..38d8259 100644
--- a/drivers/misc/eeprom/Makefile
+++ b/drivers/misc/eeprom/Makefile
@@ -3,3 +3,4 @@ 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
+obj-$(CONFIG_EEPROM_GPIO_93XX46) += gpio-93xx46.o
diff --git a/drivers/misc/eeprom/gpio-93xx46.c b/drivers/misc/eeprom/gpio-93xx46.c
new file mode 100644
index 0000000..5c7d7dd
--- /dev/null
+++ b/drivers/misc/eeprom/gpio-93xx46.c
@@ -0,0 +1,525 @@
+/*
+ * Driver for 93xx46 EEPROMs over GPIO lines.
+ *
+ * (C) 2011 DENX Software Engineering, Anatolij Gustschin <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/fs.h>
+#include <linux/gpio.h>
+#include <linux/gpio-93xx46.h>
+#include <linux/delay.h>
+#include <linux/mutex.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/sysctl.h>
+
+#define OP_START 0x4
+#define OP_WRITE (OP_START | 0x1)
+#define OP_READ (OP_START | 0x2)
+#define OP_ERASE (OP_START | 0x3)
+#define OP_EWEN (OP_START | 0x0)
+#define OP_EWDS (OP_START | 0x0)
+#define ADDR_EWDS 0x00
+#define ADDR_ERAL 0x20
+#define ADDR_EWEN 0x30
+#define DELAY 450
+
+struct gpio_93xx46_dev {
+ struct device *dev;
+ struct gpio_93xx46_platform_data *pdata;
+ struct bin_attribute bin;
+ int addrlen;
+
+ struct gpio pins[4];
+};
+
+static DEFINE_MUTEX(gpio_93xx46_mutex);
+
+static int gpio_93xx46_request_gpios(struct gpio_93xx46_dev *edev)
+{
+ struct gpio_93xx46_platform_data *pd = edev->pdata;
+ const char *name = to_platform_device(edev->dev)->name;
+ int ret;
+
+ edev->pins[0].gpio = pd->clk;
+ edev->pins[0].flags = GPIOF_OUT_INIT_LOW;
+ edev->pins[0].label = name;
+ edev->pins[1].gpio = pd->cs;
+ edev->pins[1].flags = GPIOF_OUT_INIT_LOW;
+ edev->pins[1].label = name;
+ edev->pins[2].gpio = pd->din;
+ edev->pins[2].flags = GPIOF_OUT_INIT_LOW;
+ edev->pins[2].label = name;
+ edev->pins[3].gpio = pd->dout;
+ edev->pins[3].flags = GPIOF_IN;
+ edev->pins[3].label = name;
+
+ ret = gpio_request_array(edev->pins, ARRAY_SIZE(edev->pins));
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static void gpio_93xx46_tx_bit(struct gpio_93xx46_dev *edev, bool bit)
+{
+ struct gpio_93xx46_platform_data *pd = edev->pdata;
+
+ if (bit)
+ gpio_set_value(pd->din, 1);
+ else
+ gpio_set_value(pd->din, 0);
+
+ ndelay(DELAY);
+ gpio_set_value(pd->clk, 1);
+ ndelay(DELAY);
+ gpio_set_value(pd->clk, 0);
+}
+
+static inline unsigned char
+gpio_93xx46_rx_byte(struct gpio_93xx46_platform_data *pd)
+{
+ int data = 0, i;
+
+ for (i = 0; i < 8 ; i++) {
+ gpio_set_value(pd->clk, 1);
+ ndelay(DELAY);
+ gpio_set_value(pd->clk, 0);
+
+ if (gpio_get_value(pd->dout))
+ data |= 1;
+ data <<= 1;
+ ndelay(DELAY);
+ }
+ return data >>= 1;
+}
+
+static void gpio_93xx46_read(struct gpio_93xx46_dev *edev,
+ char *buf, unsigned offs, size_t cnt)
+{
+ struct gpio_93xx46_platform_data *pd = edev->pdata;
+ int active = !(pd->flags & EE_CS_LOW);
+ int cmd_addr, len, mask;
+ int i;
+
+ cmd_addr = (OP_READ << edev->addrlen);
+ if (edev->addrlen == 7) {
+ cmd_addr |= (offs & 0x7f);
+ len = 10;
+ } else {
+ cmd_addr |= (offs & 0x3f);
+ len = 9;
+ }
+ mask = 1 << (len - 1);
+
+ mutex_lock(&gpio_93xx46_mutex);
+
+ gpio_set_value(pd->cs, !active);
+ gpio_set_value(pd->clk, 0);
+ gpio_set_value(pd->din, 1);
+ ndelay(DELAY);
+
+ if (pd->prepare)
+ pd->prepare(edev);
+
+ gpio_set_value(pd->cs, active);
+ ndelay(DELAY);
+
+ for (i = 0; i < len; i++, mask >>= 1)
+ gpio_93xx46_tx_bit(edev, !!(cmd_addr & mask));
+
+ ndelay(DELAY);
+
+ for (i = 0; i < cnt; i++)
+ buf[i] = gpio_93xx46_rx_byte(pd);
+
+ gpio_set_value(pd->clk, 0);
+ gpio_set_value(pd->din, 0);
+ gpio_set_value(pd->cs, !active);
+ ndelay(DELAY);
+
+ if (pd->finish)
+ pd->finish(edev);
+
+ mutex_unlock(&gpio_93xx46_mutex);
+}
+
+static void gpio_93xx46_ewen(struct gpio_93xx46_dev *edev)
+{
+ struct gpio_93xx46_platform_data *pd = edev->pdata;
+ int active = !(pd->flags & EE_CS_LOW);
+ int cmd_addr, len, mask;
+ int i;
+
+ cmd_addr = OP_EWEN << edev->addrlen;
+ if (edev->addrlen == 7) {
+ cmd_addr |= ADDR_EWEN << 1;
+ len = 10;
+ } else {
+ cmd_addr |= ADDR_EWEN;
+ len = 9;
+ }
+ mask = 1 << (len - 1);
+
+ mutex_lock(&gpio_93xx46_mutex);
+
+ gpio_set_value(pd->cs, !active);
+ gpio_set_value(pd->clk, 0);
+ gpio_set_value(pd->din, 1);
+ ndelay(DELAY);
+
+ if (pd->prepare)
+ pd->prepare(edev);
+
+ gpio_set_value(pd->cs, active);
+ ndelay(DELAY);
+
+ for (i = 0; i < len; i++, mask >>= 1)
+ gpio_93xx46_tx_bit(edev, !!(cmd_addr & mask));
+
+ ndelay(DELAY);
+
+ gpio_set_value(pd->clk, 0);
+ gpio_set_value(pd->din, 0);
+ gpio_set_value(pd->cs, !active);
+ ndelay(DELAY);
+
+ if (pd->finish)
+ pd->finish(edev);
+
+ mutex_unlock(&gpio_93xx46_mutex);
+}
+
+static void gpio_93xx46_ewds(struct gpio_93xx46_dev *edev)
+{
+ struct gpio_93xx46_platform_data *pd = edev->pdata;
+ int active = !(pd->flags & EE_CS_LOW);
+ int cmd_addr, len, mask;
+ int i;
+
+ cmd_addr = OP_EWDS << edev->addrlen;
+ if (edev->addrlen == 7) {
+ cmd_addr |= ADDR_EWDS << 1;
+ len = 10;
+ } else {
+ cmd_addr |= ADDR_EWDS;
+ len = 9;
+ }
+ mask = 1 << (len - 1);
+
+ mutex_lock(&gpio_93xx46_mutex);
+
+ gpio_set_value(pd->cs, !active);
+ gpio_set_value(pd->clk, 0);
+ gpio_set_value(pd->din, 1);
+ ndelay(DELAY);
+
+ if (pd->prepare)
+ pd->prepare(edev);
+
+ gpio_set_value(pd->cs, active);
+ ndelay(DELAY);
+
+ for (i = 0; i < len; i++, mask >>= 1)
+ gpio_93xx46_tx_bit(edev, !!(cmd_addr & mask));
+
+ ndelay(DELAY);
+
+ gpio_set_value(pd->clk, 0);
+ gpio_set_value(pd->din, 0);
+ gpio_set_value(pd->cs, !active);
+ ndelay(DELAY);
+
+ if (pd->finish)
+ pd->finish(edev);
+
+ mutex_unlock(&gpio_93xx46_mutex);
+}
+
+static void gpio_93xx46_eral(struct gpio_93xx46_dev *edev)
+{
+ struct gpio_93xx46_platform_data *pd = edev->pdata;
+ int active = !(pd->flags & EE_CS_LOW);
+ int cmd_addr, len, mask;
+ int i, to = 10;
+
+ cmd_addr = OP_START << edev->addrlen;
+ if (edev->addrlen == 7) {
+ cmd_addr |= ADDR_ERAL << 1;
+ len = 10;
+ } else {
+ cmd_addr |= ADDR_ERAL;
+ len = 9;
+ }
+ mask = 1 << (len - 1);
+
+ mutex_lock(&gpio_93xx46_mutex);
+
+ gpio_set_value(pd->cs, !active);
+ gpio_set_value(pd->clk, 0);
+ gpio_set_value(pd->din, 1);
+ ndelay(DELAY);
+
+ if (pd->prepare)
+ pd->prepare(edev);
+
+ gpio_set_value(pd->cs, active);
+ ndelay(DELAY);
+
+ for (i = 0; i < len; i++, mask >>= 1)
+ gpio_93xx46_tx_bit(edev, !!(cmd_addr & mask));
+
+ gpio_set_value(pd->cs, !active);
+ ndelay(DELAY);
+ gpio_set_value(pd->cs, active);
+ ndelay(DELAY);
+
+ while (!gpio_get_value(pd->dout)) {
+ if (!to--) {
+ dev_err(edev->dev, "erase not ready timeout\n");
+ break;
+ }
+ mdelay(1);
+ }
+
+ gpio_set_value(pd->clk, 0);
+ gpio_set_value(pd->din, 0);
+ gpio_set_value(pd->cs, !active);
+ ndelay(DELAY);
+
+ if (pd->finish)
+ pd->finish(edev);
+
+ mutex_unlock(&gpio_93xx46_mutex);
+}
+
+static void gpio_93xx46_write(struct gpio_93xx46_dev *edev,
+ unsigned offs, char data)
+{
+ struct gpio_93xx46_platform_data *pd = edev->pdata;
+ int active = !(pd->flags & EE_CS_LOW);
+ int cmd_addr, len, mask;
+ int i, to = 10;
+
+ cmd_addr = (OP_WRITE << edev->addrlen);
+ if (edev->addrlen == 7) {
+ cmd_addr |= (offs & 0x7f);
+ len = 10;
+ } else {
+ cmd_addr |= (offs & 0x3f);
+ len = 9;
+ }
+ mask = 1 << (len - 1);
+
+ mutex_lock(&gpio_93xx46_mutex);
+
+ gpio_set_value(pd->cs, !active);
+ gpio_set_value(pd->clk, 0);
+ gpio_set_value(pd->din, 1);
+ ndelay(DELAY);
+
+ if (pd->prepare)
+ pd->prepare(edev);
+
+ gpio_set_value(pd->cs, active);
+ ndelay(DELAY);
+
+ for (i = 0; i < len; i++, mask >>= 1)
+ gpio_93xx46_tx_bit(edev, !!(cmd_addr & mask));
+
+ ndelay(DELAY);
+
+ for (i = 0; i < 8; i++) {
+ gpio_93xx46_tx_bit(edev, !!(data & 0x80));
+ data <<= 1;
+ }
+
+ gpio_set_value(pd->cs, !active);
+ ndelay(DELAY);
+ gpio_set_value(pd->cs, active);
+ ndelay(DELAY);
+
+ while (!gpio_get_value(pd->dout)) {
+ if (!to--) {
+ dev_err(edev->dev, "write not ready timeout\n");
+ break;
+ }
+ mdelay(1);
+ }
+
+ gpio_set_value(pd->clk, 0);
+ gpio_set_value(pd->din, 0);
+ gpio_set_value(pd->cs, !active);
+ ndelay(DELAY);
+
+ if (pd->finish)
+ pd->finish(edev);
+
+ mutex_unlock(&gpio_93xx46_mutex);
+}
+
+static ssize_t
+gpio_93xx46_bin_read(struct file *filp, struct kobject *kobj,
+ struct bin_attribute *bin_attr,
+ char *buf, loff_t off, size_t count)
+{
+ struct device *dev = container_of(kobj, struct device, kobj);
+ struct gpio_93xx46_dev *edev = dev_get_drvdata(dev);
+
+ if (unlikely(!count))
+ return count;
+ if (unlikely(off >= edev->bin.size))
+ return 0;
+ if ((off + count) > edev->bin.size)
+ count = edev->bin.size - off;
+
+ gpio_93xx46_read(edev, buf, off, count);
+ return count;
+}
+
+static ssize_t
+gpio_93xx46_bin_write(struct file *filp, struct kobject *kobj,
+ struct bin_attribute *bin_attr,
+ char *buf, loff_t off, size_t count)
+{
+ struct device *dev = container_of(kobj, struct device, kobj);
+ struct gpio_93xx46_dev *edev = dev_get_drvdata(dev);
+ int i;
+
+ if (unlikely(!count))
+ return count;
+ if (unlikely(off >= edev->bin.size))
+ return 0;
+ if ((off + count) > edev->bin.size)
+ count = edev->bin.size - off;
+
+ gpio_93xx46_ewen(edev);
+
+ for (i = 0; i < count; i++)
+ gpio_93xx46_write(edev, off + i, buf[i]);
+
+ gpio_93xx46_ewds(edev);
+
+ return count;
+}
+
+ssize_t gpio_93xx46_store_erase(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct gpio_93xx46_dev *edev = dev_get_drvdata(dev);
+ int erase = 0;
+
+ sscanf(buf, "%d", &erase);
+ if (erase) {
+ gpio_93xx46_ewen(edev);
+ gpio_93xx46_eral(edev);
+ gpio_93xx46_ewds(edev);
+ }
+
+ return count;
+}
+static DEVICE_ATTR(erase, S_IWUSR, NULL, gpio_93xx46_store_erase);
+
+static int __devinit gpio_93xx46_probe(struct platform_device *pdev)
+{
+ struct gpio_93xx46_platform_data *pd = pdev->dev.platform_data;
+ struct gpio_93xx46_dev *edev = NULL;
+ int err;
+
+ if (!pd)
+ return -ENODEV;
+
+ edev = kzalloc(sizeof(*edev), GFP_KERNEL);
+ if (!edev)
+ return -ENOMEM;
+
+ edev->dev = &pdev->dev;
+ edev->pdata = pd;
+ if (pd->flags & EE_ADDR8)
+ edev->addrlen = 7;
+ else if (pd->flags & EE_ADDR16)
+ edev->addrlen = 6;
+ else {
+ dev_err(&pdev->dev,
+ "invalid address flags 0x%x\n", pd->flags);
+ err = -EINVAL;
+ goto fail;
+ }
+
+ err = gpio_93xx46_request_gpios(edev);
+ if (err)
+ goto fail;
+
+ sysfs_bin_attr_init(&edev->bin);
+ edev->bin.attr.name = "eeprom";
+ edev->bin.attr.mode = S_IRUSR;
+ edev->bin.read = gpio_93xx46_bin_read;
+ edev->bin.size = 128;
+ if (!(pd->flags & EE_READONLY)) {
+ edev->bin.write = gpio_93xx46_bin_write;
+ edev->bin.attr.mode |= S_IWUSR;
+ }
+
+ err = sysfs_create_bin_file(&pdev->dev.kobj, &edev->bin);
+ if (err)
+ goto fail;
+
+ if (!(pd->flags & EE_READONLY)) {
+ if (device_create_file(&pdev->dev, &dev_attr_erase))
+ dev_err(&pdev->dev, "can't create erase interface\n");
+ }
+
+ platform_set_drvdata(pdev, edev);
+
+ return 0;
+fail:
+ kfree(edev);
+ return err;
+}
+
+static int __devexit gpio_93xx46_remove(struct platform_device *pdev)
+{
+ struct gpio_93xx46_platform_data *pd = pdev->dev.platform_data;
+ struct gpio_93xx46_dev *edev = platform_get_drvdata(pdev);
+
+ if (!(pd->flags & EE_READONLY))
+ device_remove_file(&pdev->dev, &dev_attr_erase);
+ sysfs_remove_bin_file(&pdev->dev.kobj, &edev->bin);
+ gpio_free_array(edev->pins, ARRAY_SIZE(edev->pins));
+ platform_set_drvdata(pdev, NULL);
+ kfree(edev);
+ return 0;
+}
+
+static struct platform_driver gpio_93xx46_driver = {
+ .probe = gpio_93xx46_probe,
+ .remove = __devexit_p(gpio_93xx46_remove),
+ .driver = {
+ .name = "gpio-93xx46",
+ .owner = THIS_MODULE,
+ }
+};
+
+static int __init gpio_93xx46_init(void)
+{
+ return platform_driver_register(&gpio_93xx46_driver);
+}
+module_init(gpio_93xx46_init);
+
+static void __exit gpio_93xx46_exit(void)
+{
+ platform_driver_unregister(&gpio_93xx46_driver);
+}
+module_exit(gpio_93xx46_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Driver for 93xx46 EEPROMs over GPIO");
+MODULE_AUTHOR("Anatolij Gustschin <[email protected]>");
+MODULE_ALIAS("platform:gpio-93xx46");
diff --git a/include/linux/gpio-93xx46.h b/include/linux/gpio-93xx46.h
new file mode 100644
index 0000000..a565de6
--- /dev/null
+++ b/include/linux/gpio-93xx46.h
@@ -0,0 +1,19 @@
+/*
+ * Module: gpio-93xx46
+ * Interface description for 93xx46 EEPROMs connected over GPIO.
+ */
+struct gpio_93xx46_platform_data {
+ unsigned clk;
+ unsigned cs;
+ unsigned din;
+ unsigned dout;
+
+ u8 flags;
+#define EE_ADDR8 0x01 /* 8 bit addr. cfg */
+#define EE_ADDR16 0x02 /* 16 bit addr. cfg */
+#define EE_READONLY 0x08 /* forbid writing */
+#define EE_CS_LOW 0x10 /* CS is active low */
+
+ void (*prepare)(void *);
+ void (*finish)(void *);
+};
--
1.7.1


2011-05-24 16:03:10

by Anatolij Gustschin

[permalink] [raw]
Subject: [PATCH 2/2] misc/eeprom: add eeprom access driver for digsy_mtc board

Both displays on digsy_mtc board obtain their configuration
from MicroWire EEPROMs which are connected to the SoC
over GPIO lines. We need an easy way to access the EEPROMs
to write the needed display configuration or to read out the
currently programmed configuration. The generic gpio-93xx46
driver added by previous patch allows EEPROM access over sysfs.
Using the simple driver added by this patch we provide used
GPIO interface and access control description on the board for
generic gpio-93xx46 driver.

Signed-off-by: Anatolij Gustschin <[email protected]>
---
drivers/misc/eeprom/Kconfig | 13 ++++++
drivers/misc/eeprom/Makefile | 1 +
drivers/misc/eeprom/digsy_mtc_eeprom.c | 75 ++++++++++++++++++++++++++++++++
3 files changed, 89 insertions(+), 0 deletions(-)
create mode 100644 drivers/misc/eeprom/digsy_mtc_eeprom.c

diff --git a/drivers/misc/eeprom/Kconfig b/drivers/misc/eeprom/Kconfig
index fcceffd..b74b402 100644
--- a/drivers/misc/eeprom/Kconfig
+++ b/drivers/misc/eeprom/Kconfig
@@ -80,4 +80,17 @@ config EEPROM_GPIO_93XX46

If unsure, say N.

+config DIGSY_MTC_CFG_EEPROM
+ tristate "DigsyMTC display configuration EEPROMs device"
+ depends on PPC_MPC5200_GPIO
+ select EEPROM_GPIO_93XX46
+ help
+ This option enables access to display configuration EEPROMs on
+ digsy_mtc board. sysfs entries will be created for that EEPROM
+ allowing to read/write the configuration data or to erase the
+ whole EEPROM.
+
+ To compile this driver as a module, choose M here: the
+ module will be called digsy_mtc_eeprom.
+
endmenu
diff --git a/drivers/misc/eeprom/Makefile b/drivers/misc/eeprom/Makefile
index 38d8259..cbbb282 100644
--- a/drivers/misc/eeprom/Makefile
+++ b/drivers/misc/eeprom/Makefile
@@ -4,3 +4,4 @@ obj-$(CONFIG_EEPROM_LEGACY) += eeprom.o
obj-$(CONFIG_EEPROM_MAX6875) += max6875.o
obj-$(CONFIG_EEPROM_93CX6) += eeprom_93cx6.o
obj-$(CONFIG_EEPROM_GPIO_93XX46) += gpio-93xx46.o
+obj-$(CONFIG_DIGSY_MTC_CFG_EEPROM) += digsy_mtc_eeprom.o
diff --git a/drivers/misc/eeprom/digsy_mtc_eeprom.c b/drivers/misc/eeprom/digsy_mtc_eeprom.c
new file mode 100644
index 0000000..301d4bb
--- /dev/null
+++ b/drivers/misc/eeprom/digsy_mtc_eeprom.c
@@ -0,0 +1,75 @@
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/gpio.h>
+#include <linux/gpio-93xx46.h>
+#include <linux/platform_device.h>
+
+#define GPIO_EEPROM_OE 255
+
+static void digsy_mtc_op_prepare(void *p)
+{
+ /* enable */
+ gpio_set_value(GPIO_EEPROM_OE, 0);
+}
+
+static void digsy_mtc_op_finish(void *p)
+{
+ /* disable */
+ gpio_set_value(GPIO_EEPROM_OE, 1);
+}
+
+static const struct gpio_93xx46_platform_data digsy_mtc_93xx46_gpio = {
+ .clk = 216,
+ .cs = 210,
+ .din = 217,
+ .dout = 249,
+ .flags = EE_ADDR8 | EE_CS_LOW,
+ .prepare = digsy_mtc_op_prepare,
+ .finish = digsy_mtc_op_finish,
+};
+
+struct platform_device *digsy_mtc_93xx46_device;
+
+static int __init digsy_mtc_93xx46_init(void)
+{
+ struct platform_device *pdev;
+ int ret;
+
+ pdev = platform_device_alloc("gpio-93xx46", 0);
+ if (!pdev)
+ return -ENOMEM;
+
+ ret = platform_device_add_data(pdev, &digsy_mtc_93xx46_gpio,
+ sizeof(digsy_mtc_93xx46_gpio));
+ if (ret)
+ goto err;
+
+ ret = gpio_request_one(GPIO_EEPROM_OE, GPIOF_OUT_INIT_HIGH,
+ "93xx46 EEPROM OE");
+ if (ret)
+ goto err;
+
+ ret = platform_device_add(pdev);
+ if (ret)
+ goto err;
+
+ digsy_mtc_93xx46_device = pdev;
+ return 0;
+
+err:
+ platform_device_put(pdev);
+ return ret;
+}
+module_init(digsy_mtc_93xx46_init);
+
+static void __exit digsy_mtc_93xx46_exit(void)
+{
+ platform_device_unregister(digsy_mtc_93xx46_device);
+ digsy_mtc_93xx46_device = NULL;
+ gpio_free(GPIO_EEPROM_OE);
+}
+module_exit(digsy_mtc_93xx46_exit);
+
+MODULE_DESCRIPTION("DigsyMTC 93xx46 EEPROM device");
+MODULE_AUTHOR("Anatolij Gustschin <[email protected]>");
+MODULE_LICENSE("GPL");
--
1.7.1

2011-05-25 09:28:53

by Jonathan Cameron

[permalink] [raw]
Subject: Re: [PATCH 1/2] misc/eeprom: add driver for 93xx46 EEPROMs over GPIO

On 05/24/11 17:02, Anatolij Gustschin wrote:
> 93xx46 EEPROMs can be connected using GPIO lines. Add a generic
> 93xx46 EEPROM driver using common GPIO API for such configurations.
> A platform is supposed to register appropriate 93xx46 gpio device
> providing GPIO interface description and using this driver
> read/write/erase access to the EEPROM chip can be easily done
> over sysfs files.
Could you explain why this makes more sense than an spi driver and
use of spi_gpio ?

It's microwire compatible according to random google provided datasheet,
which iirc is a particular form of spi (half duplex, spi mode 0 according
to wikipedia)

That would give us a more generally useful driver.
>
> Signed-off-by: Anatolij Gustschin <[email protected]>
> ---
> drivers/misc/eeprom/Kconfig | 10 +
> drivers/misc/eeprom/Makefile | 1 +
> drivers/misc/eeprom/gpio-93xx46.c | 525 +++++++++++++++++++++++++++++++++++++
> include/linux/gpio-93xx46.h | 19 ++
> 4 files changed, 555 insertions(+), 0 deletions(-)
> create mode 100644 drivers/misc/eeprom/gpio-93xx46.c
> create mode 100644 include/linux/gpio-93xx46.h
>
> diff --git a/drivers/misc/eeprom/Kconfig b/drivers/misc/eeprom/Kconfig
> index 9118613..fcceffd 100644
> --- a/drivers/misc/eeprom/Kconfig
> +++ b/drivers/misc/eeprom/Kconfig
> @@ -70,4 +70,14 @@ config EEPROM_93CX6
>
> If unsure, say N.
>
> +config EEPROM_GPIO_93XX46
> + tristate "EEPROM 93XX46 over GPIO support"
> + depends on GPIOLIB && SYSFS
> + help
> + Driver for the EEPROM chipsets 93xx46x connected with GPIO.
> + The driver supports both read and write commands and also
> + the command to erase the whole EEPROM.
> +
> + If unsure, say N.
> +
> endmenu
> diff --git a/drivers/misc/eeprom/Makefile b/drivers/misc/eeprom/Makefile
> index df3d68f..38d8259 100644
> --- a/drivers/misc/eeprom/Makefile
> +++ b/drivers/misc/eeprom/Makefile
> @@ -3,3 +3,4 @@ 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
> +obj-$(CONFIG_EEPROM_GPIO_93XX46) += gpio-93xx46.o
> diff --git a/drivers/misc/eeprom/gpio-93xx46.c b/drivers/misc/eeprom/gpio-93xx46.c
> new file mode 100644
> index 0000000..5c7d7dd
> --- /dev/null
> +++ b/drivers/misc/eeprom/gpio-93xx46.c
> @@ -0,0 +1,525 @@
> +/*
> + * Driver for 93xx46 EEPROMs over GPIO lines.
> + *
> + * (C) 2011 DENX Software Engineering, Anatolij Gustschin <[email protected]>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + */
> +
> +#include <linux/module.h>
> +#include <linux/init.h>
> +#include <linux/fs.h>
> +#include <linux/gpio.h>
> +#include <linux/gpio-93xx46.h>
> +#include <linux/delay.h>
> +#include <linux/mutex.h>
> +#include <linux/platform_device.h>
> +#include <linux/slab.h>
> +#include <linux/sysctl.h>
> +
> +#define OP_START 0x4
> +#define OP_WRITE (OP_START | 0x1)
> +#define OP_READ (OP_START | 0x2)
> +#define OP_ERASE (OP_START | 0x3)
> +#define OP_EWEN (OP_START | 0x0)
> +#define OP_EWDS (OP_START | 0x0)
> +#define ADDR_EWDS 0x00
> +#define ADDR_ERAL 0x20
> +#define ADDR_EWEN 0x30
> +#define DELAY 450
> +
> +struct gpio_93xx46_dev {
> + struct device *dev;
> + struct gpio_93xx46_platform_data *pdata;
> + struct bin_attribute bin;
> + int addrlen;
> +
> + struct gpio pins[4];
> +};
> +
> +static DEFINE_MUTEX(gpio_93xx46_mutex);
> +
> +static int gpio_93xx46_request_gpios(struct gpio_93xx46_dev *edev)
> +{
> + struct gpio_93xx46_platform_data *pd = edev->pdata;
> + const char *name = to_platform_device(edev->dev)->name;
> + int ret;
> +
> + edev->pins[0].gpio = pd->clk;
> + edev->pins[0].flags = GPIOF_OUT_INIT_LOW;
> + edev->pins[0].label = name;
> + edev->pins[1].gpio = pd->cs;
> + edev->pins[1].flags = GPIOF_OUT_INIT_LOW;
> + edev->pins[1].label = name;
> + edev->pins[2].gpio = pd->din;
> + edev->pins[2].flags = GPIOF_OUT_INIT_LOW;
> + edev->pins[2].label = name;
> + edev->pins[3].gpio = pd->dout;
> + edev->pins[3].flags = GPIOF_IN;
> + edev->pins[3].label = name;
> +
> + ret = gpio_request_array(edev->pins, ARRAY_SIZE(edev->pins));
> + if (ret)
> + return ret;
> +
> + return 0;
> +}
> +
> +static void gpio_93xx46_tx_bit(struct gpio_93xx46_dev *edev, bool bit)
> +{
> + struct gpio_93xx46_platform_data *pd = edev->pdata;
> +
> + if (bit)
> + gpio_set_value(pd->din, 1);
> + else
> + gpio_set_value(pd->din, 0);
> +
> + ndelay(DELAY);
> + gpio_set_value(pd->clk, 1);
> + ndelay(DELAY);
> + gpio_set_value(pd->clk, 0);
> +}
> +
> +static inline unsigned char
> +gpio_93xx46_rx_byte(struct gpio_93xx46_platform_data *pd)
> +{
> + int data = 0, i;
> +
> + for (i = 0; i < 8 ; i++) {
> + gpio_set_value(pd->clk, 1);
> + ndelay(DELAY);
> + gpio_set_value(pd->clk, 0);
> +
> + if (gpio_get_value(pd->dout))
> + data |= 1;
> + data <<= 1;
> + ndelay(DELAY);
> + }
> + return data >>= 1;
> +}
> +
> +static void gpio_93xx46_read(struct gpio_93xx46_dev *edev,
> + char *buf, unsigned offs, size_t cnt)
> +{
> + struct gpio_93xx46_platform_data *pd = edev->pdata;
> + int active = !(pd->flags & EE_CS_LOW);
> + int cmd_addr, len, mask;
> + int i;
> +
> + cmd_addr = (OP_READ << edev->addrlen);
> + if (edev->addrlen == 7) {
> + cmd_addr |= (offs & 0x7f);
> + len = 10;
> + } else {
> + cmd_addr |= (offs & 0x3f);
> + len = 9;
> + }
> + mask = 1 << (len - 1);
> +
> + mutex_lock(&gpio_93xx46_mutex);
> +
> + gpio_set_value(pd->cs, !active);
> + gpio_set_value(pd->clk, 0);
> + gpio_set_value(pd->din, 1);
> + ndelay(DELAY);
> +
> + if (pd->prepare)
> + pd->prepare(edev);
> +
> + gpio_set_value(pd->cs, active);
> + ndelay(DELAY);
> +
> + for (i = 0; i < len; i++, mask >>= 1)
> + gpio_93xx46_tx_bit(edev, !!(cmd_addr & mask));
> +
> + ndelay(DELAY);
> +
> + for (i = 0; i < cnt; i++)
> + buf[i] = gpio_93xx46_rx_byte(pd);
> +
> + gpio_set_value(pd->clk, 0);
> + gpio_set_value(pd->din, 0);
> + gpio_set_value(pd->cs, !active);
> + ndelay(DELAY);
> +
> + if (pd->finish)
> + pd->finish(edev);
> +
> + mutex_unlock(&gpio_93xx46_mutex);
> +}
> +
> +static void gpio_93xx46_ewen(struct gpio_93xx46_dev *edev)
> +{
> + struct gpio_93xx46_platform_data *pd = edev->pdata;
> + int active = !(pd->flags & EE_CS_LOW);
> + int cmd_addr, len, mask;
> + int i;
> +
> + cmd_addr = OP_EWEN << edev->addrlen;
> + if (edev->addrlen == 7) {
> + cmd_addr |= ADDR_EWEN << 1;
> + len = 10;
> + } else {
> + cmd_addr |= ADDR_EWEN;
> + len = 9;
> + }
> + mask = 1 << (len - 1);
> +
> + mutex_lock(&gpio_93xx46_mutex);
> +
> + gpio_set_value(pd->cs, !active);
> + gpio_set_value(pd->clk, 0);
> + gpio_set_value(pd->din, 1);
> + ndelay(DELAY);
> +
> + if (pd->prepare)
> + pd->prepare(edev);
> +
> + gpio_set_value(pd->cs, active);
> + ndelay(DELAY);
> +
> + for (i = 0; i < len; i++, mask >>= 1)
> + gpio_93xx46_tx_bit(edev, !!(cmd_addr & mask));
> +
> + ndelay(DELAY);
> +
> + gpio_set_value(pd->clk, 0);
> + gpio_set_value(pd->din, 0);
> + gpio_set_value(pd->cs, !active);
> + ndelay(DELAY);
> +
> + if (pd->finish)
> + pd->finish(edev);
> +
> + mutex_unlock(&gpio_93xx46_mutex);
> +}
> +
> +static void gpio_93xx46_ewds(struct gpio_93xx46_dev *edev)
> +{
> + struct gpio_93xx46_platform_data *pd = edev->pdata;
> + int active = !(pd->flags & EE_CS_LOW);
> + int cmd_addr, len, mask;
> + int i;
> +
> + cmd_addr = OP_EWDS << edev->addrlen;
> + if (edev->addrlen == 7) {
> + cmd_addr |= ADDR_EWDS << 1;
> + len = 10;
> + } else {
> + cmd_addr |= ADDR_EWDS;
> + len = 9;
> + }
> + mask = 1 << (len - 1);
> +
> + mutex_lock(&gpio_93xx46_mutex);
> +
> + gpio_set_value(pd->cs, !active);
> + gpio_set_value(pd->clk, 0);
> + gpio_set_value(pd->din, 1);
> + ndelay(DELAY);
> +
> + if (pd->prepare)
> + pd->prepare(edev);
> +
> + gpio_set_value(pd->cs, active);
> + ndelay(DELAY);
> +
> + for (i = 0; i < len; i++, mask >>= 1)
> + gpio_93xx46_tx_bit(edev, !!(cmd_addr & mask));
> +
> + ndelay(DELAY);
> +
> + gpio_set_value(pd->clk, 0);
> + gpio_set_value(pd->din, 0);
> + gpio_set_value(pd->cs, !active);
> + ndelay(DELAY);
> +
> + if (pd->finish)
> + pd->finish(edev);
> +
> + mutex_unlock(&gpio_93xx46_mutex);
> +}
> +
> +static void gpio_93xx46_eral(struct gpio_93xx46_dev *edev)
> +{
> + struct gpio_93xx46_platform_data *pd = edev->pdata;
> + int active = !(pd->flags & EE_CS_LOW);
> + int cmd_addr, len, mask;
> + int i, to = 10;
> +
> + cmd_addr = OP_START << edev->addrlen;
> + if (edev->addrlen == 7) {
> + cmd_addr |= ADDR_ERAL << 1;
> + len = 10;
> + } else {
> + cmd_addr |= ADDR_ERAL;
> + len = 9;
> + }
> + mask = 1 << (len - 1);
> +
> + mutex_lock(&gpio_93xx46_mutex);
> +
> + gpio_set_value(pd->cs, !active);
> + gpio_set_value(pd->clk, 0);
> + gpio_set_value(pd->din, 1);
> + ndelay(DELAY);
> +
> + if (pd->prepare)
> + pd->prepare(edev);
> +
> + gpio_set_value(pd->cs, active);
> + ndelay(DELAY);
> +
> + for (i = 0; i < len; i++, mask >>= 1)
> + gpio_93xx46_tx_bit(edev, !!(cmd_addr & mask));
> +
> + gpio_set_value(pd->cs, !active);
> + ndelay(DELAY);
> + gpio_set_value(pd->cs, active);
> + ndelay(DELAY);
> +
> + while (!gpio_get_value(pd->dout)) {
> + if (!to--) {
> + dev_err(edev->dev, "erase not ready timeout\n");
> + break;
> + }
> + mdelay(1);
> + }
> +
> + gpio_set_value(pd->clk, 0);
> + gpio_set_value(pd->din, 0);
> + gpio_set_value(pd->cs, !active);
> + ndelay(DELAY);
> +
> + if (pd->finish)
> + pd->finish(edev);
> +
> + mutex_unlock(&gpio_93xx46_mutex);
> +}
> +
> +static void gpio_93xx46_write(struct gpio_93xx46_dev *edev,
> + unsigned offs, char data)
> +{
> + struct gpio_93xx46_platform_data *pd = edev->pdata;
> + int active = !(pd->flags & EE_CS_LOW);
> + int cmd_addr, len, mask;
> + int i, to = 10;
> +
> + cmd_addr = (OP_WRITE << edev->addrlen);
> + if (edev->addrlen == 7) {
> + cmd_addr |= (offs & 0x7f);
> + len = 10;
> + } else {
> + cmd_addr |= (offs & 0x3f);
> + len = 9;
> + }
> + mask = 1 << (len - 1);
> +
> + mutex_lock(&gpio_93xx46_mutex);
> +
> + gpio_set_value(pd->cs, !active);
> + gpio_set_value(pd->clk, 0);
> + gpio_set_value(pd->din, 1);
> + ndelay(DELAY);
> +
> + if (pd->prepare)
> + pd->prepare(edev);
> +
> + gpio_set_value(pd->cs, active);
> + ndelay(DELAY);
> +
> + for (i = 0; i < len; i++, mask >>= 1)
> + gpio_93xx46_tx_bit(edev, !!(cmd_addr & mask));
> +
> + ndelay(DELAY);
> +
> + for (i = 0; i < 8; i++) {
> + gpio_93xx46_tx_bit(edev, !!(data & 0x80));
> + data <<= 1;
> + }
> +
> + gpio_set_value(pd->cs, !active);
> + ndelay(DELAY);
> + gpio_set_value(pd->cs, active);
> + ndelay(DELAY);
> +
> + while (!gpio_get_value(pd->dout)) {
> + if (!to--) {
> + dev_err(edev->dev, "write not ready timeout\n");
> + break;
> + }
> + mdelay(1);
> + }
> +
> + gpio_set_value(pd->clk, 0);
> + gpio_set_value(pd->din, 0);
> + gpio_set_value(pd->cs, !active);
> + ndelay(DELAY);
> +
> + if (pd->finish)
> + pd->finish(edev);
> +
> + mutex_unlock(&gpio_93xx46_mutex);
> +}
> +
> +static ssize_t
> +gpio_93xx46_bin_read(struct file *filp, struct kobject *kobj,
> + struct bin_attribute *bin_attr,
> + char *buf, loff_t off, size_t count)
> +{
> + struct device *dev = container_of(kobj, struct device, kobj);
> + struct gpio_93xx46_dev *edev = dev_get_drvdata(dev);
> +
> + if (unlikely(!count))
> + return count;
> + if (unlikely(off >= edev->bin.size))
> + return 0;
> + if ((off + count) > edev->bin.size)
> + count = edev->bin.size - off;
> +
> + gpio_93xx46_read(edev, buf, off, count);
> + return count;
> +}
> +
> +static ssize_t
> +gpio_93xx46_bin_write(struct file *filp, struct kobject *kobj,
> + struct bin_attribute *bin_attr,
> + char *buf, loff_t off, size_t count)
> +{
> + struct device *dev = container_of(kobj, struct device, kobj);
> + struct gpio_93xx46_dev *edev = dev_get_drvdata(dev);
> + int i;
> +
> + if (unlikely(!count))
> + return count;
> + if (unlikely(off >= edev->bin.size))
> + return 0;
> + if ((off + count) > edev->bin.size)
> + count = edev->bin.size - off;
> +
> + gpio_93xx46_ewen(edev);
> +
> + for (i = 0; i < count; i++)
> + gpio_93xx46_write(edev, off + i, buf[i]);
> +
> + gpio_93xx46_ewds(edev);
> +
> + return count;
> +}
> +
> +ssize_t gpio_93xx46_store_erase(struct device *dev,
> + struct device_attribute *attr,
> + const char *buf, size_t count)
> +{
> + struct gpio_93xx46_dev *edev = dev_get_drvdata(dev);
> + int erase = 0;
> +
> + sscanf(buf, "%d", &erase);
> + if (erase) {
> + gpio_93xx46_ewen(edev);
> + gpio_93xx46_eral(edev);
> + gpio_93xx46_ewds(edev);
> + }
> +
> + return count;
> +}
> +static DEVICE_ATTR(erase, S_IWUSR, NULL, gpio_93xx46_store_erase);
> +
> +static int __devinit gpio_93xx46_probe(struct platform_device *pdev)
> +{
> + struct gpio_93xx46_platform_data *pd = pdev->dev.platform_data;
> + struct gpio_93xx46_dev *edev = NULL;
> + int err;
> +
> + if (!pd)
> + return -ENODEV;
> +
> + edev = kzalloc(sizeof(*edev), GFP_KERNEL);
> + if (!edev)
> + return -ENOMEM;
> +
> + edev->dev = &pdev->dev;
> + edev->pdata = pd;
> + if (pd->flags & EE_ADDR8)
> + edev->addrlen = 7;
> + else if (pd->flags & EE_ADDR16)
> + edev->addrlen = 6;
> + else {
> + dev_err(&pdev->dev,
> + "invalid address flags 0x%x\n", pd->flags);
> + err = -EINVAL;
> + goto fail;
> + }
> +
> + err = gpio_93xx46_request_gpios(edev);
> + if (err)
> + goto fail;
> +
> + sysfs_bin_attr_init(&edev->bin);
> + edev->bin.attr.name = "eeprom";
> + edev->bin.attr.mode = S_IRUSR;
> + edev->bin.read = gpio_93xx46_bin_read;
> + edev->bin.size = 128;
> + if (!(pd->flags & EE_READONLY)) {
> + edev->bin.write = gpio_93xx46_bin_write;
> + edev->bin.attr.mode |= S_IWUSR;
> + }
> +
> + err = sysfs_create_bin_file(&pdev->dev.kobj, &edev->bin);
> + if (err)
> + goto fail;
> +
> + if (!(pd->flags & EE_READONLY)) {
> + if (device_create_file(&pdev->dev, &dev_attr_erase))
> + dev_err(&pdev->dev, "can't create erase interface\n");
> + }
> +
> + platform_set_drvdata(pdev, edev);
> +
> + return 0;
> +fail:
> + kfree(edev);
> + return err;
> +}
> +
> +static int __devexit gpio_93xx46_remove(struct platform_device *pdev)
> +{
> + struct gpio_93xx46_platform_data *pd = pdev->dev.platform_data;
> + struct gpio_93xx46_dev *edev = platform_get_drvdata(pdev);
> +
> + if (!(pd->flags & EE_READONLY))
> + device_remove_file(&pdev->dev, &dev_attr_erase);
> + sysfs_remove_bin_file(&pdev->dev.kobj, &edev->bin);
> + gpio_free_array(edev->pins, ARRAY_SIZE(edev->pins));
> + platform_set_drvdata(pdev, NULL);
> + kfree(edev);
> + return 0;
> +}
> +
> +static struct platform_driver gpio_93xx46_driver = {
> + .probe = gpio_93xx46_probe,
> + .remove = __devexit_p(gpio_93xx46_remove),
> + .driver = {
> + .name = "gpio-93xx46",
> + .owner = THIS_MODULE,
> + }
> +};
> +
> +static int __init gpio_93xx46_init(void)
> +{
> + return platform_driver_register(&gpio_93xx46_driver);
> +}
> +module_init(gpio_93xx46_init);
> +
> +static void __exit gpio_93xx46_exit(void)
> +{
> + platform_driver_unregister(&gpio_93xx46_driver);
> +}
> +module_exit(gpio_93xx46_exit);
> +
> +MODULE_LICENSE("GPL");
> +MODULE_DESCRIPTION("Driver for 93xx46 EEPROMs over GPIO");
> +MODULE_AUTHOR("Anatolij Gustschin <[email protected]>");
> +MODULE_ALIAS("platform:gpio-93xx46");
> diff --git a/include/linux/gpio-93xx46.h b/include/linux/gpio-93xx46.h
> new file mode 100644
> index 0000000..a565de6
> --- /dev/null
> +++ b/include/linux/gpio-93xx46.h
> @@ -0,0 +1,19 @@
> +/*
> + * Module: gpio-93xx46
> + * Interface description for 93xx46 EEPROMs connected over GPIO.
> + */
> +struct gpio_93xx46_platform_data {
> + unsigned clk;
> + unsigned cs;
> + unsigned din;
> + unsigned dout;
> +
> + u8 flags;
> +#define EE_ADDR8 0x01 /* 8 bit addr. cfg */
> +#define EE_ADDR16 0x02 /* 16 bit addr. cfg */
> +#define EE_READONLY 0x08 /* forbid writing */
> +#define EE_CS_LOW 0x10 /* CS is active low */
> +
> + void (*prepare)(void *);
> + void (*finish)(void *);
> +};

2011-05-25 13:35:39

by Anatolij Gustschin

[permalink] [raw]
Subject: Re: [PATCH 1/2] misc/eeprom: add driver for 93xx46 EEPROMs over GPIO

On Wed, 25 May 2011 10:31:46 +0100
Jonathan Cameron <[email protected]> wrote:

> On 05/24/11 17:02, Anatolij Gustschin wrote:
> > 93xx46 EEPROMs can be connected using GPIO lines. Add a generic
> > 93xx46 EEPROM driver using common GPIO API for such configurations.
> > A platform is supposed to register appropriate 93xx46 gpio device
> > providing GPIO interface description and using this driver
> > read/write/erase access to the EEPROM chip can be easily done
> > over sysfs files.
> Could you explain why this makes more sense than an spi driver and
> use of spi_gpio ?
>
> It's microwire compatible according to random google provided datasheet,
> which iirc is a particular form of spi (half duplex, spi mode 0 according
> to wikipedia)
>
> That would give us a more generally useful driver.

I thought about using spi_gpio first, then I decided to
do it in an independent driver since on the hardware the
driver was written for we additionally need to control
logic to hold pixel link chips in reset when eeprom access
is performed. Putting appropriate hacks to spi_gpio driver
didn't seem to be right approach. Controlling this logic
from user space is error-prone, too.

Anatolij

2011-05-25 14:04:38

by Jonathan Cameron

[permalink] [raw]
Subject: Re: [PATCH 1/2] misc/eeprom: add driver for 93xx46 EEPROMs over GPIO

On 05/25/11 14:35, Anatolij Gustschin wrote:
> On Wed, 25 May 2011 10:31:46 +0100
> Jonathan Cameron <[email protected]> wrote:
>
>> On 05/24/11 17:02, Anatolij Gustschin wrote:
>>> 93xx46 EEPROMs can be connected using GPIO lines. Add a generic
>>> 93xx46 EEPROM driver using common GPIO API for such configurations.
>>> A platform is supposed to register appropriate 93xx46 gpio device
>>> providing GPIO interface description and using this driver
>>> read/write/erase access to the EEPROM chip can be easily done
>>> over sysfs files.
>> Could you explain why this makes more sense than an spi driver and
>> use of spi_gpio ?
>>
>> It's microwire compatible according to random google provided datasheet,
>> which iirc is a particular form of spi (half duplex, spi mode 0 according
>> to wikipedia)
>>
>> That would give us a more generally useful driver.
>
> I thought about using spi_gpio first, then I decided to
> do it in an independent driver since on the hardware the
> driver was written for we additionally need to control
> logic to hold pixel link chips in reset when eeprom access
> is performed. Putting appropriate hacks to spi_gpio driver
> didn't seem to be right approach. Controlling this logic
> from user space is error-prone, too.
Why would you need to put hacks in the spi bus driver?

Surely they would still be in your eeprom driver.
Basically hold the pin down - do spi transfer - raise it again

Might need some callbacks to platform data (From the eeprom driver)
to deal with this case...

2011-06-06 08:00:54

by Anatolij Gustschin

[permalink] [raw]
Subject: Re: [PATCH 1/2] misc/eeprom: add driver for 93xx46 EEPROMs over GPIO

On Wed, 25 May 2011 10:31:46 +0100
Jonathan Cameron <[email protected]> wrote:

> On 05/24/11 17:02, Anatolij Gustschin wrote:
> > 93xx46 EEPROMs can be connected using GPIO lines. Add a generic
> > 93xx46 EEPROM driver using common GPIO API for such configurations.
> > A platform is supposed to register appropriate 93xx46 gpio device
> > providing GPIO interface description and using this driver
> > read/write/erase access to the EEPROM chip can be easily done
> > over sysfs files.
> Could you explain why this makes more sense than an spi driver and
> use of spi_gpio ?
>
> It's microwire compatible according to random google provided datasheet,
> which iirc is a particular form of spi (half duplex, spi mode 0 according
> to wikipedia)
>
> That would give us a more generally useful driver.

Reworked the driver to be an spi driver using spi_gpio so
it is more generally useful driver now. spi_gpio driver
needs some fixes, though. I'll submit the new driver soon.