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