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 "internals.h"
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_cistatic void samsung_nand_decode_id(struct nand_chip *chip)
1262306a36Sopenharmony_ci{
1362306a36Sopenharmony_ci	struct nand_device *base = &chip->base;
1462306a36Sopenharmony_ci	struct nand_ecc_props requirements = {};
1562306a36Sopenharmony_ci	struct mtd_info *mtd = nand_to_mtd(chip);
1662306a36Sopenharmony_ci	struct nand_memory_organization *memorg;
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_ci	memorg = nanddev_get_memorg(&chip->base);
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_ci	/* New Samsung (6 byte ID): Samsung K9GAG08U0F (p.44) */
2162306a36Sopenharmony_ci	if (chip->id.len == 6 && !nand_is_slc(chip) &&
2262306a36Sopenharmony_ci	    chip->id.data[5] != 0x00) {
2362306a36Sopenharmony_ci		u8 extid = chip->id.data[3];
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_ci		/* Get pagesize */
2662306a36Sopenharmony_ci		memorg->pagesize = 2048 << (extid & 0x03);
2762306a36Sopenharmony_ci		mtd->writesize = memorg->pagesize;
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_ci		extid >>= 2;
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_ci		/* Get oobsize */
3262306a36Sopenharmony_ci		switch (((extid >> 2) & 0x4) | (extid & 0x3)) {
3362306a36Sopenharmony_ci		case 1:
3462306a36Sopenharmony_ci			memorg->oobsize = 128;
3562306a36Sopenharmony_ci			break;
3662306a36Sopenharmony_ci		case 2:
3762306a36Sopenharmony_ci			memorg->oobsize = 218;
3862306a36Sopenharmony_ci			break;
3962306a36Sopenharmony_ci		case 3:
4062306a36Sopenharmony_ci			memorg->oobsize = 400;
4162306a36Sopenharmony_ci			break;
4262306a36Sopenharmony_ci		case 4:
4362306a36Sopenharmony_ci			memorg->oobsize = 436;
4462306a36Sopenharmony_ci			break;
4562306a36Sopenharmony_ci		case 5:
4662306a36Sopenharmony_ci			memorg->oobsize = 512;
4762306a36Sopenharmony_ci			break;
4862306a36Sopenharmony_ci		case 6:
4962306a36Sopenharmony_ci			memorg->oobsize = 640;
5062306a36Sopenharmony_ci			break;
5162306a36Sopenharmony_ci		default:
5262306a36Sopenharmony_ci			/*
5362306a36Sopenharmony_ci			 * We should never reach this case, but if that
5462306a36Sopenharmony_ci			 * happens, this probably means Samsung decided to use
5562306a36Sopenharmony_ci			 * a different extended ID format, and we should find
5662306a36Sopenharmony_ci			 * a way to support it.
5762306a36Sopenharmony_ci			 */
5862306a36Sopenharmony_ci			WARN(1, "Invalid OOB size value");
5962306a36Sopenharmony_ci			break;
6062306a36Sopenharmony_ci		}
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci		mtd->oobsize = memorg->oobsize;
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ci		/* Get blocksize */
6562306a36Sopenharmony_ci		extid >>= 2;
6662306a36Sopenharmony_ci		memorg->pages_per_eraseblock = (128 * 1024) <<
6762306a36Sopenharmony_ci					       (((extid >> 1) & 0x04) |
6862306a36Sopenharmony_ci						(extid & 0x03)) /
6962306a36Sopenharmony_ci					       memorg->pagesize;
7062306a36Sopenharmony_ci		mtd->erasesize = (128 * 1024) <<
7162306a36Sopenharmony_ci				 (((extid >> 1) & 0x04) | (extid & 0x03));
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ci		/* Extract ECC requirements from 5th id byte*/
7462306a36Sopenharmony_ci		extid = (chip->id.data[4] >> 4) & 0x07;
7562306a36Sopenharmony_ci		if (extid < 5) {
7662306a36Sopenharmony_ci			requirements.step_size = 512;
7762306a36Sopenharmony_ci			requirements.strength = 1 << extid;
7862306a36Sopenharmony_ci		} else {
7962306a36Sopenharmony_ci			requirements.step_size = 1024;
8062306a36Sopenharmony_ci			switch (extid) {
8162306a36Sopenharmony_ci			case 5:
8262306a36Sopenharmony_ci				requirements.strength = 24;
8362306a36Sopenharmony_ci				break;
8462306a36Sopenharmony_ci			case 6:
8562306a36Sopenharmony_ci				requirements.strength = 40;
8662306a36Sopenharmony_ci				break;
8762306a36Sopenharmony_ci			case 7:
8862306a36Sopenharmony_ci				requirements.strength = 60;
8962306a36Sopenharmony_ci				break;
9062306a36Sopenharmony_ci			default:
9162306a36Sopenharmony_ci				WARN(1, "Could not decode ECC info");
9262306a36Sopenharmony_ci				requirements.step_size = 0;
9362306a36Sopenharmony_ci			}
9462306a36Sopenharmony_ci		}
9562306a36Sopenharmony_ci	} else {
9662306a36Sopenharmony_ci		nand_decode_ext_id(chip);
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci		if (nand_is_slc(chip)) {
9962306a36Sopenharmony_ci			switch (chip->id.data[1]) {
10062306a36Sopenharmony_ci			/* K9F4G08U0D-S[I|C]B0(T00) */
10162306a36Sopenharmony_ci			case 0xDC:
10262306a36Sopenharmony_ci				requirements.step_size = 512;
10362306a36Sopenharmony_ci				requirements.strength = 1;
10462306a36Sopenharmony_ci				break;
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci			/* K9F1G08U0E 21nm chips do not support subpage write */
10762306a36Sopenharmony_ci			case 0xF1:
10862306a36Sopenharmony_ci				if (chip->id.len > 4 &&
10962306a36Sopenharmony_ci				    (chip->id.data[4] & GENMASK(1, 0)) == 0x1)
11062306a36Sopenharmony_ci					chip->options |= NAND_NO_SUBPAGE_WRITE;
11162306a36Sopenharmony_ci				break;
11262306a36Sopenharmony_ci			default:
11362306a36Sopenharmony_ci				break;
11462306a36Sopenharmony_ci			}
11562306a36Sopenharmony_ci		}
11662306a36Sopenharmony_ci	}
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_ci	nanddev_set_ecc_requirements(base, &requirements);
11962306a36Sopenharmony_ci}
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_cistatic int samsung_nand_init(struct nand_chip *chip)
12262306a36Sopenharmony_ci{
12362306a36Sopenharmony_ci	struct mtd_info *mtd = nand_to_mtd(chip);
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ci	if (mtd->writesize > 512)
12662306a36Sopenharmony_ci		chip->options |= NAND_SAMSUNG_LP_OPTIONS;
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_ci	if (!nand_is_slc(chip))
12962306a36Sopenharmony_ci		chip->options |= NAND_BBM_LASTPAGE;
13062306a36Sopenharmony_ci	else
13162306a36Sopenharmony_ci		chip->options |= NAND_BBM_FIRSTPAGE | NAND_BBM_SECONDPAGE;
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci	return 0;
13462306a36Sopenharmony_ci}
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_ciconst struct nand_manufacturer_ops samsung_nand_manuf_ops = {
13762306a36Sopenharmony_ci	.detect = samsung_nand_decode_id,
13862306a36Sopenharmony_ci	.init = samsung_nand_init,
13962306a36Sopenharmony_ci};
140