1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * Bad Block Table support for the OneNAND driver |
4 | * |
5 | * Copyright(c) 2005 Samsung Electronics |
6 | * Kyungmin Park <kyungmin.park@samsung.com> |
7 | * |
8 | * Derived from nand_bbt.c |
9 | * |
10 | * TODO: |
11 | * Split BBT core and chip specific BBT. |
12 | */ |
13 | |
14 | #include <linux/slab.h> |
15 | #include <linux/mtd/mtd.h> |
16 | #include <linux/mtd/onenand.h> |
17 | #include <linux/export.h> |
18 | |
19 | /** |
20 | * check_short_pattern - [GENERIC] check if a pattern is in the buffer |
21 | * @buf: the buffer to search |
22 | * @len: the length of buffer to search |
23 | * @paglen: the pagelength |
24 | * @td: search pattern descriptor |
25 | * |
26 | * Check for a pattern at the given place. Used to search bad block |
27 | * tables and good / bad block identifiers. Same as check_pattern, but |
28 | * no optional empty check and the pattern is expected to start |
29 | * at offset 0. |
30 | * |
31 | */ |
32 | static int check_short_pattern(uint8_t *buf, int len, int paglen, struct nand_bbt_descr *td) |
33 | { |
34 | int i; |
35 | uint8_t *p = buf; |
36 | |
37 | /* Compare the pattern */ |
38 | for (i = 0; i < td->len; i++) { |
39 | if (p[i] != td->pattern[i]) |
40 | return -1; |
41 | } |
42 | return 0; |
43 | } |
44 | |
45 | /** |
46 | * create_bbt - [GENERIC] Create a bad block table by scanning the device |
47 | * @mtd: MTD device structure |
48 | * @buf: temporary buffer |
49 | * @bd: descriptor for the good/bad block search pattern |
50 | * @chip: create the table for a specific chip, -1 read all chips. |
51 | * Applies only if NAND_BBT_PERCHIP option is set |
52 | * |
53 | * Create a bad block table by scanning the device |
54 | * for the given good/bad block identify pattern |
55 | */ |
56 | static int create_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr *bd, int chip) |
57 | { |
58 | struct onenand_chip *this = mtd->priv; |
59 | struct bbm_info *bbm = this->bbm; |
60 | int i, j, numblocks, len, scanlen; |
61 | int startblock; |
62 | loff_t from; |
63 | size_t readlen; |
64 | struct mtd_oob_ops ops = { }; |
65 | int rgn; |
66 | |
67 | printk(KERN_INFO "Scanning device for bad blocks\n" ); |
68 | |
69 | len = 2; |
70 | |
71 | /* We need only read few bytes from the OOB area */ |
72 | scanlen = 0; |
73 | readlen = bd->len; |
74 | |
75 | /* chip == -1 case only */ |
76 | /* Note that numblocks is 2 * (real numblocks) here; |
77 | * see i += 2 below as it makses shifting and masking less painful |
78 | */ |
79 | numblocks = this->chipsize >> (bbm->bbt_erase_shift - 1); |
80 | startblock = 0; |
81 | from = 0; |
82 | |
83 | ops.mode = MTD_OPS_PLACE_OOB; |
84 | ops.ooblen = readlen; |
85 | ops.oobbuf = buf; |
86 | ops.len = ops.ooboffs = ops.retlen = ops.oobretlen = 0; |
87 | |
88 | for (i = startblock; i < numblocks; ) { |
89 | int ret; |
90 | |
91 | for (j = 0; j < len; j++) { |
92 | /* No need to read pages fully, |
93 | * just read required OOB bytes */ |
94 | ret = onenand_bbt_read_oob(mtd, |
95 | from: from + j * this->writesize + bd->offs, ops: &ops); |
96 | |
97 | /* If it is a initial bad block, just ignore it */ |
98 | if (ret == ONENAND_BBT_READ_FATAL_ERROR) |
99 | return -EIO; |
100 | |
101 | if (ret || check_short_pattern(buf: &buf[j * scanlen], |
102 | len: scanlen, paglen: this->writesize, td: bd)) { |
103 | bbm->bbt[i >> 3] |= 0x03 << (i & 0x6); |
104 | printk(KERN_INFO "OneNAND eraseblock %d is an " |
105 | "initial bad block\n" , i >> 1); |
106 | mtd->ecc_stats.badblocks++; |
107 | break; |
108 | } |
109 | } |
110 | i += 2; |
111 | |
112 | if (FLEXONENAND(this)) { |
113 | rgn = flexonenand_region(mtd, addr: from); |
114 | from += mtd->eraseregions[rgn].erasesize; |
115 | } else |
116 | from += (1 << bbm->bbt_erase_shift); |
117 | } |
118 | |
119 | return 0; |
120 | } |
121 | |
122 | |
123 | /** |
124 | * onenand_memory_bbt - [GENERIC] create a memory based bad block table |
125 | * @mtd: MTD device structure |
126 | * @bd: descriptor for the good/bad block search pattern |
127 | * |
128 | * The function creates a memory based bbt by scanning the device |
129 | * for manufacturer / software marked good / bad blocks |
130 | */ |
131 | static inline int onenand_memory_bbt (struct mtd_info *mtd, struct nand_bbt_descr *bd) |
132 | { |
133 | struct onenand_chip *this = mtd->priv; |
134 | |
135 | return create_bbt(mtd, buf: this->page_buf, bd, chip: -1); |
136 | } |
137 | |
138 | /** |
139 | * onenand_isbad_bbt - [OneNAND Interface] Check if a block is bad |
140 | * @mtd: MTD device structure |
141 | * @offs: offset in the device |
142 | * @allowbbt: allow access to bad block table region |
143 | */ |
144 | static int onenand_isbad_bbt(struct mtd_info *mtd, loff_t offs, int allowbbt) |
145 | { |
146 | struct onenand_chip *this = mtd->priv; |
147 | struct bbm_info *bbm = this->bbm; |
148 | int block; |
149 | uint8_t res; |
150 | |
151 | /* Get block number * 2 */ |
152 | block = (int) (onenand_block(this, addr: offs) << 1); |
153 | res = (bbm->bbt[block >> 3] >> (block & 0x06)) & 0x03; |
154 | |
155 | pr_debug("onenand_isbad_bbt: bbt info for offs 0x%08x: (block %d) 0x%02x\n" , |
156 | (unsigned int) offs, block >> 1, res); |
157 | |
158 | switch ((int) res) { |
159 | case 0x00: return 0; |
160 | case 0x01: return 1; |
161 | case 0x02: return allowbbt ? 0 : 1; |
162 | } |
163 | |
164 | return 1; |
165 | } |
166 | |
167 | /** |
168 | * onenand_scan_bbt - [OneNAND Interface] scan, find, read and maybe create bad block table(s) |
169 | * @mtd: MTD device structure |
170 | * @bd: descriptor for the good/bad block search pattern |
171 | * |
172 | * The function checks, if a bad block table(s) is/are already |
173 | * available. If not it scans the device for manufacturer |
174 | * marked good / bad blocks and writes the bad block table(s) to |
175 | * the selected place. |
176 | * |
177 | * The bad block table memory is allocated here. It is freed |
178 | * by the onenand_release function. |
179 | * |
180 | */ |
181 | static int onenand_scan_bbt(struct mtd_info *mtd, struct nand_bbt_descr *bd) |
182 | { |
183 | struct onenand_chip *this = mtd->priv; |
184 | struct bbm_info *bbm = this->bbm; |
185 | int len, ret = 0; |
186 | |
187 | len = this->chipsize >> (this->erase_shift + 2); |
188 | /* Allocate memory (2bit per block) and clear the memory bad block table */ |
189 | bbm->bbt = kzalloc(size: len, GFP_KERNEL); |
190 | if (!bbm->bbt) |
191 | return -ENOMEM; |
192 | |
193 | /* Set erase shift */ |
194 | bbm->bbt_erase_shift = this->erase_shift; |
195 | |
196 | if (!bbm->isbad_bbt) |
197 | bbm->isbad_bbt = onenand_isbad_bbt; |
198 | |
199 | /* Scan the device to build a memory based bad block table */ |
200 | if ((ret = onenand_memory_bbt(mtd, bd))) { |
201 | printk(KERN_ERR "onenand_scan_bbt: Can't scan flash and build the RAM-based BBT\n" ); |
202 | kfree(objp: bbm->bbt); |
203 | bbm->bbt = NULL; |
204 | } |
205 | |
206 | return ret; |
207 | } |
208 | |
209 | /* |
210 | * Define some generic bad / good block scan pattern which are used |
211 | * while scanning a device for factory marked good / bad blocks. |
212 | */ |
213 | static uint8_t scan_ff_pattern[] = { 0xff, 0xff }; |
214 | |
215 | static struct nand_bbt_descr largepage_memorybased = { |
216 | .options = 0, |
217 | .offs = 0, |
218 | .len = 2, |
219 | .pattern = scan_ff_pattern, |
220 | }; |
221 | |
222 | /** |
223 | * onenand_default_bbt - [OneNAND Interface] Select a default bad block table for the device |
224 | * @mtd: MTD device structure |
225 | * |
226 | * This function selects the default bad block table |
227 | * support for the device and calls the onenand_scan_bbt function |
228 | */ |
229 | int onenand_default_bbt(struct mtd_info *mtd) |
230 | { |
231 | struct onenand_chip *this = mtd->priv; |
232 | struct bbm_info *bbm; |
233 | |
234 | this->bbm = kzalloc(size: sizeof(struct bbm_info), GFP_KERNEL); |
235 | if (!this->bbm) |
236 | return -ENOMEM; |
237 | |
238 | bbm = this->bbm; |
239 | |
240 | /* 1KB page has same configuration as 2KB page */ |
241 | if (!bbm->badblock_pattern) |
242 | bbm->badblock_pattern = &largepage_memorybased; |
243 | |
244 | return onenand_scan_bbt(mtd, bd: bbm->badblock_pattern); |
245 | } |
246 | |