1// SPDX-License-Identifier: GPL-2.0-or-later
2/*
3 * Copyright (C) 2017 Free Electrons
4 * Copyright (C) 2017 NextThing Co
5 *
6 * Author: Boris Brezillon <boris.brezillon@free-electrons.com>
7 */
8
9#include "internals.h"
10
11static void samsung_nand_decode_id(struct nand_chip *chip)
12{
13	struct nand_device *base = &chip->base;
14	struct nand_ecc_props requirements = {};
15	struct mtd_info *mtd = nand_to_mtd(chip);
16	struct nand_memory_organization *memorg;
17
18	memorg = nanddev_get_memorg(&chip->base);
19
20	/* New Samsung (6 byte ID): Samsung K9GAG08U0F (p.44) */
21	if (chip->id.len == 6 && !nand_is_slc(chip) &&
22	    chip->id.data[5] != 0x00) {
23		u8 extid = chip->id.data[3];
24
25		/* Get pagesize */
26		memorg->pagesize = 2048 << (extid & 0x03);
27		mtd->writesize = memorg->pagesize;
28
29		extid >>= 2;
30
31		/* Get oobsize */
32		switch (((extid >> 2) & 0x4) | (extid & 0x3)) {
33		case 1:
34			memorg->oobsize = 128;
35			break;
36		case 2:
37			memorg->oobsize = 218;
38			break;
39		case 3:
40			memorg->oobsize = 400;
41			break;
42		case 4:
43			memorg->oobsize = 436;
44			break;
45		case 5:
46			memorg->oobsize = 512;
47			break;
48		case 6:
49			memorg->oobsize = 640;
50			break;
51		default:
52			/*
53			 * We should never reach this case, but if that
54			 * happens, this probably means Samsung decided to use
55			 * a different extended ID format, and we should find
56			 * a way to support it.
57			 */
58			WARN(1, "Invalid OOB size value");
59			break;
60		}
61
62		mtd->oobsize = memorg->oobsize;
63
64		/* Get blocksize */
65		extid >>= 2;
66		memorg->pages_per_eraseblock = (128 * 1024) <<
67					       (((extid >> 1) & 0x04) |
68						(extid & 0x03)) /
69					       memorg->pagesize;
70		mtd->erasesize = (128 * 1024) <<
71				 (((extid >> 1) & 0x04) | (extid & 0x03));
72
73		/* Extract ECC requirements from 5th id byte*/
74		extid = (chip->id.data[4] >> 4) & 0x07;
75		if (extid < 5) {
76			requirements.step_size = 512;
77			requirements.strength = 1 << extid;
78		} else {
79			requirements.step_size = 1024;
80			switch (extid) {
81			case 5:
82				requirements.strength = 24;
83				break;
84			case 6:
85				requirements.strength = 40;
86				break;
87			case 7:
88				requirements.strength = 60;
89				break;
90			default:
91				WARN(1, "Could not decode ECC info");
92				requirements.step_size = 0;
93			}
94		}
95	} else {
96		nand_decode_ext_id(chip);
97
98		if (nand_is_slc(chip)) {
99			switch (chip->id.data[1]) {
100			/* K9F4G08U0D-S[I|C]B0(T00) */
101			case 0xDC:
102				requirements.step_size = 512;
103				requirements.strength = 1;
104				break;
105
106			/* K9F1G08U0E 21nm chips do not support subpage write */
107			case 0xF1:
108				if (chip->id.len > 4 &&
109				    (chip->id.data[4] & GENMASK(1, 0)) == 0x1)
110					chip->options |= NAND_NO_SUBPAGE_WRITE;
111				break;
112			default:
113				break;
114			}
115		}
116	}
117
118	nanddev_set_ecc_requirements(base, &requirements);
119}
120
121static int samsung_nand_init(struct nand_chip *chip)
122{
123	struct mtd_info *mtd = nand_to_mtd(chip);
124
125	if (mtd->writesize > 512)
126		chip->options |= NAND_SAMSUNG_LP_OPTIONS;
127
128	if (!nand_is_slc(chip))
129		chip->options |= NAND_BBM_LASTPAGE;
130	else
131		chip->options |= NAND_BBM_FIRSTPAGE | NAND_BBM_SECONDPAGE;
132
133	return 0;
134}
135
136const struct nand_manufacturer_ops samsung_nand_manuf_ops = {
137	.detect = samsung_nand_decode_id,
138	.init = samsung_nand_init,
139};
140