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