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 JEDEC_PARAM_PAGES 3
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_ci/*
2262306a36Sopenharmony_ci * Check if the NAND chip is JEDEC compliant, returns 1 if it is, 0 otherwise.
2362306a36Sopenharmony_ci */
2462306a36Sopenharmony_ciint nand_jedec_detect(struct nand_chip *chip)
2562306a36Sopenharmony_ci{
2662306a36Sopenharmony_ci	struct nand_device *base = &chip->base;
2762306a36Sopenharmony_ci	struct mtd_info *mtd = nand_to_mtd(chip);
2862306a36Sopenharmony_ci	struct nand_memory_organization *memorg;
2962306a36Sopenharmony_ci	struct nand_jedec_params *p;
3062306a36Sopenharmony_ci	struct jedec_ecc_info *ecc;
3162306a36Sopenharmony_ci	bool use_datain = false;
3262306a36Sopenharmony_ci	int jedec_version = 0;
3362306a36Sopenharmony_ci	char id[5];
3462306a36Sopenharmony_ci	int i, val, ret;
3562306a36Sopenharmony_ci	u16 crc;
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ci	memorg = nanddev_get_memorg(&chip->base);
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_ci	/* Try JEDEC for unknown chip or LP */
4062306a36Sopenharmony_ci	ret = nand_readid_op(chip, 0x40, id, sizeof(id));
4162306a36Sopenharmony_ci	if (ret || strncmp(id, "JEDEC", sizeof(id)))
4262306a36Sopenharmony_ci		return 0;
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_ci	/* JEDEC chip: allocate a buffer to hold its parameter page */
4562306a36Sopenharmony_ci	p = kzalloc(sizeof(*p), GFP_KERNEL);
4662306a36Sopenharmony_ci	if (!p)
4762306a36Sopenharmony_ci		return -ENOMEM;
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_ci	if (!nand_has_exec_op(chip) || chip->controller->supported_op.data_only_read)
5062306a36Sopenharmony_ci		use_datain = true;
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ci	for (i = 0; i < JEDEC_PARAM_PAGES; i++) {
5362306a36Sopenharmony_ci		if (!i)
5462306a36Sopenharmony_ci			ret = nand_read_param_page_op(chip, 0x40, p,
5562306a36Sopenharmony_ci						      sizeof(*p));
5662306a36Sopenharmony_ci		else if (use_datain)
5762306a36Sopenharmony_ci			ret = nand_read_data_op(chip, p, sizeof(*p), true,
5862306a36Sopenharmony_ci						false);
5962306a36Sopenharmony_ci		else
6062306a36Sopenharmony_ci			ret = nand_change_read_column_op(chip, sizeof(*p) * i,
6162306a36Sopenharmony_ci							 p, sizeof(*p), true);
6262306a36Sopenharmony_ci		if (ret) {
6362306a36Sopenharmony_ci			ret = 0;
6462306a36Sopenharmony_ci			goto free_jedec_param_page;
6562306a36Sopenharmony_ci		}
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_ci		crc = onfi_crc16(ONFI_CRC_BASE, (u8 *)p, 510);
6862306a36Sopenharmony_ci		if (crc == le16_to_cpu(p->crc))
6962306a36Sopenharmony_ci			break;
7062306a36Sopenharmony_ci	}
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ci	if (i == JEDEC_PARAM_PAGES) {
7362306a36Sopenharmony_ci		pr_err("Could not find valid JEDEC parameter page; aborting\n");
7462306a36Sopenharmony_ci		goto free_jedec_param_page;
7562306a36Sopenharmony_ci	}
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ci	/* Check version */
7862306a36Sopenharmony_ci	val = le16_to_cpu(p->revision);
7962306a36Sopenharmony_ci	if (val & (1 << 2))
8062306a36Sopenharmony_ci		jedec_version = 10;
8162306a36Sopenharmony_ci	else if (val & (1 << 1))
8262306a36Sopenharmony_ci		jedec_version = 1; /* vendor specific version */
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ci	if (!jedec_version) {
8562306a36Sopenharmony_ci		pr_info("unsupported JEDEC version: %d\n", val);
8662306a36Sopenharmony_ci		goto free_jedec_param_page;
8762306a36Sopenharmony_ci	}
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ci	sanitize_string(p->manufacturer, sizeof(p->manufacturer));
9062306a36Sopenharmony_ci	sanitize_string(p->model, sizeof(p->model));
9162306a36Sopenharmony_ci	chip->parameters.model = kstrdup(p->model, GFP_KERNEL);
9262306a36Sopenharmony_ci	if (!chip->parameters.model) {
9362306a36Sopenharmony_ci		ret = -ENOMEM;
9462306a36Sopenharmony_ci		goto free_jedec_param_page;
9562306a36Sopenharmony_ci	}
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ci	if (p->opt_cmd[0] & JEDEC_OPT_CMD_READ_CACHE)
9862306a36Sopenharmony_ci		chip->parameters.supports_read_cache = true;
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ci	memorg->pagesize = le32_to_cpu(p->byte_per_page);
10162306a36Sopenharmony_ci	mtd->writesize = memorg->pagesize;
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ci	/* Please reference to the comment for nand_flash_detect_onfi. */
10462306a36Sopenharmony_ci	memorg->pages_per_eraseblock =
10562306a36Sopenharmony_ci			1 << (fls(le32_to_cpu(p->pages_per_block)) - 1);
10662306a36Sopenharmony_ci	mtd->erasesize = memorg->pages_per_eraseblock * memorg->pagesize;
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ci	memorg->oobsize = le16_to_cpu(p->spare_bytes_per_page);
10962306a36Sopenharmony_ci	mtd->oobsize = memorg->oobsize;
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_ci	memorg->luns_per_target = p->lun_count;
11262306a36Sopenharmony_ci	memorg->planes_per_lun = 1 << p->multi_plane_addr;
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ci	/* Please reference to the comment for nand_flash_detect_onfi. */
11562306a36Sopenharmony_ci	memorg->eraseblocks_per_lun =
11662306a36Sopenharmony_ci		1 << (fls(le32_to_cpu(p->blocks_per_lun)) - 1);
11762306a36Sopenharmony_ci	memorg->bits_per_cell = p->bits_per_cell;
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_ci	if (le16_to_cpu(p->features) & JEDEC_FEATURE_16_BIT_BUS)
12062306a36Sopenharmony_ci		chip->options |= NAND_BUSWIDTH_16;
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_ci	/* ECC info */
12362306a36Sopenharmony_ci	ecc = &p->ecc_info[0];
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ci	if (ecc->codeword_size >= 9) {
12662306a36Sopenharmony_ci		struct nand_ecc_props requirements = {
12762306a36Sopenharmony_ci			.strength = ecc->ecc_bits,
12862306a36Sopenharmony_ci			.step_size = 1 << ecc->codeword_size,
12962306a36Sopenharmony_ci		};
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_ci		nanddev_set_ecc_requirements(base, &requirements);
13262306a36Sopenharmony_ci	} else {
13362306a36Sopenharmony_ci		pr_warn("Invalid codeword size\n");
13462306a36Sopenharmony_ci	}
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_ci	ret = 1;
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_cifree_jedec_param_page:
13962306a36Sopenharmony_ci	kfree(p);
14062306a36Sopenharmony_ci	return ret;
14162306a36Sopenharmony_ci}
142