aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/mtd/nand
diff options
context:
space:
mode:
authorYoshihiro Shimoda <shimoda.yoshihiro@renesas.com>2008-10-14 08:23:26 -0400
committerDavid Woodhouse <David.Woodhouse@intel.com>2008-10-14 08:33:05 -0400
commit6028aa01f759a1dae11e5d0e495b3dc9d2b0a47b (patch)
treec553cf676af448182a1cad908bf210fe25ae49f2 /drivers/mtd/nand
parent3fc2389847a84d06263c13a3b6dfe1f1d6eea935 (diff)
[MTD] [NAND] sh_flctl: add support for Renesas SuperH FLCTL
Several Renesas SuperH CPU has FLCTL. The FLCTL support NAND Flash. This driver support SH7723. Signed-off-by: Yoshihiro Shimoda <shimoda.yoshihiro@renesas.com> Acked-by: Paul Mundt <lethal@linux-sh.org> Signed-off-by: David Woodhouse <David.Woodhouse@intel.com>
Diffstat (limited to 'drivers/mtd/nand')
-rw-r--r--drivers/mtd/nand/Kconfig7
-rw-r--r--drivers/mtd/nand/Makefile1
-rw-r--r--drivers/mtd/nand/sh_flctl.c301
3 files changed, 309 insertions, 0 deletions
diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig
index 82815dd64bf6..89b4d39386ab 100644
--- a/drivers/mtd/nand/Kconfig
+++ b/drivers/mtd/nand/Kconfig
@@ -407,4 +407,11 @@ config MTD_NAND_MXC
407 This enables the driver for the NAND flash controller on the 407 This enables the driver for the NAND flash controller on the
408 MXC processors. 408 MXC processors.
409 409
410config MTD_NAND_SH_FLCTL
411 tristate "Support for NAND on Renesas SuperH FLCTL"
412 depends on MTD_NAND && SUPERH && CPU_SUBTYPE_SH7723
413 help
414 Several Renesas SuperH CPU has FLCTL. This option enables support
415 for NAND Flash using FLCTL. This driver support SH7723.
416
410endif # MTD_NAND 417endif # MTD_NAND
diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile
index e0fee048c1b4..9bfeca324b32 100644
--- a/drivers/mtd/nand/Makefile
+++ b/drivers/mtd/nand/Makefile
@@ -33,6 +33,7 @@ obj-$(CONFIG_MTD_NAND_PASEMI) += pasemi_nand.o
33obj-$(CONFIG_MTD_NAND_ORION) += orion_nand.o 33obj-$(CONFIG_MTD_NAND_ORION) += orion_nand.o
34obj-$(CONFIG_MTD_NAND_FSL_ELBC) += fsl_elbc_nand.o 34obj-$(CONFIG_MTD_NAND_FSL_ELBC) += fsl_elbc_nand.o
35obj-$(CONFIG_MTD_NAND_FSL_UPM) += fsl_upm.o 35obj-$(CONFIG_MTD_NAND_FSL_UPM) += fsl_upm.o
36obj-$(CONFIG_MTD_NAND_SH_FLCTL) += sh_flctl.o
36obj-$(CONFIG_MTD_NAND_MXC) += mxc_nand.o 37obj-$(CONFIG_MTD_NAND_MXC) += mxc_nand.o
37 38
38nand-objs := nand_base.o nand_bbt.o 39nand-objs := nand_base.o nand_bbt.o
diff --git a/drivers/mtd/nand/sh_flctl.c b/drivers/mtd/nand/sh_flctl.c
new file mode 100644
index 000000000000..600a76f5580e
--- /dev/null
+++ b/drivers/mtd/nand/sh_flctl.c
@@ -0,0 +1,301 @@
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
35static 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
43static 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
51static uint8_t scan_ff_pattern[] = { 0xff, 0xff };
52
53static 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
60static struct nand_bbt_descr flctl_4secc_largepage = {
61 .options = 0,
62 .offs = 58,
63 .len = 2,
64 .pattern = scan_ff_pattern,
65};
66
67static 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
73static void start_translation(struct sh_flctl *flctl)
74{
75 writeb(TRSTRT, FLTRCR(flctl));
76}
77
78static 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
94static 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
123static 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
138static 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
152static 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
200static 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
215static 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
226static 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
241static 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
257static 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
270static 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;