diff options
Diffstat (limited to 'drivers/mtd/nand/raw/mxc_nand.c')
-rw-r--r-- | drivers/mtd/nand/raw/mxc_nand.c | 140 |
1 files changed, 86 insertions, 54 deletions
diff --git a/drivers/mtd/nand/raw/mxc_nand.c b/drivers/mtd/nand/raw/mxc_nand.c index 003008355b3c..3fe0b471f4a2 100644 --- a/drivers/mtd/nand/raw/mxc_nand.c +++ b/drivers/mtd/nand/raw/mxc_nand.c @@ -20,6 +20,7 @@ #include <linux/irq.h> #include <linux/completion.h> #include <linux/of.h> +#include <linux/bitfield.h> #define DRIVER_NAME "mxc_nand" @@ -47,6 +48,8 @@ #define NFC_V1_V2_CONFIG1 (host->regs + 0x1a) #define NFC_V1_V2_CONFIG2 (host->regs + 0x1c) +#define NFC_V1_V2_ECC_STATUS_RESULT_ERM GENMASK(3, 2) + #define NFC_V2_CONFIG1_ECC_MODE_4 (1 << 0) #define NFC_V1_V2_CONFIG1_SP_EN (1 << 2) #define NFC_V1_V2_CONFIG1_ECC_EN (1 << 3) @@ -132,7 +135,7 @@ struct mxc_nand_devtype_data { uint16_t (*get_dev_status)(struct mxc_nand_host *); int (*check_int)(struct mxc_nand_host *); void (*irq_control)(struct mxc_nand_host *, int); - u32 (*get_ecc_status)(struct mxc_nand_host *); + u32 (*get_ecc_status)(struct nand_chip *); const struct mtd_ooblayout_ops *ooblayout; void (*select_chip)(struct nand_chip *chip, int cs); int (*setup_interface)(struct nand_chip *chip, int csline, @@ -175,6 +178,7 @@ struct mxc_nand_host { int eccsize; int used_oobsize; int active_cs; + unsigned int ecc_stats_v1; struct completion op_completion; @@ -406,19 +410,81 @@ static void irq_control(struct mxc_nand_host *host, int activate) } } -static u32 get_ecc_status_v1(struct mxc_nand_host *host) +static u32 get_ecc_status_v1(struct nand_chip *chip) { - return readw(NFC_V1_V2_ECC_STATUS_RESULT); + struct mtd_info *mtd = nand_to_mtd(chip); + struct mxc_nand_host *host = nand_get_controller_data(chip); + unsigned int ecc_stats, max_bitflips = 0; + int no_subpages, i; + + no_subpages = mtd->writesize >> 9; + + ecc_stats = host->ecc_stats_v1; + + for (i = 0; i < no_subpages; i++) { + switch (ecc_stats & 0x3) { + case 0: + default: + break; + case 1: + mtd->ecc_stats.corrected++; + max_bitflips = 1; + break; + case 2: + mtd->ecc_stats.failed++; + break; + } + + ecc_stats >>= 2; + } + + return max_bitflips; } -static u32 get_ecc_status_v2(struct mxc_nand_host *host) +static u32 get_ecc_status_v2_v3(struct nand_chip *chip, unsigned int ecc_stat) { - return readl(NFC_V1_V2_ECC_STATUS_RESULT); + struct mtd_info *mtd = nand_to_mtd(chip); + struct mxc_nand_host *host = nand_get_controller_data(chip); + u8 ecc_bit_mask, err_limit; + unsigned int max_bitflips = 0; + int no_subpages, err; + + ecc_bit_mask = (host->eccsize == 4) ? 0x7 : 0xf; + err_limit = (host->eccsize == 4) ? 0x4 : 0x8; + + no_subpages = mtd->writesize >> 9; + + do { + err = ecc_stat & ecc_bit_mask; + if (err > err_limit) { + mtd->ecc_stats.failed++; + } else { + mtd->ecc_stats.corrected += err; + max_bitflips = max_t(unsigned int, max_bitflips, err); + } + + ecc_stat >>= 4; + } while (--no_subpages); + + return max_bitflips; } -static u32 get_ecc_status_v3(struct mxc_nand_host *host) +static u32 get_ecc_status_v2(struct nand_chip *chip) { - return readl(NFC_V3_ECC_STATUS_RESULT); + struct mxc_nand_host *host = nand_get_controller_data(chip); + + u32 ecc_stat = readl(NFC_V1_V2_ECC_STATUS_RESULT); + + return get_ecc_status_v2_v3(chip, ecc_stat); +} + +static u32 get_ecc_status_v3(struct nand_chip *chip) +{ + struct mxc_nand_host *host = nand_get_controller_data(chip); + + u32 ecc_stat = readl(NFC_V3_ECC_STATUS_RESULT); + + return get_ecc_status_v2_v3(chip, ecc_stat); } static irqreturn_t mxc_nfc_irq(int irq, void *dev_id) @@ -712,9 +778,9 @@ static int mxc_nand_read_page_v1(struct nand_chip *chip, void *buf, void *oob, { struct mtd_info *mtd = nand_to_mtd(chip); struct mxc_nand_host *host = nand_get_controller_data(chip); - unsigned int bitflips_corrected = 0; int no_subpages; int i; + unsigned int ecc_stats = 0; host->devtype_data->enable_hwecc(chip, ecc); @@ -727,8 +793,6 @@ static int mxc_nand_read_page_v1(struct nand_chip *chip, void *buf, void *oob, no_subpages = mtd->writesize >> 9; for (i = 0; i < no_subpages; i++) { - uint16_t ecc_stats; - /* NANDFC buffer 0 is used for page read/write */ writew((host->active_cs << 4) | i, NFC_V1_V2_BUF_ADDR); @@ -737,32 +801,18 @@ static int mxc_nand_read_page_v1(struct nand_chip *chip, void *buf, void *oob, /* Wait for operation to complete */ wait_op_done(host, true); - ecc_stats = get_ecc_status_v1(host); - - ecc_stats >>= 2; - - if (buf && ecc) { - switch (ecc_stats & 0x3) { - case 0: - default: - break; - case 1: - mtd->ecc_stats.corrected++; - bitflips_corrected = 1; - break; - case 2: - mtd->ecc_stats.failed++; - break; - } - } + ecc_stats |= FIELD_GET(NFC_V1_V2_ECC_STATUS_RESULT_ERM, + readw(NFC_V1_V2_ECC_STATUS_RESULT)) << i * 2; } + host->ecc_stats_v1 = ecc_stats; + if (buf) memcpy32_fromio(buf, host->main_area0, mtd->writesize); if (oob) copy_spare(mtd, true, oob); - return bitflips_corrected; + return 0; } static int mxc_nand_read_page_v2_v3(struct nand_chip *chip, void *buf, @@ -770,10 +820,6 @@ static int mxc_nand_read_page_v2_v3(struct nand_chip *chip, void *buf, { struct mtd_info *mtd = nand_to_mtd(chip); struct mxc_nand_host *host = nand_get_controller_data(chip); - unsigned int max_bitflips = 0; - u32 ecc_stat, err; - int no_subpages; - u8 ecc_bit_mask, err_limit; host->devtype_data->enable_hwecc(chip, ecc); @@ -791,26 +837,7 @@ static int mxc_nand_read_page_v2_v3(struct nand_chip *chip, void *buf, if (oob) copy_spare(mtd, true, oob); - ecc_bit_mask = (host->eccsize == 4) ? 0x7 : 0xf; - err_limit = (host->eccsize == 4) ? 0x4 : 0x8; - - no_subpages = mtd->writesize >> 9; - - ecc_stat = host->devtype_data->get_ecc_status(host); - - do { - err = ecc_stat & ecc_bit_mask; - if (err > err_limit) { - mtd->ecc_stats.failed++; - } else { - mtd->ecc_stats.corrected += err; - max_bitflips = max_t(unsigned int, max_bitflips, err); - } - - ecc_stat >>= 4; - } while (--no_subpages); - - return max_bitflips; + return 0; } static int mxc_nand_read_page(struct nand_chip *chip, uint8_t *buf, @@ -818,13 +845,18 @@ static int mxc_nand_read_page(struct nand_chip *chip, uint8_t *buf, { struct mxc_nand_host *host = nand_get_controller_data(chip); void *oob_buf; + int ret; if (oob_required) oob_buf = chip->oob_poi; else oob_buf = NULL; - return host->devtype_data->read_page(chip, buf, oob_buf, 1, page); + ret = host->devtype_data->read_page(chip, buf, oob_buf, 1, page); + if (ret) + return ret; + + return host->devtype_data->get_ecc_status(chip); } static int mxc_nand_read_page_raw(struct nand_chip *chip, uint8_t *buf, |