xref: /linux/drivers/mtd/nand/raw/nand_samsung.c (revision 0be3ff0c)
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 
11 static 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 
121 static 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 
136 const struct nand_manufacturer_ops samsung_nand_manuf_ops = {
137 	.detect = samsung_nand_decode_id,
138 	.init = samsung_nand_init,
139 };
140