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 "internals.h"
108c2ecf20Sopenharmony_ci
118c2ecf20Sopenharmony_cistatic void samsung_nand_decode_id(struct nand_chip *chip)
128c2ecf20Sopenharmony_ci{
138c2ecf20Sopenharmony_ci	struct nand_device *base = &chip->base;
148c2ecf20Sopenharmony_ci	struct nand_ecc_props requirements = {};
158c2ecf20Sopenharmony_ci	struct mtd_info *mtd = nand_to_mtd(chip);
168c2ecf20Sopenharmony_ci	struct nand_memory_organization *memorg;
178c2ecf20Sopenharmony_ci
188c2ecf20Sopenharmony_ci	memorg = nanddev_get_memorg(&chip->base);
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_ci	/* New Samsung (6 byte ID): Samsung K9GAG08U0F (p.44) */
218c2ecf20Sopenharmony_ci	if (chip->id.len == 6 && !nand_is_slc(chip) &&
228c2ecf20Sopenharmony_ci	    chip->id.data[5] != 0x00) {
238c2ecf20Sopenharmony_ci		u8 extid = chip->id.data[3];
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_ci		/* Get pagesize */
268c2ecf20Sopenharmony_ci		memorg->pagesize = 2048 << (extid & 0x03);
278c2ecf20Sopenharmony_ci		mtd->writesize = memorg->pagesize;
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_ci		extid >>= 2;
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_ci		/* Get oobsize */
328c2ecf20Sopenharmony_ci		switch (((extid >> 2) & 0x4) | (extid & 0x3)) {
338c2ecf20Sopenharmony_ci		case 1:
348c2ecf20Sopenharmony_ci			memorg->oobsize = 128;
358c2ecf20Sopenharmony_ci			break;
368c2ecf20Sopenharmony_ci		case 2:
378c2ecf20Sopenharmony_ci			memorg->oobsize = 218;
388c2ecf20Sopenharmony_ci			break;
398c2ecf20Sopenharmony_ci		case 3:
408c2ecf20Sopenharmony_ci			memorg->oobsize = 400;
418c2ecf20Sopenharmony_ci			break;
428c2ecf20Sopenharmony_ci		case 4:
438c2ecf20Sopenharmony_ci			memorg->oobsize = 436;
448c2ecf20Sopenharmony_ci			break;
458c2ecf20Sopenharmony_ci		case 5:
468c2ecf20Sopenharmony_ci			memorg->oobsize = 512;
478c2ecf20Sopenharmony_ci			break;
488c2ecf20Sopenharmony_ci		case 6:
498c2ecf20Sopenharmony_ci			memorg->oobsize = 640;
508c2ecf20Sopenharmony_ci			break;
518c2ecf20Sopenharmony_ci		default:
528c2ecf20Sopenharmony_ci			/*
538c2ecf20Sopenharmony_ci			 * We should never reach this case, but if that
548c2ecf20Sopenharmony_ci			 * happens, this probably means Samsung decided to use
558c2ecf20Sopenharmony_ci			 * a different extended ID format, and we should find
568c2ecf20Sopenharmony_ci			 * a way to support it.
578c2ecf20Sopenharmony_ci			 */
588c2ecf20Sopenharmony_ci			WARN(1, "Invalid OOB size value");
598c2ecf20Sopenharmony_ci			break;
608c2ecf20Sopenharmony_ci		}
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_ci		mtd->oobsize = memorg->oobsize;
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_ci		/* Get blocksize */
658c2ecf20Sopenharmony_ci		extid >>= 2;
668c2ecf20Sopenharmony_ci		memorg->pages_per_eraseblock = (128 * 1024) <<
678c2ecf20Sopenharmony_ci					       (((extid >> 1) & 0x04) |
688c2ecf20Sopenharmony_ci						(extid & 0x03)) /
698c2ecf20Sopenharmony_ci					       memorg->pagesize;
708c2ecf20Sopenharmony_ci		mtd->erasesize = (128 * 1024) <<
718c2ecf20Sopenharmony_ci				 (((extid >> 1) & 0x04) | (extid & 0x03));
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_ci		/* Extract ECC requirements from 5th id byte*/
748c2ecf20Sopenharmony_ci		extid = (chip->id.data[4] >> 4) & 0x07;
758c2ecf20Sopenharmony_ci		if (extid < 5) {
768c2ecf20Sopenharmony_ci			requirements.step_size = 512;
778c2ecf20Sopenharmony_ci			requirements.strength = 1 << extid;
788c2ecf20Sopenharmony_ci		} else {
798c2ecf20Sopenharmony_ci			requirements.step_size = 1024;
808c2ecf20Sopenharmony_ci			switch (extid) {
818c2ecf20Sopenharmony_ci			case 5:
828c2ecf20Sopenharmony_ci				requirements.strength = 24;
838c2ecf20Sopenharmony_ci				break;
848c2ecf20Sopenharmony_ci			case 6:
858c2ecf20Sopenharmony_ci				requirements.strength = 40;
868c2ecf20Sopenharmony_ci				break;
878c2ecf20Sopenharmony_ci			case 7:
888c2ecf20Sopenharmony_ci				requirements.strength = 60;
898c2ecf20Sopenharmony_ci				break;
908c2ecf20Sopenharmony_ci			default:
918c2ecf20Sopenharmony_ci				WARN(1, "Could not decode ECC info");
928c2ecf20Sopenharmony_ci				requirements.step_size = 0;
938c2ecf20Sopenharmony_ci			}
948c2ecf20Sopenharmony_ci		}
958c2ecf20Sopenharmony_ci	} else {
968c2ecf20Sopenharmony_ci		nand_decode_ext_id(chip);
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_ci		if (nand_is_slc(chip)) {
998c2ecf20Sopenharmony_ci			switch (chip->id.data[1]) {
1008c2ecf20Sopenharmony_ci			/* K9F4G08U0D-S[I|C]B0(T00) */
1018c2ecf20Sopenharmony_ci			case 0xDC:
1028c2ecf20Sopenharmony_ci				requirements.step_size = 512;
1038c2ecf20Sopenharmony_ci				requirements.strength = 1;
1048c2ecf20Sopenharmony_ci				break;
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_ci			/* K9F1G08U0E 21nm chips do not support subpage write */
1078c2ecf20Sopenharmony_ci			case 0xF1:
1088c2ecf20Sopenharmony_ci				if (chip->id.len > 4 &&
1098c2ecf20Sopenharmony_ci				    (chip->id.data[4] & GENMASK(1, 0)) == 0x1)
1108c2ecf20Sopenharmony_ci					chip->options |= NAND_NO_SUBPAGE_WRITE;
1118c2ecf20Sopenharmony_ci				break;
1128c2ecf20Sopenharmony_ci			default:
1138c2ecf20Sopenharmony_ci				break;
1148c2ecf20Sopenharmony_ci			}
1158c2ecf20Sopenharmony_ci		}
1168c2ecf20Sopenharmony_ci	}
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_ci	nanddev_set_ecc_requirements(base, &requirements);
1198c2ecf20Sopenharmony_ci}
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_cistatic int samsung_nand_init(struct nand_chip *chip)
1228c2ecf20Sopenharmony_ci{
1238c2ecf20Sopenharmony_ci	struct mtd_info *mtd = nand_to_mtd(chip);
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_ci	if (mtd->writesize > 512)
1268c2ecf20Sopenharmony_ci		chip->options |= NAND_SAMSUNG_LP_OPTIONS;
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_ci	if (!nand_is_slc(chip))
1298c2ecf20Sopenharmony_ci		chip->options |= NAND_BBM_LASTPAGE;
1308c2ecf20Sopenharmony_ci	else
1318c2ecf20Sopenharmony_ci		chip->options |= NAND_BBM_FIRSTPAGE | NAND_BBM_SECONDPAGE;
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_ci	return 0;
1348c2ecf20Sopenharmony_ci}
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_ciconst struct nand_manufacturer_ops samsung_nand_manuf_ops = {
1378c2ecf20Sopenharmony_ci	.detect = samsung_nand_decode_id,
1388c2ecf20Sopenharmony_ci	.init = samsung_nand_init,
1398c2ecf20Sopenharmony_ci};
140