18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (C) 2017 Free Electrons 48c2ecf20Sopenharmony_ci * Copyright (C) 2017 NextThing Co 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * Author: Boris Brezillon <boris.brezillon@free-electrons.com> 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#include <linux/slab.h> 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci#include "internals.h" 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci/* 148c2ecf20Sopenharmony_ci * Special Micron status bit 3 indicates that the block has been 158c2ecf20Sopenharmony_ci * corrected by on-die ECC and should be rewritten. 168c2ecf20Sopenharmony_ci */ 178c2ecf20Sopenharmony_ci#define NAND_ECC_STATUS_WRITE_RECOMMENDED BIT(3) 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci/* 208c2ecf20Sopenharmony_ci * On chips with 8-bit ECC and additional bit can be used to distinguish 218c2ecf20Sopenharmony_ci * cases where a errors were corrected without needing a rewrite 228c2ecf20Sopenharmony_ci * 238c2ecf20Sopenharmony_ci * Bit 4 Bit 3 Bit 0 Description 248c2ecf20Sopenharmony_ci * ----- ----- ----- ----------- 258c2ecf20Sopenharmony_ci * 0 0 0 No Errors 268c2ecf20Sopenharmony_ci * 0 0 1 Multiple uncorrected errors 278c2ecf20Sopenharmony_ci * 0 1 0 4 - 6 errors corrected, recommend rewrite 288c2ecf20Sopenharmony_ci * 0 1 1 Reserved 298c2ecf20Sopenharmony_ci * 1 0 0 1 - 3 errors corrected 308c2ecf20Sopenharmony_ci * 1 0 1 Reserved 318c2ecf20Sopenharmony_ci * 1 1 0 7 - 8 errors corrected, recommend rewrite 328c2ecf20Sopenharmony_ci */ 338c2ecf20Sopenharmony_ci#define NAND_ECC_STATUS_MASK (BIT(4) | BIT(3) | BIT(0)) 348c2ecf20Sopenharmony_ci#define NAND_ECC_STATUS_UNCORRECTABLE BIT(0) 358c2ecf20Sopenharmony_ci#define NAND_ECC_STATUS_4_6_CORRECTED BIT(3) 368c2ecf20Sopenharmony_ci#define NAND_ECC_STATUS_1_3_CORRECTED BIT(4) 378c2ecf20Sopenharmony_ci#define NAND_ECC_STATUS_7_8_CORRECTED (BIT(4) | BIT(3)) 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_cistruct nand_onfi_vendor_micron { 408c2ecf20Sopenharmony_ci u8 two_plane_read; 418c2ecf20Sopenharmony_ci u8 read_cache; 428c2ecf20Sopenharmony_ci u8 read_unique_id; 438c2ecf20Sopenharmony_ci u8 dq_imped; 448c2ecf20Sopenharmony_ci u8 dq_imped_num_settings; 458c2ecf20Sopenharmony_ci u8 dq_imped_feat_addr; 468c2ecf20Sopenharmony_ci u8 rb_pulldown_strength; 478c2ecf20Sopenharmony_ci u8 rb_pulldown_strength_feat_addr; 488c2ecf20Sopenharmony_ci u8 rb_pulldown_strength_num_settings; 498c2ecf20Sopenharmony_ci u8 otp_mode; 508c2ecf20Sopenharmony_ci u8 otp_page_start; 518c2ecf20Sopenharmony_ci u8 otp_data_prot_addr; 528c2ecf20Sopenharmony_ci u8 otp_num_pages; 538c2ecf20Sopenharmony_ci u8 otp_feat_addr; 548c2ecf20Sopenharmony_ci u8 read_retry_options; 558c2ecf20Sopenharmony_ci u8 reserved[72]; 568c2ecf20Sopenharmony_ci u8 param_revision; 578c2ecf20Sopenharmony_ci} __packed; 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_cistruct micron_on_die_ecc { 608c2ecf20Sopenharmony_ci bool forced; 618c2ecf20Sopenharmony_ci bool enabled; 628c2ecf20Sopenharmony_ci void *rawbuf; 638c2ecf20Sopenharmony_ci}; 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_cistruct micron_nand { 668c2ecf20Sopenharmony_ci struct micron_on_die_ecc ecc; 678c2ecf20Sopenharmony_ci}; 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_cistatic int micron_nand_setup_read_retry(struct nand_chip *chip, int retry_mode) 708c2ecf20Sopenharmony_ci{ 718c2ecf20Sopenharmony_ci u8 feature[ONFI_SUBFEATURE_PARAM_LEN] = {retry_mode}; 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci return nand_set_features(chip, ONFI_FEATURE_ADDR_READ_RETRY, feature); 748c2ecf20Sopenharmony_ci} 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci/* 778c2ecf20Sopenharmony_ci * Configure chip properties from Micron vendor-specific ONFI table 788c2ecf20Sopenharmony_ci */ 798c2ecf20Sopenharmony_cistatic int micron_nand_onfi_init(struct nand_chip *chip) 808c2ecf20Sopenharmony_ci{ 818c2ecf20Sopenharmony_ci struct nand_parameters *p = &chip->parameters; 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci if (p->onfi) { 848c2ecf20Sopenharmony_ci struct nand_onfi_vendor_micron *micron = (void *)p->onfi->vendor; 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci chip->read_retries = micron->read_retry_options; 878c2ecf20Sopenharmony_ci chip->ops.setup_read_retry = micron_nand_setup_read_retry; 888c2ecf20Sopenharmony_ci } 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci if (p->supports_set_get_features) { 918c2ecf20Sopenharmony_ci set_bit(ONFI_FEATURE_ADDR_READ_RETRY, p->set_feature_list); 928c2ecf20Sopenharmony_ci set_bit(ONFI_FEATURE_ON_DIE_ECC, p->set_feature_list); 938c2ecf20Sopenharmony_ci set_bit(ONFI_FEATURE_ADDR_READ_RETRY, p->get_feature_list); 948c2ecf20Sopenharmony_ci set_bit(ONFI_FEATURE_ON_DIE_ECC, p->get_feature_list); 958c2ecf20Sopenharmony_ci } 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci return 0; 988c2ecf20Sopenharmony_ci} 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_cistatic int micron_nand_on_die_4_ooblayout_ecc(struct mtd_info *mtd, 1018c2ecf20Sopenharmony_ci int section, 1028c2ecf20Sopenharmony_ci struct mtd_oob_region *oobregion) 1038c2ecf20Sopenharmony_ci{ 1048c2ecf20Sopenharmony_ci if (section >= 4) 1058c2ecf20Sopenharmony_ci return -ERANGE; 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci oobregion->offset = (section * 16) + 8; 1088c2ecf20Sopenharmony_ci oobregion->length = 8; 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci return 0; 1118c2ecf20Sopenharmony_ci} 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_cistatic int micron_nand_on_die_4_ooblayout_free(struct mtd_info *mtd, 1148c2ecf20Sopenharmony_ci int section, 1158c2ecf20Sopenharmony_ci struct mtd_oob_region *oobregion) 1168c2ecf20Sopenharmony_ci{ 1178c2ecf20Sopenharmony_ci if (section >= 4) 1188c2ecf20Sopenharmony_ci return -ERANGE; 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci oobregion->offset = (section * 16) + 2; 1218c2ecf20Sopenharmony_ci oobregion->length = 6; 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci return 0; 1248c2ecf20Sopenharmony_ci} 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_cistatic const struct mtd_ooblayout_ops micron_nand_on_die_4_ooblayout_ops = { 1278c2ecf20Sopenharmony_ci .ecc = micron_nand_on_die_4_ooblayout_ecc, 1288c2ecf20Sopenharmony_ci .free = micron_nand_on_die_4_ooblayout_free, 1298c2ecf20Sopenharmony_ci}; 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_cistatic int micron_nand_on_die_8_ooblayout_ecc(struct mtd_info *mtd, 1328c2ecf20Sopenharmony_ci int section, 1338c2ecf20Sopenharmony_ci struct mtd_oob_region *oobregion) 1348c2ecf20Sopenharmony_ci{ 1358c2ecf20Sopenharmony_ci struct nand_chip *chip = mtd_to_nand(mtd); 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci if (section) 1388c2ecf20Sopenharmony_ci return -ERANGE; 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci oobregion->offset = mtd->oobsize - chip->ecc.total; 1418c2ecf20Sopenharmony_ci oobregion->length = chip->ecc.total; 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci return 0; 1448c2ecf20Sopenharmony_ci} 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_cistatic int micron_nand_on_die_8_ooblayout_free(struct mtd_info *mtd, 1478c2ecf20Sopenharmony_ci int section, 1488c2ecf20Sopenharmony_ci struct mtd_oob_region *oobregion) 1498c2ecf20Sopenharmony_ci{ 1508c2ecf20Sopenharmony_ci struct nand_chip *chip = mtd_to_nand(mtd); 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci if (section) 1538c2ecf20Sopenharmony_ci return -ERANGE; 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci oobregion->offset = 2; 1568c2ecf20Sopenharmony_ci oobregion->length = mtd->oobsize - chip->ecc.total - 2; 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci return 0; 1598c2ecf20Sopenharmony_ci} 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_cistatic const struct mtd_ooblayout_ops micron_nand_on_die_8_ooblayout_ops = { 1628c2ecf20Sopenharmony_ci .ecc = micron_nand_on_die_8_ooblayout_ecc, 1638c2ecf20Sopenharmony_ci .free = micron_nand_on_die_8_ooblayout_free, 1648c2ecf20Sopenharmony_ci}; 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_cistatic int micron_nand_on_die_ecc_setup(struct nand_chip *chip, bool enable) 1678c2ecf20Sopenharmony_ci{ 1688c2ecf20Sopenharmony_ci struct micron_nand *micron = nand_get_manufacturer_data(chip); 1698c2ecf20Sopenharmony_ci u8 feature[ONFI_SUBFEATURE_PARAM_LEN] = { 0, }; 1708c2ecf20Sopenharmony_ci int ret; 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci if (micron->ecc.forced) 1738c2ecf20Sopenharmony_ci return 0; 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci if (micron->ecc.enabled == enable) 1768c2ecf20Sopenharmony_ci return 0; 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci if (enable) 1798c2ecf20Sopenharmony_ci feature[0] |= ONFI_FEATURE_ON_DIE_ECC_EN; 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci ret = nand_set_features(chip, ONFI_FEATURE_ON_DIE_ECC, feature); 1828c2ecf20Sopenharmony_ci if (!ret) 1838c2ecf20Sopenharmony_ci micron->ecc.enabled = enable; 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci return ret; 1868c2ecf20Sopenharmony_ci} 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_cistatic int micron_nand_on_die_ecc_status_4(struct nand_chip *chip, u8 status, 1898c2ecf20Sopenharmony_ci void *buf, int page, 1908c2ecf20Sopenharmony_ci int oob_required) 1918c2ecf20Sopenharmony_ci{ 1928c2ecf20Sopenharmony_ci struct micron_nand *micron = nand_get_manufacturer_data(chip); 1938c2ecf20Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(chip); 1948c2ecf20Sopenharmony_ci unsigned int step, max_bitflips = 0; 1958c2ecf20Sopenharmony_ci bool use_datain = false; 1968c2ecf20Sopenharmony_ci int ret; 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci if (!(status & NAND_ECC_STATUS_WRITE_RECOMMENDED)) { 1998c2ecf20Sopenharmony_ci if (status & NAND_STATUS_FAIL) 2008c2ecf20Sopenharmony_ci mtd->ecc_stats.failed++; 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci return 0; 2038c2ecf20Sopenharmony_ci } 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci /* 2068c2ecf20Sopenharmony_ci * The internal ECC doesn't tell us the number of bitflips that have 2078c2ecf20Sopenharmony_ci * been corrected, but tells us if it recommends to rewrite the block. 2088c2ecf20Sopenharmony_ci * If it's the case, we need to read the page in raw mode and compare 2098c2ecf20Sopenharmony_ci * its content to the corrected version to extract the actual number of 2108c2ecf20Sopenharmony_ci * bitflips. 2118c2ecf20Sopenharmony_ci * But before we do that, we must make sure we have all OOB bytes read 2128c2ecf20Sopenharmony_ci * in non-raw mode, even if the user did not request those bytes. 2138c2ecf20Sopenharmony_ci */ 2148c2ecf20Sopenharmony_ci if (!oob_required) { 2158c2ecf20Sopenharmony_ci /* 2168c2ecf20Sopenharmony_ci * We first check which operation is supported by the controller 2178c2ecf20Sopenharmony_ci * before running it. This trick makes it possible to support 2188c2ecf20Sopenharmony_ci * all controllers, even the most constraints, without almost 2198c2ecf20Sopenharmony_ci * any performance hit. 2208c2ecf20Sopenharmony_ci * 2218c2ecf20Sopenharmony_ci * TODO: could be enhanced to avoid repeating the same check 2228c2ecf20Sopenharmony_ci * over and over in the fast path. 2238c2ecf20Sopenharmony_ci */ 2248c2ecf20Sopenharmony_ci if (!nand_has_exec_op(chip) || 2258c2ecf20Sopenharmony_ci !nand_read_data_op(chip, chip->oob_poi, mtd->oobsize, false, 2268c2ecf20Sopenharmony_ci true)) 2278c2ecf20Sopenharmony_ci use_datain = true; 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci if (use_datain) 2308c2ecf20Sopenharmony_ci ret = nand_read_data_op(chip, chip->oob_poi, 2318c2ecf20Sopenharmony_ci mtd->oobsize, false, false); 2328c2ecf20Sopenharmony_ci else 2338c2ecf20Sopenharmony_ci ret = nand_change_read_column_op(chip, mtd->writesize, 2348c2ecf20Sopenharmony_ci chip->oob_poi, 2358c2ecf20Sopenharmony_ci mtd->oobsize, false); 2368c2ecf20Sopenharmony_ci if (ret) 2378c2ecf20Sopenharmony_ci return ret; 2388c2ecf20Sopenharmony_ci } 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_ci micron_nand_on_die_ecc_setup(chip, false); 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci ret = nand_read_page_op(chip, page, 0, micron->ecc.rawbuf, 2438c2ecf20Sopenharmony_ci mtd->writesize + mtd->oobsize); 2448c2ecf20Sopenharmony_ci if (ret) 2458c2ecf20Sopenharmony_ci return ret; 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci for (step = 0; step < chip->ecc.steps; step++) { 2488c2ecf20Sopenharmony_ci unsigned int offs, i, nbitflips = 0; 2498c2ecf20Sopenharmony_ci u8 *rawbuf, *corrbuf; 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci offs = step * chip->ecc.size; 2528c2ecf20Sopenharmony_ci rawbuf = micron->ecc.rawbuf + offs; 2538c2ecf20Sopenharmony_ci corrbuf = buf + offs; 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci for (i = 0; i < chip->ecc.size; i++) 2568c2ecf20Sopenharmony_ci nbitflips += hweight8(corrbuf[i] ^ rawbuf[i]); 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_ci offs = (step * 16) + 4; 2598c2ecf20Sopenharmony_ci rawbuf = micron->ecc.rawbuf + mtd->writesize + offs; 2608c2ecf20Sopenharmony_ci corrbuf = chip->oob_poi + offs; 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci for (i = 0; i < chip->ecc.bytes + 4; i++) 2638c2ecf20Sopenharmony_ci nbitflips += hweight8(corrbuf[i] ^ rawbuf[i]); 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ci if (WARN_ON(nbitflips > chip->ecc.strength)) 2668c2ecf20Sopenharmony_ci return -EINVAL; 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ci max_bitflips = max(nbitflips, max_bitflips); 2698c2ecf20Sopenharmony_ci mtd->ecc_stats.corrected += nbitflips; 2708c2ecf20Sopenharmony_ci } 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci return max_bitflips; 2738c2ecf20Sopenharmony_ci} 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_cistatic int micron_nand_on_die_ecc_status_8(struct nand_chip *chip, u8 status) 2768c2ecf20Sopenharmony_ci{ 2778c2ecf20Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(chip); 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_ci /* 2808c2ecf20Sopenharmony_ci * With 8/512 we have more information but still don't know precisely 2818c2ecf20Sopenharmony_ci * how many bit-flips were seen. 2828c2ecf20Sopenharmony_ci */ 2838c2ecf20Sopenharmony_ci switch (status & NAND_ECC_STATUS_MASK) { 2848c2ecf20Sopenharmony_ci case NAND_ECC_STATUS_UNCORRECTABLE: 2858c2ecf20Sopenharmony_ci mtd->ecc_stats.failed++; 2868c2ecf20Sopenharmony_ci return 0; 2878c2ecf20Sopenharmony_ci case NAND_ECC_STATUS_1_3_CORRECTED: 2888c2ecf20Sopenharmony_ci mtd->ecc_stats.corrected += 3; 2898c2ecf20Sopenharmony_ci return 3; 2908c2ecf20Sopenharmony_ci case NAND_ECC_STATUS_4_6_CORRECTED: 2918c2ecf20Sopenharmony_ci mtd->ecc_stats.corrected += 6; 2928c2ecf20Sopenharmony_ci /* rewrite recommended */ 2938c2ecf20Sopenharmony_ci return 6; 2948c2ecf20Sopenharmony_ci case NAND_ECC_STATUS_7_8_CORRECTED: 2958c2ecf20Sopenharmony_ci mtd->ecc_stats.corrected += 8; 2968c2ecf20Sopenharmony_ci /* rewrite recommended */ 2978c2ecf20Sopenharmony_ci return 8; 2988c2ecf20Sopenharmony_ci default: 2998c2ecf20Sopenharmony_ci return 0; 3008c2ecf20Sopenharmony_ci } 3018c2ecf20Sopenharmony_ci} 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_cistatic int 3048c2ecf20Sopenharmony_cimicron_nand_read_page_on_die_ecc(struct nand_chip *chip, uint8_t *buf, 3058c2ecf20Sopenharmony_ci int oob_required, int page) 3068c2ecf20Sopenharmony_ci{ 3078c2ecf20Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(chip); 3088c2ecf20Sopenharmony_ci bool use_datain = false; 3098c2ecf20Sopenharmony_ci u8 status; 3108c2ecf20Sopenharmony_ci int ret, max_bitflips = 0; 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_ci ret = micron_nand_on_die_ecc_setup(chip, true); 3138c2ecf20Sopenharmony_ci if (ret) 3148c2ecf20Sopenharmony_ci return ret; 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_ci ret = nand_read_page_op(chip, page, 0, NULL, 0); 3178c2ecf20Sopenharmony_ci if (ret) 3188c2ecf20Sopenharmony_ci goto out; 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_ci ret = nand_status_op(chip, &status); 3218c2ecf20Sopenharmony_ci if (ret) 3228c2ecf20Sopenharmony_ci goto out; 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_ci /* 3258c2ecf20Sopenharmony_ci * We first check which operation is supported by the controller before 3268c2ecf20Sopenharmony_ci * running it. This trick makes it possible to support all controllers, 3278c2ecf20Sopenharmony_ci * even the most constraints, without almost any performance hit. 3288c2ecf20Sopenharmony_ci * 3298c2ecf20Sopenharmony_ci * TODO: could be enhanced to avoid repeating the same check over and 3308c2ecf20Sopenharmony_ci * over in the fast path. 3318c2ecf20Sopenharmony_ci */ 3328c2ecf20Sopenharmony_ci if (!nand_has_exec_op(chip) || 3338c2ecf20Sopenharmony_ci !nand_read_data_op(chip, buf, mtd->writesize, false, true)) 3348c2ecf20Sopenharmony_ci use_datain = true; 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_ci if (use_datain) { 3378c2ecf20Sopenharmony_ci ret = nand_exit_status_op(chip); 3388c2ecf20Sopenharmony_ci if (ret) 3398c2ecf20Sopenharmony_ci goto out; 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_ci ret = nand_read_data_op(chip, buf, mtd->writesize, false, 3428c2ecf20Sopenharmony_ci false); 3438c2ecf20Sopenharmony_ci if (!ret && oob_required) 3448c2ecf20Sopenharmony_ci ret = nand_read_data_op(chip, chip->oob_poi, 3458c2ecf20Sopenharmony_ci mtd->oobsize, false, false); 3468c2ecf20Sopenharmony_ci } else { 3478c2ecf20Sopenharmony_ci ret = nand_change_read_column_op(chip, 0, buf, mtd->writesize, 3488c2ecf20Sopenharmony_ci false); 3498c2ecf20Sopenharmony_ci if (!ret && oob_required) 3508c2ecf20Sopenharmony_ci ret = nand_change_read_column_op(chip, mtd->writesize, 3518c2ecf20Sopenharmony_ci chip->oob_poi, 3528c2ecf20Sopenharmony_ci mtd->oobsize, false); 3538c2ecf20Sopenharmony_ci } 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_ci if (chip->ecc.strength == 4) 3568c2ecf20Sopenharmony_ci max_bitflips = micron_nand_on_die_ecc_status_4(chip, status, 3578c2ecf20Sopenharmony_ci buf, page, 3588c2ecf20Sopenharmony_ci oob_required); 3598c2ecf20Sopenharmony_ci else 3608c2ecf20Sopenharmony_ci max_bitflips = micron_nand_on_die_ecc_status_8(chip, status); 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ciout: 3638c2ecf20Sopenharmony_ci micron_nand_on_die_ecc_setup(chip, false); 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_ci return ret ? ret : max_bitflips; 3668c2ecf20Sopenharmony_ci} 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_cistatic int 3698c2ecf20Sopenharmony_cimicron_nand_write_page_on_die_ecc(struct nand_chip *chip, const uint8_t *buf, 3708c2ecf20Sopenharmony_ci int oob_required, int page) 3718c2ecf20Sopenharmony_ci{ 3728c2ecf20Sopenharmony_ci int ret; 3738c2ecf20Sopenharmony_ci 3748c2ecf20Sopenharmony_ci ret = micron_nand_on_die_ecc_setup(chip, true); 3758c2ecf20Sopenharmony_ci if (ret) 3768c2ecf20Sopenharmony_ci return ret; 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_ci ret = nand_write_page_raw(chip, buf, oob_required, page); 3798c2ecf20Sopenharmony_ci micron_nand_on_die_ecc_setup(chip, false); 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_ci return ret; 3828c2ecf20Sopenharmony_ci} 3838c2ecf20Sopenharmony_ci 3848c2ecf20Sopenharmony_cienum { 3858c2ecf20Sopenharmony_ci /* The NAND flash doesn't support on-die ECC */ 3868c2ecf20Sopenharmony_ci MICRON_ON_DIE_UNSUPPORTED, 3878c2ecf20Sopenharmony_ci 3888c2ecf20Sopenharmony_ci /* 3898c2ecf20Sopenharmony_ci * The NAND flash supports on-die ECC and it can be 3908c2ecf20Sopenharmony_ci * enabled/disabled by a set features command. 3918c2ecf20Sopenharmony_ci */ 3928c2ecf20Sopenharmony_ci MICRON_ON_DIE_SUPPORTED, 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_ci /* 3958c2ecf20Sopenharmony_ci * The NAND flash supports on-die ECC, and it cannot be 3968c2ecf20Sopenharmony_ci * disabled. 3978c2ecf20Sopenharmony_ci */ 3988c2ecf20Sopenharmony_ci MICRON_ON_DIE_MANDATORY, 3998c2ecf20Sopenharmony_ci}; 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_ci#define MICRON_ID_INTERNAL_ECC_MASK GENMASK(1, 0) 4028c2ecf20Sopenharmony_ci#define MICRON_ID_ECC_ENABLED BIT(7) 4038c2ecf20Sopenharmony_ci 4048c2ecf20Sopenharmony_ci/* 4058c2ecf20Sopenharmony_ci * Try to detect if the NAND support on-die ECC. To do this, we enable 4068c2ecf20Sopenharmony_ci * the feature, and read back if it has been enabled as expected. We 4078c2ecf20Sopenharmony_ci * also check if it can be disabled, because some Micron NANDs do not 4088c2ecf20Sopenharmony_ci * allow disabling the on-die ECC and we don't support such NANDs for 4098c2ecf20Sopenharmony_ci * now. 4108c2ecf20Sopenharmony_ci * 4118c2ecf20Sopenharmony_ci * This function also has the side effect of disabling on-die ECC if 4128c2ecf20Sopenharmony_ci * it had been left enabled by the firmware/bootloader. 4138c2ecf20Sopenharmony_ci */ 4148c2ecf20Sopenharmony_cistatic int micron_supports_on_die_ecc(struct nand_chip *chip) 4158c2ecf20Sopenharmony_ci{ 4168c2ecf20Sopenharmony_ci const struct nand_ecc_props *requirements = 4178c2ecf20Sopenharmony_ci nanddev_get_ecc_requirements(&chip->base); 4188c2ecf20Sopenharmony_ci u8 id[5]; 4198c2ecf20Sopenharmony_ci int ret; 4208c2ecf20Sopenharmony_ci 4218c2ecf20Sopenharmony_ci if (!chip->parameters.onfi) 4228c2ecf20Sopenharmony_ci return MICRON_ON_DIE_UNSUPPORTED; 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_ci if (nanddev_bits_per_cell(&chip->base) != 1) 4258c2ecf20Sopenharmony_ci return MICRON_ON_DIE_UNSUPPORTED; 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_ci /* 4288c2ecf20Sopenharmony_ci * We only support on-die ECC of 4/512 or 8/512 4298c2ecf20Sopenharmony_ci */ 4308c2ecf20Sopenharmony_ci if (requirements->strength != 4 && requirements->strength != 8) 4318c2ecf20Sopenharmony_ci return MICRON_ON_DIE_UNSUPPORTED; 4328c2ecf20Sopenharmony_ci 4338c2ecf20Sopenharmony_ci /* 0x2 means on-die ECC is available. */ 4348c2ecf20Sopenharmony_ci if (chip->id.len != 5 || 4358c2ecf20Sopenharmony_ci (chip->id.data[4] & MICRON_ID_INTERNAL_ECC_MASK) != 0x2) 4368c2ecf20Sopenharmony_ci return MICRON_ON_DIE_UNSUPPORTED; 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_ci /* 4398c2ecf20Sopenharmony_ci * It seems that there are devices which do not support ECC officially. 4408c2ecf20Sopenharmony_ci * At least the MT29F2G08ABAGA / MT29F2G08ABBGA devices supports 4418c2ecf20Sopenharmony_ci * enabling the ECC feature but don't reflect that to the READ_ID table. 4428c2ecf20Sopenharmony_ci * So we have to guarantee that we disable the ECC feature directly 4438c2ecf20Sopenharmony_ci * after we did the READ_ID table command. Later we can evaluate the 4448c2ecf20Sopenharmony_ci * ECC_ENABLE support. 4458c2ecf20Sopenharmony_ci */ 4468c2ecf20Sopenharmony_ci ret = micron_nand_on_die_ecc_setup(chip, true); 4478c2ecf20Sopenharmony_ci if (ret) 4488c2ecf20Sopenharmony_ci return MICRON_ON_DIE_UNSUPPORTED; 4498c2ecf20Sopenharmony_ci 4508c2ecf20Sopenharmony_ci ret = nand_readid_op(chip, 0, id, sizeof(id)); 4518c2ecf20Sopenharmony_ci if (ret) 4528c2ecf20Sopenharmony_ci return MICRON_ON_DIE_UNSUPPORTED; 4538c2ecf20Sopenharmony_ci 4548c2ecf20Sopenharmony_ci ret = micron_nand_on_die_ecc_setup(chip, false); 4558c2ecf20Sopenharmony_ci if (ret) 4568c2ecf20Sopenharmony_ci return MICRON_ON_DIE_UNSUPPORTED; 4578c2ecf20Sopenharmony_ci 4588c2ecf20Sopenharmony_ci if (!(id[4] & MICRON_ID_ECC_ENABLED)) 4598c2ecf20Sopenharmony_ci return MICRON_ON_DIE_UNSUPPORTED; 4608c2ecf20Sopenharmony_ci 4618c2ecf20Sopenharmony_ci ret = nand_readid_op(chip, 0, id, sizeof(id)); 4628c2ecf20Sopenharmony_ci if (ret) 4638c2ecf20Sopenharmony_ci return MICRON_ON_DIE_UNSUPPORTED; 4648c2ecf20Sopenharmony_ci 4658c2ecf20Sopenharmony_ci if (id[4] & MICRON_ID_ECC_ENABLED) 4668c2ecf20Sopenharmony_ci return MICRON_ON_DIE_MANDATORY; 4678c2ecf20Sopenharmony_ci 4688c2ecf20Sopenharmony_ci /* 4698c2ecf20Sopenharmony_ci * We only support on-die ECC of 4/512 or 8/512 4708c2ecf20Sopenharmony_ci */ 4718c2ecf20Sopenharmony_ci if (requirements->strength != 4 && requirements->strength != 8) 4728c2ecf20Sopenharmony_ci return MICRON_ON_DIE_UNSUPPORTED; 4738c2ecf20Sopenharmony_ci 4748c2ecf20Sopenharmony_ci return MICRON_ON_DIE_SUPPORTED; 4758c2ecf20Sopenharmony_ci} 4768c2ecf20Sopenharmony_ci 4778c2ecf20Sopenharmony_cistatic int micron_nand_init(struct nand_chip *chip) 4788c2ecf20Sopenharmony_ci{ 4798c2ecf20Sopenharmony_ci struct nand_device *base = &chip->base; 4808c2ecf20Sopenharmony_ci const struct nand_ecc_props *requirements = 4818c2ecf20Sopenharmony_ci nanddev_get_ecc_requirements(base); 4828c2ecf20Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(chip); 4838c2ecf20Sopenharmony_ci struct micron_nand *micron; 4848c2ecf20Sopenharmony_ci int ondie; 4858c2ecf20Sopenharmony_ci int ret; 4868c2ecf20Sopenharmony_ci 4878c2ecf20Sopenharmony_ci micron = kzalloc(sizeof(*micron), GFP_KERNEL); 4888c2ecf20Sopenharmony_ci if (!micron) 4898c2ecf20Sopenharmony_ci return -ENOMEM; 4908c2ecf20Sopenharmony_ci 4918c2ecf20Sopenharmony_ci nand_set_manufacturer_data(chip, micron); 4928c2ecf20Sopenharmony_ci 4938c2ecf20Sopenharmony_ci ret = micron_nand_onfi_init(chip); 4948c2ecf20Sopenharmony_ci if (ret) 4958c2ecf20Sopenharmony_ci goto err_free_manuf_data; 4968c2ecf20Sopenharmony_ci 4978c2ecf20Sopenharmony_ci chip->options |= NAND_BBM_FIRSTPAGE; 4988c2ecf20Sopenharmony_ci 4998c2ecf20Sopenharmony_ci if (mtd->writesize == 2048) 5008c2ecf20Sopenharmony_ci chip->options |= NAND_BBM_SECONDPAGE; 5018c2ecf20Sopenharmony_ci 5028c2ecf20Sopenharmony_ci ondie = micron_supports_on_die_ecc(chip); 5038c2ecf20Sopenharmony_ci 5048c2ecf20Sopenharmony_ci if (ondie == MICRON_ON_DIE_MANDATORY && 5058c2ecf20Sopenharmony_ci chip->ecc.engine_type != NAND_ECC_ENGINE_TYPE_ON_DIE) { 5068c2ecf20Sopenharmony_ci pr_err("On-die ECC forcefully enabled, not supported\n"); 5078c2ecf20Sopenharmony_ci ret = -EINVAL; 5088c2ecf20Sopenharmony_ci goto err_free_manuf_data; 5098c2ecf20Sopenharmony_ci } 5108c2ecf20Sopenharmony_ci 5118c2ecf20Sopenharmony_ci if (chip->ecc.engine_type == NAND_ECC_ENGINE_TYPE_ON_DIE) { 5128c2ecf20Sopenharmony_ci if (ondie == MICRON_ON_DIE_UNSUPPORTED) { 5138c2ecf20Sopenharmony_ci pr_err("On-die ECC selected but not supported\n"); 5148c2ecf20Sopenharmony_ci ret = -EINVAL; 5158c2ecf20Sopenharmony_ci goto err_free_manuf_data; 5168c2ecf20Sopenharmony_ci } 5178c2ecf20Sopenharmony_ci 5188c2ecf20Sopenharmony_ci if (ondie == MICRON_ON_DIE_MANDATORY) { 5198c2ecf20Sopenharmony_ci micron->ecc.forced = true; 5208c2ecf20Sopenharmony_ci micron->ecc.enabled = true; 5218c2ecf20Sopenharmony_ci } 5228c2ecf20Sopenharmony_ci 5238c2ecf20Sopenharmony_ci /* 5248c2ecf20Sopenharmony_ci * In case of 4bit on-die ECC, we need a buffer to store a 5258c2ecf20Sopenharmony_ci * page dumped in raw mode so that we can compare its content 5268c2ecf20Sopenharmony_ci * to the same page after ECC correction happened and extract 5278c2ecf20Sopenharmony_ci * the real number of bitflips from this comparison. 5288c2ecf20Sopenharmony_ci * That's not needed for 8-bit ECC, because the status expose 5298c2ecf20Sopenharmony_ci * a better approximation of the number of bitflips in a page. 5308c2ecf20Sopenharmony_ci */ 5318c2ecf20Sopenharmony_ci if (requirements->strength == 4) { 5328c2ecf20Sopenharmony_ci micron->ecc.rawbuf = kmalloc(mtd->writesize + 5338c2ecf20Sopenharmony_ci mtd->oobsize, 5348c2ecf20Sopenharmony_ci GFP_KERNEL); 5358c2ecf20Sopenharmony_ci if (!micron->ecc.rawbuf) { 5368c2ecf20Sopenharmony_ci ret = -ENOMEM; 5378c2ecf20Sopenharmony_ci goto err_free_manuf_data; 5388c2ecf20Sopenharmony_ci } 5398c2ecf20Sopenharmony_ci } 5408c2ecf20Sopenharmony_ci 5418c2ecf20Sopenharmony_ci if (requirements->strength == 4) 5428c2ecf20Sopenharmony_ci mtd_set_ooblayout(mtd, 5438c2ecf20Sopenharmony_ci µn_nand_on_die_4_ooblayout_ops); 5448c2ecf20Sopenharmony_ci else 5458c2ecf20Sopenharmony_ci mtd_set_ooblayout(mtd, 5468c2ecf20Sopenharmony_ci µn_nand_on_die_8_ooblayout_ops); 5478c2ecf20Sopenharmony_ci 5488c2ecf20Sopenharmony_ci chip->ecc.bytes = requirements->strength * 2; 5498c2ecf20Sopenharmony_ci chip->ecc.size = 512; 5508c2ecf20Sopenharmony_ci chip->ecc.strength = requirements->strength; 5518c2ecf20Sopenharmony_ci chip->ecc.algo = NAND_ECC_ALGO_BCH; 5528c2ecf20Sopenharmony_ci chip->ecc.read_page = micron_nand_read_page_on_die_ecc; 5538c2ecf20Sopenharmony_ci chip->ecc.write_page = micron_nand_write_page_on_die_ecc; 5548c2ecf20Sopenharmony_ci 5558c2ecf20Sopenharmony_ci if (ondie == MICRON_ON_DIE_MANDATORY) { 5568c2ecf20Sopenharmony_ci chip->ecc.read_page_raw = nand_read_page_raw_notsupp; 5578c2ecf20Sopenharmony_ci chip->ecc.write_page_raw = nand_write_page_raw_notsupp; 5588c2ecf20Sopenharmony_ci } else { 5598c2ecf20Sopenharmony_ci if (!chip->ecc.read_page_raw) 5608c2ecf20Sopenharmony_ci chip->ecc.read_page_raw = nand_read_page_raw; 5618c2ecf20Sopenharmony_ci if (!chip->ecc.write_page_raw) 5628c2ecf20Sopenharmony_ci chip->ecc.write_page_raw = nand_write_page_raw; 5638c2ecf20Sopenharmony_ci } 5648c2ecf20Sopenharmony_ci } 5658c2ecf20Sopenharmony_ci 5668c2ecf20Sopenharmony_ci return 0; 5678c2ecf20Sopenharmony_ci 5688c2ecf20Sopenharmony_cierr_free_manuf_data: 5698c2ecf20Sopenharmony_ci kfree(micron->ecc.rawbuf); 5708c2ecf20Sopenharmony_ci kfree(micron); 5718c2ecf20Sopenharmony_ci 5728c2ecf20Sopenharmony_ci return ret; 5738c2ecf20Sopenharmony_ci} 5748c2ecf20Sopenharmony_ci 5758c2ecf20Sopenharmony_cistatic void micron_nand_cleanup(struct nand_chip *chip) 5768c2ecf20Sopenharmony_ci{ 5778c2ecf20Sopenharmony_ci struct micron_nand *micron = nand_get_manufacturer_data(chip); 5788c2ecf20Sopenharmony_ci 5798c2ecf20Sopenharmony_ci kfree(micron->ecc.rawbuf); 5808c2ecf20Sopenharmony_ci kfree(micron); 5818c2ecf20Sopenharmony_ci} 5828c2ecf20Sopenharmony_ci 5838c2ecf20Sopenharmony_cistatic void micron_fixup_onfi_param_page(struct nand_chip *chip, 5848c2ecf20Sopenharmony_ci struct nand_onfi_params *p) 5858c2ecf20Sopenharmony_ci{ 5868c2ecf20Sopenharmony_ci /* 5878c2ecf20Sopenharmony_ci * MT29F1G08ABAFAWP-ITE:F and possibly others report 00 00 for the 5888c2ecf20Sopenharmony_ci * revision number field of the ONFI parameter page. Assume ONFI 5898c2ecf20Sopenharmony_ci * version 1.0 if the revision number is 00 00. 5908c2ecf20Sopenharmony_ci */ 5918c2ecf20Sopenharmony_ci if (le16_to_cpu(p->revision) == 0) 5928c2ecf20Sopenharmony_ci p->revision = cpu_to_le16(ONFI_VERSION_1_0); 5938c2ecf20Sopenharmony_ci} 5948c2ecf20Sopenharmony_ci 5958c2ecf20Sopenharmony_ciconst struct nand_manufacturer_ops micron_nand_manuf_ops = { 5968c2ecf20Sopenharmony_ci .init = micron_nand_init, 5978c2ecf20Sopenharmony_ci .cleanup = micron_nand_cleanup, 5988c2ecf20Sopenharmony_ci .fixup_onfi_param_page = micron_fixup_onfi_param_page, 5998c2ecf20Sopenharmony_ci}; 600