| Submitter | Russell King - ARM Linux |
|---|---|
| Date | Nov. 29, 2009, 3:23 p.m. |
| Message ID | <20091129152351.GE11445@n2100.arm.linux.org.uk> |
| Download | mbox | patch |
| Permalink | /patch/39731/ |
| State | New |
| Headers | show |
Comments
Ping. On Sun, Nov 29, 2009 at 03:23:51PM +0000, Russell King - ARM Linux wrote: > The following patch adds support for PISMO modules found on ARM Ltd > development platforms. These are MTD modules, and can have a > selection of SRAM, flash or DOC devices as described by an on-board > I2C EEPROM. > > We support SRAM and NOR flash devices only by registering appropriate > conventional MTD platform devices as children of the 'pismo' device. > > Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk> > -- > drivers/mtd/maps/Kconfig | 17 ++ > drivers/mtd/maps/Makefile | 1 > drivers/mtd/maps/pismo.c | 320 ++++++++++++++++++++++++++++++++++++++++++++++ > include/linux/mtd/pismo.h | 17 ++ > 4 files changed, 355 insertions(+) > > diff --git a/drivers/mtd/maps/Kconfig b/drivers/mtd/maps/Kconfig > index 0b98654..631b060 100644 > --- a/drivers/mtd/maps/Kconfig > +++ b/drivers/mtd/maps/Kconfig > @@ -557,4 +557,21 @@ config MTD_VMU > To build this as a module select M here, the module will be called > vmu-flash. > > +config MTD_PISMO > + tristate "MTD discovery driver for PISMO modules" > + depends on I2C > + depends on ARCH_VERSATILE > + help > + This driver allows for discovery of PISMO modules - see > + <http://www.pismoworld.org/>. These are small modules containing > + up to five memory devices (eg, SRAM, flash, DOC) described by an > + I2C EEPROM. > + > + This driver does not create any MTD maps itself; instead it > + creates MTD physmap and MTD SRAM platform devices. If you > + enable this option, you should consider enabling MTD_PHYSMAP > + and/or MTD_PLATRAM according to the devices on your module. > + > + When built as a module, it will be called pismo.ko > + > endmenu > diff --git a/drivers/mtd/maps/Makefile b/drivers/mtd/maps/Makefile > index 8bae7f9..cffaaf1 100644 > --- a/drivers/mtd/maps/Makefile > +++ b/drivers/mtd/maps/Makefile > @@ -61,3 +61,4 @@ obj-$(CONFIG_MTD_INTEL_VR_NOR) += intel_vr_nor.o > obj-$(CONFIG_MTD_RBTX4939) += rbtx4939-flash.o > obj-$(CONFIG_MTD_VMU) += vmu-flash.o > obj-$(CONFIG_MTD_GPIO_ADDR) += gpio-addr-flash.o > +obj-$(CONFIG_MTD_PISMO) += pismo.o > --- /dev/null 2009-08-03 10:19:25.737008066 +0100 > +++ b/drivers/mtd/maps/pismo.c 2009-08-03 14:23:32.000000000 +0100 > @@ -0,0 +1,320 @@ > +/* > + * PISMO memory driver - http://www.pismoworld.org/ > + * > + * For ARM Realview and Versatile platforms > + * > + * 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. > + */ > +#include <linux/init.h> > +#include <linux/module.h> > +#include <linux/i2c.h> > +#include <linux/platform_device.h> > +#include <linux/spinlock.h> > +#include <linux/mutex.h> > +#include <linux/mtd/physmap.h> > +#include <linux/mtd/plat-ram.h> > +#include <linux/mtd/pismo.h> > + > +#define PISMO_NUM_CS 5 > + > +struct pismo_cs_block { > + u8 type; > + u8 width; > + __le16 access; > + __le32 size; > + u32 reserved[2]; > + char device[32]; > +} __packed; > + > +struct pismo_eeprom { > + struct pismo_cs_block cs[PISMO_NUM_CS]; > + char board[15]; > + u8 sum; > +} __packed; > + > +struct pismo_mem { > + phys_addr_t base; > + u32 size; > + u16 access; > + u8 width; > + u8 type; > +}; > + > +struct pismo_data { > + struct i2c_client *client; > + void (*vpp)(void *, int); > + void *vpp_data; > + struct platform_device *dev[PISMO_NUM_CS]; > +}; > + > +/* FIXME: set_vpp could do with a better calling convention */ > +static struct pismo_data *vpp_pismo; > +static DEFINE_MUTEX(pismo_mutex); > + > +static int pismo_setvpp_probe_fix(struct pismo_data *pismo) > +{ > + mutex_lock(&pismo_mutex); > + if (vpp_pismo) { > + mutex_unlock(&pismo_mutex); > + kfree(pismo); > + return -EBUSY; > + } > + vpp_pismo = pismo; > + mutex_unlock(&pismo_mutex); > + return 0; > +} > + > +static void pismo_setvpp_remove_fix(struct pismo_data *pismo) > +{ > + mutex_lock(&pismo_mutex); > + if (vpp_pismo == pismo) > + vpp_pismo = NULL; > + mutex_unlock(&pismo_mutex); > +} > + > +static void pismo_set_vpp(struct map_info *map, int on) > +{ > + struct pismo_data *pismo = vpp_pismo; > + > + pismo->vpp(pismo->vpp_data, on); > +} > +/* end of hack */ > + > + > +static unsigned int __devinit pismo_width_to_bytes(unsigned int width) > +{ > + width &= 15; > + if (width > 2) > + return 0; > + return 1 << width; > +} > + > +static int __devinit pismo_eeprom_read(struct i2c_client *client, void *buf, > + u8 addr, size_t size) > +{ > + int ret; > + struct i2c_msg msg[] = { > + { > + .addr = client->addr, > + .len = sizeof(addr), > + .buf = &addr, > + }, { > + .addr = client->addr, > + .flags = I2C_M_RD, > + .len = size, > + .buf = buf, > + }, > + }; > + > + ret = i2c_transfer(client->adapter, msg, ARRAY_SIZE(msg)); > + > + return ret == ARRAY_SIZE(msg) ? size : -EIO; > +} > + > +static int __devinit pismo_add_device(struct pismo_data *pismo, int i, > + struct pismo_mem *region, const char *name, void *pdata, size_t psize) > +{ > + struct platform_device *dev; > + struct resource res = { }; > + phys_addr_t base = region.base; > + int ret; > + > + if (base == ~0) > + return -ENXIO; > + > + res.start = base; > + res.end = base + region->size - 1; > + res.flags = IORESOURCE_MEM; > + > + dev = platform_device_alloc(name, i); > + if (!dev) > + return -ENOMEM; > + dev->dev.parent = &pismo->client->dev; > + > + do { > + ret = platform_device_add_resources(dev, &res, 1); > + if (ret) > + break; > + > + ret = platform_device_add_data(dev, pdata, psize); > + if (ret) > + break; > + > + ret = platform_device_add(dev); > + if (ret) > + break; > + > + pismo->dev[i] = dev; > + return 0; > + } while (0); > + > + platform_device_put(dev); > + return ret; > +} > + > +static int __devinit pismo_add_nor(struct pismo_data *pismo, int i, > + struct pismo_mem *region) > +{ > + struct physmap_flash_data data = { > + .width = region->width, > + }; > + > + if (pismo->vpp) > + data.set_vpp = pismo_set_vpp; > + > + return pismo_add_device(pismo, i, region, "physmap-flash", > + &data, sizeof(data)); > +} > + > +static int __devinit pismo_add_sram(struct pismo_data *pismo, int i, > + struct pismo_mem *region) > +{ > + struct platdata_mtd_ram data = { > + .bankwidth = region->width, > + }; > + > + return pismo_add_device(pismo, i, region, "mtd-ram", > + &data, sizeof(data)); > +} > + > +static void __devinit pismo_add_one(struct pismo_data *pismo, int i, > + const struct pismo_cs_block *cs, phys_addr_t base) > +{ > + struct device *dev = &pismo->client->dev; > + struct pismo_mem region; > + > + region.base = base; > + region.type = cs->type; > + region.width = pismo_width_to_bytes(cs->width); > + region.access = le16_to_cpu(cs->access); > + region.size = le32_to_cpu(cs->size); > + > + if (region.width == 0) { > + dev_err(dev, "cs%u: bad width: %02x, ignoring\n", i, cs->width); > + return; > + } > + > + /* > + * FIXME: may need to the platforms memory controller here, but at > + * the moment we assume that it has already been correctly setup. > + * The memory controller can also tell us the base address as well. > + */ > + > + dev_info(dev, "cs%u: %.32s: type %02x access %u00ps size %uK\n", > + i, cs->device, region.type, region.access, region.size / 1024); > + > + switch (region.type) { > + case 0: > + break; > + case 1: > + /* static DOC */ > + break; > + case 2: > + /* static NOR */ > + pismo_add_nor(pismo, i, ®ion); > + break; > + case 3: > + /* static RAM */ > + pismo_add_sram(pismo, i, ®ion); > + break; > + } > +} > + > +static int __devexit pismo_remove(struct i2c_client *client) > +{ > + struct pismo_data *pismo = i2c_get_clientdata(client); > + int i; > + > + for (i = 0; i < ARRAY_SIZE(pismo->dev); i++) > + platform_device_unregister(pismo->dev[i]); > + > + /* FIXME: set_vpp needs saner arguments */ > + pismo_setvpp_remove_fix(pismo); > + > + kfree(pismo); > + > + return 0; > +} > + > +static int __devinit pismo_probe(struct i2c_client *client, > + const struct i2c_device_id *id) > +{ > + struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); > + struct pismo_pdata *pdata = client->dev.platform_data; > + struct pismo_eeprom eeprom; > + struct pismo_data *pismo; > + int ret, i; > + > + if (!i2c_check_functionality(adapter, I2C_FUNC_I2C)) { > + dev_err(&client->dev, "functionality mismatch\n"); > + return -EIO; > + } > + > + pismo = kzalloc(sizeof(*pismo), GFP_KERNEL); > + if (!pismo) > + return -ENOMEM; > + > + /* FIXME: set_vpp needs saner arguments */ > + ret = pismo_setvpp_probe_fix(pismo); > + if (ret) > + return ret; > + > + pismo->client = client; > + if (pdata) { > + pismo->vpp = pdata->set_vpp; > + pismo->vpp_data = pdata->vpp_data; > + } > + i2c_set_clientdata(client, pismo); > + > + ret = pismo_eeprom_read(client, &eeprom, 0, sizeof(eeprom)); > + if (ret < 0) { > + dev_err(&client->dev, "error reading EEPROM: %d\n", ret); > + return ret; > + } > + > + dev_info(&client->dev, "%.15s board found\n", eeprom.board); > + > + for (i = 0; i < ARRAY_SIZE(eeprom.cs); i++) > + if (eeprom.cs[i].type != 0xff) > + pismo_add_one(pismo, i, &eeprom.cs[i], > + pdata->cs_addrs[i]); > + > + return 0; > +} > + > +static const struct i2c_device_id pismo_id[] = { > + { "pismo" }, > + { }, > +}; > +MODULE_DEVICE_TABLE(i2c, pismo_id); > + > +static struct i2c_driver pismo_driver = { > + .driver = { > + .name = "pismo", > + .owner = THIS_MODULE, > + }, > + .probe = pismo_probe, > + .remove = __devexit_p(pismo_remove), > + .id_table = pismo_id, > +}; > + > +static int __init pismo_init(void) > +{ > + BUILD_BUG_ON(sizeof(struct pismo_cs_block) != 48); > + BUILD_BUG_ON(sizeof(struct pismo_eeprom) != 256); > + > + return i2c_add_driver(&pismo_driver); > +} > +module_init(pismo_init); > + > +static void __exit pismo_exit(void) > +{ > + i2c_del_driver(&pismo_driver); > +} > +module_exit(pismo_exit); > + > +MODULE_AUTHOR("Russell King <linux@arm.linux.org.uk>"); > +MODULE_DESCRIPTION("PISMO memory driver"); > +MODULE_LICENSE("GPL"); > --- /dev/null 2009-08-03 10:19:25.737008066 +0100 > +++ b/include/linux/mtd/pismo.h 2009-07-18 10:32:59.000000000 +0100 > @@ -0,0 +1,17 @@ > +/* > + * PISMO memory driver - http://www.pismoworld.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. > + */ > +#ifndef __LINUX_MTD_PISMO_H > +#define __LINUX_MTD_PISMO_H > + > +struct pismo_pdata { > + void (*set_vpp)(void *, int); > + void *vpp_data; > + phys_addr_t cs_addrs[5]; > +}; > + > +#endif
Patch
diff --git a/drivers/mtd/maps/Kconfig b/drivers/mtd/maps/Kconfig index 0b98654..631b060 100644 --- a/drivers/mtd/maps/Kconfig +++ b/drivers/mtd/maps/Kconfig @@ -557,4 +557,21 @@ config MTD_VMU To build this as a module select M here, the module will be called vmu-flash. +config MTD_PISMO + tristate "MTD discovery driver for PISMO modules" + depends on I2C + depends on ARCH_VERSATILE + help + This driver allows for discovery of PISMO modules - see + <http://www.pismoworld.org/>. These are small modules containing + up to five memory devices (eg, SRAM, flash, DOC) described by an + I2C EEPROM. + + This driver does not create any MTD maps itself; instead it + creates MTD physmap and MTD SRAM platform devices. If you + enable this option, you should consider enabling MTD_PHYSMAP + and/or MTD_PLATRAM according to the devices on your module. + + When built as a module, it will be called pismo.ko + endmenu diff --git a/drivers/mtd/maps/Makefile b/drivers/mtd/maps/Makefile index 8bae7f9..cffaaf1 100644 --- a/drivers/mtd/maps/Makefile +++ b/drivers/mtd/maps/Makefile @@ -61,3 +61,4 @@ obj-$(CONFIG_MTD_INTEL_VR_NOR) += intel_vr_nor.o obj-$(CONFIG_MTD_RBTX4939) += rbtx4939-flash.o obj-$(CONFIG_MTD_VMU) += vmu-flash.o obj-$(CONFIG_MTD_GPIO_ADDR) += gpio-addr-flash.o +obj-$(CONFIG_MTD_PISMO) += pismo.o --- /dev/null 2009-08-03 10:19:25.737008066 +0100 +++ b/drivers/mtd/maps/pismo.c 2009-08-03 14:23:32.000000000 +0100 @@ -0,0 +1,320 @@ +/* + * PISMO memory driver - http://www.pismoworld.org/ + * + * For ARM Realview and Versatile platforms + * + * 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. + */ +#include <linux/init.h> +#include <linux/module.h> +#include <linux/i2c.h> +#include <linux/platform_device.h> +#include <linux/spinlock.h> +#include <linux/mutex.h> +#include <linux/mtd/physmap.h> +#include <linux/mtd/plat-ram.h> +#include <linux/mtd/pismo.h> + +#define PISMO_NUM_CS 5 + +struct pismo_cs_block { + u8 type; + u8 width; + __le16 access; + __le32 size; + u32 reserved[2]; + char device[32]; +} __packed; + +struct pismo_eeprom { + struct pismo_cs_block cs[PISMO_NUM_CS]; + char board[15]; + u8 sum; +} __packed; + +struct pismo_mem { + phys_addr_t base; + u32 size; + u16 access; + u8 width; + u8 type; +}; + +struct pismo_data { + struct i2c_client *client; + void (*vpp)(void *, int); + void *vpp_data; + struct platform_device *dev[PISMO_NUM_CS]; +}; + +/* FIXME: set_vpp could do with a better calling convention */ +static struct pismo_data *vpp_pismo; +static DEFINE_MUTEX(pismo_mutex); + +static int pismo_setvpp_probe_fix(struct pismo_data *pismo) +{ + mutex_lock(&pismo_mutex); + if (vpp_pismo) { + mutex_unlock(&pismo_mutex); + kfree(pismo); + return -EBUSY; + } + vpp_pismo = pismo; + mutex_unlock(&pismo_mutex); + return 0; +} + +static void pismo_setvpp_remove_fix(struct pismo_data *pismo) +{ + mutex_lock(&pismo_mutex); + if (vpp_pismo == pismo) + vpp_pismo = NULL; + mutex_unlock(&pismo_mutex); +} + +static void pismo_set_vpp(struct map_info *map, int on) +{ + struct pismo_data *pismo = vpp_pismo; + + pismo->vpp(pismo->vpp_data, on); +} +/* end of hack */ + + +static unsigned int __devinit pismo_width_to_bytes(unsigned int width) +{ + width &= 15; + if (width > 2) + return 0; + return 1 << width; +} + +static int __devinit pismo_eeprom_read(struct i2c_client *client, void *buf, + u8 addr, size_t size) +{ + int ret; + struct i2c_msg msg[] = { + { + .addr = client->addr, + .len = sizeof(addr), + .buf = &addr, + }, { + .addr = client->addr, + .flags = I2C_M_RD, + .len = size, + .buf = buf, + }, + }; + + ret = i2c_transfer(client->adapter, msg, ARRAY_SIZE(msg)); + + return ret == ARRAY_SIZE(msg) ? size : -EIO; +} + +static int __devinit pismo_add_device(struct pismo_data *pismo, int i, + struct pismo_mem *region, const char *name, void *pdata, size_t psize) +{ + struct platform_device *dev; + struct resource res = { }; + phys_addr_t base = region.base; + int ret; + + if (base == ~0) + return -ENXIO; + + res.start = base; + res.end = base + region->size - 1; + res.flags = IORESOURCE_MEM; + + dev = platform_device_alloc(name, i); + if (!dev) + return -ENOMEM; + dev->dev.parent = &pismo->client->dev; + + do { + ret = platform_device_add_resources(dev, &res, 1); + if (ret) + break; + + ret = platform_device_add_data(dev, pdata, psize); + if (ret) + break; + + ret = platform_device_add(dev); + if (ret) + break; + + pismo->dev[i] = dev; + return 0; + } while (0); + + platform_device_put(dev); + return ret; +} + +static int __devinit pismo_add_nor(struct pismo_data *pismo, int i, + struct pismo_mem *region) +{ + struct physmap_flash_data data = { + .width = region->width, + }; + + if (pismo->vpp) + data.set_vpp = pismo_set_vpp; + + return pismo_add_device(pismo, i, region, "physmap-flash", + &data, sizeof(data)); +} + +static int __devinit pismo_add_sram(struct pismo_data *pismo, int i, + struct pismo_mem *region) +{ + struct platdata_mtd_ram data = { + .bankwidth = region->width, + }; + + return pismo_add_device(pismo, i, region, "mtd-ram", + &data, sizeof(data)); +} + +static void __devinit pismo_add_one(struct pismo_data *pismo, int i, + const struct pismo_cs_block *cs, phys_addr_t base) +{ + struct device *dev = &pismo->client->dev; + struct pismo_mem region; + + region.base = base; + region.type = cs->type; + region.width = pismo_width_to_bytes(cs->width); + region.access = le16_to_cpu(cs->access); + region.size = le32_to_cpu(cs->size); + + if (region.width == 0) { + dev_err(dev, "cs%u: bad width: %02x, ignoring\n", i, cs->width); + return; + } + + /* + * FIXME: may need to the platforms memory controller here, but at + * the moment we assume that it has already been correctly setup. + * The memory controller can also tell us the base address as well. + */ + + dev_info(dev, "cs%u: %.32s: type %02x access %u00ps size %uK\n", + i, cs->device, region.type, region.access, region.size / 1024); + + switch (region.type) { + case 0: + break; + case 1: + /* static DOC */ + break; + case 2: + /* static NOR */ + pismo_add_nor(pismo, i, ®ion); + break; + case 3: + /* static RAM */ + pismo_add_sram(pismo, i, ®ion); + break; + } +} + +static int __devexit pismo_remove(struct i2c_client *client) +{ + struct pismo_data *pismo = i2c_get_clientdata(client); + int i; + + for (i = 0; i < ARRAY_SIZE(pismo->dev); i++) + platform_device_unregister(pismo->dev[i]); + + /* FIXME: set_vpp needs saner arguments */ + pismo_setvpp_remove_fix(pismo); + + kfree(pismo); + + return 0; +} + +static int __devinit pismo_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); + struct pismo_pdata *pdata = client->dev.platform_data; + struct pismo_eeprom eeprom; + struct pismo_data *pismo; + int ret, i; + + if (!i2c_check_functionality(adapter, I2C_FUNC_I2C)) { + dev_err(&client->dev, "functionality mismatch\n"); + return -EIO; + } + + pismo = kzalloc(sizeof(*pismo), GFP_KERNEL); + if (!pismo) + return -ENOMEM; + + /* FIXME: set_vpp needs saner arguments */ + ret = pismo_setvpp_probe_fix(pismo); + if (ret) + return ret; + + pismo->client = client; + if (pdata) { + pismo->vpp = pdata->set_vpp; + pismo->vpp_data = pdata->vpp_data; + } + i2c_set_clientdata(client, pismo); + + ret = pismo_eeprom_read(client, &eeprom, 0, sizeof(eeprom)); + if (ret < 0) { + dev_err(&client->dev, "error reading EEPROM: %d\n", ret); + return ret; + } + + dev_info(&client->dev, "%.15s board found\n", eeprom.board); + + for (i = 0; i < ARRAY_SIZE(eeprom.cs); i++) + if (eeprom.cs[i].type != 0xff) + pismo_add_one(pismo, i, &eeprom.cs[i], + pdata->cs_addrs[i]); + + return 0; +} + +static const struct i2c_device_id pismo_id[] = { + { "pismo" }, + { }, +}; +MODULE_DEVICE_TABLE(i2c, pismo_id); + +static struct i2c_driver pismo_driver = { + .driver = { + .name = "pismo", + .owner = THIS_MODULE, + }, + .probe = pismo_probe, + .remove = __devexit_p(pismo_remove), + .id_table = pismo_id, +}; + +static int __init pismo_init(void) +{ + BUILD_BUG_ON(sizeof(struct pismo_cs_block) != 48); + BUILD_BUG_ON(sizeof(struct pismo_eeprom) != 256); + + return i2c_add_driver(&pismo_driver); +} +module_init(pismo_init); + +static void __exit pismo_exit(void) +{ + i2c_del_driver(&pismo_driver); +} +module_exit(pismo_exit); + +MODULE_AUTHOR("Russell King <linux@arm.linux.org.uk>"); +MODULE_DESCRIPTION("PISMO memory driver"); +MODULE_LICENSE("GPL"); --- /dev/null 2009-08-03 10:19:25.737008066 +0100 +++ b/include/linux/mtd/pismo.h 2009-07-18 10:32:59.000000000 +0100 @@ -0,0 +1,17 @@ +/* + * PISMO memory driver - http://www.pismoworld.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. + */ +#ifndef __LINUX_MTD_PISMO_H +#define __LINUX_MTD_PISMO_H + +struct pismo_pdata { + void (*set_vpp)(void *, int); + void *vpp_data; + phys_addr_t cs_addrs[5]; +}; + +#endif
The following patch adds support for PISMO modules found on ARM Ltd development platforms. These are MTD modules, and can have a selection of SRAM, flash or DOC devices as described by an on-board I2C EEPROM. We support SRAM and NOR flash devices only by registering appropriate conventional MTD platform devices as children of the 'pismo' device. Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk> -- drivers/mtd/maps/Kconfig | 17 ++ drivers/mtd/maps/Makefile | 1 drivers/mtd/maps/pismo.c | 320 ++++++++++++++++++++++++++++++++++++++++++++++ include/linux/mtd/pismo.h | 17 ++ 4 files changed, 355 insertions(+)