aboutsummaryrefslogtreecommitdiffstats
path: root/arch
diff options
context:
space:
mode:
Diffstat (limited to 'arch')
-rw-r--r--arch/arm/configs/rx51_defconfig2
-rw-r--r--arch/arm/mach-omap2/Makefile3
-rw-r--r--arch/arm/mach-omap2/board-rx51-peripherals.c58
-rw-r--r--arch/arm/mach-omap2/gpmc-onenand.c330
-rw-r--r--arch/arm/plat-omap/include/mach/onenand.h22
5 files changed, 412 insertions, 3 deletions
diff --git a/arch/arm/configs/rx51_defconfig b/arch/arm/configs/rx51_defconfig
index 593102da8cd7..eb2cb31825c0 100644
--- a/arch/arm/configs/rx51_defconfig
+++ b/arch/arm/configs/rx51_defconfig
@@ -282,7 +282,7 @@ CONFIG_ALIGNMENT_TRAP=y
282# 282#
283CONFIG_ZBOOT_ROM_TEXT=0x0 283CONFIG_ZBOOT_ROM_TEXT=0x0
284CONFIG_ZBOOT_ROM_BSS=0x0 284CONFIG_ZBOOT_ROM_BSS=0x0
285CONFIG_CMDLINE="init=/sbin/preinit ubi.mtd=4 root=ubi0:rootfs rootfstype=ubifs rw console=ttyMTD5" 285CONFIG_CMDLINE="init=/sbin/preinit ubi.mtd=rootfs root=ubi0:rootfs rootfstype=ubifs rootflags=bulk_read,no_chk_data_crc rw console=ttyMTD,log console=tty0"
286# CONFIG_XIP_KERNEL is not set 286# CONFIG_XIP_KERNEL is not set
287# CONFIG_KEXEC is not set 287# CONFIG_KEXEC is not set
288 288
diff --git a/arch/arm/mach-omap2/Makefile b/arch/arm/mach-omap2/Makefile
index bf3827ae2c5f..11f7f660879b 100644
--- a/arch/arm/mach-omap2/Makefile
+++ b/arch/arm/mach-omap2/Makefile
@@ -58,3 +58,6 @@ obj-$(CONFIG_MACH_NOKIA_RX51) += board-rx51.o \
58ifeq ($(CONFIG_USB_MUSB_SOC),y) 58ifeq ($(CONFIG_USB_MUSB_SOC),y)
59obj-y += usb-musb.o 59obj-y += usb-musb.o
60endif 60endif
61
62onenand-$(CONFIG_MTD_ONENAND_OMAP2) := gpmc-onenand.o
63obj-y += $(onenand-m) $(onenand-y)
diff --git a/arch/arm/mach-omap2/board-rx51-peripherals.c b/arch/arm/mach-omap2/board-rx51-peripherals.c
index a7381729645c..ca18ae94185a 100644
--- a/arch/arm/mach-omap2/board-rx51-peripherals.c
+++ b/arch/arm/mach-omap2/board-rx51-peripherals.c
@@ -27,6 +27,7 @@
27#include <mach/dma.h> 27#include <mach/dma.h>
28#include <mach/gpmc.h> 28#include <mach/gpmc.h>
29#include <mach/keypad.h> 29#include <mach/keypad.h>
30#include <mach/onenand.h>
30 31
31#include "mmc-twl4030.h" 32#include "mmc-twl4030.h"
32 33
@@ -408,6 +409,62 @@ static int __init rx51_i2c_init(void)
408 return 0; 409 return 0;
409} 410}
410 411
412#if defined(CONFIG_MTD_ONENAND_OMAP2) || \
413 defined(CONFIG_MTD_ONENAND_OMAP2_MODULE)
414
415static struct mtd_partition onenand_partitions[] = {
416 {
417 .name = "bootloader",
418 .offset = 0,
419 .size = 0x20000,
420 .mask_flags = MTD_WRITEABLE, /* Force read-only */
421 },
422 {
423 .name = "config",
424 .offset = MTDPART_OFS_APPEND,
425 .size = 0x60000,
426 },
427 {
428 .name = "log",
429 .offset = MTDPART_OFS_APPEND,
430 .size = 0x40000,
431 },
432 {
433 .name = "kernel",
434 .offset = MTDPART_OFS_APPEND,
435 .size = 0x200000,
436 },
437 {
438 .name = "initfs",
439 .offset = MTDPART_OFS_APPEND,
440 .size = 0x200000,
441 },
442 {
443 .name = "rootfs",
444 .offset = MTDPART_OFS_APPEND,
445 .size = MTDPART_SIZ_FULL,
446 },
447};
448
449static struct omap_onenand_platform_data board_onenand_data = {
450 .cs = 0,
451 .gpio_irq = 65,
452 .parts = onenand_partitions,
453 .nr_parts = ARRAY_SIZE(onenand_partitions),
454};
455
456static void __init board_onenand_init(void)
457{
458 gpmc_onenand_init(&board_onenand_data);
459}
460
461#else
462
463static inline void board_onenand_init(void)
464{
465}
466
467#endif
411 468
412void __init rx51_peripherals_init(void) 469void __init rx51_peripherals_init(void)
413{ 470{
@@ -415,5 +472,6 @@ void __init rx51_peripherals_init(void)
415 ARRAY_SIZE(rx51_peripherals_devices)); 472 ARRAY_SIZE(rx51_peripherals_devices));
416 rx51_i2c_init(); 473 rx51_i2c_init();
417 rx51_init_smc91x(); 474 rx51_init_smc91x();
475 board_onenand_init();
418} 476}
419 477
diff --git a/arch/arm/mach-omap2/gpmc-onenand.c b/arch/arm/mach-omap2/gpmc-onenand.c
new file mode 100644
index 000000000000..2fd22f9c5f0e
--- /dev/null
+++ b/arch/arm/mach-omap2/gpmc-onenand.c
@@ -0,0 +1,330 @@
1/*
2 * linux/arch/arm/mach-omap2/gpmc-onenand.c
3 *
4 * Copyright (C) 2006 - 2009 Nokia Corporation
5 * Contacts: Juha Yrjola
6 * Tony Lindgren
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License version 2 as
10 * published by the Free Software Foundation.
11 */
12
13#include <linux/kernel.h>
14#include <linux/platform_device.h>
15#include <linux/mtd/onenand_regs.h>
16#include <linux/io.h>
17
18#include <asm/mach/flash.h>
19
20#include <mach/onenand.h>
21#include <mach/board.h>
22#include <mach/gpmc.h>
23
24static struct omap_onenand_platform_data *gpmc_onenand_data;
25
26static struct platform_device gpmc_onenand_device = {
27 .name = "omap2-onenand",
28 .id = -1,
29};
30
31static int omap2_onenand_set_async_mode(int cs, void __iomem *onenand_base)
32{
33 struct gpmc_timings t;
34
35 const int t_cer = 15;
36 const int t_avdp = 12;
37 const int t_aavdh = 7;
38 const int t_ce = 76;
39 const int t_aa = 76;
40 const int t_oe = 20;
41 const int t_cez = 20; /* max of t_cez, t_oez */
42 const int t_ds = 30;
43 const int t_wpl = 40;
44 const int t_wph = 30;
45
46 memset(&t, 0, sizeof(t));
47 t.sync_clk = 0;
48 t.cs_on = 0;
49 t.adv_on = 0;
50
51 /* Read */
52 t.adv_rd_off = gpmc_round_ns_to_ticks(max_t(int, t_avdp, t_cer));
53 t.oe_on = t.adv_rd_off + gpmc_round_ns_to_ticks(t_aavdh);
54 t.access = t.adv_on + gpmc_round_ns_to_ticks(t_aa);
55 t.access = max_t(int, t.access, t.cs_on + gpmc_round_ns_to_ticks(t_ce));
56 t.access = max_t(int, t.access, t.oe_on + gpmc_round_ns_to_ticks(t_oe));
57 t.oe_off = t.access + gpmc_round_ns_to_ticks(1);
58 t.cs_rd_off = t.oe_off;
59 t.rd_cycle = t.cs_rd_off + gpmc_round_ns_to_ticks(t_cez);
60
61 /* Write */
62 t.adv_wr_off = t.adv_rd_off;
63 t.we_on = t.oe_on;
64 if (cpu_is_omap34xx()) {
65 t.wr_data_mux_bus = t.we_on;
66 t.wr_access = t.we_on + gpmc_round_ns_to_ticks(t_ds);
67 }
68 t.we_off = t.we_on + gpmc_round_ns_to_ticks(t_wpl);
69 t.cs_wr_off = t.we_off + gpmc_round_ns_to_ticks(t_wph);
70 t.wr_cycle = t.cs_wr_off + gpmc_round_ns_to_ticks(t_cez);
71
72 /* Configure GPMC for asynchronous read */
73 gpmc_cs_write_reg(cs, GPMC_CS_CONFIG1,
74 GPMC_CONFIG1_DEVICESIZE_16 |
75 GPMC_CONFIG1_MUXADDDATA);
76
77 return gpmc_cs_set_timings(cs, &t);
78}
79
80static void set_onenand_cfg(void __iomem *onenand_base, int latency,
81 int sync_read, int sync_write, int hf)
82{
83 u32 reg;
84
85 reg = readw(onenand_base + ONENAND_REG_SYS_CFG1);
86 reg &= ~((0x7 << ONENAND_SYS_CFG1_BRL_SHIFT) | (0x7 << 9));
87 reg |= (latency << ONENAND_SYS_CFG1_BRL_SHIFT) |
88 ONENAND_SYS_CFG1_BL_16;
89 if (sync_read)
90 reg |= ONENAND_SYS_CFG1_SYNC_READ;
91 else
92 reg &= ~ONENAND_SYS_CFG1_SYNC_READ;
93 if (sync_write)
94 reg |= ONENAND_SYS_CFG1_SYNC_WRITE;
95 else
96 reg &= ~ONENAND_SYS_CFG1_SYNC_WRITE;
97 if (hf)
98 reg |= ONENAND_SYS_CFG1_HF;
99 else
100 reg &= ~ONENAND_SYS_CFG1_HF;
101 writew(reg, onenand_base + ONENAND_REG_SYS_CFG1);
102}
103
104static int omap2_onenand_set_sync_mode(struct omap_onenand_platform_data *cfg,
105 void __iomem *onenand_base,
106 int freq)
107{
108 struct gpmc_timings t;
109 const int t_cer = 15;
110 const int t_avdp = 12;
111 const int t_cez = 20; /* max of t_cez, t_oez */
112 const int t_ds = 30;
113 const int t_wpl = 40;
114 const int t_wph = 30;
115 int min_gpmc_clk_period, t_ces, t_avds, t_avdh, t_ach, t_aavdh, t_rdyo;
116 int tick_ns, div, fclk_offset_ns, fclk_offset, gpmc_clk_ns, latency;
117 int first_time = 0, hf = 0, sync_read = 0, sync_write = 0;
118 int err, ticks_cez;
119 int cs = cfg->cs;
120 u32 reg;
121
122 if (cfg->flags & ONENAND_SYNC_READ) {
123 sync_read = 1;
124 } else if (cfg->flags & ONENAND_SYNC_READWRITE) {
125 sync_read = 1;
126 sync_write = 1;
127 }
128
129 if (!freq) {
130 /* Very first call freq is not known */
131 err = omap2_onenand_set_async_mode(cs, onenand_base);
132 if (err)
133 return err;
134 reg = readw(onenand_base + ONENAND_REG_VERSION_ID);
135 switch ((reg >> 4) & 0xf) {
136 case 0:
137 freq = 40;
138 break;
139 case 1:
140 freq = 54;
141 break;
142 case 2:
143 freq = 66;
144 break;
145 case 3:
146 freq = 83;
147 break;
148 case 4:
149 freq = 104;
150 break;
151 default:
152 freq = 54;
153 break;
154 }
155 first_time = 1;
156 }
157
158 switch (freq) {
159 case 83:
160 min_gpmc_clk_period = 12; /* 83 MHz */
161 t_ces = 5;
162 t_avds = 4;
163 t_avdh = 2;
164 t_ach = 6;
165 t_aavdh = 6;
166 t_rdyo = 9;
167 break;
168 case 66:
169 min_gpmc_clk_period = 15; /* 66 MHz */
170 t_ces = 6;
171 t_avds = 5;
172 t_avdh = 2;
173 t_ach = 6;
174 t_aavdh = 6;
175 t_rdyo = 11;
176 break;
177 default:
178 min_gpmc_clk_period = 18; /* 54 MHz */
179 t_ces = 7;
180 t_avds = 7;
181 t_avdh = 7;
182 t_ach = 9;
183 t_aavdh = 7;
184 t_rdyo = 15;
185 sync_write = 0;
186 break;
187 }
188
189 tick_ns = gpmc_ticks_to_ns(1);
190 div = gpmc_cs_calc_divider(cs, min_gpmc_clk_period);
191 gpmc_clk_ns = gpmc_ticks_to_ns(div);
192 if (gpmc_clk_ns < 15) /* >66Mhz */
193 hf = 1;
194 if (hf)
195 latency = 6;
196 else if (gpmc_clk_ns >= 25) /* 40 MHz*/
197 latency = 3;
198 else
199 latency = 4;
200
201 if (first_time)
202 set_onenand_cfg(onenand_base, latency,
203 sync_read, sync_write, hf);
204
205 if (div == 1) {
206 reg = gpmc_cs_read_reg(cs, GPMC_CS_CONFIG2);
207 reg |= (1 << 7);
208 gpmc_cs_write_reg(cs, GPMC_CS_CONFIG2, reg);
209 reg = gpmc_cs_read_reg(cs, GPMC_CS_CONFIG3);
210 reg |= (1 << 7);
211 gpmc_cs_write_reg(cs, GPMC_CS_CONFIG3, reg);
212 reg = gpmc_cs_read_reg(cs, GPMC_CS_CONFIG4);
213 reg |= (1 << 7);
214 reg |= (1 << 23);
215 gpmc_cs_write_reg(cs, GPMC_CS_CONFIG4, reg);
216 } else {
217 reg = gpmc_cs_read_reg(cs, GPMC_CS_CONFIG2);
218 reg &= ~(1 << 7);
219 gpmc_cs_write_reg(cs, GPMC_CS_CONFIG2, reg);
220 reg = gpmc_cs_read_reg(cs, GPMC_CS_CONFIG3);
221 reg &= ~(1 << 7);
222 gpmc_cs_write_reg(cs, GPMC_CS_CONFIG3, reg);
223 reg = gpmc_cs_read_reg(cs, GPMC_CS_CONFIG4);
224 reg &= ~(1 << 7);
225 reg &= ~(1 << 23);
226 gpmc_cs_write_reg(cs, GPMC_CS_CONFIG4, reg);
227 }
228
229 /* Set synchronous read timings */
230 memset(&t, 0, sizeof(t));
231 t.sync_clk = min_gpmc_clk_period;
232 t.cs_on = 0;
233 t.adv_on = 0;
234 fclk_offset_ns = gpmc_round_ns_to_ticks(max_t(int, t_ces, t_avds));
235 fclk_offset = gpmc_ns_to_ticks(fclk_offset_ns);
236 t.page_burst_access = gpmc_clk_ns;
237
238 /* Read */
239 t.adv_rd_off = gpmc_ticks_to_ns(fclk_offset + gpmc_ns_to_ticks(t_avdh));
240 t.oe_on = gpmc_ticks_to_ns(fclk_offset + gpmc_ns_to_ticks(t_ach));
241 t.access = gpmc_ticks_to_ns(fclk_offset + (latency + 1) * div);
242 t.oe_off = t.access + gpmc_round_ns_to_ticks(1);
243 t.cs_rd_off = t.oe_off;
244 ticks_cez = ((gpmc_ns_to_ticks(t_cez) + div - 1) / div) * div;
245 t.rd_cycle = gpmc_ticks_to_ns(fclk_offset + (latency + 1) * div +
246 ticks_cez);
247
248 /* Write */
249 if (sync_write) {
250 t.adv_wr_off = t.adv_rd_off;
251 t.we_on = 0;
252 t.we_off = t.cs_rd_off;
253 t.cs_wr_off = t.cs_rd_off;
254 t.wr_cycle = t.rd_cycle;
255 if (cpu_is_omap34xx()) {
256 t.wr_data_mux_bus = gpmc_ticks_to_ns(fclk_offset +
257 gpmc_ns_to_ticks(min_gpmc_clk_period +
258 t_rdyo));
259 t.wr_access = t.access;
260 }
261 } else {
262 t.adv_wr_off = gpmc_round_ns_to_ticks(max_t(int,
263 t_avdp, t_cer));
264 t.we_on = t.adv_wr_off + gpmc_round_ns_to_ticks(t_aavdh);
265 t.we_off = t.we_on + gpmc_round_ns_to_ticks(t_wpl);
266 t.cs_wr_off = t.we_off + gpmc_round_ns_to_ticks(t_wph);
267 t.wr_cycle = t.cs_wr_off + gpmc_round_ns_to_ticks(t_cez);
268 if (cpu_is_omap34xx()) {
269 t.wr_data_mux_bus = t.we_on;
270 t.wr_access = t.we_on + gpmc_round_ns_to_ticks(t_ds);
271 }
272 }
273
274 /* Configure GPMC for synchronous read */
275 gpmc_cs_write_reg(cs, GPMC_CS_CONFIG1,
276 GPMC_CONFIG1_WRAPBURST_SUPP |
277 GPMC_CONFIG1_READMULTIPLE_SUPP |
278 (sync_read ? GPMC_CONFIG1_READTYPE_SYNC : 0) |
279 (sync_write ? GPMC_CONFIG1_WRITEMULTIPLE_SUPP : 0) |
280 (sync_write ? GPMC_CONFIG1_WRITETYPE_SYNC : 0) |
281 GPMC_CONFIG1_CLKACTIVATIONTIME(fclk_offset) |
282 GPMC_CONFIG1_PAGE_LEN(2) |
283 (cpu_is_omap34xx() ? 0 :
284 (GPMC_CONFIG1_WAIT_READ_MON |
285 GPMC_CONFIG1_WAIT_PIN_SEL(0))) |
286 GPMC_CONFIG1_DEVICESIZE_16 |
287 GPMC_CONFIG1_DEVICETYPE_NOR |
288 GPMC_CONFIG1_MUXADDDATA);
289
290 err = gpmc_cs_set_timings(cs, &t);
291 if (err)
292 return err;
293
294 set_onenand_cfg(onenand_base, latency, sync_read, sync_write, hf);
295
296 return 0;
297}
298
299static int gpmc_onenand_setup(void __iomem *onenand_base, int freq)
300{
301 struct device *dev = &gpmc_onenand_device.dev;
302
303 /* Set sync timings in GPMC */
304 if (omap2_onenand_set_sync_mode(gpmc_onenand_data, onenand_base,
305 freq) < 0) {
306 dev_err(dev, "Unable to set synchronous mode\n");
307 return -EINVAL;
308 }
309
310 return 0;
311}
312
313void __init gpmc_onenand_init(struct omap_onenand_platform_data *_onenand_data)
314{
315 gpmc_onenand_data = _onenand_data;
316 gpmc_onenand_data->onenand_setup = gpmc_onenand_setup;
317 gpmc_onenand_device.dev.platform_data = gpmc_onenand_data;
318
319 if (cpu_is_omap24xx() &&
320 (gpmc_onenand_data->flags & ONENAND_SYNC_READWRITE)) {
321 printk(KERN_ERR "Onenand using only SYNC_READ on 24xx\n");
322 gpmc_onenand_data->flags &= ~ONENAND_SYNC_READWRITE;
323 gpmc_onenand_data->flags |= ONENAND_SYNC_READ;
324 }
325
326 if (platform_device_register(&gpmc_onenand_device) < 0) {
327 printk(KERN_ERR "Unable to register OneNAND device\n");
328 return;
329 }
330}
diff --git a/arch/arm/plat-omap/include/mach/onenand.h b/arch/arm/plat-omap/include/mach/onenand.h
index 4649d302c263..72f433d7d827 100644
--- a/arch/arm/plat-omap/include/mach/onenand.h
+++ b/arch/arm/plat-omap/include/mach/onenand.h
@@ -9,8 +9,12 @@
9 * published by the Free Software Foundation. 9 * published by the Free Software Foundation.
10 */ 10 */
11 11
12#include <linux/mtd/mtd.h>
12#include <linux/mtd/partitions.h> 13#include <linux/mtd/partitions.h>
13 14
15#define ONENAND_SYNC_READ (1 << 0)
16#define ONENAND_SYNC_READWRITE (1 << 1)
17
14struct omap_onenand_platform_data { 18struct omap_onenand_platform_data {
15 int cs; 19 int cs;
16 int gpio_irq; 20 int gpio_irq;
@@ -18,8 +22,22 @@ struct omap_onenand_platform_data {
18 int nr_parts; 22 int nr_parts;
19 int (*onenand_setup)(void __iomem *, int freq); 23 int (*onenand_setup)(void __iomem *, int freq);
20 int dma_channel; 24 int dma_channel;
25 u8 flags;
21}; 26};
22 27
23int omap2_onenand_rephase(void);
24
25#define ONENAND_MAX_PARTITIONS 8 28#define ONENAND_MAX_PARTITIONS 8
29
30#if defined(CONFIG_MTD_ONENAND_OMAP2) || \
31 defined(CONFIG_MTD_ONENAND_OMAP2_MODULE)
32
33extern void gpmc_onenand_init(struct omap_onenand_platform_data *d);
34
35#else
36
37#define board_onenand_data NULL
38
39static inline void gpmc_onenand_init(struct omap_onenand_platform_data *d)
40{
41}
42
43#endif