From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from [2002:4e20:1eda::1] (helo=caramon.arm.linux.org.uk) by bombadil.infradead.org with esmtps (Exim 4.69 #1 (Red Hat Linux)) id 1NLapA-0002dp-DV for linux-mtd@lists.infradead.org; Fri, 18 Dec 2009 11:16:46 +0000 Date: Fri, 18 Dec 2009 11:16:30 +0000 From: Russell King - ARM Linux To: Mike Frysinger Subject: Re: [PATCH] add pismo support Message-ID: <20091218111630.GA32469@n2100.arm.linux.org.uk> References: <20091129145623.GD11445@n2100.arm.linux.org.uk> <8bd0f97a0911290712y298a530fwc6c1ab1c2bcb020b@mail.gmail.com> <20091129152351.GE11445@n2100.arm.linux.org.uk> MIME-Version: 1.0 Content-Type: text/plain; charset=us-ascii Content-Disposition: inline In-Reply-To: <20091129152351.GE11445@n2100.arm.linux.org.uk> Sender: Russell King - ARM Linux Cc: linux-mtd@lists.infradead.org List-Id: Linux MTD discussion mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , 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 > -- > 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 > + . 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 > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > + > +#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 "); > +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