/****************************************************************************** ** ** FILE NAME : ifxmips_nand.c ** PROJECT : UEIP ** MODULES : NAND Flash ** ** DATE : 23 Apr 2005 ** AUTHOR : Wu Qi Ming ** DESCRIPTION : NAND Flash MTD Driver ** COPYRIGHT : Copyright (c) 2006 ** Infineon Technologies AG ** 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 ** 23 Apr 2008 Wu Qi Ming 1.0 initial version ** 7 Aug 2009 Yin Elaine 1.1 Modification for UEIP project *******************************************************************************/ #ifndef AUTOCONF_INCLUDED #include #endif /* AUTOCONF_INCLUDED */ /*! \defgroup IFX_NAND_DRV UEIP Project - nand flash driver \brief UEIP Project - Nand flash driver, supports LANTIQ CPE platforms(Danube/ASE/ARx/VRx). */ /*! \defgroup IFX_NAND_DRV_API External APIs \ingroup IFX_NAND_DRV \brief External APIs definitions for other modules. */ /*! \defgroup IFX_NAND_DRV_STRUCTURE Driver Structures \ingroup IFX_NAND_DRV \brief Definitions/Structures of nand module. */ /*! \file ifxmips_mtd_nand.h \ingroup IFX_NAND_DRV \brief Header file for LANTIQ nand driver */ /*! \file ifxmips_mtd_nand.c \ingroup IFX_NAND_DRV \brief nand driver main source file. */ #include #include #include #include #include #include #include #include #include #include /* Project header */ #include #include #include #include #include #include "ifxmips_mtd_nand.h" /*--- #define DBG_NAND(args...) printk(args) ---*/ #define DBG_NAND(args...) #define IFX_MTD_NAND_BANK_NAME "ifx_nand" /* cmd line bank name should be the same */ /* * MTD structure for NAND controller */ static struct mtd_info *ifx_nand_mtd = NULL; static u32 latchcmd=0; #ifdef CONFIG_MTD_PARTITIONS #ifdef CONFIG_MTD_CMDLINE_PARTS static const char *part_probes[] = { "cmdlinepart", NULL }; #endif //ifdef CONFIG_MTD_CMDLINE_PARTS #endif //ifdef CONFIG_MTD_PARTITIONS /* Partition table, defined in platform_board.c */ extern struct mtd_partition ifx_nand_partitions[IFX_MTD_NAND_PARTS]; 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); } #if !defined(RDBY_NOT_USED) static int ifx_nand_ready(struct mtd_info *mtd __attribute__((unused))) { return NAND_READY; } #endif /*! \fn u_char ifx_nand_read_byte(struct mtd_info *mtd) \ingroup IFX_NAND_DRV \brief read one byte from the chip, read function for 8bit buswith \param mtd MTD device structure \return value of the byte */ static u_char ifx_nand_read_byte(struct mtd_info *mtd) { u_char ret; NAND_READ(NAND_READ_DATA, ret); return ret; } /*! \fn void ifx_nand_select_chip(struct mtd_info *mtd, int chip) \ingroup IFX_NAND_DRV \brief control CE line \param mtd MTD device structure \param chipnumber to select, -1 for deselect \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); 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); /*--- Schrott (macht bei jedem CS einen Reset was bei manchen NANDs halt laenger dauert.) =>: NAND_WRITE(NAND_WRITE_CMD, NAND_WRITE_CMD_RESET); // Reset nand chip ---*/ break; default: BUG(); } } /*! \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 int ifx_nand_verify_buf(struct mtd_info *mtd, const uint8_t *buf, int len) \ingroup IFX_NAND_DRV \brief Verify chip data against buffer \param mtd MTD device structure \param buf buffer containing the data to compare \param len number of bytes to compare \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()) return -EFAULT; return 0; } /*! \fn void ifx_nand_cmd_ctrl(struct mtd_info *mtd, int data, unsigned int ctrl) \ingroup IFX_NAND_DRV \brief Hardware specific access to control-lines \param mtd MTD device structure \param data data to write to nand if necessary \param ctrl control value, refer to nand_base.c \return none */ 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; } /*------------------------------------------------------------------------------------------*\ * ifx_nand_command_hwecc - Send command to NAND large page device & test Readstatus * @mtd: MTD device structure * @command: the command to be sent * @column: the column address for this command, -1 if none * @page_addr: the page address for this command, -1 if none * * 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. \*------------------------------------------------------------------------------------------*/ static void ifx_nand_command_hwecc(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; } /*--- printk(KERN_ERR "NAND_WAIT 0x%x cmd 0x%x\n", IFX_REG_R32(IFX_EBU_NAND_WAIT), command); ---*/ NAND_READY_CLEAR; /*--- printk(KERN_ERR "NAND_WAIT 0x%x\n", IFX_REG_R32(IFX_EBU_NAND_WAIT)); ---*/ /* 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_NCE | NAND_ALE; /* 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, ctrl); ctrl &= ~NAND_CTRL_CHANGE; chip->cmd_ctrl(mtd, column >> 8, ctrl); } if (page_addr != -1) { chip->cmd_ctrl(mtd, page_addr, ctrl); chip->cmd_ctrl(mtd, page_addr >> 8, NAND_NCE | NAND_ALE); /* One more address cycle for devices > 128MiB */ if (chip->chipsize > (128 << 20)) chip->cmd_ctrl(mtd, page_addr >> 16, 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: chip->cmd_ctrl(mtd, NAND_CMD_READSTART, NAND_NCE | NAND_CLE | NAND_CTRL_CHANGE); chip->cmd_ctrl(mtd, NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE); /* This applies to read commands */ 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: { unsigned int status; 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) { printk(KERN_ERR "[%s] read block failed (status: 0x%x, column: 0x%x page: 0x%x)\n", __FUNCTION__, status, column, page_addr); mtd->ecc_stats.failed++; } if(status & NAND_STATUS_CRITICAL_BLOCK) { printk(KERN_ERR "[%s] read block is critical (status: 0x%x, column: 0x%x page: 0x%x)\n", __FUNCTION__, status, column, page_addr); mtd->ecc_stats.corrected++; } } break; default: break; } } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ static int ifx_nand_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip, uint8_t *buf, int page) { chip->read_buf(mtd, buf, mtd->writesize); chip->read_buf(mtd, chip->oob_poi, mtd->oobsize); return 0; } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ static void ifx_nand_write_page_raw(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); } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ static int ifx_nand_write_page(struct mtd_info *mtd, struct nand_chip *chip, const uint8_t *buf, int page, int cached, 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 ---*/ 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 progamming 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); if (status & NAND_STATUS_FAIL) return -EIO; } else { chip->cmdfunc(mtd, NAND_CMD_CACHEDPROG, -1, -1); status = chip->waitfunc(mtd, chip); } #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; /* Send command to read back the data */ chip->cmdfunc(mtd, NAND_CMD_READ0, 0, page); /*--- printk(KERN_ERR "[%s:%d] was here\n", __func__, __LINE__); ---*/ if ( (no_of_verify_retries == 0) && (mtd->ecc_stats.corrected - stats.corrected)) { 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; } else if(mtd->ecc_stats.failed - stats.failed) { printk(KERN_ERR "VERIFY-ERROR on page: %d: non correctable error detected\n", page); return -EIO; } else if(mtd->ecc_stats.corrected - stats.corrected) { printk(KERN_ERR "VERIFY-ERROR on page: %d: rewrite also results in a correctable error\n", page); } if (chip->verify_buf(mtd, buf, mtd->writesize)) { printk(KERN_ERR "VERIFY-ERROR on page: %d: verify read results in a mismatch\n", page); return -EIO; } } #endif return 0; } /*! \fn void ifx_nand_chip_init(void) \ingroup IFX_NAND_DRV \brief platform specific initialization routine \param none \return none */ static void ifx_nand_chip_init(void) { 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); #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 defined(CONFIG_NAND_CS1) reg = (NAND_BASE_ADDRESS & 0x1fffff00)| IFX_EBU_ADDSEL1_MASK(2)| IFX_EBU_ADDSEL1_REGEN; IFX_REG_W32(~IFX_EBU_ADDSEL1_REGEN, IFX_EBU_ADDSEL0); IFX_REG_W32(reg, IFX_EBU_ADDSEL1); 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); IFX_REG_W32(reg,IFX_EBU_NAND_CON); /* byte swap;minimum delay*/ 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); #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); #endif mb(); /* Set bus signals to inactive */ NAND_WRITE(NAND_WRITE_CMD, NAND_CMD_RESET); // Reset nand chip } void ifx_nand_avm_check_for_hweccnand(struct nand_chip *this) { #define PRINT_TO_SCREEN(...) printk(KERN_ERR "[NAND] "); printk(__VA_ARGS__); printk("\n") #define MAX_HWECC_TRIES 10 RESET_CHIP(); // 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 char chip_maf_id; unsigned char chip_dev_id; unsigned char cellinfo, extid; WRITE_NAND_COMMAND(0x90); // Read id WRITE_NAND_ADDRESS(0x00); chip_maf_id = READ_NAND(); chip_dev_id = READ_NAND(); if( (chip_maf_id == 0x2c) && ( (chip_dev_id == 0xf1) // MT29F1G08ABADA || (chip_dev_id == 0xdc) ) ) // MT29F4G08ABADA { int no_tries = 0; cellinfo = READ_NAND(); extid = READ_NAND(); try_again: // use setfeature option to turn on internal ecc WRITE_NAND_COMMAND(0xEF); // setFeature WRITE_NAND_ADDRESS(0x90); // array operation mode WRITE_NAND(0x08); WRITE_NAND(0x00); WRITE_NAND(0x00); WRITE_NAND(0x00); while(!NAND_READY); // use getfeature to read result WRITE_NAND_COMMAND(0xEE); // getFeature WRITE_NAND_ADDRESS(0x90); // array operation mode while(!NAND_READY); // read 4 bytes... result = READ_NAND(); 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()); 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("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"); } else { PRINT_TO_SCREEN("Hardware-ECC activated"); this->ecc.layout = &nand_oob_64_NANDwithHWEcc; this->ecc.size = 1024 << (extid & 0x3); /*--- die ecc.size muss gleich der mtd->write_size sein! ---*/ this->ecc.mode = NAND_ECC_HW; this->ecc.read_page = ifx_nand_read_page_raw; this->ecc.write_page = ifx_nand_write_page_raw; this->ecc.read_page_raw = ifx_nand_read_page_raw; this->ecc.write_page_raw = ifx_nand_write_page_raw; this->write_page = ifx_nand_write_page; this->cmdfunc = ifx_nand_command_hwecc; } } } } /*------------------------------------------------------------------------------------------*\ * hbl: start * Section mismatch, weil __init eine __exit funktion aufgerufen hat \*------------------------------------------------------------------------------------------*/ static inline void ifx_nand_cleanup(void) { /* Release resources, unregister device */ nand_release (ifx_nand_mtd); ifx_gpio_deregister(IFX_GPIO_MODULE_NAND); /* Free the MTD device structure */ kfree (ifx_nand_mtd); } /*------------------------------------------------------------------------------------------*\ * hbl: end \*------------------------------------------------------------------------------------------*/ /* * Main initialization routine */ /*! \fn int ifx_nand_init(void) \ingroup IFX_NAND_DRV \brief Main initialization routine \param none \return error message */ static int __init ifx_nand_init(void) { struct nand_chip *this; #if defined(CONFIG_MTD_PARTITIONS) && defined(CONFIG_MTD_CMDLINE_PARTS) struct mtd_partition *mtd_parts = 0; #endif int retval = 0; extern unsigned long long ifxmips_flashsize_nand; printk(KERN_ERR "[NAND] nand_size = 0x%llx" , ifxmips_flashsize_nand); if(ifxmips_flashsize_nand == 0ULL) { printk(KERN_ERR "ifx_nand_init: no NAND\n"); return -ENXIO; } DBG_NAND(KERN_ERR "[NAND]%s: scan for NAND size=0x%llx\n", __func__, ifxmips_flashsize_nand); ifx_nand_chip_init(); /* Allocate memory for MTD device structure and private data */ ifx_nand_mtd = kmalloc(sizeof(struct mtd_info) + sizeof(struct nand_chip), GFP_KERNEL); if (!ifx_nand_mtd) { printk("Unable to allocate NAND MTD dev structure.\n"); return -ENOMEM; } /* Get pointer to private data */ this = (struct nand_chip *)(&ifx_nand_mtd[1]); /* Initialize structures */ memset(ifx_nand_mtd, 0, sizeof(struct mtd_info)); memset(this, 0, sizeof(struct nand_chip)); ifx_nand_mtd->name = (char *) kmalloc (16, GFP_KERNEL); if (ifx_nand_mtd->name == NULL) { retval = -ENOMEM; goto out; } memset ((void *) ifx_nand_mtd->name, 0, 16); sprintf ((char *)ifx_nand_mtd->name, IFX_MTD_NAND_BANK_NAME); /* Link the private data with the MTD structure */ ifx_nand_mtd->priv = this; ifx_nand_mtd->owner = THIS_MODULE; /* insert callbacks */ this->IO_ADDR_R = (void *)NAND_BASE_ADDRESS; this->IO_ADDR_W = (void *)NAND_BASE_ADDRESS; this->cmd_ctrl = ifx_nand_cmd_ctrl; #if defined(CONFIG_AR9) this->options|=NAND_USE_FLASH_BBT; #endif /* 30 us command delay time */ this->chip_delay = 30; this->ecc.mode = NAND_ECC_SOFT; this->read_byte=ifx_nand_read_byte; this->read_buf=ifx_nand_read_buf; this->write_buf=ifx_nand_write_buf; this->verify_buf=ifx_nand_verify_buf; #if !defined(RDBY_NOT_USED) this->dev_ready=ifx_nand_ready; #endif this->select_chip=ifx_nand_select_chip; ifx_nand_avm_check_for_hweccnand(this); /* Scan to find existence of the device */ printk("Probe for NAND flash...\n"); if (nand_scan(ifx_nand_mtd, 1)) { retval = -ENXIO; goto out; } #ifdef CONFIG_MTD_PARTITIONS #ifdef CONFIG_MTD_CMDLINE_PARTS int n=0; struct mtd_partition *mtd_parts=NULL; /* * Select dynamic from cmdline partition definitions */ n= parse_mtd_partitions(ifx_nand_mtd, part_probes, &mtd_parts, 0); if (n<= 0) { kfree(ifx_nand_mtd); return retval; } add_mtd_partitions(ifx_nand_mtd, mtd_parts,n); #else retval=add_mtd_partitions(ifx_nand_mtd, ifx_nand_partitions, IFX_MTD_NAND_PARTS); #endif // CONFIG_MTD_CMDLINE_PARTS #else retval=add_mtd_device(ifx_nand_mtd); #endif //CONFIG_MTD_PARTITIONS out: if(retval < 0) { ifx_nand_cleanup(); } return retval; } static void __exit ifx_nand_exit(void) { ifx_nand_cleanup(); } module_init(ifx_nand_init); module_exit(ifx_nand_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Wu Qi Ming"); MODULE_DESCRIPTION("NAND driver for IFX");