diff -urN linux-2.6.24.orig/arch/powerpc/boot/dts/mpc8313erdb.dts linux-2.6.24/arch/powerpc/boot/dts/mpc8313erdb.dts --- linux-2.6.24.orig/arch/powerpc/boot/dts/mpc8313erdb.dts 2008-01-24 23:58:37.000000000 +0100 +++ linux-2.6.24/arch/powerpc/boot/dts/mpc8313erdb.dts 2008-02-18 16:39:43.000000000 +0100 @@ -36,6 +36,12 @@ device_type = "memory"; reg = <00000000 08000000>; // 128MB at 0 }; + + nand0 { + device_type = "nand"; + compatible = "fsl-nand"; + reg = ; + }; soc8313@e0000000 { #address-cells = <1>; @@ -177,6 +183,16 @@ reg = <700 100>; device_type = "ipic"; }; + + elbc@5000 { + device_type = "elbc"; + compatible = "fsl-elbc"; + reg = <5000 1000>; + interrupts = <4d 8>; + interrupt-parent = < &ipic >; + allow-direct-device-sleep; + }; + }; pci@e0008500 { diff -urN linux-2.6.24.orig/arch/powerpc/sysdev/fsl_soc.c linux-2.6.24/arch/powerpc/sysdev/fsl_soc.c --- linux-2.6.24.orig/arch/powerpc/sysdev/fsl_soc.c 2008-01-24 23:58:37.000000000 +0100 +++ linux-2.6.24/arch/powerpc/sysdev/fsl_soc.c 2008-02-18 17:07:57.000000000 +0100 @@ -6,6 +6,12 @@ * 2006 (c) MontaVista Software, Inc. * Vitaly Bordug * + * Change log: + * Copyright (C) 2006 Freescale Semiconductor, Inc. + * 2006: Lo Wilson (r43300@freescale.com) + * Added support for Enhanced Local Bus Controller + * Added support for USB UTMI mode on-chip PHY + * * 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 @@ -28,6 +34,8 @@ #include #include #include +#include +#include #include #include @@ -671,6 +679,75 @@ arch_initcall(fsl_usb_of_init); +static int __init fsl_elbc_of_init(void) +{ + struct device_node *np; + unsigned int i; + struct platform_device *elbc_dev = NULL; + struct platform_device *nand_dev = NULL; + int ret; + + /* find and register the enhanced local bus controller */ + for (np = NULL, i = 0; + (np = of_find_compatible_node(np, "elbc", "fsl-elbc")) != NULL; + i++) { + struct resource r[2]; + + memset(&r, 0, sizeof(r)); + + ret = of_address_to_resource(np, 0, &r[0]); + if (ret) + goto err; + + r[1].start = r[1].end = irq_of_parse_and_map(np, 0); + r[1].flags = IORESOURCE_IRQ; + + elbc_dev = + platform_device_register_simple("fsl-elbc", i, r, 2); + if (IS_ERR(elbc_dev)) { + ret = PTR_ERR(elbc_dev); + goto err; + } + } + + /* find and register NAND memories if the eLBC was found */ + for (np = NULL, i = 0; + elbc_dev && + (np = of_find_compatible_node(np, "nand", "fsl-nand")) != NULL; + i++) { + struct resource r; + struct platform_fsl_nand_chip chip_data; + + memset(&r, 0, sizeof(r)); + memset(&chip_data, 0, sizeof(chip_data)); + + ret = of_address_to_resource(np, 0, &r); + if (ret) + goto err; + + nand_dev = + platform_device_register_simple("fsl-nand", i, &r, 1); + if (IS_ERR(nand_dev)) { + ret = PTR_ERR(nand_dev); + goto err; + } + + chip_data.name = of_get_property(np, "name", NULL); + chip_data.partitions_str = of_get_property(np, "partitions", NULL); + + ret = platform_device_add_data(nand_dev, &chip_data, + sizeof(struct platform_fsl_nand_chip)); + if (ret) + goto err; + } + return 0; + +err: + return ret; +} + +arch_initcall(fsl_elbc_of_init); + #ifndef CONFIG_PPC_CPM_NEW_BINDING #ifdef CONFIG_CPM2 diff -urN linux-2.6.24.orig/drivers/mtd/nand/fsl_elbc.c linux-2.6.24/drivers/mtd/nand/fsl_elbc.c --- linux-2.6.24.orig/drivers/mtd/nand/fsl_elbc.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24/drivers/mtd/nand/fsl_elbc.c 2008-02-18 17:08:08.000000000 +0100 @@ -0,0 +1,1324 @@ +/* linux/drivers/mtd/nand/fsl_elbc.c + * + * Copyright (C) 2006 Freescale Semiconductor, Inc. + * + * Freescale Enhanced Local Bus Controller NAND driver + * + * Author: Nick Spence + * Maintainer: Tony Li + * + * Changelog: + * 2006-12 Tony Li + * Adopt to MPC8313ERDB board + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +//#ifdef CONFIG_MTD_NAND_DEBUG +//#define DEBUG +//#endif +//#define DEBUG + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +#define PFX "fsl-elbc: " + +#undef CFG_FCM_DEBUG +#define CFG_FCM_DEBUG_LVL 3 +#ifdef CFG_FCM_DEBUG +static int fcm_debug_level = CFG_FCM_DEBUG_LVL; +#define FCM_DEBUG(n, args...) \ + do { \ + if (n <= fcm_debug_level) \ + printk(args); \ + } while(0) +#else /* CONFIG_FCM_DEBUG */ +#define FCM_DEBUG(n, args...) do { } while(0) +#endif + +#define FCM_SIZE (8 * 1024) + +#define MAX_BANKS (8) + +/* use interrupt instead of busy waiting TODO */ +#define FCM_USE_INTERRUPT + +#define MIN(x, y) ((x < y) ? x : y) + +#define ERR_BYTE 0xFF /* Value returned for read bytes when read failed */ + +#define FCM_TIMEOUT_MSECS 100 /* 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 platform_fsl_nand_chip pl_chip; + struct fsl_elbc_ctrl *ctrl; + + struct device *device; +// int nr_chips; /* Number of chips in set */ +// int nr_partitions; /* Number of partitions or 0 */ + char *name; /* Name of set (optional) */ + int *nr_map; /* Physical chip num (option)*/ +// struct mtd_partition *partitions; /* MTD partition list (option*/ +// struct nand_ecclayout *ecclayout; + unsigned int options; + struct resource *area; + int bank; /* Chip select bank number */ + unsigned int pbase; /* Chip select base physical address */ + unsigned int vbase; /* Chip select base virtual address */ + int pgs; /* NAND page size (0=512, 1=2048) */ + unsigned int fmr; /* FCM Flash Mode Register value */ +}; + +/* overview of the fsl elbc controller */ + +struct fsl_elbc_ctrl { + struct nand_hw_control controller; + struct fsl_elbc_mtd *nmtd[MAX_BANKS]; + + /* device info */ + atomic_t childs_active; + struct device *device; + struct resource *area; + lbus83xx_t *regs; + int irq; + wait_queue_head_t irq_wait; + unsigned int irq_status; /* status read from LTESR by irq handler */ + u_char *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 */ + unsigned int index; /* Pointer to next byte to 'read' */ + unsigned int status; /* status read from LTESR after last op */ + int oobbuf; /* Pointer to OOB block */ + unsigned int mdr; /* UPM/FCM Data Register value */ + unsigned int use_mdr; /* Non zero if the MDR is to be set */ +}; + +struct fsl_elbc_ctrl elbc_ctrl; + +/* These map to the positions used by the FCM hardware ECC generator */ + +/* Small Page FLASH with FMR[ECCM] = 0 */ +static struct nand_ecclayout fsl_elbc_oob_sp_eccm0 = { /* TODO */ +//TODO .useecc = MTD_NANDECC_AUTOPL_USR, /* MTD_NANDECC_PLACEONLY, */ + .eccbytes = 3, + .eccpos = {6, 7, 8}, + .oobfree = { {0, 5}, {9, 7} } +}; + +/* Small Page FLASH with FMR[ECCM] = 1 */ +static struct nand_ecclayout fsl_elbc_oob_sp_eccm1 = { /* TODO */ +//TODO .useecc = MTD_NANDECC_AUTOPL_USR, /* MTD_NANDECC_PLACEONLY, */ + .eccbytes = 3, + .eccpos = {8, 9, 10}, + .oobfree = { {0, 5}, {6, 2}, {11, 5} } +}; + +/* Large Page FLASH with FMR[ECCM] = 0 */ +static struct nand_ecclayout fsl_elbc_oob_lp_eccm0 = { +//TODO .useecc = MTD_NANDECC_AUTOPL_USR, /* MTD_NANDECC_PLACEONLY, */ + .eccbytes = 12, + .eccpos = {6, 7, 8, 22, 23, 24, 38, 39, 40, 54, 55, 56}, + .oobfree = { {1, 5}, {9, 13}, {25, 13}, {41, 13}, {57, 7} } +}; + +/* Large Page FLASH with FMR[ECCM] = 1 */ +static struct nand_ecclayout fsl_elbc_oob_lp_eccm1 = { +//TODO .useecc = MTD_NANDECC_AUTOPL_USR, /* MTD_NANDECC_PLACEONLY, */ + .eccbytes = 12, + .eccpos = {8, 9, 10, 24, 25, 26, 40, 41, 42, 56, 57, 58}, + .oobfree = { {1, 7}, {11, 13}, {27, 13}, {43, 13}, {59, 5} } +}; + +/*=================================*/ + +/* + * Set up the FCM hardware block and page address fields, and the fcm + * structure addr field to point to the correct FCM buffer in memory + */ +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 *nmtd = chip->priv; + struct fsl_elbc_ctrl *ctrl = nmtd->ctrl; + volatile lbus83xx_t *lbc = ctrl->regs; + int buf_num; + + ctrl->page = page_addr; + + lbc->fbar = page_addr >> (chip->phys_erase_shift - chip->page_shift); + if (nmtd->pgs) { + lbc->fpar = ((page_addr << FPAR_LP_PI_SHIFT) & FPAR_LP_PI) | + ( oob ? FPAR_LP_MS : 0) | + column; + buf_num = (page_addr & 1) << 2; + } else { + lbc->fpar = ((page_addr << FPAR_SP_PI_SHIFT) & FPAR_SP_PI) | + ( oob ? FPAR_SP_MS : 0) | + column; + buf_num = page_addr & 7; + } + ctrl->addr = (unsigned char*)(nmtd->vbase + (buf_num * 1024)); + + /* for OOB data point to the second half of the buffer */ + if (oob) { + ctrl->addr += (nmtd->pgs ? 2048 : 512); + } + FCM_DEBUG(2,"set_addr: bank=%d, ctrl->addr=0x%p (0x%08x)\n", buf_num, ctrl->addr, nmtd->vbase); +} + +/* + * execute FCM command and wait for it to complete + */ +static int fsl_elbc_run_command(struct mtd_info *mtd) +{ + struct nand_chip *chip = mtd->priv; + struct fsl_elbc_mtd *nmtd = chip->priv; + struct fsl_elbc_ctrl *ctrl = nmtd->ctrl; + volatile lbus83xx_t *lbc = ctrl->regs; + /* Setup the FMR[OP] to execute without write protection */ + lbc->fmr = nmtd->fmr | 3; + if (ctrl->use_mdr) + lbc->mdr = ctrl->mdr; + + FCM_DEBUG(5,"fsl_elbc_run_command: fmr= %08X fir= %08X fcr= %08X\n", + lbc->fmr, lbc->fir, lbc->fcr); + FCM_DEBUG(5,"fsl_elbc_run_command: fbar=%08X fpar=%08X fbcr=%08X bank=%d\n", + lbc->fbar, lbc->fpar, lbc->fbcr, nmtd->bank); + + /* clear event registers */ + lbc->lteatr = 0; + lbc->ltesr |= (LTESR_FCT | LTESR_PAR | LTESR_CC); + + /* execute special operation */ + lbc->lsor = nmtd->bank; + + /* wait for FCM complete flag or timeout */ +/* TODO */ +#ifdef FCM_USE_INTERRUPT + ctrl->status = ctrl->irq_status = 0; + wait_event_timeout(ctrl->irq_wait, ctrl->irq_status, FCM_TIMEOUT_MSECS * HZ/1000); + ctrl->status = ctrl->irq_status; +#else + { + unsigned long timeout; + unsigned long now; + now = jiffies_to_msecs(jiffies); + timeout = now + FCM_TIMEOUT_MSECS; + while (time_before(now, timeout)) { + ctrl->status = lbc->ltesr & (LTESR_FCT | LTESR_PAR | LTESR_CC); + if (ctrl->status) + break; + now = jiffies_to_msecs(jiffies); + } + } +#endif + + /* store mdr value in case it was needed */ + if (ctrl->use_mdr) + ctrl->mdr = lbc->mdr; + + ctrl->use_mdr = 0; + + FCM_DEBUG(5,"fsl_elbc_run_command: stat=%08X mdr= %08X fmr= %08X\n", + ctrl->status, ctrl->mdr, lbc->fmr); + + /* returns 0 on success otherwise non-zero) */ + return (ctrl->status == LTESR_CC ? 0 : EFAULT); +} + +/* cmdfunc send commands to the FCM */ +static void fsl_elbc_cmdfunc(struct mtd_info *mtd, unsigned command, + int column, int page_addr) +{ + struct nand_chip *chip = mtd->priv; + struct fsl_elbc_mtd *nmtd = chip->priv; + struct fsl_elbc_ctrl *ctrl = nmtd->ctrl; + volatile lbus83xx_t *lbc = ctrl->regs; + + ctrl->use_mdr = 0; + + /* clear the read buffer */ + ctrl->read_bytes = 0; + if (command != NAND_CMD_PAGEPROG) { + ctrl->index = 0; + ctrl->oobbuf = -1; + } + + switch (command) { + /* READ0 and READ1 read the entire buffer to use hardware ECC */ + case NAND_CMD_READ1: + FCM_DEBUG(2,"fsl_elbc_cmdfunc: NAND_CMD_READ1, page_addr:" + " 0x%x, column: 0x%x.\n", page_addr, column); + ctrl->index = column + 256; + goto read0; + case NAND_CMD_READ0: + FCM_DEBUG(2,"fsl_elbc_cmdfunc: NAND_CMD_READ0, page_addr:" + " 0x%x, column: 0x%x.\n", page_addr, column); + ctrl->index = column; +read0: + if (nmtd->pgs) { + lbc->fir = (FIR_OP_CW0 << FIR_OP0_SHIFT) | + (FIR_OP_CA << FIR_OP1_SHIFT) | + (FIR_OP_PA << FIR_OP2_SHIFT) | + (FIR_OP_CW1 << FIR_OP3_SHIFT) | + (FIR_OP_RBW << FIR_OP4_SHIFT); + } else { + lbc->fir = (FIR_OP_CW0 << FIR_OP0_SHIFT) | + (FIR_OP_CA << FIR_OP1_SHIFT) | + (FIR_OP_PA << FIR_OP2_SHIFT) | + (FIR_OP_RBW << FIR_OP3_SHIFT); + } + lbc->fcr = (NAND_CMD_READ0 << FCR_CMD0_SHIFT) | + (NAND_CMD_READSTART << FCR_CMD1_SHIFT); + lbc->fbcr = 0; /* read entire page to enable ECC */ + set_addr(mtd, 0, page_addr, 0); + ctrl->read_bytes = mtd->writesize + mtd->oobsize; + goto write_cmd2; + /* READOOB read only the OOB becasue no ECC is performed */ + case NAND_CMD_READOOB: + FCM_DEBUG(2,"fsl_elbc_cmdfunc: NAND_CMD_READOOB, page_addr:" + " 0x%x, column: 0x%x.\n", page_addr, column); + if (nmtd->pgs) { + lbc->fir = (FIR_OP_CW0 << FIR_OP0_SHIFT) | + (FIR_OP_CA << FIR_OP1_SHIFT) | + (FIR_OP_PA << FIR_OP2_SHIFT) | + (FIR_OP_CW1 << FIR_OP3_SHIFT) | + (FIR_OP_RBW << FIR_OP4_SHIFT); + lbc->fcr = (NAND_CMD_READ0 << FCR_CMD0_SHIFT) | + (NAND_CMD_READSTART << FCR_CMD1_SHIFT); + } else { + lbc->fir = (FIR_OP_CW0 << FIR_OP0_SHIFT) | + (FIR_OP_CA << FIR_OP1_SHIFT) | + (FIR_OP_PA << FIR_OP2_SHIFT) | + (FIR_OP_RBW << FIR_OP3_SHIFT); + lbc->fcr = (NAND_CMD_READOOB << FCR_CMD0_SHIFT); + } + lbc->fbcr = mtd->oobsize - column; + set_addr(mtd, column, page_addr, 1); + ctrl->read_bytes = mtd->oobsize; + ctrl->index = column; + goto write_cmd2; + /* READID must read all 5 possible bytes while CEB is active */ + case NAND_CMD_READID: + FCM_DEBUG(2,"fsl_elbc_cmdfunc: NAND_CMD_READID.\n"); + lbc->fir = (FIR_OP_CW0 << FIR_OP0_SHIFT) | + (FIR_OP_UA << FIR_OP1_SHIFT) | + (FIR_OP_RBW << FIR_OP2_SHIFT); + lbc->fcr = (NAND_CMD_READID << FCR_CMD0_SHIFT); + lbc->fbcr = 5; /* 5 bytes for manuf, device and exts */ + ctrl->use_mdr = 1; + ctrl->mdr = 0; + goto write_cmd0; + /* ERASE1 stores the block and page address */ + case NAND_CMD_ERASE1: + FCM_DEBUG(2,"fsl_elbc_cmdfunc: NAND_CMD_ERASE1, page_addr:" + " 0x%x.\n", page_addr); + set_addr(mtd, 0, page_addr, 0); + goto end; + /* ERASE2 uses the block and page address from ERASE1 */ + case NAND_CMD_ERASE2: + FCM_DEBUG(2,"fsl_elbc_cmdfunc: NAND_CMD_ERASE2.\n"); + lbc->fir = (FIR_OP_CW0 << FIR_OP0_SHIFT) | + (FIR_OP_PA << FIR_OP1_SHIFT) | + (FIR_OP_CM1 << FIR_OP2_SHIFT); + lbc->fcr = (NAND_CMD_ERASE1 << FCR_CMD0_SHIFT) | + (NAND_CMD_ERASE2 << FCR_CMD1_SHIFT); + lbc->fbcr = 0; + goto write_cmd1; + /* SEQIN sets up the addr buffer and all registers except the length */ + case NAND_CMD_SEQIN: + FCM_DEBUG(2,"fsl_elbc_cmdfunc: NAND_CMD_SEQIN/PAGE_PROG, page_addr:" + " 0x%x, column: 0x%x.\n", page_addr, column); + if (column == 0) { + lbc->fbcr = 0; /* write entire page to enable ECC */ + } else { + lbc->fbcr = 1; /* mark as partial page so no HW ECC */ + } + if (nmtd->pgs) { + /* always use READ0 for large page devices */ + lbc->fir = (FIR_OP_CW0 << FIR_OP0_SHIFT) | + (FIR_OP_CA << FIR_OP1_SHIFT) | + (FIR_OP_PA << FIR_OP2_SHIFT) | + (FIR_OP_WB << FIR_OP3_SHIFT) | + (FIR_OP_CW1 << FIR_OP4_SHIFT); + lbc->fcr = (NAND_CMD_SEQIN << FCR_CMD0_SHIFT) | + (NAND_CMD_PAGEPROG << FCR_CMD1_SHIFT); + set_addr(mtd, column, page_addr, 0); + } else { + lbc->fir = (FIR_OP_CW0 << FIR_OP0_SHIFT) | + (FIR_OP_CM2 << FIR_OP1_SHIFT) | + (FIR_OP_CA << FIR_OP2_SHIFT) | + (FIR_OP_PA << FIR_OP3_SHIFT) | + (FIR_OP_WB << FIR_OP4_SHIFT) | + (FIR_OP_CW1 << FIR_OP5_SHIFT); + if (column >= mtd->writesize) { + /* OOB area --> READOOB */ + column -= mtd->writesize; + lbc->fcr = (NAND_CMD_READOOB << FCR_CMD0_SHIFT) + | (NAND_CMD_PAGEPROG<< FCR_CMD1_SHIFT) + | (NAND_CMD_SEQIN << FCR_CMD2_SHIFT); + set_addr(mtd, column, page_addr, 1); + } else if (column < 256) { + /* First 256 bytes --> READ0 */ + lbc->fcr = (NAND_CMD_READ0 << FCR_CMD0_SHIFT) + | (NAND_CMD_PAGEPROG<< FCR_CMD1_SHIFT) + | (NAND_CMD_SEQIN << FCR_CMD2_SHIFT); + set_addr(mtd, column, page_addr, 0); + } else { + /* Second 256 bytes --> READ1 */ + column -= 256; + lbc->fcr = (NAND_CMD_READ1 << FCR_CMD0_SHIFT) + | (NAND_CMD_PAGEPROG<< FCR_CMD1_SHIFT) + | (NAND_CMD_SEQIN << FCR_CMD2_SHIFT); + set_addr(mtd, column, page_addr, 0); + } + } + goto end; + /* PAGEPROG reuses all of the setup from SEQIN and adds the length */ + case NAND_CMD_PAGEPROG: + FCM_DEBUG(2,"fsl_elbc_cmdfunc: NAND_CMD_PAGEPROG" + " writing %d bytes.\n",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 (lbc->fbcr || + (ctrl->index != (mtd->writesize + mtd->oobsize))) + lbc->fbcr = ctrl->index; + goto write_cmd2; + /* CMD_STATUS must read the status byte while CEB is active */ + /* Note - it does not wait for the ready line */ + case NAND_CMD_STATUS: + FCM_DEBUG(2,"fsl_elbc_cmdfunc: NAND_CMD_STATUS.\n"); + lbc->fir = (FIR_OP_CM0 << FIR_OP0_SHIFT) | + (FIR_OP_RBW << FIR_OP1_SHIFT); + lbc->fcr = (NAND_CMD_STATUS << FCR_CMD0_SHIFT); + lbc->fbcr = 1; + goto write_cmd0; + /* RESET without waiting for the ready line */ + case NAND_CMD_RESET: + FCM_DEBUG(2,"fsl_elbc_cmdfunc: NAND_CMD_RESET.\n"); + lbc->fir = (FIR_OP_CM0 << FIR_OP0_SHIFT); + lbc->fcr = (NAND_CMD_RESET << FCR_CMD0_SHIFT); + lbc->fbcr = 0; + goto write_cmd0; + default: + printk("fsl_elbc_cmdfunc: error, unsupported command.\n"); + goto end; + } + + /* Short cuts fall through to save code */ + write_cmd0: + set_addr(mtd, 0, 0, 0); + write_cmd1: + ctrl->read_bytes = lbc->fbcr; + write_cmd2: + fsl_elbc_run_command(mtd); + +#ifdef CONFIG_MTD_NAND_VERIFY_WRITE + /* if we wrote a page then read back the oob to get the ECC */ + if ((command == NAND_CMD_PAGEPROG) && + (chip->ecc.mode > NAND_ECC_SOFT) && + (lbc->fbcr == 0) && + (ctrl->oobbuf != 0) && + (ctrl->oobbuf != -1)) { + int i; + uint *oob_config; + unsigned char *oob_buf; + + i = ctrl->page; + oob_buf = (unsigned char*) ctrl->oobbuf; + oob_config = chip->ecc.layout->eccpos; + + /* wait for the write to complete and check it passed */ + if (!(chip->waitfunc(mtd, chip) & 0x01)) { + /* read back the OOB */ + fsl_elbc_cmdfunc(mtd, NAND_CMD_READOOB, 0, i); + /* if it succeeded then copy the ECC bytes */ + if (ctrl->status == LTESR_CC) { + for (i=0; i < chip->ecc.layout->eccbytes; i++) { + oob_buf[oob_config[i]] = + ctrl->addr[oob_config[i]]; + } + } + } + } +#endif + + end: + return; +} + +/* select chip */ + +static void fsl_elbc_select_chip(struct mtd_info *mtd, int chip) +{ +} + +/* fsl_elbc_cmd_ctrl + * + * Issue command and address cycles to the chip +*/ + +static void fsl_elbc_cmd_ctrl(struct mtd_info *mtd, int dat, + unsigned int ctrl) +{ +} + +/* fsl_elbc_dev_ready() + * + * returns 0 if the nand is busy, 1 if it is ready +*/ + +static int fsl_elbc_dev_ready(struct mtd_info *mtd) +{ + return 0; +} + +/* + * FCM does not support 16 bit data busses + */ +static u16 fsl_elbc_read_word(struct mtd_info *mtd) +{ + struct nand_chip *chip = mtd->priv; + struct fsl_elbc_ctrl *ctrl = (struct fsl_elbc_ctrl *) chip->controller; + + dev_err(ctrl->device, "fsl_elbc_read_word: UNIMPLEMENTED.\n"); + return 0; +} + +/* + * Write buf to the FCM Controller Data Buffer + */ +static void fsl_elbc_write_buf(struct mtd_info *mtd, const u_char *buf, int len) +{ + struct nand_chip *chip = mtd->priv; + struct fsl_elbc_mtd *nmtd = chip->priv; + struct fsl_elbc_ctrl *ctrl = nmtd->ctrl; + + FCM_DEBUG(3,"fsl_elbc_write_buf: writing %d bytes starting with 0x%lx" + " at %d.\n", len, *((unsigned long*) buf), ctrl->index); + + /* If armed catch the address of the OOB buffer so that it can be */ + /* updated with the real signature after the program comletes */ + if (!ctrl->oobbuf) + ctrl->oobbuf = (int) buf; + + /* copy the data into the FCM hardware buffer and update the index */ + memcpy(&(ctrl->addr[ctrl->index]), buf, len); + ctrl->index += len; + return; +} + + +/* + * read a byte from either the FCM hardware buffer if it has any data left + * otherwise issue a command to read a single byte. + */ +static u_char fsl_elbc_read_byte(struct mtd_info *mtd) +{ + struct nand_chip *chip = mtd->priv; + struct fsl_elbc_mtd *nmtd = chip->priv; + struct fsl_elbc_ctrl *ctrl = nmtd->ctrl; + volatile lbus83xx_t *lbc = ctrl->regs; + unsigned char byte; + + /* If there are still bytes in the FCM then use the next byte */ + if(ctrl->index < ctrl->read_bytes) { + byte = ctrl->addr[(ctrl->index)++]; + FCM_DEBUG(4,"fsl_elbc_read_byte: byte %u (%02X): %d of %d.\n", + byte, byte, ctrl->index-1, ctrl->read_bytes); + } else { + /* otherwise issue a command to read 1 byte */ + lbc->fir = (FIR_OP_RSW << FIR_OP0_SHIFT); + ctrl->use_mdr = 1; + ctrl->read_bytes = 0; + ctrl->index = 0; + ctrl->read_bytes = 0; + ctrl->index = 0; + byte = fsl_elbc_run_command(mtd) ? ERR_BYTE : ctrl->mdr & 0xff; + FCM_DEBUG(4,"fsl_elbc_read_byte: byte %u (%02X) from bus.\n", + byte, byte); + } + + return byte; +} + +/* + * Read from the FCM Controller Data Buffer + */ +static void fsl_elbc_read_buf(struct mtd_info *mtd, u_char* buf, int len) +{ + struct nand_chip *chip = mtd->priv; + struct fsl_elbc_mtd *nmtd = chip->priv; + struct fsl_elbc_ctrl *ctrl = nmtd->ctrl; + int i; + int rest; + unsigned long old_status; + + FCM_DEBUG(3,"fsl_elbc_read_buf: reading %d bytes.\n", len); + + /* see how much is still in the FCM buffer */ + i = min((unsigned int)len, (ctrl->read_bytes - ctrl->index)); + rest = len - i; + len = i; + + /* copying bytes even if there was an error so that the oob works */ + memcpy(buf, &(ctrl->addr[(ctrl->index)]), len); + ctrl->index += len; + + /* If more data is needed then issue another block read */ + if (rest) { + FCM_DEBUG(3,"fsl_elbc_read_buf: getting %d more bytes.\n", + rest); + + buf += len; + + /* keep last status in case it was an error */ + old_status = ctrl->status; + + /* read full next page to use HW ECC if enabled */ + fsl_elbc_cmdfunc(mtd, NAND_CMD_READ0, 0, ctrl->page + 1); + + /* preserve the worst status code */ + if (ctrl->status == LTESR_CC) + ctrl->status = old_status; + + fsl_elbc_read_buf(mtd, buf, rest); + } + return; +} + + +/* + * Verify buffer against the FCM Controller Data Buffer + */ +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 *nmtd = chip->priv; + struct fsl_elbc_ctrl *ctrl = nmtd->ctrl; + int i; + int rest; + + FCM_DEBUG(3,"fsl_elbc_verify_buf: checking %d bytes starting with 0x%02lx.\n", + len, *((unsigned long*) buf)); + + /* If last read failed then return error bytes */ + if (ctrl->status != LTESR_CC) { + return EFAULT; + } + + /* see how much is still in the FCM buffer */ + i = min((unsigned int)len, (ctrl->read_bytes - ctrl->index)); + rest = len - i; + len = i; + + if (memcmp(buf, &(ctrl->addr[(ctrl->index)]), len)) { + return EFAULT; + } + + ctrl->index += len; + if (rest) { + FCM_DEBUG(3,"fsl_elbc_verify_buf: getting %d more bytes.\n", rest); + buf += len; + + /* read full next page to use HW ECC if enabled */ + fsl_elbc_cmdfunc(mtd, NAND_CMD_READ0, 0, ctrl->page + 1); + + return fsl_elbc_verify_buf(mtd, buf, rest); + } + return 0; +} + +/* this function is called after Program and Erase Operations to + * check for success or failure */ +static int fsl_elbc_wait(struct mtd_info *mtd, struct nand_chip *this) +{ + struct nand_chip *chip = mtd->priv; + struct fsl_elbc_mtd *nmtd = chip->priv; + struct fsl_elbc_ctrl *ctrl = nmtd->ctrl; + volatile lbus83xx_t *lbc = ctrl->regs; + + if (ctrl->status != LTESR_CC) { + return(0x1); /* Status Read error */ + } + + /* Use READ_STATUS command, but wait for the device to be ready */ + ctrl->use_mdr = 0; + ctrl->oobbuf = -1; + lbc->fir = (FIR_OP_CW0 << FIR_OP0_SHIFT) | + (FIR_OP_RBW << FIR_OP1_SHIFT); + lbc->fcr = (NAND_CMD_STATUS << FCR_CMD0_SHIFT); + set_addr(mtd, 0, 0, 0); + lbc->fbcr = 1; + ctrl->index = 0; + ctrl->read_bytes = lbc->fbcr; + fsl_elbc_run_command(mtd); + if (ctrl->status != LTESR_CC) { + return(0x1); /* Status Read error */ + } + return chip->read_byte(mtd); +} + +/* ECC handling functions */ + +/* + * fsl_elbc_enable_hwecc - start ECC generation + */ +static void fsl_elbc_enable_hwecc(struct mtd_info *mtd, int mode) +{ + return; +} + +/* + * fsl_elbc_calculate_ecc - Calculate the ECC bytes + * This is done by hardware during the write process, so we use this + * to arm the oob buf capture on the next write_buf() call. The ECC bytes + * only need to be captured if CONFIG_MTD_NAND_VERIFY_WRITE is defined which + * reads back the pages and checks they match the data and oob buffers. + */ +static int fsl_elbc_calculate_ecc(struct mtd_info *mtd, const u_char *dat, u_char *ecc_code) +{ +#ifdef CONFIG_MTD_NAND_VERIFY_WRITE + struct nand_chip *chip = mtd->priv; + struct fsl_elbc_mtd *nmtd = chip->priv; + struct fsl_elbc_ctrl *ctrl = nmtd->ctrl; + + /* arm capture of oob buf ptr on next write_buf */ + ctrl->oobbuf = 0; +#endif + return 0; +} + +/* + * fsl_elbc_correct_data - Detect and correct bit error(s) + * The detection and correction is done automatically by the hardware, + * if the complete page was read. If the status code is okay then there + * was no error, otherwise we return an error code indicating an uncorrectable + * error. + */ +static int fsl_elbc_correct_data(struct mtd_info *mtd, u_char *dat, u_char *read_ecc, u_char *calc_ecc) +{ + struct nand_chip *chip = mtd->priv; + struct fsl_elbc_mtd *nmtd = chip->priv; + struct fsl_elbc_ctrl *ctrl = nmtd->ctrl; + + /* No errors */ + if (ctrl->status == LTESR_CC) + return 0; + + return -1; /* uncorrectable error */ +} + +/*************************************************************************/ +/* Chip setup and control functions */ +/*************************************************************************/ + +/* + * Dummy scan_bbt to complete setup of the FMR based on NAND size + */ +static int fsl_elbc_chip_init_tail (struct mtd_info *mtd) +{ + struct nand_chip *chip = mtd->priv; + struct fsl_elbc_mtd *nmtd = chip->priv; + struct fsl_elbc_ctrl *ctrl = nmtd->ctrl; + volatile lbus83xx_t *lbc = ctrl->regs; + unsigned int i; + unsigned int al; + + /* calculate FMR Address Length field */ + al = 0; + for (i = chip->pagemask >> 16; i ; i >>= 8) { + al++; + } + + /* add to ECCM mode set in fsl_elbc_init */ + nmtd->fmr |= 12 << FMR_CWTO_SHIFT | /* Timeout > 12 mSecs */ + al << FMR_AL_SHIFT; + + FCM_DEBUG(1,"fsl_elbc_init: nand->options = %08X\n", chip->options); + FCM_DEBUG(1,"fsl_elbc_init: nand->numchips = %10d\n", chip->numchips); + FCM_DEBUG(1,"fsl_elbc_init: nand->chipsize = %10ld\n", chip->chipsize); + FCM_DEBUG(1,"fsl_elbc_init: nand->pagemask = %10X\n", chip->pagemask); + FCM_DEBUG(1,"fsl_elbc_init: nand->chip_delay = %8d\n", chip->chip_delay); + FCM_DEBUG(1,"fsl_elbc_init: nand->badblockpos = %7d\n", chip->badblockpos); + FCM_DEBUG(1,"fsl_elbc_init: nand->chip_shift = %8d\n", chip->chip_shift); + FCM_DEBUG(1,"fsl_elbc_init: nand->page_shift = %8d\n", chip->page_shift); + FCM_DEBUG(1,"fsl_elbc_init: nand->phys_erase_shift = %2d\n", + chip->phys_erase_shift); + FCM_DEBUG(1,"fsl_elbc_init: nand->ecclayout= %10p\n", chip->ecclayout); + FCM_DEBUG(1,"fsl_elbc_init: nand->eccmode = %10d\n", chip->ecc.mode ); + FCM_DEBUG(1,"fsl_elbc_init: nand->eccsteps = %10d\n", chip->ecc.steps); + FCM_DEBUG(1,"fsl_elbc_init: nand->eccsize = %10d\n", chip->ecc.size ); + FCM_DEBUG(1,"fsl_elbc_init: nand->eccbytes = %10d\n", chip->ecc.bytes); + FCM_DEBUG(1,"fsl_elbc_init: nand->ecctotal = %10d\n", chip->ecc.total); + FCM_DEBUG(1,"fsl_elbc_init: nand->ecclayout= %10p\n", chip->ecc.layout); + FCM_DEBUG(1,"fsl_elbc_init: mtd->flags = %08X\n", mtd->flags); + FCM_DEBUG(1,"fsl_elbc_init: mtd->size = %10d\n", mtd->size); + FCM_DEBUG(1,"fsl_elbc_init: mtd->erasesize = %10d\n", mtd->erasesize); + FCM_DEBUG(1,"fsl_elbc_init: mtd->writesize = %10d\n", mtd->writesize); + FCM_DEBUG(1,"fsl_elbc_init: mtd->oobsize = %10d\n", mtd->oobsize); + FCM_DEBUG(1,"fsl_elbc_init: mtd->ecctype = %10d\n", mtd->ecctype); + FCM_DEBUG(1,"fsl_elbc_init: mtd->eccsize = %10d\n", mtd->eccsize); + + /* adjust Option Register and ECC to match Flash page size */ + if (mtd->writesize == 512) + lbc->bank[nmtd->bank].or &= ~(OR_FCM_PGS); + else if (mtd->writesize == 2048) { + lbc->bank[nmtd->bank].or |= OR_FCM_PGS; + /* adjust ecc setup if needed */ + if ( (lbc->bank[nmtd->bank].br & BR_DECC) == BR_DECC_CHK_GEN) { + chip->ecc.size = 2048; + chip->ecc.steps = 1; +//TODO chip->ecc.bytes += 9; +//TODO chip->ecc.total += 9; + chip->ecc.layout = (nmtd->fmr & FMR_ECCM) ? + &fsl_elbc_oob_lp_eccm1 : &fsl_elbc_oob_lp_eccm0; + mtd->ecclayout = chip->ecc.layout; + } + } + else { + printk("fsl_elbc_init: page size %d is not supported\n", + mtd->writesize); + return -1; + } + nmtd->pgs = (lbc->bank[nmtd->bank].or>>OR_FCM_PGS_SHIFT) & 1; + + /* fix up the oobavail size in case the layout was changed */ + chip->ecc.layout->oobavail = 0; + for (i = 0; chip->ecc.layout->oobfree[i].length; i++) + chip->ecc.layout->oobavail += + chip->ecc.layout->oobfree[i].length; + + /* return to the default bbt_scan_routine */ + chip->scan_bbt = nand_default_bbt; + + /* restore complete options including the real SKIP_BBTSCAN setting */ + chip->options = nmtd->options; + + /* Check, if we should skip the bad block table scan */ + if (chip->options & NAND_SKIP_BBTSCAN) + return 0; + + return chip->scan_bbt(mtd); +} +/* fsl_elbc_chip_init + * + * init a single instance of an chip +*/ + +static int fsl_elbc_chip_init(struct fsl_elbc_mtd *nmtd) +{ + struct fsl_elbc_ctrl *ctrl = nmtd->ctrl; + volatile lbus83xx_t *lbc = ctrl->regs; + struct nand_chip *chip = &nmtd->chip; + + FCM_DEBUG(1,"eLBC Set Information for bank %d\n", nmtd->bank); + FCM_DEBUG(1," name = %s\n", + nmtd->pl_chip.name ? nmtd->pl_chip.name : "(NULL)"); + FCM_DEBUG(1," nr_chips = %d\n", nmtd->pl_chip.nr_chips); + FCM_DEBUG(1," partitions = %s\n", + nmtd->pl_chip.partitions_str ? + nmtd->pl_chip.partitions_str : "(NULL)"); + dev_dbg(nmtd->device,"eLBC Set Information for bank %d\n", nmtd->bank); + dev_dbg(nmtd->device," name = %s\n", + nmtd->name ? nmtd->name : "(NULL)"); + dev_dbg(nmtd->device," nr_chips = %d\n", nmtd->pl_chip.nr_chips); + dev_dbg(nmtd->device," partitions = %s\n", + nmtd->pl_chip.partitions_str ? + nmtd->pl_chip.partitions_str : "(NULL)"); + + /* Fill in fsl_elbc_mtd structure */ + nmtd->name = (char *) nmtd->pl_chip.name; + nmtd->mtd.name = nmtd->name; + nmtd->mtd.priv = chip; + nmtd->mtd.owner = THIS_MODULE; + nmtd->pgs = (lbc->bank[nmtd->bank].or>>OR_FCM_PGS_SHIFT) & 1; +// TODO nmtd->fmr = FMR_ECCM; /* rest filled in later */ + nmtd->fmr = 0; /* rest filled in later */ + + /* fill in nand_chip structure */ + /* set physical base address from the Base Register */ + chip->IO_ADDR_W = (void __iomem*) (nmtd->pbase); + chip->IO_ADDR_R = chip->IO_ADDR_W; + + /* set up function call table */ +// chip->hwcontrol = fsl_elbc_hwcontrol; + chip->read_byte = fsl_elbc_read_byte; + chip->read_word = fsl_elbc_read_word; + chip->write_buf = fsl_elbc_write_buf; + chip->read_buf = fsl_elbc_read_buf; + chip->verify_buf = fsl_elbc_verify_buf; + chip->select_chip = fsl_elbc_select_chip; +// TODO chip->block_bad +// TODO chip->block_markbad + chip->cmd_ctrl = fsl_elbc_cmd_ctrl; + chip->dev_ready = fsl_elbc_dev_ready; + chip->cmdfunc = fsl_elbc_cmdfunc; + chip->waitfunc = fsl_elbc_wait; + chip->scan_bbt = fsl_elbc_chip_init_tail; +// TODO chip->errstat + + /* set up nand options */ + chip->options = NAND_NO_READRDY; + chip->chip_delay = 1; + + chip->controller = &ctrl->controller; + chip->priv = nmtd; + + /* If CS Base Register selects full hardware ECC then use it */ + if ( (lbc->bank[nmtd->bank].br & BR_DECC) == BR_DECC_CHK_GEN) { + chip->ecc.mode = NAND_ECC_HW; + chip->ecc.calculate = fsl_elbc_calculate_ecc; + chip->ecc.correct = fsl_elbc_correct_data; + chip->ecc.hwctl = fsl_elbc_enable_hwecc; + /* put in small page settings and adjust later if needed */ + chip->ecc.layout = (nmtd->fmr & FMR_ECCM) ? + &fsl_elbc_oob_sp_eccm1 : &fsl_elbc_oob_sp_eccm0; + chip->ecc.size = 512; + chip->ecc.bytes = 3; + } else { + /* otherwise fall back to default software ECC */ + chip->ecc.mode = NAND_ECC_SOFT; + } + + /* force BBT scan to get to custom scan_bbt for final settings */ + nmtd->options = chip->options; + chip-> options &= ~(NAND_SKIP_BBTSCAN); + + return 0; +} + + + +static int fsl_elbc_chip_remove(struct platform_device *pdev) +{ + struct fsl_elbc_mtd *nmtd = platform_get_drvdata(pdev); + struct fsl_elbc_ctrl *ctrl = nmtd->ctrl; + + nand_release(&nmtd->mtd); + + if (nmtd->vbase != 0) { + iounmap((void __iomem*)nmtd->vbase); + nmtd->vbase = 0; + } + +/* TODO + if (nmtd->area != NULL) { + release_resource(nmtd->area); + kfree(nmtd->area); + nmtd->area = NULL; + } +*/ + + platform_set_drvdata(pdev, NULL); + + ctrl->nmtd[nmtd->bank] = NULL; + atomic_dec(&ctrl->childs_active); + + kfree(nmtd); + + return 0; +} + +#ifdef CONFIG_MTD_PARTITIONS +const char *part_probes[] = { "cmdlinepart", NULL }; +#endif + +static int fsl_elbc_chip_probe(struct platform_device *pdev) +{ + struct platform_fsl_nand_chip *pnc = pdev->dev.platform_data; + struct fsl_elbc_ctrl *ctrl = &elbc_ctrl; + volatile lbus83xx_t *lbc = ctrl->regs; + struct fsl_elbc_mtd *nmtd; + struct resource *res; + int err = 0; + int size; + int bank; + int mtd_parts_nb = 0; + struct mtd_partition *mtd_parts = 0; + + dev_dbg(&pdev->dev, "fsl_elbc_chip_probe(%p)\n", pdev); + + /* check that the platform data structure was supplied */ + if (pnc == NULL) { + dev_err(&pdev->dev,"Device needs a platform data structure\n"); + return -ENOENT; + } + + /* check that the device has a name */ + if (pnc->name == NULL) { + dev_err(&pdev->dev,"Device requires a name\n"); + return -ENOENT; + } + /* get, allocate and map the memory resource */ + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (res == NULL) { + dev_err(&pdev->dev,"failed to get memory region resource\n"); + return -ENOENT; + } + /* find which chip select it is connected to */ + for (bank=0; bank < MAX_BANKS; bank++) { + if ( (lbc->bank[bank].br & BR_V) && + ((lbc->bank[bank].br & BR_MSEL) == BR_MS_FCM) && + ((lbc->bank[bank].br & lbc->bank[bank].or & BR_BA) == + res->start) ) { + break; + } + } + + if (bank >= MAX_BANKS) { + dev_err(&pdev->dev,"address did not match any chip selects\n"); + return -ENOENT; + } + + nmtd = kmalloc (sizeof(*nmtd), GFP_KERNEL); + if (!nmtd) { + dev_err(ctrl->device, "no memory for nand chip structure\n"); + return -ENOMEM; + } + memset(nmtd, 0, sizeof(*nmtd)); + + platform_set_drvdata(pdev, nmtd); + + atomic_inc(&ctrl->childs_active); + if (pnc) + memcpy(&(nmtd->pl_chip), pnc, sizeof(*pnc)); + ctrl->nmtd[bank] = nmtd; + nmtd->bank = bank; + nmtd->ctrl = ctrl; + nmtd->device = &pdev->dev; + + size = ( res->end - res->start ) + 1; +/* TODO - already requested by the elbc instance ????? + ctrl->area = request_mem_region(res->start, size, pdev->name); + if (ctrl->area == NULL) { + dev_err(&pdev->dev, "failed to get memory region\n"); + err = -ENOENT; + goto exit_error; + } +*/ + nmtd->pbase = res->start; + nmtd->vbase = (unsigned int) ioremap(nmtd->pbase, FCM_SIZE); + if (nmtd->vbase == 0) { + dev_err(ctrl->device, "failed to ioremap() memory region\n"); + err = -EIO; + goto exit_error; + } + + err = fsl_elbc_chip_init(nmtd); + if (err != 0) + goto exit_error; + + err = nand_scan(&nmtd->mtd, + nmtd->pl_chip.nr_chips ? nmtd->pl_chip.nr_chips : 1 ); + if (err != 0) + goto exit_error; + +#ifdef CONFIG_MTD_PARTITIONS + /* check for command line partition information */ + if (!(nmtd->pl_chip.options & FSL_ELBC_NO_CMDLINE_PARTITIONS)) + mtd_parts_nb = parse_mtd_partitions( + &nmtd->mtd, + part_probes, + &mtd_parts, + 0); +#if 0 + /* otherwise try local partition string */ + if (mtd_parts_nb <= 0 && nmtd->pl_chip.partitions_str) + mtd_parts_nb = parse_mtd_string_partitions( + &nmtd->mtd, + part_probes, + &mtd_parts, + 0, + nmtd->pl_chip.partitions_str); +#endif + if (mtd_parts_nb > 0) + err = add_mtd_partitions(&nmtd->mtd, + mtd_parts, + mtd_parts_nb); + else +#endif + err = add_mtd_device(&nmtd->mtd); + + if (err == 0) + return 0; + + exit_error: + fsl_elbc_chip_remove(pdev); + + if (err == 0) + err = -EINVAL; + return err; +} + + +/**************************************************************************/ +/* Controller setup and control functions */ +/**************************************************************************/ + +static int fsl_elbc_ctrl_init(struct fsl_elbc_ctrl *ctrl, + struct platform_device *pdev) +{ + volatile lbus83xx_t *lbc= (lbus83xx_t*) ctrl->regs; + + /* Enable only FCM detection of timeouts, ECC errors and completion */ + lbc->ltedr = ~(LTESR_FCT | LTESR_PAR | LTESR_CC); + + /* clear event registers */ + lbc->lteatr = 0; + lbc->ltesr |= (LTESR_FCT | LTESR_PAR | LTESR_CC); + + /* Enable interrupts for any detected events */ + lbc->lteir = ~0; + + ctrl->read_bytes = 0; + ctrl->index = 0; + ctrl->addr = (unsigned char*) (NULL); + ctrl->oobbuf = -1; + + return 0; +} + +static int fsl_elbc_ctrl_remove(struct platform_device *pdev) +{ + struct fsl_elbc_ctrl *ctrl = platform_get_drvdata(pdev); + + if (atomic_read(&ctrl->childs_active)) + return -EBUSY; + + if (ctrl->regs != NULL) { + iounmap(ctrl->regs); + ctrl->regs = NULL; + } + +/* TODO + if (ctrl->area != NULL) { + release_resource(ctrl->area); + kfree(ctrl->area); + ctrl->area = NULL; + } +*/ + if (ctrl->irq) { + free_irq(ctrl->irq, pdev); + ctrl->irq = 0; + } + + platform_set_drvdata(pdev, NULL); + memset(ctrl, 0, sizeof(*ctrl)); + + return 0; +} + + +/* interrupt handler code */ + +static irqreturn_t fsl_elbc_ctrl_irq(int irqno, void *param) +{ + struct fsl_elbc_ctrl *ctrl = platform_get_drvdata((struct platform_device*)param); + volatile lbus83xx_t *lbc= (lbus83xx_t*) ctrl->regs; + + ctrl->irq_status = lbc->ltesr & (LTESR_FCT | LTESR_PAR | LTESR_CC); + if (ctrl->irq_status) + wake_up(&ctrl->irq_wait); + + /* clear event registers */ + lbc->lteatr = 0; + lbc->ltesr |= ctrl->irq_status; + + return IRQ_HANDLED; +} + + +/* 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 fsl_elbc_ctrl_probe(struct platform_device *pdev) +{ + struct fsl_elbc_ctrl *ctrl; + struct resource *res; + int err = 0; + int size; + int ret; + + dev_dbg(&pdev->dev, "fsl_elbc_ctrl_probe(%p)\n", pdev); + ctrl = &elbc_ctrl; + + memset(ctrl, 0, sizeof(*ctrl)); + platform_set_drvdata(pdev, ctrl); + + spin_lock_init(&ctrl->controller.lock); + init_waitqueue_head(&ctrl->controller.wq); + init_waitqueue_head(&ctrl->irq_wait); + + /* get, allocate and map the memory resource */ + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (res == NULL) { + dev_err(&pdev->dev,"failed to get memory region resouce\n"); + err = -ENOENT; + goto exit_error; + } + + size = ( res->end - res->start ) + 1; +/* TODO - already requested by the elbc instance ????? + ctrl->area = request_mem_region(res->start, size, pdev->name); + if (ctrl->area == NULL) { + dev_err(&pdev->dev, "failed to get memory region\n"); + err = -ENOENT; + goto exit_error; + } +*/ + ctrl->regs = ioremap(res->start, size); + if (ctrl->regs == 0) { + dev_err(&pdev->dev, "failed to ioremap() region\n"); + err = -EIO; + goto exit_error; + } + + /* get and allocate the irq resource */ + res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + if (res == NULL) { + dev_err(&pdev->dev, "failed to get irq resource\n"); + err = -ENOENT; + goto exit_error; + } + + ret = request_irq(res->start, fsl_elbc_ctrl_irq, 0, pdev->name, pdev); + if (ret != 0) { + dev_err(&pdev->dev, "failed to install irq (%d)\n", ret); + err = -EIO; + goto exit_error; + } + + ctrl->irq = res->start; + ctrl->device = &pdev->dev; + dev_dbg(&pdev->dev, "mapped registers at %p\n", ctrl->regs); + + /* initialise the hardware */ + + err = fsl_elbc_ctrl_init(ctrl, pdev); + if (err == 0) + return 0; + + exit_error: + fsl_elbc_ctrl_remove(pdev); + + if (err == 0) + err = -EINVAL; + return err; +} + +/* PM Support */ +#ifdef CONFIG_PM + +static int fsl_elbc_ctrl_suspend(struct platform_device *dev, pm_message_t pm) +{ + return 0; +} + +static int fsl_elbc_ctrl_resume(struct platform_device *dev) +{ + return 0; +} + +#else +#define fsl_elbc_ctrl_suspend NULL +#define fsl_elbc_ctrl_resume NULL +#endif + +/*************************************************************************/ +/* device driver registration */ +/*************************************************************************/ + + +static struct platform_driver fsl_elbc_ctrl_driver = { + .probe = fsl_elbc_ctrl_probe, + .remove = fsl_elbc_ctrl_remove, + .suspend = fsl_elbc_ctrl_suspend, + .resume = fsl_elbc_ctrl_resume, + .driver = { + .name = "fsl-elbc", + .owner = THIS_MODULE, + }, +}; + +static struct platform_driver fsl_elbc_chip_driver = { + .probe = fsl_elbc_chip_probe, + .remove = fsl_elbc_chip_remove, + .driver = { + .name = "fsl-nand", + .owner = THIS_MODULE, + }, +}; + +static int __init fsl_elbc_init(void) +{ + int ret; + + printk("Freescale eLBC NAND Driver (C) 2006 Freescale\n"); + + ret = platform_driver_register(&fsl_elbc_ctrl_driver); + if (!ret) + ret = platform_driver_register(&fsl_elbc_chip_driver); + + return ret; +} + +static void __exit fsl_elbc_exit(void) +{ + platform_driver_unregister(&fsl_elbc_chip_driver); + platform_driver_unregister(&fsl_elbc_ctrl_driver); +} + +module_init(fsl_elbc_init); +module_exit(fsl_elbc_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Nick Spence"); +MODULE_DESCRIPTION("Freescale Enhanced Local Bus Controller MTD NAND driver"); diff -urN linux-2.6.24.orig/drivers/mtd/nand/Kconfig linux-2.6.24/drivers/mtd/nand/Kconfig --- linux-2.6.24.orig/drivers/mtd/nand/Kconfig 2008-01-24 23:58:37.000000000 +0100 +++ linux-2.6.24/drivers/mtd/nand/Kconfig 2008-02-18 16:39:43.000000000 +0100 @@ -284,6 +284,14 @@ depends on MTD_NAND && MACH_ARMCORE +config MTD_NAND_FSL_ELBC + tristate "NAND support for MPC831x" + depends on MTD_NAND && PPC_MPC831x + help + The MPC831x includes a NAND FLASH Controller Module with built-in hardware + ECC capabilities. Enabling this This option will enable you to use these to + control external NAND device. + config MTD_NAND_NANDSIM tristate "Support for NAND Flash Simulator" depends on MTD_PARTITIONS diff -urN linux-2.6.24.orig/drivers/mtd/nand/Makefile linux-2.6.24/drivers/mtd/nand/Makefile --- linux-2.6.24.orig/drivers/mtd/nand/Makefile 2008-01-24 23:58:37.000000000 +0100 +++ linux-2.6.24/drivers/mtd/nand/Makefile 2008-02-18 16:40:08.000000000 +0100 @@ -29,5 +29,6 @@ obj-$(CONFIG_MTD_NAND_BASLER_EXCITE) += excite_nandflash.o obj-$(CONFIG_MTD_NAND_PLATFORM) += plat_nand.o obj-$(CONFIG_MTD_ALAUDA) += alauda.o +obj-$(CONFIG_MTD_NAND_FSL_ELBC) += fsl_elbc.o nand-objs := nand_base.o nand_bbt.o diff -urN linux-2.6.24.orig/include/linux/mtd/fsl_elbc.h linux-2.6.24/include/linux/mtd/fsl_elbc.h --- linux-2.6.24.orig/include/linux/mtd/fsl_elbc.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24/include/linux/mtd/fsl_elbc.h 2008-02-18 16:39:43.000000000 +0100 @@ -0,0 +1,313 @@ +/* + * (C) Copyright 2004-2006 Freescale Semiconductor, Inc. + * + * Freescale Enhanced Local Bus Controller Internal Memory Map + * + * History : + * 20061010 : Extracted fomr immap_83xx.h + * + * 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., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + */ +#ifdef __KERNEL__ +#ifndef __FSL_ELBC__ +#define __FSL_ELBC__ + +/* + * Local Bus Controller Registers + */ +typedef struct lbus_bank{ + u32 br; /**< Base Register */ +#define BR0 0x5000 +#define BR1 0x5008 +#define BR2 0x5010 +#define BR3 0x5018 +#define BR4 0x5020 +#define BR5 0x5028 +#define BR6 0x5030 +#define BR7 0x5038 + +#define BR_BA 0xFFFF8000 +#define BR_BA_SHIFT 15 +#define BR_PS 0x00001800 +#define BR_PS_SHIFT 11 +#define BR_PS_8 0x00000800 /* Port Size 8 bit */ +#define BR_PS_16 0x00001000 /* Port Size 16 bit */ +#define BR_PS_32 0x00001800 /* Port Size 32 bit */ +#define BR_DECC 0x00000600 +#define BR_DECC_SHIFT 9 +#define BR_DECC_OFF 0x00000000 /* HW ECC checking and generation off */ +#define BR_DECC_CHK 0x00000200 /* HW ECC checking on, generation off */ +#define BR_DECC_CHK_GEN 0x00000400 /* HW ECC checking and generation on */ +#define BR_WP 0x00000100 +#define BR_WP_SHIFT 8 +#define BR_MSEL 0x000000E0 +#define BR_MSEL_SHIFT 5 +#define BR_MS_GPCM 0x00000000 /* GPCM */ +#define BR_MS_FCM 0x00000020 /* FCM */ +#define BR_MS_SDRAM 0x00000060 /* SDRAM */ +#define BR_MS_UPMA 0x00000080 /* UPMA */ +#define BR_MS_UPMB 0x000000A0 /* UPMB */ +#define BR_MS_UPMC 0x000000C0 /* UPMC */ +#define BR_V 0x00000001 +#define BR_V_SHIFT 0 +#define BR_RES ~(BR_BA|BR_PS|BR_DECC|BR_WP|BR_MSEL|BR_V) + + u32 or; /**< Base Register */ +#define OR0 0x5004 +#define OR1 0x500C +#define OR2 0x5014 +#define OR3 0x501C +#define OR4 0x5024 +#define OR5 0x502C +#define OR6 0x5034 +#define OR7 0x503C + +#define OR_GPCM_AM 0xFFFF8000 +#define OR_GPCM_AM_SHIFT 15 +#define OR_GPCM_BCTLD 0x00001000 +#define OR_GPCM_BCTLD_SHIFT 12 +#define OR_GPCM_CSNT 0x00000800 +#define OR_GPCM_CSNT_SHIFT 11 +#define OR_GPCM_ACS 0x00000600 +#define OR_GPCM_ACS_SHIFT 9 +#define OR_GPCM_ACS_0b10 0x00000400 +#define OR_GPCM_ACS_0b11 0x00000600 +#define OR_GPCM_XACS 0x00000100 +#define OR_GPCM_XACS_SHIFT 8 +#define OR_GPCM_SCY 0x000000F0 +#define OR_GPCM_SCY_SHIFT 4 +#define OR_GPCM_SCY_1 0x00000010 +#define OR_GPCM_SCY_2 0x00000020 +#define OR_GPCM_SCY_3 0x00000030 +#define OR_GPCM_SCY_4 0x00000040 +#define OR_GPCM_SCY_5 0x00000050 +#define OR_GPCM_SCY_6 0x00000060 +#define OR_GPCM_SCY_7 0x00000070 +#define OR_GPCM_SCY_8 0x00000080 +#define OR_GPCM_SCY_9 0x00000090 +#define OR_GPCM_SCY_10 0x000000a0 +#define OR_GPCM_SCY_11 0x000000b0 +#define OR_GPCM_SCY_12 0x000000c0 +#define OR_GPCM_SCY_13 0x000000d0 +#define OR_GPCM_SCY_14 0x000000e0 +#define OR_GPCM_SCY_15 0x000000f0 +#define OR_GPCM_SETA 0x00000008 +#define OR_GPCM_SETA_SHIFT 3 +#define OR_GPCM_TRLX 0x00000004 +#define OR_GPCM_TRLX_SHIFT 2 +#define OR_GPCM_EHTR 0x00000002 +#define OR_GPCM_EHTR_SHIFT 1 +#define OR_GPCM_EAD 0x00000001 +#define OR_GPCM_EAD_SHIFT 0 + +#define OR_UPM_AM 0xFFFF8000 +#define OR_UPM_AM_SHIFT 15 +#define OR_UPM_XAM 0x00006000 +#define OR_UPM_XAM_SHIFT 13 +#define OR_UPM_BCTLD 0x00001000 +#define OR_UPM_BCTLD_SHIFT 12 +#define OR_UPM_BI 0x00000100 +#define OR_UPM_BI_SHIFT 8 +#define OR_UPM_TRLX 0x00000004 +#define OR_UPM_TRLX_SHIFT 2 +#define OR_UPM_EHTR 0x00000002 +#define OR_UPM_EHTR_SHIFT 1 +#define OR_UPM_EAD 0x00000001 +#define OR_UPM_EAD_SHIFT 0 + +#define OR_SDRAM_AM 0xFFFF8000 +#define OR_SDRAM_AM_SHIFT 15 +#define OR_SDRAM_XAM 0x00006000 +#define OR_SDRAM_XAM_SHIFT 13 +#define OR_SDRAM_COLS 0x00001C00 +#define OR_SDRAM_COLS_SHIFT 10 +#define OR_SDRAM_ROWS 0x000001C0 +#define OR_SDRAM_ROWS_SHIFT 6 +#define OR_SDRAM_PMSEL 0x00000020 +#define OR_SDRAM_PMSEL_SHIFT 5 +#define OR_SDRAM_EAD 0x00000001 +#define OR_SDRAM_EAD_SHIFT 0 + +#define OR_FCM_AM 0xFFFF8000 +#define OR_FCM_AM_SHIFT 15 +#define OR_FCM_BCTLD 0x00001000 +#define OR_FCM_BCTLD_SHIFT 12 +#define OR_FCM_PGS 0x00000400 +#define OR_FCM_PGS_SHIFT 10 +#define OR_FCM_CSCT 0x00000200 +#define OR_FCM_CSCT_SHIFT 9 +#define OR_FCM_CST 0x00000100 +#define OR_FCM_CST_SHIFT 8 +#define OR_FCM_CHT 0x00000080 +#define OR_FCM_CHT_SHIFT 7 +#define OR_FCM_SCY 0x00000070 +#define OR_FCM_SCY_SHIFT 4 +#define OR_FCM_SCY_1 0x00000010 +#define OR_FCM_SCY_2 0x00000020 +#define OR_FCM_SCY_3 0x00000030 +#define OR_FCM_SCY_4 0x00000040 +#define OR_FCM_SCY_5 0x00000050 +#define OR_FCM_SCY_6 0x00000060 +#define OR_FCM_SCY_7 0x00000070 +#define OR_FCM_RST 0x00000008 +#define OR_FCM_RST_SHIFT 3 +#define OR_FCM_TRLX 0x00000004 +#define OR_FCM_TRLX_SHIFT 2 +#define OR_FCM_EHTR 0x00000002 +#define OR_FCM_EHTR_SHIFT 1 +} lbus_bank_t; + +typedef struct lbus83xx { + lbus_bank_t bank[8]; + u8 res0[0x28]; + u32 mar; /**< UPM Address Register */ + u8 res1[0x4]; + u32 mamr; /**< UPMA Mode Register */ + u32 mbmr; /**< UPMB Mode Register */ + u32 mcmr; /**< UPMC Mode Register */ + u8 res2[0x8]; + u32 mrtpr; /**< Memory Refresh Timer Prescaler Register */ + u32 mdr; /**< UPM Data Register */ + u8 res3[0x4]; + u32 lsor; /**< Special Operation Initiation Register */ + u32 lsdmr; /**< SDRAM Mode Register */ + u8 res4[0x8]; + u32 lurt; /**< UPM Refresh Timer */ + u32 lsrt; /**< SDRAM Refresh Timer */ + u8 res5[0x8]; + u32 ltesr; /**< Transfer Error Status Register */ +#define LTESR_BM 0x80000000 +#define LTESR_FCT 0x40000000 +#define LTESR_PAR 0x20000000 +#define LTESR_WP 0x04000000 +#define LTESR_ATMW 0x00800000 +#define LTESR_ATMR 0x00400000 +#define LTESR_CS 0x00080000 +#define LTESR_CC 0x00000001 + u32 ltedr; /**< Transfer Error Disable Register */ + u32 lteir; /**< Transfer Error Interrupt Register */ + u32 lteatr; /**< Transfer Error Attributes Register */ + u32 ltear; /**< Transfer Error Address Register */ + u8 res6[0xC]; + u32 lbcr; /**< Configuration Register */ +#define LBCR_LDIS 0x80000000 +#define LBCR_LDIS_SHIFT 31 +#define LBCR_BCTLC 0x00C00000 +#define LBCR_BCTLC_SHIFT 22 +#define LBCR_AHD 0x00200000 +#define LBCR_LPBSE 0x00020000 +#define LBCR_LPBSE_SHIFT 17 +#define LBCR_EPAR 0x00010000 +#define LBCR_EPAR_SHIFT 16 +#define LBCR_BMT 0x0000FF00 +#define LBCR_BMT_SHIFT 8 +#define LBCR_INIT 0x00040000 + u32 lcrr; /**< Clock Ratio Register */ +#define LCRR_DBYP 0x80000000 +#define LCRR_DBYP_SHIFT 31 +#define LCRR_BUFCMDC 0x30000000 +#define LCRR_BUFCMDC_SHIFT 28 +#define LCRR_ECL 0x03000000 +#define LCRR_ECL_SHIFT 24 +#define LCRR_EADC 0x00030000 +#define LCRR_EADC_SHIFT 16 +#define LCRR_CLKDIV 0x0000000F +#define LCRR_CLKDIV_SHIFT 0 + u8 res7[0x8]; + u32 fmr; /**< Flash Mode Register */ +#define FMR_CWTO 0x0000F000 +#define FMR_CWTO_SHIFT 12 +#define FMR_BOOT 0x00000800 +#define FMR_ECCM 0x00000100 +#define FMR_AL 0x00000030 +#define FMR_AL_SHIFT 4 +#define FMR_OP 0x00000003 +#define FMR_OP_SHIFT 0 + u32 fir; /**< Flash Instruction Register */ +#define FIR_OP0 0xF0000000 +#define FIR_OP0_SHIFT 28 +#define FIR_OP1 0x0F000000 +#define FIR_OP1_SHIFT 24 +#define FIR_OP2 0x00F00000 +#define FIR_OP2_SHIFT 20 +#define FIR_OP3 0x000F0000 +#define FIR_OP3_SHIFT 16 +#define FIR_OP4 0x0000F000 +#define FIR_OP4_SHIFT 12 +#define FIR_OP5 0x00000F00 +#define FIR_OP5_SHIFT 8 +#define FIR_OP6 0x000000F0 +#define FIR_OP6_SHIFT 4 +#define FIR_OP7 0x0000000F +#define FIR_OP7_SHIFT 0 +#define FIR_OP_NOP 0x0 /* No operation and end of sequence */ +#define FIR_OP_CA 0x1 /* Issue current column address */ +#define FIR_OP_PA 0x2 /* Issue current block+page address */ +#define FIR_OP_UA 0x3 /* Issue user defined address */ +#define FIR_OP_CM0 0x4 /* Issue command from FCR[CMD0] */ +#define FIR_OP_CM1 0x5 /* Issue command from FCR[CMD1] */ +#define FIR_OP_CM2 0x6 /* Issue command from FCR[CMD2] */ +#define FIR_OP_CM3 0x7 /* Issue command from FCR[CMD3] */ +#define FIR_OP_WB 0x8 /* Write FBCR bytes from FCM buffer */ +#define FIR_OP_WS 0x9 /* Write 1 or 2 bytes from MDR[AS] */ +#define FIR_OP_RB 0xA /* Read FBCR bytes to FCM buffer */ +#define FIR_OP_RS 0xB /* Read 1 or 2 bytes to MDR[AS] */ +#define FIR_OP_CW0 0xC /* Wait then issue FCR[CMD0] */ +#define FIR_OP_CW1 0xD /* Wait then issue FCR[CMD1] */ +#define FIR_OP_RBW 0xE /* Wait then read FBCR bytes */ +#define FIR_OP_RSW 0xE /* Wait then read 1 or 2 bytes */ + u32 fcr; /**< Flash Command Register */ +#define FCR_CMD0 0xFF000000 +#define FCR_CMD0_SHIFT 24 +#define FCR_CMD1 0x00FF0000 +#define FCR_CMD1_SHIFT 16 +#define FCR_CMD2 0x0000FF00 +#define FCR_CMD2_SHIFT 8 +#define FCR_CMD3 0x000000FF +#define FCR_CMD3_SHIFT 0 + u32 fbar; /**< Flash Block Address Register */ +#define FBAR_BLK 0x00FFFFFF + u32 fpar; /**< Flash Page Address Register */ +#define FPAR_SP_PI 0x00007C00 +#define FPAR_SP_PI_SHIFT 10 +#define FPAR_SP_MS 0x00000200 +#define FPAR_SP_CI 0x000001FF +#define FPAR_SP_CI_SHIFT 0 +#define FPAR_LP_PI 0x0003F000 +#define FPAR_LP_PI_SHIFT 12 +#define FPAR_LP_MS 0x00000800 +#define FPAR_LP_CI 0x000007FF +#define FPAR_LP_CI_SHIFT 0 + u32 fbcr; /**< Flash Byte Count Register */ +#define FBCR_BC 0x00000FFF + u8 res11[0x8]; + u8 res8[0xF00]; +} lbus83xx_t; + +struct platform_fsl_nand_chip { + const char *name; + int nr_chips; + const char *partitions_str; + unsigned int options; +}; + +/* Setting this option prevents the command line from being parsed + * for MTD partitions. */ +#define FSL_ELBC_NO_CMDLINE_PARTITIONS 0x10000000 + +#endif /* __FSL_ELBC__ */ +#endif /* __KERNEL__ */