diff --git a/drivers/mtd/nand/raw/nand_micron.c b/drivers/mtd/nand/raw/nand_micron.c index c0192881906b8..3af30d4eae149 100644 --- a/drivers/mtd/nand/raw/nand_micron.c +++ b/drivers/mtd/nand/raw/nand_micron.c @@ -365,6 +365,46 @@ micron_nand_read_page_on_die_ecc(struct nand_chip *chip, uint8_t *buf, return ret ? ret : max_bitflips; } +static int +micron_nand_read_subpage_on_die_ecc(struct nand_chip *chip, uint32_t data_offs, + uint32_t readlen, uint8_t *bufpoi, int page) +{ + struct mtd_info *mtd = nand_to_mtd(chip); + u8 status; + int ret, max_bitflips = 0; + + ret = micron_nand_on_die_ecc_setup(chip, true); + if (ret) + return ret; + + ret = nand_status_op(chip, &status); + if (ret) + goto out; + + ret = nand_read_page_op(chip, page, data_offs, + bufpoi + data_offs, readlen); + if (ret) + goto out; + + ret = nand_status_op(chip, &status); + if (ret) + goto out; + + if (status & NAND_STATUS_FAIL) { + /* uncorrected */ + mtd->ecc_stats.failed++; + } else if (status & NAND_ECC_STATUS_WRITE_RECOMMENDED) { + /* corrected */ + max_bitflips = mtd->bitflip_threshold; + mtd->ecc_stats.corrected += max_bitflips; + } + +out: + micron_nand_on_die_ecc_setup(chip, false); + + return ret ? ret : max_bitflips; +} + static int micron_nand_write_page_on_die_ecc(struct nand_chip *chip, const uint8_t *buf, int oob_required, int page) @@ -381,6 +421,15 @@ micron_nand_write_page_on_die_ecc(struct nand_chip *chip, const uint8_t *buf, return ret; } +static int +micron_nand_write_subpage_on_die_ecc(struct nand_chip *chip, + uint32_t offset, uint32_t data_len, + const uint8_t *data_buf, + int oob_required, int page) +{ + return micron_nand_write_page_on_die_ecc(chip, data_buf, oob_required, page); +} + enum { /* The NAND flash doesn't support on-die ECC */ MICRON_ON_DIE_UNSUPPORTED, @@ -550,7 +599,9 @@ static int micron_nand_init(struct nand_chip *chip) chip->ecc.strength = requirements->strength; chip->ecc.algo = NAND_ECC_ALGO_BCH; chip->ecc.read_page = micron_nand_read_page_on_die_ecc; + chip->ecc.read_subpage = micron_nand_read_subpage_on_die_ecc; chip->ecc.write_page = micron_nand_write_page_on_die_ecc; + chip->ecc.write_subpage = micron_nand_write_subpage_on_die_ecc; if (ondie == MICRON_ON_DIE_MANDATORY) { chip->ecc.read_page_raw = nand_read_page_raw_notsupp; diff --git a/drivers/mtd/nand/raw/pl35x-nand-controller.c b/drivers/mtd/nand/raw/pl35x-nand-controller.c index 5f6115db7c3cf..6024718ce3b98 100644 --- a/drivers/mtd/nand/raw/pl35x-nand-controller.c +++ b/drivers/mtd/nand/raw/pl35x-nand-controller.c @@ -144,6 +144,14 @@ struct pl35x_nandc { u8 *ecc_buf; }; +static bool enable_subpage_read = 1; +module_param(enable_subpage_read, bool, 0444); +MODULE_PARM_DESC(enable_subpage_read, "Load-time parameter to toggle subpage reads on supported nand chips. Enabled by default."); + +static bool enable_subpage_write = 1; +module_param(enable_subpage_write, bool, 0444); +MODULE_PARM_DESC(enable_subpage_write, "Load-time parameter to toggle subpage writes on supported nand chips. Enabled by default."); + static inline struct pl35x_nandc *to_pl35x_nandc(struct nand_controller *ctrl) { return container_of(ctrl, struct pl35x_nandc, controller); @@ -499,14 +507,6 @@ static int pl35x_nand_recover_data_hwecc(struct pl35x_nandc *nfc, return max_bitflips; } -static int pl35x_nand_write_subpage_raw(struct nand_chip *chip, - uint32_t offset, uint32_t data_len, - const uint8_t *data_buf, - int oob_required, int page) -{ - return nand_monolithic_write_page_raw(chip, data_buf, oob_required, page); -} - static int pl35x_nand_write_page_hwecc(struct nand_chip *chip, const u8 *buf, int oob_required, int page) @@ -667,12 +667,6 @@ static int pl35x_nand_read_page_hwecc(struct nand_chip *chip, return ret; } -static int pl35x_nand_read_subpage_raw(struct nand_chip *chip, uint32_t data_offs, - uint32_t readlen, uint8_t *bufpoi, int page) -{ - return nand_read_page_op(chip, page, data_offs, bufpoi + data_offs, readlen); -} - static int pl35x_nand_exec_op(struct nand_chip *chip, const struct nand_subop *subop) { @@ -991,27 +985,19 @@ static int pl35x_nand_init_hw_ecc_controller(struct pl35x_nandc *nfc, return ret; } -static void pl35x_nand_init_ondie_ecc(struct pl35x_nandc *nfc, - struct nand_chip *chip) +static void pl35x_nand_setup_ondie_ecc(struct nand_chip *chip) { - struct mtd_info *mtd = nand_to_mtd(chip); - - /* Bypass the controller ECC block */ - pl35x_smc_set_ecc_mode(nfc, chip, PL35X_SMC_ECC_CFG_MODE_BYPASS); - - chip->ecc.strength = 1; - chip->ecc.bytes = 0; - chip->ecc.read_page = nand_monolithic_read_page_raw; - chip->ecc.read_page_raw = nand_monolithic_read_page_raw; - chip->ecc.read_subpage = pl35x_nand_read_subpage_raw; - chip->ecc.write_page = nand_monolithic_write_page_raw; - chip->ecc.write_page_raw = nand_monolithic_write_page_raw; - chip->ecc.write_subpage = pl35x_nand_write_subpage_raw; - chip->ecc.size = mtd->writesize; + /* NAND with on-die ECC supports subpage reads */ + if (enable_subpage_read) + chip->options |= NAND_SUBPAGE_READ; + else + chip->options &= ~(NAND_SUBPAGE_READ); - /* NAND with on-die ECC supports subpage reads and writes */ - chip->options |= NAND_SUBPAGE_READ; - chip->options &= ~(NAND_NO_SUBPAGE_WRITE); + /* NAND with on-die ECC may support subpage writes */ + if (enable_subpage_write) + chip->options &= ~(NAND_NO_SUBPAGE_WRITE); + else + chip->options |= NAND_NO_SUBPAGE_WRITE; } static int pl35x_nand_attach_chip(struct nand_chip *chip) @@ -1048,7 +1034,7 @@ static int pl35x_nand_attach_chip(struct nand_chip *chip) switch (chip->ecc.engine_type) { case NAND_ECC_ENGINE_TYPE_ON_DIE: - pl35x_nand_init_ondie_ecc(nfc, chip); + pl35x_nand_setup_ondie_ecc(chip); /* Keep these legacy BBT descriptors for ON_DIE situations */ chip->bbt_td = &bbt_main_descr; chip->bbt_md = &bbt_mirror_descr;