blob: db52c4b28f8b0be124b2dce4501b0e24fff2b9b5 [file] [log] [blame]
Juha Yrjolaaa62e902009-05-28 13:23:52 -07001/*
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
Paul Gortmakerd44b28c2011-07-31 10:52:44 -040013#include <linux/string.h>
Juha Yrjolaaa62e902009-05-28 13:23:52 -070014#include <linux/kernel.h>
15#include <linux/platform_device.h>
16#include <linux/mtd/onenand_regs.h>
17#include <linux/io.h>
Arnd Bergmann22037472012-08-24 15:21:06 +020018#include <linux/platform_data/mtd-onenand-omap2.h>
Afzal Mohammed46376882012-06-05 10:11:48 +053019#include <linux/err.h>
Juha Yrjolaaa62e902009-05-28 13:23:52 -070020
21#include <asm/mach/flash.h>
22
Afzal Mohammed3ef5d002012-10-05 10:37:27 +053023#include "gpmc.h"
Tony Lindgrendbc04162012-08-31 10:59:07 -070024#include "soc.h"
Afzal Mohammedb6ab13e2012-09-29 10:32:42 +053025#include "gpmc-onenand.h"
Tony Lindgrendbc04162012-08-31 10:59:07 -070026
Afzal Mohammed681988b2012-08-30 12:53:23 -070027#define ONENAND_IO_SIZE SZ_128K
28
Afzal Mohammed46376882012-06-05 10:11:48 +053029#define ONENAND_FLAG_SYNCREAD (1 << 0)
30#define ONENAND_FLAG_SYNCWRITE (1 << 1)
31#define ONENAND_FLAG_HF (1 << 2)
32#define ONENAND_FLAG_VHF (1 << 3)
33
34static unsigned onenand_flags;
35static unsigned latency;
Afzal Mohammed46376882012-06-05 10:11:48 +053036
Juha Yrjolaaa62e902009-05-28 13:23:52 -070037static struct omap_onenand_platform_data *gpmc_onenand_data;
38
Afzal Mohammed681988b2012-08-30 12:53:23 -070039static struct resource gpmc_onenand_resource = {
40 .flags = IORESOURCE_MEM,
41};
42
Juha Yrjolaaa62e902009-05-28 13:23:52 -070043static struct platform_device gpmc_onenand_device = {
44 .name = "omap2-onenand",
45 .id = -1,
Afzal Mohammed681988b2012-08-30 12:53:23 -070046 .num_resources = 1,
47 .resource = &gpmc_onenand_resource,
Juha Yrjolaaa62e902009-05-28 13:23:52 -070048};
49
Jon Hunterbe9f10c2013-02-21 15:20:53 -060050static void omap2_onenand_calc_async_timings(struct gpmc_timings *t)
Juha Yrjolaaa62e902009-05-28 13:23:52 -070051{
Afzal Mohammed4f4426f2012-11-09 18:05:17 +053052 struct gpmc_device_timings dev_t;
Juha Yrjolaaa62e902009-05-28 13:23:52 -070053
54 const int t_cer = 15;
55 const int t_avdp = 12;
56 const int t_aavdh = 7;
57 const int t_ce = 76;
58 const int t_aa = 76;
59 const int t_oe = 20;
60 const int t_cez = 20; /* max of t_cez, t_oez */
Juha Yrjolaaa62e902009-05-28 13:23:52 -070061 const int t_wpl = 40;
62 const int t_wph = 30;
63
Afzal Mohammed4f4426f2012-11-09 18:05:17 +053064 memset(&dev_t, 0, sizeof(dev_t));
Juha Yrjolaaa62e902009-05-28 13:23:52 -070065
Afzal Mohammed4f4426f2012-11-09 18:05:17 +053066 dev_t.mux = true;
67 dev_t.t_avdp_r = max_t(int, t_avdp, t_cer) * 1000;
68 dev_t.t_avdp_w = dev_t.t_avdp_r;
69 dev_t.t_aavdh = t_aavdh * 1000;
70 dev_t.t_aa = t_aa * 1000;
71 dev_t.t_ce = t_ce * 1000;
72 dev_t.t_oe = t_oe * 1000;
73 dev_t.t_cez_r = t_cez * 1000;
74 dev_t.t_cez_w = dev_t.t_cez_r;
75 dev_t.t_wpl = t_wpl * 1000;
76 dev_t.t_wph = t_wph * 1000;
Juha Yrjolaaa62e902009-05-28 13:23:52 -070077
Jon Hunterbe9f10c2013-02-21 15:20:53 -060078 gpmc_calc_timings(t, &dev_t);
Afzal Mohammed46376882012-06-05 10:11:48 +053079}
80
81static int gpmc_set_async_mode(int cs, struct gpmc_timings *t)
82{
Juha Yrjolaaa62e902009-05-28 13:23:52 -070083 /* Configure GPMC for asynchronous read */
84 gpmc_cs_write_reg(cs, GPMC_CS_CONFIG1,
85 GPMC_CONFIG1_DEVICESIZE_16 |
86 GPMC_CONFIG1_MUXADDDATA);
87
Afzal Mohammed46376882012-06-05 10:11:48 +053088 return gpmc_cs_set_timings(cs, t);
89}
90
91static void omap2_onenand_set_async_mode(void __iomem *onenand_base)
92{
93 u32 reg;
Adrian Hunter6d453e82009-06-23 13:30:24 +030094
95 /* Ensure sync read and sync write are disabled */
96 reg = readw(onenand_base + ONENAND_REG_SYS_CFG1);
97 reg &= ~ONENAND_SYS_CFG1_SYNC_READ & ~ONENAND_SYS_CFG1_SYNC_WRITE;
98 writew(reg, onenand_base + ONENAND_REG_SYS_CFG1);
Juha Yrjolaaa62e902009-05-28 13:23:52 -070099}
100
Afzal Mohammed46376882012-06-05 10:11:48 +0530101static void set_onenand_cfg(void __iomem *onenand_base)
Juha Yrjolaaa62e902009-05-28 13:23:52 -0700102{
103 u32 reg;
104
105 reg = readw(onenand_base + ONENAND_REG_SYS_CFG1);
106 reg &= ~((0x7 << ONENAND_SYS_CFG1_BRL_SHIFT) | (0x7 << 9));
107 reg |= (latency << ONENAND_SYS_CFG1_BRL_SHIFT) |
108 ONENAND_SYS_CFG1_BL_16;
Afzal Mohammed46376882012-06-05 10:11:48 +0530109 if (onenand_flags & ONENAND_FLAG_SYNCREAD)
Juha Yrjolaaa62e902009-05-28 13:23:52 -0700110 reg |= ONENAND_SYS_CFG1_SYNC_READ;
111 else
112 reg &= ~ONENAND_SYS_CFG1_SYNC_READ;
Afzal Mohammed46376882012-06-05 10:11:48 +0530113 if (onenand_flags & ONENAND_FLAG_SYNCWRITE)
Juha Yrjolaaa62e902009-05-28 13:23:52 -0700114 reg |= ONENAND_SYS_CFG1_SYNC_WRITE;
115 else
116 reg &= ~ONENAND_SYS_CFG1_SYNC_WRITE;
Afzal Mohammed46376882012-06-05 10:11:48 +0530117 if (onenand_flags & ONENAND_FLAG_HF)
Juha Yrjolaaa62e902009-05-28 13:23:52 -0700118 reg |= ONENAND_SYS_CFG1_HF;
119 else
120 reg &= ~ONENAND_SYS_CFG1_HF;
Afzal Mohammed46376882012-06-05 10:11:48 +0530121 if (onenand_flags & ONENAND_FLAG_VHF)
Adrian Hunter1435ca02011-02-07 10:46:58 +0200122 reg |= ONENAND_SYS_CFG1_VHF;
123 else
124 reg &= ~ONENAND_SYS_CFG1_VHF;
Juha Yrjolaaa62e902009-05-28 13:23:52 -0700125 writew(reg, onenand_base + ONENAND_REG_SYS_CFG1);
126}
127
Adrian Hunter5714b7e2011-02-07 10:47:00 +0200128static int omap2_onenand_get_freq(struct omap_onenand_platform_data *cfg,
Jon Hunter757ef792012-06-28 13:41:29 -0500129 void __iomem *onenand_base)
Adrian Hunter5714b7e2011-02-07 10:47:00 +0200130{
131 u16 ver = readw(onenand_base + ONENAND_REG_VERSION_ID);
Jon Hunter757ef792012-06-28 13:41:29 -0500132 int freq;
Adrian Hunter5714b7e2011-02-07 10:47:00 +0200133
134 switch ((ver >> 4) & 0xf) {
135 case 0:
136 freq = 40;
137 break;
138 case 1:
139 freq = 54;
140 break;
141 case 2:
142 freq = 66;
143 break;
144 case 3:
145 freq = 83;
146 break;
147 case 4:
148 freq = 104;
149 break;
150 default:
151 freq = 54;
152 break;
153 }
154
155 return freq;
156}
157
Jon Hunterbe9f10c2013-02-21 15:20:53 -0600158static void omap2_onenand_calc_sync_timings(struct gpmc_timings *t,
159 unsigned int flags,
160 int freq)
Juha Yrjolaaa62e902009-05-28 13:23:52 -0700161{
Afzal Mohammed4f4426f2012-11-09 18:05:17 +0530162 struct gpmc_device_timings dev_t;
Juha Yrjolaaa62e902009-05-28 13:23:52 -0700163 const int t_cer = 15;
164 const int t_avdp = 12;
165 const int t_cez = 20; /* max of t_cez, t_oez */
Juha Yrjolaaa62e902009-05-28 13:23:52 -0700166 const int t_wpl = 40;
167 const int t_wph = 30;
168 int min_gpmc_clk_period, t_ces, t_avds, t_avdh, t_ach, t_aavdh, t_rdyo;
Afzal Mohammed4f4426f2012-11-09 18:05:17 +0530169 int div, gpmc_clk_ns;
Juha Yrjolaaa62e902009-05-28 13:23:52 -0700170
Jon Hunterbe9f10c2013-02-21 15:20:53 -0600171 if (flags & ONENAND_SYNC_READ)
Afzal Mohammed46376882012-06-05 10:11:48 +0530172 onenand_flags = ONENAND_FLAG_SYNCREAD;
Jon Hunterbe9f10c2013-02-21 15:20:53 -0600173 else if (flags & ONENAND_SYNC_READWRITE)
Afzal Mohammed46376882012-06-05 10:11:48 +0530174 onenand_flags = ONENAND_FLAG_SYNCREAD | ONENAND_FLAG_SYNCWRITE;
Juha Yrjolaaa62e902009-05-28 13:23:52 -0700175
176 switch (freq) {
Adrian Hunter49314452010-12-09 11:22:50 +0200177 case 104:
178 min_gpmc_clk_period = 9600; /* 104 MHz */
179 t_ces = 3;
180 t_avds = 4;
181 t_avdh = 2;
182 t_ach = 3;
183 t_aavdh = 6;
Adrian Hunter1435ca02011-02-07 10:46:58 +0200184 t_rdyo = 6;
Adrian Hunter49314452010-12-09 11:22:50 +0200185 break;
Juha Yrjolaaa62e902009-05-28 13:23:52 -0700186 case 83:
Adrian Huntera3551f52010-12-09 10:48:27 +0200187 min_gpmc_clk_period = 12000; /* 83 MHz */
Juha Yrjolaaa62e902009-05-28 13:23:52 -0700188 t_ces = 5;
189 t_avds = 4;
190 t_avdh = 2;
191 t_ach = 6;
192 t_aavdh = 6;
193 t_rdyo = 9;
194 break;
195 case 66:
Adrian Huntera3551f52010-12-09 10:48:27 +0200196 min_gpmc_clk_period = 15000; /* 66 MHz */
Juha Yrjolaaa62e902009-05-28 13:23:52 -0700197 t_ces = 6;
198 t_avds = 5;
199 t_avdh = 2;
200 t_ach = 6;
201 t_aavdh = 6;
202 t_rdyo = 11;
203 break;
204 default:
Adrian Huntera3551f52010-12-09 10:48:27 +0200205 min_gpmc_clk_period = 18500; /* 54 MHz */
Juha Yrjolaaa62e902009-05-28 13:23:52 -0700206 t_ces = 7;
207 t_avds = 7;
208 t_avdh = 7;
209 t_ach = 9;
210 t_aavdh = 7;
211 t_rdyo = 15;
Afzal Mohammed46376882012-06-05 10:11:48 +0530212 onenand_flags &= ~ONENAND_FLAG_SYNCWRITE;
Juha Yrjolaaa62e902009-05-28 13:23:52 -0700213 break;
214 }
215
Afzal Mohammed1b47ca12012-08-19 18:29:45 +0530216 div = gpmc_calc_divider(min_gpmc_clk_period);
Juha Yrjolaaa62e902009-05-28 13:23:52 -0700217 gpmc_clk_ns = gpmc_ticks_to_ns(div);
218 if (gpmc_clk_ns < 15) /* >66Mhz */
Afzal Mohammed46376882012-06-05 10:11:48 +0530219 onenand_flags |= ONENAND_FLAG_HF;
220 else
221 onenand_flags &= ~ONENAND_FLAG_HF;
Adrian Hunter1435ca02011-02-07 10:46:58 +0200222 if (gpmc_clk_ns < 12) /* >83Mhz */
Afzal Mohammed46376882012-06-05 10:11:48 +0530223 onenand_flags |= ONENAND_FLAG_VHF;
224 else
225 onenand_flags &= ~ONENAND_FLAG_VHF;
226 if (onenand_flags & ONENAND_FLAG_VHF)
Adrian Hunter1435ca02011-02-07 10:46:58 +0200227 latency = 8;
Afzal Mohammed46376882012-06-05 10:11:48 +0530228 else if (onenand_flags & ONENAND_FLAG_HF)
Juha Yrjolaaa62e902009-05-28 13:23:52 -0700229 latency = 6;
230 else if (gpmc_clk_ns >= 25) /* 40 MHz*/
231 latency = 3;
232 else
233 latency = 4;
234
Afzal Mohammed46376882012-06-05 10:11:48 +0530235 /* Set synchronous read timings */
Afzal Mohammed4f4426f2012-11-09 18:05:17 +0530236 memset(&dev_t, 0, sizeof(dev_t));
Juha Yrjolaaa62e902009-05-28 13:23:52 -0700237
Afzal Mohammed4f4426f2012-11-09 18:05:17 +0530238 dev_t.mux = true;
239 dev_t.sync_read = true;
Afzal Mohammed46376882012-06-05 10:11:48 +0530240 if (onenand_flags & ONENAND_FLAG_SYNCWRITE) {
Afzal Mohammed4f4426f2012-11-09 18:05:17 +0530241 dev_t.sync_write = true;
Juha Yrjolaaa62e902009-05-28 13:23:52 -0700242 } else {
Afzal Mohammed4f4426f2012-11-09 18:05:17 +0530243 dev_t.t_avdp_w = max(t_avdp, t_cer) * 1000;
244 dev_t.t_wpl = t_wpl * 1000;
245 dev_t.t_wph = t_wph * 1000;
246 dev_t.t_aavdh = t_aavdh * 1000;
Juha Yrjolaaa62e902009-05-28 13:23:52 -0700247 }
Afzal Mohammed4f4426f2012-11-09 18:05:17 +0530248 dev_t.ce_xdelay = true;
249 dev_t.avd_xdelay = true;
250 dev_t.oe_xdelay = true;
251 dev_t.we_xdelay = true;
252 dev_t.clk = min_gpmc_clk_period;
253 dev_t.t_bacc = dev_t.clk;
254 dev_t.t_ces = t_ces * 1000;
255 dev_t.t_avds = t_avds * 1000;
256 dev_t.t_avdh = t_avdh * 1000;
257 dev_t.t_ach = t_ach * 1000;
258 dev_t.cyc_iaa = (latency + 1);
259 dev_t.t_cez_r = t_cez * 1000;
260 dev_t.t_cez_w = dev_t.t_cez_r;
261 dev_t.cyc_aavdh_oe = 1;
262 dev_t.t_rdyo = t_rdyo * 1000 + min_gpmc_clk_period;
263
Jon Hunterbe9f10c2013-02-21 15:20:53 -0600264 gpmc_calc_timings(t, &dev_t);
Afzal Mohammed46376882012-06-05 10:11:48 +0530265}
266
267static int gpmc_set_sync_mode(int cs, struct gpmc_timings *t)
268{
269 unsigned sync_read = onenand_flags & ONENAND_FLAG_SYNCREAD;
270 unsigned sync_write = onenand_flags & ONENAND_FLAG_SYNCWRITE;
271
Juha Yrjolaaa62e902009-05-28 13:23:52 -0700272 /* Configure GPMC for synchronous read */
273 gpmc_cs_write_reg(cs, GPMC_CS_CONFIG1,
274 GPMC_CONFIG1_WRAPBURST_SUPP |
275 GPMC_CONFIG1_READMULTIPLE_SUPP |
276 (sync_read ? GPMC_CONFIG1_READTYPE_SYNC : 0) |
277 (sync_write ? GPMC_CONFIG1_WRITEMULTIPLE_SUPP : 0) |
278 (sync_write ? GPMC_CONFIG1_WRITETYPE_SYNC : 0) |
Juha Yrjolaaa62e902009-05-28 13:23:52 -0700279 GPMC_CONFIG1_PAGE_LEN(2) |
280 (cpu_is_omap34xx() ? 0 :
281 (GPMC_CONFIG1_WAIT_READ_MON |
282 GPMC_CONFIG1_WAIT_PIN_SEL(0))) |
283 GPMC_CONFIG1_DEVICESIZE_16 |
284 GPMC_CONFIG1_DEVICETYPE_NOR |
285 GPMC_CONFIG1_MUXADDDATA);
286
Afzal Mohammed46376882012-06-05 10:11:48 +0530287 return gpmc_cs_set_timings(cs, t);
288}
Juha Yrjolaaa62e902009-05-28 13:23:52 -0700289
Afzal Mohammed46376882012-06-05 10:11:48 +0530290static int omap2_onenand_setup_async(void __iomem *onenand_base)
291{
292 struct gpmc_timings t;
293 int ret;
294
295 omap2_onenand_set_async_mode(onenand_base);
296
Jon Hunterbe9f10c2013-02-21 15:20:53 -0600297 omap2_onenand_calc_async_timings(&t);
Afzal Mohammed46376882012-06-05 10:11:48 +0530298
299 ret = gpmc_set_async_mode(gpmc_onenand_data->cs, &t);
Russell King71856842013-03-13 20:44:21 +0000300 if (ret < 0)
Afzal Mohammed46376882012-06-05 10:11:48 +0530301 return ret;
302
303 omap2_onenand_set_async_mode(onenand_base);
304
305 return 0;
306}
307
308static int omap2_onenand_setup_sync(void __iomem *onenand_base, int *freq_ptr)
309{
310 int ret, freq = *freq_ptr;
311 struct gpmc_timings t;
Afzal Mohammed46376882012-06-05 10:11:48 +0530312
313 if (!freq) {
314 /* Very first call freq is not known */
Jon Hunter757ef792012-06-28 13:41:29 -0500315 freq = omap2_onenand_get_freq(gpmc_onenand_data, onenand_base);
Afzal Mohammed46376882012-06-05 10:11:48 +0530316 set_onenand_cfg(onenand_base);
317 }
318
Jon Hunterbe9f10c2013-02-21 15:20:53 -0600319 omap2_onenand_calc_sync_timings(&t, gpmc_onenand_data->flags, freq);
Afzal Mohammed46376882012-06-05 10:11:48 +0530320
321 ret = gpmc_set_sync_mode(gpmc_onenand_data->cs, &t);
Russell King71856842013-03-13 20:44:21 +0000322 if (ret < 0)
Afzal Mohammed46376882012-06-05 10:11:48 +0530323 return ret;
324
325 set_onenand_cfg(onenand_base);
Juha Yrjolaaa62e902009-05-28 13:23:52 -0700326
Adrian Hunter3ad2d862011-02-07 10:46:59 +0200327 *freq_ptr = freq;
328
Juha Yrjolaaa62e902009-05-28 13:23:52 -0700329 return 0;
330}
331
Adrian Hunter3ad2d862011-02-07 10:46:59 +0200332static int gpmc_onenand_setup(void __iomem *onenand_base, int *freq_ptr)
Juha Yrjolaaa62e902009-05-28 13:23:52 -0700333{
334 struct device *dev = &gpmc_onenand_device.dev;
Afzal Mohammed46376882012-06-05 10:11:48 +0530335 unsigned l = ONENAND_SYNC_READ | ONENAND_SYNC_READWRITE;
336 int ret;
Juha Yrjolaaa62e902009-05-28 13:23:52 -0700337
Afzal Mohammed46376882012-06-05 10:11:48 +0530338 ret = omap2_onenand_setup_async(onenand_base);
339 if (ret) {
340 dev_err(dev, "unable to set to async mode\n");
341 return ret;
Juha Yrjolaaa62e902009-05-28 13:23:52 -0700342 }
343
Afzal Mohammed46376882012-06-05 10:11:48 +0530344 if (!(gpmc_onenand_data->flags & l))
345 return 0;
346
347 ret = omap2_onenand_setup_sync(onenand_base, freq_ptr);
348 if (ret)
349 dev_err(dev, "unable to set to sync mode\n");
350 return ret;
Juha Yrjolaaa62e902009-05-28 13:23:52 -0700351}
352
Ezequiel Garciafaf5d2f2013-01-25 09:23:10 -0300353void gpmc_onenand_init(struct omap_onenand_platform_data *_onenand_data)
Juha Yrjolaaa62e902009-05-28 13:23:52 -0700354{
Afzal Mohammed681988b2012-08-30 12:53:23 -0700355 int err;
Ezequiel Garciadf25cb42013-02-12 16:22:22 -0300356 struct device *dev = &gpmc_onenand_device.dev;
Afzal Mohammed681988b2012-08-30 12:53:23 -0700357
Juha Yrjolaaa62e902009-05-28 13:23:52 -0700358 gpmc_onenand_data = _onenand_data;
359 gpmc_onenand_data->onenand_setup = gpmc_onenand_setup;
360 gpmc_onenand_device.dev.platform_data = gpmc_onenand_data;
361
362 if (cpu_is_omap24xx() &&
363 (gpmc_onenand_data->flags & ONENAND_SYNC_READWRITE)) {
Ezequiel Garcia7ab91592013-02-12 16:22:23 -0300364 dev_warn(dev, "OneNAND using only SYNC_READ on 24xx\n");
Juha Yrjolaaa62e902009-05-28 13:23:52 -0700365 gpmc_onenand_data->flags &= ~ONENAND_SYNC_READWRITE;
366 gpmc_onenand_data->flags |= ONENAND_SYNC_READ;
367 }
368
Afzal Mohammedeb77b6a2012-10-05 11:39:54 +0530369 if (cpu_is_omap34xx())
370 gpmc_onenand_data->flags |= ONENAND_IN_OMAP34XX;
371 else
372 gpmc_onenand_data->flags &= ~ONENAND_IN_OMAP34XX;
373
Afzal Mohammed681988b2012-08-30 12:53:23 -0700374 err = gpmc_cs_request(gpmc_onenand_data->cs, ONENAND_IO_SIZE,
375 (unsigned long *)&gpmc_onenand_resource.start);
376 if (err < 0) {
Ezequiel Garciadf25cb42013-02-12 16:22:22 -0300377 dev_err(dev, "Cannot request GPMC CS %d, error %d\n",
378 gpmc_onenand_data->cs, err);
Afzal Mohammed681988b2012-08-30 12:53:23 -0700379 return;
380 }
381
382 gpmc_onenand_resource.end = gpmc_onenand_resource.start +
383 ONENAND_IO_SIZE - 1;
384
Juha Yrjolaaa62e902009-05-28 13:23:52 -0700385 if (platform_device_register(&gpmc_onenand_device) < 0) {
Ezequiel Garciadf25cb42013-02-12 16:22:22 -0300386 dev_err(dev, "Unable to register OneNAND device\n");
Afzal Mohammed681988b2012-08-30 12:53:23 -0700387 gpmc_cs_free(gpmc_onenand_data->cs);
Juha Yrjolaaa62e902009-05-28 13:23:52 -0700388 return;
389 }
390}