Commit | Line | Data |
---|---|---|
6028aa01 YS |
1 | /* |
2 | * SuperH FLCTL nand controller | |
3 | * | |
4 | * Copyright © 2008 Renesas Solutions Corp. | |
5 | * Copyright © 2008 Atom Create Engineering Co., Ltd. | |
6 | * | |
7 | * Based on fsl_elbc_nand.c, Copyright © 2006-2007 Freescale Semiconductor | |
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> | |
27 | #include <linux/io.h> | |
28 | #include <linux/platform_device.h> | |
29 | ||
30 | #include <linux/mtd/mtd.h> | |
31 | #include <linux/mtd/nand.h> | |
32 | #include <linux/mtd/partitions.h> | |
33 | #include <linux/mtd/sh_flctl.h> | |
34 | ||
35 | static struct nand_ecclayout flctl_4secc_oob_16 = { | |
36 | .eccbytes = 10, | |
37 | .eccpos = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}, | |
38 | .oobfree = { | |
39 | {.offset = 12, | |
40 | . length = 4} }, | |
41 | }; | |
42 | ||
43 | static struct nand_ecclayout flctl_4secc_oob_64 = { | |
44 | .eccbytes = 10, | |
45 | .eccpos = {48, 49, 50, 51, 52, 53, 54, 55, 56, 57}, | |
46 | .oobfree = { | |
47 | {.offset = 60, | |
48 | . length = 4} }, | |
49 | }; | |
50 | ||
51 | static uint8_t scan_ff_pattern[] = { 0xff, 0xff }; | |
52 | ||
53 | static struct nand_bbt_descr flctl_4secc_smallpage = { | |
54 | .options = NAND_BBT_SCAN2NDPAGE, | |
55 | .offs = 11, | |
56 | .len = 1, | |
57 | .pattern = scan_ff_pattern, | |
58 | }; | |
59 | ||
60 | static struct nand_bbt_descr flctl_4secc_largepage = { | |
61 | .options = 0, | |
62 | .offs = 58, | |
63 | .len = 2, | |
64 | .pattern = scan_ff_pattern, | |
65 | }; | |
66 | ||
67 | static void empty_fifo(struct sh_flctl *flctl) | |
68 | { | |
69 | writel(0x000c0000, FLINTDMACR(flctl)); /* FIFO Clear */ | |
70 | writel(0x00000000, FLINTDMACR(flctl)); /* Clear Error flags */ | |
71 | } | |
72 | ||
73 | static void start_translation(struct sh_flctl *flctl) | |
74 | { | |
75 | writeb(TRSTRT, FLTRCR(flctl)); | |
76 | } | |
77 | ||
78 | static void wait_completion(struct sh_flctl *flctl) | |
79 | { | |
80 | uint32_t timeout = LOOP_TIMEOUT_MAX; | |
81 | ||
82 | while (timeout--) { | |
83 | if (readb(FLTRCR(flctl)) & TREND) { | |
84 | writeb(0x0, FLTRCR(flctl)); | |
85 | return; | |
86 | } | |
87 | udelay(1); | |
88 | } | |
89 | ||
90 | printk(KERN_ERR "wait_completion(): Timeout occured \n"); | |
91 | writeb(0x0, FLTRCR(flctl)); | |
92 | } | |
93 | ||
94 | static void set_addr(struct mtd_info *mtd, int column, int page_addr) | |
95 | { | |
96 | struct sh_flctl *flctl = mtd_to_flctl(mtd); | |
97 | uint32_t addr = 0; | |
98 | ||
99 | if (column == -1) { | |
100 | addr = page_addr; /* ERASE1 */ | |
101 | } else if (page_addr != -1) { | |
102 | /* SEQIN, READ0, etc.. */ | |
103 | if (flctl->page_size) { | |
104 | addr = column & 0x0FFF; | |
105 | addr |= (page_addr & 0xff) << 16; | |
106 | addr |= ((page_addr >> 8) & 0xff) << 24; | |
107 | /* big than 128MB */ | |
108 | if (flctl->rw_ADRCNT == ADRCNT2_E) { | |
109 | uint32_t addr2; | |
110 | addr2 = (page_addr >> 16) & 0xff; | |
111 | writel(addr2, FLADR2(flctl)); | |
112 | } | |
113 | } else { | |
114 | addr = column; | |
115 | addr |= (page_addr & 0xff) << 8; | |
116 | addr |= ((page_addr >> 8) & 0xff) << 16; | |
117 | addr |= ((page_addr >> 16) & 0xff) << 24; | |
118 | } | |
119 | } | |
120 | writel(addr, FLADR(flctl)); | |
121 | } | |
122 | ||
123 | static void wait_rfifo_ready(struct sh_flctl *flctl) | |
124 | { | |
125 | uint32_t timeout = LOOP_TIMEOUT_MAX; | |
126 | ||
127 | while (timeout--) { | |
128 | uint32_t val; | |
129 | /* check FIFO */ | |
130 | val = readl(FLDTCNTR(flctl)) >> 16; | |
131 | if (val & 0xFF) | |
132 | return; | |
133 | udelay(1); | |
134 | } | |
135 | printk(KERN_ERR "wait_rfifo_ready(): Timeout occured \n"); | |
136 | } | |
137 | ||
138 | static void wait_wfifo_ready(struct sh_flctl *flctl) | |
139 | { | |
140 | uint32_t len, timeout = LOOP_TIMEOUT_MAX; | |
141 | ||
142 | while (timeout--) { | |
143 | /* check FIFO */ | |
144 | len = (readl(FLDTCNTR(flctl)) >> 16) & 0xFF; | |
145 | if (len >= 4) | |
146 | return; | |
147 | udelay(1); | |
148 | } | |
149 | printk(KERN_ERR "wait_wfifo_ready(): Timeout occured \n"); | |
150 | } | |
151 | ||
152 | static int wait_recfifo_ready(struct sh_flctl *flctl) | |
153 | { | |
154 | uint32_t timeout = LOOP_TIMEOUT_MAX; | |
155 | int checked[4]; | |
156 | void __iomem *ecc_reg[4]; | |
157 | int i; | |
158 | uint32_t data, size; | |
159 | ||
160 | memset(checked, 0, sizeof(checked)); | |
161 | ||
162 | while (timeout--) { | |
163 | size = readl(FLDTCNTR(flctl)) >> 24; | |
164 | if (size & 0xFF) | |
165 | return 0; /* success */ | |
166 | ||
167 | if (readl(FL4ECCCR(flctl)) & _4ECCFA) | |
168 | return 1; /* can't correct */ | |
169 | ||
170 | udelay(1); | |
171 | if (!(readl(FL4ECCCR(flctl)) & _4ECCEND)) | |
172 | continue; | |
173 | ||
174 | /* start error correction */ | |
175 | ecc_reg[0] = FL4ECCRESULT0(flctl); | |
176 | ecc_reg[1] = FL4ECCRESULT1(flctl); | |
177 | ecc_reg[2] = FL4ECCRESULT2(flctl); | |
178 | ecc_reg[3] = FL4ECCRESULT3(flctl); | |
179 | ||
180 | for (i = 0; i < 3; i++) { | |
181 | data = readl(ecc_reg[i]); | |
182 | if (data != INIT_FL4ECCRESULT_VAL && !checked[i]) { | |
183 | uint8_t org; | |
184 | int index; | |
185 | ||
186 | index = data >> 16; | |
187 | org = flctl->done_buff[index]; | |
188 | flctl->done_buff[index] = org ^ (data & 0xFF); | |
189 | checked[i] = 1; | |
190 | } | |
191 | } | |
192 | ||
193 | writel(0, FL4ECCCR(flctl)); | |
194 | } | |
195 | ||
196 | printk(KERN_ERR "wait_recfifo_ready(): Timeout occured \n"); | |
197 | return 1; /* timeout */ | |
198 | } | |
199 | ||
200 | static void wait_wecfifo_ready(struct sh_flctl *flctl) | |
201 | { | |
202 | uint32_t timeout = LOOP_TIMEOUT_MAX; | |
203 | uint32_t len; | |
204 | ||
205 | while (timeout--) { | |
206 | /* check FLECFIFO */ | |
207 | len = (readl(FLDTCNTR(flctl)) >> 24) & 0xFF; | |
208 | if (len >= 4) | |
209 | return; | |
210 | udelay(1); | |
211 | } | |
212 | printk(KERN_ERR "wait_wecfifo_ready(): Timeout occured \n"); | |
213 | } | |
214 | ||
215 | static void read_datareg(struct sh_flctl *flctl, int offset) | |
216 | { | |
217 | unsigned long data; | |
218 | unsigned long *buf = (unsigned long *)&flctl->done_buff[offset]; | |
219 | ||
220 | wait_completion(flctl); | |
221 | ||
222 | data = readl(FLDATAR(flctl)); | |
223 | *buf = le32_to_cpu(data); | |
224 | } | |
225 | ||
226 | static void read_fiforeg(struct sh_flctl *flctl, int rlen, int offset) | |
227 | { | |
228 | int i, len_4align; | |
229 | unsigned long *buf = (unsigned long *)&flctl->done_buff[offset]; | |
230 | void *fifo_addr = (void *)FLDTFIFO(flctl); | |
231 | ||
232 | len_4align = (rlen + 3) / 4; | |
233 | ||
234 | for (i = 0; i < len_4align; i++) { | |
235 | wait_rfifo_ready(flctl); | |
236 | buf[i] = readl(fifo_addr); | |
237 | buf[i] = be32_to_cpu(buf[i]); | |
238 | } | |
239 | } | |
240 | ||
241 | static int read_ecfiforeg(struct sh_flctl *flctl, uint8_t *buff) | |
242 | { | |
243 | int i; | |
244 | unsigned long *ecc_buf = (unsigned long *)buff; | |
245 | void *fifo_addr = (void *)FLECFIFO(flctl); | |
246 | ||
247 | for (i = 0; i < 4; i++) { | |
248 | if (wait_recfifo_ready(flctl)) | |
249 | return 1; | |
250 | ecc_buf[i] = readl(fifo_addr); | |
251 | ecc_buf[i] = be32_to_cpu(ecc_buf[i]); | |
252 | } | |
253 | ||
254 | return 0; | |
255 | } | |
256 | ||
257 | static void write_fiforeg(struct sh_flctl *flctl, int rlen, int offset) | |
258 | { | |
259 | int i, len_4align; | |
260 | unsigned long *data = (unsigned long *)&flctl->done_buff[offset]; | |
261 | void *fifo_addr = (void *)FLDTFIFO(flctl); | |
262 | ||
263 | len_4align = (rlen + 3) / 4; | |
264 | for (i = 0; i < len_4align; i++) { | |
265 | wait_wfifo_ready(flctl); | |
266 | writel(cpu_to_be32(data[i]), fifo_addr); | |
267 | } | |
268 | } | |
269 | ||
270 | static void set_cmd_regs(struct mtd_info *mtd, uint32_t cmd, uint32_t flcmcdr_val) | |
271 | { | |
272 | struct sh_flctl *flctl = mtd_to_flctl(mtd); | |
273 | uint32_t flcmncr_val = readl(FLCMNCR(flctl)); | |
274 | uint32_t flcmdcr_val, addr_len_bytes = 0; | |
275 | ||
276 | /* Set SNAND bit if page size is 2048byte */ | |
277 | if (flctl->page_size) | |
278 | flcmncr_val |= SNAND_E; | |
279 | else | |
280 | flcmncr_val &= ~SNAND_E; | |
281 | ||
282 | /* default FLCMDCR val */ | |
283 | flcmdcr_val = DOCMD1_E | DOADR_E; | |
284 | ||
285 | /* Set for FLCMDCR */ | |
286 | switch (cmd) { | |
287 | case NAND_CMD_ERASE1: | |
288 | addr_len_bytes = flctl->erase_ADRCNT; | |
289 | flcmdcr_val |= DOCMD2_E; | |
290 | break; | |
291 | case NAND_CMD_READ0: | |
292 | case NAND_CMD_READOOB: | |
293 | addr_len_bytes = flctl->rw_ADRCNT; | |
294 | flcmdcr_val |= CDSRC_E; | |
295 | break; | |
296 | case NAND_CMD_SEQIN: | |
297 | /* This case is that cmd is READ0 or READ1 or READ00 */ | |
298 | flcmdcr_val &= ~DOADR_E; /* ONLY execute 1st cmd */ | |
299 | break; | |
300 | case NAND_CMD_PAGEPROG: | |
301 | addr_len_bytes = flctl->rw_ADRCNT; |