]>
Commit | Line | Data |
---|---|---|
12201a13 DB |
1 | /* |
2 | * (C) Copyright 2004-2008 Texas Instruments, <www.ti.com> | |
3 | * Rohit Choraria <[email protected]> | |
4 | * | |
1a459660 | 5 | * SPDX-License-Identifier: GPL-2.0+ |
12201a13 DB |
6 | */ |
7 | ||
8 | #include <common.h> | |
9 | #include <asm/io.h> | |
10 | #include <asm/errno.h> | |
11 | #include <asm/arch/mem.h> | |
6aff0509 | 12 | #include <linux/mtd/omap_gpmc.h> |
12201a13 | 13 | #include <linux/mtd/nand_ecc.h> |
4a093006 | 14 | #include <linux/bch.h> |
f7dad8f1 | 15 | #include <linux/compiler.h> |
12201a13 | 16 | #include <nand.h> |
2eda892f | 17 | #include <linux/mtd/omap_elm.h> |
d016dc42 | 18 | |
19 | #define BADBLOCK_MARKER_LENGTH 2 | |
20 | #define SECTOR_BYTES 512 | |
f5f1f614 | 21 | #define ECCCLEAR (0x1 << 8) |
22 | #define ECCRESULTREG1 (0x1 << 0) | |
6e562b11 | 23 | /* 4 bit padding to make byte aligned, 56 = 52 + 4 */ |
24 | #define BCH4_BIT_PAD 4 | |
25 | ||
71a7f956 | 26 | #ifdef CONFIG_BCH |
27 | static u8 bch8_polynomial[] = {0xef, 0x51, 0x2e, 0x09, 0xed, 0x93, 0x9a, 0xc2, | |
28 | 0x97, 0x79, 0xe5, 0x24, 0xb5}; | |
29 | #endif | |
12201a13 | 30 | static uint8_t cs; |
d016dc42 | 31 | static __maybe_unused struct nand_ecclayout omap_ecclayout; |
12201a13 DB |
32 | |
33 | /* | |
34 | * omap_nand_hwcontrol - Set the address pointers corretly for the | |
35 | * following address/data/command operation | |
36 | */ | |
37 | static void omap_nand_hwcontrol(struct mtd_info *mtd, int32_t cmd, | |
38 | uint32_t ctrl) | |
39 | { | |
40 | register struct nand_chip *this = mtd->priv; | |
41 | ||
42 | /* | |
43 | * Point the IO_ADDR to DATA and ADDRESS registers instead | |
44 | * of chip address | |
45 | */ | |
46 | switch (ctrl) { | |
47 | case NAND_CTRL_CHANGE | NAND_CTRL_CLE: | |
89411352 | 48 | this->IO_ADDR_W = (void __iomem *)&gpmc_cfg->cs[cs].nand_cmd; |
12201a13 DB |
49 | break; |
50 | case NAND_CTRL_CHANGE | NAND_CTRL_ALE: | |
89411352 | 51 | this->IO_ADDR_W = (void __iomem *)&gpmc_cfg->cs[cs].nand_adr; |
12201a13 DB |
52 | break; |
53 | case NAND_CTRL_CHANGE | NAND_NCE: | |
89411352 | 54 | this->IO_ADDR_W = (void __iomem *)&gpmc_cfg->cs[cs].nand_dat; |
12201a13 DB |
55 | break; |
56 | } | |
57 | ||
58 | if (cmd != NAND_CMD_NONE) | |
59 | writeb(cmd, this->IO_ADDR_W); | |
60 | } | |
61 | ||
12c2f1ee SS |
62 | #ifdef CONFIG_SPL_BUILD |
63 | /* Check wait pin as dev ready indicator */ | |
64 | int omap_spl_dev_ready(struct mtd_info *mtd) | |
65 | { | |
66 | return gpmc_cfg->status & (1 << 8); | |
67 | } | |
68 | #endif | |
69 | ||
12201a13 DB |
70 | |
71 | /* | |
72 | * gen_true_ecc - This function will generate true ECC value, which | |
73 | * can be used when correcting data read from NAND flash memory core | |
74 | * | |
75 | * @ecc_buf: buffer to store ecc code | |
76 | * | |
77 | * @return: re-formatted ECC value | |
78 | */ | |
79 | static uint32_t gen_true_ecc(uint8_t *ecc_buf) | |
80 | { | |
81 | return ecc_buf[0] | (ecc_buf[1] << 16) | ((ecc_buf[2] & 0xF0) << 20) | | |
82 | ((ecc_buf[2] & 0x0F) << 8); | |
83 | } | |
84 | ||
85 | /* | |
86 | * omap_correct_data - Compares the ecc read from nand spare area with ECC | |
87 | * registers values and corrects one bit error if it has occured | |
88 | * Further details can be had from OMAP TRM and the following selected links: | |
89 | * http://en.wikipedia.org/wiki/Hamming_code | |
90 | * http://www.cs.utexas.edu/users/plaxton/c/337/05f/slides/ErrorCorrection-4.pdf | |
91 | * | |
92 | * @mtd: MTD device structure | |
93 | * @dat: page data | |
94 | * @read_ecc: ecc read from nand flash | |
95 | * @calc_ecc: ecc read from ECC registers | |
96 | * | |
97 | * @return 0 if data is OK or corrected, else returns -1 | |
98 | */ | |
f7dad8f1 | 99 | static int __maybe_unused omap_correct_data(struct mtd_info *mtd, uint8_t *dat, |
12201a13 DB |
100 | uint8_t *read_ecc, uint8_t *calc_ecc) |
101 | { | |
102 | uint32_t orig_ecc, new_ecc, res, hm; | |
103 | uint16_t parity_bits, byte; | |
104 | uint8_t bit; | |
105 | ||
106 | /* Regenerate the orginal ECC */ | |
107 | orig_ecc = gen_true_ecc(read_ecc); | |
108 | new_ecc = gen_true_ecc(calc_ecc); | |
109 | /* Get the XOR of real ecc */ | |
110 | res = orig_ecc ^ new_ecc; | |
111 | if (res) { | |
112 | /* Get the hamming width */ | |
113 | hm = hweight32(res); | |
114 | /* Single bit errors can be corrected! */ | |
115 | if (hm == 12) { | |
116 | /* Correctable data! */ | |
117 | parity_bits = res >> 16; | |
118 | bit = (parity_bits & 0x7); | |
119 | byte = (parity_bits >> 3) & 0x1FF; | |
120 | /* Flip the bit to correct */ | |
121 | dat[byte] ^= (0x1 << bit); | |
122 | } else if (hm == 1) { | |
123 | printf("Error: Ecc is wrong\n"); | |
124 | /* ECC itself is corrupted */ | |
125 | return 2; | |
126 | } else { | |
127 | /* | |
128 | * hm distance != parity pairs OR one, could mean 2 bit | |
129 | * error OR potentially be on a blank page.. | |
130 | * orig_ecc: contains spare area data from nand flash. | |
131 | * new_ecc: generated ecc while reading data area. | |
132 | * Note: if the ecc = 0, all data bits from which it was | |
133 | * generated are 0xFF. | |
134 | * The 3 byte(24 bits) ecc is generated per 512byte | |
135 | * chunk of a page. If orig_ecc(from spare area) | |
136 | * is 0xFF && new_ecc(computed now from data area)=0x0, | |
137 | * this means that data area is 0xFF and spare area is | |
138 | * 0xFF. A sure sign of a erased page! | |
139 | */ | |
140 | if ((orig_ecc == 0x0FFF0FFF) && (new_ecc == 0x00000000)) | |
141 | return 0; | |
142 | printf("Error: Bad compare! failed\n"); | |
143 | /* detected 2 bit error */ | |
144 | return -1; | |
145 | } | |
146 | } | |
147 | return 0; | |
148 | } | |
149 | ||
c3754e9c | 150 | /* |
9233279f | 151 | * Driver configurations |
c3754e9c | 152 | */ |
9233279f | 153 | struct omap_nand_info { |
4a093006 | 154 | struct bch_control *control; |
d016dc42 | 155 | enum omap_ecc ecc_scheme; |
c3754e9c MA |
156 | }; |
157 | ||
4a093006 AB |
158 | /* |
159 | * This can be a single instance cause all current users have only one NAND | |
160 | * with nearly the same setup (BCH8, some with ELM and others with sw BCH | |
161 | * library). | |
162 | * When some users with other BCH strength will exists this have to change! | |
163 | */ | |
9233279f | 164 | static __maybe_unused struct omap_nand_info omap_nand_info = { |
4a093006 | 165 | .control = NULL |
c3754e9c MA |
166 | }; |
167 | ||
6e562b11 | 168 | /* |
169 | * omap_reverse_list - re-orders list elements in reverse order [internal] | |
170 | * @list: pointer to start of list | |
171 | * @length: length of list | |
172 | */ | |
173 | void omap_reverse_list(u8 *list, unsigned int length) | |
174 | { | |
175 | unsigned int i, j; | |
176 | unsigned int half_length = length / 2; | |
177 | u8 tmp; | |
178 | for (i = 0, j = length - 1; i < half_length; i++, j--) { | |
179 | tmp = list[i]; | |
180 | list[i] = list[j]; | |
181 | list[j] = tmp; | |
182 | } | |
183 | } | |
184 | ||
4a093006 | 185 | /* |
f5f1f614 | 186 | * omap_enable_hwecc - configures GPMC as per ECC scheme before read/write |
4a093006 AB |
187 | * @mtd: MTD device structure |
188 | * @mode: Read/Write mode | |
189 | */ | |
190 | __maybe_unused | |
f5f1f614 | 191 | static void omap_enable_hwecc(struct mtd_info *mtd, int32_t mode) |
4a093006 | 192 | { |
f5f1f614 | 193 | struct nand_chip *nand = mtd->priv; |
9233279f | 194 | struct omap_nand_info *info = nand->priv; |
f5f1f614 | 195 | unsigned int dev_width = (nand->options & NAND_BUSWIDTH_16) ? 1 : 0; |
196 | unsigned int ecc_algo = 0; | |
197 | unsigned int bch_type = 0; | |
198 | unsigned int eccsize1 = 0x00, eccsize0 = 0x00, bch_wrapmode = 0x00; | |
199 | u32 ecc_size_config_val = 0; | |
200 | u32 ecc_config_val = 0; | |
201 | ||
202 | /* configure GPMC for specific ecc-scheme */ | |
9233279f | 203 | switch (info->ecc_scheme) { |
f5f1f614 | 204 | case OMAP_ECC_HAM1_CODE_SW: |
205 | return; | |
206 | case OMAP_ECC_HAM1_CODE_HW: | |
207 | ecc_algo = 0x0; | |
208 | bch_type = 0x0; | |
209 | bch_wrapmode = 0x00; | |
210 | eccsize0 = 0xFF; | |
211 | eccsize1 = 0xFF; | |
212 | break; | |
213 | case OMAP_ECC_BCH8_CODE_HW_DETECTION_SW: | |
214 | case OMAP_ECC_BCH8_CODE_HW: | |
215 | ecc_algo = 0x1; | |
216 | bch_type = 0x1; | |
217 | if (mode == NAND_ECC_WRITE) { | |
218 | bch_wrapmode = 0x01; | |
219 | eccsize0 = 0; /* extra bits in nibbles per sector */ | |
220 | eccsize1 = 28; /* OOB bits in nibbles per sector */ | |
221 | } else { | |
222 | bch_wrapmode = 0x01; | |
223 | eccsize0 = 26; /* ECC bits in nibbles per sector */ | |
224 | eccsize1 = 2; /* non-ECC bits in nibbles per sector */ | |
5d7a49b9 | 225 | } |
f5f1f614 | 226 | break; |
227 | default: | |
228 | return; | |
d016dc42 | 229 | } |
f5f1f614 | 230 | /* Clear ecc and enable bits */ |
231 | writel(ECCCLEAR | ECCRESULTREG1, &gpmc_cfg->ecc_control); | |
232 | /* Configure ecc size for BCH */ | |
233 | ecc_size_config_val = (eccsize1 << 22) | (eccsize0 << 12); | |
234 | writel(ecc_size_config_val, &gpmc_cfg->ecc_size_config); | |
235 | ||
236 | /* Configure device details for BCH engine */ | |
237 | ecc_config_val = ((ecc_algo << 16) | /* HAM1 | BCHx */ | |
238 | (bch_type << 12) | /* BCH4/BCH8/BCH16 */ | |
239 | (bch_wrapmode << 8) | /* wrap mode */ | |
240 | (dev_width << 7) | /* bus width */ | |
241 | (0x0 << 4) | /* number of sectors */ | |
242 | (cs << 1) | /* ECC CS */ | |
243 | (0x1)); /* enable ECC */ | |
244 | writel(ecc_config_val, &gpmc_cfg->ecc_config); | |
4a093006 AB |
245 | } |
246 | ||
247 | /* | |
71a7f956 | 248 | * omap_calculate_ecc - Read ECC result |
249 | * @mtd: MTD structure | |
250 | * @dat: unused | |
251 | * @ecc_code: ecc_code buffer | |
252 | * Using noninverted ECC can be considered ugly since writing a blank | |
253 | * page ie. padding will clear the ECC bytes. This is no problem as | |
254 | * long nobody is trying to write data on the seemingly unused page. | |
255 | * Reading an erased page will produce an ECC mismatch between | |
256 | * generated and read ECC bytes that has to be dealt with separately. | |
257 | * E.g. if page is 0xFF (fresh erased), and if HW ECC engine within GPMC | |
258 | * is used, the result of read will be 0x0 while the ECC offsets of the | |
259 | * spare area will be 0xFF which will result in an ECC mismatch. | |
c3754e9c | 260 | */ |
71a7f956 | 261 | static int omap_calculate_ecc(struct mtd_info *mtd, const uint8_t *dat, |
c3754e9c MA |
262 | uint8_t *ecc_code) |
263 | { | |
71a7f956 | 264 | struct nand_chip *chip = mtd->priv; |
9233279f | 265 | struct omap_nand_info *info = chip->priv; |
71a7f956 | 266 | uint32_t *ptr, val = 0; |
c3754e9c MA |
267 | int8_t i = 0, j; |
268 | ||
9233279f | 269 | switch (info->ecc_scheme) { |
71a7f956 | 270 | case OMAP_ECC_HAM1_CODE_HW: |
271 | val = readl(&gpmc_cfg->ecc1_result); | |
272 | ecc_code[0] = val & 0xFF; | |
273 | ecc_code[1] = (val >> 16) & 0xFF; | |
274 | ecc_code[2] = ((val >> 8) & 0x0F) | ((val >> 20) & 0xF0); | |
275 | break; | |
276 | #ifdef CONFIG_BCH | |
277 | case OMAP_ECC_BCH8_CODE_HW_DETECTION_SW: | |
278 | #endif | |
279 | case OMAP_ECC_BCH8_CODE_HW: | |
c3754e9c | 280 | ptr = &gpmc_cfg->bch_result_0_3[0].bch_result_x[3]; |
71a7f956 | 281 | val = readl(ptr); |
282 | ecc_code[i++] = (val >> 0) & 0xFF; | |
c3754e9c MA |
283 | ptr--; |
284 | for (j = 0; j < 3; j++) { | |
71a7f956 | 285 | val = readl(ptr); |
286 | ecc_code[i++] = (val >> 24) & 0xFF; | |
287 | ecc_code[i++] = (val >> 16) & 0xFF; | |
288 | ecc_code[i++] = (val >> 8) & 0xFF; | |
289 | ecc_code[i++] = (val >> 0) & 0xFF; | |
c3754e9c MA |
290 | ptr--; |
291 | } | |
71a7f956 | 292 | break; |
293 | default: | |
294 | return -EINVAL; | |
295 | } | |
296 | /* ECC scheme specific syndrome customizations */ | |
9233279f | 297 | switch (info->ecc_scheme) { |
71a7f956 | 298 | case OMAP_ECC_HAM1_CODE_HW: |
299 | break; | |
300 | #ifdef CONFIG_BCH | |
301 | case OMAP_ECC_BCH8_CODE_HW_DETECTION_SW: | |
302 | ||
303 | for (i = 0; i < chip->ecc.bytes; i++) | |
304 | *(ecc_code + i) = *(ecc_code + i) ^ | |
305 | bch8_polynomial[i]; | |
306 | break; | |
307 | #endif | |
308 | case OMAP_ECC_BCH8_CODE_HW: | |
309 | ecc_code[chip->ecc.bytes - 1] = 0x00; | |
310 | break; | |
311 | default: | |
312 | return -EINVAL; | |
c3754e9c | 313 | } |
71a7f956 | 314 | return 0; |
c3754e9c MA |
315 | } |
316 | ||
71a7f956 | 317 | #ifdef CONFIG_NAND_OMAP_ELM |
c3754e9c MA |
318 | /* |
319 | * omap_correct_data_bch - Compares the ecc read from nand spare area | |
320 | * with ECC registers values and corrects one bit error if it has occured | |
321 | * | |
322 | * @mtd: MTD device structure | |
323 | * @dat: page data | |
324 | * @read_ecc: ecc read from nand flash (ignored) | |
325 | * @calc_ecc: ecc read from ECC registers | |
326 | * | |
327 | * @return 0 if data is OK or corrected, else returns -1 | |
328 | */ | |
329 | static int omap_correct_data_bch(struct mtd_info *mtd, uint8_t *dat, | |
330 | uint8_t *read_ecc, uint8_t *calc_ecc) | |
331 | { | |
332 | struct nand_chip *chip = mtd->priv; | |
9233279f | 333 | struct omap_nand_info *info = chip->priv; |
a09431da | 334 | struct nand_ecc_ctrl *ecc = &chip->ecc; |
6e562b11 | 335 | uint32_t error_count = 0, error_max; |
c3754e9c | 336 | uint32_t error_loc[8]; |
d21e77ff | 337 | enum bch_level bch_type; |
6e562b11 | 338 | uint32_t i, ecc_flag = 0; |
339 | uint8_t count, err = 0; | |
340 | uint32_t byte_pos, bit_pos; | |
341 | ||
342 | /* check calculated ecc */ | |
a09431da | 343 | for (i = 0; i < ecc->bytes && !ecc_flag; i++) { |
6e562b11 | 344 | if (calc_ecc[i] != 0x00) |
345 | ecc_flag = 1; | |
346 | } | |
347 | if (!ecc_flag) | |
348 | return 0; | |
c3754e9c | 349 | |
6e562b11 | 350 | /* check for whether its a erased-page */ |
c3754e9c | 351 | ecc_flag = 0; |
a09431da | 352 | for (i = 0; i < ecc->bytes && !ecc_flag; i++) { |
c3754e9c MA |
353 | if (read_ecc[i] != 0xff) |
354 | ecc_flag = 1; | |
6e562b11 | 355 | } |
c3754e9c MA |
356 | if (!ecc_flag) |
357 | return 0; | |
358 | ||
c3754e9c MA |
359 | /* |
360 | * while reading ECC result we read it in big endian. | |
361 | * Hence while loading to ELM we have rotate to get the right endian. | |
362 | */ | |
9233279f | 363 | switch (info->ecc_scheme) { |
6e562b11 | 364 | case OMAP_ECC_BCH8_CODE_HW: |
d21e77ff | 365 | bch_type = BCH_8_BIT; |
a09431da | 366 | omap_reverse_list(calc_ecc, ecc->bytes - 1); |
6e562b11 | 367 | break; |
368 | default: | |
369 | return -EINVAL; | |
370 | } | |
c3754e9c | 371 | /* use elm module to check for errors */ |
d21e77ff | 372 | elm_config(bch_type); |
3f990dc8 | 373 | err = elm_check_error(calc_ecc, bch_type, &error_count, error_loc); |
374 | if (err) | |
375 | return err; | |
376 | ||
c3754e9c | 377 | /* correct bch error */ |
6e562b11 | 378 | for (count = 0; count < error_count; count++) { |
9233279f | 379 | switch (info->ecc_scheme) { |
d21e77ff | 380 | case OMAP_ECC_BCH8_CODE_HW: |
6e562b11 | 381 | /* 14th byte in ECC is reserved to match ROM layout */ |
a09431da | 382 | error_max = SECTOR_BYTES + (ecc->bytes - 1); |
6e562b11 | 383 | break; |
384 | default: | |
385 | return -EINVAL; | |
386 | } | |
387 | byte_pos = error_max - (error_loc[count] / 8) - 1; | |
388 | bit_pos = error_loc[count] % 8; | |
389 | if (byte_pos < SECTOR_BYTES) { | |
390 | dat[byte_pos] ^= 1 << bit_pos; | |
391 | printf("nand: bit-flip corrected @data=%d\n", byte_pos); | |
392 | } else if (byte_pos < error_max) { | |
97eeae1a | 393 | read_ecc[byte_pos - SECTOR_BYTES] ^= 1 << bit_pos; |
6e562b11 | 394 | printf("nand: bit-flip corrected @oob=%d\n", byte_pos - |
395 | SECTOR_BYTES); | |
396 | } else { | |
397 | err = -EBADMSG; | |
398 | printf("nand: error: invalid bit-flip location\n"); | |
399 | } | |
400 | } | |
401 | return (err) ? err : error_count; | |
c3754e9c | 402 | } |
c3754e9c MA |
403 | |
404 | /** | |
405 | * omap_read_page_bch - hardware ecc based page read function | |
406 | * @mtd: mtd info structure | |
407 | * @chip: nand chip info structure | |
408 | * @buf: buffer to store read data | |
dfe64e2c | 409 | * @oob_required: caller expects OOB data read to chip->oob_poi |
c3754e9c MA |
410 | * @page: page number to read |
411 | * | |
412 | */ | |
413 | static int omap_read_page_bch(struct mtd_info *mtd, struct nand_chip *chip, | |
dfe64e2c | 414 | uint8_t *buf, int oob_required, int page) |
c3754e9c MA |
415 | { |
416 | int i, eccsize = chip->ecc.size; | |
417 | int eccbytes = chip->ecc.bytes; | |
418 | int eccsteps = chip->ecc.steps; | |
419 | uint8_t *p = buf; | |
420 | uint8_t *ecc_calc = chip->buffers->ecccalc; | |
421 | uint8_t *ecc_code = chip->buffers->ecccode; | |
422 | uint32_t *eccpos = chip->ecc.layout->eccpos; | |
423 | uint8_t *oob = chip->oob_poi; | |
424 | uint32_t data_pos; | |
425 | uint32_t oob_pos; | |
426 | ||
427 | data_pos = 0; | |
428 | /* oob area start */ | |
429 | oob_pos = (eccsize * eccsteps) + chip->ecc.layout->eccpos[0]; | |
430 | oob += chip->ecc.layout->eccpos[0]; | |
431 | ||
432 | for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize, | |
433 | oob += eccbytes) { | |
434 | chip->ecc.hwctl(mtd, NAND_ECC_READ); | |
435 | /* read data */ | |
436 | chip->cmdfunc(mtd, NAND_CMD_RNDOUT, data_pos, page); | |
437 | chip->read_buf(mtd, p, eccsize); | |
438 | ||
439 | /* read respective ecc from oob area */ | |
440 | chip->cmdfunc(mtd, NAND_CMD_RNDOUT, oob_pos, page); | |
441 | chip->read_buf(mtd, oob, eccbytes); | |
442 | /* read syndrome */ | |
443 | chip->ecc.calculate(mtd, p, &ecc_calc[i]); | |
444 | ||
445 | data_pos += eccsize; | |
446 | oob_pos += eccbytes; | |
447 | } | |
448 | ||
449 | for (i = 0; i < chip->ecc.total; i++) | |
450 | ecc_code[i] = chip->oob_poi[eccpos[i]]; | |
451 | ||
452 | eccsteps = chip->ecc.steps; | |
453 | p = buf; | |
454 | ||
455 | for (i = 0 ; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { | |
456 | int stat; | |
457 | ||
458 | stat = chip->ecc.correct(mtd, p, &ecc_code[i], &ecc_calc[i]); | |
459 | if (stat < 0) | |
460 | mtd->ecc_stats.failed++; | |
461 | else | |
462 | mtd->ecc_stats.corrected += stat; | |
463 | } | |
464 | return 0; | |
465 | } | |
d016dc42 | 466 | #endif /* CONFIG_NAND_OMAP_ELM */ |
c3754e9c | 467 | |
4a093006 AB |
468 | /* |
469 | * OMAP3 BCH8 support (with BCH library) | |
470 | */ | |
d016dc42 | 471 | #ifdef CONFIG_BCH |
4a093006 | 472 | /** |
d016dc42 | 473 | * omap_correct_data_bch_sw - Decode received data and correct errors |
4a093006 AB |
474 | * @mtd: MTD device structure |
475 | * @data: page data | |
476 | * @read_ecc: ecc read from nand flash | |
477 | * @calc_ecc: ecc read from HW ECC registers | |
478 | */ | |
d016dc42 | 479 | static int omap_correct_data_bch_sw(struct mtd_info *mtd, u_char *data, |
4a093006 AB |
480 | u_char *read_ecc, u_char *calc_ecc) |
481 | { | |
482 | int i, count; | |
483 | /* cannot correct more than 8 errors */ | |
484 | unsigned int errloc[8]; | |
485 | struct nand_chip *chip = mtd->priv; | |
9233279f | 486 | struct omap_nand_info *info = chip->priv; |
4a093006 | 487 | |
9233279f | 488 | count = decode_bch(info->control, NULL, 512, read_ecc, calc_ecc, |
489 | NULL, errloc); | |
4a093006 AB |
490 | if (count > 0) { |
491 | /* correct errors */ | |
492 | for (i = 0; i < count; i++) { | |
493 | /* correct data only, not ecc bytes */ | |
494 | if (errloc[i] < 8*512) | |
495 | data[errloc[i]/8] ^= 1 << (errloc[i] & 7); | |
496 | printf("corrected bitflip %u\n", errloc[i]); | |
497 | #ifdef DEBUG | |
498 | puts("read_ecc: "); | |
499 | /* | |
500 | * BCH8 have 13 bytes of ECC; BCH4 needs adoption | |
501 | * here! | |
502 | */ | |
503 | for (i = 0; i < 13; i++) | |
504 | printf("%02x ", read_ecc[i]); | |
505 | puts("\n"); | |
506 | puts("calc_ecc: "); | |
507 | for (i = 0; i < 13; i++) | |
508 | printf("%02x ", calc_ecc[i]); | |
509 | puts("\n"); | |
510 | #endif | |
511 | } | |
512 | } else if (count < 0) { | |
513 | puts("ecc unrecoverable error\n"); | |
514 | } | |
515 | return count; | |
516 | } | |
517 | ||
518 | /** | |
519 | * omap_free_bch - Release BCH ecc resources | |
520 | * @mtd: MTD device structure | |
521 | */ | |
522 | static void __maybe_unused omap_free_bch(struct mtd_info *mtd) | |
523 | { | |
524 | struct nand_chip *chip = mtd->priv; | |
9233279f | 525 | struct omap_nand_info *info = chip->priv; |
4a093006 | 526 | |
9233279f | 527 | if (info->control) { |
528 | free_bch(info->control); | |
529 | info->control = NULL; | |
4a093006 AB |
530 | } |
531 | } | |
d016dc42 | 532 | #endif /* CONFIG_BCH */ |
533 | ||
534 | /** | |
535 | * omap_select_ecc_scheme - configures driver for particular ecc-scheme | |
536 | * @nand: NAND chip device structure | |
537 | * @ecc_scheme: ecc scheme to configure | |
538 | * @pagesize: number of main-area bytes per page of NAND device | |
539 | * @oobsize: number of OOB/spare bytes per page of NAND device | |
540 | */ | |
541 | static int omap_select_ecc_scheme(struct nand_chip *nand, | |
542 | enum omap_ecc ecc_scheme, unsigned int pagesize, unsigned int oobsize) { | |
9233279f | 543 | struct omap_nand_info *info = nand->priv; |
eb237a15 | 544 | struct nand_ecclayout *ecclayout = &omap_ecclayout; |
d016dc42 | 545 | int eccsteps = pagesize / SECTOR_BYTES; |
546 | int i; | |
547 | ||
548 | switch (ecc_scheme) { | |
549 | case OMAP_ECC_HAM1_CODE_SW: | |
550 | debug("nand: selected OMAP_ECC_HAM1_CODE_SW\n"); | |
551 | /* For this ecc-scheme, ecc.bytes, ecc.layout, ... are | |
552 | * initialized in nand_scan_tail(), so just set ecc.mode */ | |
9233279f | 553 | info->control = NULL; |
d016dc42 | 554 | nand->ecc.mode = NAND_ECC_SOFT; |
555 | nand->ecc.layout = NULL; | |
2528460c | 556 | nand->ecc.size = 0; |
d016dc42 | 557 | break; |
558 | ||
559 | case OMAP_ECC_HAM1_CODE_HW: | |
560 | debug("nand: selected OMAP_ECC_HAM1_CODE_HW\n"); | |
561 | /* check ecc-scheme requirements before updating ecc info */ | |
562 | if ((3 * eccsteps) + BADBLOCK_MARKER_LENGTH > oobsize) { | |
563 | printf("nand: error: insufficient OOB: require=%d\n", ( | |
564 | (3 * eccsteps) + BADBLOCK_MARKER_LENGTH)); | |
565 | return -EINVAL; | |
566 | } | |
9233279f | 567 | info->control = NULL; |
d016dc42 | 568 | /* populate ecc specific fields */ |
fcd05245 | 569 | memset(&nand->ecc, 0, sizeof(struct nand_ecc_ctrl)); |
d016dc42 | 570 | nand->ecc.mode = NAND_ECC_HW; |
571 | nand->ecc.strength = 1; | |
572 | nand->ecc.size = SECTOR_BYTES; | |
573 | nand->ecc.bytes = 3; | |
574 | nand->ecc.hwctl = omap_enable_hwecc; | |
575 | nand->ecc.correct = omap_correct_data; | |
576 | nand->ecc.calculate = omap_calculate_ecc; | |
577 | /* define ecc-layout */ | |
578 | ecclayout->eccbytes = nand->ecc.bytes * eccsteps; | |
69cc97f8 | 579 | for (i = 0; i < ecclayout->eccbytes; i++) { |
580 | if (nand->options & NAND_BUSWIDTH_16) | |
581 | ecclayout->eccpos[i] = i + 2; | |
582 | else | |
583 | ecclayout->eccpos[i] = i + 1; | |
584 | } | |
d016dc42 | 585 | ecclayout->oobfree[0].offset = i + BADBLOCK_MARKER_LENGTH; |
586 | ecclayout->oobfree[0].length = oobsize - ecclayout->eccbytes - | |
587 | BADBLOCK_MARKER_LENGTH; | |
d016dc42 | 588 | break; |
589 | ||
590 | case OMAP_ECC_BCH8_CODE_HW_DETECTION_SW: | |
591 | #ifdef CONFIG_BCH | |
592 | debug("nand: selected OMAP_ECC_BCH8_CODE_HW_DETECTION_SW\n"); | |
593 | /* check ecc-scheme requirements before updating ecc info */ | |
594 | if ((13 * eccsteps) + BADBLOCK_MARKER_LENGTH > oobsize) { | |
595 | printf("nand: error: insufficient OOB: require=%d\n", ( | |
596 | (13 * eccsteps) + BADBLOCK_MARKER_LENGTH)); | |
597 | return -EINVAL; | |
598 | } | |
599 | /* check if BCH S/W library can be used for error detection */ | |
9233279f | 600 | info->control = init_bch(13, 8, 0x201b); |
601 | if (!info->control) { | |
d016dc42 | 602 | printf("nand: error: could not init_bch()\n"); |
603 | return -ENODEV; | |
604 | } | |
d016dc42 | 605 | /* populate ecc specific fields */ |
fcd05245 | 606 | memset(&nand->ecc, 0, sizeof(struct nand_ecc_ctrl)); |
d016dc42 | 607 | nand->ecc.mode = NAND_ECC_HW; |
608 | nand->ecc.strength = 8; | |
609 | nand->ecc.size = SECTOR_BYTES; | |
610 | nand->ecc.bytes = 13; | |
f5f1f614 | 611 | nand->ecc.hwctl = omap_enable_hwecc; |
d016dc42 | 612 | nand->ecc.correct = omap_correct_data_bch_sw; |
71a7f956 | 613 | nand->ecc.calculate = omap_calculate_ecc; |
d016dc42 | 614 | /* define ecc-layout */ |
615 | ecclayout->eccbytes = nand->ecc.bytes * eccsteps; | |
616 | ecclayout->eccpos[0] = BADBLOCK_MARKER_LENGTH; | |
617 | for (i = 1; i < ecclayout->eccbytes; i++) { | |
618 | if (i % nand->ecc.bytes) | |
619 | ecclayout->eccpos[i] = | |
620 | ecclayout->eccpos[i - 1] + 1; | |
621 | else | |
622 | ecclayout->eccpos[i] = | |
623 | ecclayout->eccpos[i - 1] + 2; | |
624 | } | |
625 | ecclayout->oobfree[0].offset = i + BADBLOCK_MARKER_LENGTH; | |
626 | ecclayout->oobfree[0].length = oobsize - ecclayout->eccbytes - | |
627 | BADBLOCK_MARKER_LENGTH; | |
d016dc42 | 628 | break; |
629 | #else | |
630 | printf("nand: error: CONFIG_BCH required for ECC\n"); | |
631 | return -EINVAL; | |
632 | #endif | |
633 | ||
634 | case OMAP_ECC_BCH8_CODE_HW: | |
635 | #ifdef CONFIG_NAND_OMAP_ELM | |
636 | debug("nand: selected OMAP_ECC_BCH8_CODE_HW\n"); | |
637 | /* check ecc-scheme requirements before updating ecc info */ | |
638 | if ((14 * eccsteps) + BADBLOCK_MARKER_LENGTH > oobsize) { | |
639 | printf("nand: error: insufficient OOB: require=%d\n", ( | |
640 | (14 * eccsteps) + BADBLOCK_MARKER_LENGTH)); | |
641 | return -EINVAL; | |
642 | } | |
643 | /* intialize ELM for ECC error detection */ | |
644 | elm_init(); | |
9233279f | 645 | info->control = NULL; |
d016dc42 | 646 | /* populate ecc specific fields */ |
fcd05245 | 647 | memset(&nand->ecc, 0, sizeof(struct nand_ecc_ctrl)); |
d016dc42 | 648 | nand->ecc.mode = NAND_ECC_HW; |
649 | nand->ecc.strength = 8; | |
650 | nand->ecc.size = SECTOR_BYTES; | |
651 | nand->ecc.bytes = 14; | |
f5f1f614 | 652 | nand->ecc.hwctl = omap_enable_hwecc; |
d016dc42 | 653 | nand->ecc.correct = omap_correct_data_bch; |
71a7f956 | 654 | nand->ecc.calculate = omap_calculate_ecc; |
d016dc42 | 655 | nand->ecc.read_page = omap_read_page_bch; |
656 | /* define ecc-layout */ | |
657 | ecclayout->eccbytes = nand->ecc.bytes * eccsteps; | |
658 | for (i = 0; i < ecclayout->eccbytes; i++) | |
659 | ecclayout->eccpos[i] = i + BADBLOCK_MARKER_LENGTH; | |
660 | ecclayout->oobfree[0].offset = i + BADBLOCK_MARKER_LENGTH; | |
661 | ecclayout->oobfree[0].length = oobsize - ecclayout->eccbytes - | |
662 | BADBLOCK_MARKER_LENGTH; | |
d016dc42 | 663 | break; |
664 | #else | |
665 | printf("nand: error: CONFIG_NAND_OMAP_ELM required for ECC\n"); | |
666 | return -EINVAL; | |
667 | #endif | |
668 | ||
669 | default: | |
670 | debug("nand: error: ecc scheme not enabled or supported\n"); | |
671 | return -EINVAL; | |
672 | } | |
eb237a15 NK |
673 | |
674 | /* nand_scan_tail() sets ham1 sw ecc; hw ecc layout is set by driver */ | |
675 | if (ecc_scheme != OMAP_ECC_HAM1_CODE_SW) | |
676 | nand->ecc.layout = ecclayout; | |
677 | ||
9233279f | 678 | info->ecc_scheme = ecc_scheme; |
d016dc42 | 679 | return 0; |
680 | } | |
4a093006 | 681 | |
12c2f1ee | 682 | #ifndef CONFIG_SPL_BUILD |
12201a13 | 683 | /* |
da634ae3 AB |
684 | * omap_nand_switch_ecc - switch the ECC operation between different engines |
685 | * (h/w and s/w) and different algorithms (hamming and BCHx) | |
12201a13 | 686 | * |
da634ae3 AB |
687 | * @hardware - true if one of the HW engines should be used |
688 | * @eccstrength - the number of bits that could be corrected | |
689 | * (1 - hamming, 4 - BCH4, 8 - BCH8, 16 - BCH16) | |
12201a13 | 690 | */ |
d016dc42 | 691 | int __maybe_unused omap_nand_switch_ecc(uint32_t hardware, uint32_t eccstrength) |
12201a13 DB |
692 | { |
693 | struct nand_chip *nand; | |
694 | struct mtd_info *mtd; | |
d016dc42 | 695 | int err = 0; |
12201a13 DB |
696 | |
697 | if (nand_curr_device < 0 || | |
698 | nand_curr_device >= CONFIG_SYS_MAX_NAND_DEVICE || | |
699 | !nand_info[nand_curr_device].name) { | |
d016dc42 | 700 | printf("nand: error: no NAND devices found\n"); |
701 | return -ENODEV; | |
12201a13 DB |
702 | } |
703 | ||
704 | mtd = &nand_info[nand_curr_device]; | |
705 | nand = mtd->priv; | |
12201a13 | 706 | nand->options |= NAND_OWN_BUFFERS; |
13fbde6e | 707 | nand->options &= ~NAND_SUBPAGE_READ; |
12201a13 | 708 | /* Setup the ecc configurations again */ |
da634ae3 AB |
709 | if (hardware) { |
710 | if (eccstrength == 1) { | |
d016dc42 | 711 | err = omap_select_ecc_scheme(nand, |
712 | OMAP_ECC_HAM1_CODE_HW, | |
713 | mtd->writesize, mtd->oobsize); | |
714 | } else if (eccstrength == 8) { | |
715 | err = omap_select_ecc_scheme(nand, | |
716 | OMAP_ECC_BCH8_CODE_HW, | |
717 | mtd->writesize, mtd->oobsize); | |
718 | } else { | |
719 | printf("nand: error: unsupported ECC scheme\n"); | |
720 | return -EINVAL; | |
da634ae3 | 721 | } |
12201a13 | 722 | } else { |
d016dc42 | 723 | err = omap_select_ecc_scheme(nand, OMAP_ECC_HAM1_CODE_SW, |
724 | mtd->writesize, mtd->oobsize); | |
12201a13 DB |
725 | } |
726 | ||
727 | /* Update NAND handling after ECC mode switch */ | |
d016dc42 | 728 | if (!err) |
729 | err = nand_scan_tail(mtd); | |
730 | return err; | |
12201a13 | 731 | } |
12c2f1ee | 732 | #endif /* CONFIG_SPL_BUILD */ |
12201a13 DB |
733 | |
734 | /* | |
735 | * Board-specific NAND initialization. The following members of the | |
736 | * argument are board-specific: | |
737 | * - IO_ADDR_R: address to read the 8 I/O lines of the flash device | |
738 | * - IO_ADDR_W: address to write the 8 I/O lines of the flash device | |
739 | * - cmd_ctrl: hardwarespecific function for accesing control-lines | |
740 | * - waitfunc: hardwarespecific function for accesing device ready/busy line | |
741 | * - ecc.hwctl: function to enable (reset) hardware ecc generator | |
742 | * - ecc.mode: mode of ecc, see defines | |
743 | * - chip_delay: chip dependent delay for transfering data from array to | |
744 | * read regs (tR) | |
745 | * - options: various chip options. They can partly be set to inform | |
746 | * nand_scan about special functionality. See the defines for further | |
747 | * explanation | |
748 | */ | |
749 | int board_nand_init(struct nand_chip *nand) | |
750 | { | |
751 | int32_t gpmc_config = 0; | |
752 | cs = 0; | |
d016dc42 | 753 | int err = 0; |
12201a13 DB |
754 | /* |
755 | * xloader/Uboot's gpmc configuration would have configured GPMC for | |
756 | * nand type of memory. The following logic scans and latches on to the | |
757 | * first CS with NAND type memory. | |
758 | * TBD: need to make this logic generic to handle multiple CS NAND | |
759 | * devices. | |
760 | */ | |
761 | while (cs < GPMC_MAX_CS) { | |
12201a13 | 762 | /* Check if NAND type is set */ |
89411352 | 763 | if ((readl(&gpmc_cfg->cs[cs].config1) & 0xC00) == 0x800) { |
12201a13 DB |
764 | /* Found it!! */ |
765 | break; | |
766 | } | |
767 | cs++; | |
768 | } | |
769 | if (cs >= GPMC_MAX_CS) { | |
d016dc42 | 770 | printf("nand: error: Unable to find NAND settings in " |
12201a13 DB |
771 | "GPMC Configuration - quitting\n"); |
772 | return -ENODEV; | |
773 | } | |
774 | ||
89411352 | 775 | gpmc_config = readl(&gpmc_cfg->config); |
12201a13 DB |
776 | /* Disable Write protect */ |
777 | gpmc_config |= 0x10; | |
89411352 | 778 | writel(gpmc_config, &gpmc_cfg->config); |
12201a13 | 779 | |
89411352 DB |
780 | nand->IO_ADDR_R = (void __iomem *)&gpmc_cfg->cs[cs].nand_dat; |
781 | nand->IO_ADDR_W = (void __iomem *)&gpmc_cfg->cs[cs].nand_cmd; | |
9233279f | 782 | nand->priv = &omap_nand_info; |
d016dc42 | 783 | nand->cmd_ctrl = omap_nand_hwcontrol; |
784 | nand->options |= NAND_NO_PADDING | NAND_CACHEPRG; | |
12201a13 | 785 | nand->chip_delay = 100; |
d016dc42 | 786 | nand->ecc.layout = &omap_ecclayout; |
787 | ||
b80a6603 | 788 | /* configure driver and controller based on NAND device bus-width */ |
789 | gpmc_config = readl(&gpmc_cfg->cs[cs].config1); | |
790 | #if defined(CONFIG_SYS_NAND_BUSWIDTH_16BIT) | |
791 | nand->options |= NAND_BUSWIDTH_16; | |
792 | writel(gpmc_config | (0x1 << 12), &gpmc_cfg->cs[cs].config1); | |
793 | #else | |
794 | nand->options &= ~NAND_BUSWIDTH_16; | |
795 | writel(gpmc_config & ~(0x1 << 12), &gpmc_cfg->cs[cs].config1); | |
796 | #endif | |
d016dc42 | 797 | /* select ECC scheme */ |
3f719069 | 798 | #if defined(CONFIG_NAND_OMAP_ECCSCHEME) |
799 | err = omap_select_ecc_scheme(nand, CONFIG_NAND_OMAP_ECCSCHEME, | |
d016dc42 | 800 | CONFIG_SYS_NAND_PAGE_SIZE, CONFIG_SYS_NAND_OOBSIZE); |
3f719069 | 801 | #else |
802 | /* pagesize and oobsize are not required to configure sw ecc-scheme */ | |
d016dc42 | 803 | err = omap_select_ecc_scheme(nand, OMAP_ECC_HAM1_CODE_SW, |
804 | 0, 0); | |
c3754e9c | 805 | #endif |
d016dc42 | 806 | if (err) |
807 | return err; | |
12c2f1ee | 808 | |
ff62fb4c | 809 | #ifdef CONFIG_SPL_BUILD |
12c2f1ee SS |
810 | if (nand->options & NAND_BUSWIDTH_16) |
811 | nand->read_buf = nand_read_buf16; | |
812 | else | |
813 | nand->read_buf = nand_read_buf; | |
814 | nand->dev_ready = omap_spl_dev_ready; | |
815 | #endif | |
12201a13 DB |
816 | |
817 | return 0; | |
818 | } |