Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 51 additions & 0 deletions drivers/mtd/nand/raw/nand_micron.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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,
Expand Down Expand Up @@ -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;
Expand Down
54 changes: 20 additions & 34 deletions drivers/mtd/nand/raw/pl35x-nand-controller.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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)
{
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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;
Expand Down