162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (C) 2017 Free Electrons
462306a36Sopenharmony_ci * Copyright (C) 2017 NextThing Co
562306a36Sopenharmony_ci *
662306a36Sopenharmony_ci * Author: Boris Brezillon <boris.brezillon@free-electrons.com>
762306a36Sopenharmony_ci */
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci#include <linux/slab.h>
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci#include "internals.h"
1262306a36Sopenharmony_ci
1362306a36Sopenharmony_ci/*
1462306a36Sopenharmony_ci * Special Micron status bit 3 indicates that the block has been
1562306a36Sopenharmony_ci * corrected by on-die ECC and should be rewritten.
1662306a36Sopenharmony_ci */
1762306a36Sopenharmony_ci#define NAND_ECC_STATUS_WRITE_RECOMMENDED	BIT(3)
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ci/*
2062306a36Sopenharmony_ci * On chips with 8-bit ECC and additional bit can be used to distinguish
2162306a36Sopenharmony_ci * cases where a errors were corrected without needing a rewrite
2262306a36Sopenharmony_ci *
2362306a36Sopenharmony_ci * Bit 4 Bit 3 Bit 0 Description
2462306a36Sopenharmony_ci * ----- ----- ----- -----------
2562306a36Sopenharmony_ci * 0     0     0     No Errors
2662306a36Sopenharmony_ci * 0     0     1     Multiple uncorrected errors
2762306a36Sopenharmony_ci * 0     1     0     4 - 6 errors corrected, recommend rewrite
2862306a36Sopenharmony_ci * 0     1     1     Reserved
2962306a36Sopenharmony_ci * 1     0     0     1 - 3 errors corrected
3062306a36Sopenharmony_ci * 1     0     1     Reserved
3162306a36Sopenharmony_ci * 1     1     0     7 - 8 errors corrected, recommend rewrite
3262306a36Sopenharmony_ci */
3362306a36Sopenharmony_ci#define NAND_ECC_STATUS_MASK		(BIT(4) | BIT(3) | BIT(0))
3462306a36Sopenharmony_ci#define NAND_ECC_STATUS_UNCORRECTABLE	BIT(0)
3562306a36Sopenharmony_ci#define NAND_ECC_STATUS_4_6_CORRECTED	BIT(3)
3662306a36Sopenharmony_ci#define NAND_ECC_STATUS_1_3_CORRECTED	BIT(4)
3762306a36Sopenharmony_ci#define NAND_ECC_STATUS_7_8_CORRECTED	(BIT(4) | BIT(3))
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_cistruct nand_onfi_vendor_micron {
4062306a36Sopenharmony_ci	u8 two_plane_read;
4162306a36Sopenharmony_ci	u8 read_cache;
4262306a36Sopenharmony_ci	u8 read_unique_id;
4362306a36Sopenharmony_ci	u8 dq_imped;
4462306a36Sopenharmony_ci	u8 dq_imped_num_settings;
4562306a36Sopenharmony_ci	u8 dq_imped_feat_addr;
4662306a36Sopenharmony_ci	u8 rb_pulldown_strength;
4762306a36Sopenharmony_ci	u8 rb_pulldown_strength_feat_addr;
4862306a36Sopenharmony_ci	u8 rb_pulldown_strength_num_settings;
4962306a36Sopenharmony_ci	u8 otp_mode;
5062306a36Sopenharmony_ci	u8 otp_page_start;
5162306a36Sopenharmony_ci	u8 otp_data_prot_addr;
5262306a36Sopenharmony_ci	u8 otp_num_pages;
5362306a36Sopenharmony_ci	u8 otp_feat_addr;
5462306a36Sopenharmony_ci	u8 read_retry_options;
5562306a36Sopenharmony_ci	u8 reserved[72];
5662306a36Sopenharmony_ci	u8 param_revision;
5762306a36Sopenharmony_ci} __packed;
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_cistruct micron_on_die_ecc {
6062306a36Sopenharmony_ci	bool forced;
6162306a36Sopenharmony_ci	bool enabled;
6262306a36Sopenharmony_ci	void *rawbuf;
6362306a36Sopenharmony_ci};
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_cistruct micron_nand {
6662306a36Sopenharmony_ci	struct micron_on_die_ecc ecc;
6762306a36Sopenharmony_ci};
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_cistatic int micron_nand_setup_read_retry(struct nand_chip *chip, int retry_mode)
7062306a36Sopenharmony_ci{
7162306a36Sopenharmony_ci	u8 feature[ONFI_SUBFEATURE_PARAM_LEN] = {retry_mode};
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ci	return nand_set_features(chip, ONFI_FEATURE_ADDR_READ_RETRY, feature);
7462306a36Sopenharmony_ci}
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_ci/*
7762306a36Sopenharmony_ci * Configure chip properties from Micron vendor-specific ONFI table
7862306a36Sopenharmony_ci */
7962306a36Sopenharmony_cistatic int micron_nand_onfi_init(struct nand_chip *chip)
8062306a36Sopenharmony_ci{
8162306a36Sopenharmony_ci	struct nand_parameters *p = &chip->parameters;
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ci	if (p->onfi) {
8462306a36Sopenharmony_ci		struct nand_onfi_vendor_micron *micron = (void *)p->onfi->vendor;
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ci		chip->read_retries = micron->read_retry_options;
8762306a36Sopenharmony_ci		chip->ops.setup_read_retry = micron_nand_setup_read_retry;
8862306a36Sopenharmony_ci	}
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_ci	if (p->supports_set_get_features) {
9162306a36Sopenharmony_ci		set_bit(ONFI_FEATURE_ADDR_READ_RETRY, p->set_feature_list);
9262306a36Sopenharmony_ci		set_bit(ONFI_FEATURE_ON_DIE_ECC, p->set_feature_list);
9362306a36Sopenharmony_ci		set_bit(ONFI_FEATURE_ADDR_READ_RETRY, p->get_feature_list);
9462306a36Sopenharmony_ci		set_bit(ONFI_FEATURE_ON_DIE_ECC, p->get_feature_list);
9562306a36Sopenharmony_ci	}
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ci	return 0;
9862306a36Sopenharmony_ci}
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_cistatic int micron_nand_on_die_4_ooblayout_ecc(struct mtd_info *mtd,
10162306a36Sopenharmony_ci					      int section,
10262306a36Sopenharmony_ci					      struct mtd_oob_region *oobregion)
10362306a36Sopenharmony_ci{
10462306a36Sopenharmony_ci	if (section >= 4)
10562306a36Sopenharmony_ci		return -ERANGE;
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_ci	oobregion->offset = (section * 16) + 8;
10862306a36Sopenharmony_ci	oobregion->length = 8;
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_ci	return 0;
11162306a36Sopenharmony_ci}
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_cistatic int micron_nand_on_die_4_ooblayout_free(struct mtd_info *mtd,
11462306a36Sopenharmony_ci					       int section,
11562306a36Sopenharmony_ci					       struct mtd_oob_region *oobregion)
11662306a36Sopenharmony_ci{
11762306a36Sopenharmony_ci	if (section >= 4)
11862306a36Sopenharmony_ci		return -ERANGE;
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_ci	oobregion->offset = (section * 16) + 2;
12162306a36Sopenharmony_ci	oobregion->length = 6;
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_ci	return 0;
12462306a36Sopenharmony_ci}
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_cistatic const struct mtd_ooblayout_ops micron_nand_on_die_4_ooblayout_ops = {
12762306a36Sopenharmony_ci	.ecc = micron_nand_on_die_4_ooblayout_ecc,
12862306a36Sopenharmony_ci	.free = micron_nand_on_die_4_ooblayout_free,
12962306a36Sopenharmony_ci};
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_cistatic int micron_nand_on_die_8_ooblayout_ecc(struct mtd_info *mtd,
13262306a36Sopenharmony_ci					      int section,
13362306a36Sopenharmony_ci					      struct mtd_oob_region *oobregion)
13462306a36Sopenharmony_ci{
13562306a36Sopenharmony_ci	struct nand_chip *chip = mtd_to_nand(mtd);
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_ci	if (section)
13862306a36Sopenharmony_ci		return -ERANGE;
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_ci	oobregion->offset = mtd->oobsize - chip->ecc.total;
14162306a36Sopenharmony_ci	oobregion->length = chip->ecc.total;
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_ci	return 0;
14462306a36Sopenharmony_ci}
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_cistatic int micron_nand_on_die_8_ooblayout_free(struct mtd_info *mtd,
14762306a36Sopenharmony_ci					       int section,
14862306a36Sopenharmony_ci					       struct mtd_oob_region *oobregion)
14962306a36Sopenharmony_ci{
15062306a36Sopenharmony_ci	struct nand_chip *chip = mtd_to_nand(mtd);
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_ci	if (section)
15362306a36Sopenharmony_ci		return -ERANGE;
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci	oobregion->offset = 2;
15662306a36Sopenharmony_ci	oobregion->length = mtd->oobsize - chip->ecc.total - 2;
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ci	return 0;
15962306a36Sopenharmony_ci}
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_cistatic const struct mtd_ooblayout_ops micron_nand_on_die_8_ooblayout_ops = {
16262306a36Sopenharmony_ci	.ecc = micron_nand_on_die_8_ooblayout_ecc,
16362306a36Sopenharmony_ci	.free = micron_nand_on_die_8_ooblayout_free,
16462306a36Sopenharmony_ci};
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_cistatic int micron_nand_on_die_ecc_setup(struct nand_chip *chip, bool enable)
16762306a36Sopenharmony_ci{
16862306a36Sopenharmony_ci	struct micron_nand *micron = nand_get_manufacturer_data(chip);
16962306a36Sopenharmony_ci	u8 feature[ONFI_SUBFEATURE_PARAM_LEN] = { 0, };
17062306a36Sopenharmony_ci	int ret;
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_ci	if (micron->ecc.forced)
17362306a36Sopenharmony_ci		return 0;
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_ci	if (micron->ecc.enabled == enable)
17662306a36Sopenharmony_ci		return 0;
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_ci	if (enable)
17962306a36Sopenharmony_ci		feature[0] |= ONFI_FEATURE_ON_DIE_ECC_EN;
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_ci	ret = nand_set_features(chip, ONFI_FEATURE_ON_DIE_ECC, feature);
18262306a36Sopenharmony_ci	if (!ret)
18362306a36Sopenharmony_ci		micron->ecc.enabled = enable;
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_ci	return ret;
18662306a36Sopenharmony_ci}
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_cistatic int micron_nand_on_die_ecc_status_4(struct nand_chip *chip, u8 status,
18962306a36Sopenharmony_ci					   void *buf, int page,
19062306a36Sopenharmony_ci					   int oob_required)
19162306a36Sopenharmony_ci{
19262306a36Sopenharmony_ci	struct micron_nand *micron = nand_get_manufacturer_data(chip);
19362306a36Sopenharmony_ci	struct mtd_info *mtd = nand_to_mtd(chip);
19462306a36Sopenharmony_ci	unsigned int step, max_bitflips = 0;
19562306a36Sopenharmony_ci	bool use_datain = false;
19662306a36Sopenharmony_ci	int ret;
19762306a36Sopenharmony_ci
19862306a36Sopenharmony_ci	if (!(status & NAND_ECC_STATUS_WRITE_RECOMMENDED)) {
19962306a36Sopenharmony_ci		if (status & NAND_STATUS_FAIL)
20062306a36Sopenharmony_ci			mtd->ecc_stats.failed++;
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_ci		return 0;
20362306a36Sopenharmony_ci	}
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_ci	/*
20662306a36Sopenharmony_ci	 * The internal ECC doesn't tell us the number of bitflips that have
20762306a36Sopenharmony_ci	 * been corrected, but tells us if it recommends to rewrite the block.
20862306a36Sopenharmony_ci	 * If it's the case, we need to read the page in raw mode and compare
20962306a36Sopenharmony_ci	 * its content to the corrected version to extract the actual number of
21062306a36Sopenharmony_ci	 * bitflips.
21162306a36Sopenharmony_ci	 * But before we do that, we must make sure we have all OOB bytes read
21262306a36Sopenharmony_ci	 * in non-raw mode, even if the user did not request those bytes.
21362306a36Sopenharmony_ci	 */
21462306a36Sopenharmony_ci	if (!oob_required) {
21562306a36Sopenharmony_ci		/*
21662306a36Sopenharmony_ci		 * We first check which operation is supported by the controller
21762306a36Sopenharmony_ci		 * before running it. This trick makes it possible to support
21862306a36Sopenharmony_ci		 * all controllers, even the most constraints, without almost
21962306a36Sopenharmony_ci		 * any performance hit.
22062306a36Sopenharmony_ci		 *
22162306a36Sopenharmony_ci		 * TODO: could be enhanced to avoid repeating the same check
22262306a36Sopenharmony_ci		 * over and over in the fast path.
22362306a36Sopenharmony_ci		 */
22462306a36Sopenharmony_ci		if (!nand_has_exec_op(chip) ||
22562306a36Sopenharmony_ci		    !nand_read_data_op(chip, chip->oob_poi, mtd->oobsize, false,
22662306a36Sopenharmony_ci				       true))
22762306a36Sopenharmony_ci			use_datain = true;
22862306a36Sopenharmony_ci
22962306a36Sopenharmony_ci		if (use_datain)
23062306a36Sopenharmony_ci			ret = nand_read_data_op(chip, chip->oob_poi,
23162306a36Sopenharmony_ci						mtd->oobsize, false, false);
23262306a36Sopenharmony_ci		else
23362306a36Sopenharmony_ci			ret = nand_change_read_column_op(chip, mtd->writesize,
23462306a36Sopenharmony_ci							 chip->oob_poi,
23562306a36Sopenharmony_ci							 mtd->oobsize, false);
23662306a36Sopenharmony_ci		if (ret)
23762306a36Sopenharmony_ci			return ret;
23862306a36Sopenharmony_ci	}
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_ci	micron_nand_on_die_ecc_setup(chip, false);
24162306a36Sopenharmony_ci
24262306a36Sopenharmony_ci	ret = nand_read_page_op(chip, page, 0, micron->ecc.rawbuf,
24362306a36Sopenharmony_ci				mtd->writesize + mtd->oobsize);
24462306a36Sopenharmony_ci	if (ret)
24562306a36Sopenharmony_ci		return ret;
24662306a36Sopenharmony_ci
24762306a36Sopenharmony_ci	for (step = 0; step < chip->ecc.steps; step++) {
24862306a36Sopenharmony_ci		unsigned int offs, i, nbitflips = 0;
24962306a36Sopenharmony_ci		u8 *rawbuf, *corrbuf;
25062306a36Sopenharmony_ci
25162306a36Sopenharmony_ci		offs = step * chip->ecc.size;
25262306a36Sopenharmony_ci		rawbuf = micron->ecc.rawbuf + offs;
25362306a36Sopenharmony_ci		corrbuf = buf + offs;
25462306a36Sopenharmony_ci
25562306a36Sopenharmony_ci		for (i = 0; i < chip->ecc.size; i++)
25662306a36Sopenharmony_ci			nbitflips += hweight8(corrbuf[i] ^ rawbuf[i]);
25762306a36Sopenharmony_ci
25862306a36Sopenharmony_ci		offs = (step * 16) + 4;
25962306a36Sopenharmony_ci		rawbuf = micron->ecc.rawbuf + mtd->writesize + offs;
26062306a36Sopenharmony_ci		corrbuf = chip->oob_poi + offs;
26162306a36Sopenharmony_ci
26262306a36Sopenharmony_ci		for (i = 0; i < chip->ecc.bytes + 4; i++)
26362306a36Sopenharmony_ci			nbitflips += hweight8(corrbuf[i] ^ rawbuf[i]);
26462306a36Sopenharmony_ci
26562306a36Sopenharmony_ci		if (WARN_ON(nbitflips > chip->ecc.strength))
26662306a36Sopenharmony_ci			return -EINVAL;
26762306a36Sopenharmony_ci
26862306a36Sopenharmony_ci		max_bitflips = max(nbitflips, max_bitflips);
26962306a36Sopenharmony_ci		mtd->ecc_stats.corrected += nbitflips;
27062306a36Sopenharmony_ci	}
27162306a36Sopenharmony_ci
27262306a36Sopenharmony_ci	return max_bitflips;
27362306a36Sopenharmony_ci}
27462306a36Sopenharmony_ci
27562306a36Sopenharmony_cistatic int micron_nand_on_die_ecc_status_8(struct nand_chip *chip, u8 status)
27662306a36Sopenharmony_ci{
27762306a36Sopenharmony_ci	struct mtd_info *mtd = nand_to_mtd(chip);
27862306a36Sopenharmony_ci
27962306a36Sopenharmony_ci	/*
28062306a36Sopenharmony_ci	 * With 8/512 we have more information but still don't know precisely
28162306a36Sopenharmony_ci	 * how many bit-flips were seen.
28262306a36Sopenharmony_ci	 */
28362306a36Sopenharmony_ci	switch (status & NAND_ECC_STATUS_MASK) {
28462306a36Sopenharmony_ci	case NAND_ECC_STATUS_UNCORRECTABLE:
28562306a36Sopenharmony_ci		mtd->ecc_stats.failed++;
28662306a36Sopenharmony_ci		return 0;
28762306a36Sopenharmony_ci	case NAND_ECC_STATUS_1_3_CORRECTED:
28862306a36Sopenharmony_ci		mtd->ecc_stats.corrected += 3;
28962306a36Sopenharmony_ci		return 3;
29062306a36Sopenharmony_ci	case NAND_ECC_STATUS_4_6_CORRECTED:
29162306a36Sopenharmony_ci		mtd->ecc_stats.corrected += 6;
29262306a36Sopenharmony_ci		/* rewrite recommended */
29362306a36Sopenharmony_ci		return 6;
29462306a36Sopenharmony_ci	case NAND_ECC_STATUS_7_8_CORRECTED:
29562306a36Sopenharmony_ci		mtd->ecc_stats.corrected += 8;
29662306a36Sopenharmony_ci		/* rewrite recommended */
29762306a36Sopenharmony_ci		return 8;
29862306a36Sopenharmony_ci	default:
29962306a36Sopenharmony_ci		return 0;
30062306a36Sopenharmony_ci	}
30162306a36Sopenharmony_ci}
30262306a36Sopenharmony_ci
30362306a36Sopenharmony_cistatic int
30462306a36Sopenharmony_cimicron_nand_read_page_on_die_ecc(struct nand_chip *chip, uint8_t *buf,
30562306a36Sopenharmony_ci				 int oob_required, int page)
30662306a36Sopenharmony_ci{
30762306a36Sopenharmony_ci	struct mtd_info *mtd = nand_to_mtd(chip);
30862306a36Sopenharmony_ci	bool use_datain = false;
30962306a36Sopenharmony_ci	u8 status;
31062306a36Sopenharmony_ci	int ret, max_bitflips = 0;
31162306a36Sopenharmony_ci
31262306a36Sopenharmony_ci	ret = micron_nand_on_die_ecc_setup(chip, true);
31362306a36Sopenharmony_ci	if (ret)
31462306a36Sopenharmony_ci		return ret;
31562306a36Sopenharmony_ci
31662306a36Sopenharmony_ci	ret = nand_read_page_op(chip, page, 0, NULL, 0);
31762306a36Sopenharmony_ci	if (ret)
31862306a36Sopenharmony_ci		goto out;
31962306a36Sopenharmony_ci
32062306a36Sopenharmony_ci	ret = nand_status_op(chip, &status);
32162306a36Sopenharmony_ci	if (ret)
32262306a36Sopenharmony_ci		goto out;
32362306a36Sopenharmony_ci
32462306a36Sopenharmony_ci	/*
32562306a36Sopenharmony_ci	 * We first check which operation is supported by the controller before
32662306a36Sopenharmony_ci	 * running it. This trick makes it possible to support all controllers,
32762306a36Sopenharmony_ci	 * even the most constraints, without almost any performance hit.
32862306a36Sopenharmony_ci	 *
32962306a36Sopenharmony_ci	 * TODO: could be enhanced to avoid repeating the same check over and
33062306a36Sopenharmony_ci	 * over in the fast path.
33162306a36Sopenharmony_ci	 */
33262306a36Sopenharmony_ci	if (!nand_has_exec_op(chip) ||
33362306a36Sopenharmony_ci	    !nand_read_data_op(chip, buf, mtd->writesize, false, true))
33462306a36Sopenharmony_ci		use_datain = true;
33562306a36Sopenharmony_ci
33662306a36Sopenharmony_ci	if (use_datain) {
33762306a36Sopenharmony_ci		ret = nand_exit_status_op(chip);
33862306a36Sopenharmony_ci		if (ret)
33962306a36Sopenharmony_ci			goto out;
34062306a36Sopenharmony_ci
34162306a36Sopenharmony_ci		ret = nand_read_data_op(chip, buf, mtd->writesize, false,
34262306a36Sopenharmony_ci					false);
34362306a36Sopenharmony_ci		if (!ret && oob_required)
34462306a36Sopenharmony_ci			ret = nand_read_data_op(chip, chip->oob_poi,
34562306a36Sopenharmony_ci						mtd->oobsize, false, false);
34662306a36Sopenharmony_ci	} else {
34762306a36Sopenharmony_ci		ret = nand_change_read_column_op(chip, 0, buf, mtd->writesize,
34862306a36Sopenharmony_ci						 false);
34962306a36Sopenharmony_ci		if (!ret && oob_required)
35062306a36Sopenharmony_ci			ret = nand_change_read_column_op(chip, mtd->writesize,
35162306a36Sopenharmony_ci							 chip->oob_poi,
35262306a36Sopenharmony_ci							 mtd->oobsize, false);
35362306a36Sopenharmony_ci	}
35462306a36Sopenharmony_ci
35562306a36Sopenharmony_ci	if (chip->ecc.strength == 4)
35662306a36Sopenharmony_ci		max_bitflips = micron_nand_on_die_ecc_status_4(chip, status,
35762306a36Sopenharmony_ci							       buf, page,
35862306a36Sopenharmony_ci							       oob_required);
35962306a36Sopenharmony_ci	else
36062306a36Sopenharmony_ci		max_bitflips = micron_nand_on_die_ecc_status_8(chip, status);
36162306a36Sopenharmony_ci
36262306a36Sopenharmony_ciout:
36362306a36Sopenharmony_ci	micron_nand_on_die_ecc_setup(chip, false);
36462306a36Sopenharmony_ci
36562306a36Sopenharmony_ci	return ret ? ret : max_bitflips;
36662306a36Sopenharmony_ci}
36762306a36Sopenharmony_ci
36862306a36Sopenharmony_cistatic int
36962306a36Sopenharmony_cimicron_nand_write_page_on_die_ecc(struct nand_chip *chip, const uint8_t *buf,
37062306a36Sopenharmony_ci				  int oob_required, int page)
37162306a36Sopenharmony_ci{
37262306a36Sopenharmony_ci	int ret;
37362306a36Sopenharmony_ci
37462306a36Sopenharmony_ci	ret = micron_nand_on_die_ecc_setup(chip, true);
37562306a36Sopenharmony_ci	if (ret)
37662306a36Sopenharmony_ci		return ret;
37762306a36Sopenharmony_ci
37862306a36Sopenharmony_ci	ret = nand_write_page_raw(chip, buf, oob_required, page);
37962306a36Sopenharmony_ci	micron_nand_on_die_ecc_setup(chip, false);
38062306a36Sopenharmony_ci
38162306a36Sopenharmony_ci	return ret;
38262306a36Sopenharmony_ci}
38362306a36Sopenharmony_ci
38462306a36Sopenharmony_cienum {
38562306a36Sopenharmony_ci	/* The NAND flash doesn't support on-die ECC */
38662306a36Sopenharmony_ci	MICRON_ON_DIE_UNSUPPORTED,
38762306a36Sopenharmony_ci
38862306a36Sopenharmony_ci	/*
38962306a36Sopenharmony_ci	 * The NAND flash supports on-die ECC and it can be
39062306a36Sopenharmony_ci	 * enabled/disabled by a set features command.
39162306a36Sopenharmony_ci	 */
39262306a36Sopenharmony_ci	MICRON_ON_DIE_SUPPORTED,
39362306a36Sopenharmony_ci
39462306a36Sopenharmony_ci	/*
39562306a36Sopenharmony_ci	 * The NAND flash supports on-die ECC, and it cannot be
39662306a36Sopenharmony_ci	 * disabled.
39762306a36Sopenharmony_ci	 */
39862306a36Sopenharmony_ci	MICRON_ON_DIE_MANDATORY,
39962306a36Sopenharmony_ci};
40062306a36Sopenharmony_ci
40162306a36Sopenharmony_ci#define MICRON_ID_INTERNAL_ECC_MASK	GENMASK(1, 0)
40262306a36Sopenharmony_ci#define MICRON_ID_ECC_ENABLED		BIT(7)
40362306a36Sopenharmony_ci
40462306a36Sopenharmony_ci/*
40562306a36Sopenharmony_ci * Try to detect if the NAND support on-die ECC. To do this, we enable
40662306a36Sopenharmony_ci * the feature, and read back if it has been enabled as expected. We
40762306a36Sopenharmony_ci * also check if it can be disabled, because some Micron NANDs do not
40862306a36Sopenharmony_ci * allow disabling the on-die ECC and we don't support such NANDs for
40962306a36Sopenharmony_ci * now.
41062306a36Sopenharmony_ci *
41162306a36Sopenharmony_ci * This function also has the side effect of disabling on-die ECC if
41262306a36Sopenharmony_ci * it had been left enabled by the firmware/bootloader.
41362306a36Sopenharmony_ci */
41462306a36Sopenharmony_cistatic int micron_supports_on_die_ecc(struct nand_chip *chip)
41562306a36Sopenharmony_ci{
41662306a36Sopenharmony_ci	const struct nand_ecc_props *requirements =
41762306a36Sopenharmony_ci		nanddev_get_ecc_requirements(&chip->base);
41862306a36Sopenharmony_ci	u8 id[5];
41962306a36Sopenharmony_ci	int ret;
42062306a36Sopenharmony_ci
42162306a36Sopenharmony_ci	if (!chip->parameters.onfi)
42262306a36Sopenharmony_ci		return MICRON_ON_DIE_UNSUPPORTED;
42362306a36Sopenharmony_ci
42462306a36Sopenharmony_ci	if (nanddev_bits_per_cell(&chip->base) != 1)
42562306a36Sopenharmony_ci		return MICRON_ON_DIE_UNSUPPORTED;
42662306a36Sopenharmony_ci
42762306a36Sopenharmony_ci	/*
42862306a36Sopenharmony_ci	 * We only support on-die ECC of 4/512 or 8/512
42962306a36Sopenharmony_ci	 */
43062306a36Sopenharmony_ci	if  (requirements->strength != 4 && requirements->strength != 8)
43162306a36Sopenharmony_ci		return MICRON_ON_DIE_UNSUPPORTED;
43262306a36Sopenharmony_ci
43362306a36Sopenharmony_ci	/* 0x2 means on-die ECC is available. */
43462306a36Sopenharmony_ci	if (chip->id.len != 5 ||
43562306a36Sopenharmony_ci	    (chip->id.data[4] & MICRON_ID_INTERNAL_ECC_MASK) != 0x2)
43662306a36Sopenharmony_ci		return MICRON_ON_DIE_UNSUPPORTED;
43762306a36Sopenharmony_ci
43862306a36Sopenharmony_ci	/*
43962306a36Sopenharmony_ci	 * It seems that there are devices which do not support ECC officially.
44062306a36Sopenharmony_ci	 * At least the MT29F2G08ABAGA / MT29F2G08ABBGA devices supports
44162306a36Sopenharmony_ci	 * enabling the ECC feature but don't reflect that to the READ_ID table.
44262306a36Sopenharmony_ci	 * So we have to guarantee that we disable the ECC feature directly
44362306a36Sopenharmony_ci	 * after we did the READ_ID table command. Later we can evaluate the
44462306a36Sopenharmony_ci	 * ECC_ENABLE support.
44562306a36Sopenharmony_ci	 */
44662306a36Sopenharmony_ci	ret = micron_nand_on_die_ecc_setup(chip, true);
44762306a36Sopenharmony_ci	if (ret)
44862306a36Sopenharmony_ci		return MICRON_ON_DIE_UNSUPPORTED;
44962306a36Sopenharmony_ci
45062306a36Sopenharmony_ci	ret = nand_readid_op(chip, 0, id, sizeof(id));
45162306a36Sopenharmony_ci	if (ret)
45262306a36Sopenharmony_ci		return MICRON_ON_DIE_UNSUPPORTED;
45362306a36Sopenharmony_ci
45462306a36Sopenharmony_ci	ret = micron_nand_on_die_ecc_setup(chip, false);
45562306a36Sopenharmony_ci	if (ret)
45662306a36Sopenharmony_ci		return MICRON_ON_DIE_UNSUPPORTED;
45762306a36Sopenharmony_ci
45862306a36Sopenharmony_ci	if (!(id[4] & MICRON_ID_ECC_ENABLED))
45962306a36Sopenharmony_ci		return MICRON_ON_DIE_UNSUPPORTED;
46062306a36Sopenharmony_ci
46162306a36Sopenharmony_ci	ret = nand_readid_op(chip, 0, id, sizeof(id));
46262306a36Sopenharmony_ci	if (ret)
46362306a36Sopenharmony_ci		return MICRON_ON_DIE_UNSUPPORTED;
46462306a36Sopenharmony_ci
46562306a36Sopenharmony_ci	if (id[4] & MICRON_ID_ECC_ENABLED)
46662306a36Sopenharmony_ci		return MICRON_ON_DIE_MANDATORY;
46762306a36Sopenharmony_ci
46862306a36Sopenharmony_ci	/*
46962306a36Sopenharmony_ci	 * We only support on-die ECC of 4/512 or 8/512
47062306a36Sopenharmony_ci	 */
47162306a36Sopenharmony_ci	if  (requirements->strength != 4 && requirements->strength != 8)
47262306a36Sopenharmony_ci		return MICRON_ON_DIE_UNSUPPORTED;
47362306a36Sopenharmony_ci
47462306a36Sopenharmony_ci	return MICRON_ON_DIE_SUPPORTED;
47562306a36Sopenharmony_ci}
47662306a36Sopenharmony_ci
47762306a36Sopenharmony_cistatic int micron_nand_init(struct nand_chip *chip)
47862306a36Sopenharmony_ci{
47962306a36Sopenharmony_ci	struct nand_device *base = &chip->base;
48062306a36Sopenharmony_ci	const struct nand_ecc_props *requirements =
48162306a36Sopenharmony_ci		nanddev_get_ecc_requirements(base);
48262306a36Sopenharmony_ci	struct mtd_info *mtd = nand_to_mtd(chip);
48362306a36Sopenharmony_ci	struct micron_nand *micron;
48462306a36Sopenharmony_ci	int ondie;
48562306a36Sopenharmony_ci	int ret;
48662306a36Sopenharmony_ci
48762306a36Sopenharmony_ci	micron = kzalloc(sizeof(*micron), GFP_KERNEL);
48862306a36Sopenharmony_ci	if (!micron)
48962306a36Sopenharmony_ci		return -ENOMEM;
49062306a36Sopenharmony_ci
49162306a36Sopenharmony_ci	nand_set_manufacturer_data(chip, micron);
49262306a36Sopenharmony_ci
49362306a36Sopenharmony_ci	ret = micron_nand_onfi_init(chip);
49462306a36Sopenharmony_ci	if (ret)
49562306a36Sopenharmony_ci		goto err_free_manuf_data;
49662306a36Sopenharmony_ci
49762306a36Sopenharmony_ci	chip->options |= NAND_BBM_FIRSTPAGE;
49862306a36Sopenharmony_ci
49962306a36Sopenharmony_ci	if (mtd->writesize == 2048)
50062306a36Sopenharmony_ci		chip->options |= NAND_BBM_SECONDPAGE;
50162306a36Sopenharmony_ci
50262306a36Sopenharmony_ci	ondie = micron_supports_on_die_ecc(chip);
50362306a36Sopenharmony_ci
50462306a36Sopenharmony_ci	if (ondie == MICRON_ON_DIE_MANDATORY &&
50562306a36Sopenharmony_ci	    chip->ecc.engine_type != NAND_ECC_ENGINE_TYPE_ON_DIE) {
50662306a36Sopenharmony_ci		pr_err("On-die ECC forcefully enabled, not supported\n");
50762306a36Sopenharmony_ci		ret = -EINVAL;
50862306a36Sopenharmony_ci		goto err_free_manuf_data;
50962306a36Sopenharmony_ci	}
51062306a36Sopenharmony_ci
51162306a36Sopenharmony_ci	if (chip->ecc.engine_type == NAND_ECC_ENGINE_TYPE_ON_DIE) {
51262306a36Sopenharmony_ci		if (ondie == MICRON_ON_DIE_UNSUPPORTED) {
51362306a36Sopenharmony_ci			pr_err("On-die ECC selected but not supported\n");
51462306a36Sopenharmony_ci			ret = -EINVAL;
51562306a36Sopenharmony_ci			goto err_free_manuf_data;
51662306a36Sopenharmony_ci		}
51762306a36Sopenharmony_ci
51862306a36Sopenharmony_ci		if (ondie == MICRON_ON_DIE_MANDATORY) {
51962306a36Sopenharmony_ci			micron->ecc.forced = true;
52062306a36Sopenharmony_ci			micron->ecc.enabled = true;
52162306a36Sopenharmony_ci		}
52262306a36Sopenharmony_ci
52362306a36Sopenharmony_ci		/*
52462306a36Sopenharmony_ci		 * In case of 4bit on-die ECC, we need a buffer to store a
52562306a36Sopenharmony_ci		 * page dumped in raw mode so that we can compare its content
52662306a36Sopenharmony_ci		 * to the same page after ECC correction happened and extract
52762306a36Sopenharmony_ci		 * the real number of bitflips from this comparison.
52862306a36Sopenharmony_ci		 * That's not needed for 8-bit ECC, because the status expose
52962306a36Sopenharmony_ci		 * a better approximation of the number of bitflips in a page.
53062306a36Sopenharmony_ci		 */
53162306a36Sopenharmony_ci		if (requirements->strength == 4) {
53262306a36Sopenharmony_ci			micron->ecc.rawbuf = kmalloc(mtd->writesize +
53362306a36Sopenharmony_ci						     mtd->oobsize,
53462306a36Sopenharmony_ci						     GFP_KERNEL);
53562306a36Sopenharmony_ci			if (!micron->ecc.rawbuf) {
53662306a36Sopenharmony_ci				ret = -ENOMEM;
53762306a36Sopenharmony_ci				goto err_free_manuf_data;
53862306a36Sopenharmony_ci			}
53962306a36Sopenharmony_ci		}
54062306a36Sopenharmony_ci
54162306a36Sopenharmony_ci		if (requirements->strength == 4)
54262306a36Sopenharmony_ci			mtd_set_ooblayout(mtd,
54362306a36Sopenharmony_ci					  &micron_nand_on_die_4_ooblayout_ops);
54462306a36Sopenharmony_ci		else
54562306a36Sopenharmony_ci			mtd_set_ooblayout(mtd,
54662306a36Sopenharmony_ci					  &micron_nand_on_die_8_ooblayout_ops);
54762306a36Sopenharmony_ci
54862306a36Sopenharmony_ci		chip->ecc.bytes = requirements->strength * 2;
54962306a36Sopenharmony_ci		chip->ecc.size = 512;
55062306a36Sopenharmony_ci		chip->ecc.strength = requirements->strength;
55162306a36Sopenharmony_ci		chip->ecc.algo = NAND_ECC_ALGO_BCH;
55262306a36Sopenharmony_ci		chip->ecc.read_page = micron_nand_read_page_on_die_ecc;
55362306a36Sopenharmony_ci		chip->ecc.write_page = micron_nand_write_page_on_die_ecc;
55462306a36Sopenharmony_ci
55562306a36Sopenharmony_ci		if (ondie == MICRON_ON_DIE_MANDATORY) {
55662306a36Sopenharmony_ci			chip->ecc.read_page_raw = nand_read_page_raw_notsupp;
55762306a36Sopenharmony_ci			chip->ecc.write_page_raw = nand_write_page_raw_notsupp;
55862306a36Sopenharmony_ci		} else {
55962306a36Sopenharmony_ci			if (!chip->ecc.read_page_raw)
56062306a36Sopenharmony_ci				chip->ecc.read_page_raw = nand_read_page_raw;
56162306a36Sopenharmony_ci			if (!chip->ecc.write_page_raw)
56262306a36Sopenharmony_ci				chip->ecc.write_page_raw = nand_write_page_raw;
56362306a36Sopenharmony_ci		}
56462306a36Sopenharmony_ci	}
56562306a36Sopenharmony_ci
56662306a36Sopenharmony_ci	return 0;
56762306a36Sopenharmony_ci
56862306a36Sopenharmony_cierr_free_manuf_data:
56962306a36Sopenharmony_ci	kfree(micron->ecc.rawbuf);
57062306a36Sopenharmony_ci	kfree(micron);
57162306a36Sopenharmony_ci
57262306a36Sopenharmony_ci	return ret;
57362306a36Sopenharmony_ci}
57462306a36Sopenharmony_ci
57562306a36Sopenharmony_cistatic void micron_nand_cleanup(struct nand_chip *chip)
57662306a36Sopenharmony_ci{
57762306a36Sopenharmony_ci	struct micron_nand *micron = nand_get_manufacturer_data(chip);
57862306a36Sopenharmony_ci
57962306a36Sopenharmony_ci	kfree(micron->ecc.rawbuf);
58062306a36Sopenharmony_ci	kfree(micron);
58162306a36Sopenharmony_ci}
58262306a36Sopenharmony_ci
58362306a36Sopenharmony_cistatic void micron_fixup_onfi_param_page(struct nand_chip *chip,
58462306a36Sopenharmony_ci					 struct nand_onfi_params *p)
58562306a36Sopenharmony_ci{
58662306a36Sopenharmony_ci	/*
58762306a36Sopenharmony_ci	 * MT29F1G08ABAFAWP-ITE:F and possibly others report 00 00 for the
58862306a36Sopenharmony_ci	 * revision number field of the ONFI parameter page. Assume ONFI
58962306a36Sopenharmony_ci	 * version 1.0 if the revision number is 00 00.
59062306a36Sopenharmony_ci	 */
59162306a36Sopenharmony_ci	if (le16_to_cpu(p->revision) == 0)
59262306a36Sopenharmony_ci		p->revision = cpu_to_le16(ONFI_VERSION_1_0);
59362306a36Sopenharmony_ci}
59462306a36Sopenharmony_ci
59562306a36Sopenharmony_ciconst struct nand_manufacturer_ops micron_nand_manuf_ops = {
59662306a36Sopenharmony_ci	.init = micron_nand_init,
59762306a36Sopenharmony_ci	.cleanup = micron_nand_cleanup,
59862306a36Sopenharmony_ci	.fixup_onfi_param_page = micron_fixup_onfi_param_page,
59962306a36Sopenharmony_ci};
600