summaryrefslogtreecommitdiff
path: root/drivers/mtd/nand/fsl_nfc.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/mtd/nand/fsl_nfc.c')
-rw-r--r--drivers/mtd/nand/fsl_nfc.c129
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)) {