diff options
Diffstat (limited to 'drivers/mtd/nand/fsl_nfc.c')
-rw-r--r-- | drivers/mtd/nand/fsl_nfc.c | 129 |
1 files changed, 72 insertions, 57 deletions
diff --git a/drivers/mtd/nand/fsl_nfc.c b/drivers/mtd/nand/fsl_nfc.c index f84c785e4383..27562653956a 100644 --- a/drivers/mtd/nand/fsl_nfc.c +++ b/drivers/mtd/nand/fsl_nfc.c @@ -27,6 +27,7 @@ #include <linux/slab.h> #include <asm/fsl_nfc.h> #include <mach/hardware.h> +#include <mach/mxc_nand.h> #ifdef CONFIG_COLDFIRE #include <asm/mcfsim.h> @@ -351,39 +352,20 @@ fsl_nfc_addr_cycle(struct mtd_info *mtd, int column, int page) CONFIG_PAGE_CNT_SHIFT, 0x1); } -/* Control chips select signal on m54418twr board */ static void nfc_select_chip(struct mtd_info *mtd, int chip) { -#ifdef CONFIG_COLDFIRE - if (chip < 0) { - MCF_GPIO_PAR_FBCTL &= (MCF_GPIO_PAR_FBCTL_ALE_MASK & - MCF_GPIO_PAR_FBCTL_TA_MASK); - MCF_GPIO_PAR_FBCTL |= MCF_GPIO_PAR_FBCTL_ALE_FB_TS | - MCF_GPIO_PAR_FBCTL_TA_TA; - - MCF_GPIO_PAR_BE = - MCF_GPIO_PAR_BE_BE3_BE3 | MCF_GPIO_PAR_BE_BE2_BE2 | - MCF_GPIO_PAR_BE_BE1_BE1 | MCF_GPIO_PAR_BE_BE0_BE0; - - MCF_GPIO_PAR_CS &= ~MCF_GPIO_PAR_CS_CS1_NFC_CE; - MCF_GPIO_PAR_CS |= MCF_GPIO_PAR_CS_CS0_CS0; - return; - } + nfc_set_field(mtd, NFC_ROW_ADDR, ROW_ADDR_CHIP_SEL_RB_MASK, + ROW_ADDR_CHIP_SEL_RB_SHIFT, 1); - MCF_GPIO_PAR_FBCTL &= (MCF_GPIO_PAR_FBCTL_ALE_MASK & - MCF_GPIO_PAR_FBCTL_TA_MASK); - MCF_GPIO_PAR_FBCTL |= MCF_GPIO_PAR_FBCTL_ALE_FB_ALE | - MCF_GPIO_PAR_FBCTL_TA_NFC_RB; - MCF_GPIO_PAR_BE = MCF_GPIO_PAR_BE_BE3_FB_A1 | - MCF_GPIO_PAR_BE_BE2_FB_A0 | - MCF_GPIO_PAR_BE_BE1_BE1 | MCF_GPIO_PAR_BE_BE0_BE0; - - MCF_GPIO_PAR_CS &= (MCF_GPIO_PAR_BE_BE3_MASK & - MCF_GPIO_PAR_BE_BE2_MASK); - MCF_GPIO_PAR_CS |= MCF_GPIO_PAR_CS_CS1_NFC_CE; - return; -#endif + if (chip == 0) + nfc_set_field(mtd, NFC_ROW_ADDR, ROW_ADDR_CHIP_SEL_MASK, + ROW_ADDR_CHIP_SEL_SHIFT, 1); + else if (chip == 1) + nfc_set_field(mtd, NFC_ROW_ADDR, ROW_ADDR_CHIP_SEL_MASK, + ROW_ADDR_CHIP_SEL_SHIFT, 2); + else + nfc_clear(mtd, NFC_ROW_ADDR, ROW_ADDR_CHIP_SEL_MASK); } /* Read NAND Ready/Busy signal */ @@ -423,19 +405,8 @@ fsl_nfc_command(struct mtd_info *mtd, unsigned command, CONFIG_ECC_MODE_MASK, CONFIG_ECC_MODE_SHIFT, ECC_BYPASS); - if (!(page%0x40)) { - nfc_set_field(mtd, NFC_FLASH_CONFIG, - CONFIG_ECC_MODE_MASK, - CONFIG_ECC_MODE_SHIFT, ECC_BYPASS); - } - switch (command) { case NAND_CMD_PAGEPROG: - if (!(prv->page%0x40)) - nfc_set_field(mtd, NFC_FLASH_CONFIG, - CONFIG_ECC_MODE_MASK, - CONFIG_ECC_MODE_SHIFT, ECC_BYPASS); - fsl_nfc_send_cmd(mtd, PROGRAM_PAGE_CMD_BYTE1, PROGRAM_PAGE_CMD_BYTE2, @@ -663,25 +634,55 @@ fsl_nfc_read_word(struct mtd_info *mtd) return tmp; } -#if 0 -static void fsl_nfc_check_ecc_status(struct mtd_info *mtd) +static int nfc_calculate_ecc(struct mtd_info *mtd, const u_char *dat, + u_char *ecc_code) +{ + return 0; +} + +/* Count the number of 0's in buff upto max_bits */ +static int count_written_bits(uint8_t *buff, int size, int max_bits) +{ + int k, written_bits = 0; + + for (k = 0; k < size; k++) { + written_bits += hweight8(~buff[k]); + if (written_bits > max_bits) + break; + } + + return written_bits; +} + +static int fsl_nfc_check_ecc_status(struct mtd_info *mtd, u_char *dat) { struct nand_chip *chip = mtd->priv; struct fsl_nfc_prv *prv = chip->priv; - u8 ecc_status, ecc_count; + u32 ecc_status; + u8 ecc_count; + int flip; - ecc_status = *(u8 *)(prv->regs + ECC_SRAM_ADDR * 8 + 7); + /* + * ECC status is stored at NFC_CFG[ECCADD] +4 for + * little-endian and +7 for big-endian SOC. Access as 32 bits + * and use low byte. + */ + ecc_status = __raw_readl(prv->regs + ECC_SRAM_ADDR * 8 + 4); ecc_count = ecc_status & ECC_ERR_COUNT; - if (ecc_status & ECC_STATUS_MASK) { - /*mtd->ecc_stats.failed++;*/ - printk("ECC failed to correct all errors!\n"); - } else if (ecc_count) { - /*mtd->ecc_stats.corrected += ecc_count;*/ - printk(KERN_INFO"ECC corrected %d errors\n", ecc_count); + if (!(ecc_status & ECC_STATUS_MASK)) + return ecc_count; + + /* If 'ecc_count' zero or less then buffer is all 0xff or erased. */ + flip = count_written_bits(dat, chip->ecc.size, ecc_count); + + + if (flip > ecc_count) { + printk("ECC failed to correct all errors (%08x)\n", ecc_status); + return -1; } + return 0; } -#endif static void copy_from_to_spare(struct mtd_info *mtd, void *pbuf, int len, int wr) @@ -748,12 +749,19 @@ static int fsl_nfc_write_oob(struct mtd_info *mtd, struct nand_chip *chip, static int fsl_nfc_read_page(struct mtd_info *mtd, struct nand_chip *chip, uint8_t *buf, int page) { + int stat; struct fsl_nfc_prv *prv = chip->priv; - /*fsl_nfc_check_ecc_status(mtd);*/ memcpy_fromio((void *)buf, prv->regs + NFC_MAIN_AREA(0), mtd->writesize); copy_from_to_spare(mtd, chip->oob_poi, mtd->oobsize, 0); + + stat = fsl_nfc_check_ecc_status(mtd, buf); + if (stat < 0) + mtd->ecc_stats.failed++; + else + mtd->ecc_stats.corrected += stat; + return 0; } @@ -787,6 +795,7 @@ fsl_nfc_probe(struct platform_device *pdev) struct resource *res; struct mtd_info *mtd; struct mtd_partition *parts; + struct mxc_nand_platform_data *pdata = pdev->dev.platform_data; struct nand_chip *chip; unsigned long regs_paddr, regs_size; int retval = 0; @@ -830,7 +839,7 @@ fsl_nfc_probe(struct platform_device *pdev) return -ENOMEM; } - mtd->name = "NAND"; + mtd->name = "fsl_nfc"; mtd->writesize = 2048; mtd->oobsize = 64; @@ -842,7 +851,11 @@ fsl_nfc_probe(struct platform_device *pdev) chip->write_buf = fsl_nfc_write_buf; chip->verify_buf = fsl_nfc_verify_buf; chip->options = NAND_NO_AUTOINCR | NAND_USE_FLASH_BBT | - NAND_BUSWIDTH_16 | NAND_CACHEPRG; + NAND_CACHEPRG; + + /* NAND bus width determines access funtions used by upper layer */ + if (pdata->width == 2) + chip->options |= NAND_BUSWIDTH_16; chip->select_chip = nfc_select_chip; @@ -917,9 +930,11 @@ fsl_nfc_probe(struct platform_device *pdev) CONFIG_FAST_FLASH_SHIFT, 1); #endif - nfc_set_field(mtd, NFC_FLASH_CONFIG, - CONFIG_16BIT_MASK, - CONFIG_16BIT_SHIFT, 1); + /* Flash mode width (BITWIDTH) required for 16-bit access */ + if (pdata->width == 2) + nfc_set_field(mtd, NFC_FLASH_CONFIG, + CONFIG_16BIT_MASK, + CONFIG_16BIT_SHIFT, 1); /* Detect NAND chips */ if (nand_scan(mtd, 1)) { |