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 µn_nand_on_die_4_ooblayout_ops); 54462306a36Sopenharmony_ci else 54562306a36Sopenharmony_ci mtd_set_ooblayout(mtd, 54662306a36Sopenharmony_ci µn_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