diff options
author | Quinn Jensen <quinn.jensen@freescale.com> | 2007-10-24 21:17:08 -0600 |
---|---|---|
committer | Quinn Jensen <quinn.jensen@freescale.com> | 2007-10-24 21:17:08 -0600 |
commit | ea2b7e75d21a9c89fd599aa61033efc8f1cd1b0e (patch) | |
tree | 1ba7cd19863fbbcc7100529d53b9e6cd52ec383b | |
parent | 79ad4fac806a55852048b33e97abb01525416a8f (diff) |
This patch adds mtd NOR and NAND capability to the linux 2.6.22 kernel
for MX platforms.
http://www.bitshrine.org/gpp/linux-2.6.22-mx-drivers_mtd.patch
-rw-r--r-- | drivers/mtd/chips/cfi_probe.c | 26 | ||||
-rw-r--r-- | drivers/mtd/chips/cfi_util.c | 13 | ||||
-rw-r--r-- | drivers/mtd/maps/Kconfig | 10 | ||||
-rw-r--r-- | drivers/mtd/maps/Makefile | 1 | ||||
-rw-r--r-- | drivers/mtd/maps/mxc_nor.c | 181 | ||||
-rw-r--r-- | drivers/mtd/nand/Kconfig | 43 | ||||
-rw-r--r-- | drivers/mtd/nand/Makefile | 2 | ||||
-rw-r--r-- | drivers/mtd/nand/mxc_nd.c | 1346 | ||||
-rw-r--r-- | drivers/mtd/nand/mxc_nd.h | 112 | ||||
-rw-r--r-- | drivers/mtd/nand/mxc_nd2.c | 1329 | ||||
-rw-r--r-- | include/linux/mtd/nand.h | 3 |
11 files changed, 3054 insertions, 12 deletions
diff --git a/drivers/mtd/chips/cfi_probe.c b/drivers/mtd/chips/cfi_probe.c index 60e11a0ada97..df2e4bc80a89 100644 --- a/drivers/mtd/chips/cfi_probe.c +++ b/drivers/mtd/chips/cfi_probe.c @@ -27,7 +27,8 @@ static void print_cfi_ident(struct cfi_ident *); static int cfi_probe_chip(struct map_info *map, __u32 base, unsigned long *chip_map, struct cfi_private *cfi); -static int cfi_chip_setup(struct map_info *map, struct cfi_private *cfi); +static int cfi_chip_setup(struct map_info *map, struct cfi_private *cfi, + int amd555); struct mtd_info *cfi_probe(struct map_info *map); @@ -50,12 +51,12 @@ do { \ xip_allowed(base, map); \ } while (0) -#define xip_disable_qry(base, map, cfi) \ +#define xip_disable_qry(base, map, cfi, amd555) \ do { \ xip_disable(); \ cfi_send_gen_cmd(0xF0, 0, base, map, cfi, cfi->device_type, NULL); \ cfi_send_gen_cmd(0xFF, 0, base, map, cfi, cfi->device_type, NULL); \ - cfi_send_gen_cmd(0x98, 0x55, base, map, cfi, cfi->device_type, NULL); \ + cfi_send_gen_cmd(0x98, amd555 ? 0x555 : 0x55, base, map, cfi, cfi->device_type, NULL);\ } while (0) #else @@ -63,7 +64,7 @@ do { \ #define xip_disable() do { } while (0) #define xip_allowed(base, map) do { } while (0) #define xip_enable(base, map, cfi) do { } while (0) -#define xip_disable_qry(base, map, cfi) do { } while (0) +#define xip_disable_qry(base, map, cfi, amd555) do { } while (0) #endif @@ -102,6 +103,7 @@ static int __xipram cfi_probe_chip(struct map_info *map, __u32 base, unsigned long *chip_map, struct cfi_private *cfi) { int i; + int amd555 = 0; if ((base + 0) >= map->size) { printk(KERN_NOTICE @@ -122,14 +124,20 @@ static int __xipram cfi_probe_chip(struct map_info *map, __u32 base, cfi_send_gen_cmd(0x98, 0x55, base, map, cfi, cfi->device_type, NULL); if (!qry_present(map,base,cfi)) { - xip_enable(base, map, cfi); - return 0; + cfi_send_gen_cmd(0x98, 0x555, base, map, cfi, cfi->device_type, + NULL); + if (!qry_present(map,base,cfi)) { + xip_enable(base, map, cfi); + return 0; + } + + amd555 = 1; } if (!cfi->numchips) { /* This is the first time we're called. Set up the CFI stuff accordingly and return */ - return cfi_chip_setup(map, cfi); + return cfi_chip_setup(map, cfi, amd555); } /* Check each previous chip to see if it's an alias */ @@ -189,7 +197,7 @@ static int __xipram cfi_probe_chip(struct map_info *map, __u32 base, } static int __xipram cfi_chip_setup(struct map_info *map, - struct cfi_private *cfi) + struct cfi_private *cfi, int amd555) { int ofs_factor = cfi->interleave*cfi->device_type; __u32 base = 0; @@ -214,7 +222,7 @@ static int __xipram cfi_chip_setup(struct map_info *map, cfi->cfi_mode = CFI_MODE_CFI; /* Read the CFI info structure */ - xip_disable_qry(base, map, cfi); + xip_disable_qry(base, map, cfi, amd555); for (i=0; i<(sizeof(struct cfi_ident) + num_erase_regions * 4); i++) ((unsigned char *)cfi->cfiq)[i] = cfi_read_query(map,base + (0x10 + i)*ofs_factor); diff --git a/drivers/mtd/chips/cfi_util.c b/drivers/mtd/chips/cfi_util.c index 2e51496c248e..01c8ebff35f5 100644 --- a/drivers/mtd/chips/cfi_util.c +++ b/drivers/mtd/chips/cfi_util.c @@ -50,9 +50,16 @@ __xipram cfi_read_pri(struct map_info *map, __u16 adr, __u16 size, const char* n local_irq_disable(); #endif - /* Switch it into Query Mode */ - cfi_send_gen_cmd(0x98, 0x55, base, map, cfi, cfi->device_type, NULL); - + /* Switch it into Query Mode. Some chips want address 0x55, some + * want 0x555. + */ + if ((cfi->mfr == CFI_MFR_AMD) && (cfi->id == 0x227E)) + cfi_send_gen_cmd(0x98, 0x555, base, map, cfi, cfi->device_type, + NULL); + else + cfi_send_gen_cmd(0x98, 0x55, base, map, cfi, cfi->device_type, + NULL); + /* Read in the Extended Query Table */ for (i=0; i<size; i++) { ((unsigned char *)extp)[i] = diff --git a/drivers/mtd/maps/Kconfig b/drivers/mtd/maps/Kconfig index b665e4ac2208..92fe291725dc 100644 --- a/drivers/mtd/maps/Kconfig +++ b/drivers/mtd/maps/Kconfig @@ -620,5 +620,15 @@ config MTD_PLATRAM This selection automatically selects the map_ram driver. +config MTD_MXC + bool "Map driver for Freescale MXC boards" + depends on MTD && ARCH_MXC + default y + select MTD_CFI + select MTD_PARTITIONS + help + This enables access to the flash chips on Freescale MXC based + platforms. If you have such a board, say 'Y'. + endmenu diff --git a/drivers/mtd/maps/Makefile b/drivers/mtd/maps/Makefile index 3acbb5d01ca4..661f2064c23d 100644 --- a/drivers/mtd/maps/Makefile +++ b/drivers/mtd/maps/Makefile @@ -72,3 +72,4 @@ obj-$(CONFIG_MTD_PLATRAM) += plat-ram.o obj-$(CONFIG_MTD_OMAP_NOR) += omap_nor.o obj-$(CONFIG_MTD_MTX1) += mtx-1_flash.o obj-$(CONFIG_MTD_TQM834x) += tqm834x.o +obj-$(CONFIG_MTD_MXC) += mxc_nor.o diff --git a/drivers/mtd/maps/mxc_nor.c b/drivers/mtd/maps/mxc_nor.c new file mode 100644 index 000000000000..6ffe33944c94 --- /dev/null +++ b/drivers/mtd/maps/mxc_nor.c @@ -0,0 +1,181 @@ +/* + * Copyright 2004-2007 Freescale Semiconductor, Inc. All Rights Reserved. + * (c) 2005 MontaVista Software, Inc. + */ + +/* + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +#include <linux/module.h> +#include <linux/types.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/platform_device.h> +#include <linux/err.h> +#include <linux/ioport.h> +#include <linux/mtd/mtd.h> +#include <linux/mtd/map.h> +#include <linux/mtd/partitions.h> +#include <asm/mach-types.h> +#include <asm/mach/flash.h> + +#define DVR_VER "2.0" + +#ifdef CONFIG_MTD_PARTITIONS +static const char *part_probes[] = { "RedBoot", "cmdlinepart", NULL }; +#endif + +struct mxcflash_info { + struct mtd_partition *parts; + struct mtd_info *mtd; + struct map_info map; +}; + +/*! + * @defgroup NOR_MTD NOR Flash MTD Driver + */ + +/*! + * @file mxc_nor.c + * + * @brief This file contains the MTD Mapping information on the MXC. + * + * @ingroup NOR_MTD + */ + +static int __devinit mxcflash_probe(struct platform_device *pdev) +{ + int err, nr_parts = 0; + struct mxcflash_info *info; + struct flash_platform_data *flash = pdev->dev.platform_data; + struct resource *res = pdev->resource; + unsigned long size = res->end - res->start + 1; + + info = kmalloc(sizeof(struct mxcflash_info), GFP_KERNEL); + if (!info) + return -ENOMEM; + + memset(info, 0, sizeof(struct mxcflash_info)); + + if (!request_mem_region(res->start, size, "flash")) { + err = -EBUSY; + goto out_free_info; + } + info->map.virt = ioremap(res->start, size); + if (!info->map.virt) { + err = -ENOMEM; + goto out_release_mem_region; + } + info->map.name = pdev->dev.bus_id; + info->map.phys = res->start; + info->map.size = size; + info->map.bankwidth = flash->width; + + simple_map_init(&info->map); + info->mtd = do_map_probe(flash->map_name, &info->map); + if (!info->mtd) { + err = -EIO; + goto out_iounmap; + } + info->mtd->owner = THIS_MODULE; + +#ifdef CONFIG_MTD_PARTITIONS + nr_parts = + parse_mtd_partitions(info->mtd, part_probes, &info->parts, 0); + if (nr_parts > 0) { + add_mtd_partitions(info->mtd, info->parts, nr_parts); + } else if (nr_parts < 0 && flash->parts) { + add_mtd_partitions(info->mtd, flash->parts, flash->nr_parts); + } else +#endif + { + printk(KERN_NOTICE "MXC flash: no partition info " + "available, registering whole flash\n"); + add_mtd_device(info->mtd); + } + + platform_set_drvdata(pdev, info); + return 0; + + out_iounmap: + iounmap(info->map.virt); + out_release_mem_region: + release_mem_region(res->start, size); + out_free_info: + kfree(info); + + return err; +} + +static int __devexit mxcflash_remove(struct platform_device *pdev) +{ + + struct mxcflash_info *info = platform_get_drvdata(pdev); + struct flash_platform_data *flash = pdev->dev.platform_data; + + platform_set_drvdata(pdev, NULL); + + if (info) { + if (info->parts) { + del_mtd_partitions(info->mtd); + kfree(info->parts); + } else if (flash->parts) + del_mtd_partitions(info->mtd); + else + del_mtd_device(info->mtd); + + map_destroy(info->mtd); + release_mem_region(info->map.phys, info->map.size); + iounmap((void __iomem *)info->map.virt); + kfree(info); + } + return 0; +} + +static struct platform_driver mxcflash_driver = { + .driver = { + .name = "mxc_nor_flash", + }, + .probe = mxcflash_probe, + .remove = __devexit_p(mxcflash_remove), +}; + +/*! + * This is the module's entry function. It passes board specific + * config details into the MTD physmap driver which then does the + * real work for us. After this function runs, our job is done. + * + * @return 0 if successful; non-zero otherwise + */ +static int __init mxc_mtd_init(void) +{ + pr_info("MXC MTD nor Driver %s\n", DVR_VER); + if (platform_driver_register(&mxcflash_driver) != 0) { + printk(KERN_ERR "Driver register failed for mxcflash_driver\n"); + return -ENODEV; + } + return 0; +} + +/*! + * This function is the module's exit function. It's empty because the + * MTD physmap driver is doing the real work and our job was done after + * mxc_mtd_init() runs. + */ +static void __exit mxc_mtd_exit(void) +{ + platform_driver_unregister(&mxcflash_driver); +} + +module_init(mxc_mtd_init); +module_exit(mxc_mtd_exit); + +MODULE_AUTHOR("Freescale Semiconductor, Inc."); +MODULE_DESCRIPTION("MTD map and partitions for Freescale MXC boards"); +MODULE_LICENSE("GPL"); diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig index f1d60b6f048e..6045e1c7076f 100644 --- a/drivers/mtd/nand/Kconfig +++ b/drivers/mtd/nand/Kconfig @@ -272,6 +272,49 @@ config MTD_NAND_NANDSIM The simulator may simulate various NAND flash chips for the MTD nand layer. +config MTD_NAND_MXC + tristate "MXC NAND support" + depends on MTD_NAND && ARCH_MXC_HAS_NFC_V1 + help + This enables the driver for the NAND flash controller on the + MXC processors. + +config MTD_NAND_MXC_V2 + tristate "MXC NAND Version 2 support" + depends on MTD_NAND && ARCH_MXC_HAS_NFC_V2 + help + This enables the driver for the version 2 of NAND flash controller + on the MXC processors. + +config MTD_NAND_MXC_SWECC + bool "Software ECC support " + depends on MTD_NAND_MXC || MTD_NAND_MXC_V2 + help + This enables the support for Software ECC handling. By + default MXC NAND controller Hardware ECC is supported. + + +config MTD_NAND_MXC_FORCE_CE + bool "NAND chip select operation support" + depends on MTD_NAND_MXC || MTD_NAND_MXC_V2 + help + This enables the NAND chip select by using CE control line. By + default CE operation is disabled. + +config MTD_NAND_MXC_ECC_CORRECTION_OPTION2 + bool "ECC correction in S/W" + depends on MTD_NAND_MXC + help + This enables the Option2 NFC ECC correction in software. By + default Option 1 is selected. Enable if you need option2 ECC correction. + +config MXC_NAND_LOW_LEVEL_ERASE + bool "Low level NAND erase" + depends on MTD_NAND_MXC || MTD_NAND_MXC_V2 + help + This enables the erase of whole NAND flash. By + default low level erase operation is disabled. + config MTD_NAND_PLATFORM tristate "Support for generic platform NAND driver" depends on MTD_NAND diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile index edba1db14bfa..5035adbc0e73 100644 --- a/drivers/mtd/nand/Makefile +++ b/drivers/mtd/nand/Makefile @@ -23,6 +23,8 @@ obj-$(CONFIG_MTD_NAND_TS7250) += ts7250.o obj-$(CONFIG_MTD_NAND_NANDSIM) += nandsim.o obj-$(CONFIG_MTD_NAND_CS553X) += cs553x_nand.o obj-$(CONFIG_MTD_NAND_NDFC) += ndfc.o +obj-$(CONFIG_MTD_NAND_MXC) += mxc_nd.o +obj-$(CONFIG_MTD_NAND_MXC_V2) += mxc_nd2.o obj-$(CONFIG_MTD_NAND_AT91) += at91_nand.o obj-$(CONFIG_MTD_NAND_CM_X270) += cmx270_nand.o obj-$(CONFIG_MTD_NAND_BASLER_EXCITE) += excite_nandflash.o diff --git a/drivers/mtd/nand/mxc_nd.c b/drivers/mtd/nand/mxc_nd.c new file mode 100644 index 000000000000..ccab9e120791 --- /dev/null +++ b/drivers/mtd/nand/mxc_nd.c @@ -0,0 +1,1346 @@ +/* + * Copyright 2004-2007 Freescale Semiconductor, Inc. All Rights Reserved. + */ + +/* + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +#include <linux/delay.h> +#include <linux/slab.h> +#include <linux/init.h> +#include <linux/module.h> +#include <linux/mtd/mtd.h> +#include <linux/mtd/nand.h> +#include <linux/mtd/partitions.h> +#include <linux/interrupt.h> +#include <linux/device.h> +#include <linux/platform_device.h> +#include <linux/clk.h> +#include <linux/err.h> + +#include <asm/mach/flash.h> +#include <asm/io.h> + +#include "mxc_nd.h" + +/*! + * Number of static partitions on NAND Flash. + */ +#define NUM_PARTITIONS (sizeof(partition_info)/sizeof(struct mtd_partition)) + +#define DVR_VER "2.0" + +struct mxc_mtd_s { + struct mtd_info mtd; + struct nand_chip nand; + struct mtd_partition *parts; + struct device *dev; +}; + +static struct mxc_mtd_s *mxc_nand_data = NULL; + +/* + * Define delays in microsec for NAND device operations + */ +#define TROP_US_DELAY 2000 +/* + * Macros to get byte and bit positions of ECC + */ +#define COLPOS(x) ((x) >> 3) +#define BITPOS(x) ((x)& 0xf) + +/* Define single bit Error positions in Main & Spare area */ +#define MAIN_SINGLEBIT_ERROR 0x4 +#define SPARE_SINGLEBIT_ERROR 0x1 + +struct nand_info { + bool bSpareOnly; + bool bStatusRequest; + u16 colAddr; +}; + +static struct nand_info g_nandfc_info; + +#ifdef CONFIG_MTD_NAND_MXC_SWECC +static int hardware_ecc = 0; +#else +static int hardware_ecc = 1; +#endif + +#ifndef CONFIG_MTD_NAND_MXC_ECC_CORRECTION_OPTION2 +static int Ecc_disabled; +#endif + +static int is2k_Pagesize = 0; + +static struct clk *nfc_clk; + +/* + * OOB placement block for use with hardware ecc generation + */ +static struct nand_ecclayout nand_hw_eccoob_8 = { + .eccbytes = 5, + .eccpos = {6, 7, 8, 9, 10}, + .oobfree = {{0, 5}, {11, 5}} +}; + +static struct nand_ecclayout nand_hw_eccoob_16 = { + .eccbytes = 5, + .eccpos = {6, 7, 8, 9, 10}, + .oobfree = {{0, 6}, {12, 4}} +}; + +/*! + * @defgroup NAND_MTD NAND Flash MTD Driver for MXC processors + */ + +/*! + * @file mxc_nd.c + * + * @brief This file contains the hardware specific layer for NAND Flash on + * MXC processor + * + * @ingroup NAND_MTD + */ + +#ifdef CONFIG_MTD_PARTITIONS +static const char *part_probes[] = { /* "RedBoot", */ "cmdlinepart", NULL }; +#endif + +static wait_queue_head_t irq_waitq; + +static irqreturn_t mxc_nfc_irq(int irq, void *dev_id) +{ + NFC_CONFIG1 |= NFC_INT_MSK; /* Disable interrupt */ + wake_up(&irq_waitq); + + return IRQ_RETVAL(1); +} + +/*! + * This function polls the NANDFC to wait for the basic operation to complete by + * checking the INT bit of config2 register. + * + * @param maxRetries number of retry attempts (separated by 1 us) + * @param param parameter for debug + * @param useirq True if IRQ should be used rather than polling + */ +static void wait_op_done(int maxRetries, u16 param, bool useirq) +{ + if (useirq) { + if ((NFC_CONFIG2 & NFC_INT) == 0) { + NFC_CONFIG1 &= ~NFC_INT_MSK; /* Enable interrupt */ + wait_event(irq_waitq, NFC_CONFIG2 & NFC_INT); + NFC_CONFIG2 &= ~NFC_INT; + } + } else { + while (maxRetries-- > 0) { + if (NFC_CONFIG2 & NFC_INT) { + NFC_CONFIG2 &= ~NFC_INT; + break; + } + udelay(1); + } + if (maxRetries <= 0) + DEBUG(MTD_DEBUG_LEVEL0, "%s(%d): INT not set\n", + __FUNCTION__, param); + } +} + +/*! + * This function issues the specified command to the NAND device and + * waits for completion. + * + * @param cmd command for NAND Flash + * @param useirq True if IRQ should be used rather than polling + */ +static void send_cmd(u16 cmd, bool useirq) +{ + DEBUG(MTD_DEBUG_LEVEL3, "send_cmd(0x%x, %d)\n", cmd, useirq); + + NFC_FLASH_CMD = (u16) cmd; + NFC_CONFIG2 = NFC_CMD; + + /* Wait for operation to complete */ + wait_op_done(TROP_US_DELAY, cmd, useirq); +} + +/*! + * This function sends an address (or partial address) to the + * NAND device. The address is used to select the source/destination for + * a NAND command. + * + * @param addr address to be written to NFC. + * @param islast True if this is the last address cycle for command + */ +static void send_addr(u16 addr, bool islast) +{ + DEBUG(MTD_DEBUG_LEVEL3, "send_addr(0x%x %d)\n", addr, islast); + + NFC_FLASH_ADDR = addr; + NFC_CONFIG2 = NFC_ADDR; + + /* Wait for operation to complete */ + wait_op_done(TROP_US_DELAY, addr, islast); +} + +/*! + * This function requests the NANDFC to initate the transfer + * of data currently in the NANDFC RAM buffer to the NAND device. + * + * @param buf_id Specify Internal RAM Buffer number (0-3) + * @param bSpareOnly set true if only the spare area is transferred + */ +static void send_prog_page(u8 buf_id, bool bSpareOnly) +{ + DEBUG(MTD_DEBUG_LEVEL3, "send_prog_page (%d)\n", bSpareOnly); + + /* NANDFC buffer 0 is used for page read/write */ + + NFC_BUF_ADDR = buf_id; + + /* Configure spare or page+spare access */ + if (!is2k_Pagesize) { + if (bSpareOnly) { + NFC_CONFIG1 |= NFC_SP_EN; + } else { + NFC_CONFIG1 &= ~(NFC_SP_EN); + } + } + NFC_CONFIG2 = NFC_INPUT; + + /* Wait for operation to complete */ + wait_op_done(TROP_US_DELAY, bSpareOnly, true); +} + +/*! + * This function will correct the single bit ECC error + * + * @param buf_id Specify Internal RAM Buffer number (0-3) + * @param eccpos Ecc byte and bit position + * @param bSpareOnly set to true if only spare area needs correction + */ + +static void mxc_nd_correct_error(u8 buf_id, u16 eccpos, bool bSpareOnly) +{ + u16 col; + u8 pos; + volatile u16 *buf; + + /* Get col & bit position of error + these macros works for both 8 & 16 bits */ + col = COLPOS(eccpos); /* Get half-word position */ + pos = BITPOS(eccpos); /* Get bit position */ + + DEBUG(MTD_DEBUG_LEVEL3, + "mxc_nd_correct_error (col=%d pos=%d)\n", col, pos); + + /* Set the pointer for main / spare area */ + if (!bSpareOnly) { + buf = MAIN_AREA0 + (col >> 1) + (512 * buf_id); + } else { + buf = SPARE_AREA0 + (col >> 1) + (16 * buf_id); + } + + /* Fix the data */ + *buf ^= (1 << pos); +} + +/*! + * This function will maintains state of single bit Error + * in Main & spare area + * + * @param buf_id Specify Internal RAM Buffer number (0-3) + * @param spare set to true if only spare area needs correction + */ +static void mxc_nd_correct_ecc(u8 buf_id, bool spare) +{ +#ifdef CONFIG_MTD_NAND_MXC_ECC_CORRECTION_OPTION2 + static int lastErrMain = 0, lastErrSpare = 0; /* To maintain single bit + error in previous page */ +#endif + u16 value, ecc_status; + /* Read the ECC result */ + ecc_status = NFC_ECC_STATUS_RESULT; + DEBUG(MTD_DEBUG_LEVEL3, + "mxc_nd_correct_ecc (Ecc status=%x)\n", ecc_status); + +#ifdef CONFIG_MTD_NAND_MXC_ECC_CORRECTION_OPTION2 + /* Check for Error in Mainarea */ + if ((ecc_status & 0xC) == MAIN_SINGLEBIT_ERROR) { + /* Check for error in previous page */ + if (lastErrMain && !spare) { + value = NFC_RSLTMAIN_AREA; + /* Correct single bit error in Mainarea + NFC will not correct the error in + current page */ + mxc_nd_correct_error(buf_id, value, false); + } else { + /* Set if single bit error in current page */ + lastErrMain = 1; + } + } else { + /* Reset if no single bit error in current page */ + lastErrMain = 0; + } + + /* Check for Error in Sparearea */ + if ((ecc_status & 0x3) == SPARE_SINGLEBIT_ERROR) { + /* Check for error in previous page */ + if (lastErrSpare) { + value = NFC_RSLTSPARE_AREA; + /* Correct single bit error in Mainarea + NFC will not correct the error in + current page */ + mxc_nd_correct_error(buf_id, value, true); + } else { + /* Set if single bit error in current page */ + lastErrSpare = 1; + } + } else { + /* Reset if no single bit error in current page */ + lastErrSpare = 0; + } +#else + if (((ecc_status & 0xC) == MAIN_SINGLEBIT_ERROR) + || ((ecc_status & 0x3) == SPARE_SINGLEBIT_ERROR)) { + if (Ecc_disabled) { + if ((ecc_status & 0xC) == MAIN_SINGLEBIT_ERROR) { + value = NFC_RSLTMAIN_AREA; + /* Correct single bit error in Mainarea + NFC will not correct the error in + current page */ + mxc_nd_correct_error(buf_id, value, false); + } + if ((ecc_status & 0x3) == SPARE_SINGLEBIT_ERROR) { + value = NFC_RSLTSPARE_AREA; + /* Correct single bit error in Mainarea + NFC will not correct the error in + current page */ + mxc_nd_correct_error(buf_id, value, true); + } + + } else { + /* Disable ECC */ + NFC_CONFIG1 &= ~(NFC_ECC_EN); + Ecc_disabled = 1; + } + } else if (ecc_status == 0) { + if (Ecc_disabled) { + /* Enable ECC */ + NFC_CONFIG1 |= NFC_ECC_EN; + Ecc_disabled = 0; + } + } else { + /* 2-bit Error Do nothing */ + } +#endif /* CONFIG_MTD_NAND_MXC_ECC_CORRECTION_OPTION2 */ + +} + +/*! + * This function requests the NANDFC to initated the transfer + * of data from the NAND device into in the NANDFC ram buffer. + * + * @param buf_id Specify Internal RAM Buffer number (0-3) + * @param bSpareOnly set true if only the spare area is transferred + */ +static void send_read_page(u8 buf_id, bool bSpareOnly) +{ + DEBUG(MTD_DEBUG_LEVEL3, "send_read_page (%d)\n", bSpareOnly); + + /* NANDFC buffer 0 is used for page read/write */ + NFC_BUF_ADDR = buf_id; + + /* Configure spare or page+spare access */ + if (!is2k_Pagesize) { + if (bSpareOnly) { + NFC_CONFIG1 |= NFC_SP_EN; + } else { + NFC_CONFIG1 &= ~(NFC_SP_EN); + } + } + + NFC_CONFIG2 = NFC_OUTPUT; + + /* Wait for operation to complete */ + wait_op_done(TROP_US_DELAY, bSpareOnly, true); + + /* If there are single bit errors in + two consecutive page reads then + the error is not corrected by the + NFC for the second page. + Correct single bit error in driver */ + + mxc_nd_correct_ecc(buf_id, bSpareOnly); +} + +/*! + * This function requests the NANDFC to perform a read of the + * NAND device ID. + */ +static void send_read_id(void) +{ + struct nand_chip *this = &mxc_nand_data->nand; + + /* NANDFC buffer 0 is used for device ID output */ + NFC_BUF_ADDR = 0x0; + + /* Read ID into main buffer */ + NFC_CONFIG1 &= (~(NFC_SP_EN)); + NFC_CONFIG2 = NFC_ID; + + /* Wait for operation to complete */ + wait_op_done(TROP_US_DELAY, 0, true); + + if (this->options & NAND_BUSWIDTH_16) { + volatile u16 *mainBuf = MAIN_AREA0; + + /* + * Pack the every-other-byte result for 16-bit ID reads + * into every-byte as the generic code expects and various + * chips implement. + */ + + mainBuf[0] = (mainBuf[0] & 0xff) | ((mainBuf[1] & 0xff) << 8); + mainBuf[1] = (mainBuf[2] & 0xff) | ((mainBuf[3] & 0xff) << 8); + mainBuf[2] = (mainBuf[4] & 0xff) | ((mainBuf[5] & 0xff) << 8); + } +} + +/*! + * This function requests the NANDFC to perform a read of the + * NAND device status and returns the current status. + * + * @return device status + */ +static u16 get_dev_status(void) +{ + volatile u16 *mainBuf = MAIN_AREA1; + u32 store; + u16 ret; + /* Issue status request to NAND device */ + + /* store the main area1 first word, later do recovery */ + store = *((u32 *) mainBuf); + /* + * NANDFC buffer 1 is used for device status to prevent + * corruption of read/write buffer on status requests. + */ + NFC_BUF_ADDR = 1; + + /* Read status into main buffer */ + NFC_CONFIG1 &= (~(NFC_SP_EN)); + NFC_CONFIG2 = NFC_STATUS; + + /* Wait for operation to complete */ + wait_op_done(TROP_US_DELAY, 0, true); + + /* Status is placed in first word of main buffer */ + /* get status, then recovery area 1 data */ + ret = mainBuf[0]; + *((u32 *) mainBuf) = store; + + return ret; +} + +/*! + * This functions is used by upper layer to checks if device is ready + * + * @param mtd MTD structure for the NAND Flash + * + * @return 0 if device is busy else 1 + */ +static int mxc_nand_dev_ready(struct mtd_info *mtd) +{ + /* + * NFC handles R/B internally.Therefore,this function + * always returns status as ready. + */ + return 1; +} + +static void mxc_nand_enable_hwecc(struct mtd_info *mtd, int mode) +{ + /* + * If HW ECC is enabled, we turn it on during init. There is + * no need to enable again here. + */ +} + +static int mxc_nand_correct_data(struct mtd_info *mtd, u_char * dat, + u_char * read_ecc, u_char * calc_ecc) +{ + /* + * 1-Bit errors are automatically corrected in HW. No need for + * additional correction. 2-Bit errors cannot be corrected by + * HW ECC, so we need to return failure + */ + u16 ecc_status = NFC_ECC_STATUS_RESULT; + + if (((ecc_status & 0x3) == 2) || ((ecc_status >> 2) == 2)) { + DEBUG(MTD_DEBUG_LEVEL0, + "MXC_NAND: HWECC uncorrectable 2-bit ECC error\n"); + return -1; + } + + return 0; +} + +static int mxc_nand_calculate_ecc(struct mtd_info *mtd, const u_char * dat, + u_char * ecc_code) +{ + /* + * Just return success. HW ECC does not read/write the NFC spare + * buffer. Only the FLASH spare area contains the calcuated ECC. + */ + return 0; +} + +/*! + * This function reads byte from the NAND Flash + * + * @param mtd MTD structure for the NAND Flash + * + * @return data read from the NAND Flash + */ +static u_char mxc_nand_read_byte(struct mtd_info *mtd) +{ + u_char retVal = 0; + u16 col, rdWord; + volatile u16 *mainBuf = MAIN_AREA0; + volatile u16 *spareBuf = SPARE_AREA0; + + /* Check for status request */ + if (g_nandfc_info.bStatusRequest) { + return (get_dev_status() & 0xFF); + } + + /* Get column for 16-bit access */ + col = g_nandfc_info.colAddr >> 1; + + /* If we are accessing the spare region */ + if (g_nandfc_info.bSpareOnly) { + rdWord = spareBuf[col]; + } else { + rdWord = mainBuf[col]; + } + + /* Pick upper/lower byte of word from RAM buffer */ + if (g_nandfc_info.colAddr & 0x1) { + retVal = (rdWord >> 8) & 0xFF; + } else { + retVal = rdWord & 0xFF; + } + + /* Update saved column address */ + g_nandfc_info.colAddr++; + + return retVal; +} + +/*! + * This function reads word from the NAND Flash + * + * @param mtd MTD structure for the NAND Flash + * + * @return data read from the NAND Flash + */ +static u16 mxc_nand_read_word(struct mtd_info *mtd) +{ + u16 col; + u16 rdWord, retVal; + volatile u16 *p; + + DEBUG(MTD_DEBUG_LEVEL3, + "mxc_nand_read_word(col = %d)\n", g_nandfc_info.colAddr); + + col = g_nandfc_info.colAddr; + /* Adjust saved column address */ + if (col < mtd->writesize && g_nandfc_info.bSpareOnly) + col += mtd->writesize; + + if (col < mtd->writesize) + p = (MAIN_AREA0) + (col >> 1); + else + p = (SPARE_AREA0) + ((col - mtd->writesize) >> 1); + + if (col & 1) { + rdWord = *p; + retVal = (rdWord >> 8) & 0xff; + rdWord = *(p + 1); + retVal |= (rdWord << 8) & 0xff00; + + } else { + retVal = *p; + + } + + /* Update saved column address */ + g_nandfc_info.colAddr = col + 2; + + return retVal; +} + +/*! + * This function writes data of length \b len to buffer \b buf. The data to be + * written on NAND Flash is first copied to RAMbuffer. After the Data Input + * Operation by the NFC, the data is written to NAND Flash + * + * @param mtd MTD structure for the NAND Flash + * @param buf data to be written to NAND Flash + * @param len number of bytes to be written + */ +static void mxc_nand_write_buf(struct mtd_info *mtd, + const u_char * buf, int len) +{ + int n; + int col; + int i = 0; + + DEBUG(MTD_DEBUG_LEVEL3, + "mxc_nand_write_buf(col = %d, len = %d)\n", g_nandfc_info.colAddr, + len); + + col = g_nandfc_info.colAddr; + + /* Adjust saved column address */ + if (col < mtd->writesize && g_nandfc_info.bSpareOnly) + col += mtd->writesize; + + n = mtd->writesize + mtd->oobsize - col; + n = min(len, n); + + DEBUG(MTD_DEBUG_LEVEL3, + "%s:%d: col = %d, n = %d\n", __FUNCTION__, __LINE__, col, n); + + while (n) { + volatile u32 *p; + if (col < mtd->writesize) + p = (volatile u32 *)((ulong) (MAIN_AREA0) + (col & ~3)); + else + p = (volatile u32 *)((ulong) (SPARE_AREA0) - + mtd->writesize + (col & ~3)); + + DEBUG(MTD_DEBUG_LEVEL3, "%s:%d: p = %p\n", __FUNCTION__, + __LINE__, p); + + if (((col | (int)&buf[i]) & 3) || n < 16) { + u32 data = 0; + + if (col & 3 || n < 4) + data = *p; + + switch (col & 3) { + case 0: + if (n) { + data = (data & 0xffffff00) | + (buf[i++] << 0); + n--; + col++; + } + case 1: + if (n) { + data = (data & 0xffff00ff) | + (buf[i++] << 8); + n--; + col++; + } + case 2: + if (n) { + data = (data & 0xff00ffff) | + (buf[i++] << 16); + n--; + col++; + } + case 3: + if (n) { + data = (data & 0x00ffffff) | + (buf[i++] << 24); + n--; + col++; + } + } + + *p = data; + } else { + int m = mtd->writesize - col; + + if (col >= mtd->writesize) + m += mtd->oobsize; + + m = min(n, m) & ~3; + + DEBUG(MTD_DEBUG_LEVEL3, + "%s:%d: n = %d, m = %d, i = %d, col = %d\n", + __FUNCTION__, __LINE__, n, m, i, col); + + memcpy((void *)(p), &buf[i], m); + col += m; + i += m; + n -= m; + } + } + /* Update saved column address */ + g_nandfc_info.colAddr = col; + +} + +/*! + * This function id is used to read the data buffer from the NAND Flash. To + * read the data from NAND Flash first the data output cycle is initiated by + * the NFC, which copies the data to RAMbuffer. This data of length \b len is + * then copied to buffer \b buf. + * + * @param mtd MTD structure for the NAND Flash + * @param buf data to be read from NAND Flash + * @param len number of bytes to be read + */ +static void mxc_nand_read_buf(struct mtd_info *mtd, u_char * buf, int len) +{ + + int n; + int col; + int i = 0; + + DEBUG(MTD_DEBUG_LEVEL3, + "mxc_nand_read_buf(col = %d, len = %d)\n", g_nandfc_info.colAddr, + len); + + col = g_nandfc_info.colAddr; + /* Adjust saved column address */ + if (col < mtd->writesize && g_nandfc_info.bSpareOnly) + col += mtd->writesize; + + n = mtd->writesize + mtd->oobsize - col; + n = min(len, n); + + while (n) { + volatile u32 *p; + + if (col < mtd->writesize) + p = (volatile u32 *)((ulong) (MAIN_AREA0) + (col & ~3)); + else + p = (volatile u32 *)((ulong) (SPARE_AREA0) - + mtd->writesize + (col & ~3)); + + if (((col | (int)&buf[i]) & 3) || n < 16) { + u32 data; + + data = *p; + switch (col & 3) { + case 0: + if (n) { + buf[i++] = (u8) (data); + n--; + col++; + } + case 1: + if (n) { + buf[i++] = (u8) (data >> 8); + n--; + col++; + } + case 2: + if (n) { + buf[i++] = (u8) (data >> 16); + n--; + col++; + } + case 3: + if (n) { + buf[i++] = (u8) (data >> 24); + n--; + col++; + } + } + } else { + int m = mtd->writesize - col; + + if (col >= mtd->writesize) + m += mtd->oobsize; + + m = min(n, m) & ~3; + memcpy(&buf[i], (void *)(p), m); + col += m; + i += m; + n -= m; + } + } + /* Update saved column address */ + g_nandfc_info.colAddr = col; + +} + +/*! + * This function is used by the upper layer to verify the data in NAND Flash + * with the data in the \b buf. + * + * @param mtd MTD structure for the NAND Flash + * @param buf data to be verified + * @param len length of the data to be verified + * + * @return -EFAULT if error else 0 + * + */ +static int +mxc_nand_verify_buf(struct mtd_info *mtd, const u_char * buf, int len) +{ + return -EFAULT; +} + +/*! + * This function is used by upper layer for select and deselect of the NAND + * chip + * + * @param mtd MTD structure for the NAND Flash + * @param chip val indicating select or deselect + */ +static void mxc_nand_select_chip(struct mtd_info *mtd, int chip) +{ +#ifdef CONFIG_MTD_NAND_MXC_FORCE_CE + if (chip > 0) { + DEBUG(MTD_DEBUG_LEVEL0, + "ERROR: Illegal chip select (chip = %d)\n", chip); + return; + } + + if (chip == -1) { + NFC_CONFIG1 &= (~(NFC_CE)); + return; + } + + NFC_CONFIG1 |= NFC_CE; +#endif + + switch (chip) { + case -1: + /* Disable the NFC clock */ + clk_disable(nfc_clk); + break; + case 0: + /* Enable the NFC clock */ + clk_enable(nfc_clk); + break; + + default: + break; + } +} + +/*! + * This function is used by the upper layer to write command to NAND Flash for + * different operations to be carried out on NAND Flash + * + * @param mtd MTD structure for the NAND Flash + * @param command command for NAND Flash + * @param column column offset for the page read + * @param page_addr page to be read from NAND Flash + */ +static void mxc_nand_command(struct mtd_info *mtd, unsigned command, + int column, int page_addr) +{ + bool useirq = true; + + DEBUG(MTD_DEBUG_LEVEL3, + "mxc_nand_command (cmd = 0x%x, col = 0x%x, page = 0x%x)\n", + command, column, page_addr); + + if ((NFMS >> NFMS_BIT) & 0x1) + is2k_Pagesize = 1; + + /* + * Reset command state information + */ + g_nandfc_info.bStatusRequest = false; + + /* + * Command pre-processing step + */ + switch (command) { + + case NAND_CMD_STATUS: + g_nandfc_info.colAddr = 0; + g_nandfc_info.bStatusRequest = true; + break; + + case NAND_CMD_READ0: + g_nandfc_info.colAddr = column; + g_nandfc_info.bSpareOnly = false; + useirq = false; + break; + + case NAND_CMD_READOOB: + g_nandfc_info.colAddr = column; + g_nandfc_info.bSpareOnly = true; + useirq = false; + if (is2k_Pagesize) + command = NAND_CMD_READ0; /* only READ0 is valid */ + break; + + case NAND_CMD_SEQIN: + if (column >= mtd->writesize) { + if (is2k_Pagesize) { + /** + * FIXME: before send SEQIN command for write OOB, + * We must read one page out. + * For K9F1GXX has no READ1 command to set current HW + * pointer to spare area, we must write the whole page including OOB together. + */ + /* call itself to read a page */ + mxc_nand_command(mtd, NAND_CMD_READ0, 0, + page_addr); + } + g_nandfc_info.colAddr = column - mtd->writesize; + g_nandfc_info.bSpareOnly = true; + /* Set program pointer to spare region */ + if (!is2k_Pagesize) + send_cmd(NAND_CMD_READOOB, false); + } else { + g_nandfc_info.bSpareOnly = false; + g_nandfc_info.colAddr = column; + /* Set program pointer to page start */ + if (!is2k_Pagesize) + send_cmd(NAND_CMD_READ0, false); + } + useirq = false; + break; + + case NAND_CMD_PAGEPROG: +#ifndef CONFIG_MTD_NAND_MXC_ECC_CORRECTION_OPTION2 + if (Ecc_disabled) { + /* Enable Ecc for page writes */ + NFC_CONFIG1 |= NFC_ECC_EN; + } +#endif + + send_prog_page(0, g_nandfc_info.bSpareOnly); + + if (is2k_Pagesize) { + /* data in 4 areas datas */ + send_prog_page(1, g_nandfc_info.bSpareOnly); + send_prog_page(2, g_nandfc_info.bSpareOnly); + send_prog_page(3, g_nandfc_info.bSpareOnly); + } + + break; + + case NAND_CMD_ERASE1: + useirq = false; + break; + } + + /* + * Write out the command to the device. + */ + send_cmd(command, useirq); + + /* + * Write out column address, if necessary + */ + if (column != -1) { + /* + * MXC NANDFC can only perform full page+spare or + * spare-only read/write. When the upper layers + * layers perform a read/write buf operation, + * we will used the saved column adress to index into + * the full page. + */ + send_addr(0, page_addr == -1); + if (is2k_Pagesize) + send_addr(0, false); /* another col addr cycle for 2k page */ + } + + /* + * Write out page address, if necessary + */ + if (page_addr != -1) { + send_addr((page_addr & 0xff), false); /* paddr_0 - p_addr_7 */ + + if (is2k_Pagesize) { + send_addr((page_addr >> 8) & 0xFF, false); + if (mtd->size >= 0x42000000) { + send_addr((page_addr >> 16) & 0xff, true); + } + } else { + /* One more address cycle for higher density devices */ + if (mtd->size >= 0x4000000) { + send_addr((page_addr >> 8) & 0xff, false); /* paddr_8 - paddr_15 */ + send_addr((page_addr >> 16) & 0xff, true); + } else + send_addr((page_addr >> 8) & 0xff, true); /* paddr_8 - paddr_15 */ + } + } + + /* + * Command post-processing step + */ + switch (command) { + + case NAND_CMD_RESET: + break; + + case NAND_CMD_READOOB: + case NAND_CMD_READ0: + if (is2k_Pagesize) { + /* send read confirm command */ + send_cmd(NAND_CMD_READSTART, true); + /* read for each AREA */ + send_read_page(0, g_nandfc_info.bSpareOnly); + send_read_page(1, g_nandfc_info.bSpareOnly); + send_read_page(2, g_nandfc_info.bSpareOnly); + send_read_page(3, g_nandfc_info.bSpareOnly); + } else { + send_read_page(0, g_nandfc_info.bSpareOnly); + } + break; + + case NAND_CMD_READID: + send_read_id(); + break; + + case NAND_CMD_PAGEPROG: +#ifndef CONFIG_MTD_NAND_MXC_ECC_CORRECTION_OPTION2 + if (Ecc_disabled) { + /* Disble Ecc after page writes */ + NFC_CONFIG1 &= ~(NFC_ECC_EN); + } +#endif + break; + + case NAND_CMD_STATUS: + break; + + case NAND_CMD_ERASE2: + break; + } +} + +/* Define some generic bad / good block scan pattern which are used + * while scanning a device for factory marked good / bad blocks. */ +static uint8_t scan_ff_pattern[] = { 0xff, 0xff }; + +static struct nand_bbt_descr smallpage_memorybased = { + .options = NAND_BBT_SCAN2NDPAGE, + .offs = 5, + .len = 1, + .pattern = scan_ff_pattern +}; + +static struct nand_bbt_descr largepage_memorybased = { + .options = 0, + .offs = 0, + .len = 2, + .pattern = scan_ff_pattern +}; + +static int mxc_nand_scan_bbt(struct mtd_info *mtd) +{ + struct nand_chip *this = mtd->priv; + + /* Config before scanning */ + if (mtd->writesize == NAND_PAGESIZE_2KB) { + NFMS |= (1 << NFMS_BIT); + } + + this->bbt_td = NULL; + this->bbt_md = NULL; + if (!this->badblock_pattern) { + if (mtd->writesize == NAND_PAGESIZE_2KB) + this->badblock_pattern = &smallpage_memorybased; + else + this->badblock_pattern = (mtd->writesize > 512) ? + &largepage_memorybased : &smallpage_memorybased; + } + /* Build bad block table */ + return nand_scan_bbt(mtd, this->badblock_pattern); +} + +#ifdef CONFIG_MXC_NAND_LOW_LEVEL_ERASE +static void mxc_low_erase(struct mtd_info *mtd) +{ + + struct nand_chip *this = mtd->priv; + unsigned int page_addr, addr; + u_char status; + + DEBUG(MTD_DEBUG_LEVEL0, "MXC_ND : mxc_low_erase:Erasing NAND\n"); + for (addr = 0; addr < this->chipsize; addr += mtd->erasesize) { + page_addr = addr / mtd->writesize; + mxc_nand_command(mtd, NAND_CMD_ERASE1, -1, page_addr); + mxc_nand_command(mtd, NAND_CMD_ERASE2, -1, -1); + mxc_nand_command(mtd, NAND_CMD_STATUS, -1, -1); + status = mxc_nand_read_byte(mtd); + if (status & NAND_STATUS_FAIL) { + printk(KERN_ERR + "ERASE FAILED(block = %d,status = 0x%x)\n", + addr / mtd->erasesize, status); + } + } + +} +#endif +/*! + * This function is called during the driver binding process. + * + * @param pdev the device structure used to store device specific + * information that is used by the suspend, resume and + * remove functions + * + * @return The function always returns 0. + */ +static int __init mxcnd_probe(struct platform_device *pdev) +{ + struct nand_chip *this; + struct mtd_info *mtd; + struct flash_platform_data *flash = pdev->dev.platform_data; + int nr_parts = 0; + + int err = 0; + /* Allocate memory for MTD device structure and private data */ + mxc_nand_data = kmalloc(sizeof(struct mxc_mtd_s), GFP_KERNEL); + if (!mxc_nand_data) { + printk(KERN_ERR "%s: failed to allocate mtd_info\n", + __FUNCTION__); + err = -ENOMEM; + goto out; + } + memset(mxc_nand_data, 0, sizeof(struct mxc_mtd_s)); + memset((char *)&g_nandfc_info, 0, sizeof(g_nandfc_info)); + + mxc_nand_data->dev = &pdev->dev; + /* structures must be linked */ + this = &mxc_nand_data->nand; + mtd = &mxc_nand_data->mtd; + mtd->priv = this; + mtd->owner = THIS_MODULE; + + /* 50 us command delay time */ + this->chip_delay = 5; + + this->priv = mxc_nand_data; + this->dev_ready = mxc_nand_dev_ready; + this->cmdfunc = mxc_nand_command; + this->select_chip = mxc_nand_select_chip; + this->read_byte = mxc_nand_read_byte; + this->read_word = mxc_nand_read_word; + this->write_buf = mxc_nand_write_buf; + this->read_buf = mxc_nand_read_buf; + this->verify_buf = mxc_nand_verify_buf; + this->scan_bbt = mxc_nand_scan_bbt; + + nfc_clk = clk_get(&pdev->dev, "nfc_clk"); + clk_enable(nfc_clk); + + NFC_CONFIG1 |= NFC_INT_MSK; + init_waitqueue_head(&irq_waitq); + err = request_irq(INT_NANDFC, mxc_nfc_irq, 0, "mxc_nd", NULL); + if (err) { + goto out_1; + } + + if (hardware_ecc) { + this->ecc.calculate = mxc_nand_calculate_ecc; + this->ecc.hwctl = mxc_nand_enable_hwecc; + this->ecc.correct = mxc_nand_correct_data; + this->ecc.mode = NAND_ECC_HW; + this->ecc.size = 512; + this->ecc.bytes = 3; + this->ecc.layout = &nand_hw_eccoob_8; + NFC_CONFIG1 |= NFC_ECC_EN; + } else { + this->ecc.mode = NAND_ECC_SOFT; + } + + /* Reset NAND */ + this->cmdfunc(mtd, NAND_CMD_RESET, -1, -1); + + /* preset operation */ + /* Unlock the internal RAM Buffer */ + NFC_CONFIG = 0x2; + + /* Blocks to be unlocked */ + NFC_UNLOCKSTART_BLKADDR = 0x0; + NFC_UNLOCKEND_BLKADDR = 0x4000; + + /* Unlock Block Command for given address range */ + NFC_WRPROT = 0x4; + + /* NAND bus width determines access funtions used by upper layer */ + if (flash->width == 2) { + this->options |= NAND_BUSWIDTH_16; + this->ecc.layout = &nand_hw_eccoob_16; + } else { + this->options |= 0; + } + + is2k_Pagesize = 0; + + /* Scan to find existence of the device */ + if (nand_scan(mtd, 1)) { + DEBUG(MTD_DEBUG_LEVEL0, + "MXC_ND: Unable to find any NAND device.\n"); + err = -ENXIO; + goto out_1; + } + + /* Register the partitions */ +#ifdef CONFIG_MTD_PARTITIONS + nr_parts = + parse_mtd_partitions(mtd, part_probes, &mxc_nand_data->parts, 0); + if (nr_parts > 0) + add_mtd_partitions(mtd, mxc_nand_data->parts, nr_parts); + else if (flash->parts) + add_mtd_partitions(mtd, flash->parts, flash->nr_parts); + else +#endif + { + pr_info("Registering %s as whole device\n", mtd->name); + add_mtd_device(mtd); + } +#ifdef CONFIG_MXC_NAND_LOW_LEVEL_ERASE + /* Erase all the blocks of a NAND */ + mxc_low_erase(mtd); +#endif + + platform_set_drvdata(pdev, mtd); + return 0; + + out_1: + kfree(mxc_nand_data); + out: + return err; + +} + + /*! + * Dissociates the driver from the device. + * + * @param pdev the device structure used to give information on which + * + * @return The function always returns 0. + */ + +static int __exit mxcnd_remove(struct platform_device *pdev) +{ + struct mtd_info *mtd = platform_get_drvdata(pdev); + + clk_put(nfc_clk); + platform_set_drvdata(pdev, NULL); + + if (mxc_nand_data) { + nand_release(mtd); + free_irq(INT_NANDFC, NULL); + kfree(mxc_nand_data); + } + + return 0; +} + +#ifdef CONFIG_PM +/*! + * This function is called to put the NAND in a low power state. Refer to the + * document driver-model/driver.txt in the kernel source tree for more + * information. + * + * @param pdev the device information structure + * + * @param state the power state the device is entering + * + * @return The function returns 0 on success and -1 on failure + */ + +static int mxcnd_suspend(struct platform_device *pdev, pm_message_t state) +{ + struct mtd_info *info = platform_get_drvdata(pdev); + int ret = 0; + + DEBUG(MTD_DEBUG_LEVEL0, "MXC_ND : NAND suspend\n"); + if (info) + ret = info->suspend(info); + + /* Disable the NFC clock */ + clk_disable(nfc_clk); + + return ret; +} + +/*! + * This function is called to bring the NAND back from a low power state. Refer + * to the document driver-model/driver.txt in the kernel source tree for more + * information. + * + * @param pdev the device information structure + * + * @return The function returns 0 on success and -1 on failure + */ +static int mxcnd_resume(struct platform_device *pdev) +{ + struct mtd_info *info = platform_get_drvdata(pdev); + int ret = 0; + + DEBUG(MTD_DEBUG_LEVEL0, "MXC_ND : NAND resume\n"); + /* Enable the NFC clock */ + clk_enable(nfc_clk); + + if (info) { + info->resume(info); + } + + return ret; +} + +#else +#define mxcnd_suspend NULL +#define mxcnd_resume NULL +#endif /* CONFIG_PM */ + +/*! + * This structure contains pointers to the power management callback functions. + */ +static struct platform_driver mxcnd_driver = { + .driver = { + .name = "mxc_nand_flash", + }, + .probe = mxcnd_probe, + .remove = __exit_p(mxcnd_remove), + .suspend = mxcnd_suspend, + .resume = mxcnd_resume, +}; + +/*! + * Main initialization routine + * @return 0 if successful; non-zero otherwise + */ +static int __init mxc_nd_init(void) +{ + /* Register the device driver structure. */ + pr_info("MXC MTD nand Driver %s\n", DVR_VER); + if (platform_driver_register(&mxcnd_driver) != 0) { + printk(KERN_ERR "Driver register failed for mxcnd_driver\n"); + return -ENODEV; + } + return 0; +} + +/*! + * Clean up routine + */ +static void __exit mxc_nd_cleanup(void) +{ + /* Unregister the device structure */ + platform_driver_unregister(&mxcnd_driver); +} + +module_init(mxc_nd_init); +module_exit(mxc_nd_cleanup); + +MODULE_AUTHOR("Freescale Semiconductor, Inc."); +MODULE_DESCRIPTION("MXC NAND MTD driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/mtd/nand/mxc_nd.h b/drivers/mtd/nand/mxc_nd.h new file mode 100644 index 000000000000..fd15cb48cb9c --- /dev/null +++ b/drivers/mtd/nand/mxc_nd.h @@ -0,0 +1,112 @@ +/* + * Copyright 2004-2007 Freescale Semiconductor, Inc. All Rights Reserved. + */ + +/* + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +/*! + * @file mxc_nd.h + * + * @brief This file contains the NAND Flash Controller register information. + * + * + * @ingroup NAND_MTD + */ + +#ifndef __MXC_ND_H__ +#define __MXC_ND_H__ + +#include <asm/hardware.h> + +/* + * Addresses for NFC registers + */ +#define NFC_BUF_SIZE (*((volatile u16 *)IO_ADDRESS(NFC_BASE_ADDR + 0xE00))) +#define NFC_BUF_ADDR (*((volatile u16 *)IO_ADDRESS(NFC_BASE_ADDR + 0xE04))) +#define NFC_FLASH_ADDR (*((volatile u16 *)IO_ADDRESS(NFC_BASE_ADDR + 0xE06))) +#define NFC_FLASH_CMD (*((volatile u16 *)IO_ADDRESS(NFC_BASE_ADDR + 0xE08))) +#define NFC_CONFIG (*((volatile u16 *)IO_ADDRESS(NFC_BASE_ADDR + 0xE0A))) +#define NFC_ECC_STATUS_RESULT (*((volatile u16 *)IO_ADDRESS(NFC_BASE_ADDR + 0xE0C))) +#define NFC_RSLTMAIN_AREA (*((volatile u16 *)IO_ADDRESS(NFC_BASE_ADDR + 0xE0E))) +#define NFC_RSLTSPARE_AREA (*((volatile u16 *)IO_ADDRESS(NFC_BASE_ADDR + 0xE10))) +#define NFC_WRPROT (*((volatile u16 *)IO_ADDRESS(NFC_BASE_ADDR + 0xE12))) +#define NFC_UNLOCKSTART_BLKADDR (*((volatile u16 *)IO_ADDRESS(NFC_BASE_ADDR + 0xE14))) +#define NFC_UNLOCKEND_BLKADDR (*((volatile u16 *)IO_ADDRESS(NFC_BASE_ADDR + 0xE16))) +#define NFC_NF_WRPRST (*((volatile u16 *)IO_ADDRESS(NFC_BASE_ADDR + 0xE18))) +#define NFC_CONFIG1 (*((volatile u16 *)IO_ADDRESS(NFC_BASE_ADDR + 0xE1A))) +#define NFC_CONFIG2 (*((volatile u16 *)IO_ADDRESS(NFC_BASE_ADDR + 0xE1C))) + +/*! + * Addresses for NFC RAM BUFFER Main area 0 + */ +#define MAIN_AREA0 (volatile u16 *)IO_ADDRESS(NFC_BASE_ADDR + 0x000) +#define MAIN_AREA1 (volatile u16 *)IO_ADDRESS(NFC_BASE_ADDR + 0x200) +#define MAIN_AREA2 (volatile u16 *)IO_ADDRESS(NFC_BASE_ADDR + 0x400) +#define MAIN_AREA3 (volatile u16 *)IO_ADDRESS(NFC_BASE_ADDR + 0x600) + +/*! + * Addresses for NFC SPARE BUFFER Spare area 0 + */ +#define SPARE_AREA0 (volatile u16 *)IO_ADDRESS(NFC_BASE_ADDR + 0x800) +#define SPARE_AREA1 (volatile u16 *)IO_ADDRESS(NFC_BASE_ADDR + 0x810) +#define SPARE_AREA2 (volatile u16 *)IO_ADDRESS(NFC_BASE_ADDR + 0x820) +#define SPARE_AREA3 (volatile u16 *)IO_ADDRESS(NFC_BASE_ADDR + 0x830) + +/*! + * Set INT to 0, FCMD to 1, rest to 0 in NFC_CONFIG2 Register for Command + * operation + */ +#define NFC_CMD 0x1 + +/*! + * Set INT to 0, FADD to 1, rest to 0 in NFC_CONFIG2 Register for Address + * operation + */ +#define NFC_ADDR 0x2 + +/*! + * Set INT to 0, FDI to 1, rest to 0 in NFC_CONFIG2 Register for Input + * operation + */ +#define NFC_INPUT 0x4 + +/*! + * Set INT to 0, FDO to 001, rest to 0 in NFC_CONFIG2 Register for Data Output + * operation + */ +#define NFC_OUTPUT 0x8 + +/*! + * Set INT to 0, FD0 to 010, rest to 0 in NFC_CONFIG2 Register for Read ID + * operation + */ +#define NFC_ID 0x10 + +/*! + * Set INT to 0, FDO to 100, rest to 0 in NFC_CONFIG2 Register for Read Status + * operation + */ +#define NFC_STATUS 0x20 + +/*! + * Set INT to 1, rest to 0 in NFC_CONFIG2 Register for Read Status + * operation + */ +#define NFC_INT 0x8000 + +#define NFC_SP_EN (1 << 2) +#define NFC_ECC_EN (1 << 3) +#define NFC_INT_MSK (1 << 4) +#define NFC_BIG (1 << 5) +#define NFC_RST (1 << 6) +#define NFC_CE (1 << 7) +#define NFC_ONE_CYCLE (1 << 8) + +#endif /* MXCND_H */ diff --git a/drivers/mtd/nand/mxc_nd2.c b/drivers/mtd/nand/mxc_nd2.c new file mode 100644 index 000000000000..d3a8993ccb2e --- /dev/null +++ b/drivers/mtd/nand/mxc_nd2.c @@ -0,0 +1,1329 @@ +/* + * Copyright 2004-2007 Freescale Semiconductor, Inc. All Rights Reserved. + */ + +/* + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +#include <linux/delay.h> +#include <linux/slab.h> +#include <linux/init.h> +#include <linux/module.h> +#include <linux/mtd/mtd.h> +#include <linux/mtd/nand.h> +#include <linux/mtd/partitions.h> +#include <linux/interrupt.h> +#include <linux/device.h> +#include <linux/platform_device.h> +#include <linux/clk.h> +#include <linux/err.h> + +#include <asm/mach/flash.h> +#include <asm/io.h> + +#include "mxc_nd.h" + +#define DVR_VER "2.11" + +/* + * Define delays in microsec for NAND device operations + */ +#define TROP_US_DELAY 2000 + +/* + * Macro useful to check if the nand chip is 2K page + */ +#define IS_2K_PAGE_NAND (mtd->writesize == NAND_PAGESIZE_2KB) + +static void mxc_swap_2k_bi_main_sp(void); + +struct mxc_mtd_s { + struct mtd_info mtd; + struct nand_chip nand; + struct mtd_partition *parts; + struct device *dev; +}; + +static struct mxc_mtd_s *mxc_nand_data; + +struct nand_info { + bool bSpareOnly; + bool bStatusRequest; + u16 colAddr; +}; + +static struct nand_info g_nandfc_info; + +#ifdef CONFIG_MTD_NAND_MXC_SWECC +static int hardware_ecc = 0; +#else +static int hardware_ecc = 1; +#endif + +static int scan_done; +static int skip_erase; +static int page_to_block_shift; +static int g_page_mask; +static uint8_t *oob_data_shadow_p; +/* + * OOB data that is shadowed in the SDRAM to prevent the Spare only access + * to the Nand chip. This is valid only for the JFFS2 File System. + */ +static uint8_t *shadow_oob_data; + +static uint8_t oob_data_512[] = { + 0x85, 0x19, 0x03, 0x20, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff +}; + +static uint8_t oob_data_2k[] = { + 0xff, 0xff, 0x85, 0x19, 0x03, 0x20, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff +}; + +static struct clk *nfc_clk; + +/* + * OOB placement block for use with hardware ecc generation + */ +static struct nand_ecclayout nand_hw_eccoob_512 = { + .eccbytes = 9, + .eccpos = {7, 8, 9, 10, 11, 12, 13, 14, 15}, + .oobavail = 4, + .oobfree = {{0, 4}} +}; + +static struct nand_ecclayout nand_hw_eccoob_2k = { + .eccbytes = 9, + .eccpos = {7, 8, 9, 10, 11, 12, 13, 14, 15}, + .oobavail = 4, + .oobfree = {{2, 4}} +}; + +/*! + * @defgroup NAND_MTD NAND Flash MTD Driver for MXC processors + */ + +/*! + * @file mxc_nd2.c + * + * @brief This file contains the hardware specific layer for NAND Flash on + * MXC processor + * + * @ingroup NAND_MTD + */ + +#ifdef CONFIG_MTD_PARTITIONS +static const char *part_probes[] = { "cmdlinepart", NULL }; +#endif + +static wait_queue_head_t irq_waitq; + +static irqreturn_t mxc_nfc_irq(int irq, void *dev_id) +{ + NFC_CONFIG1 |= NFC_INT_MSK; /* Disable interrupt */ + wake_up(&irq_waitq); + + return IRQ_HANDLED; +} + +static u8 mxc_main_xfer_buf[2048] ____cacheline_aligned; + +/* + * Functions that operate on the shadow table maintained in the RAM. + * Each block in the Nand chip has one bit entry in this table + * indicating if the block has a JFFS2 clean marker. + * mark_oob_data_dirty - marks a block to indicate that the block has a JFFS2 + * clean marker + * is_oob_data_dirty - checks if the block has a JFFS2 clean marker + * mark_oob_data_clean - marks a block to indicate that the block is erased + * and doesnot contain JFFS2 clean marker. + */ +static void mark_oob_data_dirty(u32 page, int update_sp) +{ + u32 blk = page >> page_to_block_shift; + u32 off = blk / 8; + u32 bit = blk % 8; + + oob_data_shadow_p[off] |= (1 << bit); +} + +static int is_oob_data_dirty(u32 page) +{ + u32 blk = page >> page_to_block_shift; + u32 off = blk / 8; + u32 bit = blk % 8; + + return oob_data_shadow_p[off] & (1 << bit); +} + +static void mark_oob_data_clean(u32 page) +{ + u32 blk = page >> page_to_block_shift; + u32 off = blk / 8; + u32 bit = blk % 8; + + oob_data_shadow_p[off] &= ~(1 << bit); +} + +/* + * Functions to handle 32-bit aligned memcpy. + */ +static void nfc_memcpy(void *dst, const void *src, int len) +{ + volatile u16 *d = (volatile u16 *)dst; + volatile u16 *s = (volatile u16 *)src; + int wc; + + switch ((u32) dst & 3) { + case 2: + wc = len / 2; + /* adjust alignment */ + *d = *s; + memcpy((void *)(d + 1), (const void *)(s + 1), len - 4); + *(d + wc - 1) = *(s + wc - 1); + break; + + case 1: + case 3: + memcpy((void *)mxc_main_xfer_buf, (const void *)src, + (len + 3) & (~3)); + memcpy((void *)d, (const void *)mxc_main_xfer_buf, len); + break; + case 0: + memcpy((void *)d, (const void *)s, len); + } +} + +/*! + * This function polls the NFC to wait for the basic operation to complete by + * checking the INT bit of config2 register. + * + * @param maxRetries number of retry attempts (separated by 1 us) + * @param useirq True if IRQ should be used rather than polling + */ +static void wait_op_done(int maxRetries, bool useirq) +{ + if (useirq) { + if ((NFC_CONFIG2 & NFC_INT) == 0) { + NFC_CONFIG1 &= ~NFC_INT_MSK; /* Enable interrupt */ + wait_event(irq_waitq, NFC_CONFIG2 & NFC_INT); + NFC_CONFIG2 &= ~NFC_INT; + } + } else { + while (1) { + maxRetries--; + if (NFC_CONFIG2 & NFC_INT) { + NFC_CONFIG2 &= ~NFC_INT; + break; + } + udelay(1); + } + if (maxRetries <= 0) { + DEBUG(MTD_DEBUG_LEVEL0, "%s(%d): INT not set\n", + __FUNCTION__); + } + } +} + +/*! + * This function issues the specified command to the NAND device and + * waits for completion. + * + * @param cmd command for NAND Flash + * @param useirq True if IRQ should be used rather than polling + */ +static void send_cmd(u16 cmd, bool useirq) +{ + DEBUG(MTD_DEBUG_LEVEL3, "send_cmd(0x%x, %d)\n", cmd, useirq); + + NFC_FLASH_CMD = (u16) cmd; + NFC_CONFIG2 = NFC_CMD; + + /* Wait for operation to complete */ + wait_op_done(TROP_US_DELAY, useirq); +} + +/*! + * This function sends an address (or partial address) to the + * NAND device. The address is used to select the source/destination for + * a NAND command. + * + * @param addr address to be written to NFC. + * @param useirq True if IRQ should be used rather than polling + */ +static void send_addr(u16 addr, bool useirq) +{ + DEBUG(MTD_DEBUG_LEVEL3, "send_addr(0x%x %d)\n", addr, useirq); + + NFC_FLASH_ADDR = addr; + NFC_CONFIG2 = NFC_ADDR; + + /* Wait for operation to complete */ + wait_op_done(TROP_US_DELAY, useirq); +} + +/*! + * This function requests the NFC to initate the transfer + * of data currently in the NFC RAM buffer to the NAND device. + * + * @param buf_id Specify Internal RAM Buffer number (0-3) + */ +static void send_prog_page(u8 buf_id) +{ + DEBUG(MTD_DEBUG_LEVEL3, "%s\n", __FUNCTION__); + + /* NFC buffer 0 is used for page read/write */ + NFC_BUF_ADDR = buf_id; + + NFC_CONFIG2 = NFC_INPUT; + + /* Wait for operation to complete */ + wait_op_done(TROP_US_DELAY, true); +} + +/*! + * This function requests the NFC to initated the transfer + * of data from the NAND device into in the NFC ram buffer. + * + * @param buf_id Specify Internal RAM Buffer number (0-3) + */ +static void send_read_page(u8 buf_id) +{ + DEBUG(MTD_DEBUG_LEVEL3, "%s\n", __FUNCTION__); + + /* NFC buffer 0 is used for page read/write */ + NFC_BUF_ADDR = buf_id; + + NFC_CONFIG2 = NFC_OUTPUT; + + /* Wait for operation to complete */ + wait_op_done(TROP_US_DELAY, true); +} + +/*! + * This function requests the NFC to perform a read of the + * NAND device ID. + */ +static void send_read_id(void) +{ + /* NFC buffer 0 is used for device ID output */ + NFC_BUF_ADDR = 0x0; + + /* Read ID into main buffer */ + NFC_CONFIG2 = NFC_ID; + + /* Wait for operation to complete */ + wait_op_done(TROP_US_DELAY, true); +} + +/*! + * This function requests the NFC to perform a read of the + * NAND device status and returns the current status. + * + * @return device status + */ +static u16 get_dev_status(void) +{ + volatile u16 *mainBuf = MAIN_AREA1; + u32 store; + u16 ret; + /* Issue status request to NAND device */ + + /* store the main area1 first word, later do recovery */ + store = *((u32 *) mainBuf); + /* + * NFC buffer 1 is used for device status to prevent + * corruption of read/write buffer on status requests. + */ + NFC_BUF_ADDR = 1; + + /* Read status into main buffer */ + NFC_CONFIG2 = NFC_STATUS; + + /* Wait for operation to complete */ + wait_op_done(TROP_US_DELAY, true); + + /* Status is placed in first word of main buffer */ + /* get status, then recovery area 1 data */ + ret = mainBuf[0]; + *((u32 *) mainBuf) = store; + + return ret; +} + +/*! + * This functions is used by upper layer to checks if device is ready + * + * @param mtd MTD structure for the NAND Flash + * + * @return 0 if device is busy else 1 + */ +static int mxc_nand_dev_ready(struct mtd_info *mtd) +{ + /* + * NFC handles R/B internally.Therefore,this function + * always returns status as ready. + */ + return 1; +} + +static void mxc_nand_enable_hwecc(struct mtd_info *mtd, int mode) +{ + NFC_CONFIG1 |= NFC_ECC_EN; + return; +} + +/* + * Function to record the ECC corrected/uncorrected errors resulted + * after a page read. This NFC detects and corrects upto to 4 symbols + * of 9-bits each. + */ +static int mxc_check_ecc_status(struct mtd_info *mtd) +{ + u16 ecc_stat, err; + int no_subpages = 1; + int ret = 0; + + if (IS_2K_PAGE_NAND) { + no_subpages = 4; + } + + ecc_stat = NFC_ECC_STATUS_RESULT; + do { + err = ecc_stat & 0x7; + if (err > 0x4) { + return -1; + } else { + ret += err; + } + ecc_stat >>= 4; + } while (--no_subpages); + + return ret; +} + +/* + * Function to correct the detected errors. This NFC corrects all the errors + * detected. So this function is not required. + */ +static int mxc_nand_correct_data(struct mtd_info *mtd, u_char * dat, + u_char * read_ecc, u_char * calc_ecc) +{ + panic("Shouldn't be called here: %d\n", __LINE__); + return 0; //FIXME +} + +/* + * Function to calculate the ECC for the data to be stored in the Nand device. + * This NFC has a hardware RS(511,503) ECC engine together with the RS ECC + * CONTROL blocks are responsible for detection and correction of up to + * 4 symbols of 9 bits each in 528 byte page. + * So this function is not required. + */ +static int mxc_nand_calculate_ecc(struct mtd_info *mtd, const u_char * dat, + u_char * ecc_code) +{ + panic(KERN_ERR "Shouldn't be called here %d \n", __LINE__); + return 0; //FIXME +} + +/*! + * This function reads byte from the NAND Flash + * + * @param mtd MTD structure for the NAND Flash + * + * @return data read from the NAND Flash + */ +static u_char mxc_nand_read_byte(struct mtd_info *mtd) +{ + u_char retVal = 0; + u16 col, rdWord; + volatile u16 *mainBuf = MAIN_AREA0; + volatile u16 *spareBuf = SPARE_AREA0; + + /* Check for status request */ + if (g_nandfc_info.bStatusRequest) { + return (get_dev_status() & 0xFF); + } + + /* Get column for 16-bit access */ + col = g_nandfc_info.colAddr >> 1; + + /* If we are accessing the spare region */ + if (g_nandfc_info.bSpareOnly) { + rdWord = spareBuf[col]; + } else { + rdWord = mainBuf[col]; + } + + /* Pick upper/lower byte of word from RAM buffer */ + if (g_nandfc_info.colAddr & 0x1) { + retVal = (rdWord >> 8) & 0xFF; + } else { + retVal = rdWord & 0xFF; + } + + /* Update saved column address */ + g_nandfc_info.colAddr++; + + return retVal; +} + +/*! + * This function reads word from the NAND Flash + * + * @param mtd MTD structure for the NAND Flash + * + * @return data read from the NAND Flash + */ +static u16 mxc_nand_read_word(struct mtd_info *mtd) +{ + u16 col, rdWord; + volatile u16 *mainBuf = MAIN_AREA0; + volatile u16 *spareBuf = SPARE_AREA0; + + /* Get column for 16-bit access */ + col = g_nandfc_info.colAddr >> 1; + + /* If we are accessing the spare region */ + if (g_nandfc_info.bSpareOnly) { + rdWord = spareBuf[col]; + } else { + rdWord = mainBuf[col]; + } + + /* Update saved column address */ + g_nandfc_info.colAddr += 2; + + return rdWord; +} + +/*! + * This function reads byte from the NAND Flash + * + * @param mtd MTD structure for the NAND Flash + * + * @return data read from the NAND Flash + */ +static u_char mxc_nand_read_byte16(struct mtd_info *mtd) +{ + return mxc_nand_read_word(mtd) & 0xFF; +} + +/*! + * This function writes data of length \b len from buffer \b buf to the NAND + * internal RAM buffer's MAIN area 0. + * + * @param mtd MTD structure for the NAND Flash + * @param buf data to be written to NAND Flash + * @param len number of bytes to be written + */ +static void mxc_nand_write_buf(struct mtd_info *mtd, + const u_char * buf, int len) +{ + volatile uint32_t *base; + panic("re-work needed\n"); + if (g_nandfc_info.colAddr >= mtd->writesize || g_nandfc_info.bSpareOnly) { + base = (uint32_t *) SPARE_AREA0; + } else { + g_nandfc_info.colAddr += len; + base = (uint32_t *) MAIN_AREA0; + } + memcpy((void *)base, (void *)buf, len); +} + +/*! + * This function id is used to read the data buffer from the NAND Flash. To + * read the data from NAND Flash first the data output cycle is initiated by + * the NFC, which copies the data to RAMbuffer. This data of length \b len is + * then copied to buffer \b buf. + * + * @param mtd MTD structure for the NAND Flash + * @param buf data to be read from NAND Flash + * @param len number of bytes to be read + */ +static void mxc_nand_read_buf(struct mtd_info *mtd, u_char * buf, int len) +{ + volatile uint32_t *base; + + if (g_nandfc_info.colAddr >= mtd->writesize || g_nandfc_info.bSpareOnly) { + base = (uint32_t *) SPARE_AREA0; + } else { + base = (uint32_t *) MAIN_AREA0; + g_nandfc_info.colAddr += len; + } + nfc_memcpy((void *)buf, (void *)base, len); +} + +/*! + * This function is used by the upper layer to verify the data in NAND Flash + * with the data in the \b buf. + * + * @param mtd MTD structure for the NAND Flash + * @param buf data to be verified + * @param len length of the data to be verified + * + * @return -EFAULT if error else 0 + * + */ +static int mxc_nand_verify_buf(struct mtd_info *mtd, const u_char * buf, + int len) +{ + volatile u32 *mainBuf = (u32 *) MAIN_AREA0; + /* check for 32-bit alignment? */ + uint32_t *p = (uint32_t *) buf; + if (IS_2K_PAGE_NAND) + mxc_swap_2k_bi_main_sp(); + for (; len > 0; len -= 4) { + if (*p++ != *mainBuf++) { + return -EFAULT; + } + } + + return 0; +} + +/*! + * This function is used by upper layer for select and deselect of the NAND + * chip + * + * @param mtd MTD structure for the NAND Flash + * @param chip val indicating select or deselect + */ +static void mxc_nand_select_chip(struct mtd_info *mtd, int chip) +{ +#ifdef CONFIG_MTD_NAND_MXC_FORCE_CE + if (chip > 0) { + DEBUG(MTD_DEBUG_LEVEL0, + "ERROR: Illegal chip select (chip = %d)\n", chip); + return; + } + + if (chip == -1) { + NFC_CONFIG1 &= ~(NFC_CE); + return; + } + + NFC_CONFIG1 |= NFC_CE; +#endif + + switch (chip) { + case -1: + /* Disable the NFC clock */ + clk_disable(nfc_clk); + break; + case 0: + /* Enable the NFC clock */ + clk_enable(nfc_clk); + break; + + default: + break; + } +} + +/* + * Function to perform the address cycles. + */ +static void mxc_do_addr_cycle(struct mtd_info *mtd, int column, int page_addr) +{ + u32 page_mask = g_page_mask; + + if (column != -1) { + send_addr(column & 0xFF, false); + if (IS_2K_PAGE_NAND) { + /* another col addr cycle for 2k page */ + send_addr((column >> 8) & 0xF, false); + } + } + if (page_addr != -1) { + do { + send_addr((page_addr & 0xff), false); + page_mask >>= 8; + page_addr >>= 8; + } while (page_mask != 0); + } +} + +/* + * Function to read a page from nand device. + */ +static void read_full_page(struct mtd_info *mtd, int page_addr) +{ + send_cmd(NAND_CMD_READ0, false); + + mxc_do_addr_cycle(mtd, 0, page_addr); + + if (IS_2K_PAGE_NAND) { + send_cmd(NAND_CMD_READSTART, false); + send_read_page(0); + send_read_page(1); + send_read_page(2); + send_read_page(3); + mxc_swap_2k_bi_main_sp(); + } else { + send_read_page(0); + } +} + +/* + * Function to check if the page read is a clean page.(Valid only + * the first page of the block. + * It is done by checking if all spare data of the page is all 0xFF. + * This is valid even if ECC generated is all 0xFF as the JFFS2 places + * clean marker bytes in the first page of each block which is non 0xFF. + */ +static int is_page_clean(struct mtd_info *mtd) +{ + volatile u32 *p = (u32 *) SPARE_AREA0; + int len; + + /*Check spare page */ + len = mtd->oobsize; + for (; len > 0; len -= 4) { + if (*p++ != 0xFFFFFFFF) { + return 0; + } + } + + return 1; +} + +/*! + * This function is used by the upper layer to write command to NAND Flash for + * different operations to be carried out on NAND Flash + * + * @param mtd MTD structure for the NAND Flash + * @param command command for NAND Flash + * @param column column offset for the page read + * @param page_addr page to be read from NAND Flash + */ +static void mxc_nand_command(struct mtd_info *mtd, unsigned command, + int column, int page_addr) +{ + bool useirq = true; + + DEBUG(MTD_DEBUG_LEVEL3, + "mxc_nand_command (cmd = 0x%x, col = 0x%x, page = 0x%x)\n", + command, column, page_addr); + + /* + * Reset command state information + */ + g_nandfc_info.bStatusRequest = false; + + /* Reset column address to 0 */ + g_nandfc_info.colAddr = 0; + + /* + * Command pre-processing step + */ + switch (command) { + case NAND_CMD_STATUS: + g_nandfc_info.bStatusRequest = true; + break; + + case NAND_CMD_READ0: + g_nandfc_info.bSpareOnly = false; + useirq = false; + break; + + case NAND_CMD_READOOB: + g_nandfc_info.colAddr = column; + g_nandfc_info.bSpareOnly = true; + useirq = false; + command = NAND_CMD_READ0; /* only READ0 is valid */ + break; + + case NAND_CMD_SEQIN: + if (column >= mtd->writesize) { + g_nandfc_info.bSpareOnly = true; + mark_oob_data_dirty(page_addr, 1); + } else { + if (is_oob_data_dirty(page_addr)) { + memcpy((void *)SPARE_AREA0, shadow_oob_data, + mtd->oobsize); + } else { + memset((void *)SPARE_AREA0, 0xFF, mtd->oobsize); + } + g_nandfc_info.bSpareOnly = false; + /* Set program pointer to page start */ + send_cmd(NAND_CMD_READ0, false); + } + useirq = false; + break; + + case NAND_CMD_PAGEPROG: + if (!g_nandfc_info.bSpareOnly) { + send_prog_page(0); + if (IS_2K_PAGE_NAND) { + send_prog_page(1); + send_prog_page(2); + send_prog_page(3); + } + } else { + return; + } + break; + + case NAND_CMD_ERASE1: + /*Decide to erase */ + read_full_page(mtd, page_addr); + if (is_page_clean(mtd)) { + mark_oob_data_clean(page_addr); + skip_erase = 1; + return; + } + useirq = false; + break; + case NAND_CMD_ERASE2: + if (skip_erase) { + skip_erase = 0; + return; + } + useirq = false; + break; + } + + /* + * Write out the command to the device. + */ + send_cmd(command, useirq); + + mxc_do_addr_cycle(mtd, column, page_addr); + + /* + * Command post-processing step + */ + switch (command) { + + case NAND_CMD_READOOB: + case NAND_CMD_READ0: + if (IS_2K_PAGE_NAND) { + /* send read confirm command */ + send_cmd(NAND_CMD_READSTART, true); + /* read for each AREA */ + send_read_page(0); + send_read_page(1); + send_read_page(2); + send_read_page(3); + } else { + send_read_page(0); + } + break; + + case NAND_CMD_READID: + send_read_id(); + break; + } +} + +#ifdef CONFIG_MXC_NAND_LOW_LEVEL_ERASE +static void mxc_low_erase(struct mtd_info *mtd) +{ + + struct nand_chip *this = mtd->priv; + unsigned int page_addr, addr; + u_char status; + + DEBUG(MTD_DEBUG_LEVEL0, "MXC_ND : mxc_low_erase:Erasing NAND\n"); + for (addr = 0; addr < this->chipsize; addr += mtd->erasesize) { + page_addr = addr / mtd->writesize; + mxc_nand_command(mtd, NAND_CMD_ERASE1, -1, page_addr); + mxc_nand_command(mtd, NAND_CMD_ERASE2, -1, -1); + mxc_nand_command(mtd, NAND_CMD_STATUS, -1, -1); + status = mxc_nand_read_byte(mtd); + if (status & NAND_STATUS_FAIL) { + printk(KERN_ERR + "ERASE FAILED(block = %d,status = 0x%x)\n", + addr / mtd->erasesize, status); + } + } + +} +#else +#define mxc_low_erase(x) +#endif + +// Kevin: why do we need this??? +static int mxc_nand_wait(struct mtd_info *mtd, struct nand_chip *chip) +{ + unsigned long timeo = jiffies; + int status, state = chip->state; + + if (state == FL_ERASING) + timeo += (HZ * 400) / 1000; + else + timeo += (HZ * 20) / 1000; + + send_cmd(NAND_CMD_STATUS, 1); + + while (time_before(jiffies, timeo)) { + if (get_dev_status() & NAND_STATUS_READY) + break; + cond_resched(); + } + + status = (int)(get_dev_status()); + return status; +} + +static int mxc_nand_read_oob(struct mtd_info *mtd, struct nand_chip *chip, + int page, int sndcmd) +{ + if (scan_done && is_oob_data_dirty(page)) { + memcpy((void *)chip->oob_poi, (void *)shadow_oob_data, + mtd->oobsize); + return 0; + } + + if (sndcmd) { + read_full_page(mtd, page); + sndcmd = 0; + } + nfc_memcpy((void *)chip->oob_poi, (void *)SPARE_AREA0, mtd->oobsize); + + return sndcmd; +} + +static int mxc_nand_write_oob(struct mtd_info *mtd, struct nand_chip *chip, + int page) +{ + int status = 0; + const uint8_t *buf = chip->oob_poi; + + //FIXME Check for bad block marking + if (0xFF == buf[chip->badblockpos]) { + mark_oob_data_dirty(page, 1); + } else { + int read_oob_col; + int read_oob_cmd; + volatile uint16_t *p_addr; + + if (IS_2K_PAGE_NAND) { + read_oob_col = 2048; + read_oob_cmd = NAND_CMD_READ0; + p_addr = MAIN_AREA0; + } else { + read_oob_col = 0; + read_oob_cmd = NAND_CMD_READOOB; + p_addr = SPARE_AREA0; + } + /* This is only place, Spare Only write happens */ + send_cmd(read_oob_cmd, false); + send_cmd(NAND_CMD_SEQIN, false); + mxc_do_addr_cycle(mtd, read_oob_col, page); + + memcpy((void *)p_addr, buf, mtd->oobsize); + /* Send command to program the OOB data */ + send_prog_page(0); + send_cmd(NAND_CMD_PAGEPROG, true); + + status = mxc_nand_wait(mtd, chip); + + if (status & NAND_STATUS_FAIL) + return -EIO; + } + + return 0; +} + +// read column 464-465 byte but only 464 for bad block marker +#define BAD_BLK_MARKER_464 (*((volatile u16 *)IO_ADDRESS(NFC_BASE_ADDR + 0x600 + 464))) +// read column 0-1 byte, but only 1 is used for swapped main area data +#define BAD_BLK_MARKER_SP_0 (*((volatile u16 *)IO_ADDRESS(NFC_BASE_ADDR + 0x800))) + +/* + * This function does the trick of swapping the 464th byte in the last RAM + * buffer in the main area with the 0th byte in the spare area. This seems + * to be the optimal way of addressing the NFC imcompatibility problem with + * the NAND flash out of factory in terms of BI field. + * Note: this function only operates on the NFC's internal RAM buffers and + * for 2K page only. + */ +static void mxc_swap_2k_bi_main_sp(void) +{ + u16 tmp1, tmp2, new_tmp1; + tmp1 = BAD_BLK_MARKER_464; + tmp2 = BAD_BLK_MARKER_SP_0; + + new_tmp1 = (tmp1 & 0xFF00) | (tmp2 >> 8); + tmp2 = (tmp1 << 8) | (tmp2 & 0xFF); + BAD_BLK_MARKER_464 = new_tmp1; + BAD_BLK_MARKER_SP_0 = tmp2; +} + +/* Kevin: This is solid but need to optimize the nfc_memcpy */ +static int mxc_nand_read_page(struct mtd_info *mtd, struct nand_chip *chip, + uint8_t * buf) +{ + int stat; + + stat = mxc_check_ecc_status(mtd); + if (stat == -1) { + mtd->ecc_stats.failed++; + printk(KERN_WARNING "UnCorrectable RS-ECC Error\n"); + } else { + mtd->ecc_stats.corrected += stat; + if (stat) + pr_debug("%d Symbol Correctable RS-ECC Error\n", stat); + } + + if (IS_2K_PAGE_NAND) { + mxc_swap_2k_bi_main_sp(); + } + + nfc_memcpy((void *)buf, (void *)MAIN_AREA0, mtd->writesize); + + return 0; +} + +/* Kevin: This is clean and solid */ +static void mxc_nand_write_page(struct mtd_info *mtd, struct nand_chip *chip, + const uint8_t * buf) +{ + memcpy((void *)MAIN_AREA0, buf, mtd->writesize); + + if (IS_2K_PAGE_NAND) { + mxc_swap_2k_bi_main_sp(); + } +} + +/* Define some generic bad / good block scan pattern which are used + * while scanning a device for factory marked good / bad blocks. */ +static uint8_t scan_ff_pattern[] = { 0xff, 0xff }; + +static struct nand_bbt_descr smallpage_memorybased = { + .options = NAND_BBT_SCAN2NDPAGE, + .offs = 5, + .len = 1, + .pattern = scan_ff_pattern +}; + +static struct nand_bbt_descr largepage_memorybased = { + .options = 0, + .offs = 0, + .len = 2, + .pattern = scan_ff_pattern +}; + +static int mxc_nand_scan_bbt(struct mtd_info *mtd) +{ + struct nand_chip *this = mtd->priv; + + /* Do some configurations before scanning */ + page_to_block_shift = this->phys_erase_shift - this->page_shift; + g_page_mask = this->pagemask; + + if (IS_2K_PAGE_NAND) { + NFMS |= (1 << NFMS_NF_PG_SZ); + this->ecc.layout = &nand_hw_eccoob_2k; + shadow_oob_data = oob_data_2k; + } else { + this->ecc.layout = &nand_hw_eccoob_512; + shadow_oob_data = oob_data_512; + } + + /* propagate ecc.layout to mtd_info */ + mtd->ecclayout = this->ecc.layout; + + /* NAND bus width determines access funtions used by upper layer */ + if (this->options & NAND_BUSWIDTH_16) { + NFMS |= (1 << NFMS_NF_DWIDTH); + } + + this->bbt_td = NULL; + this->bbt_md = NULL; + if (!this->badblock_pattern) { + this->badblock_pattern = (mtd->writesize > 512) ? + &largepage_memorybased : &smallpage_memorybased; + } + /* Build bad block table */ + return nand_scan_bbt(mtd, this->badblock_pattern); +} + +/*! + * This function is called during the driver binding process. + * + * @param pdev the device structure used to store device specific + * information that is used by the suspend, resume and + * remove functions + * + * @return The function always returns 0. + */ +static int __init mxcnd_probe(struct platform_device *pdev) +{ + struct nand_chip *this; + struct mtd_info *mtd; + struct flash_platform_data *flash = pdev->dev.platform_data; + int nr_parts = 0, n; + + int err = 0; + + /* Allocate memory for MTD device structure and private data */ + mxc_nand_data = kmalloc(sizeof(struct mxc_mtd_s), GFP_KERNEL); + if (!mxc_nand_data) { + printk(KERN_ERR "%s: failed to allocate mtd_info\n", + __FUNCTION__); + err = -ENOMEM; + goto out; + } + memset(mxc_nand_data, 0, sizeof(struct mxc_mtd_s)); + memset((char *)&g_nandfc_info, 0, sizeof(g_nandfc_info)); + + mxc_nand_data->dev = &pdev->dev; + /* structures must be linked */ + this = &mxc_nand_data->nand; + mtd = &mxc_nand_data->mtd; + mtd->priv = this; + mtd->owner = THIS_MODULE; + + /* 5 us command delay time */ + this->chip_delay = 5; + + this->priv = mxc_nand_data; + this->dev_ready = mxc_nand_dev_ready; + this->cmdfunc = mxc_nand_command; + this->waitfunc = mxc_nand_wait; + this->select_chip = mxc_nand_select_chip; + this->read_byte = mxc_nand_read_byte; + this->read_word = mxc_nand_read_word; + this->write_buf = mxc_nand_write_buf; + this->read_buf = mxc_nand_read_buf; + this->verify_buf = mxc_nand_verify_buf; + this->scan_bbt = mxc_nand_scan_bbt; + if (flash->width == 2) { + this->options |= NAND_BUSWIDTH_16; + this->read_byte = mxc_nand_read_byte16; + } + + nfc_clk = clk_get(&pdev->dev, "nfc_clk"); + clk_enable(nfc_clk); /* Enabled here to satisfy following reset command to succeed */ + + NFC_CONFIG1 |= NFC_INT_MSK; + init_waitqueue_head(&irq_waitq); + err = request_irq(INT_NANDFC, mxc_nfc_irq, 0, "mxc_nd", NULL); + if (err) { + goto out_1; + } + + if (hardware_ecc) { + this->ecc.read_page = mxc_nand_read_page; + this->ecc.write_page = mxc_nand_write_page; + this->ecc.read_oob = mxc_nand_read_oob; + this->ecc.write_oob = mxc_nand_write_oob; + this->ecc.layout = &nand_hw_eccoob_512; + this->ecc.calculate = mxc_nand_calculate_ecc; + this->ecc.hwctl = mxc_nand_enable_hwecc; + this->ecc.correct = mxc_nand_correct_data; + this->ecc.mode = NAND_ECC_HW; + this->ecc.size = 512; /* RS-ECC is applied for both MAIN+SPARE not MAIN alone */ + this->ecc.bytes = 9; /* used for both main and spare area */ + NFC_CONFIG1 |= NFC_ECC_EN; + } else { + this->ecc.mode = NAND_ECC_SOFT; + NFC_CONFIG1 &= ~NFC_ECC_EN; + } + NFC_CONFIG1 &= ~(NFC_SP_EN); + + /* Reset NAND */ + this->cmdfunc(mtd, NAND_CMD_RESET, -1, -1); + + /* preset operation */ + /* Unlock the internal RAM Buffer */ + NFC_CONFIG = 0x2; + + /* Blocks to be unlocked */ + NFC_UNLOCKSTART_BLKADDR = 0x0; + NFC_UNLOCKEND_BLKADDR = 0xFFFF; + + /* Unlock Block Command for given address range */ + NFC_WRPROT = 0x4; + + /* Scan to find existence of the device */ + if (nand_scan(mtd, 1)) { + DEBUG(MTD_DEBUG_LEVEL0, + "MXC_ND: Unable to find any NAND device.\n"); + err = -ENXIO; + goto out_1; + } + scan_done = 1; + + /* Register the partitions */ +#ifdef CONFIG_MTD_PARTITIONS + nr_parts = + parse_mtd_partitions(mtd, part_probes, &mxc_nand_data->parts, 0); + if (nr_parts > 0) + add_mtd_partitions(mtd, mxc_nand_data->parts, nr_parts); + else if (flash->parts) + add_mtd_partitions(mtd, flash->parts, flash->nr_parts); + else +#endif + { + pr_info("Registering %s as whole device\n", mtd->name); + add_mtd_device(mtd); + } + + platform_set_drvdata(pdev, mtd); + + n = mtd->size / mtd->erasesize; + /* each bit is used for one page's dirty information */ + oob_data_shadow_p = (u8 *) kzalloc(n / 8, GFP_KERNEL); + if (!oob_data_shadow_p) { + printk(KERN_ERR "%s: failed to allocate oob_data_shadow_p\n", + __FUNCTION__); + err = -ENOMEM; + goto out; + } + + /* Erase all the blocks of a NAND -- depend on the config */ + mxc_low_erase(mtd); + + return 0; + + out_1: + kfree(mxc_nand_data); + out: + return err; + +} + + /*! + * Dissociates the driver from the device. + * + * @param pdev the device structure used to give information on which + * + * @return The function always returns 0. + */ + +static int __exit mxcnd_remove(struct platform_device *pdev) +{ + struct mtd_info *mtd = platform_get_drvdata(pdev); + + clk_disable(nfc_clk); + clk_put(nfc_clk); + platform_set_drvdata(pdev, NULL); + + if (mxc_nand_data) { + nand_release(mtd); + free_irq(INT_NANDFC, NULL); + kfree(mxc_nand_data); + } + + return 0; +} + +#ifdef CONFIG_PM +/*! + * This function is called to put the NAND in a low power state. Refer to the + * document driver-model/driver.txt in the kernel source tree for more + * information. + * + * @param pdev the device information structure + * + * @param state the power state the device is entering + * + * @return The function returns 0 on success and -1 on failure + */ + +static int mxcnd_suspend(struct platform_device *pdev, pm_message_t state) +{ + struct mtd_info *info = platform_get_drvdata(pdev); + int ret = 0; + + DEBUG(MTD_DEBUG_LEVEL0, "MXC_ND : NAND suspend\n"); + if (info) + ret = info->suspend(info); + + /* Disable the NFC clock */ + clk_disable(nfc_clk); + + return ret; +} + +/*! + * This function is called to bring the NAND back from a low power state. Refer + * to the document driver-model/driver.txt in the kernel source tree for more + * information. + * + * @param pdev the device information structure + * + * @return The function returns 0 on success and -1 on failure + */ +static int mxcnd_resume(struct platform_device *pdev) +{ + struct mtd_info *info = platform_get_drvdata(pdev); + int ret = 0; + + DEBUG(MTD_DEBUG_LEVEL0, "MXC_ND : NAND resume\n"); + /* Enable the NFC clock */ + clk_enable(nfc_clk); + + if (info) { + info->resume(info); + } + + return ret; +} + +#else +#define mxcnd_suspend NULL +#define mxcnd_resume NULL +#endif /* CONFIG_PM */ + +/*! + * This structure contains pointers to the power management callback functions. + */ +static struct platform_driver mxcnd_driver = { + .driver = { + .name = "mxc_nandv2_flash", + }, + .probe = mxcnd_probe, + .remove = __exit_p(mxcnd_remove), + .suspend = mxcnd_suspend, + .resume = mxcnd_resume, +}; + +/*! + * Main initialization routine + * @return 0 if successful; non-zero otherwise + */ +static int __init mxc_nd_init(void) +{ + /* Register the device driver structure. */ + pr_info("MXC MTD nand Driver %s\n", DVR_VER); + if (platform_driver_register(&mxcnd_driver) != 0) { + printk(KERN_ERR "Driver register failed for mxcnd_driver\n"); + return -ENODEV; + } + return 0; +} + +/*! + * Clean up routine + */ +static void __exit mxc_nd_cleanup(void) +{ + /* Unregister the device structure */ + platform_driver_unregister(&mxcnd_driver); +} + +module_init(mxc_nd_init); +module_exit(mxc_nd_cleanup); + +MODULE_AUTHOR("Freescale Semiconductor, Inc."); +MODULE_DESCRIPTION("MXC NAND MTD driver Version 2"); +MODULE_LICENSE("GPL"); diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h index d2365c8dcacc..ad617e590cbd 100644 --- a/include/linux/mtd/nand.h +++ b/include/linux/mtd/nand.h @@ -48,6 +48,9 @@ extern void nand_wait_ready(struct mtd_info *mtd); #define NAND_MAX_OOBSIZE 64 #define NAND_MAX_PAGESIZE 2048 +#define NAND_PAGESIZE_512B 512 +#define NAND_PAGESIZE_2KB 2048 + /* * Constants for hardware specific CLE/ALE/NCE function * |