162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) 2000 Steven J. Hill (sjhill@realitydiluted.com) 462306a36Sopenharmony_ci * 2002-2006 Thomas Gleixner (tglx@linutronix.de) 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * Credits: 762306a36Sopenharmony_ci * David Woodhouse for adding multichip support 862306a36Sopenharmony_ci * 962306a36Sopenharmony_ci * Aleph One Ltd. and Toby Churchill Ltd. for supporting the 1062306a36Sopenharmony_ci * rework for 2K page size chips 1162306a36Sopenharmony_ci * 1262306a36Sopenharmony_ci * This file contains all ONFI helpers. 1362306a36Sopenharmony_ci */ 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci#include <linux/slab.h> 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci#include "internals.h" 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci#define ONFI_PARAM_PAGES 3 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ciu16 onfi_crc16(u16 crc, u8 const *p, size_t len) 2262306a36Sopenharmony_ci{ 2362306a36Sopenharmony_ci int i; 2462306a36Sopenharmony_ci while (len--) { 2562306a36Sopenharmony_ci crc ^= *p++ << 8; 2662306a36Sopenharmony_ci for (i = 0; i < 8; i++) 2762306a36Sopenharmony_ci crc = (crc << 1) ^ ((crc & 0x8000) ? 0x8005 : 0); 2862306a36Sopenharmony_ci } 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci return crc; 3162306a36Sopenharmony_ci} 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci/* Parse the Extended Parameter Page. */ 3462306a36Sopenharmony_cistatic int nand_flash_detect_ext_param_page(struct nand_chip *chip, 3562306a36Sopenharmony_ci struct nand_onfi_params *p) 3662306a36Sopenharmony_ci{ 3762306a36Sopenharmony_ci struct nand_device *base = &chip->base; 3862306a36Sopenharmony_ci struct nand_ecc_props requirements; 3962306a36Sopenharmony_ci struct onfi_ext_param_page *ep; 4062306a36Sopenharmony_ci struct onfi_ext_section *s; 4162306a36Sopenharmony_ci struct onfi_ext_ecc_info *ecc; 4262306a36Sopenharmony_ci uint8_t *cursor; 4362306a36Sopenharmony_ci int ret; 4462306a36Sopenharmony_ci int len; 4562306a36Sopenharmony_ci int i; 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci len = le16_to_cpu(p->ext_param_page_length) * 16; 4862306a36Sopenharmony_ci ep = kmalloc(len, GFP_KERNEL); 4962306a36Sopenharmony_ci if (!ep) 5062306a36Sopenharmony_ci return -ENOMEM; 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci /* 5362306a36Sopenharmony_ci * Use the Change Read Column command to skip the ONFI param pages and 5462306a36Sopenharmony_ci * ensure we read at the right location. 5562306a36Sopenharmony_ci */ 5662306a36Sopenharmony_ci ret = nand_change_read_column_op(chip, 5762306a36Sopenharmony_ci sizeof(*p) * p->num_of_param_pages, 5862306a36Sopenharmony_ci ep, len, true); 5962306a36Sopenharmony_ci if (ret) 6062306a36Sopenharmony_ci goto ext_out; 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci ret = -EINVAL; 6362306a36Sopenharmony_ci if ((onfi_crc16(ONFI_CRC_BASE, ((uint8_t *)ep) + 2, len - 2) 6462306a36Sopenharmony_ci != le16_to_cpu(ep->crc))) { 6562306a36Sopenharmony_ci pr_debug("fail in the CRC.\n"); 6662306a36Sopenharmony_ci goto ext_out; 6762306a36Sopenharmony_ci } 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci /* 7062306a36Sopenharmony_ci * Check the signature. 7162306a36Sopenharmony_ci * Do not strictly follow the ONFI spec, maybe changed in future. 7262306a36Sopenharmony_ci */ 7362306a36Sopenharmony_ci if (strncmp(ep->sig, "EPPS", 4)) { 7462306a36Sopenharmony_ci pr_debug("The signature is invalid.\n"); 7562306a36Sopenharmony_ci goto ext_out; 7662306a36Sopenharmony_ci } 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci /* find the ECC section. */ 7962306a36Sopenharmony_ci cursor = (uint8_t *)(ep + 1); 8062306a36Sopenharmony_ci for (i = 0; i < ONFI_EXT_SECTION_MAX; i++) { 8162306a36Sopenharmony_ci s = ep->sections + i; 8262306a36Sopenharmony_ci if (s->type == ONFI_SECTION_TYPE_2) 8362306a36Sopenharmony_ci break; 8462306a36Sopenharmony_ci cursor += s->length * 16; 8562306a36Sopenharmony_ci } 8662306a36Sopenharmony_ci if (i == ONFI_EXT_SECTION_MAX) { 8762306a36Sopenharmony_ci pr_debug("We can not find the ECC section.\n"); 8862306a36Sopenharmony_ci goto ext_out; 8962306a36Sopenharmony_ci } 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci /* get the info we want. */ 9262306a36Sopenharmony_ci ecc = (struct onfi_ext_ecc_info *)cursor; 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci if (!ecc->codeword_size) { 9562306a36Sopenharmony_ci pr_debug("Invalid codeword size\n"); 9662306a36Sopenharmony_ci goto ext_out; 9762306a36Sopenharmony_ci } 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci requirements.strength = ecc->ecc_bits; 10062306a36Sopenharmony_ci requirements.step_size = 1 << ecc->codeword_size; 10162306a36Sopenharmony_ci nanddev_set_ecc_requirements(base, &requirements); 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci ret = 0; 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ciext_out: 10662306a36Sopenharmony_ci kfree(ep); 10762306a36Sopenharmony_ci return ret; 10862306a36Sopenharmony_ci} 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci/* 11162306a36Sopenharmony_ci * Recover data with bit-wise majority 11262306a36Sopenharmony_ci */ 11362306a36Sopenharmony_cistatic void nand_bit_wise_majority(const void **srcbufs, 11462306a36Sopenharmony_ci unsigned int nsrcbufs, 11562306a36Sopenharmony_ci void *dstbuf, 11662306a36Sopenharmony_ci unsigned int bufsize) 11762306a36Sopenharmony_ci{ 11862306a36Sopenharmony_ci int i, j, k; 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci for (i = 0; i < bufsize; i++) { 12162306a36Sopenharmony_ci u8 val = 0; 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci for (j = 0; j < 8; j++) { 12462306a36Sopenharmony_ci unsigned int cnt = 0; 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci for (k = 0; k < nsrcbufs; k++) { 12762306a36Sopenharmony_ci const u8 *srcbuf = srcbufs[k]; 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci if (srcbuf[i] & BIT(j)) 13062306a36Sopenharmony_ci cnt++; 13162306a36Sopenharmony_ci } 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci if (cnt > nsrcbufs / 2) 13462306a36Sopenharmony_ci val |= BIT(j); 13562306a36Sopenharmony_ci } 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci ((u8 *)dstbuf)[i] = val; 13862306a36Sopenharmony_ci } 13962306a36Sopenharmony_ci} 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci/* 14262306a36Sopenharmony_ci * Check if the NAND chip is ONFI compliant, returns 1 if it is, 0 otherwise. 14362306a36Sopenharmony_ci */ 14462306a36Sopenharmony_ciint nand_onfi_detect(struct nand_chip *chip) 14562306a36Sopenharmony_ci{ 14662306a36Sopenharmony_ci struct nand_device *base = &chip->base; 14762306a36Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(chip); 14862306a36Sopenharmony_ci struct nand_memory_organization *memorg; 14962306a36Sopenharmony_ci struct nand_onfi_params *p = NULL, *pbuf; 15062306a36Sopenharmony_ci struct onfi_params *onfi; 15162306a36Sopenharmony_ci bool use_datain = false; 15262306a36Sopenharmony_ci int onfi_version = 0; 15362306a36Sopenharmony_ci char id[4]; 15462306a36Sopenharmony_ci int i, ret, val; 15562306a36Sopenharmony_ci u16 crc; 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci memorg = nanddev_get_memorg(&chip->base); 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci /* Try ONFI for unknown chip or LP */ 16062306a36Sopenharmony_ci ret = nand_readid_op(chip, 0x20, id, sizeof(id)); 16162306a36Sopenharmony_ci if (ret || strncmp(id, "ONFI", 4)) 16262306a36Sopenharmony_ci return 0; 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci /* ONFI chip: allocate a buffer to hold its parameter page */ 16562306a36Sopenharmony_ci pbuf = kzalloc((sizeof(*pbuf) * ONFI_PARAM_PAGES), GFP_KERNEL); 16662306a36Sopenharmony_ci if (!pbuf) 16762306a36Sopenharmony_ci return -ENOMEM; 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci if (!nand_has_exec_op(chip) || chip->controller->supported_op.data_only_read) 17062306a36Sopenharmony_ci use_datain = true; 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci for (i = 0; i < ONFI_PARAM_PAGES; i++) { 17362306a36Sopenharmony_ci if (!i) 17462306a36Sopenharmony_ci ret = nand_read_param_page_op(chip, 0, &pbuf[i], 17562306a36Sopenharmony_ci sizeof(*pbuf)); 17662306a36Sopenharmony_ci else if (use_datain) 17762306a36Sopenharmony_ci ret = nand_read_data_op(chip, &pbuf[i], sizeof(*pbuf), 17862306a36Sopenharmony_ci true, false); 17962306a36Sopenharmony_ci else 18062306a36Sopenharmony_ci ret = nand_change_read_column_op(chip, sizeof(*pbuf) * i, 18162306a36Sopenharmony_ci &pbuf[i], sizeof(*pbuf), 18262306a36Sopenharmony_ci true); 18362306a36Sopenharmony_ci if (ret) { 18462306a36Sopenharmony_ci ret = 0; 18562306a36Sopenharmony_ci goto free_onfi_param_page; 18662306a36Sopenharmony_ci } 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci crc = onfi_crc16(ONFI_CRC_BASE, (u8 *)&pbuf[i], 254); 18962306a36Sopenharmony_ci if (crc == le16_to_cpu(pbuf[i].crc)) { 19062306a36Sopenharmony_ci p = &pbuf[i]; 19162306a36Sopenharmony_ci break; 19262306a36Sopenharmony_ci } 19362306a36Sopenharmony_ci } 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci if (i == ONFI_PARAM_PAGES) { 19662306a36Sopenharmony_ci const void *srcbufs[ONFI_PARAM_PAGES]; 19762306a36Sopenharmony_ci unsigned int j; 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci for (j = 0; j < ONFI_PARAM_PAGES; j++) 20062306a36Sopenharmony_ci srcbufs[j] = pbuf + j; 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci pr_warn("Could not find a valid ONFI parameter page, trying bit-wise majority to recover it\n"); 20362306a36Sopenharmony_ci nand_bit_wise_majority(srcbufs, ONFI_PARAM_PAGES, pbuf, 20462306a36Sopenharmony_ci sizeof(*pbuf)); 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci crc = onfi_crc16(ONFI_CRC_BASE, (u8 *)pbuf, 254); 20762306a36Sopenharmony_ci if (crc != le16_to_cpu(pbuf->crc)) { 20862306a36Sopenharmony_ci pr_err("ONFI parameter recovery failed, aborting\n"); 20962306a36Sopenharmony_ci goto free_onfi_param_page; 21062306a36Sopenharmony_ci } 21162306a36Sopenharmony_ci p = pbuf; 21262306a36Sopenharmony_ci } 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci if (chip->manufacturer.desc && chip->manufacturer.desc->ops && 21562306a36Sopenharmony_ci chip->manufacturer.desc->ops->fixup_onfi_param_page) 21662306a36Sopenharmony_ci chip->manufacturer.desc->ops->fixup_onfi_param_page(chip, p); 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci /* Check version */ 21962306a36Sopenharmony_ci val = le16_to_cpu(p->revision); 22062306a36Sopenharmony_ci if (val & ONFI_VERSION_2_3) 22162306a36Sopenharmony_ci onfi_version = 23; 22262306a36Sopenharmony_ci else if (val & ONFI_VERSION_2_2) 22362306a36Sopenharmony_ci onfi_version = 22; 22462306a36Sopenharmony_ci else if (val & ONFI_VERSION_2_1) 22562306a36Sopenharmony_ci onfi_version = 21; 22662306a36Sopenharmony_ci else if (val & ONFI_VERSION_2_0) 22762306a36Sopenharmony_ci onfi_version = 20; 22862306a36Sopenharmony_ci else if (val & ONFI_VERSION_1_0) 22962306a36Sopenharmony_ci onfi_version = 10; 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci if (!onfi_version) { 23262306a36Sopenharmony_ci pr_info("unsupported ONFI version: %d\n", val); 23362306a36Sopenharmony_ci goto free_onfi_param_page; 23462306a36Sopenharmony_ci } 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci sanitize_string(p->manufacturer, sizeof(p->manufacturer)); 23762306a36Sopenharmony_ci sanitize_string(p->model, sizeof(p->model)); 23862306a36Sopenharmony_ci chip->parameters.model = kstrdup(p->model, GFP_KERNEL); 23962306a36Sopenharmony_ci if (!chip->parameters.model) { 24062306a36Sopenharmony_ci ret = -ENOMEM; 24162306a36Sopenharmony_ci goto free_onfi_param_page; 24262306a36Sopenharmony_ci } 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci memorg->pagesize = le32_to_cpu(p->byte_per_page); 24562306a36Sopenharmony_ci mtd->writesize = memorg->pagesize; 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci /* 24862306a36Sopenharmony_ci * pages_per_block and blocks_per_lun may not be a power-of-2 size 24962306a36Sopenharmony_ci * (don't ask me who thought of this...). MTD assumes that these 25062306a36Sopenharmony_ci * dimensions will be power-of-2, so just truncate the remaining area. 25162306a36Sopenharmony_ci */ 25262306a36Sopenharmony_ci memorg->pages_per_eraseblock = 25362306a36Sopenharmony_ci 1 << (fls(le32_to_cpu(p->pages_per_block)) - 1); 25462306a36Sopenharmony_ci mtd->erasesize = memorg->pages_per_eraseblock * memorg->pagesize; 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci memorg->oobsize = le16_to_cpu(p->spare_bytes_per_page); 25762306a36Sopenharmony_ci mtd->oobsize = memorg->oobsize; 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_ci memorg->luns_per_target = p->lun_count; 26062306a36Sopenharmony_ci memorg->planes_per_lun = 1 << p->interleaved_bits; 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci /* See erasesize comment */ 26362306a36Sopenharmony_ci memorg->eraseblocks_per_lun = 26462306a36Sopenharmony_ci 1 << (fls(le32_to_cpu(p->blocks_per_lun)) - 1); 26562306a36Sopenharmony_ci memorg->max_bad_eraseblocks_per_lun = le32_to_cpu(p->blocks_per_lun); 26662306a36Sopenharmony_ci memorg->bits_per_cell = p->bits_per_cell; 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci if (le16_to_cpu(p->features) & ONFI_FEATURE_16_BIT_BUS) 26962306a36Sopenharmony_ci chip->options |= NAND_BUSWIDTH_16; 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci if (p->ecc_bits != 0xff) { 27262306a36Sopenharmony_ci struct nand_ecc_props requirements = { 27362306a36Sopenharmony_ci .strength = p->ecc_bits, 27462306a36Sopenharmony_ci .step_size = 512, 27562306a36Sopenharmony_ci }; 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci nanddev_set_ecc_requirements(base, &requirements); 27862306a36Sopenharmony_ci } else if (onfi_version >= 21 && 27962306a36Sopenharmony_ci (le16_to_cpu(p->features) & ONFI_FEATURE_EXT_PARAM_PAGE)) { 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci /* 28262306a36Sopenharmony_ci * The nand_flash_detect_ext_param_page() uses the 28362306a36Sopenharmony_ci * Change Read Column command which maybe not supported 28462306a36Sopenharmony_ci * by the chip->legacy.cmdfunc. So try to update the 28562306a36Sopenharmony_ci * chip->legacy.cmdfunc now. We do not replace user supplied 28662306a36Sopenharmony_ci * command function. 28762306a36Sopenharmony_ci */ 28862306a36Sopenharmony_ci nand_legacy_adjust_cmdfunc(chip); 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_ci /* The Extended Parameter Page is supported since ONFI 2.1. */ 29162306a36Sopenharmony_ci if (nand_flash_detect_ext_param_page(chip, p)) 29262306a36Sopenharmony_ci pr_warn("Failed to detect ONFI extended param page\n"); 29362306a36Sopenharmony_ci } else { 29462306a36Sopenharmony_ci pr_warn("Could not retrieve ONFI ECC requirements\n"); 29562306a36Sopenharmony_ci } 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci /* Save some parameters from the parameter page for future use */ 29862306a36Sopenharmony_ci if (le16_to_cpu(p->opt_cmd) & ONFI_OPT_CMD_SET_GET_FEATURES) { 29962306a36Sopenharmony_ci chip->parameters.supports_set_get_features = true; 30062306a36Sopenharmony_ci bitmap_set(chip->parameters.get_feature_list, 30162306a36Sopenharmony_ci ONFI_FEATURE_ADDR_TIMING_MODE, 1); 30262306a36Sopenharmony_ci bitmap_set(chip->parameters.set_feature_list, 30362306a36Sopenharmony_ci ONFI_FEATURE_ADDR_TIMING_MODE, 1); 30462306a36Sopenharmony_ci } 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci if (le16_to_cpu(p->opt_cmd) & ONFI_OPT_CMD_READ_CACHE) 30762306a36Sopenharmony_ci chip->parameters.supports_read_cache = true; 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci onfi = kzalloc(sizeof(*onfi), GFP_KERNEL); 31062306a36Sopenharmony_ci if (!onfi) { 31162306a36Sopenharmony_ci ret = -ENOMEM; 31262306a36Sopenharmony_ci goto free_model; 31362306a36Sopenharmony_ci } 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ci onfi->version = onfi_version; 31662306a36Sopenharmony_ci onfi->tPROG = le16_to_cpu(p->t_prog); 31762306a36Sopenharmony_ci onfi->tBERS = le16_to_cpu(p->t_bers); 31862306a36Sopenharmony_ci onfi->tR = le16_to_cpu(p->t_r); 31962306a36Sopenharmony_ci onfi->tCCS = le16_to_cpu(p->t_ccs); 32062306a36Sopenharmony_ci onfi->fast_tCAD = le16_to_cpu(p->nvddr_nvddr2_features) & BIT(0); 32162306a36Sopenharmony_ci onfi->sdr_timing_modes = le16_to_cpu(p->sdr_timing_modes); 32262306a36Sopenharmony_ci if (le16_to_cpu(p->features) & ONFI_FEATURE_NV_DDR) 32362306a36Sopenharmony_ci onfi->nvddr_timing_modes = le16_to_cpu(p->nvddr_timing_modes); 32462306a36Sopenharmony_ci onfi->vendor_revision = le16_to_cpu(p->vendor_revision); 32562306a36Sopenharmony_ci memcpy(onfi->vendor, p->vendor, sizeof(p->vendor)); 32662306a36Sopenharmony_ci chip->parameters.onfi = onfi; 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_ci /* Identification done, free the full ONFI parameter page and exit */ 32962306a36Sopenharmony_ci kfree(pbuf); 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ci return 1; 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_cifree_model: 33462306a36Sopenharmony_ci kfree(chip->parameters.model); 33562306a36Sopenharmony_cifree_onfi_param_page: 33662306a36Sopenharmony_ci kfree(pbuf); 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_ci return ret; 33962306a36Sopenharmony_ci} 340