Commit | Line | Data |
---|---|---|
6028aa01 YS |
1 | /* |
2 | * SuperH FLCTL nand controller | |
3 | * | |
b79c7adf MD |
4 | * Copyright (c) 2008 Renesas Solutions Corp. |
5 | * Copyright (c) 2008 Atom Create Engineering Co., Ltd. | |
6028aa01 | 6 | * |
b79c7adf | 7 | * Based on fsl_elbc_nand.c, Copyright (c) 2006-2007 Freescale Semiconductor |
6028aa01 YS |
8 | * |
9 | * This program is free software; you can redistribute it and/or modify | |
10 | * it under the terms of the GNU General Public License as published by | |
11 | * the Free Software Foundation; version 2 of the License. | |
12 | * | |
13 | * This program is distributed in the hope that it will be useful, | |
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
16 | * GNU General Public License for more details. | |
17 | * | |
18 | * You should have received a copy of the GNU General Public License | |
19 | * along with this program; if not, write to the Free Software | |
20 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | |
21 | * | |
22 | */ | |
23 | ||
24 | #include <linux/module.h> | |
25 | #include <linux/kernel.h> | |
26 | #include <linux/delay.h> | |
3c7ea4ec | 27 | #include <linux/interrupt.h> |
6028aa01 YS |
28 | #include <linux/io.h> |
29 | #include <linux/platform_device.h> | |
cfe78194 | 30 | #include <linux/pm_runtime.h> |
5a0e3ad6 | 31 | #include <linux/slab.h> |
6028aa01 YS |
32 | |
33 | #include <linux/mtd/mtd.h> | |
34 | #include <linux/mtd/nand.h> | |
35 | #include <linux/mtd/partitions.h> | |
36 | #include <linux/mtd/sh_flctl.h> | |
37 | ||
38 | static struct nand_ecclayout flctl_4secc_oob_16 = { | |
39 | .eccbytes = 10, | |
40 | .eccpos = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}, | |
41 | .oobfree = { | |
42 | {.offset = 12, | |
43 | . length = 4} }, | |
44 | }; | |
45 | ||
46 | static struct nand_ecclayout flctl_4secc_oob_64 = { | |
aa32d1f0 BH |
47 | .eccbytes = 4 * 10, |
48 | .eccpos = { | |
49 | 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, | |
50 | 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, | |
51 | 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, | |
52 | 54, 55, 56, 57, 58, 59, 60, 61, 62, 63 }, | |
6028aa01 | 53 | .oobfree = { |
aa32d1f0 BH |
54 | {.offset = 2, .length = 4}, |
55 | {.offset = 16, .length = 6}, | |
56 | {.offset = 32, .length = 6}, | |
57 | {.offset = 48, .length = 6} }, | |
6028aa01 YS |
58 | }; |
59 | ||
60 | static uint8_t scan_ff_pattern[] = { 0xff, 0xff }; | |
61 | ||
62 | static struct nand_bbt_descr flctl_4secc_smallpage = { | |
63 | .options = NAND_BBT_SCAN2NDPAGE, | |
64 | .offs = 11, | |
65 | .len = 1, | |
66 | .pattern = scan_ff_pattern, | |
67 | }; | |
68 | ||
69 | static struct nand_bbt_descr flctl_4secc_largepage = { | |
c0e6616a | 70 | .options = NAND_BBT_SCAN2NDPAGE, |
aa32d1f0 | 71 | .offs = 0, |
6028aa01 YS |
72 | .len = 2, |
73 | .pattern = scan_ff_pattern, | |
74 | }; | |
75 | ||
76 | static void empty_fifo(struct sh_flctl *flctl) | |
77 | { | |
3c7ea4ec BH |
78 | writel(flctl->flintdmacr_base | AC1CLR | AC0CLR, FLINTDMACR(flctl)); |
79 | writel(flctl->flintdmacr_base, FLINTDMACR(flctl)); | |
6028aa01 YS |
80 | } |
81 | ||
82 | static void start_translation(struct sh_flctl *flctl) | |
83 | { | |
84 | writeb(TRSTRT, FLTRCR(flctl)); | |
85 | } | |
86 | ||
b79c7adf MD |
87 | static void timeout_error(struct sh_flctl *flctl, const char *str) |
88 | { | |
25985edc | 89 | dev_err(&flctl->pdev->dev, "Timeout occurred in %s\n", str); |
b79c7adf MD |
90 | } |
91 | ||
6028aa01 YS |
92 | static void wait_completion(struct sh_flctl *flctl) |
93 | { | |
94 | uint32_t timeout = LOOP_TIMEOUT_MAX; | |
95 | ||
96 | while (timeout--) { | |
97 | if (readb(FLTRCR(flctl)) & TREND) { | |
98 | writeb(0x0, FLTRCR(flctl)); | |
99 | return; | |
100 | } | |
101 | udelay(1); | |
102 | } | |
103 | ||
b79c7adf | 104 | timeout_error(flctl, __func__); |
6028aa01 YS |
105 | writeb(0x0, FLTRCR(flctl)); |
106 | } | |
107 | ||
108 | static void set_addr(struct mtd_info *mtd, int column, int page_addr) | |
109 | { | |
110 | struct sh_flctl *flctl = mtd_to_flctl(mtd); | |
111 | uint32_t addr = 0; | |
112 | ||
113 | if (column == -1) { | |
114 | addr = page_addr; /* ERASE1 */ | |
115 | } else if (page_addr != -1) { | |
116 | /* SEQIN, READ0, etc.. */ | |
010ab820 MD |
117 | if (flctl->chip.options & NAND_BUSWIDTH_16) |
118 | column >>= 1; | |
6028aa01 YS |
119 | if (flctl->page_size) { |
120 | addr = column & 0x0FFF; | |
121 | addr |= (page_addr & 0xff) << 16; | |
122 | addr |= ((page_addr >> 8) & 0xff) << 24; | |
123 | /* big than 128MB */ | |
124 | if (flctl->rw_ADRCNT == ADRCNT2_E) { | |
125 | uint32_t addr2; | |
126 | addr2 = (page_addr >> 16) & 0xff; | |
127 | writel(addr2, FLADR2(flctl)); | |
128 | } | |
129 | } else { | |
130 | addr = column; | |
131 | addr |= (page_addr & 0xff) << 8; | |
132 | addr |= ((page_addr >> 8) & 0xff) << 16; | |
133 | addr |= ((page_addr >> 16) & 0xff) << 24; | |
134 | } | |
135 | } | |
136 | writel(addr, FLADR(flctl)); | |
137 | } | |
138 | ||
139 | static void wait_rfifo_ready(struct sh_flctl *flctl) | |
140 | { | |
141 | uint32_t timeout = LOOP_TIMEOUT_MAX; | |
142 | ||
143 | while (timeout--) { | |
144 | uint32_t val; | |
145 | /* check FIFO */ | |
146 | val = readl(FLDTCNTR(flctl)) >> 16; | |
147 | if (val & 0xFF) | |
148 | return; | |
149 | udelay(1); | |
150 | } | |
b79c7adf | 151 | timeout_error(flctl, __func__); |
6028aa01 YS |
152 | } |
153 | ||
154 | static void wait_wfifo_ready(struct sh_flctl *flctl) | |
155 | { | |
156 | uint32_t len, timeout = LOOP_TIMEOUT_MAX; | |
157 | ||
158 | while (timeout--) { | |
159 | /* check FIFO */ | |
160 | len = (readl(FLDTCNTR(flctl)) >> 16) & 0xFF; | |
161 | if (len >= 4) | |
162 | return; | |
163 | udelay(1); | |
164 | } | |
b79c7adf | 165 | timeout_error(flctl, __func__); |
6028aa01 YS |
166 | } |
167 | ||
c0e6616a | 168 | static int wait_recfifo_ready(struct sh_flctl *flctl, int sector_number) |
6028aa01 YS |
169 | { |
170 | uint32_t timeout = LOOP_TIMEOUT_MAX; | |
171 | int checked[4]; | |
172 | void __iomem *ecc_reg[4]; | |
173 | int i; | |
174 | uint32_t data, size; | |
175 | ||
176 | memset(checked, 0, sizeof(checked)); | |
177 | ||
178 | while (timeout--) { | |
179 | size = readl(FLDTCNTR(flctl)) >> 24; | |
180 | if (size & 0xFF) | |
181 | return 0; /* success */ | |
182 | ||
183 | if (readl(FL4ECCCR(flctl)) & _4ECCFA) | |
184 | return 1; /* can't correct */ | |
185 | ||
186 | udelay(1); | |
187 | if (!(readl(FL4ECCCR(flctl)) & _4ECCEND)) | |
188 | continue; | |
189 | ||
190 | /* start error correction */ | |
191 | ecc_reg[0] = FL4ECCRESULT0(flctl); | |
192 | ecc_reg[1] = FL4ECCRESULT1(flctl); | |
193 | ecc_reg[2] = FL4ECCRESULT2(flctl); | |
194 | ecc_reg[3] = FL4ECCRESULT3(flctl); | |
195 | ||
196 | for (i = 0; i < 3; i++) { | |
197 | data = readl(ecc_reg[i]); | |
198 | if (data != INIT_FL4ECCRESULT_VAL && !checked[i]) { | |
199 | uint8_t org; | |
200 | int index; | |
201 | ||
c0e6616a YS |
202 | if (flctl->page_size) |
203 | index = (512 * sector_number) + | |
204 | (data >> 16); | |
205 | else | |
206 | index = data >> 16; | |
207 | ||
6028aa01 YS |
208 | org = flctl->done_buff[index]; |
209 | flctl->done_buff[index] = org ^ (data & 0xFF); | |
210 | checked[i] = 1; | |
211 | } | |
212 | } | |
213 | ||
214 | writel(0, FL4ECCCR(flctl)); | |
215 | } | |
216 | ||
b79c7adf | 217 | timeout_error(flctl, __func__); |
6028aa01 YS |
218 | return 1; /* timeout */ |
219 | } | |
220 | ||
221 | static void wait_wecfifo_ready(struct sh_flctl *flctl) | |
222 | { | |
223 | uint32_t timeout = LOOP_TIMEOUT_MAX; | |
224 | uint32_t len; | |
225 | ||
226 | while (timeout--) { | |
227 | /* check FLECFIFO */ | |
228 | len = (readl(FLDTCNTR(flctl)) >> 24) & 0xFF; | |
229 | if (len >= 4) | |
230 | return; | |
231 | udelay(1); | |
232 | } | |
b79c7adf | 233 | timeout_error(flctl, __func__); |
6028aa01 YS |
234 | } |
235 | ||
236 | static void read_datareg(struct sh_flctl *flctl, int offset) | |
237 | { | |
238 | unsigned long data; | |
239 | unsigned long *buf = (unsigned long *)&flctl->done_buff[offset]; | |
240 | ||
241 | wait_completion(flctl); | |
242 | ||
243 | data = readl(FLDATAR(flctl)); | |
244 | *buf = le32_to_cpu(data); | |
245 | } | |
246 | ||
247 | static void read_fiforeg(struct sh_flctl *flctl, int rlen, int offset) | |
248 | { | |
249 | int i, len_4align; | |
250 | unsigned long *buf = (unsigned long *)&flctl->done_buff[offset]; | |
251 | void *fifo_addr = (void *)FLDTFIFO(flctl); | |
252 | ||
253 | len_4align = (rlen + 3) / 4; | |
254 | ||
255 | for (i = 0; i < len_4align; i++) { | |
256 | wait_rfifo_ready(flctl); | |
257 | buf[i] = readl(fifo_addr); | |
258 | buf[i] = be32_to_cpu(buf[i]); | |
259 | } | |
260 | } | |
261 | ||
c0e6616a | 262 | static int read_ecfiforeg(struct sh_flctl *flctl, uint8_t *buff, int sector) |
6028aa01 YS |
263 | { |
264 | int i; | |
265 | unsigned long *ecc_buf = (unsigned long *)buff; | |
266 | void *fifo_addr = (void *)FLECFIFO(flctl); | |
267 | ||
268 | for (i = 0; i < 4; i++) { | |
c0e6616a | 269 | if (wait_recfifo_ready(flctl , sector)) |
6028aa01 YS |
270 | return 1; |
271 | ecc_buf[i] = readl(fifo_addr); | |
272 | ecc_buf[i] = be32_to_cpu(ecc_buf[i]); | |
273 | } | |
274 | ||
275 | return 0; | |
276 | } | |
277 | ||
278 | static void write_fiforeg(struct sh_flctl *flctl, int rlen, int offset) | |
279 | { | |
280 | int i, len_4align; | |
281 | unsigned long *data = (unsigned long *)&flctl->done_buff[offset]; | |
282 | void *fifo_addr = (void *)FLDTFIFO(flctl); | |
283 | ||
284 | len_4align = (rlen + 3) / 4; | |
285 | for (i = 0; i < len_4align; i++) { | |
286 | wait_wfifo_ready(flctl); | |
287 | writel(cpu_to_be32(data[i]), fifo_addr); | |
288 | } | |
289 | } | |
290 | ||
291 | static void set_cmd_regs(struct mtd_info *mtd, uint32_t cmd, uint32_t flcmcdr_val) | |
292 | { | |
293 | struct sh_flctl *flctl = mtd_to_flctl(mtd); | |
0b3f0d12 | 294 | uint32_t flcmncr_val = flctl->flcmncr_base & ~SEL_16BIT; |
6028aa01 YS |
295 | uint32_t flcmdcr_val, addr_len_bytes = 0; |
296 | ||
297 | /* Set SNAND bit if page size is 2048byte */ | |
298 | if (flctl->page_size) | |
299 | flcmncr_val |= SNAND_E; | |
300 | else | |
301 | flcmncr_val &= ~SNAND_E; | |
302 | ||
303 | /* default FLCMDCR val */ | |
304 | flcmdcr_val = DOCMD1_E | DOADR_E; | |
305 | ||
306 | /* Set for FLCMDCR */ | |
307 | switch (cmd) { | |
308 | case NAND_CMD_ERASE1: | |
309 | addr_len_bytes = flctl->erase_ADRCNT; | |
310 | flcmdcr_val |= DOCMD2_E; | |
311 | break; | |
312 | case NAND_CMD_READ0: | |
313 | case NAND_CMD_READOOB: | |
dd5ab248 | 314 | case NAND_CMD_RNDOUT: |
6028aa01 YS |
315 | addr_len_bytes = flctl->rw_ADRCNT; |
316 | flcmdcr_val |= CDSRC_E; | |
010ab820 MD |
317 | if (flctl->chip.options & NAND_BUSWIDTH_16) |
318 | flcmncr_val |= SEL_16BIT; | |
6028aa01 YS |
319 | break; |
320 | case NAND_CMD_SEQIN: | |
321 | /* This case is that cmd is READ0 or READ1 or READ00 */ | |
322 | flcmdcr_val &= ~DOADR_E; /* ONLY execute 1st cmd */ | |
323 | break; | |
324 | case NAND_CMD_PAGEPROG: | |
325 | addr_len_bytes = flctl->rw_ADRCNT; | |
35a34799 | 326 | flcmdcr_val |= DOCMD2_E | CDSRC_E | SELRW; |
010ab820 MD |
327 | if (flctl->chip.options & NAND_BUSWIDTH_16) |
328 | flcmncr_val |= SEL_16BIT; | |
35a34799 YS |
329 | break; |
330 | case NAND_CMD_READID: | |
331 | flcmncr_val &= ~SNAND_E; | |
7b6b2303 | 332 | flcmdcr_val |= CDSRC_E; |
35a34799 YS |
333 | addr_len_bytes = ADRCNT_1; |
334 | break; | |
335 | case NAND_CMD_STATUS: | |
336 | case NAND_CMD_RESET: | |
337 | flcmncr_val &= ~SNAND_E; | |
338 | flcmdcr_val &= ~(DOADR_E | DOSR_E); | |
339 | break; | |
340 | default: | |
341 | break; | |
342 | } | |
343 | ||
344 | /* Set address bytes parameter */ | |
345 | flcmdcr_val |= addr_len_bytes; | |
346 | ||
347 | /* Now actually write */ | |
348 | writel(flcmncr_val, FLCMNCR(flctl)); | |
349 | writel(flcmdcr_val, FLCMDCR(flctl)); | |
350 | writel(flcmcdr_val, FLCMCDR(flctl)); | |
351 | } | |
352 | ||
353 | static int flctl_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, | |
1fbb938d | 354 | uint8_t *buf, int oob_required, int page) |
35a34799 YS |
355 | { |
356 | int i, eccsize = chip->ecc.size; | |
357 | int eccbytes = chip->ecc.bytes; | |
358 | int eccsteps = chip->ecc.steps; | |
359 | uint8_t *p = buf; | |
360 | struct sh_flctl *flctl = mtd_to_flctl(mtd); | |
361 | ||
362 | for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) | |
363 | chip->read_buf(mtd, p, eccsize); | |
364 | ||
365 | for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { | |
366 | if (flctl->hwecc_cant_correct[i]) | |
367 | mtd->ecc_stats.failed++; | |
368 | else | |
3f91e94f | 369 | mtd->ecc_stats.corrected += 0; /* FIXME */ |
35a34799 YS |
370 | } |
371 | ||
372 | return 0; | |
373 | } | |
374 | ||
375 | static void flctl_write_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, | |
1fbb938d | 376 | const uint8_t *buf, int oob_required) |
35a34799 YS |
377 | { |
378 | int i, eccsize = chip->ecc.size; | |
379 | int eccbytes = chip->ecc.bytes; | |
380 | int eccsteps = chip->ecc.steps; | |
381 | const uint8_t *p = buf; | |
382 | ||
383 | for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) | |
384 | chip->write_buf(mtd, p, eccsize); | |
385 | } | |
386 | ||
387 | static void execmd_read_page_sector(struct mtd_info *mtd, int page_addr) | |
388 | { | |
389 | struct sh_flctl *flctl = mtd_to_flctl(mtd); | |
390 | int sector, page_sectors; | |
391 | ||
392 | if (flctl->page_size) | |
393 | page_sectors = 4; | |
394 | else | |
395 | page_sectors = 1; | |
396 | ||
397 | writel(readl(FLCMNCR(flctl)) | ACM_SACCES_MODE | _4ECCCORRECT, | |
398 | FLCMNCR(flctl)); | |
399 | ||
400 | set_cmd_regs(mtd, NAND_CMD_READ0, | |
401 | (NAND_CMD_READSTART << 8) | NAND_CMD_READ0); | |
402 | ||
403 | for (sector = 0; sector < page_sectors; sector++) { | |
404 | int ret; | |
405 | ||
406 | empty_fifo(flctl); | |
407 | writel(readl(FLCMDCR(flctl)) | 1, FLCMDCR(flctl)); | |
408 | writel(page_addr << 2 | sector, FLADR(flctl)); | |
409 | ||
410 | start_translation(flctl); | |
411 | read_fiforeg(flctl, 512, 512 * sector); | |
412 | ||
413 | ret = read_ecfiforeg(flctl, | |
c0e6616a YS |
414 | &flctl->done_buff[mtd->writesize + 16 * sector], |
415 | sector); | |
35a34799 YS |
416 | |
417 | if (ret) | |
418 | flctl->hwecc_cant_correct[sector] = 1; | |
419 | ||
420 | writel(0x0, FL4ECCCR(flctl)); | |
421 | wait_completion(flctl); | |
422 | } | |
423 | writel(readl(FLCMNCR(flctl)) & ~(ACM_SACCES_MODE | _4ECCCORRECT), | |
424 | FLCMNCR(flctl)); | |
425 | } | |
426 | ||
427 | static void execmd_read_oob(struct mtd_info *mtd, int page_addr) | |
428 | { | |
429 | struct sh_flctl *flctl = mtd_to_flctl(mtd); | |
ef4ce0bc BH |
430 | int page_sectors = flctl->page_size ? 4 : 1; |
431 | int i; | |
35a34799 YS |
432 | |
433 | set_cmd_regs(mtd, NAND_CMD_READ0, | |
434 | (NAND_CMD_READSTART << 8) | NAND_CMD_READ0); | |
435 | ||
436 | empty_fifo(flctl); | |
35a34799 | 437 | |
ef4ce0bc BH |
438 | for (i = 0; i < page_sectors; i++) { |
439 | set_addr(mtd, (512 + 16) * i + 512 , page_addr); | |
35a34799 YS |
440 | writel(16, FLDTCNTR(flctl)); |
441 | ||
442 | start_translation(flctl); | |
ef4ce0bc | 443 | read_fiforeg(flctl, 16, 16 * i); |
35a34799 YS |
444 | wait_completion(flctl); |
445 | } | |
446 | } | |
447 | ||
448 | static void execmd_write_page_sector(struct mtd_info *mtd) | |
449 | { | |
450 | struct sh_flctl *flctl = mtd_to_flctl(mtd); | |
451 | int i, page_addr = flctl->seqin_page_addr; | |
452 | int sector, page_sectors; | |
453 | ||
454 | if (flctl->page_size) | |
455 | page_sectors = 4; | |
456 | else | |
457 | page_sectors = 1; | |
458 | ||
459 | writel(readl(FLCMNCR(flctl)) | ACM_SACCES_MODE, FLCMNCR(flctl)); | |
460 | ||
461 | set_cmd_regs(mtd, NAND_CMD_PAGEPROG, | |
462 | (NAND_CMD_PAGEPROG << 8) | NAND_CMD_SEQIN); | |
463 | ||
464 | for (sector = 0; sector < page_sectors; sector++) { | |
465 | empty_fifo(flctl); | |
466 | writel(readl(FLCMDCR(flctl)) | 1, FLCMDCR(flctl)); | |
467 | writel(page_addr << 2 | sector, FLADR(flctl)); | |
468 | ||
469 | start_translation(flctl); | |
470 | write_fiforeg(flctl, 512, 512 * sector); | |
471 | ||
472 | for (i = 0; i < 4; i++) { | |
473 | wait_wecfifo_ready(flctl); /* wait for write ready */ | |
474 | writel(0xFFFFFFFF, FLECFIFO(flctl)); | |
475 | } | |
476 | wait_completion(flctl); | |
477 | } | |
478 | ||
479 | writel(readl(FLCMNCR(flctl)) & ~ACM_SACCES_MODE, FLCMNCR(flctl)); | |
480 | } | |
481 | ||
482 | static void execmd_write_oob(struct mtd_info *mtd) | |
483 | { | |
484 | struct sh_flctl *flctl = mtd_to_flctl(mtd); | |
485 | int page_addr = flctl->seqin_page_addr; | |
486 | int sector, page_sectors; | |
487 | ||
ef4ce0bc | 488 | page_sectors = flctl->page_size ? 4 : 1; |
35a34799 YS |
489 | |
490 | set_cmd_regs(mtd, NAND_CMD_PAGEPROG, | |
491 | (NAND_CMD_PAGEPROG << 8) | NAND_CMD_SEQIN); | |
492 | ||
ef4ce0bc | 493 | for (sector = 0; sector < page_sectors; sector++) { |
35a34799 YS |
494 | empty_fifo(flctl); |
495 | set_addr(mtd, sector * 528 + 512, page_addr); | |
496 | writel(16, FLDTCNTR(flctl)); /* set read size */ | |
497 | ||
498 | start_translation(flctl); | |
499 | write_fiforeg(flctl, 16, 16 * sector); | |
500 | wait_completion(flctl); | |
501 | } | |
502 | } | |
503 | ||
504 | static void flctl_cmdfunc(struct mtd_info *mtd, unsigned int command, | |
505 | int column, int page_addr) | |
506 | { | |
507 | struct sh_flctl *flctl = mtd_to_flctl(mtd); | |
508 | uint32_t read_cmd = 0; | |
509 | ||
cfe78194 BH |
510 | pm_runtime_get_sync(&flctl->pdev->dev); |
511 | ||
35a34799 YS |
512 | flctl->read_bytes = 0; |
513 | if (command != NAND_CMD_PAGEPROG) | |
514 | flctl->index = 0; | |
515 | ||
516 | switch (command) { | |
517 | case NAND_CMD_READ1: | |
518 | case NAND_CMD_READ0: | |
519 | if (flctl->hwecc) { | |
520 | /* read page with hwecc */ | |
521 | execmd_read_page_sector(mtd, page_addr); | |
522 | break; | |
523 | } | |
35a34799 YS |
524 | if (flctl->page_size) |
525 | set_cmd_regs(mtd, command, (NAND_CMD_READSTART << 8) | |
526 | | command); | |
527 | else | |
528 | set_cmd_regs(mtd, command, command); | |
529 | ||
530 | set_addr(mtd, 0, page_addr); | |
531 | ||
532 | flctl->read_bytes = mtd->writesize + mtd->oobsize; | |
010ab820 MD |
533 | if (flctl->chip.options & NAND_BUSWIDTH_16) |
534 | column >>= 1; | |
35a34799 YS |
535 | flctl->index += column; |
536 | goto read_normal_exit; | |
537 | ||
538 | case NAND_CMD_READOOB: | |
539 | if (flctl->hwecc) { | |
540 | /* read page with hwecc */ | |
541 | execmd_read_oob(mtd, page_addr); | |
542 | break; | |
543 | } | |
544 | ||
35a34799 YS |
545 | if (flctl->page_size) { |
546 | set_cmd_regs(mtd, command, (NAND_CMD_READSTART << 8) | |
547 | | NAND_CMD_READ0); | |
548 | set_addr(mtd, mtd->writesize, page_addr); | |
549 | } else { | |
550 | set_cmd_regs(mtd, command, command); | |
551 | set_addr(mtd, 0, page_addr); | |
552 | } | |
553 | flctl->read_bytes = mtd->oobsize; | |
554 | goto read_normal_exit; | |
555 | ||
dd5ab248 BH |
556 | case NAND_CMD_RNDOUT: |
557 | if (flctl->hwecc) | |
558 | break; | |
559 | ||
560 | if (flctl->page_size) | |
561 | set_cmd_regs(mtd, command, (NAND_CMD_RNDOUTSTART << 8) | |
562 | | command); | |
563 | else | |
564 | set_cmd_regs(mtd, command, command); | |
565 | ||
566 | set_addr(mtd, column, 0); | |
567 | ||
568 | flctl->read_bytes = mtd->writesize + mtd->oobsize - column; | |
569 | goto read_normal_exit; | |
570 | ||
35a34799 | 571 | case NAND_CMD_READID: |
35a34799 | 572 | set_cmd_regs(mtd, command, command); |
35a34799 | 573 | |
7b6b2303 BH |
574 | /* READID is always performed using an 8-bit bus */ |
575 | if (flctl->chip.options & NAND_BUSWIDTH_16) | |
576 | column <<= 1; | |
577 | set_addr(mtd, column, 0); | |
578 | ||
579 | flctl->read_bytes = 8; | |
35a34799 | 580 | writel(flctl->read_bytes, FLDTCNTR(flctl)); /* set read size */ |
abb59ef3 | 581 | empty_fifo(flctl); |
35a34799 | 582 | start_translation(flctl); |
7b6b2303 BH |
583 | read_fiforeg(flctl, flctl->read_bytes, 0); |
584 | wait_completion(flctl); | |
35a34799 YS |
585 | break; |
586 | ||
587 | case NAND_CMD_ERASE1: | |
588 | flctl->erase1_page_addr = page_addr; | |
589 | break; | |
590 | ||
591 | case NAND_CMD_ERASE2: | |
592 | set_cmd_regs(mtd, NAND_CMD_ERASE1, | |
593 | (command << 8) | NAND_CMD_ERASE1); | |
594 | set_addr(mtd, -1, flctl->erase1_page_addr); | |
595 | start_translation(flctl); | |
596 | wait_completion(flctl); | |
597 | break; | |
598 | ||
599 | case NAND_CMD_SEQIN: | |
600 | if (!flctl->page_size) { | |
601 | /* output read command */ | |
602 | if (column >= mtd->writesize) { | |
603 | column -= mtd->writesize; | |
604 | read_cmd = NAND_CMD_READOOB; | |
605 | } else if (column < 256) { | |
606 | read_cmd = NAND_CMD_READ0; | |
607 | } else { | |
608 | column -= 256; | |
609 | read_cmd = NAND_CMD_READ1; | |
610 | } | |
611 | } | |
612 | flctl->seqin_column = column; | |
613 | flctl->seqin_page_addr = page_addr; | |
614 | flctl->seqin_read_cmd = read_cmd; | |
615 | break; | |
616 | ||
617 | case NAND_CMD_PAGEPROG: | |
618 | empty_fifo(flctl); | |
619 | if (!flctl->page_size) { | |
620 | set_cmd_regs(mtd, NAND_CMD_SEQIN, | |
621 | flctl->seqin_read_cmd); | |
622 | set_addr(mtd, -1, -1); | |
623 | writel(0, FLDTCNTR(flctl)); /* set 0 size */ | |
624 | start_translation(flctl); | |
625 | wait_completion(flctl); | |
626 | } | |
627 | if (flctl->hwecc) { | |
628 | /* write page with hwecc */ | |
629 | if (flctl->seqin_column == mtd->writesize) | |
630 | execmd_write_oob(mtd); | |
631 | else if (!flctl->seqin_column) | |
632 | execmd_write_page_sector(mtd); | |
633 | else | |
634 | printk(KERN_ERR "Invalid address !?\n"); | |
635 | break; | |
636 | } | |
637 | set_cmd_regs(mtd, command, (command << 8) | NAND_CMD_SEQIN); | |
638 | set_addr(mtd, flctl->seqin_column, flctl->seqin_page_addr); | |
639 | writel(flctl->index, FLDTCNTR(flctl)); /* set write size */ | |
640 | start_translation(flctl); | |
641 | write_fiforeg(flctl, flctl->index, 0); | |
642 | wait_completion(flctl); | |
643 | break; | |
644 | ||
645 | case NAND_CMD_STATUS: | |
646 | set_cmd_regs(mtd, command, command); | |
647 | set_addr(mtd, -1, -1); | |
648 | ||
649 | flctl->read_bytes = 1; | |
650 | writel(flctl->read_bytes, FLDTCNTR(flctl)); /* set read size */ | |
651 | start_translation(flctl); | |
652 | read_datareg(flctl, 0); /* read and end */ | |
653 | break; | |
654 | ||
655 | case NAND_CMD_RESET: | |
656 | set_cmd_regs(mtd, command, command); | |
657 | set_addr(mtd, -1, -1); | |
658 | ||
659 | writel(0, FLDTCNTR(flctl)); /* set 0 size */ | |
660 | start_translation(flctl); | |
661 | wait_completion(flctl); | |
662 | break; | |
663 | ||
664 | default: | |
665 | break; | |
666 | } | |
cfe78194 | 667 | goto runtime_exit; |
35a34799 YS |
668 | |
669 | read_normal_exit: | |
670 | writel(flctl->read_bytes, FLDTCNTR(flctl)); /* set read size */ | |
abb59ef3 | 671 | empty_fifo(flctl); |
35a34799 YS |
672 | start_translation(flctl); |
673 | read_fiforeg(flctl, flctl->read_bytes, 0); | |
674 | wait_completion(flctl); | |
cfe78194 BH |
675 | runtime_exit: |
676 | pm_runtime_put_sync(&flctl->pdev->dev); | |
35a34799 YS |
677 | return; |
678 | } | |
679 | ||
680 | static void flctl_select_chip(struct mtd_info *mtd, int chipnr) | |
681 | { | |
682 | struct sh_flctl *flctl = mtd_to_flctl(mtd); | |
cfe78194 | 683 | int ret; |
35a34799 YS |
684 | |
685 | switch (chipnr) { | |
686 | case -1: | |
0b3f0d12 | 687 | flctl->flcmncr_base &= ~CE0_ENABLE; |
cfe78194 BH |
688 | |
689 | pm_runtime_get_sync(&flctl->pdev->dev); | |
0b3f0d12 | 690 | writel(flctl->flcmncr_base, FLCMNCR(flctl)); |
cfe78194 BH |
691 | |
692 | if (flctl->qos_request) { | |
693 | dev_pm_qos_remove_request(&flctl->pm_qos); | |
694 | flctl->qos_request = 0; | |
695 | } | |
696 | ||
697 | pm_runtime_put_sync(&flctl->pdev->dev); | |
35a34799 YS |
698 | break; |
699 | case 0: | |
0b3f0d12 | 700 | flctl->flcmncr_base |= CE0_ENABLE; |
cfe78194 BH |
701 | |
702 | if (!flctl->qos_request) { | |
703 | ret = dev_pm_qos_add_request(&flctl->pdev->dev, | |
704 | &flctl->pm_qos, 100); | |
705 | if (ret < 0) | |
706 | dev_err(&flctl->pdev->dev, | |
707 | "PM QoS request failed: %d\n", ret); | |
708 | flctl->qos_request = 1; | |
709 | } | |
710 | ||
711 | if (flctl->holden) { | |
712 | pm_runtime_get_sync(&flctl->pdev->dev); | |
3f2e924b | 713 | writel(HOLDEN, FLHOLDCR(flctl)); |
cfe78194 BH |
714 | pm_runtime_put_sync(&flctl->pdev->dev); |
715 | } | |
35a34799 YS |
716 | break; |
717 | default: | |
718 | BUG(); | |
719 | } | |
720 | } | |
721 | ||
722 | static void flctl_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len) | |
723 | { | |
724 | struct sh_flctl *flctl = mtd_to_flctl(mtd); | |
725 | int i, index = flctl->index; | |
726 | ||
727 | for (i = 0; i < len; i++) | |
728 | flctl->done_buff[index + i] = buf[i]; | |
729 | flctl->index += len; | |
730 | } | |
731 | ||
732 | static uint8_t flctl_read_byte(struct mtd_info *mtd) | |
733 | { | |
734 | struct sh_flctl *flctl = mtd_to_flctl(mtd); | |
735 | int index = flctl->index; | |
736 | uint8_t data; | |
737 | ||
738 | data = flctl->done_buff[index]; | |
739 | flctl->index++; | |
740 | return data; | |
741 | } | |
742 | ||
010ab820 MD |
743 | static uint16_t flctl_read_word(struct mtd_info *mtd) |
744 | { | |
745 | struct sh_flctl *flctl = mtd_to_flctl(mtd); | |
746 | int index = flctl->index; | |
747 | uint16_t data; | |
748 | uint16_t *buf = (uint16_t *)&flctl->done_buff[index]; | |
749 | ||
750 | data = *buf; | |
751 | flctl->index += 2; | |
752 | return data; | |
753 | } | |
754 | ||
35a34799 YS |
755 | static void flctl_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) |
756 | { | |
757 | int i; | |
758 | ||
759 | for (i = 0; i < len; i++) | |
760 | buf[i] = flctl_read_byte(mtd); | |
761 | } | |
762 | ||
763 | static int flctl_verify_buf(struct mtd_info *mtd, const u_char *buf, int len) | |
764 | { | |
765 | int i; | |
766 | ||
767 | for (i = 0; i < len; i++) | |
768 | if (buf[i] != flctl_read_byte(mtd)) | |
769 | return -EFAULT; | |
770 | return 0; | |
771 | } | |
772 | ||
35a34799 YS |
773 | static int flctl_chip_init_tail(struct mtd_info *mtd) |
774 | { | |
775 | struct sh_flctl *flctl = mtd_to_flctl(mtd); | |
776 | struct nand_chip *chip = &flctl->chip; | |
777 | ||
778 | if (mtd->writesize == 512) { | |
779 | flctl->page_size = 0; | |
780 | if (chip->chipsize > (32 << 20)) { | |
781 | /* big than 32MB */ | |
782 | flctl->rw_ADRCNT = ADRCNT_4; | |
783 | flctl->erase_ADRCNT = ADRCNT_3; | |
784 | } else if (chip->chipsize > (2 << 16)) { | |
785 | /* big than 128KB */ | |
786 | flctl->rw_ADRCNT = ADRCNT_3; | |
787 | flctl->erase_ADRCNT = ADRCNT_2; | |
788 | } else { | |
789 | flctl->rw_ADRCNT = ADRCNT_2; | |
790 | flctl->erase_ADRCNT = ADRCNT_1; | |
791 | } | |
792 | } else { | |
793 | flctl->page_size = 1; | |
794 | if (chip->chipsize > (128 << 20)) { | |
795 | /* big than 128MB */ | |
796 | flctl->rw_ADRCNT = ADRCNT2_E; | |
797 | flctl->erase_ADRCNT = ADRCNT_3; | |
798 | } else if (chip->chipsize > (8 << 16)) { | |
799 | /* big than 512KB */ | |
800 | flctl->rw_ADRCNT = ADRCNT_4; | |
801 | flctl->erase_ADRCNT = ADRCNT_2; | |
802 | } else { | |
803 | flctl->rw_ADRCNT = ADRCNT_3; | |
804 | flctl->erase_ADRCNT = ADRCNT_1; | |
805 | } | |
806 | } | |
807 | ||
808 | if (flctl->hwecc) { | |
809 | if (mtd->writesize == 512) { | |
810 | chip->ecc.layout = &flctl_4secc_oob_16; | |
811 | chip->badblock_pattern = &flctl_4secc_smallpage; | |
812 | } else { | |
813 | chip->ecc.layout = &flctl_4secc_oob_64; | |
814 | chip->badblock_pattern = &flctl_4secc_largepage; | |
815 | } | |
816 | ||
817 | chip->ecc.size = 512; | |
818 | chip->ecc.bytes = 10; | |
6a918bad | 819 | chip->ecc.strength = 4; |
35a34799 YS |
820 | chip->ecc.read_page = flctl_read_page_hwecc; |
821 | chip->ecc.write_page = flctl_write_page_hwecc; | |
822 | chip->ecc.mode = NAND_ECC_HW; | |
823 | ||
824 | /* 4 symbols ECC enabled */ | |
aa32d1f0 | 825 | flctl->flcmncr_base |= _4ECCEN; |
35a34799 YS |
826 | } else { |
827 | chip->ecc.mode = NAND_ECC_SOFT; | |
828 | } | |
829 | ||
830 | return 0; | |
831 | } | |
832 | ||
3c7ea4ec BH |
833 | static irqreturn_t flctl_handle_flste(int irq, void *dev_id) |
834 | { | |
835 | struct sh_flctl *flctl = dev_id; | |
836 | ||
837 | dev_err(&flctl->pdev->dev, "flste irq: %x\n", readl(FLINTDMACR(flctl))); | |
838 | writel(flctl->flintdmacr_base, FLINTDMACR(flctl)); | |
839 | ||
840 | return IRQ_HANDLED; | |
841 | } | |
842 | ||
b79c7adf | 843 | static int __devinit flctl_probe(struct platform_device *pdev) |
35a34799 YS |
844 | { |
845 | struct resource *res; | |
846 | struct sh_flctl *flctl; | |
847 | struct mtd_info *flctl_mtd; | |
848 | struct nand_chip *nand; | |
849 | struct sh_flctl_platform_data *pdata; | |
b79c7adf | 850 | int ret = -ENXIO; |
3c7ea4ec | 851 | int irq; |
35a34799 YS |
852 | |
853 | pdata = pdev->dev.platform_data; | |
854 | if (pdata == NULL) { | |
b79c7adf MD |
855 | dev_err(&pdev->dev, "no platform data defined\n"); |
856 | return -EINVAL; | |
35a34799 YS |
857 | } |
858 | ||
859 | flctl = kzalloc(sizeof(struct sh_flctl), GFP_KERNEL); | |
860 | if (!flctl) { | |
b79c7adf | 861 | dev_err(&pdev->dev, "failed to allocate driver data\n"); |
35a34799 YS |
862 | return -ENOMEM; |
863 | } | |
864 | ||
865 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | |
866 | if (!res) { | |
b79c7adf | 867 | dev_err(&pdev->dev, "failed to get I/O memory\n"); |
cfe78194 | 868 | goto err_iomap; |
35a34799 YS |
869 | } |
870 | ||
cbd38a87 | 871 | flctl->reg = ioremap(res->start, resource_size(res)); |
35a34799 | 872 | if (flctl->reg == NULL) { |
b79c7adf | 873 | dev_err(&pdev->dev, "failed to remap I/O memory\n"); |
cfe78194 | 874 | goto err_iomap; |
35a34799 YS |
875 | } |
876 | ||
3c7ea4ec BH |
877 | irq = platform_get_irq(pdev, 0); |
878 | if (irq < 0) { | |
879 | dev_err(&pdev->dev, "failed to get flste irq data\n"); | |
880 | goto err_flste; | |
881 | } | |
882 | ||
883 | ret = request_irq(irq, flctl_handle_flste, IRQF_SHARED, "flste", flctl); | |
884 | if (ret) { | |
885 | dev_err(&pdev->dev, "request interrupt failed.\n"); | |
886 | goto err_flste; | |
887 | } | |
888 | ||
35a34799 YS |
889 | platform_set_drvdata(pdev, flctl); |
890 | flctl_mtd = &flctl->mtd; | |
891 | nand = &flctl->chip; | |
892 | flctl_mtd->priv = nand; | |
b79c7adf | 893 | flctl->pdev = pdev; |
35a34799 | 894 | flctl->hwecc = pdata->has_hwecc; |
3f2e924b | 895 | flctl->holden = pdata->use_holden; |
3c7ea4ec BH |
896 | flctl->flcmncr_base = pdata->flcmncr_val; |
897 | flctl->flintdmacr_base = flctl->hwecc ? (STERINTE | ECERB) : STERINTE; | |
35a34799 | 898 | |
35a34799 YS |
899 | /* Set address of hardware control function */ |
900 | /* 20 us command delay time */ | |
901 | nand->chip_delay = 20; | |
902 | ||
903 | nand->read_byte = flctl_read_byte; | |
904 | nand->write_buf = flctl_write_buf; | |
905 | nand->read_buf = flctl_read_buf; | |
906 | nand->verify_buf = flctl_verify_buf; | |
907 | nand->select_chip = flctl_select_chip; | |
908 | nand->cmdfunc = flctl_cmdfunc; | |
909 | ||
010ab820 MD |
910 | if (pdata->flcmncr_val & SEL_16BIT) { |
911 | nand->options |= NAND_BUSWIDTH_16; | |
912 | nand->read_word = flctl_read_word; | |
913 | } | |
914 | ||
cfe78194 BH |
915 | pm_runtime_enable(&pdev->dev); |
916 | pm_runtime_resume(&pdev->dev); | |
917 | ||
5e81e88a | 918 | ret = nand_scan_ident(flctl_mtd, 1, NULL); |
35a34799 | 919 | if (ret) |
cfe78194 | 920 | goto err_chip; |
35a34799 YS |
921 | |
922 | ret = flctl_chip_init_tail(flctl_mtd); | |
923 | if (ret) | |
cfe78194 | 924 | goto err_chip; |
35a34799 YS |
925 | |
926 | ret = nand_scan_tail(flctl_mtd); | |
927 | if (ret) | |
cfe78194 | 928 | goto err_chip; |
35a34799 | 929 | |
ee0e87b1 | 930 | mtd_device_register(flctl_mtd, pdata->parts, pdata->nr_parts); |
35a34799 YS |
931 | |
932 | return 0; | |
933 | ||
cfe78194 BH |
934 | err_chip: |
935 | pm_runtime_disable(&pdev->dev); | |
3c7ea4ec BH |
936 | free_irq(irq, flctl); |
937 | err_flste: | |
cb54751d | 938 | iounmap(flctl->reg); |
cfe78194 | 939 | err_iomap: |
35a34799 YS |
940 | kfree(flctl); |
941 | return ret; | |
942 | } | |
943 | ||
b79c7adf | 944 | static int __devexit flctl_remove(struct platform_device *pdev) |
35a34799 YS |
945 | { |
946 | struct sh_flctl *flctl = platform_get_drvdata(pdev); | |
947 | ||
948 | nand_release(&flctl->mtd); | |
cfe78194 | 949 | pm_runtime_disable(&pdev->dev); |
3c7ea4ec | 950 | free_irq(platform_get_irq(pdev, 0), flctl); |
cb54751d | 951 | iounmap(flctl->reg); |
35a34799 YS |
952 | kfree(flctl); |
953 | ||
954 | return 0; | |
955 | } | |
956 | ||
957 | static struct platform_driver flctl_driver = { | |
35a34799 YS |
958 | .remove = flctl_remove, |
959 | .driver = { | |
960 | .name = "sh_flctl", | |
961 | .owner = THIS_MODULE, | |
962 | }, | |
963 | }; | |
964 | ||
965 | static int __init flctl_nand_init(void) | |
966 | { | |
894572a3 | 967 | return platform_driver_probe(&flctl_driver, flctl_probe); |
35a34799 YS |
968 | } |
969 | ||
970 | static void __exit flctl_nand_cleanup(void) | |
971 | { | |
972 | platform_driver_unregister(&flctl_driver); | |
973 | } | |
974 | ||
975 | module_init(flctl_nand_init); | |
976 | module_exit(flctl_nand_cleanup); | |
977 | ||
978 | MODULE_LICENSE("GPL"); | |
979 | MODULE_AUTHOR("Yoshihiro Shimoda"); | |
980 | MODULE_DESCRIPTION("SuperH FLCTL driver"); | |
981 | MODULE_ALIAS("platform:sh_flctl"); |