/****************************************************************************** ** ** FILE NAME : ifxmips_hsnand.c ** PROJECT : UEIP ** MODULES : NAND Flash ** ** DATE : 23 July 2010 ** AUTHOR : Md Firdaus B Alias Thani ** DESCRIPTION : HSNAND Flash MTD Driver ** COPYRIGHT : Copyright (c) 2010 ** Lantiq Asia Pacific ** Am Campeon 1-12, 85579 Neubiberg, Germany ** ** 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. ** ** HISTORY ** $Date $Author $Version $Comment ** 24 July 2010 Mohammad Firdaus B Alias Thani 1.0 initial version ** 15 Oct 2010 Mohammad Firdaus B Alias Thani 1.1 Ported to incl. Kern 2.6.32 ** 3 Nov 2010 Mohammad Firdaus B Alias Thani 1.2 Fixed performance issue ** 4 Jan 2011 Mohammad Firdaus B Alias Thani 1.3 Scattered DMA transfer with 2 descriptors *******************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* Project header */ #include #include #include #include #include #include #include #include "ifxmips_mtd_nand.h" #if defined(CONFIG_VR9) #include #include #endif #if defined(CONFIG_TFFS_DEV_MTDNAND) #include #endif #define AVM_NAND_STATISTIC #if defined(AVM_NAND_STATISTIC) #include struct _nand_avm_statistic { unsigned long start_jiffies; unsigned long writepages_raw; unsigned long readpages_ecc; unsigned long readpages_ecc_corrected; unsigned long readpages_ecc_uncorrectable; unsigned long writepages_ecc; unsigned long writepages_ecc_corrected; unsigned long writepages_ecc_uncorrectable; }; static void init_nandstat(struct _nand_avm_statistic *pnand_stat); #endif/*--- #if defined(AVM_NAND_STATISTIC) ---*/ /*--- #if ! defined(CONFIG_VR9) || ! defined(CONFIG_AR10) ---*/ /*--- #error "ERROR: CONFIG_VR9 or CONFIG_AR10 ---*/ /*--- #endif ---*/ #define IFX_MTD_HSNAND_BANK_NAME "ifx_hsnand" #define IFX_MTD_NAND_BANK_NAME "ifx_nand" #define HSNAND_DMA_BURST_LEN DMA_BURSTL_8DW #define DEBUG_HSNAND #define HSNAND_EVENT 1 #define HSNAND_CURRENT_DMA_CH 0 #define HSNAND_MAX_HWECC_ERROR 3 /*--- erst 3 Fehler werden nach oben gemeldet ---*/ #define HSNAND_AR10_DMA_MODE 3 /*--- transparent DMA without ECC ---*/ #if defined(CONFIG_NAND_CS1) #define IFX_ND_MDCTL_CSx IFX_ND_MDCTL_CS1 #else #define IFX_ND_MDCTL_CSx IFX_ND_MDCTL_CS0 #endif #define CHIP_ENABLE_HW_ECC 0x08 #define CHIP_DISABLE_HW_ECC 0x00 enum chip_hwecc { chip_disable_hwecc = CHIP_DISABLE_HW_ECC, chip_enable_hwecc = CHIP_ENABLE_HW_ECC, }; enum chip_ecc { no_error, correctable, uncorrectable, }; #if defined(CONFIG_MTD_PARTITIONS) #if defined(CONFIG_MTD_CMDLINE_PARTS) static const char *part_probes[] = { "cmdlinepart", NULL }; #endif /* CONFIG_MTD_PARTITIONS */ #endif /* CONFIG_MTD_CMDLINE_PARTS */ /* Partition table, defined in platform_board.c */ extern struct mtd_partition ifx_nand_partitions[IFX_MTD_NAND_PARTS]; static struct dma_device_info *dma_device = NULL; static struct mtd_info *ifx_hsnand_mtd = NULL; static inline void NAND_DISABLE_CE(struct nand_chip *nand) { IFX_REG_W32_MASK(NAND_CON_LATCH_CE,0,IFX_EBU_NAND_CON); } static inline void NAND_ENABLE_CE(struct nand_chip *nand) { IFX_REG_W32_MASK(0,NAND_CON_LATCH_CE,IFX_EBU_NAND_CON); } /* HSNAND private structure */ struct hsnand_info { enum chip_ecc ecc_error; #if defined(CONFIG_AR10) unsigned int current_page; unsigned int ndac_ctl_1; unsigned int ndac_ctl_2; #endif unsigned char *hwecc_buffer; unsigned char *tmp_buffer; wait_queue_head_t hsnand_wait; volatile long wait_flag; #if defined(AVM_NAND_STATISTIC) struct _nand_avm_statistic nandstat; #endif/*--- #if defined(AVM_NAND_STATISTIC) ---*/ enum chip_ecc (*read_eccstatus)(struct mtd_info *mtd, unsigned int command); }; struct hsnand_info hsnand_dev; void ifx_hsnand_ar10_update_addr(struct mtd_info *mtd, unsigned int command, int column, int page_addr); /*------------------------------------------------------------------------------------------*\ * fn static int ifx_hsnand_dma_intr_handler(struct dma_device_info *dma_dev, int status) * ingroup IFX_HSNAND_DRV * brief DMA interrupt handler routine function * param dma_dev DMA device structure * param status Type of interrupt generated * return none \*------------------------------------------------------------------------------------------*/ static int ifx_hsnand_dma_intr_handler(struct dma_device_info *dma_dev, int status, int chan_no) { struct hsnand_info *hsnand = &hsnand_dev; /*--- printk(KERN_ERR "{%s} status 0x%x chan_no 0x%x\n", __func__, status, chan_no); ---*/ switch (status) { case RCV_INT: dma_dev->rx_chan[chan_no]->close(dma_dev->rx_chan[chan_no]); set_bit(HSNAND_EVENT, &hsnand->wait_flag); /*--- wake_up_interruptible(&hsnand->hsnand_wait); ---*/ wake_up(&hsnand->hsnand_wait); break; case TX_BUF_FULL_INT: if (dma_dev->tx_chan[chan_no]->control == IFX_DMA_CH_ON) { dma_dev->tx_chan[chan_no]->enable_irq(dma_dev->tx_chan[chan_no]); } break; case TRANSMIT_CPT_INT: dma_dev->tx_chan[chan_no]->disable_irq(dma_dev->tx_chan[chan_no]); set_bit(HSNAND_EVENT, &hsnand->wait_flag); /*--- wake_up_interruptible(&hsnand->hsnand_wait); ---*/ wake_up(&hsnand->hsnand_wait); break; } return IFX_SUCCESS; } #if defined(DEBUG_HSNAND) void debug_nand(void) { printk("\n**** Dumping HSNAND/EBU Registers *****\n"); printk("EBU_CLC: 0x%08x\n", IFX_REG_R32(IFX_EBU_CLC)); printk("IEN_ECC: 0x%08x\n", IFX_REG_R32(IFX_EBU_ECC_IEN)); printk("ECC CR: 0x%08x\n", IFX_REG_R32(IFX_EBU_NAND_ECC_CR)); printk("ECC0: 0x%08x\n", IFX_REG_R32(IFX_EBU_NAND_ECC0)); printk("EBU CON: 0x%08x\n", IFX_REG_R32(IFX_EBU_CON)); printk("ADDRSEL1: 0x%08x\n", IFX_REG_R32(IFX_EBU_ADDSEL1)); printk("EBU_NAND_CON: 0x%08x\n", IFX_REG_R32(IFX_EBU_NAND_CON)); printk("EBU_BUSCON: 0x%08x\n", IFX_REG_R32(IFX_EBU_BUSCON1)); printk("CS_BASE_A: 0x%08x\n", IFX_REG_R32(IFX_CS_BASE_A)); printk("HSNAND_DEV: 0x%08x\n", IFX_REG_R32(IFX_NAND_INFO)); printk("NDAC_CTL1: 0x%08x\n", IFX_REG_R32(IFX_NDAC_CTL1)); printk("NDAC_CTL2: 0x%08x\n", IFX_REG_R32(IFX_NDAC_CTL2)); printk("HSMD_CTL: 0x%08x\n", IFX_REG_R32(IFX_HSMD_CTRL)); printk("RX_CNT: 0x%08x\n", IFX_REG_R32(IFX_RX_CNT)); printk("INTR_MSK_CTRL: 0x%08x\n",IFX_REG_R32(IFX_HSNAND_INTR_MASK_CTRL)); printk("INTR_STAT: 0x%08x\n", IFX_REG_R32(IFX_HSNAND_INTR_STAT)); printk("RX_CNT: 0x%08x\n", IFX_REG_R32(IFX_RX_CNT)); printk("DX_PLUS: 0x%08x\n", IFX_REG_R32(IFX_DPLUS_CTRL)); } void debug_dma(int channel) { u32 tmp; printk("\n**** Dumping DMA Registers for channel %d\n", channel); tmp = IFX_REG_R32(IFX_DMA_CS(0)); IFX_REG_W32(channel, IFX_DMA_CS(0)); printk("DMA_CS : 0x%08x\n", IFX_REG_R32(IFX_DMA_CS(0))); printk("DMA_CDLEN : 0x%08x\n", IFX_REG_R32(IFX_DMA_CDLEN(0))); printk("CIE : 0x%08x\n", IFX_REG_R32(IFX_DMA_CIE(0))); printk("CIS : 0x%08x\n", IFX_REG_R32(IFX_DMA_CIS(0))); printk("CPOLL : 0x%08x\n", IFX_REG_R32(IFX_DMA_CPOLL)); printk("CDBA : 0x%08x\n", IFX_REG_R32(IFX_DMA_CDBA(0))); printk("CCTRL : 0x%08x\n", IFX_REG_R32(IFX_DMA_CCTRL(0))); printk("IRNEN : 0x%08x\n", IFX_REG_R32(IFX_DMA_IRNEN)); printk("IRNCR : 0x%08x\n", IFX_REG_R32(IFX_DMA_IRNCR)); printk("IRNICR : 0x%08x\n", IFX_REG_R32(IFX_DMA_IRNICR)); IFX_REG_W32(tmp, IFX_DMA_CS(0)); tmp = IFX_REG_R32(IFX_DMA_PS(0)); IFX_REG_W32(6, IFX_DMA_PS(0)); printk("DMA_PS : 0x%08x\n", IFX_REG_R32(IFX_DMA_PS(0))); printk("PCTRL : 0x%08x\n", IFX_REG_R32(IFX_DMA_PCTRL(0))); IFX_REG_W32(tmp, IFX_DMA_PS(0)); printk("IM4_IER : 0x%08x\n", IFX_REG_R32(IFX_ICU_IM4_IER)); } #endif /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ static int ifx_nand_ready(struct mtd_info *mtd __attribute__((unused))) { return NAND_READY; } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ static void ifx_hsnand_setup_chipecc(enum chip_hwecc status) { RESET_CHIP(); WRITE_NAND_COMMAND(0xEF); // setFeature WRITE_NAND_ADDRESS(0x90); // array operation mode ndelay(70); WRITE_NAND(status); WRITE_NAND(0x00); WRITE_NAND(0x00); WRITE_NAND(0x00); while(!NAND_READY); } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ static void ifx_hsnand_hwctl(struct mtd_info *mtd, int mode) { #if 0 unsigned int reg, ecc_status; *IFX_EBU_NAND_ECC0 = 0; reg = SM(IFX_EBU_ECC_IEN_ENABLE, IFX_EBU_ECC_IEN_IR); IFX_REG_W32(reg | IFX_REG_R32(IFX_EBU_ECC_IEN), IFX_EBU_ECC_IEN); *IFX_EBU_NAND_CON |= IFX_EBU_NAND_CON_ECC; *IFX_RX_CNT = 0; reg = SM(IFX_EBU_NAND_ECC_CRM_ENABLE, IFX_EBU_NAND_ECC_CRM) | SM(IFX_EBU_NAND_ECC_PAGE_512, IFX_EBU_NAND_ECC_PAGE); IFX_REG_W32(reg, IFX_EBU_NAND_ECC_CR); debug_nand(); #endif } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ static enum chip_ecc ifx_hsnand_read_eccstatus(struct mtd_info *mtd, unsigned int command) { unsigned int status; register struct nand_chip *chip = mtd->priv; chip->cmd_ctrl(mtd, NAND_CMD_STATUS, NAND_CLE | NAND_CTRL_CHANGE); status = chip->read_byte(mtd); chip->cmd_ctrl(mtd, command, NAND_NCE | NAND_CLE | NAND_CTRL_CHANGE); chip->cmd_ctrl(mtd, NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE); if(status & NAND_STATUS_FAIL) { return uncorrectable; } if(status & NAND_STATUS_CRITICAL_BLOCK) { return correctable; } return no_error; } /*------------------------------------------------------------------------------------------*\ * Toshiba ECC-Status * | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | * | Sector - info | Errors | \*------------------------------------------------------------------------------------------*/ static enum chip_ecc ifx_hsnand_toshiba_read_eccstatus(struct mtd_info *mtd, unsigned int command) { unsigned int i, status, eccstatus; register struct nand_chip *chip = mtd->priv; enum chip_ecc chip_ecc_status = no_error; chip->cmd_ctrl(mtd, NAND_CMD_STATUS, NAND_CLE | NAND_CTRL_CHANGE); status = chip->read_byte(mtd); if (status & NAND_STATUS_CRITICAL_BLOCK) { printk(KERN_ERR "{%s} 0x%x ecc.steps %d\n", "tsh_read_ecc", status, chip->ecc.steps); chip->cmd_ctrl(mtd, NAND_CMD_ECCSTATUS, NAND_CLE | NAND_CTRL_CHANGE); /*-------------------------------------------------------------*\ * die FLASH-interne ECC wird für je 512 Byte berechnet, * wir schreiben aber eine 1Bit ECC für 256 Bytes \*-------------------------------------------------------------*/ for (i = 0; i < (chip->ecc.steps >> 1); i++) { eccstatus = chip->read_byte(mtd); printk(KERN_ERR "{%s} Sector %d ecc 0x%x\n", "tsh_read_ecc", i, eccstatus); if ((eccstatus & 0xFF) < 7) { chip_ecc_status = correctable; } } } if (status & NAND_STATUS_FAIL) { printk(KERN_ERR "{%s} status 0x%x\n", "tsh_read_ecc", status); chip_ecc_status = uncorrectable; } chip->cmd_ctrl(mtd, command, NAND_NCE | NAND_CLE | NAND_CTRL_CHANGE); chip->cmd_ctrl(mtd, NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE); return chip_ecc_status; } /*------------------------------------------------------------------------------------------*\ * fn static void ifx_hsnand_write_page_raw(struct mtd_info *mtd, struct nand_chip *chip, const u8 *buf) * ingroup IFX_HSNAND_DRV * brief write page with hardware ECC checking mechanism * param mtd MTD device structure * param chip Nand chip device structure * param buf memory location to get write data from * return none \*------------------------------------------------------------------------------------------*/ static void ifx_hsnand_write_page_raw(struct mtd_info *mtd, struct nand_chip *chip, const u8 *buf) { if (mtd->writesize == 4096) { /*--- der VR9 kann keine 4k Pages schreiben ---*/ chip->write_buf(mtd, buf, 2048); chip->write_buf(mtd, &buf[2048], 2048); } else chip->write_buf(mtd, buf, mtd->writesize); chip->write_buf(mtd, chip->oob_poi, mtd->oobsize); #if defined(AVM_NAND_STATISTIC) hsnand_dev.nandstat.writepages_raw++; #endif/*--- #if defined(AVM_NAND_STATISTIC) ---*/ return; } #if 0 #if defined(CONFIG_AR10) static void ifx_hsnand_ar10_write_page_raw(struct mtd_info *mtd, struct nand_chip *chip, const u8 *buf) { struct hsnand_info *hsnand = &hsnand_dev; chip->write_buf(mtd, buf, mtd->writesize); chip->ecc.write_oob(mtd, chip, hsnand->current_page); return; } #endif #endif /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ static unsigned int ifx_hsnand_verify_hwecc(unsigned char *buf, unsigned char *tmp, unsigned int len) { unsigned int i, mask, zeros; int bits = 0; unsigned int *p = (unsigned int *)buf; unsigned int *q = (unsigned int *)tmp; for (i = 0; i < len >> 2; i++) { mask = *p ^ *q; while (mask) { zeros = ffs(mask); bits++; mask &= ~(1 << (zeros - 1)); } p++; q++; } #if 0 if (bits) printk(KERN_ERR "{%s} Bitfehler %d\n", __func__, bits); #endif return bits; } /*------------------------------------------------------------------------------------------*\ * read page with internal micron-nandchip ecc \*------------------------------------------------------------------------------------------*/ static int ifx_hsnand_micron_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, uint8_t *buf, int page) { struct hsnand_info *hsnand = &hsnand_dev; unsigned int i, biterror, biterror_oob, biterror_oob_ecc; unsigned int oob_offset, oob_len; chip->read_buf(mtd, buf, mtd->writesize); #if defined(CONFIG_VR9) chip->read_buf(mtd, chip->oob_poi, mtd->oobsize); #else chip->ecc.read_oob(mtd, chip, page, 1); #endif #if defined(AVM_NAND_STATISTIC) hsnand->nandstat.readpages_ecc++; #endif/*--- #if defined(AVM_NAND_STATISTIC) ---*/ switch (hsnand->ecc_error) { case correctable: ifx_hsnand_setup_chipecc(chip_disable_hwecc); /*--- noch mal lesen ohne HWECC ---*/ #if defined(CONFIG_VR9) chip->cmdfunc(mtd, NAND_CMD_READ0, 0, page); chip->read_buf(mtd, hsnand->hwecc_buffer, mtd->writesize); chip->read_buf(mtd, &hsnand->hwecc_buffer[mtd->writesize], mtd->oobsize); #else ifx_hsnand_ar10_update_addr(mtd, NAND_CMD_READ0, 0, page); chip->read_buf(mtd, hsnand->hwecc_buffer, mtd->writesize); chip->ecc.read_oob(mtd, chip, page, 1); #endif ifx_hsnand_setup_chipecc(chip_enable_hwecc); for (i = 0; i < chip->ecc.steps; i++) { biterror = ifx_hsnand_verify_hwecc(&buf[i * chip->ecc.size], &hsnand->hwecc_buffer[i * chip->ecc.size], chip->ecc.size); #if 1 oob_offset = chip->ecc.layout->oobfree[i].offset; oob_len = chip->ecc.layout->oobfree[i].length; biterror_oob = ifx_hsnand_verify_hwecc( &chip->oob_poi[oob_offset], &hsnand->hwecc_buffer[mtd->writesize + oob_offset], oob_len); biterror_oob_ecc = ifx_hsnand_verify_hwecc( &chip->oob_poi[oob_offset + oob_len], &hsnand->hwecc_buffer[mtd->writesize + oob_offset + oob_len], 8); #else biterror_oob = ifx_hsnand_verify_hwecc( chip->oob_poi, &hsnand->hwecc_buffer[mtd->writesize], mtd->oobsize); if (biterror_oob) { int j; for (j=0;joobsize;j++) { printk(" 0x%x 0x%x", chip->oob_poi[j], hsnand->hwecc_buffer[mtd->writesize + j]); } } #endif if (biterror || biterror_oob || biterror_oob_ecc) { printk(KERN_ERR "{%s} page 0x%x Sec %d %d Berr %d OOB %d ECC\n", "micron_read_hwecc", page, i, biterror, biterror_oob, biterror_oob_ecc); } biterror += biterror_oob; if (biterror < HSNAND_MAX_HWECC_ERROR) { continue; } mtd->ecc_stats.corrected++; } #if defined(AVM_NAND_STATISTIC) hsnand->nandstat.readpages_ecc_corrected++; #endif/*--- #if defined(AVM_NAND_STATISTIC) ---*/ break; case uncorrectable: mtd->ecc_stats.failed++; #if defined(AVM_NAND_STATISTIC) hsnand->nandstat.readpages_ecc_uncorrectable++; #endif/*--- #if defined(AVM_NAND_STATISTIC) ---*/ break; case no_error: break; } return 0; } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ static int ifx_hsnand_toshiba_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, uint8_t *buf, int page) { struct hsnand_info *hsnand = &hsnand_dev; #if defined(AVM_NAND_STATISTIC) hsnand->nandstat.readpages_ecc++; #endif/*--- #if defined(AVM_NAND_STATISTIC) ---*/ if (mtd->writesize == 4096) { /*--- der VR9 kann keine 4k Pages schreiben ---*/ chip->read_buf(mtd, buf, 2048); chip->read_buf(mtd, &buf[2048], 2048); } else chip->read_buf(mtd, buf, mtd->writesize); #if defined(CONFIG_VR9) chip->read_buf(mtd, chip->oob_poi, mtd->oobsize); #else chip->ecc.read_oob(mtd, chip, page, 1); #endif switch (hsnand->ecc_error) { case correctable: mtd->ecc_stats.corrected++; #if defined(AVM_NAND_STATISTIC) hsnand->nandstat.readpages_ecc_corrected++; #endif/*--- #if defined(AVM_NAND_STATISTIC) ---*/ break; case uncorrectable: mtd->ecc_stats.failed++; #if defined(AVM_NAND_STATISTIC) hsnand->nandstat.readpages_ecc_uncorrectable++; #endif/*--- #if defined(AVM_NAND_STATISTIC) ---*/ break; case no_error: break; } return 0; } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ static int ifx_hsnand_write_page(struct mtd_info *mtd, struct nand_chip *chip, const uint8_t *buf, int page, int cached __attribute__((unused)), int raw) { int status; #ifdef CONFIG_MTD_NAND_VERIFY_WRITE unsigned char no_of_verify_retries = 0; verify_retry: #endif /*--- #ifdef CONFIG_MTD_NAND_VERIFY_WRITE ---*/ /*--- printk(KERN_ERR "{%s} page 0x%x raw %d\n", __func__, page, raw); ---*/ chip->cmdfunc(mtd, NAND_CMD_SEQIN, 0x00, page); chip->ecc.write_page(mtd, chip, buf); chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1); status = chip->waitfunc(mtd, chip); if (status & NAND_STATUS_FAIL) { printk(KERN_ERR "{%s} \n", __func__); return -EIO; } #ifdef CONFIG_MTD_NAND_VERIFY_WRITE { // 1. Durchlauf // .Korrigierbarer Fehler erkannt? // -> Chip resetten (es wird nichts gelesen) // -> erneutes Schreiben auf die selbe Page (ohne Löschen) // // 2. Durchlauf // .korrigierbare Fehler sind egal // .'verify_buf' entscheidet, ob Schreiben funktioniert hat // struct mtd_ecc_stats stats = mtd->ecc_stats; struct hsnand_info *hsnand = &hsnand_dev; /* Send command to read back the data */ chip->cmdfunc(mtd, NAND_CMD_READ0, 0, page); if ((no_of_verify_retries == 0) && (hsnand->ecc_error == correctable)) { printk(KERN_ERR "VERIFY-ERROR on page: %d: rewrite...\n", page); no_of_verify_retries++; chip->cmdfunc(mtd, NAND_CMD_RESET, -1, -1); goto verify_retry; } #if defined(AVM_NAND_STATISTIC) hsnand->nandstat.writepages_ecc++; #endif/*--- #if defined(AVM_NAND_STATISTIC) ---*/ switch (hsnand->ecc_error) { case no_error: break; case correctable: printk(KERN_ERR "VERIFY-ERROR page: %d: readwrite results correctable errors\n", page); chip->ecc.read_page_raw(mtd, chip, hsnand->tmp_buffer, page); /*--- ifx_hsnand_read_page_hwecc(mtd, chip, hsnand->tmp_buffer, page); ---*/ if(mtd->ecc_stats.failed - stats.failed) { printk(KERN_ERR "VERIFY-ERROR on page: %d: too many biterrors\n", page); #if defined(AVM_NAND_STATISTIC) hsnand->nandstat.writepages_ecc_uncorrectable++; #endif/*--- #if defined(AVM_NAND_STATISTIC) ---*/ return -EIO; } if (mtd->ecc_stats.corrected - stats.corrected) { printk(KERN_ERR "VERIFY-ERROR on page: %d: verify OK with %d biterrors\n", page, mtd->ecc_stats.corrected - stats.corrected); } #if defined(AVM_NAND_STATISTIC) hsnand->nandstat.writepages_ecc_corrected++; #endif/*--- #if defined(AVM_NAND_STATISTIC) ---*/ break; case uncorrectable: printk(KERN_ERR "VERIFY-ERROR on page: %d: non correctable error detected\n", page); mtd->ecc_stats.failed++; #if defined(AVM_NAND_STATISTIC) hsnand->nandstat.writepages_ecc_uncorrectable++; #endif/*--- #if defined(AVM_NAND_STATISTIC) ---*/ return -EIO; } } #endif /*--- #ifdef CONFIG_MTD_NAND_VERIFY_WRITE ---*/ return 0; } /*------------------------------------------------------------------------------------------*\ * fn static u_char ifx_nand_read_byte(struct mtd_info *mtd) * ingroup IFX_HSNAND_DRV * brief Read data byte-by-byte * param mtd MTD device structure * return ret data read \*------------------------------------------------------------------------------------------*/ static u_char ifx_nand_read_byte(struct mtd_info *mtd) { return READ_NAND(); } /*------------------------------------------------------------------------------------------*\ * fn void ifx_nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) * ingroup IFX_NAND_DRV * brief read chip data into buffer * param mtd MTD device structure * param buf buffer to store date * param len number of bytes to read * return none \*------------------------------------------------------------------------------------------*/ static void ifx_nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) { int i; for (i = 0; i < len; i++) buf[i]=READ_NAND(); } /*------------------------------------------------------------------------------------------*\ * fn void ifx_nand_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len) * ingroup IFX_NAND_DRV * brief write buffer to chip * param mtd MTD device structure * param buf data buffer * param len number of bytes to write * return none \*------------------------------------------------------------------------------------------*/ static void ifx_nand_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len) { int i; for (i = 0; i < len; i++) WRITE_NAND(buf[i]); } /*------------------------------------------------------------------------------------------*\ * fn static int ifx_nand_verify_buf(struct mtd_info *mtd, const u8 *buf, int len) * ingroup IFX_HSNAND_DRV * brief Function to verify buffer after a write * param mtd MTD device structure * param buf Buffer to be verified * param len Length of buffer to be verified * return none \*------------------------------------------------------------------------------------------*/ static int ifx_nand_verify_buf(struct mtd_info *mtd, const uint8_t *buf, int len) { int i; for (i = 0; i < len; i++) if (buf[i] != READ_NAND()) { printk(KERN_ERR "<%s> failed\n", __func__); return -EFAULT; } return 0; } /*------------------------------------------------------------------------------------------*\ * fn static void ifx_nand_select_chip(struct mtd_info *mtd, int chip) * ingroup IFX_HSNAND_DRV * brief Selects NAND chip to enable/disable * param mtd MTD device structure * paran chip Control chip number. -1 to deselect chip * return none \*------------------------------------------------------------------------------------------*/ static void ifx_nand_select_chip(struct mtd_info *mtd, int chip) { struct nand_chip *nand = mtd->priv; switch (chip) { case -1: NAND_DISABLE_CE(nand); /*--- disable CE Latch ---*/ IFX_REG_W32_MASK(IFX_EBU_NAND_CON_NANDM, 0, IFX_EBU_NAND_CON); break; case 0: IFX_REG_W32_MASK(0, IFX_EBU_NAND_CON_NANDM, IFX_EBU_NAND_CON); NAND_ENABLE_CE(nand); /*--- enable CE Latch ---*/ break; default: printk(KERN_ERR "[%s] Unknown chip select option\n", __func__); } } /*------------------------------------------------------------------------------------------*\ * fn static void ifx_hsnand_read_buf(struct mtd_info *mtd, u8 *buf, int len) * ingroup IFX_HSNAND_DRV * brief Low-level read function to read data from NAND into buffer * param mtd MTD device structure * param buf Buffer that is used for storing read data * param len Length to be read * return none \*------------------------------------------------------------------------------------------*/ #if defined(CONFIG_VR9) static void ifx_hsnand_read_buf(struct mtd_info *mtd, u8 *buf, int len) { struct dma_device_info *dma_dev = dma_device; struct hsnand_info *hsnand = &hsnand_dev; *IFX_RX_CNT = len; dma_device_desc_setup(dma_dev, buf, len, HSNAND_CURRENT_DMA_CH); dma_dev->rx_chan[HSNAND_CURRENT_DMA_CH]->open(dma_dev->rx_chan[HSNAND_CURRENT_DMA_CH]); *IFX_DPLUS_CTRL = IFX_START_HSDMA_RX; wait_event(hsnand->hsnand_wait, test_and_clear_bit(HSNAND_EVENT, &hsnand->wait_flag)); return; } /*------------------------------------------------------------------------------------------*\ * fn static void ifx_hsnand_write_buf(struct mtd_info *mtd, const u8 *buf, int len) * ingroup IFX_HSNAND_DRV * brief Low-level write function to write data from buffer to NAND device * param mtd MTD device structure * param buf Buffer that is needed to write data into * param len Length to be write * return none \*------------------------------------------------------------------------------------------*/ static void ifx_hsnand_write_buf(struct mtd_info *mtd, const unsigned char *buf, int len) { struct dma_device_info *dma_dev = dma_device; struct hsnand_info *hsnand = &hsnand_dev; /* start writing to NAND device */ dma_dev->tx_chan[HSNAND_CURRENT_DMA_CH]->open(dma_dev->tx_chan[HSNAND_CURRENT_DMA_CH]); dma_device_write(dma_dev, buf, len, NULL, HSNAND_CURRENT_DMA_CH); /* enable DMA tx chan interrupt for multi desc write */ if (dma_dev->tx_chan[HSNAND_CURRENT_DMA_CH]->control == IFX_DMA_CH_ON) dma_dev->tx_chan[HSNAND_CURRENT_DMA_CH]->enable_irq(dma_dev->tx_chan[HSNAND_CURRENT_DMA_CH]); *IFX_DPLUS_CTRL = IFX_START_HSDMA_TX; wait_event(hsnand->hsnand_wait, test_and_clear_bit(HSNAND_EVENT, &hsnand->wait_flag)); return; } #endif /*--- #if defined(CONFIG_VR9) ---*/ /*------------------------------------------------------------------------------------------*\ * fn static void ifx_hsnand_read_buf(struct mtd_info *mtd, u8 *buf, int len) * ingroup IFX_HSNAND_DRV * brief Low-level read function to read data from NAND into buffer * param mtd MTD device structure * param buf Buffer that is used for storing read data * param len Length to be read * return none \*------------------------------------------------------------------------------------------*/ #if defined(CONFIG_AR10) static void ifx_hsnand_ar10_read_buf(struct mtd_info *mtd, u8 *buf, int len) { struct dma_device_info *dma_dev = dma_device; struct hsnand_info *hsnand = &hsnand_dev; dma_device_desc_setup(dma_dev, buf, len, HSNAND_CURRENT_DMA_CH); dma_dev->rx_chan[HSNAND_CURRENT_DMA_CH]->open(dma_dev->rx_chan[HSNAND_CURRENT_DMA_CH]); *IFX_ND_PARA0 = IFX_ND_PARA0_TYPE_ONFI | IFX_ND_PARA0_PIB_64 | IFX_ND_PARA0_PAGE_2k; *IFX_ND_MDCTL = IFX_ND_MDCTL_MODE_DMA | IFX_ND_MDCTL_CSx; /*--- printk(KERN_ERR "{%s} READ PARA0 reg: %08x len %d\n", __func__, *IFX_ND_PARA0, len); ---*/ /* Update ndac address registers */ IFX_REG_W32(hsnand->ndac_ctl_1, IFX_NDAC_CTL_1); IFX_REG_W32(hsnand->ndac_ctl_2, IFX_NDAC_CTL_2); *IFX_ND_MDCTL |= IFX_ND_MDCTL_GO; wait_event(hsnand->hsnand_wait, test_and_clear_bit(HSNAND_EVENT, &hsnand->wait_flag)); return; } /*------------------------------------------------------------------------------------------*\ * fn static void ifx_hsnand_write_buf(struct mtd_info *mtd, const u8 *buf, int len) * ingroup IFX_HSNAND_DRV * brief Low-level write function to write data from buffer to NAND device * param mtd MTD device structure * param buf Buffer that is needed to write data into * param len Length to be write * return none \*------------------------------------------------------------------------------------------*/ #if 0 static void ifx_hsnand_ar10_write_buf(struct mtd_info *mtd, const unsigned char *buf, int len) { struct dma_device_info *dma_dev = dma_device; struct hsnand_info *hsnand = &hsnand_dev; /* start writing to NAND device */ dma_dev->tx_chan[HSNAND_CURRENT_DMA_CH]->open(dma_dev->tx_chan[HSNAND_CURRENT_DMA_CH]); dma_device_write(dma_dev, buf, len, NULL, HSNAND_CURRENT_DMA_CH); *IFX_ND_PARA0 = IFX_ND_PARA0_TYPE_ONFI | IFX_ND_PARA0_PIB_64 | IFX_ND_PARA0_PAGE_2k; *IFX_ND_MDCTL = IFX_ND_MDCTL_MODE_DMA | IFX_ND_MDCTL_CSx | IFX_ND_MDCTL_WR; /* enable DMA tx chan interrupt for multi desc write */ if (dma_dev->tx_chan[HSNAND_CURRENT_DMA_CH]->control == IFX_DMA_CH_ON) dma_dev->tx_chan[HSNAND_CURRENT_DMA_CH]->enable_irq(dma_dev->tx_chan[HSNAND_CURRENT_DMA_CH]); /* Update ndac address registers */ IFX_REG_W32(hsnand->ndac_ctl_1, IFX_NDAC_CTL_1); IFX_REG_W32(hsnand->ndac_ctl_2, IFX_NDAC_CTL_2); *IFX_ND_MDCTL |= IFX_ND_MDCTL_GO; wait_event(hsnand->hsnand_wait, test_and_clear_bit(HSNAND_EVENT, &hsnand->wait_flag)); return; } #else static void ifx_hsnand_ar10_write_buf(struct mtd_info *mtd, const unsigned char *buf, int len) { int i; for (i = 0; i < len; i++) WRITE_NAND(buf[i]); } #endif /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ static int ifx_hsnand_ar10_read_oob(struct mtd_info *mtd, struct nand_chip *chip, int page, int sndcmd) { int i; u8 *buf = chip->oob_poi; if (sndcmd) { chip->cmdfunc(mtd, NAND_CMD_READOOB, 0, page); sndcmd = 0; } for (i = 0; i < mtd->oobsize; i++) { buf[i] = READ_NAND(); } return sndcmd; } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ static int ifx_hsnand_ar10_write_oob(struct mtd_info *mtd, struct nand_chip *chip, int page) { int i, status; unsigned char *buf = chip->oob_poi; chip->cmdfunc(mtd, NAND_CMD_SEQIN, mtd->writesize, page); for (i = 0; i < mtd->oobsize; i++) { WRITE_NAND(buf[i]); } chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1); status = chip->waitfunc(mtd, chip); return status & NAND_STATUS_FAIL ? -EIO : 0; } #endif /*--- #if defined(CONFIG_AR10) ---*/ /*------------------------------------------------------------------------------------------*\ * fn static void ifx_nand_cmd_ctrl (struct mtd_info *mtd, int data, unsigned int ctrl) * ingroup IFX_HSNAND_DRV * brief Control commands that is used to control hardware specific lines * param mtd MTD device structure * param data Command that is needed to be sent to the NAND controller * param ctrl Control commands that determines to control lines enabled/disabled * return none \*------------------------------------------------------------------------------------------*/ static unsigned int latchcmd = 0; static void ifx_nand_cmd_ctrl (struct mtd_info *mtd, int data, unsigned int ctrl) { struct nand_chip *this = mtd->priv; if (ctrl & NAND_CTRL_CHANGE) { if (ctrl & NAND_CLE) latchcmd = NAND_WRITE_CMD; else if(ctrl & NAND_ALE) latchcmd = NAND_WRITE_ADDR; } if(data != NAND_CMD_NONE){ *(volatile u8*)((u32)this->IO_ADDR_W | latchcmd) = data; while((IFX_REG_R32(IFX_EBU_NAND_WAIT) & IFX_EBU_NAND_WAIT_WR_C) == 0); } return; } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ #if defined(CONFIG_AR10) void ifx_hsnand_ar10_update_addr(struct mtd_info *mtd, unsigned int command, int column, int page_addr) { struct hsnand_info *hsnand = &hsnand_dev; unsigned int tmp_addr, addr_0, addr_1, addr_2, addr_3, addr_4; hsnand->current_page = page_addr; /*--- printk(KERN_ERR "[%s] page address: 0x%x\n", __func__, page_addr); ---*/ tmp_addr = page_addr << 12; addr_0 = tmp_addr & 0x000000FF; addr_1 = (tmp_addr & 0x00000F00) >> 8; addr_2 = (tmp_addr & 0x000FF000) >> 12; addr_3 = (tmp_addr & 0x0FF00000) >> 20; addr_4 = (tmp_addr & 0x70000000) >> 28; hsnand->ndac_ctl_1 = (addr_2 << 24) | (addr_1 << 16) | (addr_0 << 8) | command; hsnand->ndac_ctl_2 = IFX_ND_ADDR_CYL_5 | (addr_4 << 8) | addr_3; /*--- printk(KERN_ERR "[%s] cur page: %d, ndac_1: %08x, ndac_2: %08x\n", __func__, hsnand->current_page, hsnand->ndac_ctl_1, hsnand->ndac_ctl_2); ---*/ return; } #endif /*--- #if defined(CONFIG_AR10) ---*/ /*------------------------------------------------------------------------------------------*\ * fn static void ifx_hsnand_command(struct mtd_info *mtd, unsigned int command, * int column, int page_addr) * brief Send command to NAND device. This is the version for the new large page * devices We dont have the separate regions as we have in the small page * devices. We must emulate NAND_CMD_READOOB to keep the code compatible. * param mtd MTD device structure * param command the command to be sent * param column the column address for this command, -1 if none * param page_addr the page address for this command, -1 if none * return none \*------------------------------------------------------------------------------------------*/ static void ifx_hsnand_command(struct mtd_info *mtd, unsigned int command, int column, int page_addr) { register struct nand_chip *chip = mtd->priv; /* Emulate NAND_CMD_READOOB */ if (command == NAND_CMD_READOOB) { column += mtd->writesize; command = NAND_CMD_READ0; } #if defined(CONFIG_AR10) else { ifx_hsnand_ar10_update_addr(mtd, command, 0, page_addr); } #endif NAND_READY_CLEAR; /* Command latch cycle */ chip->cmd_ctrl(mtd, command & 0xff, NAND_NCE | NAND_CLE | NAND_CTRL_CHANGE); if (column != -1 || page_addr != -1) { int ctrl = NAND_CTRL_CHANGE | NAND_ALE | NAND_NCE; /* Serially input address */ if (column != -1) { /* Adjust columns for 16 bit buswidth */ if (chip->options & NAND_BUSWIDTH_16) column >>= 1; chip->cmd_ctrl(mtd, column & 0xff, ctrl); ctrl &= ~NAND_CTRL_CHANGE; chip->cmd_ctrl(mtd, (column >> 8) & 0xff, ctrl); } if (page_addr != -1) { chip->cmd_ctrl(mtd, page_addr & 0xff, ctrl); chip->cmd_ctrl(mtd, (page_addr >> 8) & 0xff, ctrl); /* One more address cycle for devices > 128MiB */ if (chip->chipsize > (128 << 20)) chip->cmd_ctrl(mtd, (page_addr >> 16) & 0xff, NAND_NCE | NAND_ALE); } } chip->cmd_ctrl(mtd, NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE); /* * program and erase have their own busy handlers * status, sequential in, and deplete1 need no delay */ switch (command) { case NAND_CMD_CACHEDPROG: case NAND_CMD_PAGEPROG: case NAND_CMD_ERASE1: case NAND_CMD_ERASE2: case NAND_CMD_SEQIN: case NAND_CMD_RNDIN: case NAND_CMD_STATUS: case NAND_CMD_DEPLETE1: return; /*--- read error status commands require only a short delay ---*/ case NAND_CMD_STATUS_ERROR: case NAND_CMD_STATUS_ERROR0: case NAND_CMD_STATUS_ERROR1: case NAND_CMD_STATUS_ERROR2: case NAND_CMD_STATUS_ERROR3: case NAND_CMD_READID: udelay(chip->chip_delay); return; case NAND_CMD_RESET: if (chip->dev_ready) break; udelay(chip->chip_delay); chip->cmd_ctrl(mtd, NAND_CMD_STATUS, NAND_NCE | NAND_CLE | NAND_CTRL_CHANGE); chip->cmd_ctrl(mtd, NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE); while (!(chip->read_byte(mtd) & NAND_STATUS_READY)) ; return; case NAND_CMD_RNDOUT: /*--- No ready / busy check necessary ---*/ chip->cmd_ctrl(mtd, NAND_CMD_RNDOUTSTART, NAND_NCE | NAND_CLE | NAND_CTRL_CHANGE); chip->cmd_ctrl(mtd, NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE); return; case NAND_CMD_READ0: /*--- This applies to read commands, newer high density flash device needs a 2nd read cmd for READ0. ---*/ chip->cmd_ctrl(mtd, NAND_CMD_READSTART, NAND_CLE | NAND_CTRL_CHANGE); chip->cmd_ctrl(mtd, NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE); default: /*--- If we don't have access to the busy pin, we apply the given command delay ---*/ #if !defined(RDBY_NOT_USED) if (!chip->dev_ready) { udelay(chip->chip_delay); return; } #endif } while( ! NAND_READY_BY_E); /*--- auf fallende Flanke warten ---*/ while( ! NAND_READY); switch (command) { case NAND_CMD_READ0: case NAND_CMD_READ1: { struct hsnand_info *hsnand = &hsnand_dev; if (hsnand->read_eccstatus) hsnand->ecc_error = hsnand->read_eccstatus(mtd, command); else hsnand->ecc_error = no_error; if (hsnand->ecc_error == uncorrectable) printk(KERN_ERR "[%s] read block failed (column: 0x%x page: 0x%x)\n", __func__, column, page_addr); if (hsnand->ecc_error == correctable) printk(KERN_ERR "[%s] read block is critical (column: 0x%x page: 0x%x)\n", __func__, column, page_addr); } break; default: break; } } /*------------------------------------------------------------------------------------------*\ * buffer alloc for DMA \*------------------------------------------------------------------------------------------*/ static u8* ifx_hsnand_buffer_alloc(int len, int *byte_offset, void **opt) { return NULL; } /*------------------------------------------------------------------------------------------*\ * free buffer \*------------------------------------------------------------------------------------------*/ static int ifx_hsnand_buffer_free(u8 *dataptr, void *opt) { return 0; } /*------------------------------------------------------------------------------------------*\ * initialize DMA \*------------------------------------------------------------------------------------------*/ static int ifx_hsnand_dma_setup(struct mtd_info *mtd) { unsigned int i; int dma_register; dma_device = dma_device_reserve("HSNAND"); if (dma_device == NULL) { printk(KERN_ERR "[%s] Reserve DMA for HSNAND failed!\n", __func__); return -EAGAIN; } dma_device->intr_handler = &ifx_hsnand_dma_intr_handler; dma_device->buffer_alloc = &ifx_hsnand_buffer_alloc; dma_device->buffer_free = &ifx_hsnand_buffer_free; dma_device->tx_endianness_mode = IFX_DMA_ENDIAN_TYPE3; dma_device->rx_endianness_mode = IFX_DMA_ENDIAN_TYPE3; dma_device->tx_burst_len = HSNAND_DMA_BURST_LEN; dma_device->rx_burst_len = HSNAND_DMA_BURST_LEN; dma_device->num_rx_chan = 1; dma_device->num_tx_chan = 1; /* DMA Channel Config for TX direction */ for (i = 0; i < dma_device->num_tx_chan; i++) { dma_device->tx_chan[i]->desc_len = 2; dma_device->tx_chan[i]->byte_offset = 0; //dma_device->tx_chan[i]->packet_size = HSNAND_PAGE_SIZE; dma_device->tx_chan[i]->control = IFX_DMA_CH_ON; } /* DMA Channel Config for RX direction */ for (i = 0; i < dma_device->num_rx_chan; i++) { dma_device->rx_chan[i]->desc_len = 1; dma_device->rx_chan[i]->byte_offset = 0; dma_device->rx_chan[i]->packet_size = mtd->writesize + mtd->oobsize; dma_device->rx_chan[i]->control = IFX_DMA_CH_ON; } dma_register = dma_device_register(dma_device); if (dma_register != IFX_SUCCESS) { printk(KERN_ERR "[%s] DMA register failed!\n", __func__); return -EAGAIN; } #if 0 for (i = 0; i < dma_device->num_rx_chan; i++) { dma_device->rx_chan[i]->reset(dma_device->rx_chan[i]); dma_device->rx_chan[i]->close(dma_device->rx_chan[i]); } #endif return 0; } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ void ifx_hsnand_chip_init(unsigned int chipselect) { u32 reg; if(ifx_gpio_register(IFX_GPIO_MODULE_NAND) != IFX_SUCCESS) { panic("[NAND]%s: ifx_gpio_register(IFX_GPIO_MODULE_NAND) failed\n", __func__); } EBU_PMU_SETUP(IFX_EBU_ENABLE); IFX_REG_W32(0x0, IFX_EBU_CLC); #if !defined(CONFIG_AR10) /*P1.7 FL_CS1 used as output*/ ifx_gpio_pin_reserve(IFX_NAND_CS1, IFX_GPIO_MODULE_NAND); ifx_gpio_dir_out_set(IFX_NAND_CS1, IFX_GPIO_MODULE_NAND); ifx_gpio_altsel0_set(IFX_NAND_CS1, IFX_GPIO_MODULE_NAND); ifx_gpio_altsel1_clear(IFX_NAND_CS1, IFX_GPIO_MODULE_NAND); ifx_gpio_open_drain_set(IFX_NAND_CS1, IFX_GPIO_MODULE_NAND); /*P1.8 FL_A23 NAND_CLE used as output*/ ifx_gpio_pin_reserve(IFX_NAND_CLE, IFX_GPIO_MODULE_NAND); ifx_gpio_dir_out_set(IFX_NAND_CLE, IFX_GPIO_MODULE_NAND); ifx_gpio_altsel0_set(IFX_NAND_CLE, IFX_GPIO_MODULE_NAND); ifx_gpio_altsel1_clear(IFX_NAND_CLE, IFX_GPIO_MODULE_NAND); ifx_gpio_open_drain_set(IFX_NAND_CLE, IFX_GPIO_MODULE_NAND); /*P0.13 FL_A24 used as output, set GPIO 13 to NAND_ALE*/ ifx_gpio_pin_reserve(IFX_NAND_ALE, IFX_GPIO_MODULE_NAND); ifx_gpio_dir_out_set(IFX_NAND_ALE, IFX_GPIO_MODULE_NAND); ifx_gpio_altsel0_set(IFX_NAND_ALE, IFX_GPIO_MODULE_NAND); ifx_gpio_altsel1_clear(IFX_NAND_ALE, IFX_GPIO_MODULE_NAND); ifx_gpio_open_drain_set(IFX_NAND_ALE, IFX_GPIO_MODULE_NAND); #if defined(CONFIG_VR9) || defined(CONFIG_AR9) /*P3.0 set as NAND Read Busy*/ ifx_gpio_pin_reserve(IFX_NAND_RDY, IFX_GPIO_MODULE_NAND); ifx_gpio_dir_in_set(IFX_NAND_RDY, IFX_GPIO_MODULE_NAND); ifx_gpio_altsel0_set(IFX_NAND_RDY, IFX_GPIO_MODULE_NAND); ifx_gpio_altsel1_clear(IFX_NAND_RDY, IFX_GPIO_MODULE_NAND); /*P3.1 set as NAND Read*/ ifx_gpio_pin_reserve(IFX_NAND_RD, IFX_GPIO_MODULE_NAND); ifx_gpio_dir_out_set(IFX_NAND_RD, IFX_GPIO_MODULE_NAND); ifx_gpio_altsel0_set(IFX_NAND_RD, IFX_GPIO_MODULE_NAND); ifx_gpio_altsel1_clear(IFX_NAND_RD, IFX_GPIO_MODULE_NAND); ifx_gpio_open_drain_set(IFX_NAND_RD, IFX_GPIO_MODULE_NAND); #endif #endif /*--- #if !defined(CONFIG_AR10) ---*/ if (chipselect) { reg = (NAND_BASE_ADDRESS & 0x1fffff00)| IFX_EBU_ADDSEL1_MASK(2)| IFX_EBU_ADDSEL1_REGEN; IFX_REG_W32(reg, IFX_EBU_ADDSEL1); reg = IFX_EBU_BUSCON1_SETUP | SM(IFX_EBU_BUSCON1_ALEC3,IFX_EBU_BUSCON1_ALEC) | SM(IFX_EBU_BUSCON1_BCGEN_RES,IFX_EBU_BUSCON1_BCGEN) | SM(IFX_EBU_BUSCON1_WAITWRC2,IFX_EBU_BUSCON1_WAITWRC) | SM(IFX_EBU_BUSCON1_WAITRDC2,IFX_EBU_BUSCON1_WAITRDC) | SM(IFX_EBU_BUSCON1_HOLDC1,IFX_EBU_BUSCON1_HOLDC) | SM(IFX_EBU_BUSCON1_RECOVC1,IFX_EBU_BUSCON1_RECOVC) | SM(IFX_EBU_BUSCON1_CMULT4,IFX_EBU_BUSCON1_CMULT); IFX_REG_W32(reg, IFX_EBU_BUSCON1); reg = SM(IFX_EBU_NAND_CON_NANDM_ENABLE, IFX_EBU_NAND_CON_NANDM) | SM(IFX_EBU_NAND_CON_CSMUX_E_ENABLE,IFX_EBU_NAND_CON_CSMUX_E) | SM(IFX_EBU_NAND_CON_CS_P_LOW,IFX_EBU_NAND_CON_CS_P) | SM(IFX_EBU_NAND_CON_SE_P_LOW,IFX_EBU_NAND_CON_SE_P) | SM(IFX_EBU_NAND_CON_WP_P_LOW,IFX_EBU_NAND_CON_WP_P) | SM(IFX_EBU_NAND_CON_PRE_P_LOW,IFX_EBU_NAND_CON_PRE_P) | SM(IFX_EBU_NAND_CON_IN_CS1,IFX_EBU_NAND_CON_IN_CS) | SM(IFX_EBU_NAND_CON_OUT_CS1,IFX_EBU_NAND_CON_OUT_CS) | SM(IFX_EBU_NAND_CON_ECC_ON,IFX_EBU_NAND_CON_ECC); IFX_REG_W32(reg,IFX_EBU_NAND_CON); #if defined(CONFIG_VR9) IFX_REG_W32(NAND_BASE_ADDRESS & 0x1fffffff, IFX_CS_BASE_A); /*--- set DMA-Baseaddress - include Chip-Select ---*/ IFX_REG_W32((NAND_BASE_ADDRESS & 0x1fffffff) + NAND_CMD_CS, IFX_BASE_A); #endif /*--- #if defined(CONFIG_VR9) ---*/ } else { reg = (NAND_BASE_ADDRESS & 0x1fffff00)| IFX_EBU_ADDSEL0_MASK(3)| IFX_EBU_ADDSEL0_REGEN; IFX_REG_W32(reg, IFX_EBU_ADDSEL0); reg = IFX_EBU_BUSCON1_SETUP | SM( IFX_EBU_BUSCON1_ALEC3 ,IFX_EBU_BUSCON1_ALEC) | SM( IFX_EBU_BUSCON1_BCGEN_RES ,IFX_EBU_BUSCON1_BCGEN) | SM( IFX_EBU_BUSCON1_WAITWRC2 ,IFX_EBU_BUSCON1_WAITWRC) | SM( IFX_EBU_BUSCON1_WAITRDC2 ,IFX_EBU_BUSCON1_WAITRDC) | SM( IFX_EBU_BUSCON1_HOLDC1 ,IFX_EBU_BUSCON1_HOLDC) | SM( IFX_EBU_BUSCON1_RECOVC1 ,IFX_EBU_BUSCON1_RECOVC) | SM( IFX_EBU_BUSCON1_CMULT4 ,IFX_EBU_BUSCON1_CMULT); IFX_REG_W32(reg, IFX_EBU_BUSCON0); reg = SM( IFX_EBU_NAND_CON_NANDM_ENABLE , IFX_EBU_NAND_CON_NANDM) | SM( IFX_EBU_NAND_CON_CSMUX_E_ENABLE ,IFX_EBU_NAND_CON_CSMUX_E) | SM( IFX_EBU_NAND_CON_CS_P_LOW ,IFX_EBU_NAND_CON_CS_P) | SM( IFX_EBU_NAND_CON_SE_P_LOW ,IFX_EBU_NAND_CON_SE_P) | SM( IFX_EBU_NAND_CON_WP_P_LOW ,IFX_EBU_NAND_CON_WP_P) | SM( IFX_EBU_NAND_CON_PRE_P_LOW ,IFX_EBU_NAND_CON_PRE_P) | SM( IFX_EBU_NAND_CON_IN_CS0 ,IFX_EBU_NAND_CON_IN_CS) | SM( IFX_EBU_NAND_CON_OUT_CS0 ,IFX_EBU_NAND_CON_OUT_CS); IFX_REG_W32(reg,IFX_EBU_NAND_CON); #if defined(CONFIG_VR9) IFX_REG_W32(NAND_BASE_ADDRESS & 0x1fffffff, IFX_CS_BASE_A); /*--- set DMA-Baseaddress - include Chip-Select ---*/ IFX_REG_W32((NAND_BASE_ADDRESS & 0x1fffffff) + NAND_CMD_CS, IFX_BASE_A); #endif /*--- #if defined(CONFIG_VR9) ---*/ } mb(); /* Set bus signals to inactive */ NAND_WRITE(NAND_WRITE_CMD, NAND_CMD_RESET); // Reset nand chip } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ int ifx_nand_avm_check_for_hweccnand(struct nand_chip *this, unsigned int ChipID) { #define PRINT_TO_SCREEN(...) printk(KERN_ERR "[NAND] "); printk(__VA_ARGS__); printk("\n") #define MAX_HWECC_TRIES 10 // NAND-Chip erkennen. // Wenn MT29F1 // HW-ECC des Chips nutzen, wenn das nicht geht -> software ECC mit fallback layout um die Daten noch lesen zu können // (dann natürlich nicht mehr ECC geschützt) // sonst // SW-ECC vom NAND-Treiber unsigned char result = 0; unsigned int manuf = (ChipID >> 8) & 0xFF; unsigned int dev_id = ChipID & 0xFF; /*--- MT29F1G08ABADA MT29F4G08ABADA ---*/ if( (manuf == 0x2c) && ((dev_id == 0xf1) || (dev_id == 0xdc)) ) { int no_tries = 0; try_again: ifx_hsnand_setup_chipecc(chip_enable_hwecc); // use getfeature to read result WRITE_NAND_COMMAND(0xEE); // getFeature WRITE_NAND_ADDRESS(0x90); // array operation mode while( ! NAND_READY); result = READ_NAND(); // read 4 bytes... #if defined(DEBUG_HSNAND) PRINT_TO_SCREEN("read feature bytes (after setting hw ecc): P1=0x%02x|P2=0x%02x|P3=0x%02x|P4=0x%02x", result, READ_NAND(), READ_NAND(), READ_NAND()); #endif result &= 0x08; if(!result) { no_tries++; if(no_tries < MAX_HWECC_TRIES) { PRINT_TO_SCREEN("Could not activate HW ECC -> try: %u/%u", no_tries, MAX_HWECC_TRIES); RESET_CHIP(); goto try_again; } } if(!result) { this->ecc.layout = &nand_oob_64_NANDwithHWEcc_FAILURE; PRINT_TO_SCREEN("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"); PRINT_TO_SCREEN("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"); PRINT_TO_SCREEN("!! MT29F1/MT29F4 erkannt, konnte aber den Hardware-ECC nicht aktivieren -> nutze SW-ECC !!"); PRINT_TO_SCREEN("!! -Fallback-Layout wird genutzt, welches ein Lesen der Daten erlaubt !!"); PRINT_TO_SCREEN("!! -YAFFS wird trotzdem massenhaft Warnungen auswerfen -> ECC stimmt halt nicht !!"); PRINT_TO_SCREEN("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"); PRINT_TO_SCREEN("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"); return 0; } } else { return 0; } return 1; } static uint8_t scan_ff_pattern[] = { 0xff, 0xff }; static struct nand_bbt_descr largepage_memorybased = { .options = 0, .offs = 0, .len = 1, .pattern = scan_ff_pattern }; /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ unsigned int getChipID(void) { unsigned char maf_id, dev_id, part_spec, extid; RESET_CHIP(); WRITE_NAND_COMMAND(0x90); // Read id WRITE_NAND_ADDRESS(0x00); maf_id = READ_NAND(); dev_id = READ_NAND(); part_spec = READ_NAND(); /*--- not needed ---*/ extid = READ_NAND(); part_spec = READ_NAND(); /*--- printk("{%s} chip_id 0x%x maf_id 0x%x\n", __func__, dev_id, maf_id); ---*/ return (dev_id | (maf_id << 8) | (extid << 16) | (part_spec << 24)); } #if defined (CONFIG_TFFS_DEV_MTDNAND) static int panic_wait(struct mtd_info *mtd, struct nand_chip *chip); static int panic_write(struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const uint8_t *buf); static int panic_write_oob(struct mtd_info *mtd, loff_t to, struct mtd_oob_ops *ops); static int panic_read(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, uint8_t *buf); static int panic_read_oob(struct mtd_info *mtd, loff_t from, struct mtd_oob_ops *ops); static struct mtd_info *panic_reinit(struct mtd_info *mtd) { struct mtd_info *tmp, *master; struct nand_chip *chip; if(mtd == NULL) return NULL; tmp = mtd; do { master = tmp; tmp = get_mtd_part_master(master); } while(tmp != NULL); if(master == NULL || master != ifx_hsnand_mtd){ return NULL; } chip = (struct nand_chip *) master->priv; chip->read_buf = ifx_nand_read_buf; chip->write_buf = ifx_nand_write_buf; chip->waitfunc = panic_wait; master->write = panic_write; master->write_oob = panic_write_oob; master->read = panic_read; master->read_oob = panic_read_oob; return mtd; } union tffs3_panic_funcs panic_funcs; #endif /*------------------------------------------------------------------------------------------*\ * fn int ifx_nand_init(void) * ingroup IFX_HSNAND_DRV * brief Main initialization routine * param none * return error message \*------------------------------------------------------------------------------------------*/ extern unsigned long long ifxmips_flashsize_nand; static int __init ifx_hsnand_init(void) { struct nand_chip *this; struct hsnand_info *hsnand = &hsnand_dev; unsigned int i, ChipID; int err = 0; if(ifxmips_flashsize_nand == 0ULL) { printk(KERN_ERR "[%s]: no NAND\n", __func__); return -ENXIO; } #if defined(CONFIG_VR9) { unsigned int bootselect = 0; int val = avm_get_hw_config(AVM_HW_CONFIG_VERSION, "gpio_avm_boot_sel4", &bootselect, NULL); if ( ! val) { ifx_hsnand_chip_init(1); } else ifx_hsnand_chip_init(0); } #else ifx_hsnand_chip_init(1); #endif ifx_hsnand_mtd = kmalloc(sizeof(struct mtd_info) + sizeof (struct nand_chip), GFP_KERNEL); if (!ifx_hsnand_mtd) { printk("Unable to allocate HSNAND MTD device structure\n"); err = -ENOMEM; return err; } this = (struct nand_chip *)(&ifx_hsnand_mtd[1]); memset(ifx_hsnand_mtd, 0, sizeof(struct mtd_info)); memset(this, 0, sizeof(struct nand_chip)); ifx_hsnand_mtd->name = kmalloc(16, GFP_KERNEL); if (ifx_hsnand_mtd->name == NULL) { printk("Unable to allocate HSNAND MTD device name\n"); err = -ENOMEM; goto out; } sprintf((char *)ifx_hsnand_mtd->name, IFX_MTD_HSNAND_BANK_NAME); /* Associate MTD priv members with the current MTD info*/ ifx_hsnand_mtd->priv = this; ifx_hsnand_mtd->owner = THIS_MODULE; this->IO_ADDR_R = (void *) NAND_BASE_ADDRESS; this->IO_ADDR_W = (void *) NAND_BASE_ADDRESS; this->chip_delay = 30; /* 30 us command delay, similar to NAND driver specs */ /*--- sind für alle gleich ---*/ this->cmd_ctrl = ifx_nand_cmd_ctrl; this->dev_ready = ifx_nand_ready; this->select_chip = ifx_nand_select_chip; this->read_byte = ifx_nand_read_byte; this->verify_buf = ifx_nand_verify_buf; this->options = NAND_SKIP_BBTSCAN; ChipID = getChipID(); /*--- printk("{%s} ChipID 0x%x\n", __func__, ChipID); ---*/ for (i = 0; ifx_chip_info[i].chip_id; i++) { /*--- printk("{%s} ifx_chip_info 0x%x\n", __func__, ifx_chip_info[i].chip_id); ---*/ if ((ChipID & 0xFFFF) == ifx_chip_info[i].chip_id) { break; } } this->ecc.size = ifx_chip_info[i].eccsize; this->ecc.bytes = ifx_chip_info[i].eccbytes; this->ecc.layout = ifx_chip_info[i].chip_ecclayout; this->ecc.mode = ifx_chip_info[i].ecc_mode; this->cmdfunc = ifx_hsnand_command; #if defined(CONFIG_VR9) this->read_buf = ifx_hsnand_read_buf; this->write_buf = ifx_hsnand_write_buf; #else this->read_buf = ifx_hsnand_ar10_read_buf; this->write_buf = ifx_hsnand_ar10_write_buf; #endif #if defined(AVM_NAND_STATISTIC) init_nandstat(&hsnand->nandstat); #endif/*--- #if defined(AVM_NAND_STATISTIC) ---*/ if (ifx_nand_avm_check_for_hweccnand(this, ChipID)) { printk("[HSNAND] Hardware-ECC activated\n"); /*------------------------------------------------------------------------------------------*\ * wir nutzen die HWECC des NAND-Chip \*------------------------------------------------------------------------------------------*/ hsnand->read_eccstatus = ifx_hsnand_read_eccstatus; this->write_page = ifx_hsnand_write_page; this->ecc.write_page = ifx_hsnand_write_page_raw; this->ecc.write_page_raw = ifx_hsnand_write_page_raw; this->ecc.read_page = ifx_hsnand_micron_read_page_hwecc; this->ecc.read_page_raw = ifx_hsnand_micron_read_page_hwecc; #if defined(CONFIG_AR10) this->ecc.read_oob = ifx_hsnand_ar10_read_oob; this->ecc.write_oob = ifx_hsnand_ar10_write_oob; #endif } else if (((ChipID & 0xFFFF) == 0x98f1) || ((ChipID & 0xFFFF) == 0x98dc)) { printk("[HSNAND] Toshiba-BENAND\n"); /*------------------------------------------------------------------------------------------*\ * Toshiba- BENAND wir nutzen die HWECC des NAND-Chip * schreiben aber mit ECC1 per Hardware in den Flash und nutzen die Standartfunktion * nand_write_page_hwecc wir müssen also * chip->ecc.hwctl * chip->ecc.calculate setzen \*------------------------------------------------------------------------------------------*/ hsnand->read_eccstatus = ifx_hsnand_toshiba_read_eccstatus; this->write_page = ifx_hsnand_write_page; /*--- if ((ChipID & 0xFFFF) == 0x98dc) { ---*/ if (((*IFX_RCU_RST_STAT >> 17) & 0xF) == 0xC) { /*--- boot from NAND ---*/ this->ecc.hwctl = ifx_hsnand_hwctl; this->ecc.calculate = nand_calculate_ecc; this->ecc.correct = nand_correct_data; } else { this->ecc.write_page = ifx_hsnand_write_page_raw; this->ecc.write_page_raw = ifx_hsnand_write_page_raw; } this->ecc.read_page = ifx_hsnand_toshiba_read_page_hwecc; this->ecc.read_page_raw = ifx_hsnand_toshiba_read_page_hwecc; #if defined(CONFIG_AR10) this->ecc.read_oob = ifx_hsnand_ar10_read_oob; this->ecc.write_oob = ifx_hsnand_ar10_write_oob; #endif } else { /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ sprintf((char *)ifx_hsnand_mtd->name, IFX_MTD_NAND_BANK_NAME); this->ecc.mode = NAND_ECC_SOFT; this->read_buf = ifx_nand_read_buf; this->write_buf = ifx_nand_write_buf; } /*--- wir nutzen auf jeden Fall den DMA ---*/ err = ifx_hsnand_dma_setup(ifx_hsnand_mtd); if (err < 0) { printk(KERN_ERR "[%s] HSNAND DMA setup failed\n", __func__); goto out; } init_waitqueue_head(&hsnand->hsnand_wait); if (nand_scan(ifx_hsnand_mtd, 1)) { printk(KERN_ERR "[%s] Probing for NAND flash failed, flash not found!\n", __func__); err = -ENXIO; goto out; } hsnand->hwecc_buffer = kmalloc(ifx_hsnand_mtd->writesize + ifx_hsnand_mtd->oobsize, GFP_KERNEL | GFP_DMA); if ( ! hsnand->hwecc_buffer) { err = -ENOMEM; goto out; } hsnand->tmp_buffer = kmalloc(ifx_hsnand_mtd->writesize + ifx_hsnand_mtd->oobsize, GFP_KERNEL | GFP_DMA); if ( ! hsnand->tmp_buffer) { err = -ENOMEM; goto out; } this->badblock_pattern = &largepage_memorybased; /*--- vor scan_bbt muss hwecc_buffer gesetzt sein ---*/ if (this->scan_bbt(ifx_hsnand_mtd)) { printk(KERN_ERR "[%s] scan NAND BBT failed!\n", __func__); err = -ENXIO; goto out; } #ifdef CONFIG_MTD_PARTITIONS #ifdef CONFIG_MTD_CMDLINE_PARTS struct mtd_partition *mtd_parts = NULL; err = parse_mtd_partitions(ifx_hsnand_mtd, part_probes, &mtd_parts, 0); if (err <= 0) { goto out; } add_mtd_partitions(ifx_hsnand_mtd, mtd_parts, n); #else err = add_mtd_partitions(ifx_hsnand_mtd, ifx_nand_partitions, IFX_MTD_NAND_PARTS); #endif // CONFIG_MTD_CMDLINE_PARTS #else err = add_mtd_device(ifx_hsnand_mtd); #endif /* CONFIG_MTD_PARTITIONS */ #if defined(CONFIG_TFFS_DEV_MTDNAND) panic_funcs.nand.panic_setup = panic_reinit; TFFS3_Register_Panic(tffs3_type_mtdnand, &panic_funcs); #endif return 0; out: if (dma_device) { dma_device_release(dma_device); dma_device_unregister(dma_device); dma_device = NULL; } if (ifx_hsnand_mtd->name) kfree(ifx_hsnand_mtd->name); if (ifx_hsnand_mtd) kfree(ifx_hsnand_mtd); if (hsnand->hwecc_buffer) kfree(hsnand->hwecc_buffer); if (hsnand->tmp_buffer) kfree(hsnand->tmp_buffer); return err; } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ static void __exit ifx_hsnand_exit(void) { struct hsnand_info *hsnand = &hsnand_dev; dma_device_release(dma_device); dma_device_unregister(dma_device); dma_device = NULL; /* Release resources, unregister device */ nand_release(ifx_hsnand_mtd); ifx_gpio_deregister(IFX_GPIO_MODULE_NAND); /* Free the MTD device structure */ if (ifx_hsnand_mtd->name) kfree(ifx_hsnand_mtd->name); if (ifx_hsnand_mtd) kfree(ifx_hsnand_mtd); if (hsnand->hwecc_buffer) kfree(hsnand->hwecc_buffer); if (hsnand->tmp_buffer) kfree(hsnand->tmp_buffer); } module_init(ifx_hsnand_init); module_exit(ifx_hsnand_exit); #if defined(CONFIG_TFFS_DEV_MTDNAND) /** * nand_wait - [DEFAULT] wait until the command is done * @mtd: MTD device structure * @chip: NAND chip structure * * Wait for command done. This applies to erase and program only * Erase can take up to 400ms and program up to 20ms according to * general NAND and SmartMedia specs */ static int panic_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; // led_trigger_event(nand_led_trigger, LED_FULL); /* Apply this short delay always to ensure that we do wait tWB in * any case on any machine. */ ndelay(100); if ((state == FL_ERASING) && (chip->options & NAND_IS_AND)) chip->cmdfunc(mtd, NAND_CMD_STATUS_MULTI, -1, -1); else chip->cmdfunc(mtd, NAND_CMD_STATUS, -1, -1); while (time_before(jiffies, timeo)) { if (chip->dev_ready) { if (chip->dev_ready(mtd)) break; } else { if (chip->read_byte(mtd) & NAND_STATUS_READY) break; } udelay(chip->chip_delay); // cond_resched(); } // led_trigger_event(nand_led_trigger, LED_OFF); status = (int)chip->read_byte(mtd); return status; } /** * nand_release_device - [GENERIC] release chip * @mtd: MTD device structure * * Deselect, release chip lock and wake up anyone waiting on the device */ static void panic_release_device(struct mtd_info *mtd) { struct nand_chip *chip = mtd->priv; #if 0 /* De-select the NAND device */ chip->select_chip(mtd, -1); /* Release the controller and the chip */ spin_lock(&chip->controller->lock); chip->controller->active = NULL; chip->state = FL_READY; wake_up(&chip->controller->wq); spin_unlock(&chip->controller->lock); #endif chip->select_chip(mtd, -1); chip->controller->active = NULL; chip->state = FL_READY; } /** * nand_get_device - [GENERIC] Get chip for selected access * @chip: the nand chip descriptor * @mtd: MTD device structure * @new_state: the state which is requested * * Get the device and lock it for exclusive access */ static int panic_get_device(struct nand_chip *chip, struct mtd_info *mtd, int new_state) { #if 0 spinlock_t *lock = &chip->controller->lock; wait_queue_head_t *wq = &chip->controller->wq; DECLARE_WAITQUEUE(wait, current); retry: spin_lock(lock); /* Hardware controller shared among independent devices */ if (!chip->controller->active) chip->controller->active = chip; if (chip->controller->active == chip && chip->state == FL_READY) { chip->state = new_state; spin_unlock(lock); return 0; } if (new_state == FL_PM_SUSPENDED) { spin_unlock(lock); return (chip->state == FL_PM_SUSPENDED) ? 0 : -EAGAIN; } set_current_state(TASK_UNINTERRUPTIBLE); add_wait_queue(wq, &wait); spin_unlock(lock); schedule(); remove_wait_queue(wq, &wait); goto retry; #endif chip->controller->active = chip; chip->state = new_state; return 0; } static uint8_t *panic_transfer_oob(struct nand_chip *chip, uint8_t *oob, struct mtd_oob_ops *ops, size_t len) { switch(ops->mode) { case MTD_OOB_PLACE: case MTD_OOB_RAW: memcpy(oob, chip->oob_poi + ops->ooboffs, len); return oob + len; case MTD_OOB_AUTO: { struct nand_oobfree *free = chip->ecc.layout->oobfree; uint32_t boffs = 0, roffs = ops->ooboffs; size_t bytes = 0; for(; free->length && len; free++, len -= bytes) { /* Read request not from offset 0 ? */ if (unlikely(roffs)) { if (roffs >= free->length) { roffs -= free->length; continue; } boffs = free->offset + roffs; bytes = min_t(size_t, len, (free->length - roffs)); roffs = 0; } else { bytes = min_t(size_t, len, free->length); boffs = free->offset; } memcpy(oob, chip->oob_poi + boffs, bytes); oob += bytes; } return oob; } default: BUG(); } return NULL; } /** * nand_do_read_ops - [Internal] Read data with ECC * * @mtd: MTD device structure * @from: offset to read from * @ops: oob ops structure * * Internal function. Called with chip held. */ static int panic_do_read_ops(struct mtd_info *mtd, loff_t from, struct mtd_oob_ops *ops) { int chipnr, page, realpage, col, bytes, aligned; struct nand_chip *chip = mtd->priv; struct mtd_ecc_stats stats; int blkcheck = (1 << (chip->phys_erase_shift - chip->page_shift)) - 1; int sndcmd = 1; int ret = 0; uint32_t readlen = ops->len; uint32_t oobreadlen = ops->ooblen; uint8_t *bufpoi, *oob, *buf; stats = mtd->ecc_stats; chipnr = (int)(from >> chip->chip_shift); chip->select_chip(mtd, chipnr); realpage = (int)(from >> chip->page_shift); page = realpage & chip->pagemask; col = (int)(from & (mtd->writesize - 1)); buf = ops->datbuf; oob = ops->oobbuf; while(1) { bytes = min(mtd->writesize - col, readlen); aligned = (bytes == mtd->writesize); /* Is the current page in the buffer ? */ if (realpage != chip->pagebuf || oob) { bufpoi = aligned ? buf : chip->buffers->databuf; if (likely(sndcmd)) { chip->cmdfunc(mtd, NAND_CMD_READ0, 0x00, page); sndcmd = 0; } /* Now read the page into the buffer */ if (unlikely(ops->mode == MTD_OOB_RAW)) ret = chip->ecc.read_page_raw(mtd, chip, bufpoi, page); else if (!aligned && NAND_SUBPAGE_READ(chip) && !oob) ret = chip->ecc.read_subpage(mtd, chip, col, bytes, bufpoi); else ret = chip->ecc.read_page(mtd, chip, bufpoi, page); if (ret < 0) break; /* Transfer not aligned data */ if (!aligned) { if (!NAND_SUBPAGE_READ(chip) && !oob) chip->pagebuf = realpage; memcpy(buf, chip->buffers->databuf + col, bytes); } buf += bytes; if (unlikely(oob)) { /* Raw mode does data:oob:data:oob */ if (ops->mode != MTD_OOB_RAW) { int toread = min(oobreadlen, chip->ecc.layout->oobavail); if (toread) { oob = panic_transfer_oob(chip, oob, ops, toread); oobreadlen -= toread; } } else buf = panic_transfer_oob(chip, buf, ops, mtd->oobsize); } if (!(chip->options & NAND_NO_READRDY)) { /* * Apply delay or wait for ready/busy pin. Do * this before the AUTOINCR check, so no * problems arise if a chip which does auto * increment is marked as NOAUTOINCR by the * board driver. */ // if (!chip->dev_ready) udelay(chip->chip_delay); // else // nand_wait_ready(mtd); } } else { memcpy(buf, chip->buffers->databuf + col, bytes); buf += bytes; } readlen -= bytes; if (!readlen) break; /* For subsequent reads align to page boundary. */ col = 0; /* Increment page address */ realpage++; page = realpage & chip->pagemask; /* Check, if we cross a chip boundary */ if (!page) { chipnr++; chip->select_chip(mtd, -1); chip->select_chip(mtd, chipnr); } /* Check, if the chip supports auto page increment * or if we have hit a block boundary. */ if (!NAND_CANAUTOINCR(chip) || !(page & blkcheck)) sndcmd = 1; } ops->retlen = ops->len - (size_t) readlen; if (oob) ops->oobretlen = ops->ooblen - oobreadlen; if (ret) return ret; if (mtd->ecc_stats.failed - stats.failed) return -EBADMSG; return mtd->ecc_stats.corrected - stats.corrected ? -EUCLEAN : 0; } /** * nand_do_read_oob - [Intern] NAND read out-of-band * @mtd: MTD device structure * @from: offset to read from * @ops: oob operations description structure * * NAND read out-of-band data from the spare area */ static int panic_do_read_oob(struct mtd_info *mtd, loff_t from, struct mtd_oob_ops *ops) { int page, realpage, chipnr, sndcmd = 1; struct nand_chip *chip = mtd->priv; int blkcheck = (1 << (chip->phys_erase_shift - chip->page_shift)) - 1; int readlen = ops->ooblen; int len; uint8_t *buf = ops->oobbuf; if (ops->mode == MTD_OOB_AUTO) len = chip->ecc.layout->oobavail; else len = mtd->oobsize; if (unlikely(ops->ooboffs >= len)) { return -EINVAL; } /* Do not allow reads past end of device */ if (unlikely(from >= mtd->size || ops->ooboffs + readlen > ((mtd->size >> chip->page_shift) - (from >> chip->page_shift)) * len)) { return -EINVAL; } chipnr = (int)(from >> chip->chip_shift); chip->select_chip(mtd, chipnr); /* Shift to get page */ realpage = (int)(from >> chip->page_shift); page = realpage & chip->pagemask; while(1) { sndcmd = chip->ecc.read_oob(mtd, chip, page, sndcmd); len = min(len, readlen); buf = panic_transfer_oob(chip, buf, ops, len); if (!(chip->options & NAND_NO_READRDY)) { /* * Apply delay or wait for ready/busy pin. Do this * before the AUTOINCR check, so no problems arise if a * chip which does auto increment is marked as * NOAUTOINCR by the board driver. */ // if (!chip->dev_ready) udelay(chip->chip_delay); // else // nand_wait_ready(mtd); } readlen -= len; if (!readlen) break; /* Increment page address */ realpage++; page = realpage & chip->pagemask; /* Check, if we cross a chip boundary */ if (!page) { chipnr++; chip->select_chip(mtd, -1); chip->select_chip(mtd, chipnr); } /* Check, if the chip supports auto page increment * or if we have hit a block boundary. */ if (!NAND_CANAUTOINCR(chip) || !(page & blkcheck)) sndcmd = 1; } ops->oobretlen = ops->ooblen; return 0; } /** * nand_read_oob - [MTD Interface] NAND read data and/or out-of-band * @mtd: MTD device structure * @from: offset to read from * @ops: oob operation description structure * * NAND read data and/or out-of-band data */ static int panic_read_oob(struct mtd_info *mtd, loff_t from, struct mtd_oob_ops *ops) { struct nand_chip *chip = mtd->priv; int ret = -ENOTSUPP; pr_err("[%s] Called\n", __func__); ops->retlen = 0; /* Do not allow reads past end of device */ if (ops->datbuf && (from + ops->len) > mtd->size) { return -EINVAL; } panic_get_device(chip, mtd, FL_READING); switch(ops->mode) { case MTD_OOB_PLACE: case MTD_OOB_AUTO: case MTD_OOB_RAW: break; default: goto out; } if (!ops->datbuf) ret = panic_do_read_oob(mtd, from, ops); else ret = panic_do_read_ops(mtd, from, ops); out: panic_release_device(mtd); return ret; } /** * nand_read - [MTD Interface] MTD compability function for nand_do_read_ecc * @mtd: MTD device structure * @from: offset to read from * @len: number of bytes to read * @retlen: pointer to variable to store the number of read bytes * @buf: the databuffer to put data * * Get hold of the chip and call nand_do_read */ static int panic_read(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, uint8_t *buf) { struct nand_chip *chip = mtd->priv; int ret; pr_err("[%s] Called\n", __func__); /* Do not allow reads past end of device */ if ((from + len) > mtd->size) return -EINVAL; if (!len) return 0; panic_get_device(chip, mtd, FL_READING); chip->ops.len = len; chip->ops.datbuf = buf; chip->ops.oobbuf = NULL; ret = panic_do_read_ops(mtd, from, &chip->ops); *retlen = chip->ops.retlen; panic_release_device(mtd); return ret; } /** * nand_check_wp - [GENERIC] check if the chip is write protected * @mtd: MTD device structure * Check, if the device is write protected * * The function expects, that the device is already selected */ static int panic_check_wp(struct mtd_info *mtd) { struct nand_chip *chip = mtd->priv; /* Check the WP bit */ chip->cmdfunc(mtd, NAND_CMD_STATUS, -1, -1); return (chip->read_byte(mtd) & NAND_STATUS_WP) ? 0 : 1; } /** * nand_fill_oob - [Internal] Transfer client buffer to oob * @chip: nand chip structure * @oob: oob data buffer * @ops: oob ops structure */ static uint8_t *panic_fill_oob(struct nand_chip *chip, uint8_t *oob, struct mtd_oob_ops *ops) { size_t len = ops->ooblen; switch(ops->mode) { case MTD_OOB_PLACE: case MTD_OOB_RAW: memcpy(chip->oob_poi + ops->ooboffs, oob, len); return oob + len; case MTD_OOB_AUTO: { struct nand_oobfree *free = chip->ecc.layout->oobfree; uint32_t boffs = 0, woffs = ops->ooboffs; size_t bytes = 0; for(; free->length && len; free++, len -= bytes) { /* Write request not from offset 0 ? */ if (unlikely(woffs)) { if (woffs >= free->length) { woffs -= free->length; continue; } boffs = free->offset + woffs; bytes = min_t(size_t, len, (free->length - woffs)); woffs = 0; } else { bytes = min_t(size_t, len, free->length); boffs = free->offset; } memcpy(chip->oob_poi + boffs, oob, bytes); oob += bytes; } return oob; } default: BUG(); } return NULL; } #define NOTALIGNED(x) (x & (chip->subpagesize - 1)) != 0 /** * nand_do_write_ops - [Internal] NAND write with ECC * @mtd: MTD device structure * @to: offset to write to * @ops: oob operations description structure * * NAND write with ECC */ static int panic_do_write_ops(struct mtd_info *mtd, loff_t to, struct mtd_oob_ops *ops) { int chipnr, realpage, page, blockmask, column; struct nand_chip *chip = mtd->priv; uint32_t writelen = ops->len; uint8_t *oob = ops->oobbuf; uint8_t *buf = ops->datbuf; int ret, subpage; ops->retlen = 0; if (!writelen) return 0; /* reject writes, which are not page aligned */ if (NOTALIGNED(to) || NOTALIGNED(ops->len)) { printk(KERN_NOTICE "%s: Attempt to write not " "page aligned data\n", __func__); return -EINVAL; } column = to & (mtd->writesize - 1); subpage = column || (writelen & (mtd->writesize - 1)); if (subpage && oob) return -EINVAL; chipnr = (int)(to >> chip->chip_shift); chip->select_chip(mtd, chipnr); /* Check, if it is write protected */ if (panic_check_wp(mtd)) return -EIO; realpage = (int)(to >> chip->page_shift); page = realpage & chip->pagemask; blockmask = (1 << (chip->phys_erase_shift - chip->page_shift)) - 1; /* Invalidate the page cache, when we write to the cached page */ if (to <= (chip->pagebuf << chip->page_shift) && (chip->pagebuf << chip->page_shift) < (to + ops->len)) chip->pagebuf = -1; /* If we're not given explicit OOB data, let it be 0xFF */ if (likely(!oob)) memset(chip->oob_poi, 0xff, mtd->oobsize); while(1) { int bytes = mtd->writesize; int cached = writelen > bytes && page != blockmask; uint8_t *wbuf = buf; /* Partial page write ? */ if (unlikely(column || writelen < (mtd->writesize - 1))) { cached = 0; bytes = min_t(int, bytes - column, (int) writelen); chip->pagebuf = -1; memset(chip->buffers->databuf, 0xff, mtd->writesize); memcpy(&chip->buffers->databuf[column], buf, bytes); wbuf = chip->buffers->databuf; } if (unlikely(oob)) oob = panic_fill_oob(chip, oob, ops); ret = chip->write_page(mtd, chip, wbuf, page, cached, (ops->mode == MTD_OOB_RAW)); if (ret) break; writelen -= bytes; if (!writelen) break; column = 0; buf += bytes; realpage++; page = realpage & chip->pagemask; /* Check, if we cross a chip boundary */ if (!page) { chipnr++; chip->select_chip(mtd, -1); chip->select_chip(mtd, chipnr); } } ops->retlen = ops->len - writelen; if (unlikely(oob)) ops->oobretlen = ops->ooblen; return ret; } /** * nand_write - [MTD Interface] NAND write with ECC * @mtd: MTD device structure * @to: offset to write to * @len: number of bytes to write * @retlen: pointer to variable to store the number of written bytes * @buf: the data to write * * NAND write with ECC */ static int panic_write(struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const uint8_t *buf) { struct nand_chip *chip = mtd->priv; int ret; pr_err("[%s] Called\n", __func__); /* Do not allow reads past end of device */ if ((to + len) > mtd->size) return -EINVAL; if (!len) return 0; panic_get_device(chip, mtd, FL_WRITING); chip->ops.len = len; chip->ops.datbuf = (uint8_t *)buf; chip->ops.oobbuf = NULL; ret = panic_do_write_ops(mtd, to, &chip->ops); *retlen = chip->ops.retlen; panic_release_device(mtd); return ret; } /** * nand_do_write_oob - [MTD Interface] NAND write out-of-band * @mtd: MTD device structure * @to: offset to write to * @ops: oob operation description structure * * NAND write out-of-band */ static int panic_do_write_oob(struct mtd_info *mtd, loff_t to, struct mtd_oob_ops *ops) { int chipnr, page, status, len; struct nand_chip *chip = mtd->priv; if (ops->mode == MTD_OOB_AUTO) len = chip->ecc.layout->oobavail; else len = mtd->oobsize; /* Do not allow write past end of page */ if ((ops->ooboffs + ops->ooblen) > len) { return -EINVAL; } if (unlikely(ops->ooboffs >= len)) { return -EINVAL; } /* Do not allow reads past end of device */ if (unlikely(to >= mtd->size || ops->ooboffs + ops->ooblen > ((mtd->size >> chip->page_shift) - (to >> chip->page_shift)) * len)) { return -EINVAL; } chipnr = (int)(to >> chip->chip_shift); chip->select_chip(mtd, chipnr); /* Shift to get page */ page = (int)(to >> chip->page_shift); /* * Reset the chip. Some chips (like the Toshiba TC5832DC found in one * of my DiskOnChip 2000 test units) will clear the whole data page too * if we don't do this. I have no clue why, but I seem to have 'fixed' * it in the doc2000 driver in August 1999. dwmw2. */ chip->cmdfunc(mtd, NAND_CMD_RESET, -1, -1); /* Check, if it is write protected */ if (panic_check_wp(mtd)) return -EROFS; /* Invalidate the page cache, if we write to the cached page */ if (page == chip->pagebuf) chip->pagebuf = -1; memset(chip->oob_poi, 0xff, mtd->oobsize); panic_fill_oob(chip, ops->oobbuf, ops); status = chip->ecc.write_oob(mtd, chip, page & chip->pagemask); memset(chip->oob_poi, 0xff, mtd->oobsize); if (status) return status; ops->oobretlen = ops->ooblen; return 0; } /** * nand_write_oob - [MTD Interface] NAND write data and/or out-of-band * @mtd: MTD device structure * @to: offset to write to * @ops: oob operation description structure */ static int panic_write_oob(struct mtd_info *mtd, loff_t to, struct mtd_oob_ops *ops) { struct nand_chip *chip = mtd->priv; int ret = -ENOTSUPP; pr_err("[%s] Called\n", __func__); ops->retlen = 0; /* Do not allow writes past end of device */ if (ops->datbuf && (to + ops->len) > mtd->size) { return -EINVAL; } panic_get_device(chip, mtd, FL_WRITING); switch(ops->mode) { case MTD_OOB_PLACE: case MTD_OOB_AUTO: case MTD_OOB_RAW: break; default: goto out; } if (!ops->datbuf) ret = panic_do_write_oob(mtd, to, ops); else ret = panic_do_write_ops(mtd, to, ops); out: panic_release_device(mtd); return ret; } #if defined(AVM_NAND_STATISTIC) #define PROC_NAND_PATH "avm/nandstat" /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ static char *human_time(char *buf, int len, unsigned long secs) { unsigned long seconds, minutes, hours; seconds = secs % 60; secs /= 60; minutes = secs % 60; secs /= 60; hours = secs % 24; if(hours) { snprintf(buf, len, "%lu h %lu min %lu s", hours, minutes, seconds); } else if(minutes) { snprintf(buf, len, "%lu min %lu s", minutes, seconds); } else { snprintf(buf, len, "%lu s", seconds); } return buf; } /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ static void lproc_nandstat(struct seq_file *seq, void *priv){ struct _nand_avm_statistic *pnand_stat = (struct _nand_avm_statistic *)priv; char txt[64]; seq_printf(seq, "Measuring time: %s\n", human_time(txt, sizeof(txt), (jiffies - pnand_stat->start_jiffies) / HZ)); seq_printf(seq, "write-pages (raw) %12lu\n" "write-pages (ecc) %12lu - from that corrected %12lu uncorrectable %12lu\n" "read-pages (ecc) %12lu - from that corrected %12lu uncorrectable %12lu\n", pnand_stat->writepages_raw, pnand_stat->writepages_ecc, pnand_stat->writepages_ecc_corrected, pnand_stat->writepages_ecc_uncorrectable, pnand_stat->readpages_ecc, pnand_stat->readpages_ecc_corrected, pnand_stat->readpages_ecc_uncorrectable ); } /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ static void init_nandstat(struct _nand_avm_statistic *pnand_stat){ pnand_stat->start_jiffies = jiffies; add_simple_proc_file( PROC_NAND_PATH, NULL, lproc_nandstat, pnand_stat); } #endif/*--- #if defined(AVM_NAND_STATISTIC) ---*/ #endif // defined(CONFIG_TFFS_DEV_MTDNAND)