From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from nwd2mail11.analog.com ([137.71.25.57]) by bombadil.infradead.org with esmtp (Exim 4.68 #1 (Red Hat Linux)) id 1KTvcA-0000u6-FM for linux-mtd@lists.infradead.org; Fri, 15 Aug 2008 09:28:59 +0000 From: Bryan Wu To: dwmw2@infradead.org Subject: [PATCH 1/1] MTD Maps: initial driver for using gpios to assist in addressing of parallel flash as is on the tinyboards Date: Fri, 15 Aug 2008 16:37:01 +0800 Message-Id: <1218789421-9995-1-git-send-email-cooloney@kernel.org> Cc: Bryan Wu , linux-mtd@lists.infradead.org, linux-kernel@vger.kernel.org, Mike Frysinger List-Id: Linux MTD discussion mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , From: Mike Frysinger extend driver to handle arbitrary number of gpios Signed-off-by: Mike Frysinger Signed-off-by: Bryan Wu --- drivers/mtd/maps/Kconfig | 9 + drivers/mtd/maps/Makefile | 1 + drivers/mtd/maps/gpio-addr-flash.c | 419 ++++++++++++++++++++++++++++++++++++ 3 files changed, 429 insertions(+), 0 deletions(-) create mode 100644 drivers/mtd/maps/gpio-addr-flash.c diff --git a/drivers/mtd/maps/Kconfig b/drivers/mtd/maps/Kconfig index 50bb046..76f05ad 100644 --- a/drivers/mtd/maps/Kconfig +++ b/drivers/mtd/maps/Kconfig @@ -520,6 +520,15 @@ config MTD_BFIN_ASYNC If compiled as a module, it will be called bfin-async-flash. +config MTD_GPIO_ADDR + tristate "GPIO-assisted Flash Chip Support" + select MTD_PARTITIONS + help + Map driver which allows flashes to be partially physically addressed + and assisted by GPIOs. + + If compiled as a module, it will be called gpio-addr-flash. + config MTD_UCLINUX tristate "Generic uClinux RAM/ROM filesystem support" depends on MTD_PARTITIONS && MTD_RAM && !MMU diff --git a/drivers/mtd/maps/Makefile b/drivers/mtd/maps/Makefile index 6cda6df..fdfaa5e 100644 --- a/drivers/mtd/maps/Makefile +++ b/drivers/mtd/maps/Makefile @@ -65,3 +65,4 @@ obj-$(CONFIG_MTD_PLATRAM) += plat-ram.o obj-$(CONFIG_MTD_OMAP_NOR) += omap_nor.o obj-$(CONFIG_MTD_INTEL_VR_NOR) += intel_vr_nor.o obj-$(CONFIG_MTD_BFIN_ASYNC) += bfin-async-flash.o +obj-$(CONFIG_MTD_GPIO_ADDR) += gpio-addr-flash.o diff --git a/drivers/mtd/maps/gpio-addr-flash.c b/drivers/mtd/maps/gpio-addr-flash.c new file mode 100644 index 0000000..eae74d4 --- /dev/null +++ b/drivers/mtd/maps/gpio-addr-flash.c @@ -0,0 +1,419 @@ +/* + * drivers/mtd/maps/gpio-addr-flash.c + * + * Handle the case where a flash device is mostly addressed using physical + * line and supplemented by GPIOs. This way you can hook up say a 8meg flash + * to a 2meg memory range and use the GPIOs to select a particular range. + * + * Copyright 2000 Nicolas Pitre + * Copyright 2005-2008 Analog Devices Inc. + * + * Enter bugs at http://blackfin.uclinux.org/ + * + * Licensed under the GPL-2 or later. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#define pr_devinit(fmt, args...) ({ static const __devinitconst char __fmt[] = fmt; printk(__fmt, ## args); }) + +#define DRIVER_NAME "gpio-addr-flash" + +struct async_state { + struct mtd_info *mtd; + struct map_info map; + size_t gpio_count; + unsigned *gpio_addrs; + unsigned long win_size; +}; + +static void gf_set_gpios(struct async_state *state, unsigned long ofs) +{ + size_t i; + for (i = 0; i < state->gpio_count; ++i) + gpio_set_value(state->gpio_addrs[i], !!((ofs / state->win_size) & (1 << i))); +} + +static map_word gf_read(struct map_info *map, unsigned long ofs) +{ + struct async_state *state = (struct async_state *)map->map_priv_1; + u16 word; + map_word test; + + gf_set_gpios(state, ofs); + + word = readw(map->virt + (ofs % state->win_size)); + test.x[0] = word; + return test; +} + +static void gf_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len) +{ + struct async_state *state = (struct async_state *)map->map_priv_1; + + gf_set_gpios(state, from); + + /* BUG if operation crosss the win_size */ + BUG_ON(!((from + len) % state->win_size <= (from + len))); + + /* operation does not cross the win_size, so one shot it */ + memcpy_fromio(to, map->virt + (from % state->win_size), len); +} + +static void gf_write(struct map_info *map, map_word d1, unsigned long ofs) +{ + struct async_state *state = (struct async_state *)map->map_priv_1; + u16 d; + + gf_set_gpios(state, ofs); + + d = d1.x[0]; + writew(d, map->virt + (ofs % state->win_size)); +} + +static void gf_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len) +{ + struct async_state *state = (struct async_state *)map->map_priv_1; + + gf_set_gpios(state, to); + + /* BUG if operation crosss the win_size */ + BUG_ON(!((to + len) % state->win_size <= (to + len))); + + /* operation does not cross the win_size, so one shot it */ + memcpy_toio(map->virt + (to % state->win_size), from, len); +} + +#ifdef CONFIG_MTD_PARTITIONS +static const char *part_probe_types[] = { "cmdlinepart", "RedBoot", NULL }; +#endif + +static int __devinit gpio_flash_probe(struct platform_device *pdev) +{ + int ret; + size_t i; + struct physmap_flash_data *pdata = pdev->dev.platform_data; + struct resource *memory = platform_get_resource(pdev, IORESOURCE_MEM, 0); + struct resource *gpios = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + struct async_state *state; + + state = kzalloc(sizeof(*state) + sizeof(unsigned) * gpios->end, GFP_KERNEL); + if (!state) + return -ENOMEM; + + state->gpio_count = gpios->end; + state->gpio_addrs = (void *)(state + 1); + state->win_size = memory->end - memory->start + 1; + memcpy(state->gpio_addrs, (void *)gpios->start, sizeof(unsigned) * state->gpio_count); + + state->map.name = DRIVER_NAME; + state->map.read = gf_read; + state->map.copy_from = gf_copy_from; + state->map.write = gf_write; + state->map.copy_to = gf_copy_to; + state->map.bankwidth = pdata->width; + state->map.size = state->win_size * (1 << state->gpio_count); + state->map.virt = (void __iomem *)memory->start; + state->map.phys = memory->start; + state->map.map_priv_1 = (unsigned long)state; + + platform_set_drvdata(pdev, state); + + for (i = 0; i < state->gpio_count; ++i) { + if (gpio_request(state->gpio_addrs[i], DRIVER_NAME)) { + pr_devinit(KERN_ERR DRIVER_NAME ": Failed to request gpio %d\n", state->gpio_addrs[i]); + while (i--) + gpio_free(state->gpio_addrs[i]); + kfree(state); + return -EBUSY; + } + gpio_direction_output(state->gpio_addrs[i], 0); + } + + pr_devinit(KERN_NOTICE DRIVER_NAME ": probing %d-bit flash bus\n", state->map.bankwidth * 8); + state->mtd = do_map_probe(memory->name, &state->map); + if (!state->mtd) { + for (i = 0; i < state->gpio_count; ++i) + gpio_free(state->gpio_addrs[i]); + kfree(state); + return -ENXIO; + } + +#ifdef CONFIG_MTD_PARTITIONS + ret = parse_mtd_partitions(state->mtd, part_probe_types, &pdata->parts, 0); + if (ret > 0) { + pr_devinit(KERN_NOTICE DRIVER_NAME ": Using commandline partition definition\n"); + add_mtd_partitions(state->mtd, pdata->parts, ret); + + } else if (pdata->nr_parts) { + pr_devinit(KERN_NOTICE DRIVER_NAME ": Using board partition definition\n"); + add_mtd_partitions(state->mtd, pdata->parts, pdata->nr_parts); + + } else +#endif + { + pr_devinit(KERN_NOTICE DRIVER_NAME ": no partition info available, registering whole flash at once\n"); + add_mtd_device(state->mtd); + } + + return 0; +} + +static int __devexit gpio_flash_remove(struct platform_device *pdev) +{ + struct async_state *state = platform_get_drvdata(pdev); + size_t i; + for (i = 0; i < state->gpio_count; ++i) + gpio_free(state->gpio_addrs[i]); +#ifdef CONFIG_MTD_PARTITIONS + del_mtd_partitions(state->mtd); +#endif + map_destroy(state->mtd); + kfree(state); + return 0; +} + +static struct platform_driver gpio_flash_driver = { + .probe = gpio_flash_probe, + .remove = __devexit_p(gpio_flash_remove), + .driver = { + .name = DRIVER_NAME, + }, +}; + +static int __init gpio_flash_init(void) +{ + return platform_driver_register(&gpio_flash_driver); +} +module_init(gpio_flash_init); + +static void __exit gpio_flash_exit(void) +{ + platform_driver_unregister(&gpio_flash_driver); +} +module_exit(gpio_flash_exit); + +MODULE_AUTHOR("Mike Frysinger "); +MODULE_DESCRIPTION("MTD map driver for flashes addressed physically and with gpios"); +MODULE_LICENSE("GPL"); +/* + * drivers/mtd/maps/gpio-addr-flash.c + * + * Handle the case where a flash device is mostly addressed using physical + * line and supplemented by GPIOs. This way you can hook up say a 8meg flash + * to a 2meg memory range and use the GPIOs to select a particular range. + * + * Copyright 2000 Nicolas Pitre + * Copyright 2005-2008 Analog Devices Inc. + * + * Enter bugs at http://blackfin.uclinux.org/ + * + * Licensed under the GPL-2 or later. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#define stamp(fmt, args...) pr_debug("%s:%i: " fmt "\n", __func__, __LINE__, ## args) +#define stampit() stamp("here i am") +#define pr_devinit(fmt, args...) ({ static const __devinitconst char __fmt[] = fmt; printk(__fmt, ## args); }) + +#define DRIVER_NAME "gpio-addr-flash" + +struct async_state { + struct mtd_info *mtd; + struct map_info map; + unsigned gpio_addr; + unsigned long boundary; +}; + +static map_word gf_read(struct map_info *map, unsigned long ofs) +{ + struct async_state *state = (struct async_state *)map->map_priv_1; + u16 word; + map_word test; + + stamp("gpio: %i, ofs = %lx", !!(ofs >= state->boundary), ofs); + + gpio_set_value(state->gpio_addr, !!(ofs >= state->boundary)); + + word = readw(map->virt + (ofs % state->boundary)); + + test.x[0] = word; + return test; +} + +static void gf_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len) +{ + struct async_state *state = (struct async_state *)map->map_priv_1; + + stamp("gpio: %i, to = %p, from = %lx, len = %lx", !!(from >= state->boundary), to, from, len); + + gpio_set_value(state->gpio_addr, !!(from >= state->boundary)); + + if ((from + len) % state->boundary <= (from + len)) { + /* operation does not cross the boundary, so one shot it */ + memcpy(to, map->virt + (from % state->boundary), len); + } else { + /* operation does cross the boundary, so break it up */ + stamp("two shot"); + memcpy(to, map->virt + from, state->boundary - from); + gpio_set_value(state->gpio_addr, 1); + memcpy(to, map->virt + state->boundary, (from + len) % state->boundary); + } +} + +static void gf_write(struct map_info *map, map_word d1, unsigned long ofs) +{ + struct async_state *state = (struct async_state *)map->map_priv_1; + u16 d; + + stamp("gpio: %i, ofs = %lx", !!(ofs >= state->boundary), ofs); + + d = d1.x[0]; + + gpio_set_value(state->gpio_addr, !!(ofs >= state->boundary)); + + writew(d, map->virt + (ofs % state->boundary)); +} + +static void gf_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len) +{ + struct async_state *state = (struct async_state *)map->map_priv_1; + + stamp("gpio: %i, to = %lx, from = %p, len = %lx", !!(to >= state->boundary), to, from, len); + + gpio_set_value(state->gpio_addr, !!(to >= state->boundary)); + + if ((to + len) % state->boundary <= (to + len)) { + /* operation does not cross the boundary, so one shot it */ + memcpy(map->virt + (to % state->boundary), from, len); + } else { + /* operation does cross the boundary, so break it up */ + stamp("two shot"); + memcpy(map->virt + to, from, state->boundary - to); + gpio_set_value(state->gpio_addr, 1); + memcpy(map->virt + state->boundary, from, (to + len) % state->boundary); + } +} + +#ifdef CONFIG_MTD_PARTITIONS +static const char *part_probe_types[] = { "cmdlinepart", "RedBoot", NULL }; +#endif + +static int __devinit gpio_flash_probe(struct platform_device *pdev) +{ + int ret; + struct physmap_flash_data *pdata = pdev->dev.platform_data; + struct resource *memory = platform_get_resource(pdev, IORESOURCE_MEM, 0); + struct async_state *state; + + state = kzalloc(sizeof(*state), GFP_KERNEL); + if (!state) + return -ENOMEM; + platform_set_drvdata(pdev, state); + + state->map.name = DRIVER_NAME; + state->map.read = gf_read; + state->map.copy_from = gf_copy_from; + state->map.write = gf_write; + state->map.copy_to = gf_copy_to; + state->map.bankwidth = pdata->width; + state->map.size = (memory->end - memory->start + 1) * 2; + state->map.virt = (void __iomem *)memory->start; + state->map.phys = memory->start; + state->map.map_priv_1 = (unsigned long)state; + state->gpio_addr = platform_get_irq(pdev, 0); + state->boundary = memory->end - memory->start + 1; + + if (gpio_request(state->gpio_addr, DRIVER_NAME)) { + pr_devinit(KERN_ERR DRIVER_NAME ": Failed to request gpio %d\n", state->gpio_addr); + kfree(state); + return -EBUSY; + } + gpio_direction_output(state->gpio_addr, 0); + + pr_devinit(KERN_NOTICE DRIVER_NAME ": probing %d-bit flash bus\n", state->map.bankwidth * 8); + state->mtd = do_map_probe(memory->name, &state->map); + if (!state->mtd) { + gpio_free(state->gpio_addr); + kfree(state); + return -ENXIO; + } + +#ifdef CONFIG_MTD_PARTITIONS + ret = parse_mtd_partitions(state->mtd, part_probe_types, &pdata->parts, 0); + if (ret > 0) { + pr_devinit(KERN_NOTICE DRIVER_NAME ": Using commandline partition definition\n"); + add_mtd_partitions(state->mtd, pdata->parts, ret); + + } else if (pdata->nr_parts) { + pr_devinit(KERN_NOTICE DRIVER_NAME ": Using board partition definition\n"); + add_mtd_partitions(state->mtd, pdata->parts, pdata->nr_parts); + + } else +#endif + { + pr_devinit(KERN_NOTICE DRIVER_NAME ": no partition info available, registering whole flash at once\n"); + add_mtd_device(state->mtd); + } + + return 0; +} + +static int __devexit gpio_flash_remove(struct platform_device *pdev) +{ + struct async_state *state = platform_get_drvdata(pdev); + gpio_free(state->gpio_addr); +#ifdef CONFIG_MTD_PARTITIONS + del_mtd_partitions(state->mtd); +#endif + map_destroy(state->mtd); + kfree(state); + return 0; +} + +static struct platform_driver gpio_flash_driver = { + .probe = gpio_flash_probe, + .remove = __devexit_p(gpio_flash_remove), + .driver = { + .name = DRIVER_NAME, + }, +}; + +static int __init gpio_flash_init(void) +{ + return platform_driver_register(&gpio_flash_driver); +} +module_init(gpio_flash_init); + +static void __exit gpio_flash_exit(void) +{ + platform_driver_unregister(&gpio_flash_driver); +} +module_exit(gpio_flash_exit); + +MODULE_AUTHOR("Mike Frysinger "); +MODULE_DESCRIPTION("MTD map driver for flashes addressed physically and with gpios"); +MODULE_LICENSE("GPL"); -- 1.5.6