From: Oliver Brown Date: Wed, 13 Feb 2013 17:48:35 +0000 (-0600) Subject: ENGR00244769-1 [NOR FLASH]-Improve WEIM NOR speed X-Git-Tag: v3.0.35-fsl~122 X-Git-Url: https://git.karo-electronics.de/?a=commitdiff_plain;h=f1b15874826cbaa7aee196695e7b2fddfbdedea0;p=karo-tx-linux.git ENGR00244769-1 [NOR FLASH]-Improve WEIM NOR speed Increase the NOR flash read speed. Added weimnor driver to use cached (and page mode) reads. Signed-off-by: Oliver Brown --- diff --git a/drivers/mtd/maps/Kconfig b/drivers/mtd/maps/Kconfig index c0c328c5b133..96e6f186bf57 100644 --- a/drivers/mtd/maps/Kconfig +++ b/drivers/mtd/maps/Kconfig @@ -162,6 +162,15 @@ config MTD_PXA2XX help This provides a driver for the NOR flash attached to a PXA2xx chip. +config MTD_IMX6X_WEIMNOR + tristate "CFI Flash device mapped on iMX6x based boards" + depends on MTD_CFI || MTD_JEDECPROBE || MTD_ROM || MTD_LPDDR + help + This provides a driver for the WEIM (Parallel) NOR flash attached to + an iMX6x chip. This driver provides a cached read to take advantage + of paged reads by using memcopy. If you have a board such as the + SabreAI select 'Y' to use the NOR flash chips on it. + config MTD_OCTAGON tristate "JEDEC Flash device mapped on Octagon 5066 SBC" depends on X86 && MTD_JEDEC && MTD_COMPLEX_MAPPINGS diff --git a/drivers/mtd/maps/Makefile b/drivers/mtd/maps/Makefile index cb48b11affff..8f437a0b828d 100644 --- a/drivers/mtd/maps/Makefile +++ b/drivers/mtd/maps/Makefile @@ -18,6 +18,7 @@ obj-$(CONFIG_MTD_ICHXROM) += ichxrom.o obj-$(CONFIG_MTD_CK804XROM) += ck804xrom.o obj-$(CONFIG_MTD_TSUNAMI) += tsunami_flash.o obj-$(CONFIG_MTD_PXA2XX) += pxa2xx-flash.o +obj-$(CONFIG_MTD_IMX6X_WEIMNOR) += imx6x-weimnor.o obj-$(CONFIG_MTD_MBX860) += mbx860.o obj-$(CONFIG_MTD_CEIVA) += ceiva.o obj-$(CONFIG_MTD_OCTAGON) += octagon-5066.o diff --git a/drivers/mtd/maps/imx6x-weimnor.c b/drivers/mtd/maps/imx6x-weimnor.c new file mode 100644 index 000000000000..60a96da32dcf --- /dev/null +++ b/drivers/mtd/maps/imx6x-weimnor.c @@ -0,0 +1,299 @@ +/* + * Copyright (C) 2013 Freescale Semiconductor, Inc. All Rights Reserved. + * + * 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, or + * (at your option) any later version. + + * 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., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define MAX_RESOURCES 4 + +struct imx6x_weimnor_info { + struct mtd_info *mtd[MAX_RESOURCES]; + struct mtd_info *cmtd; + struct map_info map[MAX_RESOURCES]; + int nr_parts; + struct mtd_partition *parts; +}; + +static int imx6x_weimnor_remove(struct platform_device *dev) +{ + struct imx6x_weimnor_info *info; + int i; + + info = platform_get_drvdata(dev); + + if (info == NULL) + return 0; + + platform_set_drvdata(dev, NULL); + + if (info->cmtd) { + mtd_device_unregister(info->cmtd); + if (info->nr_parts) + kfree(info->parts); + if (info->cmtd != info->mtd[0]) + mtd_concat_destroy(info->cmtd); + } + + for (i = 0; i < MAX_RESOURCES; i++) { + if (info->mtd[i] != NULL) + map_destroy(info->mtd[i]); + if (info->map[i].cached) + iounmap(info->map[i].cached); + } + + kfree(info); + + return 0; +} + +static void imx6x_set_vpp(struct map_info *map, int state) +{ + struct platform_device *pdev; + struct physmap_flash_data *flash; + + pdev = (struct platform_device *)map->map_priv_1; + flash = pdev->dev.platform_data; + + if (flash->set_vpp) + flash->set_vpp(pdev, state); +} + +#define CACHELINESIZE 32 +static void imx6x_map_inval_cache(struct map_info *map, unsigned long from, +ssize_t len) +{ + unsigned long start; + unsigned long end; + unsigned long phys_start; + unsigned long phys_end; + + if (from > map->size) { + start = (unsigned long)map->cached + map->size; + phys_start = (unsigned long)map->phys + map->size; + } else { + start = (unsigned long)map->cached + from; + phys_start = (unsigned long)map->phys + from; + } + + if ((from + len) > map->size) { + end = start + map->size; + phys_end = phys_start + map->size; + } else { + end = start + len; + phys_end = phys_start + len; + } + + start &= ~(CACHELINESIZE - 1); + while (start < end) { + /* invalidate D cache line */ + asm volatile ("mcr p15, 0, %0, c7, c6, 1" : : "r" (start)); + start += CACHELINESIZE; + } + outer_inv_range(phys_start, phys_end); +} + +static const char *rom_probe_types[] = { + "cfi_probe", + "jedec_probe", + "qinfo_probe", + "map_rom", + NULL}; + +static const char *part_probe_types[] = { "cmdlinepart", "RedBoot", "afs", + NULL}; + +static int imx6x_weimnor_probe(struct platform_device *dev) +{ + struct physmap_flash_data *flash; + struct imx6x_weimnor_info *info; + const char **probe_type; + int err = 0; + int i; + int devices_found = 0; + + flash = dev->dev.platform_data; + if (flash == NULL) + return -ENODEV; + + info = devm_kzalloc(&dev->dev, sizeof(struct imx6x_weimnor_info), + GFP_KERNEL); + if (info == NULL) { + err = -ENOMEM; + goto err_out; + } + + if (flash->init) { + err = flash->init(dev); + if (err) + goto err_out; + } + + platform_set_drvdata(dev, info); + + for (i = 0; i < dev->num_resources; i++) { + printk(KERN_NOTICE + "imx6x-flash (physmap) platform " + "flash device: %.8llx at %.8llx\n", + (unsigned long long)resource_size(&dev->resource[i]), + (unsigned long long)dev->resource[i].start); + + if (!devm_request_mem_region(&dev->dev, + dev->resource[i].start, + resource_size(&dev->resource[i]), + dev_name(&dev->dev))) { + dev_err(&dev->dev, "Could not reserve memory region\n"); + err = -ENOMEM; + goto err_out; + } + + info->map[i].name = dev_name(&dev->dev); + info->map[i].phys = dev->resource[i].start; + info->map[i].size = resource_size(&dev->resource[i]); + info->map[i].bankwidth = flash->width; + info->map[i].set_vpp = imx6x_set_vpp; + info->map[i].pfow_base = 0; + info->map[i].map_priv_1 = (unsigned long)dev; + info->map[i].virt = devm_ioremap(&dev->dev, info->map[i].phys, + info->map[i].size); + if (info->map[i].virt == NULL) { + dev_err(&dev->dev, "Failed to ioremap flash region\n"); + err = -EIO; + goto err_out; + } + + info->map[i].cached = + ioremap_cached(info->map[i].phys, info->map[i].size); + if (!info->map[i].cached) + printk(KERN_WARNING "Failed to ioremap cached %s\n", + info->map[i].name); + + info->map[i].inval_cache = imx6x_map_inval_cache; + + simple_map_init(&info->map[i]); + probe_type = rom_probe_types; + + for (; info->mtd[i] == NULL && *probe_type != NULL; + probe_type++) + info->mtd[i] = do_map_probe(*probe_type, &info->map[i]); + + if (info->mtd[i] == NULL) { + dev_err(&dev->dev, "map_probe failed\n"); + err = -ENXIO; + goto err_out; + } else { + devices_found++; + } + info->mtd[i]->owner = THIS_MODULE; + info->mtd[i]->dev.parent = &dev->dev; + } + + if (devices_found == 1) { + info->cmtd = info->mtd[0]; + } else if (devices_found > 1) { + /* + * We detected multiple devices. Concatenate them together. + */ + info->cmtd = mtd_concat_create(info->mtd, devices_found, + dev_name(&dev->dev)); + if (info->cmtd == NULL) + err = -ENXIO; + } + if (err) + goto err_out; + + err = parse_mtd_partitions(info->cmtd, part_probe_types, + &info->parts, 0); + if (err > 0) { + mtd_device_register(info->cmtd, info->parts, err); + info->nr_parts = err; + return 0; + } + + if (flash->nr_parts) { + printk(KERN_NOTICE "Using physmap partition information\n"); + mtd_device_register(info->cmtd, flash->parts, + flash->nr_parts); + return 0; + } + + mtd_device_register(info->cmtd, NULL, 0); + + return 0; + +err_out: + imx6x_weimnor_remove(dev); + return err; +} + +#ifdef CONFIG_PM +static void imx6x_weimnor_shutdown(struct platform_device *dev) +{ + struct imx6x_weimnor_info *info = platform_get_drvdata(dev); + int i; + + for (i = 0; i < MAX_RESOURCES && info->mtd[i]; i++) + if (info->mtd[i]->suspend && info->mtd[i]->resume) + if (info->mtd[i]->suspend(info->mtd[i]) == 0) + info->mtd[i]->resume(info->mtd[i]); +} +#else +#define imx6x_weimnor_shutdown NULL +#endif + +static struct platform_driver imx6x_weimnor_driver = { + .probe = imx6x_weimnor_probe, + .remove = __devexit_p(imx6x_weimnor_remove), + .shutdown = imx6x_weimnor_shutdown, + .driver = { + .name = "imx6x-weimnor", + .owner = THIS_MODULE, + }, +}; + +static int __init imx6x_init(void) +{ + int err; + err = platform_driver_register(&imx6x_weimnor_driver); + return err; +} + +static void __exit imx6x_exit(void) +{ + platform_driver_unregister(&imx6x_weimnor_driver); +} + +module_init(imx6x_init); +module_exit(imx6x_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Oliver Brown "); +MODULE_DESCRIPTION("MTD map driver for Freescale iMX"); +