/* linux/drivers/mtd/nand/fusiv_nand.c * * Copyright 2010 Ikanos Inc. * * Fusiv VX185 on-chip NAND flash controller driver * * Derived from drivers/mtd/nand/bf5xx_nand.c * Copyright 2006-2008 Analog Devices Inc. * Bryan Wu * * Changelog: * Version 1.0 => NAND driver without ECC * Version 1.1 => Added Hardware ECC support * Added bad block detection and marking support * * 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 */ #include #include #include #include #include #include #include #include #include #include #define DRV_NAME "fusiv-nand" #define DRV_VERSION "1.1" #define DRV_AUTHOR "Kiran Kumar C.S.K " #define DRV_DESC "VX185 on-chip NAND FLash Controller Driver" #define CONFIG_MTD_NAND_FUSIV_HWECC #ifdef CONFIG_MTD_NAND_FUSIV_HWECC #include "fusiv_bch_decode.c" #endif //#define FUSIV_NAND_ECC_DEBUG #ifdef FUSIV_NAND_ECC_DEBUG int nand_scan_done = 0; #endif static struct fusiv_nand_state nand_state; static unsigned int fusiv_nand_set_hwecc(unsigned int on); /* * Data structure access helper functions */ static struct fusiv_nand_info *to_nand_info(struct platform_device *pdev) { return platform_get_drvdata(pdev); } static struct fusiv_nand_platform *to_nand_plat(struct platform_device *pdev) { return pdev->dev.platform_data; } void print_buf(const char *prefix, uint8_t *buf, int len) { printk(KERN_ERR "[%s] ", prefix); while(len--){ printk("0x%02x ", *buf++); if((len % 16) == 0){ printk("\n" KERN_ERR "[%s] ", prefix); } } printk("\n"); } /* * struct nand_chip interface function pointers */ void print_NFC_registers(void) { printk("\t***NFC Registers***\n"); printk("scu_regs->nand_status: 0x%x\n", scu_regs->nand_status); printk("scu_regs->rst_strap: 0x%x\n", scu_regs->rst_strap); printk("ID_0: 0x%x\n", fusiv_nfc_readl(NFC_ID_0)); printk("ID_1: 0x%x\n", fusiv_nfc_readl(NFC_ID_2)); printk("ID_2: 0x%x\n", fusiv_nfc_readl(NFC_ID_4)); printk("ID_3: 0x%x\n", fusiv_nfc_readl(NFC_ID_6)); printk("NFC_CONFIG: 0x%x\n", fusiv_nfc_readl(NFC_CONFIG)); printk("NFC_STATUS: 0x%x\n", fusiv_nfc_readl(NFC_STATUS_0)); printk("NFC_INT_ENABLE: 0x%x\n", fusiv_nfc_readl(NFC_INT_ENABLE)); printk("NFC_INT_STATUS: 0x%x\n", fusiv_nfc_readl(NFC_INT_STATUS)); printk("NFC_INT: 0x%x\n", fusiv_nfc_readl(NFC_INT)); printk("BCH Status: 0x%x\n", fusiv_nfc_readl(NFC_BCH_STATUS)); printk("\n"); } /* * Fusiv NFC hardware initialization * - Reset the flash, clear NFC registers, enable/disable ECC */ static int fusiv_nand_hw_init(struct fusiv_nand_info *info) { int err = 0; volatile unsigned int output; nand_state.mode = NFC_MODE_NORMAL; /* clear the status */ fusiv_nfc_writel(NFC_INT_STATUS, 0); /* TODO: Read SCU registers: RESET_STRAP SCU Control Register NANDC STATUS */ /* * Size: 4Gbit, 2KB page size. 256KB block size, Bank 0 enable, * write protect off, 3 row address cycles */ fusiv_nfc_writel(NFC_CONFIG, (C_CSZ(6) | C_BNK(0) | C_PSZ(1) | C_WPR(1) | C_RAC(1))); /* Reset nand flash device on bank 0*/ fusiv_nfc_writel(NFC_COMMAND, NAND_CMD_RESET); /* wait for reset to complete */ do{ output = fusiv_nfc_readl(NFC_INT_STATUS); }while((output & INTS_RSEW) == 0); /* clear the status */ fusiv_nfc_writel(NFC_INT_STATUS, 0); /* Set timing parameters for standard flash device signals, as per flash device datasheet. The following values are derived from ST-Micro datasheet for 4Gbit flashes. The values are expected to be applicable for 2k page size MLC flash with V-DDQ 2.7V to 3.6V */ /* Modified NAND timing parameters; Please Refer bug 25257*/ fusiv_nfc_writel(NFC_TIMING_0, 0x04050505); fusiv_nfc_writel(NFC_TIMING_1, 0x19050505); /* clear the EXT_INDEX */ fusiv_nfc_writel(NFC_EXT_INDEX, 0); /* Enable/Disable Hardware ECC */ #ifdef CONFIG_MTD_NAND_FUSIV_HWECC fusiv_nand_set_hwecc(1); #else fusiv_nand_set_hwecc(0); #endif return err; } /* * fusiv_nand_devready() * * returns 0 if the nand is busy, 1 if it is ready */ static int fusiv_nand_devready(struct mtd_info *mtd) { return scu_regs->nand_status ? 0 : 1; } /* * fusiv_nand_hwcontrol * * Issue command and address cycles to the chip */ static void fusiv_nand_hwcontrol(struct mtd_info *mtd, int cmd, unsigned int ctrl) { unsigned int val, column, page, i; if(!(ctrl & NAND_ALE) && nand_state.next_addr > 0){ /* base finished latching address bytes, transfer to index register */ column = (nand_state.addr[1] << 8) | nand_state.addr[0]; page = (nand_state.addr[4] << 16) | (nand_state.addr[3] << 8) | nand_state.addr[2]; // TODO: handle large page addresses val = (page << 12) | column; fusiv_nfc_writel(NFC_INDEX, val); nand_state.addr_latched = 0; nand_state.next_addr = 0; memset(&nand_state.addr[0], 0x00, sizeof(nand_state.addr)); } if(ctrl & NAND_ALE){ nand_state.addr_latched = 1; if(nand_state.mode == NFC_MODE_READID && cmd != -1){ nand_state.readid_off = (cmd & 0xff) % 4; }else{ if(nand_state.next_addr < sizeof(nand_state.addr)){ nand_state.addr[nand_state.next_addr] = cmd & 0xff; ++nand_state.next_addr; }else{ printk(KERN_ERR "[%s] Address buffer overflow!\n", __func__); } } }else if(ctrl & NAND_CLE){ nand_state.mode = NFC_MODE_NORMAL; switch(cmd){ case NAND_CMD_READSTART: cmd = NAND_CMD_RNDOUTSTART; break; case NAND_CMD_READID: nand_state.readid_off = 0; nand_state.mode = NFC_MODE_READID; break; case NAND_CMD_STATUS: nand_state.mode = NFC_MODE_STATUS; break; case NAND_CMD_READ1: cmd = NAND_CMD_READ0; // fall through case NAND_CMD_READ0: case NAND_CMD_SEQIN: case NAND_CMD_ERASE1: case NAND_CMD_RESET: /* * New command cycle. Reset hardware controller state by issuing * CMD_CE_OFF and clear address buffer. * Probably redundant, but better safe than sorry */ fusiv_nfc_writel(NFC_COMMAND, NAND_CMD_CE_OFF); nand_state.addr_latched = 0; nand_state.next_addr = 0; memset(&nand_state.addr[0], 0x00, sizeof(nand_state.addr)); if(cmd == NAND_CMD_ERASE1){ /* we only expect page/block addresses here */ nand_state.next_addr += 2; } else if(cmd == NAND_CMD_RESET){ // clear reset complete flag // fusiv_nfc_writel(NFC_INT_STATUS, ~(INTS_RSEW)); } break; default: break; } fusiv_nfc_writel(NFC_COMMAND, cmd); if(cmd == NAND_CMD_RNDOUTSTART){ /* CHIP_BUSY takes a few cycles to go high, we should * not access the NANDC before this */ udelay(1); /* Wait for page open to complete and CHIP_BUSY to go low. This may take many clock cycles, so ensure we wait enough */ i = 1000; while((scu_regs->nand_status & NAND_CHIP_BUSY) && i--){ udelay(1); } if(!i){ pr_err("%s: page open TIMEOUT\n", __FUNCTION__); return; } } else if(cmd == NAND_CMD_RESET){ // wait for reset to complete i = 1000; while((scu_regs->nand_status & NAND_CHIP_BUSY) && i--){ udelay(1); } } #ifdef CONFIG_MTD_NAND_FUSIV_HWECC else if(cmd == NAND_CMD_PAGEPROG && nand_state.hardware_ecc){ i = 20; /* PECC_BUSY takes a few clocks to assert */ while(!(scu_regs->nand_status & NAND_PECC_BUSY) && i--) ; /* Wait for PECC_BUSY to go low */ while(scu_regs->nand_status & NAND_PECC_BUSY){ udelay(1); } } #endif }else if(!(ctrl & NAND_NCE)){ fusiv_nfc_writel(NFC_COMMAND, NAND_CMD_CE_OFF); } } /* * Generic read from NAND flash. * Assumption: INDEX register is already programmed with the address to * read from, by the user prior to calling this routine. */ static void fusiv_nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) { volatile int i, r_val; if(len <= 0){ pr_err("%s: Read request for invald length of %d bytes\n", __FUNCTION__, len); dump_stack(); return; } // FIXME: can we be sure that buf is properly aligned? while(len >= 4){ *(unsigned int *)buf = fusiv_nfc_readl(NFC_DATA); buf += 4; len -= 4; } if(len){ r_val = fusiv_nfc_readl(NFC_DATA); for(i = 0; i < len; ++i){ *buf = (r_val >> (i * 8)) & 0xff; ++buf; } } } /* Read a single byte from NAND flash * Assumption: INDEX register is already programmed with the address to * read from, by the user prior to calling this routine. */ static uint8_t fusiv_nand_read_byte(struct mtd_info *mtd) { uint8_t val; /* * when the nand_base is requesting ID or STATUS information, do fake reads to * the controller's registers instead */ switch(nand_state.mode){ case NFC_MODE_STATUS: val = fusiv_nfc_readl(NFC_STATUS_0) & 0xff; break; case NFC_MODE_READID: val = fusiv_nfc_readl(NFC_ID_0 + (8 * nand_state.readid_off)) & 0xff; ++nand_state.readid_off; nand_state.readid_off %= 4; break; default: fusiv_nand_read_buf(mtd, &val, 1); break; } return val; } /* Generic write to NAND flash * Assumption: INDEX register is already programmed with the address to * write to, by the user prior to calling this routine. */ static void fusiv_nand_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len) { volatile int i, w_val; if(len <= 0){ pr_err("%s: Write request for invalid length of %d bytes\n", __FUNCTION__, len); dump_stack(); return; } // FIXME: can we be sure that buf is properly aligned? while(len >= 4){ w_val = *(unsigned int *) buf; fusiv_nfc_writel(NFC_DATA, w_val); buf += 4; len -= 4; } if(len){ // pad partial word with non-destructive pattern w_val = 0xffffffff; for(i = 0; i < len; ++i){ w_val &= ~(0xff << (i * 8)); w_val |= ((unsigned int) *buf << (i * 8)); ++buf; } fusiv_nfc_writel(NFC_DATA, w_val); } } static void fusiv_nand_write_page_raw(struct mtd_info *mtd, struct nand_chip *chip, const uint8_t *buf) { // printk(KERN_ERR "[%s] entered\n", __func__); chip->write_buf(mtd, buf, mtd->writesize); chip->write_buf(mtd, chip->oob_poi, mtd->oobsize); } /* Get the status byte from the NAND flash */ int fusiv_nand_status(struct mtd_info *mtd, struct nand_chip *this) { return fusiv_nfc_readl(NFC_STATUS_0); } /* ECC Specific routines */ #ifdef CONFIG_MTD_NAND_FUSIV_HWECC /** * nand_write_page - [REPLACEABLE] write one page * @mtd: MTD device structure * @chip: NAND chip descriptor * @buf: the data to write * @page: page number to write * @cached: cached programming * @raw: use _raw version of write_page */ static int fusiv_nand_write_page(struct mtd_info *mtd, struct nand_chip *chip, const uint8_t *buf, int page, int cached, int raw) { int status; unsigned int ecc __attribute__ ((unused)); #ifdef CONFIG_MTD_NAND_FUSIV_HWECC if(unlikely(raw)){ ecc = fusiv_nand_set_hwecc(0); } #endif chip->cmdfunc(mtd, NAND_CMD_SEQIN, 0x00, page); if (unlikely(raw)) chip->ecc.write_page_raw(mtd, chip, buf); else chip->ecc.write_page(mtd, chip, buf); /* * Cached programming disabled for now, Not sure if its worth the * trouble. The speed gain is not very impressive. (2.3->2.6Mib/s) */ cached = 0; if (!cached || !(chip->options & NAND_CACHEPRG)) { chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1); status = chip->waitfunc(mtd, chip); /* * See if operation failed and additional status checks are * available */ if ((status & NAND_STATUS_FAIL) && (chip->errstat)) status = chip->errstat(mtd, chip, FL_WRITING, status, page); } else { chip->cmdfunc(mtd, NAND_CMD_CACHEDPROG, -1, -1); status = chip->waitfunc(mtd, chip); } #if 0 #ifdef CONFIG_MTD_NAND_VERIFY_WRITE /* Send command to read back the data */ chip->cmdfunc(mtd, NAND_CMD_READ0, 0, page); if (chip->verify_buf(mtd, buf, mtd->writesize)) status |= NAND_STATUS_FAIL; #endif #endif #ifdef CONFIG_MTD_NAND_FUSIV_HWECC if(unlikely(raw)){ fusiv_nand_set_hwecc(ecc); } #endif if (status & NAND_STATUS_FAIL) return -EIO; return 0; } static int fusiv_nand_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip, uint8_t *buf) { printk(KERN_ERR "[%s] entered\n", __func__); chip->read_buf(mtd, buf, mtd->writesize); chip->read_buf(mtd, chip->oob_poi, mtd->oobsize); return 0; } #ifdef CONFIG_MTD_NAND_FUSIV_HWECC /* Good blocks match the following pattern at chip->badblockpos * in OOB area */ static uint8_t scan_ff_pattern[] = { 0xff }; /* Good/bad block check location and pattern in OOB area */ static struct nand_bbt_descr fusiv_bb_pattern = { .options = 0, .offs = 0, .len = 1, .pattern = scan_ff_pattern }; /* OOB area layout for 2k Page size flash. Offset 0: Bad block marker Offset 1: Reserved Offset 32 to 63: ECC bytes written by controller Offset 2 to 31: Free for use (by filesystem). */ static struct nand_ecclayout fusiv_oobinfo_2048 = { .eccbytes = 32, .eccpos = { 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63 }, .oobfree = { { 2, 30 } } }; static void fusiv_nand_bug(struct mtd_info *mtd) { BUG(); } static unsigned int fusiv_nand_set_hwecc(unsigned int on) { unsigned int prev_state, reg; reg = fusiv_nfc_readl(NFC_INT_ENABLE); prev_state = (reg & INTE_ECCD) ? 0 : 1; if(on){ reg &= ~INTE_ECCD; nand_state.hardware_ecc = 1; }else{ reg |= INTE_ECCD; nand_state.hardware_ecc = 0; } fusiv_nfc_writel(NFC_INT_ENABLE, reg); return prev_state; } /* Page-write routine for ECC-ON mode */ static void fusiv_nand_write_page_syndrome(struct mtd_info *mtd, struct nand_chip *chip, const uint8_t *buf) { chip->write_buf(mtd, buf, mtd->writesize); chip->write_buf(mtd, chip->oob_poi, mtd->oobsize - chip->ecc.bytes); } /** * fusiv_nand_read_page_syndrome - {REPLACABLE] hardware ecc syndrom based page read * @mtd: mtd info structure * @chip: nand chip info structure * @buf: buffer to store read data * * The hw generator calculates the error syndrome automatically. */ static int fusiv_nand_read_page_syndrome(struct mtd_info *mtd, struct nand_chip *chip, uint8_t *buf) { int bch_error_status, erased, errs = 0; uint8_t sections, i, j, fixable = 1, k; unsigned short syn_val[10]; short errlocations[MAX_FIXABLE_ERRS]; unsigned int reg, cnt; sections = mtd->writesize / BCH_SECTION_SIZE; chip->read_buf(mtd, buf, mtd->writesize); /* Commands to calculate ECC. Make sure we don't issue them for OOB area reads to avoid false ECC error flags */ if(nand_state.hardware_ecc){ i = 20; /* Tell the controller to calculate-ECC */ fusiv_nfc_writel(NFC_COMMAND, NAND_CMD_CALC_ECC); /* PECC_BUSY takes a few clocks to assert */ while(!(scu_regs->nand_status & NAND_PECC_BUSY) && i--) ; /* Wait for PECC_BUSY to go low */ while(scu_regs->nand_status & NAND_PECC_BUSY) ; } //Turn off chip enable fusiv_nfc_writel(NFC_COMMAND, NAND_CMD_CE_OFF); /* * Start second read for OOB */ reg = fusiv_nfc_readl(NFC_INDEX); fusiv_nfc_writel(NFC_COMMAND, NAND_CMD_READ0); fusiv_nfc_writel(NFC_INDEX, reg + mtd->writesize); fusiv_nfc_writel(NFC_COMMAND, NAND_CMD_RNDOUTSTART); /* CHIP_BUSY takes a few cycles to go high, we should * not access the NANDC before this */ udelay(1); /* Wait for page open to complete and CHIP_BUSY to go low. This may take many clock cycles, so ensure we wait enough */ cnt = 1000; while((scu_regs->nand_status & NAND_CHIP_BUSY) && cnt--){ udelay(1); } if(!cnt){ printk(KERN_ERR "[%s] OOB read timeout!\n", __func__); return 0; } chip->read_buf(mtd, chip->oob_poi, mtd->oobsize); //Turn off chip enable fusiv_nfc_writel(NFC_COMMAND, NAND_CMD_CE_OFF); /* * check if this page is erased */ erased = 1; for(cnt = 32; cnt < mtd->oobsize; ++cnt){ if(chip->oob_poi[cnt] != 0xff){ erased = 0; break; } } /* Check for error on read */ bch_error_status = bch_get_error_flags(); if(nand_state.hardware_ecc && bch_error_status && !erased){ #ifdef FUSIV_NAND_ECC_DEBUG pr_debug("NAND ECC Error for page no: 0x%x\n", fusiv_nfc_readl(NFC_INDEX) >> 12); pr_debug("BCH Error flags: 0x%x\n", bch_error_status); dump_stack(); // chip->ecc.read_oob(mtd, chip, // fusiv_nfc_readl(NFC_INDEX) >> 12, 0); // pr_debug("Spare area dump for the page: \n"); // print_buf(__func__, chip->oob_poi, mtd->oobsize); #endif for(i = 0; i < sections; i++){ if(bch_error_status & (1 << i)){ read_syndrome_vals(&syn_val[1], i); syn_val[0] = 0; syn_val[9] = 0; for(k = 0; k < 4; k++) errlocations[k] = 0; errs = bch_locate_errors(&syn_val[0], &errlocations[0]); #ifdef FUSIV_NAND_ECC_DEBUG pr_debug("**Section No: %d**\n", i); for(k = 0; k < 10; k++) pr_debug("syn_val[%d] = 0x%x\n", k, syn_val[k]); printk("No of errs located = %d\n", errs); for(k = 0; k < errs; k++) printk("errlocations[%d] = 0x%x\n", k, errlocations[k]); #endif if(errs >= 0){ for(j = 0; j < errs; j++){ fix_errors(i, errlocations[j], (char *) buf); } }else{ #ifdef FUSIV_NAND_ECC_DEBUG printk("Page No 0x%x has unfixable \ ECC errors! Index: 0x%x\n", (fusiv_nfc_readl(NFC_INDEX) & 0x3FFFF000) >> 12, fusiv_nfc_readl(NFC_INDEX)); #endif /* Too many errors, page unfixable */ fixable = 0; break; } } } /* Update ECC correction stats for the device */ if(fixable) mtd->ecc_stats.corrected += errs; else mtd->ecc_stats.failed++; } return 0; } #endif static int fusiv_nand_write_oob(struct mtd_info *mtd, struct nand_chip *chip, int page) { int status, len; len = mtd->oobsize; #ifdef CONFIG_MTD_NAND_FUSIV_HWECC if(nand_state.hardware_ecc){ len -= chip->ecc.bytes; } #endif print_buf(__func__, chip->oob_poi, mtd->oobsize); chip->cmdfunc(mtd, NAND_CMD_SEQIN, mtd->writesize, page); chip->write_buf(mtd, chip->oob_poi, len); chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1); status = chip->waitfunc(mtd, chip); return status & NAND_STATUS_FAIL ? -EIO : 0; } static int fusiv_nand_read_oob(struct mtd_info *mtd, struct nand_chip *chip, int page, int sndcmd) { if(sndcmd){ chip->cmdfunc(mtd, NAND_CMD_READOOB, 0, page); } chip->read_buf(mtd, chip->oob_poi, mtd->oobsize); return 0; } #endif /* * Device management interface */ static int fusiv_nand_add_partition(struct fusiv_nand_info *info) { struct mtd_info *mtd = &info->mtd; #ifdef CONFIG_MTD_PARTITIONS struct mtd_partition *parts = info->platform->partitions; int nr = info->platform->nr_partitions; return add_mtd_partitions(mtd, parts, nr); #else return add_mtd_device(mtd); #endif } /* * fusiv_nand_probe * * called by device layer when it finds a device matching * one our driver can handled. This code checks to see if * it can allocate all necessary resources then calls the * nand layer to look for devices */ static int __devinit fusiv_nand_probe(struct platform_device *pdev) { struct fusiv_nand_platform *plat = to_nand_plat(pdev); struct fusiv_nand_info *info = NULL; struct nand_chip *chip = NULL; struct mtd_info *mtd = NULL; int err = 0; dev_dbg(&pdev->dev, "(%p)\n", pdev); if(!plat){ dev_err(&pdev->dev, "No platform specific information \ for NFC driver\n"); return -EINVAL; } info = kzalloc(sizeof(*info), GFP_KERNEL); if(info == NULL){ dev_err(&pdev->dev, "Mem allocation failed for flash \ info in NFC driver\n"); err = -ENOMEM; goto out_err_kzalloc; } platform_set_drvdata(pdev, info); spin_lock_init(&info->controller.lock); init_waitqueue_head(&info->controller.wq); info->device = &pdev->dev; info->platform = plat; /* initialise chip data struct */ chip = &info->chip; chip->read_buf = fusiv_nand_read_buf; chip->write_buf = fusiv_nand_write_buf; chip->read_byte = fusiv_nand_read_byte; chip->cmd_ctrl = fusiv_nand_hwcontrol; chip->dev_ready = fusiv_nand_devready; //chip->waitfunc = fusiv_nand_status; chip->priv = &info->mtd; chip->controller = &info->controller; chip->IO_ADDR_R = (void __iomem *) NFC_BASEADDR + NFC_DATA; chip->IO_ADDR_W = (void __iomem *) NFC_BASEADDR + NFC_DATA; chip->chip_delay = 0; /* initialise mtd info data struct */ mtd = &info->mtd; mtd->priv = chip; mtd->owner = THIS_MODULE; /* initialise the hardware */ err = fusiv_nand_hw_init(info); if(err) goto out_err_hw_init; if(nand_state.hardware_ecc){ #ifdef CONFIG_MTD_NAND_FUSIV_HWECC chip->ecc.mode = NAND_ECC_HW_SYNDROME; chip->ecc.layout = &fusiv_oobinfo_2048; chip->ecc.size = 2048; chip->ecc.bytes = 32; chip->ecc.hwctl = (void *) fusiv_nand_bug; chip->ecc.calculate = (void *) fusiv_nand_bug; chip->ecc.correct = (void *) fusiv_nand_bug; chip->write_page = fusiv_nand_write_page; chip->ecc.write_page = fusiv_nand_write_page_syndrome; chip->ecc.write_page_raw = fusiv_nand_write_page_raw; chip->ecc.read_page = fusiv_nand_read_page_syndrome; chip->ecc.read_page_raw = fusiv_nand_read_page_raw; chip->ecc.write_oob = fusiv_nand_write_oob; chip->ecc.read_oob = fusiv_nand_read_oob; chip->badblock_pattern = &fusiv_bb_pattern; #endif }else{ chip->ecc.mode = NAND_ECC_SOFT; chip->ecc.read_page_raw = fusiv_nand_read_page_syndrome; chip->ecc.write_page_raw = fusiv_nand_write_page_raw; chip->ecc.write_oob = fusiv_nand_write_oob; chip->ecc.read_oob = fusiv_nand_read_oob; } /* scan hardware nand chip and setup mtd info data struct */ if(nand_scan(mtd, 1)){ err = -ENXIO; goto out_err_nand_scan; } #ifdef FUSIV_NAND_ECC_DEBUG nand_scan_done = 1; #endif /* add NAND partition */ fusiv_nand_add_partition(info); dev_dbg(&pdev->dev, "NFC driver initialised ok\n"); return 0; out_err_nand_scan: out_err_hw_init: platform_set_drvdata(pdev, NULL); kfree(info); out_err_kzalloc: return err; } static int __devexit fusiv_nand_remove(struct platform_device *pdev) { struct fusiv_nand_info *info = to_nand_info(pdev); struct mtd_info *mtd = NULL; platform_set_drvdata(pdev, NULL); /* first thing we need to do is release all our mtds * and their partitions, then go through freeing the * resources used */ mtd = &info->mtd; if(mtd){ nand_release(mtd); kfree(mtd); } /* free the common resources */ kfree(info); return 0; } /* PM Support */ #ifdef CONFIG_PM static int fusiv_nand_suspend(struct platform_device *dev, pm_message_t pm) { struct fusiv_nand_info *info = platform_get_drvdata(dev); return 0; } static int fusiv_nand_resume(struct platform_device *dev) { struct fusiv_nand_info *info = platform_get_drvdata(dev); return 0; } #else #define fusiv_nand_suspend NULL #define fusiv_nand_resume NULL #endif /* driver device registration */ static struct platform_driver fusiv_nand_driver = { .probe = fusiv_nand_probe, .remove = __devexit_p(fusiv_nand_remove), .suspend = fusiv_nand_suspend, .resume = fusiv_nand_resume, .driver = { .name = DRV_NAME, .owner = THIS_MODULE, }, }; static int __init fusiv_nand_init(void) { pr_info("%s, Version %s (c) 2010 Ikanos, Inc.\n", DRV_DESC, DRV_VERSION); return platform_driver_register(&fusiv_nand_driver); } static void __exit fusiv_nand_exit(void) { platform_driver_unregister(&fusiv_nand_driver); } module_init(fusiv_nand_init); module_exit(fusiv_nand_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR(DRV_AUTHOR); MODULE_DESCRIPTION(DRV_DESC); MODULE_ALIAS("platform:" DRV_NAME);