From c1f82478c535f1de9fecf3cafa82014f312d5d4e Mon Sep 17 00:00:00 2001 From: Lei Wen Date: Tue, 17 Aug 2010 13:50:23 +0800 Subject: mtd: pxa3xx_nand: condense the flash definition Adding a new flash definition would need less code. Keep the platform passing flash definition method. If one flash is both defined in platform data and builtin table, driver would select the one from platform data first. By this way, platform could select the timing most suit for itself, not need to follow the common settings. Signed-off-by: Lei Wen Signed-off-by: Haojian Zhuang Acked-by: Eric Miao Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- arch/arm/plat-pxa/include/plat/pxa3xx_nand.h | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) (limited to 'arch') diff --git a/arch/arm/plat-pxa/include/plat/pxa3xx_nand.h b/arch/arm/plat-pxa/include/plat/pxa3xx_nand.h index 3478eae32d8a..01a8448e471c 100644 --- a/arch/arm/plat-pxa/include/plat/pxa3xx_nand.h +++ b/arch/arm/plat-pxa/include/plat/pxa3xx_nand.h @@ -30,15 +30,15 @@ struct pxa3xx_nand_cmdset { }; struct pxa3xx_nand_flash { - const struct pxa3xx_nand_timing *timing; /* NAND Flash timing */ - const struct pxa3xx_nand_cmdset *cmdset; - - uint32_t page_per_block;/* Pages per block (PG_PER_BLK) */ - uint32_t page_size; /* Page size in bytes (PAGE_SZ) */ - uint32_t flash_width; /* Width of Flash memory (DWIDTH_M) */ - uint32_t dfc_width; /* Width of flash controller(DWIDTH_C) */ - uint32_t num_blocks; /* Number of physical blocks in Flash */ - uint32_t chip_id; + uint32_t chip_id; + unsigned int page_per_block; /* Pages per block (PG_PER_BLK) */ + unsigned int page_size; /* Page size in bytes (PAGE_SZ) */ + unsigned int flash_width; /* Width of Flash memory (DWIDTH_M) */ + unsigned int dfc_width; /* Width of flash controller(DWIDTH_C) */ + unsigned int num_blocks; /* Number of physical blocks in Flash */ + + struct pxa3xx_nand_cmdset *cmdset; /* NAND command set */ + struct pxa3xx_nand_timing *timing; /* NAND Flash timing */ }; struct pxa3xx_nand_platform_data { -- cgit v1.2.3 From bc49c28962de0e3758ac2cdc7821d506b827f219 Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Sun, 12 Sep 2010 15:52:59 +0200 Subject: mtd: add Broadcom BCM63xx image tag partition parser This patch adds support for parsing Broadcom BCM63xx image tag format and creating MTD partitions accordingly. This driver is a platform_device which can be instantiated accordingly by bcm63xx board support code. Signed-off-by: Daniel Dickinson Signed-off-by: Mike Albon Signed-off-by: Florian Fainelli Signed-off-by: David Woodhouse --- arch/mips/include/asm/mach-bcm63xx/bcm963xx_tag.h | 97 ++++++++ drivers/mtd/maps/Kconfig | 9 + drivers/mtd/maps/Makefile | 1 + drivers/mtd/maps/bcm963xx-flash.c | 271 ++++++++++++++++++++++ 4 files changed, 378 insertions(+) create mode 100644 arch/mips/include/asm/mach-bcm63xx/bcm963xx_tag.h create mode 100644 drivers/mtd/maps/bcm963xx-flash.c (limited to 'arch') diff --git a/arch/mips/include/asm/mach-bcm63xx/bcm963xx_tag.h b/arch/mips/include/asm/mach-bcm63xx/bcm963xx_tag.h new file mode 100644 index 000000000000..5325084d5c48 --- /dev/null +++ b/arch/mips/include/asm/mach-bcm63xx/bcm963xx_tag.h @@ -0,0 +1,97 @@ +#ifndef __BCM963XX_TAG_H +#define __BCM963XX_TAG_H + +#define TAGVER_LEN 4 /* Length of Tag Version */ +#define TAGLAYOUT_LEN 4 /* Length of FlashLayoutVer */ +#define SIG1_LEN 20 /* Company Signature 1 Length */ +#define SIG2_LEN 14 /* Company Signature 2 Lenght */ +#define BOARDID_LEN 16 /* Length of BoardId */ +#define ENDIANFLAG_LEN 2 /* Endian Flag Length */ +#define CHIPID_LEN 6 /* Chip Id Length */ +#define IMAGE_LEN 10 /* Length of Length Field */ +#define ADDRESS_LEN 12 /* Length of Address field */ +#define DUALFLAG_LEN 2 /* Dual Image flag Length */ +#define INACTIVEFLAG_LEN 2 /* Inactie Flag Length */ +#define RSASIG_LEN 20 /* Length of RSA Signature in tag */ +#define TAGINFO1_LEN 30 /* Length of vendor information field1 in tag */ +#define FLASHLAYOUTVER_LEN 4 /* Length of Flash Layout Version String tag */ +#define TAGINFO2_LEN 16 /* Length of vendor information field2 in tag */ +#define CRC_LEN 4 /* Length of CRC in bytes */ +#define ALTTAGINFO_LEN 54 /* Alternate length for vendor information; Pirelli */ + +#define NUM_PIRELLI 2 +#define IMAGETAG_CRC_START 0xFFFFFFFF + +#define PIRELLI_BOARDS { \ + "AGPF-S0", \ + "DWV-S0", \ +} + +/* + * The broadcom firmware assumes the rootfs starts the image, + * therefore uses the rootfs start (flash_image_address) + * to determine where to flash the image. Since we have the kernel first + * we have to give it the kernel address, but the crc uses the length + * associated with this address (root_length), which is added to the kernel + * length (kernel_length) to determine the length of image to flash and thus + * needs to be rootfs + deadcode (jffs2 EOF marker) +*/ + +struct bcm_tag { + /* 0-3: Version of the image tag */ + char tag_version[TAGVER_LEN]; + /* 4-23: Company Line 1 */ + char sig_1[SIG1_LEN]; + /* 24-37: Company Line 2 */ + char sig_2[SIG2_LEN]; + /* 38-43: Chip this image is for */ + char chip_id[CHIPID_LEN]; + /* 44-59: Board name */ + char board_id[BOARDID_LEN]; + /* 60-61: Map endianness -- 1 BE 0 LE */ + char big_endian[ENDIANFLAG_LEN]; + /* 62-71: Total length of image */ + char total_length[IMAGE_LEN]; + /* 72-83: Address in memory of CFE */ + char cfe__address[ADDRESS_LEN]; + /* 84-93: Size of CFE */ + char cfe_length[IMAGE_LEN]; + /* 94-105: Address in memory of image start + * (kernel for OpenWRT, rootfs for stock firmware) + */ + char flash_image_start[ADDRESS_LEN]; + /* 106-115: Size of rootfs */ + char root_length[IMAGE_LEN]; + /* 116-127: Address in memory of kernel */ + char kernel_address[ADDRESS_LEN]; + /* 128-137: Size of kernel */ + char kernel_length[IMAGE_LEN]; + /* 138-139: Unused at the moment */ + char dual_image[DUALFLAG_LEN]; + /* 140-141: Unused at the moment */ + char inactive_flag[INACTIVEFLAG_LEN]; + /* 142-161: RSA Signature (not used; some vendors may use this) */ + char rsa_signature[RSASIG_LEN]; + /* 162-191: Compilation and related information (not used in OpenWrt) */ + char information1[TAGINFO1_LEN]; + /* 192-195: Version flash layout */ + char flash_layout_ver[FLASHLAYOUTVER_LEN]; + /* 196-199: kernel+rootfs CRC32 */ + char fskernel_crc[CRC_LEN]; + /* 200-215: Unused except on Alice Gate where is is information */ + char information2[TAGINFO2_LEN]; + /* 216-219: CRC32 of image less imagetag (kernel for Alice Gate) */ + char image_crc[CRC_LEN]; + /* 220-223: CRC32 of rootfs partition */ + char rootfs_crc[CRC_LEN]; + /* 224-227: CRC32 of kernel partition */ + char kernel_crc[CRC_LEN]; + /* 228-235: Unused at present */ + char reserved1[8]; + /* 236-239: CRC32 of header excluding tagVersion */ + char header_crc[CRC_LEN]; + /* 240-255: Unused at present */ + char reserved2[16]; +}; + +#endif /* __BCM63XX_TAG_H */ diff --git a/drivers/mtd/maps/Kconfig b/drivers/mtd/maps/Kconfig index 701d942c6795..e5eb65cfb3eb 100644 --- a/drivers/mtd/maps/Kconfig +++ b/drivers/mtd/maps/Kconfig @@ -251,6 +251,15 @@ config MTD_NETtel help Support for flash chips on NETtel/SecureEdge/SnapGear boards. +config MTD_BCM963XX + tristate "Map driver for Broadcom BCM963xx boards" + depends on BCM63XX + select MTD_MAP_BANK_WIDTH_2 + select MTD_CFI_I1 + help + Support for parsing CFE image tag and creating MTD partitions on + Broadcom BCM63xx boards. + config MTD_DILNETPC tristate "CFI Flash device mapped on DIL/Net PC" depends on X86 && MTD_CONCAT && MTD_PARTITIONS && MTD_CFI_INTELEXT && BROKEN diff --git a/drivers/mtd/maps/Makefile b/drivers/mtd/maps/Makefile index f216bb573713..c7869c7a6b18 100644 --- a/drivers/mtd/maps/Makefile +++ b/drivers/mtd/maps/Makefile @@ -58,3 +58,4 @@ obj-$(CONFIG_MTD_BFIN_ASYNC) += bfin-async-flash.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_BCM963XX) += bcm963xx-flash.o diff --git a/drivers/mtd/maps/bcm963xx-flash.c b/drivers/mtd/maps/bcm963xx-flash.c new file mode 100644 index 000000000000..d175c120ee84 --- /dev/null +++ b/drivers/mtd/maps/bcm963xx-flash.c @@ -0,0 +1,271 @@ +/* + * Copyright © 2006-2008 Florian Fainelli + * Mike Albon + * Copyright © 2009-2010 Daniel Dickinson + * + * 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 St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define BCM63XX_BUSWIDTH 2 /* Buswidth */ +#define BCM63XX_EXTENDED_SIZE 0xBFC00000 /* Extended flash address */ + +#define PFX KBUILD_MODNAME ": " + +static struct mtd_partition *parsed_parts; + +static struct mtd_info *bcm963xx_mtd_info; + +static struct map_info bcm963xx_map = { + .name = "bcm963xx", + .bankwidth = BCM63XX_BUSWIDTH, +}; + +static int parse_cfe_partitions(struct mtd_info *master, + struct mtd_partition **pparts) +{ + /* CFE, NVRAM and global Linux are always present */ + int nrparts = 3, curpart = 0; + struct bcm_tag *buf; + struct mtd_partition *parts; + int ret; + size_t retlen; + unsigned int rootfsaddr, kerneladdr, spareaddr; + unsigned int rootfslen, kernellen, sparelen, totallen; + int namelen = 0; + int i; + char *boardid; + char *tagversion; + + /* Allocate memory for buffer */ + buf = vmalloc(sizeof(struct bcm_tag)); + if (!buf) + return -ENOMEM; + + /* Get the tag */ + ret = master->read(master, master->erasesize, sizeof(struct bcm_tag), + &retlen, (void *)buf); + if (retlen != sizeof(struct bcm_tag)) { + vfree(buf); + return -EIO; + } + + sscanf(buf->kernel_address, "%u", &kerneladdr); + sscanf(buf->kernel_length, "%u", &kernellen); + sscanf(buf->total_length, "%u", &totallen); + tagversion = &(buf->tag_version[0]); + boardid = &(buf->board_id[0]); + + printk(KERN_INFO PFX "CFE boot tag found with version %s " + "and board type %s\n", tagversion, boardid); + + kerneladdr = kerneladdr - BCM63XX_EXTENDED_SIZE; + rootfsaddr = kerneladdr + kernellen; + spareaddr = roundup(totallen, master->erasesize) + master->erasesize; + sparelen = master->size - spareaddr - master->erasesize; + rootfslen = spareaddr - rootfsaddr; + + /* Determine number of partitions */ + namelen = 8; + if (rootfslen > 0) { + nrparts++; + namelen += 6; + }; + if (kernellen > 0) { + nrparts++; + namelen += 6; + }; + + /* Ask kernel for more memory */ + parts = kzalloc(sizeof(*parts) * nrparts + 10 * nrparts, GFP_KERNEL); + if (!parts) { + vfree(buf); + return -ENOMEM; + }; + + /* Start building partition list */ + parts[curpart].name = "CFE"; + parts[curpart].offset = 0; + parts[curpart].size = master->erasesize; + curpart++; + + if (kernellen > 0) { + parts[curpart].name = "kernel"; + parts[curpart].offset = kerneladdr; + parts[curpart].size = kernellen; + curpart++; + }; + + if (rootfslen > 0) { + parts[curpart].name = "rootfs"; + parts[curpart].offset = rootfsaddr; + parts[curpart].size = rootfslen; + if (sparelen > 0) + parts[curpart].size += sparelen; + curpart++; + }; + + parts[curpart].name = "nvram"; + parts[curpart].offset = master->size - master->erasesize; + parts[curpart].size = master->erasesize; + + /* Global partition "linux" to make easy firmware upgrade */ + curpart++; + parts[curpart].name = "linux"; + parts[curpart].offset = parts[0].size; + parts[curpart].size = master->size - parts[0].size - parts[3].size; + + for (i = 0; i < nrparts; i++) + printk(KERN_INFO PFX "Partition %d is %s offset %lx and " + "length %lx\n", i, parts[i].name, + (long unsigned int)(parts[i].offset), + (long unsigned int)(parts[i].size)); + + printk(KERN_INFO PFX "Spare partition is %x offset and length %x\n", + spareaddr, sparelen); + *pparts = parts; + vfree(buf); + + return nrparts; +}; + +static int bcm963xx_detect_cfe(struct mtd_info *master) +{ + int idoffset = 0x4e0; + static char idstring[8] = "CFE1CFE1"; + char buf[9]; + int ret; + size_t retlen; + + ret = master->read(master, idoffset, 8, &retlen, (void *)buf); + buf[retlen] = 0; + printk(KERN_INFO PFX "Read Signature value of %s\n", buf); + + return strncmp(idstring, buf, 8); +} + +static int bcm963xx_probe(struct platform_device *pdev) +{ + int err = 0; + int parsed_nr_parts = 0; + char *part_type; + struct resource *r; + + r = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!r) { + dev_err(&pdev->dev, "no resource supplied\n"); + return -ENODEV; + } + + bcm963xx_map.phys = r->start; + bcm963xx_map.size = resource_size(r); + bcm963xx_map.virt = ioremap(r->start, resource_size(r)); + if (!bcm963xx_map.virt) { + dev_err(&pdev->dev, "failed to ioremap\n"); + return -EIO; + } + + dev_info(&pdev->dev, "0x%08lx at 0x%08x\n", + bcm963xx_map.size, bcm963xx_map.phys); + + simple_map_init(&bcm963xx_map); + + bcm963xx_mtd_info = do_map_probe("cfi_probe", &bcm963xx_map); + if (!bcm963xx_mtd_info) { + dev_err(&pdev->dev, "failed to probe using CFI\n"); + err = -EIO; + goto err_probe; + } + + bcm963xx_mtd_info->owner = THIS_MODULE; + + /* This is mutually exclusive */ + if (bcm963xx_detect_cfe(bcm963xx_mtd_info) == 0) { + dev_info(&pdev->dev, "CFE bootloader detected\n"); + if (parsed_nr_parts == 0) { + int ret = parse_cfe_partitions(bcm963xx_mtd_info, + &parsed_parts); + if (ret > 0) { + part_type = "CFE"; + parsed_nr_parts = ret; + } + } + } else { + dev_info(&pdev->dev, "unsupported bootloader\n"); + err = -ENODEV; + goto err_probe; + } + + return add_mtd_partitions(bcm963xx_mtd_info, parsed_parts, + parsed_nr_parts); + +err_probe: + iounmap(bcm963xx_map.virt); + return err; +} + +static int bcm963xx_remove(struct platform_device *pdev) +{ + if (bcm963xx_mtd_info) { + del_mtd_partitions(bcm963xx_mtd_info); + map_destroy(bcm963xx_mtd_info); + } + + if (bcm963xx_map.virt) { + iounmap(bcm963xx_map.virt); + bcm963xx_map.virt = 0; + } + + return 0; +} + +static struct platform_driver bcm63xx_mtd_dev = { + .probe = bcm963xx_probe, + .remove = bcm963xx_remove, + .driver = { + .name = "bcm963xx-flash", + .owner = THIS_MODULE, + }, +}; + +static int __init bcm963xx_mtd_init(void) +{ + return platform_driver_register(&bcm63xx_mtd_dev); +} + +static void __exit bcm963xx_mtd_exit(void) +{ + platform_driver_unregister(&bcm63xx_mtd_dev); +} + +module_init(bcm963xx_mtd_init); +module_exit(bcm963xx_mtd_exit); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Broadcom BCM63xx MTD driver for CFE and RedBoot"); +MODULE_AUTHOR("Daniel Dickinson "); +MODULE_AUTHOR("Florian Fainelli "); +MODULE_AUTHOR("Mike Albon "); -- cgit v1.2.3 From 93ac5a552c831096003f9bc74471300dc1710a42 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Mon, 13 Sep 2010 00:35:37 +0200 Subject: ARM: platform data to use the FSMC NAND with the U300 Just as it says, if this is merged along with the other patch, the driver supports the U300 NAND flash interface. Signed-off-by: Linus Walleij Signed-off-by: David Woodhouse --- arch/arm/mach-u300/clock.c | 6 ++-- arch/arm/mach-u300/core.c | 47 ++++++++++++++++++++++++++--- arch/arm/mach-u300/include/mach/u300-regs.h | 2 -- 3 files changed, 45 insertions(+), 10 deletions(-) (limited to 'arch') diff --git a/arch/arm/mach-u300/clock.c b/arch/arm/mach-u300/clock.c index 60acf9e708ae..7458fc6df5c6 100644 --- a/arch/arm/mach-u300/clock.c +++ b/arch/arm/mach-u300/clock.c @@ -66,7 +66,7 @@ static DEFINE_SPINLOCK(syscon_resetreg_lock); * AMBA bus * | * +- CPU - * +- NANDIF NAND Flash interface + * +- FSMC NANDIF NAND Flash interface * +- SEMI Shared Memory interface * +- ISP Image Signal Processor (U335 only) * +- CDS (U335 only) @@ -726,7 +726,7 @@ static struct clk cpu_clk = { }; static struct clk nandif_clk = { - .name = "NANDIF", + .name = "FSMC", .parent = &amba_clk, .hw_ctrld = false, .reset = true, @@ -1259,7 +1259,7 @@ static struct clk_lookup lookups[] = { /* Connected directly to the AMBA bus */ DEF_LOOKUP("amba", &amba_clk), DEF_LOOKUP("cpu", &cpu_clk), - DEF_LOOKUP("fsmc", &nandif_clk), + DEF_LOOKUP("fsmc-nand", &nandif_clk), DEF_LOOKUP("semi", &semi_clk), #ifdef CONFIG_MACH_U300_BS335 DEF_LOOKUP("isp", &isp_clk), diff --git a/arch/arm/mach-u300/core.c b/arch/arm/mach-u300/core.c index ea41c236be0f..aa53ee22438f 100644 --- a/arch/arm/mach-u300/core.c +++ b/arch/arm/mach-u300/core.c @@ -21,7 +21,8 @@ #include #include #include -#include +#include +#include #include #include @@ -30,6 +31,7 @@ #include #include +#include #include #include #include @@ -285,6 +287,13 @@ static struct resource rtc_resources[] = { */ static struct resource fsmc_resources[] = { { + .name = "nand_data", + .start = U300_NAND_CS0_PHYS_BASE, + .end = U300_NAND_CS0_PHYS_BASE + SZ_16K - 1, + .flags = IORESOURCE_MEM, + }, + { + .name = "fsmc_regs", .start = U300_NAND_IF_PHYS_BASE, .end = U300_NAND_IF_PHYS_BASE + SZ_4K - 1, .flags = IORESOURCE_MEM, @@ -1429,11 +1438,39 @@ static struct platform_device rtc_device = { .resource = rtc_resources, }; -static struct platform_device fsmc_device = { - .name = "nandif", +static struct mtd_partition u300_partitions[] = { + { + .name = "bootrecords", + .offset = 0, + .size = SZ_128K, + }, + { + .name = "free", + .offset = SZ_128K, + .size = 8064 * SZ_1K, + }, + { + .name = "platform", + .offset = 8192 * SZ_1K, + .size = 253952 * SZ_1K, + }, +}; + +static struct fsmc_nand_platform_data nand_platform_data = { + .partitions = u300_partitions, + .nr_partitions = ARRAY_SIZE(u300_partitions), + .options = NAND_SKIP_BBTSCAN, + .width = FSMC_NAND_BW8, +}; + +static struct platform_device nand_device = { + .name = "fsmc-nand", .id = -1, - .num_resources = ARRAY_SIZE(fsmc_resources), .resource = fsmc_resources, + .num_resources = ARRAY_SIZE(fsmc_resources), + .dev = { + .platform_data = &nand_platform_data, + }, }; static struct platform_device ave_device = { @@ -1465,7 +1502,7 @@ static struct platform_device *platform_devs[] __initdata = { &keypad_device, &rtc_device, &gpio_device, - &fsmc_device, + &nand_device, &wdog_device, &ave_device }; diff --git a/arch/arm/mach-u300/include/mach/u300-regs.h b/arch/arm/mach-u300/include/mach/u300-regs.h index 56721a0cd2af..8b85df4c8d8f 100644 --- a/arch/arm/mach-u300/include/mach/u300-regs.h +++ b/arch/arm/mach-u300/include/mach/u300-regs.h @@ -20,11 +20,9 @@ /* NAND Flash CS0 */ #define U300_NAND_CS0_PHYS_BASE 0x80000000 -#define U300_NAND_CS0_VIRT_BASE 0xff040000 /* NFIF */ #define U300_NAND_IF_PHYS_BASE 0x9f800000 -#define U300_NAND_IF_VIRT_BASE 0xff030000 /* AHB Peripherals */ #define U300_AHB_PER_PHYS_BASE 0xa0000000 -- cgit v1.2.3 From 3ab8f2a2e7011c5e83363b42950757e46ef06824 Mon Sep 17 00:00:00 2001 From: Roy Zang Date: Mon, 18 Oct 2010 15:22:31 +0800 Subject: P4080/eLBC: Make Freescale elbc interrupt common to elbc devices Move Freescale elbc interrupt from nand driver to elbc driver. Then all elbc devices can use the interrupt instead of ONLY nand. For former nand driver, it had the two functions: 1. detecting nand flash partitions; 2. registering elbc interrupt. Now, second function is removed to fsl_lbc.c. Signed-off-by: Lan Chunhe-B25806 Signed-off-by: Roy Zang Reviewed-by: Anton Vorontsov Cc: Wood Scott-B07421 Signed-off-by: David Woodhouse --- arch/powerpc/Kconfig | 7 +- arch/powerpc/include/asm/fsl_lbc.h | 33 ++- arch/powerpc/sysdev/fsl_lbc.c | 231 +++++++++++++++--- drivers/mtd/nand/Kconfig | 1 + drivers/mtd/nand/fsl_elbc_nand.c | 482 +++++++++++++++---------------------- 5 files changed, 426 insertions(+), 328 deletions(-) (limited to 'arch') diff --git a/arch/powerpc/Kconfig b/arch/powerpc/Kconfig index 631e5a0fb6ab..44df1bac9701 100644 --- a/arch/powerpc/Kconfig +++ b/arch/powerpc/Kconfig @@ -687,9 +687,12 @@ config 4xx_SOC bool config FSL_LBC - bool + bool "Freescale Local Bus support" + depends on FSL_SOC help - Freescale Localbus support + Enables reporting of errors from the Freescale local bus + controller. Also contains some common code used by + drivers for specific local bus peripherals. config FSL_GTM bool diff --git a/arch/powerpc/include/asm/fsl_lbc.h b/arch/powerpc/include/asm/fsl_lbc.h index 1b5a21041f9b..06a11124dde1 100644 --- a/arch/powerpc/include/asm/fsl_lbc.h +++ b/arch/powerpc/include/asm/fsl_lbc.h @@ -1,9 +1,10 @@ /* Freescale Local Bus Controller * - * Copyright (c) 2006-2007 Freescale Semiconductor + * Copyright © 2006-2007, 2010 Freescale Semiconductor * * Authors: Nick Spence , * Scott Wood + * Jack Lan * * 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 @@ -26,6 +27,8 @@ #include #include #include +#include +#include struct fsl_lbc_bank { __be32 br; /**< Base Register */ @@ -125,13 +128,23 @@ struct fsl_lbc_regs { #define LTESR_ATMW 0x00800000 #define LTESR_ATMR 0x00400000 #define LTESR_CS 0x00080000 +#define LTESR_UPM 0x00000002 #define LTESR_CC 0x00000001 #define LTESR_NAND_MASK (LTESR_FCT | LTESR_PAR | LTESR_CC) +#define LTESR_MASK (LTESR_BM | LTESR_FCT | LTESR_PAR | LTESR_WP \ + | LTESR_ATMW | LTESR_ATMR | LTESR_CS | LTESR_UPM \ + | LTESR_CC) +#define LTESR_CLEAR 0xFFFFFFFF +#define LTECCR_CLEAR 0xFFFFFFFF +#define LTESR_STATUS LTESR_MASK +#define LTEIR_ENABLE LTESR_MASK +#define LTEDR_ENABLE 0x00000000 __be32 ltedr; /**< Transfer Error Disable Register */ __be32 lteir; /**< Transfer Error Interrupt Register */ __be32 lteatr; /**< Transfer Error Attributes Register */ __be32 ltear; /**< Transfer Error Address Register */ - u8 res6[0xC]; + __be32 lteccr; /**< Transfer Error ECC Register */ + u8 res6[0x8]; __be32 lbcr; /**< Configuration Register */ #define LBCR_LDIS 0x80000000 #define LBCR_LDIS_SHIFT 31 @@ -265,7 +278,23 @@ static inline void fsl_upm_end_pattern(struct fsl_upm *upm) cpu_relax(); } +/* overview of the fsl lbc controller */ + +struct fsl_lbc_ctrl { + /* device info */ + struct device *dev; + struct fsl_lbc_regs __iomem *regs; + int irq; + wait_queue_head_t irq_wait; + spinlock_t lock; + void *nand; + + /* status read from LTESR by irq handler */ + unsigned int irq_status; +}; + extern int fsl_upm_run_pattern(struct fsl_upm *upm, void __iomem *io_base, u32 mar); +extern struct fsl_lbc_ctrl *fsl_lbc_ctrl_dev; #endif /* __ASM_FSL_LBC_H */ diff --git a/arch/powerpc/sysdev/fsl_lbc.c b/arch/powerpc/sysdev/fsl_lbc.c index dceb8d1a843d..91c9c53b1845 100644 --- a/arch/powerpc/sysdev/fsl_lbc.c +++ b/arch/powerpc/sysdev/fsl_lbc.c @@ -1,9 +1,12 @@ /* * Freescale LBC and UPM routines. * - * Copyright (c) 2007-2008 MontaVista Software, Inc. + * Copyright © 2007-2008 MontaVista Software, Inc. + * Copyright © 2010 Freescale Semiconductor * * Author: Anton Vorontsov + * Author: Jack Lan + * Author: Roy Zang * * 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 @@ -19,39 +22,16 @@ #include #include #include +#include +#include +#include +#include #include #include static spinlock_t fsl_lbc_lock = __SPIN_LOCK_UNLOCKED(fsl_lbc_lock); -static struct fsl_lbc_regs __iomem *fsl_lbc_regs; - -static char __initdata *compat_lbc[] = { - "fsl,pq2-localbus", - "fsl,pq2pro-localbus", - "fsl,pq3-localbus", - "fsl,elbc", -}; - -static int __init fsl_lbc_init(void) -{ - struct device_node *lbus; - int i; - - for (i = 0; i < ARRAY_SIZE(compat_lbc); i++) { - lbus = of_find_compatible_node(NULL, NULL, compat_lbc[i]); - if (lbus) - goto found; - } - return -ENODEV; - -found: - fsl_lbc_regs = of_iomap(lbus, 0); - of_node_put(lbus); - if (!fsl_lbc_regs) - return -ENOMEM; - return 0; -} -arch_initcall(fsl_lbc_init); +struct fsl_lbc_ctrl *fsl_lbc_ctrl_dev; +EXPORT_SYMBOL(fsl_lbc_ctrl_dev); /** * fsl_lbc_find - find Localbus bank @@ -65,13 +45,15 @@ arch_initcall(fsl_lbc_init); int fsl_lbc_find(phys_addr_t addr_base) { int i; + struct fsl_lbc_regs __iomem *lbc; - if (!fsl_lbc_regs) + if (!fsl_lbc_ctrl_dev || !fsl_lbc_ctrl_dev->regs) return -ENODEV; - for (i = 0; i < ARRAY_SIZE(fsl_lbc_regs->bank); i++) { - __be32 br = in_be32(&fsl_lbc_regs->bank[i].br); - __be32 or = in_be32(&fsl_lbc_regs->bank[i].or); + lbc = fsl_lbc_ctrl_dev->regs; + for (i = 0; i < ARRAY_SIZE(lbc->bank); i++) { + __be32 br = in_be32(&lbc->bank[i].br); + __be32 or = in_be32(&lbc->bank[i].or); if (br & BR_V && (br & or & BR_BA) == addr_base) return i; @@ -94,22 +76,27 @@ int fsl_upm_find(phys_addr_t addr_base, struct fsl_upm *upm) { int bank; __be32 br; + struct fsl_lbc_regs __iomem *lbc; bank = fsl_lbc_find(addr_base); if (bank < 0) return bank; - br = in_be32(&fsl_lbc_regs->bank[bank].br); + if (!fsl_lbc_ctrl_dev || !fsl_lbc_ctrl_dev->regs) + return -ENODEV; + + lbc = fsl_lbc_ctrl_dev->regs; + br = in_be32(&lbc->bank[bank].br); switch (br & BR_MSEL) { case BR_MS_UPMA: - upm->mxmr = &fsl_lbc_regs->mamr; + upm->mxmr = &lbc->mamr; break; case BR_MS_UPMB: - upm->mxmr = &fsl_lbc_regs->mbmr; + upm->mxmr = &lbc->mbmr; break; case BR_MS_UPMC: - upm->mxmr = &fsl_lbc_regs->mcmr; + upm->mxmr = &lbc->mcmr; break; default: return -EINVAL; @@ -148,9 +135,12 @@ int fsl_upm_run_pattern(struct fsl_upm *upm, void __iomem *io_base, u32 mar) int ret = 0; unsigned long flags; + if (!fsl_lbc_ctrl_dev || !fsl_lbc_ctrl_dev->regs) + return -ENODEV; + spin_lock_irqsave(&fsl_lbc_lock, flags); - out_be32(&fsl_lbc_regs->mar, mar); + out_be32(&fsl_lbc_ctrl_dev->regs->mar, mar); switch (upm->width) { case 8: @@ -172,3 +162,166 @@ int fsl_upm_run_pattern(struct fsl_upm *upm, void __iomem *io_base, u32 mar) return ret; } EXPORT_SYMBOL(fsl_upm_run_pattern); + +static int __devinit fsl_lbc_ctrl_init(struct fsl_lbc_ctrl *ctrl) +{ + struct fsl_lbc_regs __iomem *lbc = ctrl->regs; + + /* clear event registers */ + setbits32(&lbc->ltesr, LTESR_CLEAR); + out_be32(&lbc->lteatr, 0); + out_be32(&lbc->ltear, 0); + out_be32(&lbc->lteccr, LTECCR_CLEAR); + out_be32(&lbc->ltedr, LTEDR_ENABLE); + + /* Enable interrupts for any detected events */ + out_be32(&lbc->lteir, LTEIR_ENABLE); + + return 0; +} + +/* + * NOTE: This interrupt is used to report localbus events of various kinds, + * such as transaction errors on the chipselects. + */ + +static irqreturn_t fsl_lbc_ctrl_irq(int irqno, void *data) +{ + struct fsl_lbc_ctrl *ctrl = data; + struct fsl_lbc_regs __iomem *lbc = ctrl->regs; + u32 status; + + status = in_be32(&lbc->ltesr); + if (!status) + return IRQ_NONE; + + out_be32(&lbc->ltesr, LTESR_CLEAR); + out_be32(&lbc->lteatr, 0); + out_be32(&lbc->ltear, 0); + ctrl->irq_status = status; + + if (status & LTESR_BM) + dev_err(ctrl->dev, "Local bus monitor time-out: " + "LTESR 0x%08X\n", status); + if (status & LTESR_WP) + dev_err(ctrl->dev, "Write protect error: " + "LTESR 0x%08X\n", status); + if (status & LTESR_ATMW) + dev_err(ctrl->dev, "Atomic write error: " + "LTESR 0x%08X\n", status); + if (status & LTESR_ATMR) + dev_err(ctrl->dev, "Atomic read error: " + "LTESR 0x%08X\n", status); + if (status & LTESR_CS) + dev_err(ctrl->dev, "Chip select error: " + "LTESR 0x%08X\n", status); + if (status & LTESR_UPM) + ; + if (status & LTESR_FCT) { + dev_err(ctrl->dev, "FCM command time-out: " + "LTESR 0x%08X\n", status); + smp_wmb(); + wake_up(&ctrl->irq_wait); + } + if (status & LTESR_PAR) { + dev_err(ctrl->dev, "Parity or Uncorrectable ECC error: " + "LTESR 0x%08X\n", status); + smp_wmb(); + wake_up(&ctrl->irq_wait); + } + if (status & LTESR_CC) { + smp_wmb(); + wake_up(&ctrl->irq_wait); + } + if (status & ~LTESR_MASK) + dev_err(ctrl->dev, "Unknown error: " + "LTESR 0x%08X\n", status); + return IRQ_HANDLED; +} + +/* + * fsl_lbc_ctrl_probe + * + * called by device layer when it finds a device matching + * one our driver can handled. This code allocates all of + * the resources needed for the controller only. The + * resources for the NAND banks themselves are allocated + * in the chip probe function. +*/ + +static int __devinit fsl_lbc_ctrl_probe(struct platform_device *dev) +{ + int ret; + + if (!dev->dev.of_node) { + dev_err(&dev->dev, "Device OF-Node is NULL"); + return -EFAULT; + } + + fsl_lbc_ctrl_dev = kzalloc(sizeof(*fsl_lbc_ctrl_dev), GFP_KERNEL); + if (!fsl_lbc_ctrl_dev) + return -ENOMEM; + + dev_set_drvdata(&dev->dev, fsl_lbc_ctrl_dev); + + spin_lock_init(&fsl_lbc_ctrl_dev->lock); + init_waitqueue_head(&fsl_lbc_ctrl_dev->irq_wait); + + fsl_lbc_ctrl_dev->regs = of_iomap(dev->dev.of_node, 0); + if (!fsl_lbc_ctrl_dev->regs) { + dev_err(&dev->dev, "failed to get memory region\n"); + ret = -ENODEV; + goto err; + } + + fsl_lbc_ctrl_dev->irq = irq_of_parse_and_map(dev->dev.of_node, 0); + if (fsl_lbc_ctrl_dev->irq == NO_IRQ) { + dev_err(&dev->dev, "failed to get irq resource\n"); + ret = -ENODEV; + goto err; + } + + fsl_lbc_ctrl_dev->dev = &dev->dev; + + ret = fsl_lbc_ctrl_init(fsl_lbc_ctrl_dev); + if (ret < 0) + goto err; + + ret = request_irq(fsl_lbc_ctrl_dev->irq, fsl_lbc_ctrl_irq, 0, + "fsl-lbc", fsl_lbc_ctrl_dev); + if (ret != 0) { + dev_err(&dev->dev, "failed to install irq (%d)\n", + fsl_lbc_ctrl_dev->irq); + ret = fsl_lbc_ctrl_dev->irq; + goto err; + } + + return 0; + +err: + iounmap(fsl_lbc_ctrl_dev->regs); + kfree(fsl_lbc_ctrl_dev); + return ret; +} + +static const struct of_device_id fsl_lbc_match[] = { + { .compatible = "fsl,elbc", }, + { .compatible = "fsl,pq3-localbus", }, + { .compatible = "fsl,pq2-localbus", }, + { .compatible = "fsl,pq2pro-localbus", }, + {}, +}; + +static struct platform_driver fsl_lbc_ctrl_driver = { + .driver = { + .name = "fsl-lbc", + .of_match_table = fsl_lbc_match, + }, + .probe = fsl_lbc_ctrl_probe, +}; + +static int __init fsl_lbc_init(void) +{ + return platform_driver_register(&fsl_lbc_ctrl_driver); +} +module_init(fsl_lbc_init); diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig index a494cce704b9..8229802b4346 100644 --- a/drivers/mtd/nand/Kconfig +++ b/drivers/mtd/nand/Kconfig @@ -451,6 +451,7 @@ config MTD_NAND_ORION config MTD_NAND_FSL_ELBC tristate "NAND support for Freescale eLBC controllers" depends on PPC_OF + select FSL_LBC help Various Freescale chips, including the 8313, include a NAND Flash Controller Module with built-in hardware ECC capabilities. diff --git a/drivers/mtd/nand/fsl_elbc_nand.c b/drivers/mtd/nand/fsl_elbc_nand.c index 80de0bff6c3a..cd8de29e38cc 100644 --- a/drivers/mtd/nand/fsl_elbc_nand.c +++ b/drivers/mtd/nand/fsl_elbc_nand.c @@ -1,9 +1,11 @@ /* Freescale Enhanced Local Bus Controller NAND driver * - * Copyright (c) 2006-2007 Freescale Semiconductor + * Copyright © 2006-2007, 2010 Freescale Semiconductor * * Authors: Nick Spence , * Scott Wood + * Jack Lan + * Roy Zang * * 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 @@ -27,6 +29,7 @@ #include #include #include +#include #include #include @@ -42,14 +45,12 @@ #define ERR_BYTE 0xFF /* Value returned for read bytes when read failed */ #define FCM_TIMEOUT_MSECS 500 /* Maximum number of mSecs to wait for FCM */ -struct fsl_elbc_ctrl; - /* mtd information per set */ struct fsl_elbc_mtd { struct mtd_info mtd; struct nand_chip chip; - struct fsl_elbc_ctrl *ctrl; + struct fsl_lbc_ctrl *ctrl; struct device *dev; int bank; /* Chip select bank number */ @@ -58,18 +59,12 @@ struct fsl_elbc_mtd { unsigned int fmr; /* FCM Flash Mode Register value */ }; -/* overview of the fsl elbc controller */ +/* Freescale eLBC FCM controller infomation */ -struct fsl_elbc_ctrl { +struct fsl_elbc_fcm_ctrl { struct nand_hw_control controller; struct fsl_elbc_mtd *chips[MAX_BANKS]; - /* device info */ - struct device *dev; - struct fsl_lbc_regs __iomem *regs; - int irq; - wait_queue_head_t irq_wait; - unsigned int irq_status; /* status read from LTESR by irq handler */ u8 __iomem *addr; /* Address of assigned FCM buffer */ unsigned int page; /* Last page written to / read from */ unsigned int read_bytes; /* Number of bytes read during command */ @@ -79,6 +74,7 @@ struct fsl_elbc_ctrl { unsigned int mdr; /* UPM/FCM Data Register value */ unsigned int use_mdr; /* Non zero if the MDR is to be set */ unsigned int oob; /* Non zero if operating on OOB data */ + unsigned int counter; /* counter for the initializations */ char *oob_poi; /* Place to write ECC after read back */ }; @@ -164,11 +160,12 @@ static void set_addr(struct mtd_info *mtd, int column, int page_addr, int oob) { struct nand_chip *chip = mtd->priv; struct fsl_elbc_mtd *priv = chip->priv; - struct fsl_elbc_ctrl *ctrl = priv->ctrl; + struct fsl_lbc_ctrl *ctrl = priv->ctrl; struct fsl_lbc_regs __iomem *lbc = ctrl->regs; + struct fsl_elbc_fcm_ctrl *elbc_fcm_ctrl = ctrl->nand; int buf_num; - ctrl->page = page_addr; + elbc_fcm_ctrl->page = page_addr; out_be32(&lbc->fbar, page_addr >> (chip->phys_erase_shift - chip->page_shift)); @@ -185,16 +182,18 @@ static void set_addr(struct mtd_info *mtd, int column, int page_addr, int oob) buf_num = page_addr & 7; } - ctrl->addr = priv->vbase + buf_num * 1024; - ctrl->index = column; + elbc_fcm_ctrl->addr = priv->vbase + buf_num * 1024; + elbc_fcm_ctrl->index = column; /* for OOB data point to the second half of the buffer */ if (oob) - ctrl->index += priv->page_size ? 2048 : 512; + elbc_fcm_ctrl->index += priv->page_size ? 2048 : 512; - dev_vdbg(ctrl->dev, "set_addr: bank=%d, ctrl->addr=0x%p (0x%p), " + dev_vdbg(priv->dev, "set_addr: bank=%d, " + "elbc_fcm_ctrl->addr=0x%p (0x%p), " "index %x, pes %d ps %d\n", - buf_num, ctrl->addr, priv->vbase, ctrl->index, + buf_num, elbc_fcm_ctrl->addr, priv->vbase, + elbc_fcm_ctrl->index, chip->phys_erase_shift, chip->page_shift); } @@ -205,18 +204,19 @@ static int fsl_elbc_run_command(struct mtd_info *mtd) { struct nand_chip *chip = mtd->priv; struct fsl_elbc_mtd *priv = chip->priv; - struct fsl_elbc_ctrl *ctrl = priv->ctrl; + struct fsl_lbc_ctrl *ctrl = priv->ctrl; + struct fsl_elbc_fcm_ctrl *elbc_fcm_ctrl = ctrl->nand; struct fsl_lbc_regs __iomem *lbc = ctrl->regs; /* Setup the FMR[OP] to execute without write protection */ out_be32(&lbc->fmr, priv->fmr | 3); - if (ctrl->use_mdr) - out_be32(&lbc->mdr, ctrl->mdr); + if (elbc_fcm_ctrl->use_mdr) + out_be32(&lbc->mdr, elbc_fcm_ctrl->mdr); - dev_vdbg(ctrl->dev, + dev_vdbg(priv->dev, "fsl_elbc_run_command: fmr=%08x fir=%08x fcr=%08x\n", in_be32(&lbc->fmr), in_be32(&lbc->fir), in_be32(&lbc->fcr)); - dev_vdbg(ctrl->dev, + dev_vdbg(priv->dev, "fsl_elbc_run_command: fbar=%08x fpar=%08x " "fbcr=%08x bank=%d\n", in_be32(&lbc->fbar), in_be32(&lbc->fpar), @@ -229,19 +229,18 @@ static int fsl_elbc_run_command(struct mtd_info *mtd) /* wait for FCM complete flag or timeout */ wait_event_timeout(ctrl->irq_wait, ctrl->irq_status, FCM_TIMEOUT_MSECS * HZ/1000); - ctrl->status = ctrl->irq_status; - + elbc_fcm_ctrl->status = ctrl->irq_status; /* store mdr value in case it was needed */ - if (ctrl->use_mdr) - ctrl->mdr = in_be32(&lbc->mdr); + if (elbc_fcm_ctrl->use_mdr) + elbc_fcm_ctrl->mdr = in_be32(&lbc->mdr); - ctrl->use_mdr = 0; + elbc_fcm_ctrl->use_mdr = 0; - if (ctrl->status != LTESR_CC) { - dev_info(ctrl->dev, + if (elbc_fcm_ctrl->status != LTESR_CC) { + dev_info(priv->dev, "command failed: fir %x fcr %x status %x mdr %x\n", in_be32(&lbc->fir), in_be32(&lbc->fcr), - ctrl->status, ctrl->mdr); + elbc_fcm_ctrl->status, elbc_fcm_ctrl->mdr); return -EIO; } @@ -251,7 +250,7 @@ static int fsl_elbc_run_command(struct mtd_info *mtd) static void fsl_elbc_do_read(struct nand_chip *chip, int oob) { struct fsl_elbc_mtd *priv = chip->priv; - struct fsl_elbc_ctrl *ctrl = priv->ctrl; + struct fsl_lbc_ctrl *ctrl = priv->ctrl; struct fsl_lbc_regs __iomem *lbc = ctrl->regs; if (priv->page_size) { @@ -284,15 +283,16 @@ static void fsl_elbc_cmdfunc(struct mtd_info *mtd, unsigned int command, { struct nand_chip *chip = mtd->priv; struct fsl_elbc_mtd *priv = chip->priv; - struct fsl_elbc_ctrl *ctrl = priv->ctrl; + struct fsl_lbc_ctrl *ctrl = priv->ctrl; + struct fsl_elbc_fcm_ctrl *elbc_fcm_ctrl = ctrl->nand; struct fsl_lbc_regs __iomem *lbc = ctrl->regs; - ctrl->use_mdr = 0; + elbc_fcm_ctrl->use_mdr = 0; /* clear the read buffer */ - ctrl->read_bytes = 0; + elbc_fcm_ctrl->read_bytes = 0; if (command != NAND_CMD_PAGEPROG) - ctrl->index = 0; + elbc_fcm_ctrl->index = 0; switch (command) { /* READ0 and READ1 read the entire buffer to use hardware ECC. */ @@ -301,7 +301,7 @@ static void fsl_elbc_cmdfunc(struct mtd_info *mtd, unsigned int command, /* fall-through */ case NAND_CMD_READ0: - dev_dbg(ctrl->dev, + dev_dbg(priv->dev, "fsl_elbc_cmdfunc: NAND_CMD_READ0, page_addr:" " 0x%x, column: 0x%x.\n", page_addr, column); @@ -309,8 +309,8 @@ static void fsl_elbc_cmdfunc(struct mtd_info *mtd, unsigned int command, out_be32(&lbc->fbcr, 0); /* read entire page to enable ECC */ set_addr(mtd, 0, page_addr, 0); - ctrl->read_bytes = mtd->writesize + mtd->oobsize; - ctrl->index += column; + elbc_fcm_ctrl->read_bytes = mtd->writesize + mtd->oobsize; + elbc_fcm_ctrl->index += column; fsl_elbc_do_read(chip, 0); fsl_elbc_run_command(mtd); @@ -318,14 +318,14 @@ static void fsl_elbc_cmdfunc(struct mtd_info *mtd, unsigned int command, /* READOOB reads only the OOB because no ECC is performed. */ case NAND_CMD_READOOB: - dev_vdbg(ctrl->dev, + dev_vdbg(priv->dev, "fsl_elbc_cmdfunc: NAND_CMD_READOOB, page_addr:" " 0x%x, column: 0x%x.\n", page_addr, column); out_be32(&lbc->fbcr, mtd->oobsize - column); set_addr(mtd, column, page_addr, 1); - ctrl->read_bytes = mtd->writesize + mtd->oobsize; + elbc_fcm_ctrl->read_bytes = mtd->writesize + mtd->oobsize; fsl_elbc_do_read(chip, 1); fsl_elbc_run_command(mtd); @@ -333,7 +333,7 @@ static void fsl_elbc_cmdfunc(struct mtd_info *mtd, unsigned int command, /* READID must read all 5 possible bytes while CEB is active */ case NAND_CMD_READID: - dev_vdbg(ctrl->dev, "fsl_elbc_cmdfunc: NAND_CMD_READID.\n"); + dev_vdbg(priv->dev, "fsl_elbc_cmdfunc: NAND_CMD_READID.\n"); out_be32(&lbc->fir, (FIR_OP_CM0 << FIR_OP0_SHIFT) | (FIR_OP_UA << FIR_OP1_SHIFT) | @@ -341,9 +341,9 @@ static void fsl_elbc_cmdfunc(struct mtd_info *mtd, unsigned int command, out_be32(&lbc->fcr, NAND_CMD_READID << FCR_CMD0_SHIFT); /* 5 bytes for manuf, device and exts */ out_be32(&lbc->fbcr, 5); - ctrl->read_bytes = 5; - ctrl->use_mdr = 1; - ctrl->mdr = 0; + elbc_fcm_ctrl->read_bytes = 5; + elbc_fcm_ctrl->use_mdr = 1; + elbc_fcm_ctrl->mdr = 0; set_addr(mtd, 0, 0, 0); fsl_elbc_run_command(mtd); @@ -351,7 +351,7 @@ static void fsl_elbc_cmdfunc(struct mtd_info *mtd, unsigned int command, /* ERASE1 stores the block and page address */ case NAND_CMD_ERASE1: - dev_vdbg(ctrl->dev, + dev_vdbg(priv->dev, "fsl_elbc_cmdfunc: NAND_CMD_ERASE1, " "page_addr: 0x%x.\n", page_addr); set_addr(mtd, 0, page_addr, 0); @@ -359,7 +359,7 @@ static void fsl_elbc_cmdfunc(struct mtd_info *mtd, unsigned int command, /* ERASE2 uses the block and page address from ERASE1 */ case NAND_CMD_ERASE2: - dev_vdbg(ctrl->dev, "fsl_elbc_cmdfunc: NAND_CMD_ERASE2.\n"); + dev_vdbg(priv->dev, "fsl_elbc_cmdfunc: NAND_CMD_ERASE2.\n"); out_be32(&lbc->fir, (FIR_OP_CM0 << FIR_OP0_SHIFT) | @@ -374,8 +374,8 @@ static void fsl_elbc_cmdfunc(struct mtd_info *mtd, unsigned int command, (NAND_CMD_ERASE2 << FCR_CMD2_SHIFT)); out_be32(&lbc->fbcr, 0); - ctrl->read_bytes = 0; - ctrl->use_mdr = 1; + elbc_fcm_ctrl->read_bytes = 0; + elbc_fcm_ctrl->use_mdr = 1; fsl_elbc_run_command(mtd); return; @@ -383,14 +383,12 @@ static void fsl_elbc_cmdfunc(struct mtd_info *mtd, unsigned int command, /* SEQIN sets up the addr buffer and all registers except the length */ case NAND_CMD_SEQIN: { __be32 fcr; - dev_vdbg(ctrl->dev, - "fsl_elbc_cmdfunc: NAND_CMD_SEQIN/PAGE_PROG, " + dev_vdbg(priv->dev, + "fsl_elbc_cmdfunc: NAND_CMD_SEQIN/PAGE_PROG, " "page_addr: 0x%x, column: 0x%x.\n", page_addr, column); - ctrl->column = column; - ctrl->oob = 0; - ctrl->use_mdr = 1; + elbc_fcm_ctrl->use_mdr = 1; fcr = (NAND_CMD_STATUS << FCR_CMD1_SHIFT) | (NAND_CMD_SEQIN << FCR_CMD2_SHIFT) | @@ -420,7 +418,7 @@ static void fsl_elbc_cmdfunc(struct mtd_info *mtd, unsigned int command, /* OOB area --> READOOB */ column -= mtd->writesize; fcr |= NAND_CMD_READOOB << FCR_CMD0_SHIFT; - ctrl->oob = 1; + elbc_fcm_ctrl->oob = 1; } else { WARN_ON(column != 0); /* First 256 bytes --> READ0 */ @@ -429,24 +427,24 @@ static void fsl_elbc_cmdfunc(struct mtd_info *mtd, unsigned int command, } out_be32(&lbc->fcr, fcr); - set_addr(mtd, column, page_addr, ctrl->oob); + set_addr(mtd, column, page_addr, elbc_fcm_ctrl->oob); return; } /* PAGEPROG reuses all of the setup from SEQIN and adds the length */ case NAND_CMD_PAGEPROG: { int full_page; - dev_vdbg(ctrl->dev, + dev_vdbg(priv->dev, "fsl_elbc_cmdfunc: NAND_CMD_PAGEPROG " - "writing %d bytes.\n", ctrl->index); + "writing %d bytes.\n", elbc_fcm_ctrl->index); /* if the write did not start at 0 or is not a full page * then set the exact length, otherwise use a full page * write so the HW generates the ECC. */ - if (ctrl->oob || ctrl->column != 0 || - ctrl->index != mtd->writesize + mtd->oobsize) { - out_be32(&lbc->fbcr, ctrl->index); + if (elbc_fcm_ctrl->oob || elbc_fcm_ctrl->column != 0 || + elbc_fcm_ctrl->index != mtd->writesize + mtd->oobsize) { + out_be32(&lbc->fbcr, elbc_fcm_ctrl->index); full_page = 0; } else { out_be32(&lbc->fbcr, 0); @@ -458,21 +456,21 @@ static void fsl_elbc_cmdfunc(struct mtd_info *mtd, unsigned int command, /* Read back the page in order to fill in the ECC for the * caller. Is this really needed? */ - if (full_page && ctrl->oob_poi) { + if (full_page && elbc_fcm_ctrl->oob_poi) { out_be32(&lbc->fbcr, 3); set_addr(mtd, 6, page_addr, 1); - ctrl->read_bytes = mtd->writesize + 9; + elbc_fcm_ctrl->read_bytes = mtd->writesize + 9; fsl_elbc_do_read(chip, 1); fsl_elbc_run_command(mtd); - memcpy_fromio(ctrl->oob_poi + 6, - &ctrl->addr[ctrl->index], 3); - ctrl->index += 3; + memcpy_fromio(elbc_fcm_ctrl->oob_poi + 6, + &elbc_fcm_ctrl->addr[elbc_fcm_ctrl->index], 3); + elbc_fcm_ctrl->index += 3; } - ctrl->oob_poi = NULL; + elbc_fcm_ctrl->oob_poi = NULL; return; } @@ -485,26 +483,26 @@ static void fsl_elbc_cmdfunc(struct mtd_info *mtd, unsigned int command, out_be32(&lbc->fcr, NAND_CMD_STATUS << FCR_CMD0_SHIFT); out_be32(&lbc->fbcr, 1); set_addr(mtd, 0, 0, 0); - ctrl->read_bytes = 1; + elbc_fcm_ctrl->read_bytes = 1; fsl_elbc_run_command(mtd); /* The chip always seems to report that it is * write-protected, even when it is not. */ - setbits8(ctrl->addr, NAND_STATUS_WP); + setbits8(elbc_fcm_ctrl->addr, NAND_STATUS_WP); return; /* RESET without waiting for the ready line */ case NAND_CMD_RESET: - dev_dbg(ctrl->dev, "fsl_elbc_cmdfunc: NAND_CMD_RESET.\n"); + dev_dbg(priv->dev, "fsl_elbc_cmdfunc: NAND_CMD_RESET.\n"); out_be32(&lbc->fir, FIR_OP_CM0 << FIR_OP0_SHIFT); out_be32(&lbc->fcr, NAND_CMD_RESET << FCR_CMD0_SHIFT); fsl_elbc_run_command(mtd); return; default: - dev_err(ctrl->dev, + dev_err(priv->dev, "fsl_elbc_cmdfunc: error, unsupported command 0x%x.\n", command); } @@ -524,24 +522,24 @@ static void fsl_elbc_write_buf(struct mtd_info *mtd, const u8 *buf, int len) { struct nand_chip *chip = mtd->priv; struct fsl_elbc_mtd *priv = chip->priv; - struct fsl_elbc_ctrl *ctrl = priv->ctrl; + struct fsl_elbc_fcm_ctrl *elbc_fcm_ctrl = priv->ctrl->nand; unsigned int bufsize = mtd->writesize + mtd->oobsize; if (len <= 0) { - dev_err(ctrl->dev, "write_buf of %d bytes", len); - ctrl->status = 0; + dev_err(priv->dev, "write_buf of %d bytes", len); + elbc_fcm_ctrl->status = 0; return; } - if ((unsigned int)len > bufsize - ctrl->index) { - dev_err(ctrl->dev, + if ((unsigned int)len > bufsize - elbc_fcm_ctrl->index) { + dev_err(priv->dev, "write_buf beyond end of buffer " "(%d requested, %u available)\n", - len, bufsize - ctrl->index); - len = bufsize - ctrl->index; + len, bufsize - elbc_fcm_ctrl->index); + len = bufsize - elbc_fcm_ctrl->index; } - memcpy_toio(&ctrl->addr[ctrl->index], buf, len); + memcpy_toio(&elbc_fcm_ctrl->addr[elbc_fcm_ctrl->index], buf, len); /* * This is workaround for the weird elbc hangs during nand write, * Scott Wood says: "...perhaps difference in how long it takes a @@ -549,9 +547,9 @@ static void fsl_elbc_write_buf(struct mtd_info *mtd, const u8 *buf, int len) * is causing problems, and sync isn't helping for some reason." * Reading back the last byte helps though. */ - in_8(&ctrl->addr[ctrl->index] + len - 1); + in_8(&elbc_fcm_ctrl->addr[elbc_fcm_ctrl->index] + len - 1); - ctrl->index += len; + elbc_fcm_ctrl->index += len; } /* @@ -562,13 +560,13 @@ static u8 fsl_elbc_read_byte(struct mtd_info *mtd) { struct nand_chip *chip = mtd->priv; struct fsl_elbc_mtd *priv = chip->priv; - struct fsl_elbc_ctrl *ctrl = priv->ctrl; + struct fsl_elbc_fcm_ctrl *elbc_fcm_ctrl = priv->ctrl->nand; /* If there are still bytes in the FCM, then use the next byte. */ - if (ctrl->index < ctrl->read_bytes) - return in_8(&ctrl->addr[ctrl->index++]); + if (elbc_fcm_ctrl->index < elbc_fcm_ctrl->read_bytes) + return in_8(&elbc_fcm_ctrl->addr[elbc_fcm_ctrl->index++]); - dev_err(ctrl->dev, "read_byte beyond end of buffer\n"); + dev_err(priv->dev, "read_byte beyond end of buffer\n"); return ERR_BYTE; } @@ -579,18 +577,19 @@ static void fsl_elbc_read_buf(struct mtd_info *mtd, u8 *buf, int len) { struct nand_chip *chip = mtd->priv; struct fsl_elbc_mtd *priv = chip->priv; - struct fsl_elbc_ctrl *ctrl = priv->ctrl; + struct fsl_elbc_fcm_ctrl *elbc_fcm_ctrl = priv->ctrl->nand; int avail; if (len < 0) return; - avail = min((unsigned int)len, ctrl->read_bytes - ctrl->index); - memcpy_fromio(buf, &ctrl->addr[ctrl->index], avail); - ctrl->index += avail; + avail = min((unsigned int)len, + elbc_fcm_ctrl->read_bytes - elbc_fcm_ctrl->index); + memcpy_fromio(buf, &elbc_fcm_ctrl->addr[elbc_fcm_ctrl->index], avail); + elbc_fcm_ctrl->index += avail; if (len > avail) - dev_err(ctrl->dev, + dev_err(priv->dev, "read_buf beyond end of buffer " "(%d requested, %d available)\n", len, avail); @@ -603,30 +602,32 @@ static int fsl_elbc_verify_buf(struct mtd_info *mtd, const u_char *buf, int len) { struct nand_chip *chip = mtd->priv; struct fsl_elbc_mtd *priv = chip->priv; - struct fsl_elbc_ctrl *ctrl = priv->ctrl; + struct fsl_elbc_fcm_ctrl *elbc_fcm_ctrl = priv->ctrl->nand; int i; if (len < 0) { - dev_err(ctrl->dev, "write_buf of %d bytes", len); + dev_err(priv->dev, "write_buf of %d bytes", len); return -EINVAL; } - if ((unsigned int)len > ctrl->read_bytes - ctrl->index) { - dev_err(ctrl->dev, - "verify_buf beyond end of buffer " - "(%d requested, %u available)\n", - len, ctrl->read_bytes - ctrl->index); + if ((unsigned int)len > + elbc_fcm_ctrl->read_bytes - elbc_fcm_ctrl->index) { + dev_err(priv->dev, + "verify_buf beyond end of buffer " + "(%d requested, %u available)\n", + len, elbc_fcm_ctrl->read_bytes - elbc_fcm_ctrl->index); - ctrl->index = ctrl->read_bytes; + elbc_fcm_ctrl->index = elbc_fcm_ctrl->read_bytes; return -EINVAL; } for (i = 0; i < len; i++) - if (in_8(&ctrl->addr[ctrl->index + i]) != buf[i]) + if (in_8(&elbc_fcm_ctrl->addr[elbc_fcm_ctrl->index + i]) + != buf[i]) break; - ctrl->index += len; - return i == len && ctrl->status == LTESR_CC ? 0 : -EIO; + elbc_fcm_ctrl->index += len; + return i == len && elbc_fcm_ctrl->status == LTESR_CC ? 0 : -EIO; } /* This function is called after Program and Erase Operations to @@ -635,22 +636,22 @@ static int fsl_elbc_verify_buf(struct mtd_info *mtd, const u_char *buf, int len) static int fsl_elbc_wait(struct mtd_info *mtd, struct nand_chip *chip) { struct fsl_elbc_mtd *priv = chip->priv; - struct fsl_elbc_ctrl *ctrl = priv->ctrl; + struct fsl_elbc_fcm_ctrl *elbc_fcm_ctrl = priv->ctrl->nand; - if (ctrl->status != LTESR_CC) + if (elbc_fcm_ctrl->status != LTESR_CC) return NAND_STATUS_FAIL; /* The chip always seems to report that it is * write-protected, even when it is not. */ - return (ctrl->mdr & 0xff) | NAND_STATUS_WP; + return (elbc_fcm_ctrl->mdr & 0xff) | NAND_STATUS_WP; } static int fsl_elbc_chip_init_tail(struct mtd_info *mtd) { struct nand_chip *chip = mtd->priv; struct fsl_elbc_mtd *priv = chip->priv; - struct fsl_elbc_ctrl *ctrl = priv->ctrl; + struct fsl_lbc_ctrl *ctrl = priv->ctrl; struct fsl_lbc_regs __iomem *lbc = ctrl->regs; unsigned int al; @@ -665,41 +666,41 @@ static int fsl_elbc_chip_init_tail(struct mtd_info *mtd) priv->fmr |= (12 << FMR_CWTO_SHIFT) | /* Timeout > 12 ms */ (al << FMR_AL_SHIFT); - dev_dbg(ctrl->dev, "fsl_elbc_init: nand->numchips = %d\n", + dev_dbg(priv->dev, "fsl_elbc_init: nand->numchips = %d\n", chip->numchips); - dev_dbg(ctrl->dev, "fsl_elbc_init: nand->chipsize = %lld\n", + dev_dbg(priv->dev, "fsl_elbc_init: nand->chipsize = %lld\n", chip->chipsize); - dev_dbg(ctrl->dev, "fsl_elbc_init: nand->pagemask = %8x\n", + dev_dbg(priv->dev, "fsl_elbc_init: nand->pagemask = %8x\n", chip->pagemask); - dev_dbg(ctrl->dev, "fsl_elbc_init: nand->chip_delay = %d\n", + dev_dbg(priv->dev, "fsl_elbc_init: nand->chip_delay = %d\n", chip->chip_delay); - dev_dbg(ctrl->dev, "fsl_elbc_init: nand->badblockpos = %d\n", + dev_dbg(priv->dev, "fsl_elbc_init: nand->badblockpos = %d\n", chip->badblockpos); - dev_dbg(ctrl->dev, "fsl_elbc_init: nand->chip_shift = %d\n", + dev_dbg(priv->dev, "fsl_elbc_init: nand->chip_shift = %d\n", chip->chip_shift); - dev_dbg(ctrl->dev, "fsl_elbc_init: nand->page_shift = %d\n", + dev_dbg(priv->dev, "fsl_elbc_init: nand->page_shift = %d\n", chip->page_shift); - dev_dbg(ctrl->dev, "fsl_elbc_init: nand->phys_erase_shift = %d\n", + dev_dbg(priv->dev, "fsl_elbc_init: nand->phys_erase_shift = %d\n", chip->phys_erase_shift); - dev_dbg(ctrl->dev, "fsl_elbc_init: nand->ecclayout = %p\n", + dev_dbg(priv->dev, "fsl_elbc_init: nand->ecclayout = %p\n", chip->ecclayout); - dev_dbg(ctrl->dev, "fsl_elbc_init: nand->ecc.mode = %d\n", + dev_dbg(priv->dev, "fsl_elbc_init: nand->ecc.mode = %d\n", chip->ecc.mode); - dev_dbg(ctrl->dev, "fsl_elbc_init: nand->ecc.steps = %d\n", + dev_dbg(priv->dev, "fsl_elbc_init: nand->ecc.steps = %d\n", chip->ecc.steps); - dev_dbg(ctrl->dev, "fsl_elbc_init: nand->ecc.bytes = %d\n", + dev_dbg(priv->dev, "fsl_elbc_init: nand->ecc.bytes = %d\n", chip->ecc.bytes); - dev_dbg(ctrl->dev, "fsl_elbc_init: nand->ecc.total = %d\n", + dev_dbg(priv->dev, "fsl_elbc_init: nand->ecc.total = %d\n", chip->ecc.total); - dev_dbg(ctrl->dev, "fsl_elbc_init: nand->ecc.layout = %p\n", + dev_dbg(priv->dev, "fsl_elbc_init: nand->ecc.layout = %p\n", chip->ecc.layout); - dev_dbg(ctrl->dev, "fsl_elbc_init: mtd->flags = %08x\n", mtd->flags); - dev_dbg(ctrl->dev, "fsl_elbc_init: mtd->size = %lld\n", mtd->size); - dev_dbg(ctrl->dev, "fsl_elbc_init: mtd->erasesize = %d\n", + dev_dbg(priv->dev, "fsl_elbc_init: mtd->flags = %08x\n", mtd->flags); + dev_dbg(priv->dev, "fsl_elbc_init: mtd->size = %lld\n", mtd->size); + dev_dbg(priv->dev, "fsl_elbc_init: mtd->erasesize = %d\n", mtd->erasesize); - dev_dbg(ctrl->dev, "fsl_elbc_init: mtd->writesize = %d\n", + dev_dbg(priv->dev, "fsl_elbc_init: mtd->writesize = %d\n", mtd->writesize); - dev_dbg(ctrl->dev, "fsl_elbc_init: mtd->oobsize = %d\n", + dev_dbg(priv->dev, "fsl_elbc_init: mtd->oobsize = %d\n", mtd->oobsize); /* adjust Option Register and ECC to match Flash page size */ @@ -719,7 +720,7 @@ static int fsl_elbc_chip_init_tail(struct mtd_info *mtd) chip->badblock_pattern = &largepage_memorybased; } } else { - dev_err(ctrl->dev, + dev_err(priv->dev, "fsl_elbc_init: page size %d is not supported\n", mtd->writesize); return -1; @@ -750,18 +751,19 @@ static void fsl_elbc_write_page(struct mtd_info *mtd, const uint8_t *buf) { struct fsl_elbc_mtd *priv = chip->priv; - struct fsl_elbc_ctrl *ctrl = priv->ctrl; + struct fsl_elbc_fcm_ctrl *elbc_fcm_ctrl = priv->ctrl->nand; fsl_elbc_write_buf(mtd, buf, mtd->writesize); fsl_elbc_write_buf(mtd, chip->oob_poi, mtd->oobsize); - ctrl->oob_poi = chip->oob_poi; + elbc_fcm_ctrl->oob_poi = chip->oob_poi; } static int fsl_elbc_chip_init(struct fsl_elbc_mtd *priv) { - struct fsl_elbc_ctrl *ctrl = priv->ctrl; + struct fsl_lbc_ctrl *ctrl = priv->ctrl; struct fsl_lbc_regs __iomem *lbc = ctrl->regs; + struct fsl_elbc_fcm_ctrl *elbc_fcm_ctrl = ctrl->nand; struct nand_chip *chip = &priv->chip; dev_dbg(priv->dev, "eLBC Set Information for bank %d\n", priv->bank); @@ -790,7 +792,7 @@ static int fsl_elbc_chip_init(struct fsl_elbc_mtd *priv) chip->options = NAND_NO_READRDY | NAND_NO_AUTOINCR | NAND_USE_FLASH_BBT; - chip->controller = &ctrl->controller; + chip->controller = &elbc_fcm_ctrl->controller; chip->priv = priv; chip->ecc.read_page = fsl_elbc_read_page; @@ -815,8 +817,7 @@ static int fsl_elbc_chip_init(struct fsl_elbc_mtd *priv) static int fsl_elbc_chip_remove(struct fsl_elbc_mtd *priv) { - struct fsl_elbc_ctrl *ctrl = priv->ctrl; - + struct fsl_elbc_fcm_ctrl *elbc_fcm_ctrl = priv->ctrl->nand; nand_release(&priv->mtd); kfree(priv->mtd.name); @@ -824,18 +825,21 @@ static int fsl_elbc_chip_remove(struct fsl_elbc_mtd *priv) if (priv->vbase) iounmap(priv->vbase); - ctrl->chips[priv->bank] = NULL; + elbc_fcm_ctrl->chips[priv->bank] = NULL; kfree(priv); - + kfree(elbc_fcm_ctrl); return 0; } -static int __devinit fsl_elbc_chip_probe(struct fsl_elbc_ctrl *ctrl, - struct device_node *node) +static DEFINE_MUTEX(fsl_elbc_nand_mutex); + +static int __devinit fsl_elbc_nand_probe(struct platform_device *pdev) { - struct fsl_lbc_regs __iomem *lbc = ctrl->regs; + struct fsl_lbc_regs __iomem *lbc; struct fsl_elbc_mtd *priv; struct resource res; + struct fsl_elbc_fcm_ctrl *elbc_fcm_ctrl; + #ifdef CONFIG_MTD_PARTITIONS static const char *part_probe_types[] = { "cmdlinepart", "RedBoot", NULL }; @@ -843,11 +847,18 @@ static int __devinit fsl_elbc_chip_probe(struct fsl_elbc_ctrl *ctrl, #endif int ret; int bank; + struct device *dev; + struct device_node *node = pdev->dev.of_node; + + if (!fsl_lbc_ctrl_dev || !fsl_lbc_ctrl_dev->regs) + return -ENODEV; + lbc = fsl_lbc_ctrl_dev->regs; + dev = fsl_lbc_ctrl_dev->dev; /* get, allocate and map the memory resource */ ret = of_address_to_resource(node, 0, &res); if (ret) { - dev_err(ctrl->dev, "failed to get resource\n"); + dev_err(dev, "failed to get resource\n"); return ret; } @@ -861,7 +872,7 @@ static int __devinit fsl_elbc_chip_probe(struct fsl_elbc_ctrl *ctrl, break; if (bank >= MAX_BANKS) { - dev_err(ctrl->dev, "address did not match any chip selects\n"); + dev_err(dev, "address did not match any chip selects\n"); return -ENODEV; } @@ -869,14 +880,33 @@ static int __devinit fsl_elbc_chip_probe(struct fsl_elbc_ctrl *ctrl, if (!priv) return -ENOMEM; - ctrl->chips[bank] = priv; + mutex_lock(&fsl_elbc_nand_mutex); + if (!fsl_lbc_ctrl_dev->nand) { + elbc_fcm_ctrl = kzalloc(sizeof(*elbc_fcm_ctrl), GFP_KERNEL); + if (!elbc_fcm_ctrl) { + dev_err(dev, "failed to allocate memory\n"); + mutex_unlock(&fsl_elbc_nand_mutex); + ret = -ENOMEM; + goto err; + } + elbc_fcm_ctrl->counter++; + + spin_lock_init(&elbc_fcm_ctrl->controller.lock); + init_waitqueue_head(&elbc_fcm_ctrl->controller.wq); + fsl_lbc_ctrl_dev->nand = elbc_fcm_ctrl; + } else { + elbc_fcm_ctrl = fsl_lbc_ctrl_dev->nand; + } + mutex_unlock(&fsl_elbc_nand_mutex); + + elbc_fcm_ctrl->chips[bank] = priv; priv->bank = bank; - priv->ctrl = ctrl; - priv->dev = ctrl->dev; + priv->ctrl = fsl_lbc_ctrl_dev; + priv->dev = dev; priv->vbase = ioremap(res.start, resource_size(&res)); if (!priv->vbase) { - dev_err(ctrl->dev, "failed to map chip region\n"); + dev_err(dev, "failed to map chip region\n"); ret = -ENOMEM; goto err; } @@ -933,171 +963,53 @@ err: return ret; } -static int __devinit fsl_elbc_ctrl_init(struct fsl_elbc_ctrl *ctrl) +static int fsl_elbc_nand_remove(struct platform_device *pdev) { - struct fsl_lbc_regs __iomem *lbc = ctrl->regs; - - /* - * NAND transactions can tie up the bus for a long time, so set the - * bus timeout to max by clearing LBCR[BMT] (highest base counter - * value) and setting LBCR[BMTPS] to the highest prescaler value. - */ - clrsetbits_be32(&lbc->lbcr, LBCR_BMT, 15); - - /* clear event registers */ - setbits32(&lbc->ltesr, LTESR_NAND_MASK); - out_be32(&lbc->lteatr, 0); - - /* Enable interrupts for any detected events */ - out_be32(&lbc->lteir, LTESR_NAND_MASK); - - ctrl->read_bytes = 0; - ctrl->index = 0; - ctrl->addr = NULL; - - return 0; -} - -static int fsl_elbc_ctrl_remove(struct platform_device *ofdev) -{ - struct fsl_elbc_ctrl *ctrl = dev_get_drvdata(&ofdev->dev); int i; - + struct fsl_elbc_fcm_ctrl *elbc_fcm_ctrl = fsl_lbc_ctrl_dev->nand; for (i = 0; i < MAX_BANKS; i++) - if (ctrl->chips[i]) - fsl_elbc_chip_remove(ctrl->chips[i]); - - if (ctrl->irq) - free_irq(ctrl->irq, ctrl); - - if (ctrl->regs) - iounmap(ctrl->regs); - - dev_set_drvdata(&ofdev->dev, NULL); - kfree(ctrl); - return 0; -} - -/* NOTE: This interrupt is also used to report other localbus events, - * such as transaction errors on other chipselects. If we want to - * capture those, we'll need to move the IRQ code into a shared - * LBC driver. - */ - -static irqreturn_t fsl_elbc_ctrl_irq(int irqno, void *data) -{ - struct fsl_elbc_ctrl *ctrl = data; - struct fsl_lbc_regs __iomem *lbc = ctrl->regs; - __be32 status = in_be32(&lbc->ltesr) & LTESR_NAND_MASK; - - if (status) { - out_be32(&lbc->ltesr, status); - out_be32(&lbc->lteatr, 0); - - ctrl->irq_status = status; - smp_wmb(); - wake_up(&ctrl->irq_wait); - - return IRQ_HANDLED; + if (elbc_fcm_ctrl->chips[i]) + fsl_elbc_chip_remove(elbc_fcm_ctrl->chips[i]); + + mutex_lock(&fsl_elbc_nand_mutex); + elbc_fcm_ctrl->counter--; + if (!elbc_fcm_ctrl->counter) { + fsl_lbc_ctrl_dev->nand = NULL; + kfree(elbc_fcm_ctrl); } - - return IRQ_NONE; -} - -/* fsl_elbc_ctrl_probe - * - * called by device layer when it finds a device matching - * one our driver can handled. This code allocates all of - * the resources needed for the controller only. The - * resources for the NAND banks themselves are allocated - * in the chip probe function. -*/ - -static int __devinit fsl_elbc_ctrl_probe(struct platform_device *ofdev, - const struct of_device_id *match) -{ - struct device_node *child; - struct fsl_elbc_ctrl *ctrl; - int ret; - - ctrl = kzalloc(sizeof(*ctrl), GFP_KERNEL); - if (!ctrl) - return -ENOMEM; - - dev_set_drvdata(&ofdev->dev, ctrl); - - spin_lock_init(&ctrl->controller.lock); - init_waitqueue_head(&ctrl->controller.wq); - init_waitqueue_head(&ctrl->irq_wait); - - ctrl->regs = of_iomap(ofdev->dev.of_node, 0); - if (!ctrl->regs) { - dev_err(&ofdev->dev, "failed to get memory region\n"); - ret = -ENODEV; - goto err; - } - - ctrl->irq = of_irq_to_resource(ofdev->dev.of_node, 0, NULL); - if (ctrl->irq == NO_IRQ) { - dev_err(&ofdev->dev, "failed to get irq resource\n"); - ret = -ENODEV; - goto err; - } - - ctrl->dev = &ofdev->dev; - - ret = fsl_elbc_ctrl_init(ctrl); - if (ret < 0) - goto err; - - ret = request_irq(ctrl->irq, fsl_elbc_ctrl_irq, 0, "fsl-elbc", ctrl); - if (ret != 0) { - dev_err(&ofdev->dev, "failed to install irq (%d)\n", - ctrl->irq); - ret = ctrl->irq; - goto err; - } - - for_each_child_of_node(ofdev->dev.of_node, child) - if (of_device_is_compatible(child, "fsl,elbc-fcm-nand")) - fsl_elbc_chip_probe(ctrl, child); + mutex_unlock(&fsl_elbc_nand_mutex); return 0; -err: - fsl_elbc_ctrl_remove(ofdev); - return ret; } -static const struct of_device_id fsl_elbc_match[] = { - { - .compatible = "fsl,elbc", - }, +static const struct of_device_id fsl_elbc_nand_match[] = { + { .compatible = "fsl,elbc-fcm-nand", }, {} }; -static struct of_platform_driver fsl_elbc_ctrl_driver = { +static struct platform_driver fsl_elbc_nand_driver = { .driver = { - .name = "fsl-elbc", + .name = "fsl,elbc-fcm-nand", .owner = THIS_MODULE, - .of_match_table = fsl_elbc_match, + .of_match_table = fsl_elbc_nand_match, }, - .probe = fsl_elbc_ctrl_probe, - .remove = fsl_elbc_ctrl_remove, + .probe = fsl_elbc_nand_probe, + .remove = fsl_elbc_nand_remove, }; -static int __init fsl_elbc_init(void) +static int __init fsl_elbc_nand_init(void) { - return of_register_platform_driver(&fsl_elbc_ctrl_driver); + return platform_driver_register(&fsl_elbc_nand_driver); } -static void __exit fsl_elbc_exit(void) +static void __exit fsl_elbc_nand_exit(void) { - of_unregister_platform_driver(&fsl_elbc_ctrl_driver); + platform_driver_unregister(&fsl_elbc_nand_driver); } -module_init(fsl_elbc_init); -module_exit(fsl_elbc_exit); +module_init(fsl_elbc_nand_init); +module_exit(fsl_elbc_nand_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Freescale"); -- cgit v1.2.3 From 0b824d2b10eacd496c608a7c41a046862d48563b Mon Sep 17 00:00:00 2001 From: Lan Chunhe-B25806 Date: Mon, 18 Oct 2010 15:22:32 +0800 Subject: P4080/mtd: Fix the freescale lbc issue with 36bit mode When system uses 36bit physical address, res.start is 36bit physical address. But the function of in_be32 returns 32bit physical address. Then both of them compared each other is wrong. So by converting the address of res.start into the right format fixes this issue. Signed-off-by: Lan Chunhe-B25806 Signed-off-by: Roy Zang Reviewed-by: Anton Vorontsov Signed-off-by: David Woodhouse --- arch/powerpc/include/asm/fsl_lbc.h | 1 + arch/powerpc/sysdev/fsl_lbc.c | 23 ++++++++++++++++++++++- drivers/mtd/nand/fsl_elbc_nand.c | 2 +- 3 files changed, 24 insertions(+), 2 deletions(-) (limited to 'arch') diff --git a/arch/powerpc/include/asm/fsl_lbc.h b/arch/powerpc/include/asm/fsl_lbc.h index 06a11124dde1..5c1bf3466749 100644 --- a/arch/powerpc/include/asm/fsl_lbc.h +++ b/arch/powerpc/include/asm/fsl_lbc.h @@ -248,6 +248,7 @@ struct fsl_upm { int width; }; +extern u32 fsl_lbc_addr(phys_addr_t addr_base); extern int fsl_lbc_find(phys_addr_t addr_base); extern int fsl_upm_find(phys_addr_t addr_base, struct fsl_upm *upm); diff --git a/arch/powerpc/sysdev/fsl_lbc.c b/arch/powerpc/sysdev/fsl_lbc.c index 91c9c53b1845..4fcb5a4e60dd 100644 --- a/arch/powerpc/sysdev/fsl_lbc.c +++ b/arch/powerpc/sysdev/fsl_lbc.c @@ -33,6 +33,27 @@ static spinlock_t fsl_lbc_lock = __SPIN_LOCK_UNLOCKED(fsl_lbc_lock); struct fsl_lbc_ctrl *fsl_lbc_ctrl_dev; EXPORT_SYMBOL(fsl_lbc_ctrl_dev); +/** + * fsl_lbc_addr - convert the base address + * @addr_base: base address of the memory bank + * + * This function converts a base address of lbc into the right format for the + * BR register. If the SOC has eLBC then it returns 32bit physical address + * else it convers a 34bit local bus physical address to correct format of + * 32bit address for BR register (Example: MPC8641). + */ +u32 fsl_lbc_addr(phys_addr_t addr_base) +{ + struct device_node *np = fsl_lbc_ctrl_dev->dev->of_node; + u32 addr = addr_base & 0xffff8000; + + if (of_device_is_compatible(np, "fsl,elbc")) + return addr; + + return addr | ((addr_base & 0x300000000ull) >> 19); +} +EXPORT_SYMBOL(fsl_lbc_addr); + /** * fsl_lbc_find - find Localbus bank * @addr_base: base address of the memory bank @@ -55,7 +76,7 @@ int fsl_lbc_find(phys_addr_t addr_base) __be32 br = in_be32(&lbc->bank[i].br); __be32 or = in_be32(&lbc->bank[i].or); - if (br & BR_V && (br & or & BR_BA) == addr_base) + if (br & BR_V && (br & or & BR_BA) == fsl_lbc_addr(addr_base)) return i; } diff --git a/drivers/mtd/nand/fsl_elbc_nand.c b/drivers/mtd/nand/fsl_elbc_nand.c index cd8de29e38cc..c141b07b25d1 100644 --- a/drivers/mtd/nand/fsl_elbc_nand.c +++ b/drivers/mtd/nand/fsl_elbc_nand.c @@ -868,7 +868,7 @@ static int __devinit fsl_elbc_nand_probe(struct platform_device *pdev) (in_be32(&lbc->bank[bank].br) & BR_MSEL) == BR_MS_FCM && (in_be32(&lbc->bank[bank].br) & in_be32(&lbc->bank[bank].or) & BR_BA) - == res.start) + == fsl_lbc_addr(res.start)) break; if (bank >= MAX_BANKS) { -- cgit v1.2.3