blob: e7f2b80721c491e2ee9730c51b50b16d07573731 [file] [log] [blame]
Juha Yrjola4bbbc1a2006-06-26 16:16:16 -07001/*
2 * GPMC support functions
3 *
4 * Copyright (C) 2005-2006 Nokia Corporation
5 *
6 * Author: Juha Yrjola
7 *
Santosh Shilimkar44169072009-05-28 14:16:04 -07008 * Copyright (C) 2009 Texas Instruments
9 * Added OMAP4 support - Santosh Shilimkar <santosh.shilimkar@ti.com>
10 *
Juha Yrjola4bbbc1a2006-06-26 16:16:16 -070011 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License version 2 as
13 * published by the Free Software Foundation.
14 */
Paul Walmsleyfd1dc872008-10-06 15:49:17 +030015#undef DEBUG
16
Sukumar Ghoraidb97eb7d2011-01-28 15:42:05 +053017#include <linux/irq.h>
Juha Yrjola4bbbc1a2006-06-26 16:16:16 -070018#include <linux/kernel.h>
19#include <linux/init.h>
20#include <linux/err.h>
21#include <linux/clk.h>
Imre Deakf37e4582006-09-25 12:41:33 +030022#include <linux/ioport.h>
23#include <linux/spinlock.h>
Russell Kingfced80c2008-09-06 12:10:45 +010024#include <linux/io.h>
Paul Walmsleyfd1dc872008-10-06 15:49:17 +030025#include <linux/module.h>
Sukumar Ghoraidb97eb7d2011-01-28 15:42:05 +053026#include <linux/interrupt.h>
Afzal Mohammedda496872012-09-23 17:28:25 -060027#include <linux/platform_device.h>
Juha Yrjola4bbbc1a2006-06-26 16:16:16 -070028
Kyungmin Park7f245162006-12-29 16:48:51 -080029#include <asm/mach-types.h>
Tony Lindgrence491cf2009-10-20 09:40:47 -070030#include <plat/gpmc.h>
Juha Yrjola4bbbc1a2006-06-26 16:16:16 -070031
Tony Lindgren7d7e1eb2012-08-27 17:43:01 -070032#include <plat/cpu.h>
Tony Lindgrendbc04162012-08-31 10:59:07 -070033#include <plat/gpmc.h>
Afzal Mohammed4be48fd2012-09-23 17:28:24 -060034#include <plat/omap_device.h>
Juha Yrjola4bbbc1a2006-06-26 16:16:16 -070035
Tony Lindgrendbc04162012-08-31 10:59:07 -070036#include "soc.h"
Tony Lindgren7d7e1eb2012-08-27 17:43:01 -070037#include "common.h"
38
Afzal Mohammed4be48fd2012-09-23 17:28:24 -060039#define DEVICE_NAME "omap-gpmc"
40
Paul Walmsleyfd1dc872008-10-06 15:49:17 +030041/* GPMC register offsets */
Juha Yrjola4bbbc1a2006-06-26 16:16:16 -070042#define GPMC_REVISION 0x00
43#define GPMC_SYSCONFIG 0x10
44#define GPMC_SYSSTATUS 0x14
45#define GPMC_IRQSTATUS 0x18
46#define GPMC_IRQENABLE 0x1c
47#define GPMC_TIMEOUT_CONTROL 0x40
48#define GPMC_ERR_ADDRESS 0x44
49#define GPMC_ERR_TYPE 0x48
50#define GPMC_CONFIG 0x50
51#define GPMC_STATUS 0x54
52#define GPMC_PREFETCH_CONFIG1 0x1e0
53#define GPMC_PREFETCH_CONFIG2 0x1e4
Thara Gopinath15e02a32008-04-28 16:55:01 +053054#define GPMC_PREFETCH_CONTROL 0x1ec
Juha Yrjola4bbbc1a2006-06-26 16:16:16 -070055#define GPMC_PREFETCH_STATUS 0x1f0
56#define GPMC_ECC_CONFIG 0x1f4
57#define GPMC_ECC_CONTROL 0x1f8
58#define GPMC_ECC_SIZE_CONFIG 0x1fc
Sukumar Ghorai948d38e2010-07-09 09:14:44 +000059#define GPMC_ECC1_RESULT 0x200
Ivan Djelic8d602cf2012-04-26 14:17:49 +020060#define GPMC_ECC_BCH_RESULT_0 0x240 /* not available on OMAP2 */
Juha Yrjola4bbbc1a2006-06-26 16:16:16 -070061
Yegor Yefremov2c65e742012-05-09 08:32:49 -070062/* GPMC ECC control settings */
63#define GPMC_ECC_CTRL_ECCCLEAR 0x100
64#define GPMC_ECC_CTRL_ECCDISABLE 0x000
65#define GPMC_ECC_CTRL_ECCREG1 0x001
66#define GPMC_ECC_CTRL_ECCREG2 0x002
67#define GPMC_ECC_CTRL_ECCREG3 0x003
68#define GPMC_ECC_CTRL_ECCREG4 0x004
69#define GPMC_ECC_CTRL_ECCREG5 0x005
70#define GPMC_ECC_CTRL_ECCREG6 0x006
71#define GPMC_ECC_CTRL_ECCREG7 0x007
72#define GPMC_ECC_CTRL_ECCREG8 0x008
73#define GPMC_ECC_CTRL_ECCREG9 0x009
74
Sukumar Ghorai948d38e2010-07-09 09:14:44 +000075#define GPMC_CS0_OFFSET 0x60
Juha Yrjola4bbbc1a2006-06-26 16:16:16 -070076#define GPMC_CS_SIZE 0x30
77
Imre Deakf37e4582006-09-25 12:41:33 +030078#define GPMC_MEM_START 0x00000000
79#define GPMC_MEM_END 0x3FFFFFFF
80#define BOOT_ROM_SPACE 0x100000 /* 1MB */
81
82#define GPMC_CHUNK_SHIFT 24 /* 16 MB */
83#define GPMC_SECTION_SHIFT 28 /* 128 MB */
84
vimal singh59e9c5a2009-07-13 16:26:24 +053085#define CS_NUM_SHIFT 24
86#define ENABLE_PREFETCH (0x1 << 7)
87#define DMA_MPU_MODE 2
88
Afzal Mohammedda496872012-09-23 17:28:25 -060089#define GPMC_REVISION_MAJOR(l) ((l >> 4) & 0xf)
90#define GPMC_REVISION_MINOR(l) (l & 0xf)
91
92#define GPMC_HAS_WR_ACCESS 0x1
93#define GPMC_HAS_WR_DATA_MUX_BUS 0x2
94
Afzal Mohammed6b6c32f2012-08-30 12:53:23 -070095/* XXX: Only NAND irq has been considered,currently these are the only ones used
96 */
97#define GPMC_NR_IRQ 2
98
99struct gpmc_client_irq {
100 unsigned irq;
101 u32 bitmask;
102};
103
Rajendra Nayaka2d3e7b2008-09-26 17:47:33 +0530104/* Structure to save gpmc cs context */
105struct gpmc_cs_config {
106 u32 config1;
107 u32 config2;
108 u32 config3;
109 u32 config4;
110 u32 config5;
111 u32 config6;
112 u32 config7;
113 int is_valid;
114};
115
116/*
117 * Structure to save/restore gpmc context
118 * to support core off on OMAP3
119 */
120struct omap3_gpmc_regs {
121 u32 sysconfig;
122 u32 irqenable;
123 u32 timeout_ctrl;
124 u32 config;
125 u32 prefetch_config1;
126 u32 prefetch_config2;
127 u32 prefetch_control;
128 struct gpmc_cs_config cs_context[GPMC_CS_NUM];
129};
130
Afzal Mohammed6b6c32f2012-08-30 12:53:23 -0700131static struct gpmc_client_irq gpmc_client_irq[GPMC_NR_IRQ];
132static struct irq_chip gpmc_irq_chip;
133static unsigned gpmc_irq_start;
134
Imre Deakf37e4582006-09-25 12:41:33 +0300135static struct resource gpmc_mem_root;
136static struct resource gpmc_cs_mem[GPMC_CS_NUM];
Thomas Gleixner87b247c2007-05-10 22:33:04 -0700137static DEFINE_SPINLOCK(gpmc_mem_lock);
Sukumar Ghorai948d38e2010-07-09 09:14:44 +0000138static unsigned int gpmc_cs_map; /* flag for cs which are initialized */
139static int gpmc_ecc_used = -EINVAL; /* cs using ecc engine */
Afzal Mohammedda496872012-09-23 17:28:25 -0600140static struct device *gpmc_dev;
141static int gpmc_irq;
142static resource_size_t phys_base, mem_size;
143static unsigned gpmc_capability;
Paul Walmsleyfd1dc872008-10-06 15:49:17 +0300144static void __iomem *gpmc_base;
Juha Yrjola4bbbc1a2006-06-26 16:16:16 -0700145
Paul Walmsleyfd1dc872008-10-06 15:49:17 +0300146static struct clk *gpmc_l3_clk;
Juha Yrjola4bbbc1a2006-06-26 16:16:16 -0700147
Sukumar Ghoraidb97eb7d2011-01-28 15:42:05 +0530148static irqreturn_t gpmc_handle_irq(int irq, void *dev);
149
Juha Yrjola4bbbc1a2006-06-26 16:16:16 -0700150static void gpmc_write_reg(int idx, u32 val)
151{
152 __raw_writel(val, gpmc_base + idx);
153}
154
155static u32 gpmc_read_reg(int idx)
156{
157 return __raw_readl(gpmc_base + idx);
158}
159
Sukumar Ghorai948d38e2010-07-09 09:14:44 +0000160static void gpmc_cs_write_byte(int cs, int idx, u8 val)
161{
162 void __iomem *reg_addr;
163
164 reg_addr = gpmc_base + GPMC_CS0_OFFSET + (cs * GPMC_CS_SIZE) + idx;
165 __raw_writeb(val, reg_addr);
166}
167
168static u8 gpmc_cs_read_byte(int cs, int idx)
169{
170 void __iomem *reg_addr;
171
172 reg_addr = gpmc_base + GPMC_CS0_OFFSET + (cs * GPMC_CS_SIZE) + idx;
173 return __raw_readb(reg_addr);
174}
175
Juha Yrjola4bbbc1a2006-06-26 16:16:16 -0700176void gpmc_cs_write_reg(int cs, int idx, u32 val)
177{
178 void __iomem *reg_addr;
179
Sukumar Ghorai948d38e2010-07-09 09:14:44 +0000180 reg_addr = gpmc_base + GPMC_CS0_OFFSET + (cs * GPMC_CS_SIZE) + idx;
Juha Yrjola4bbbc1a2006-06-26 16:16:16 -0700181 __raw_writel(val, reg_addr);
182}
183
184u32 gpmc_cs_read_reg(int cs, int idx)
185{
Paul Walmsleyfd1dc872008-10-06 15:49:17 +0300186 void __iomem *reg_addr;
187
Sukumar Ghorai948d38e2010-07-09 09:14:44 +0000188 reg_addr = gpmc_base + GPMC_CS0_OFFSET + (cs * GPMC_CS_SIZE) + idx;
Paul Walmsleyfd1dc872008-10-06 15:49:17 +0300189 return __raw_readl(reg_addr);
Juha Yrjola4bbbc1a2006-06-26 16:16:16 -0700190}
191
Paul Walmsleyfd1dc872008-10-06 15:49:17 +0300192/* TODO: Add support for gpmc_fck to clock framework and use it */
David Brownell1c22cc12006-12-06 17:13:55 -0800193unsigned long gpmc_get_fclk_period(void)
Juha Yrjola4bbbc1a2006-06-26 16:16:16 -0700194{
Paul Walmsleyfd1dc872008-10-06 15:49:17 +0300195 unsigned long rate = clk_get_rate(gpmc_l3_clk);
196
197 if (rate == 0) {
198 printk(KERN_WARNING "gpmc_l3_clk not enabled\n");
199 return 0;
200 }
201
202 rate /= 1000;
203 rate = 1000000000 / rate; /* In picoseconds */
204
205 return rate;
Juha Yrjola4bbbc1a2006-06-26 16:16:16 -0700206}
207
208unsigned int gpmc_ns_to_ticks(unsigned int time_ns)
209{
210 unsigned long tick_ps;
211
212 /* Calculate in picosecs to yield more exact results */
213 tick_ps = gpmc_get_fclk_period();
214
215 return (time_ns * 1000 + tick_ps - 1) / tick_ps;
216}
217
Adrian Huntera3551f52010-12-09 10:48:27 +0200218unsigned int gpmc_ps_to_ticks(unsigned int time_ps)
219{
220 unsigned long tick_ps;
221
222 /* Calculate in picosecs to yield more exact results */
223 tick_ps = gpmc_get_fclk_period();
224
225 return (time_ps + tick_ps - 1) / tick_ps;
226}
227
Paul Walmsleyfd1dc872008-10-06 15:49:17 +0300228unsigned int gpmc_ticks_to_ns(unsigned int ticks)
229{
230 return ticks * gpmc_get_fclk_period() / 1000;
231}
232
Kai Svahn23300592007-01-26 12:29:40 -0800233unsigned int gpmc_round_ns_to_ticks(unsigned int time_ns)
234{
235 unsigned long ticks = gpmc_ns_to_ticks(time_ns);
236
237 return ticks * gpmc_get_fclk_period() / 1000;
238}
239
Juha Yrjola4bbbc1a2006-06-26 16:16:16 -0700240#ifdef DEBUG
241static int set_gpmc_timing_reg(int cs, int reg, int st_bit, int end_bit,
Juha Yrjola2aab6462006-06-26 16:16:21 -0700242 int time, const char *name)
Juha Yrjola4bbbc1a2006-06-26 16:16:16 -0700243#else
244static int set_gpmc_timing_reg(int cs, int reg, int st_bit, int end_bit,
245 int time)
246#endif
247{
248 u32 l;
249 int ticks, mask, nr_bits;
250
251 if (time == 0)
252 ticks = 0;
253 else
254 ticks = gpmc_ns_to_ticks(time);
255 nr_bits = end_bit - st_bit + 1;
David Brownell1c22cc12006-12-06 17:13:55 -0800256 if (ticks >= 1 << nr_bits) {
257#ifdef DEBUG
258 printk(KERN_INFO "GPMC CS%d: %-10s* %3d ns, %3d ticks >= %d\n",
259 cs, name, time, ticks, 1 << nr_bits);
260#endif
Juha Yrjola4bbbc1a2006-06-26 16:16:16 -0700261 return -1;
David Brownell1c22cc12006-12-06 17:13:55 -0800262 }
Juha Yrjola4bbbc1a2006-06-26 16:16:16 -0700263
264 mask = (1 << nr_bits) - 1;
265 l = gpmc_cs_read_reg(cs, reg);
266#ifdef DEBUG
David Brownell1c22cc12006-12-06 17:13:55 -0800267 printk(KERN_INFO
268 "GPMC CS%d: %-10s: %3d ticks, %3lu ns (was %3i ticks) %3d ns\n",
Juha Yrjola2aab6462006-06-26 16:16:21 -0700269 cs, name, ticks, gpmc_get_fclk_period() * ticks / 1000,
David Brownell1c22cc12006-12-06 17:13:55 -0800270 (l >> st_bit) & mask, time);
Juha Yrjola4bbbc1a2006-06-26 16:16:16 -0700271#endif
272 l &= ~(mask << st_bit);
273 l |= ticks << st_bit;
274 gpmc_cs_write_reg(cs, reg, l);
275
276 return 0;
277}
278
279#ifdef DEBUG
280#define GPMC_SET_ONE(reg, st, end, field) \
281 if (set_gpmc_timing_reg(cs, (reg), (st), (end), \
282 t->field, #field) < 0) \
283 return -1
284#else
285#define GPMC_SET_ONE(reg, st, end, field) \
286 if (set_gpmc_timing_reg(cs, (reg), (st), (end), t->field) < 0) \
287 return -1
288#endif
289
290int gpmc_cs_calc_divider(int cs, unsigned int sync_clk)
291{
292 int div;
293 u32 l;
294
Adrian Huntera3551f52010-12-09 10:48:27 +0200295 l = sync_clk + (gpmc_get_fclk_period() - 1);
Juha Yrjola4bbbc1a2006-06-26 16:16:16 -0700296 div = l / gpmc_get_fclk_period();
297 if (div > 4)
298 return -1;
David Brownell1c22cc12006-12-06 17:13:55 -0800299 if (div <= 0)
Juha Yrjola4bbbc1a2006-06-26 16:16:16 -0700300 div = 1;
301
302 return div;
303}
304
305int gpmc_cs_set_timings(int cs, const struct gpmc_timings *t)
306{
307 int div;
308 u32 l;
309
310 div = gpmc_cs_calc_divider(cs, t->sync_clk);
311 if (div < 0)
Paul Walmsleya032d332012-08-03 09:21:10 -0600312 return div;
Juha Yrjola4bbbc1a2006-06-26 16:16:16 -0700313
314 GPMC_SET_ONE(GPMC_CS_CONFIG2, 0, 3, cs_on);
315 GPMC_SET_ONE(GPMC_CS_CONFIG2, 8, 12, cs_rd_off);
316 GPMC_SET_ONE(GPMC_CS_CONFIG2, 16, 20, cs_wr_off);
317
318 GPMC_SET_ONE(GPMC_CS_CONFIG3, 0, 3, adv_on);
319 GPMC_SET_ONE(GPMC_CS_CONFIG3, 8, 12, adv_rd_off);
320 GPMC_SET_ONE(GPMC_CS_CONFIG3, 16, 20, adv_wr_off);
321
322 GPMC_SET_ONE(GPMC_CS_CONFIG4, 0, 3, oe_on);
323 GPMC_SET_ONE(GPMC_CS_CONFIG4, 8, 12, oe_off);
324 GPMC_SET_ONE(GPMC_CS_CONFIG4, 16, 19, we_on);
325 GPMC_SET_ONE(GPMC_CS_CONFIG4, 24, 28, we_off);
326
327 GPMC_SET_ONE(GPMC_CS_CONFIG5, 0, 4, rd_cycle);
328 GPMC_SET_ONE(GPMC_CS_CONFIG5, 8, 12, wr_cycle);
329 GPMC_SET_ONE(GPMC_CS_CONFIG5, 16, 20, access);
330
331 GPMC_SET_ONE(GPMC_CS_CONFIG5, 24, 27, page_burst_access);
332
Afzal Mohammedda496872012-09-23 17:28:25 -0600333 if (gpmc_capability & GPMC_HAS_WR_DATA_MUX_BUS)
Syed Mohammed, Khasimcc26b3b2008-10-09 17:51:41 +0300334 GPMC_SET_ONE(GPMC_CS_CONFIG6, 16, 19, wr_data_mux_bus);
Afzal Mohammedda496872012-09-23 17:28:25 -0600335 if (gpmc_capability & GPMC_HAS_WR_ACCESS)
Syed Mohammed, Khasimcc26b3b2008-10-09 17:51:41 +0300336 GPMC_SET_ONE(GPMC_CS_CONFIG6, 24, 28, wr_access);
Syed Mohammed, Khasimcc26b3b2008-10-09 17:51:41 +0300337
David Brownell1c22cc12006-12-06 17:13:55 -0800338 /* caller is expected to have initialized CONFIG1 to cover
339 * at least sync vs async
340 */
Juha Yrjola4bbbc1a2006-06-26 16:16:16 -0700341 l = gpmc_cs_read_reg(cs, GPMC_CS_CONFIG1);
David Brownell1c22cc12006-12-06 17:13:55 -0800342 if (l & (GPMC_CONFIG1_READTYPE_SYNC | GPMC_CONFIG1_WRITETYPE_SYNC)) {
343#ifdef DEBUG
344 printk(KERN_INFO "GPMC CS%d CLK period is %lu ns (div %d)\n",
345 cs, (div * gpmc_get_fclk_period()) / 1000, div);
346#endif
347 l &= ~0x03;
348 l |= (div - 1);
349 gpmc_cs_write_reg(cs, GPMC_CS_CONFIG1, l);
350 }
Juha Yrjola4bbbc1a2006-06-26 16:16:16 -0700351
352 return 0;
353}
354
Imre Deakf37e4582006-09-25 12:41:33 +0300355static void gpmc_cs_enable_mem(int cs, u32 base, u32 size)
Juha Yrjola4bbbc1a2006-06-26 16:16:16 -0700356{
Imre Deakf37e4582006-09-25 12:41:33 +0300357 u32 l;
358 u32 mask;
359
360 mask = (1 << GPMC_SECTION_SHIFT) - size;
361 l = gpmc_cs_read_reg(cs, GPMC_CS_CONFIG7);
362 l &= ~0x3f;
363 l = (base >> GPMC_CHUNK_SHIFT) & 0x3f;
364 l &= ~(0x0f << 8);
365 l |= ((mask >> GPMC_CHUNK_SHIFT) & 0x0f) << 8;
Rajendra Nayaka2d3e7b2008-09-26 17:47:33 +0530366 l |= GPMC_CONFIG7_CSVALID;
Imre Deakf37e4582006-09-25 12:41:33 +0300367 gpmc_cs_write_reg(cs, GPMC_CS_CONFIG7, l);
368}
369
370static void gpmc_cs_disable_mem(int cs)
371{
372 u32 l;
373
374 l = gpmc_cs_read_reg(cs, GPMC_CS_CONFIG7);
Rajendra Nayaka2d3e7b2008-09-26 17:47:33 +0530375 l &= ~GPMC_CONFIG7_CSVALID;
Imre Deakf37e4582006-09-25 12:41:33 +0300376 gpmc_cs_write_reg(cs, GPMC_CS_CONFIG7, l);
377}
378
379static void gpmc_cs_get_memconf(int cs, u32 *base, u32 *size)
380{
381 u32 l;
382 u32 mask;
383
384 l = gpmc_cs_read_reg(cs, GPMC_CS_CONFIG7);
385 *base = (l & 0x3f) << GPMC_CHUNK_SHIFT;
386 mask = (l >> 8) & 0x0f;
387 *size = (1 << GPMC_SECTION_SHIFT) - (mask << GPMC_CHUNK_SHIFT);
388}
389
390static int gpmc_cs_mem_enabled(int cs)
391{
392 u32 l;
393
394 l = gpmc_cs_read_reg(cs, GPMC_CS_CONFIG7);
Rajendra Nayaka2d3e7b2008-09-26 17:47:33 +0530395 return l & GPMC_CONFIG7_CSVALID;
Imre Deakf37e4582006-09-25 12:41:33 +0300396}
397
Tony Lindgrenc40fae952006-12-07 13:58:10 -0800398int gpmc_cs_set_reserved(int cs, int reserved)
Imre Deakf37e4582006-09-25 12:41:33 +0300399{
Tony Lindgrenc40fae952006-12-07 13:58:10 -0800400 if (cs > GPMC_CS_NUM)
401 return -ENODEV;
402
Imre Deakf37e4582006-09-25 12:41:33 +0300403 gpmc_cs_map &= ~(1 << cs);
404 gpmc_cs_map |= (reserved ? 1 : 0) << cs;
Tony Lindgrenc40fae952006-12-07 13:58:10 -0800405
406 return 0;
Imre Deakf37e4582006-09-25 12:41:33 +0300407}
408
Tony Lindgrenc40fae952006-12-07 13:58:10 -0800409int gpmc_cs_reserved(int cs)
Imre Deakf37e4582006-09-25 12:41:33 +0300410{
Tony Lindgrenc40fae952006-12-07 13:58:10 -0800411 if (cs > GPMC_CS_NUM)
412 return -ENODEV;
413
Imre Deakf37e4582006-09-25 12:41:33 +0300414 return gpmc_cs_map & (1 << cs);
415}
416
417static unsigned long gpmc_mem_align(unsigned long size)
418{
419 int order;
420
421 size = (size - 1) >> (GPMC_CHUNK_SHIFT - 1);
422 order = GPMC_CHUNK_SHIFT - 1;
423 do {
424 size >>= 1;
425 order++;
426 } while (size);
427 size = 1 << order;
428 return size;
429}
430
431static int gpmc_cs_insert_mem(int cs, unsigned long base, unsigned long size)
432{
433 struct resource *res = &gpmc_cs_mem[cs];
434 int r;
435
436 size = gpmc_mem_align(size);
437 spin_lock(&gpmc_mem_lock);
438 res->start = base;
439 res->end = base + size - 1;
440 r = request_resource(&gpmc_mem_root, res);
441 spin_unlock(&gpmc_mem_lock);
442
443 return r;
444}
445
Afzal Mohammedda496872012-09-23 17:28:25 -0600446static int gpmc_cs_delete_mem(int cs)
447{
448 struct resource *res = &gpmc_cs_mem[cs];
449 int r;
450
451 spin_lock(&gpmc_mem_lock);
452 r = release_resource(&gpmc_cs_mem[cs]);
453 res->start = 0;
454 res->end = 0;
455 spin_unlock(&gpmc_mem_lock);
456
457 return r;
458}
459
Imre Deakf37e4582006-09-25 12:41:33 +0300460int gpmc_cs_request(int cs, unsigned long size, unsigned long *base)
461{
462 struct resource *res = &gpmc_cs_mem[cs];
463 int r = -1;
464
465 if (cs > GPMC_CS_NUM)
466 return -ENODEV;
467
468 size = gpmc_mem_align(size);
469 if (size > (1 << GPMC_SECTION_SHIFT))
470 return -ENOMEM;
471
472 spin_lock(&gpmc_mem_lock);
473 if (gpmc_cs_reserved(cs)) {
474 r = -EBUSY;
475 goto out;
476 }
477 if (gpmc_cs_mem_enabled(cs))
478 r = adjust_resource(res, res->start & ~(size - 1), size);
479 if (r < 0)
480 r = allocate_resource(&gpmc_mem_root, res, size, 0, ~0,
481 size, NULL, NULL);
482 if (r < 0)
483 goto out;
484
Tobias Klauser6d135242009-11-10 18:55:19 -0800485 gpmc_cs_enable_mem(cs, res->start, resource_size(res));
Imre Deakf37e4582006-09-25 12:41:33 +0300486 *base = res->start;
487 gpmc_cs_set_reserved(cs, 1);
488out:
489 spin_unlock(&gpmc_mem_lock);
490 return r;
491}
Paul Walmsleyfd1dc872008-10-06 15:49:17 +0300492EXPORT_SYMBOL(gpmc_cs_request);
Imre Deakf37e4582006-09-25 12:41:33 +0300493
494void gpmc_cs_free(int cs)
495{
496 spin_lock(&gpmc_mem_lock);
Roel Kluine7fdc602009-11-17 14:39:06 -0800497 if (cs >= GPMC_CS_NUM || cs < 0 || !gpmc_cs_reserved(cs)) {
Imre Deakf37e4582006-09-25 12:41:33 +0300498 printk(KERN_ERR "Trying to free non-reserved GPMC CS%d\n", cs);
499 BUG();
500 spin_unlock(&gpmc_mem_lock);
501 return;
502 }
503 gpmc_cs_disable_mem(cs);
504 release_resource(&gpmc_cs_mem[cs]);
505 gpmc_cs_set_reserved(cs, 0);
506 spin_unlock(&gpmc_mem_lock);
507}
Paul Walmsleyfd1dc872008-10-06 15:49:17 +0300508EXPORT_SYMBOL(gpmc_cs_free);
Imre Deakf37e4582006-09-25 12:41:33 +0300509
vimal singh59e9c5a2009-07-13 16:26:24 +0530510/**
Sukumar Ghorai948d38e2010-07-09 09:14:44 +0000511 * gpmc_read_status - read access request to get the different gpmc status
512 * @cmd: command type
513 * @return status
514 */
515int gpmc_read_status(int cmd)
516{
517 int status = -EINVAL;
518 u32 regval = 0;
519
520 switch (cmd) {
521 case GPMC_GET_IRQ_STATUS:
522 status = gpmc_read_reg(GPMC_IRQSTATUS);
523 break;
524
525 case GPMC_PREFETCH_FIFO_CNT:
526 regval = gpmc_read_reg(GPMC_PREFETCH_STATUS);
527 status = GPMC_PREFETCH_STATUS_FIFO_CNT(regval);
528 break;
529
530 case GPMC_PREFETCH_COUNT:
531 regval = gpmc_read_reg(GPMC_PREFETCH_STATUS);
532 status = GPMC_PREFETCH_STATUS_COUNT(regval);
533 break;
534
535 case GPMC_STATUS_BUFFER:
536 regval = gpmc_read_reg(GPMC_STATUS);
537 /* 1 : buffer is available to write */
538 status = regval & GPMC_STATUS_BUFF_EMPTY;
539 break;
540
541 default:
542 printk(KERN_ERR "gpmc_read_status: Not supported\n");
543 }
544 return status;
545}
546EXPORT_SYMBOL(gpmc_read_status);
547
548/**
549 * gpmc_cs_configure - write request to configure gpmc
550 * @cs: chip select number
551 * @cmd: command type
552 * @wval: value to write
553 * @return status of the operation
554 */
555int gpmc_cs_configure(int cs, int cmd, int wval)
556{
557 int err = 0;
558 u32 regval = 0;
559
560 switch (cmd) {
Sukumar Ghoraidb97eb7d2011-01-28 15:42:05 +0530561 case GPMC_ENABLE_IRQ:
562 gpmc_write_reg(GPMC_IRQENABLE, wval);
563 break;
564
Sukumar Ghorai948d38e2010-07-09 09:14:44 +0000565 case GPMC_SET_IRQ_STATUS:
566 gpmc_write_reg(GPMC_IRQSTATUS, wval);
567 break;
568
569 case GPMC_CONFIG_WP:
570 regval = gpmc_read_reg(GPMC_CONFIG);
571 if (wval)
572 regval &= ~GPMC_CONFIG_WRITEPROTECT; /* WP is ON */
573 else
574 regval |= GPMC_CONFIG_WRITEPROTECT; /* WP is OFF */
575 gpmc_write_reg(GPMC_CONFIG, regval);
576 break;
577
578 case GPMC_CONFIG_RDY_BSY:
579 regval = gpmc_cs_read_reg(cs, GPMC_CS_CONFIG1);
580 if (wval)
581 regval |= WR_RD_PIN_MONITORING;
582 else
583 regval &= ~WR_RD_PIN_MONITORING;
584 gpmc_cs_write_reg(cs, GPMC_CS_CONFIG1, regval);
585 break;
586
587 case GPMC_CONFIG_DEV_SIZE:
588 regval = gpmc_cs_read_reg(cs, GPMC_CS_CONFIG1);
Yegor Yefremov8ef5d842012-01-23 08:32:23 +0100589
590 /* clear 2 target bits */
591 regval &= ~GPMC_CONFIG1_DEVICESIZE(3);
592
593 /* set the proper value */
Sukumar Ghorai948d38e2010-07-09 09:14:44 +0000594 regval |= GPMC_CONFIG1_DEVICESIZE(wval);
Yegor Yefremov8ef5d842012-01-23 08:32:23 +0100595
Sukumar Ghorai948d38e2010-07-09 09:14:44 +0000596 gpmc_cs_write_reg(cs, GPMC_CS_CONFIG1, regval);
597 break;
598
599 case GPMC_CONFIG_DEV_TYPE:
600 regval = gpmc_cs_read_reg(cs, GPMC_CS_CONFIG1);
601 regval |= GPMC_CONFIG1_DEVICETYPE(wval);
602 if (wval == GPMC_DEVICETYPE_NOR)
603 regval |= GPMC_CONFIG1_MUXADDDATA;
604 gpmc_cs_write_reg(cs, GPMC_CS_CONFIG1, regval);
605 break;
606
607 default:
608 printk(KERN_ERR "gpmc_configure_cs: Not supported\n");
609 err = -EINVAL;
610 }
611
612 return err;
613}
614EXPORT_SYMBOL(gpmc_cs_configure);
615
616/**
617 * gpmc_nand_read - nand specific read access request
618 * @cs: chip select number
619 * @cmd: command type
620 */
621int gpmc_nand_read(int cs, int cmd)
622{
623 int rval = -EINVAL;
624
625 switch (cmd) {
626 case GPMC_NAND_DATA:
627 rval = gpmc_cs_read_byte(cs, GPMC_CS_NAND_DATA);
628 break;
629
630 default:
631 printk(KERN_ERR "gpmc_read_nand_ctrl: Not supported\n");
632 }
633 return rval;
634}
635EXPORT_SYMBOL(gpmc_nand_read);
636
637/**
638 * gpmc_nand_write - nand specific write request
639 * @cs: chip select number
640 * @cmd: command type
641 * @wval: value to write
642 */
643int gpmc_nand_write(int cs, int cmd, int wval)
644{
645 int err = 0;
646
647 switch (cmd) {
648 case GPMC_NAND_COMMAND:
649 gpmc_cs_write_byte(cs, GPMC_CS_NAND_COMMAND, wval);
650 break;
651
652 case GPMC_NAND_ADDRESS:
653 gpmc_cs_write_byte(cs, GPMC_CS_NAND_ADDRESS, wval);
654 break;
655
656 case GPMC_NAND_DATA:
657 gpmc_cs_write_byte(cs, GPMC_CS_NAND_DATA, wval);
658
659 default:
660 printk(KERN_ERR "gpmc_write_nand_ctrl: Not supported\n");
661 err = -EINVAL;
662 }
663 return err;
664}
665EXPORT_SYMBOL(gpmc_nand_write);
666
667
668
669/**
vimal singh59e9c5a2009-07-13 16:26:24 +0530670 * gpmc_prefetch_enable - configures and starts prefetch transfer
Sukumar Ghorai948d38e2010-07-09 09:14:44 +0000671 * @cs: cs (chip select) number
Sukumar Ghorai317379a2011-01-28 15:42:07 +0530672 * @fifo_th: fifo threshold to be used for read/ write
vimal singh59e9c5a2009-07-13 16:26:24 +0530673 * @dma_mode: dma mode enable (1) or disable (0)
674 * @u32_count: number of bytes to be transferred
675 * @is_write: prefetch read(0) or write post(1) mode
676 */
Sukumar Ghorai317379a2011-01-28 15:42:07 +0530677int gpmc_prefetch_enable(int cs, int fifo_th, int dma_mode,
vimal singh59e9c5a2009-07-13 16:26:24 +0530678 unsigned int u32_count, int is_write)
679{
vimal singh59e9c5a2009-07-13 16:26:24 +0530680
Sukumar Ghorai317379a2011-01-28 15:42:07 +0530681 if (fifo_th > PREFETCH_FIFOTHRESHOLD_MAX) {
682 pr_err("gpmc: fifo threshold is not supported\n");
683 return -1;
684 } else if (!(gpmc_read_reg(GPMC_PREFETCH_CONTROL))) {
vimal singh59e9c5a2009-07-13 16:26:24 +0530685 /* Set the amount of bytes to be prefetched */
686 gpmc_write_reg(GPMC_PREFETCH_CONFIG2, u32_count);
687
688 /* Set dma/mpu mode, the prefetch read / post write and
689 * enable the engine. Set which cs is has requested for.
690 */
Sukumar Ghorai948d38e2010-07-09 09:14:44 +0000691 gpmc_write_reg(GPMC_PREFETCH_CONFIG1, ((cs << CS_NUM_SHIFT) |
Sukumar Ghorai317379a2011-01-28 15:42:07 +0530692 PREFETCH_FIFOTHRESHOLD(fifo_th) |
vimal singh59e9c5a2009-07-13 16:26:24 +0530693 ENABLE_PREFETCH |
694 (dma_mode << DMA_MPU_MODE) |
Sukumar Ghorai948d38e2010-07-09 09:14:44 +0000695 (0x1 & is_write)));
696
697 /* Start the prefetch engine */
698 gpmc_write_reg(GPMC_PREFETCH_CONTROL, 0x1);
vimal singh59e9c5a2009-07-13 16:26:24 +0530699 } else {
700 return -EBUSY;
701 }
vimal singh59e9c5a2009-07-13 16:26:24 +0530702
703 return 0;
704}
705EXPORT_SYMBOL(gpmc_prefetch_enable);
706
707/**
708 * gpmc_prefetch_reset - disables and stops the prefetch engine
709 */
Sukumar Ghorai948d38e2010-07-09 09:14:44 +0000710int gpmc_prefetch_reset(int cs)
vimal singh59e9c5a2009-07-13 16:26:24 +0530711{
Sukumar Ghorai948d38e2010-07-09 09:14:44 +0000712 u32 config1;
713
714 /* check if the same module/cs is trying to reset */
715 config1 = gpmc_read_reg(GPMC_PREFETCH_CONFIG1);
716 if (((config1 >> CS_NUM_SHIFT) & 0x7) != cs)
717 return -EINVAL;
718
vimal singh59e9c5a2009-07-13 16:26:24 +0530719 /* Stop the PFPW engine */
720 gpmc_write_reg(GPMC_PREFETCH_CONTROL, 0x0);
721
722 /* Reset/disable the PFPW engine */
723 gpmc_write_reg(GPMC_PREFETCH_CONFIG1, 0x0);
Sukumar Ghorai948d38e2010-07-09 09:14:44 +0000724
725 return 0;
vimal singh59e9c5a2009-07-13 16:26:24 +0530726}
727EXPORT_SYMBOL(gpmc_prefetch_reset);
728
Afzal Mohammed52bd1382012-08-30 12:53:22 -0700729void gpmc_update_nand_reg(struct gpmc_nand_regs *reg, int cs)
730{
731 reg->gpmc_status = gpmc_base + GPMC_STATUS;
732 reg->gpmc_nand_command = gpmc_base + GPMC_CS0_OFFSET +
733 GPMC_CS_NAND_COMMAND + GPMC_CS_SIZE * cs;
734 reg->gpmc_nand_address = gpmc_base + GPMC_CS0_OFFSET +
735 GPMC_CS_NAND_ADDRESS + GPMC_CS_SIZE * cs;
736 reg->gpmc_nand_data = gpmc_base + GPMC_CS0_OFFSET +
737 GPMC_CS_NAND_DATA + GPMC_CS_SIZE * cs;
738 reg->gpmc_prefetch_config1 = gpmc_base + GPMC_PREFETCH_CONFIG1;
739 reg->gpmc_prefetch_config2 = gpmc_base + GPMC_PREFETCH_CONFIG2;
740 reg->gpmc_prefetch_control = gpmc_base + GPMC_PREFETCH_CONTROL;
741 reg->gpmc_prefetch_status = gpmc_base + GPMC_PREFETCH_STATUS;
742 reg->gpmc_ecc_config = gpmc_base + GPMC_ECC_CONFIG;
743 reg->gpmc_ecc_control = gpmc_base + GPMC_ECC_CONTROL;
744 reg->gpmc_ecc_size_config = gpmc_base + GPMC_ECC_SIZE_CONFIG;
745 reg->gpmc_ecc1_result = gpmc_base + GPMC_ECC1_RESULT;
746 reg->gpmc_bch_result0 = gpmc_base + GPMC_ECC_BCH_RESULT_0;
747}
748
Afzal Mohammed6b6c32f2012-08-30 12:53:23 -0700749int gpmc_get_client_irq(unsigned irq_config)
750{
751 int i;
752
753 if (hweight32(irq_config) > 1)
754 return 0;
755
756 for (i = 0; i < GPMC_NR_IRQ; i++)
757 if (gpmc_client_irq[i].bitmask & irq_config)
758 return gpmc_client_irq[i].irq;
759
760 return 0;
761}
762
763static int gpmc_irq_endis(unsigned irq, bool endis)
764{
765 int i;
766 u32 regval;
767
768 for (i = 0; i < GPMC_NR_IRQ; i++)
769 if (irq == gpmc_client_irq[i].irq) {
770 regval = gpmc_read_reg(GPMC_IRQENABLE);
771 if (endis)
772 regval |= gpmc_client_irq[i].bitmask;
773 else
774 regval &= ~gpmc_client_irq[i].bitmask;
775 gpmc_write_reg(GPMC_IRQENABLE, regval);
776 break;
777 }
778
779 return 0;
780}
781
782static void gpmc_irq_disable(struct irq_data *p)
783{
784 gpmc_irq_endis(p->irq, false);
785}
786
787static void gpmc_irq_enable(struct irq_data *p)
788{
789 gpmc_irq_endis(p->irq, true);
790}
791
792static void gpmc_irq_noop(struct irq_data *data) { }
793
794static unsigned int gpmc_irq_noop_ret(struct irq_data *data) { return 0; }
795
Afzal Mohammedda496872012-09-23 17:28:25 -0600796static int gpmc_setup_irq(void)
Afzal Mohammed6b6c32f2012-08-30 12:53:23 -0700797{
798 int i;
799 u32 regval;
800
801 if (!gpmc_irq)
802 return -EINVAL;
803
804 gpmc_irq_start = irq_alloc_descs(-1, 0, GPMC_NR_IRQ, 0);
805 if (IS_ERR_VALUE(gpmc_irq_start)) {
806 pr_err("irq_alloc_descs failed\n");
807 return gpmc_irq_start;
808 }
809
810 gpmc_irq_chip.name = "gpmc";
811 gpmc_irq_chip.irq_startup = gpmc_irq_noop_ret;
812 gpmc_irq_chip.irq_enable = gpmc_irq_enable;
813 gpmc_irq_chip.irq_disable = gpmc_irq_disable;
814 gpmc_irq_chip.irq_shutdown = gpmc_irq_noop;
815 gpmc_irq_chip.irq_ack = gpmc_irq_noop;
816 gpmc_irq_chip.irq_mask = gpmc_irq_noop;
817 gpmc_irq_chip.irq_unmask = gpmc_irq_noop;
818
819 gpmc_client_irq[0].bitmask = GPMC_IRQ_FIFOEVENTENABLE;
820 gpmc_client_irq[1].bitmask = GPMC_IRQ_COUNT_EVENT;
821
822 for (i = 0; i < GPMC_NR_IRQ; i++) {
823 gpmc_client_irq[i].irq = gpmc_irq_start + i;
824 irq_set_chip_and_handler(gpmc_client_irq[i].irq,
825 &gpmc_irq_chip, handle_simple_irq);
826 set_irq_flags(gpmc_client_irq[i].irq,
827 IRQF_VALID | IRQF_NOAUTOEN);
828 }
829
830 /* Disable interrupts */
831 gpmc_write_reg(GPMC_IRQENABLE, 0);
832
833 /* clear interrupts */
834 regval = gpmc_read_reg(GPMC_IRQSTATUS);
835 gpmc_write_reg(GPMC_IRQSTATUS, regval);
836
837 return request_irq(gpmc_irq, gpmc_handle_irq, 0, "gpmc", NULL);
838}
839
Afzal Mohammed61687c62012-10-04 14:01:57 +0530840static __devexit int gpmc_free_irq(void)
Afzal Mohammedda496872012-09-23 17:28:25 -0600841{
842 int i;
843
844 if (gpmc_irq)
845 free_irq(gpmc_irq, NULL);
846
847 for (i = 0; i < GPMC_NR_IRQ; i++) {
848 irq_set_handler(gpmc_client_irq[i].irq, NULL);
849 irq_set_chip(gpmc_client_irq[i].irq, &no_irq_chip);
850 irq_modify_status(gpmc_client_irq[i].irq, 0, 0);
851 }
852
853 irq_free_descs(gpmc_irq_start, GPMC_NR_IRQ);
854
855 return 0;
856}
857
858static void __devexit gpmc_mem_exit(void)
859{
860 int cs;
861
862 for (cs = 0; cs < GPMC_CS_NUM; cs++) {
863 if (!gpmc_cs_mem_enabled(cs))
864 continue;
865 gpmc_cs_delete_mem(cs);
866 }
867
868}
869
870static void __devinit gpmc_mem_init(void)
Imre Deakf37e4582006-09-25 12:41:33 +0300871{
872 int cs;
873 unsigned long boot_rom_space = 0;
874
Kyungmin Park7f245162006-12-29 16:48:51 -0800875 /* never allocate the first page, to facilitate bug detection;
876 * even if we didn't boot from ROM.
877 */
878 boot_rom_space = BOOT_ROM_SPACE;
879 /* In apollon the CS0 is mapped as 0x0000 0000 */
880 if (machine_is_omap_apollon())
881 boot_rom_space = 0;
Imre Deakf37e4582006-09-25 12:41:33 +0300882 gpmc_mem_root.start = GPMC_MEM_START + boot_rom_space;
883 gpmc_mem_root.end = GPMC_MEM_END;
884
885 /* Reserve all regions that has been set up by bootloader */
886 for (cs = 0; cs < GPMC_CS_NUM; cs++) {
887 u32 base, size;
888
889 if (!gpmc_cs_mem_enabled(cs))
890 continue;
891 gpmc_cs_get_memconf(cs, &base, &size);
892 if (gpmc_cs_insert_mem(cs, base, size) < 0)
893 BUG();
894 }
Juha Yrjola4bbbc1a2006-06-26 16:16:16 -0700895}
896
Afzal Mohammedda496872012-09-23 17:28:25 -0600897static __devinit int gpmc_probe(struct platform_device *pdev)
Juha Yrjola4bbbc1a2006-06-26 16:16:16 -0700898{
Afzal Mohammed6b6c32f2012-08-30 12:53:23 -0700899 u32 l;
Afzal Mohammedda496872012-09-23 17:28:25 -0600900 struct resource *res;
Juha Yrjola4bbbc1a2006-06-26 16:16:16 -0700901
Afzal Mohammedda496872012-09-23 17:28:25 -0600902 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
903 if (res == NULL)
904 return -ENOENT;
Paul Walmsleyfd1dc872008-10-06 15:49:17 +0300905
Afzal Mohammedda496872012-09-23 17:28:25 -0600906 phys_base = res->start;
907 mem_size = resource_size(res);
Kevin Hilman8d084362010-01-29 14:20:06 -0800908
Afzal Mohammedda496872012-09-23 17:28:25 -0600909 gpmc_base = devm_request_and_ioremap(&pdev->dev, res);
Paul Walmsleyfd1dc872008-10-06 15:49:17 +0300910 if (!gpmc_base) {
Afzal Mohammedda496872012-09-23 17:28:25 -0600911 dev_err(&pdev->dev, "error: request memory / ioremap\n");
912 return -EADDRNOTAVAIL;
913 }
914
915 res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
916 if (res == NULL)
917 dev_warn(&pdev->dev, "Failed to get resource: irq\n");
918 else
919 gpmc_irq = res->start;
920
921 gpmc_l3_clk = clk_get(&pdev->dev, "fck");
922 if (IS_ERR(gpmc_l3_clk)) {
923 dev_err(&pdev->dev, "error: clk_get\n");
924 gpmc_irq = 0;
925 return PTR_ERR(gpmc_l3_clk);
Paul Walmsleyfd1dc872008-10-06 15:49:17 +0300926 }
927
Rajendra Nayak4d7cb452012-09-22 02:24:16 -0600928 clk_prepare_enable(gpmc_l3_clk);
Olof Johansson1daa8c12010-01-20 22:39:29 +0000929
Afzal Mohammedda496872012-09-23 17:28:25 -0600930 gpmc_dev = &pdev->dev;
931
Juha Yrjola4bbbc1a2006-06-26 16:16:16 -0700932 l = gpmc_read_reg(GPMC_REVISION);
Afzal Mohammedda496872012-09-23 17:28:25 -0600933 if (GPMC_REVISION_MAJOR(l) > 0x4)
934 gpmc_capability = GPMC_HAS_WR_ACCESS | GPMC_HAS_WR_DATA_MUX_BUS;
935 dev_info(gpmc_dev, "GPMC revision %d.%d\n", GPMC_REVISION_MAJOR(l),
936 GPMC_REVISION_MINOR(l));
937
Imre Deakf37e4582006-09-25 12:41:33 +0300938 gpmc_mem_init();
Sukumar Ghoraidb97eb7d2011-01-28 15:42:05 +0530939
Afzal Mohammedda496872012-09-23 17:28:25 -0600940 if (IS_ERR_VALUE(gpmc_setup_irq()))
941 dev_warn(gpmc_dev, "gpmc_setup_irq failed\n");
942
943 return 0;
Sukumar Ghoraidb97eb7d2011-01-28 15:42:05 +0530944}
Afzal Mohammedda496872012-09-23 17:28:25 -0600945
Afzal Mohammed61687c62012-10-04 14:01:57 +0530946static __devexit int gpmc_remove(struct platform_device *pdev)
Afzal Mohammedda496872012-09-23 17:28:25 -0600947{
948 gpmc_free_irq();
949 gpmc_mem_exit();
950 gpmc_dev = NULL;
951 return 0;
952}
953
954static struct platform_driver gpmc_driver = {
955 .probe = gpmc_probe,
956 .remove = __devexit_p(gpmc_remove),
957 .driver = {
958 .name = DEVICE_NAME,
959 .owner = THIS_MODULE,
960 },
961};
962
963static __init int gpmc_init(void)
964{
965 return platform_driver_register(&gpmc_driver);
966}
967
968static __exit void gpmc_exit(void)
969{
970 platform_driver_unregister(&gpmc_driver);
971
972}
973
Sukumar Ghoraidb97eb7d2011-01-28 15:42:05 +0530974postcore_initcall(gpmc_init);
Afzal Mohammedda496872012-09-23 17:28:25 -0600975module_exit(gpmc_exit);
Sukumar Ghoraidb97eb7d2011-01-28 15:42:05 +0530976
Afzal Mohammed4be48fd2012-09-23 17:28:24 -0600977static int __init omap_gpmc_init(void)
978{
979 struct omap_hwmod *oh;
980 struct platform_device *pdev;
981 char *oh_name = "gpmc";
982
983 oh = omap_hwmod_lookup(oh_name);
984 if (!oh) {
985 pr_err("Could not look up %s\n", oh_name);
986 return -ENODEV;
987 }
988
989 pdev = omap_device_build(DEVICE_NAME, -1, oh, NULL, 0, NULL, 0, 0);
990 WARN(IS_ERR(pdev), "could not build omap_device for %s\n", oh_name);
991
992 return IS_ERR(pdev) ? PTR_ERR(pdev) : 0;
993}
994postcore_initcall(omap_gpmc_init);
995
Sukumar Ghoraidb97eb7d2011-01-28 15:42:05 +0530996static irqreturn_t gpmc_handle_irq(int irq, void *dev)
997{
Afzal Mohammed6b6c32f2012-08-30 12:53:23 -0700998 int i;
999 u32 regval;
Sukumar Ghoraidb97eb7d2011-01-28 15:42:05 +05301000
Afzal Mohammed6b6c32f2012-08-30 12:53:23 -07001001 regval = gpmc_read_reg(GPMC_IRQSTATUS);
1002
1003 if (!regval)
1004 return IRQ_NONE;
1005
1006 for (i = 0; i < GPMC_NR_IRQ; i++)
1007 if (regval & gpmc_client_irq[i].bitmask)
1008 generic_handle_irq(gpmc_client_irq[i].irq);
1009
1010 gpmc_write_reg(GPMC_IRQSTATUS, regval);
Sukumar Ghoraidb97eb7d2011-01-28 15:42:05 +05301011
1012 return IRQ_HANDLED;
Juha Yrjola4bbbc1a2006-06-26 16:16:16 -07001013}
Rajendra Nayaka2d3e7b2008-09-26 17:47:33 +05301014
1015#ifdef CONFIG_ARCH_OMAP3
1016static struct omap3_gpmc_regs gpmc_context;
1017
Felipe Balbib2fa3b72010-02-15 10:03:33 -08001018void omap3_gpmc_save_context(void)
Rajendra Nayaka2d3e7b2008-09-26 17:47:33 +05301019{
1020 int i;
Felipe Balbib2fa3b72010-02-15 10:03:33 -08001021
Rajendra Nayaka2d3e7b2008-09-26 17:47:33 +05301022 gpmc_context.sysconfig = gpmc_read_reg(GPMC_SYSCONFIG);
1023 gpmc_context.irqenable = gpmc_read_reg(GPMC_IRQENABLE);
1024 gpmc_context.timeout_ctrl = gpmc_read_reg(GPMC_TIMEOUT_CONTROL);
1025 gpmc_context.config = gpmc_read_reg(GPMC_CONFIG);
1026 gpmc_context.prefetch_config1 = gpmc_read_reg(GPMC_PREFETCH_CONFIG1);
1027 gpmc_context.prefetch_config2 = gpmc_read_reg(GPMC_PREFETCH_CONFIG2);
1028 gpmc_context.prefetch_control = gpmc_read_reg(GPMC_PREFETCH_CONTROL);
1029 for (i = 0; i < GPMC_CS_NUM; i++) {
1030 gpmc_context.cs_context[i].is_valid = gpmc_cs_mem_enabled(i);
1031 if (gpmc_context.cs_context[i].is_valid) {
1032 gpmc_context.cs_context[i].config1 =
1033 gpmc_cs_read_reg(i, GPMC_CS_CONFIG1);
1034 gpmc_context.cs_context[i].config2 =
1035 gpmc_cs_read_reg(i, GPMC_CS_CONFIG2);
1036 gpmc_context.cs_context[i].config3 =
1037 gpmc_cs_read_reg(i, GPMC_CS_CONFIG3);
1038 gpmc_context.cs_context[i].config4 =
1039 gpmc_cs_read_reg(i, GPMC_CS_CONFIG4);
1040 gpmc_context.cs_context[i].config5 =
1041 gpmc_cs_read_reg(i, GPMC_CS_CONFIG5);
1042 gpmc_context.cs_context[i].config6 =
1043 gpmc_cs_read_reg(i, GPMC_CS_CONFIG6);
1044 gpmc_context.cs_context[i].config7 =
1045 gpmc_cs_read_reg(i, GPMC_CS_CONFIG7);
1046 }
1047 }
1048}
1049
Felipe Balbib2fa3b72010-02-15 10:03:33 -08001050void omap3_gpmc_restore_context(void)
Rajendra Nayaka2d3e7b2008-09-26 17:47:33 +05301051{
1052 int i;
Felipe Balbib2fa3b72010-02-15 10:03:33 -08001053
Rajendra Nayaka2d3e7b2008-09-26 17:47:33 +05301054 gpmc_write_reg(GPMC_SYSCONFIG, gpmc_context.sysconfig);
1055 gpmc_write_reg(GPMC_IRQENABLE, gpmc_context.irqenable);
1056 gpmc_write_reg(GPMC_TIMEOUT_CONTROL, gpmc_context.timeout_ctrl);
1057 gpmc_write_reg(GPMC_CONFIG, gpmc_context.config);
1058 gpmc_write_reg(GPMC_PREFETCH_CONFIG1, gpmc_context.prefetch_config1);
1059 gpmc_write_reg(GPMC_PREFETCH_CONFIG2, gpmc_context.prefetch_config2);
1060 gpmc_write_reg(GPMC_PREFETCH_CONTROL, gpmc_context.prefetch_control);
1061 for (i = 0; i < GPMC_CS_NUM; i++) {
1062 if (gpmc_context.cs_context[i].is_valid) {
1063 gpmc_cs_write_reg(i, GPMC_CS_CONFIG1,
1064 gpmc_context.cs_context[i].config1);
1065 gpmc_cs_write_reg(i, GPMC_CS_CONFIG2,
1066 gpmc_context.cs_context[i].config2);
1067 gpmc_cs_write_reg(i, GPMC_CS_CONFIG3,
1068 gpmc_context.cs_context[i].config3);
1069 gpmc_cs_write_reg(i, GPMC_CS_CONFIG4,
1070 gpmc_context.cs_context[i].config4);
1071 gpmc_cs_write_reg(i, GPMC_CS_CONFIG5,
1072 gpmc_context.cs_context[i].config5);
1073 gpmc_cs_write_reg(i, GPMC_CS_CONFIG6,
1074 gpmc_context.cs_context[i].config6);
1075 gpmc_cs_write_reg(i, GPMC_CS_CONFIG7,
1076 gpmc_context.cs_context[i].config7);
1077 }
1078 }
1079}
1080#endif /* CONFIG_ARCH_OMAP3 */
Sukumar Ghorai948d38e2010-07-09 09:14:44 +00001081
1082/**
1083 * gpmc_enable_hwecc - enable hardware ecc functionality
1084 * @cs: chip select number
1085 * @mode: read/write mode
1086 * @dev_width: device bus width(1 for x16, 0 for x8)
1087 * @ecc_size: bytes for which ECC will be generated
1088 */
1089int gpmc_enable_hwecc(int cs, int mode, int dev_width, int ecc_size)
1090{
1091 unsigned int val;
1092
1093 /* check if ecc module is in used */
1094 if (gpmc_ecc_used != -EINVAL)
1095 return -EINVAL;
1096
1097 gpmc_ecc_used = cs;
1098
1099 /* clear ecc and enable bits */
Yegor Yefremov2c65e742012-05-09 08:32:49 -07001100 gpmc_write_reg(GPMC_ECC_CONTROL,
1101 GPMC_ECC_CTRL_ECCCLEAR |
1102 GPMC_ECC_CTRL_ECCREG1);
Sukumar Ghorai948d38e2010-07-09 09:14:44 +00001103
1104 /* program ecc and result sizes */
1105 val = ((((ecc_size >> 1) - 1) << 22) | (0x0000000F));
1106 gpmc_write_reg(GPMC_ECC_SIZE_CONFIG, val);
1107
1108 switch (mode) {
1109 case GPMC_ECC_READ:
Yegor Yefremov2c65e742012-05-09 08:32:49 -07001110 case GPMC_ECC_WRITE:
1111 gpmc_write_reg(GPMC_ECC_CONTROL,
1112 GPMC_ECC_CTRL_ECCCLEAR |
1113 GPMC_ECC_CTRL_ECCREG1);
Sukumar Ghorai948d38e2010-07-09 09:14:44 +00001114 break;
1115 case GPMC_ECC_READSYN:
Yegor Yefremov2c65e742012-05-09 08:32:49 -07001116 gpmc_write_reg(GPMC_ECC_CONTROL,
1117 GPMC_ECC_CTRL_ECCCLEAR |
1118 GPMC_ECC_CTRL_ECCDISABLE);
Sukumar Ghorai948d38e2010-07-09 09:14:44 +00001119 break;
1120 default:
1121 printk(KERN_INFO "Error: Unrecognized Mode[%d]!\n", mode);
1122 break;
1123 }
1124
1125 /* (ECC 16 or 8 bit col) | ( CS ) | ECC Enable */
1126 val = (dev_width << 7) | (cs << 1) | (0x1);
1127 gpmc_write_reg(GPMC_ECC_CONFIG, val);
1128 return 0;
1129}
Bernhard Wallef611b022012-03-05 16:11:01 -08001130EXPORT_SYMBOL_GPL(gpmc_enable_hwecc);
Sukumar Ghorai948d38e2010-07-09 09:14:44 +00001131
1132/**
1133 * gpmc_calculate_ecc - generate non-inverted ecc bytes
1134 * @cs: chip select number
1135 * @dat: data pointer over which ecc is computed
1136 * @ecc_code: ecc code buffer
1137 *
1138 * Using non-inverted ECC is considered ugly since writing a blank
1139 * page (padding) will clear the ECC bytes. This is not a problem as long
1140 * no one is trying to write data on the seemingly unused page. Reading
1141 * an erased page will produce an ECC mismatch between generated and read
1142 * ECC bytes that has to be dealt with separately.
1143 */
1144int gpmc_calculate_ecc(int cs, const u_char *dat, u_char *ecc_code)
1145{
1146 unsigned int val = 0x0;
1147
1148 if (gpmc_ecc_used != cs)
1149 return -EINVAL;
1150
1151 /* read ecc result */
1152 val = gpmc_read_reg(GPMC_ECC1_RESULT);
1153 *ecc_code++ = val; /* P128e, ..., P1e */
1154 *ecc_code++ = val >> 16; /* P128o, ..., P1o */
1155 /* P2048o, P1024o, P512o, P256o, P2048e, P1024e, P512e, P256e */
1156 *ecc_code++ = ((val >> 8) & 0x0f) | ((val >> 20) & 0xf0);
1157
1158 gpmc_ecc_used = -EINVAL;
1159 return 0;
1160}
Bernhard Wallef611b022012-03-05 16:11:01 -08001161EXPORT_SYMBOL_GPL(gpmc_calculate_ecc);
Ivan Djelic8d602cf2012-04-26 14:17:49 +02001162
1163#ifdef CONFIG_ARCH_OMAP3
1164
1165/**
1166 * gpmc_init_hwecc_bch - initialize hardware BCH ecc functionality
1167 * @cs: chip select number
1168 * @nsectors: how many 512-byte sectors to process
1169 * @nerrors: how many errors to correct per sector (4 or 8)
1170 *
1171 * This function must be executed before any call to gpmc_enable_hwecc_bch.
1172 */
1173int gpmc_init_hwecc_bch(int cs, int nsectors, int nerrors)
1174{
1175 /* check if ecc module is in use */
1176 if (gpmc_ecc_used != -EINVAL)
1177 return -EINVAL;
1178
1179 /* support only OMAP3 class */
1180 if (!cpu_is_omap34xx()) {
1181 printk(KERN_ERR "BCH ecc is not supported on this CPU\n");
1182 return -EINVAL;
1183 }
1184
1185 /*
1186 * For now, assume 4-bit mode is only supported on OMAP3630 ES1.x, x>=1.
1187 * Other chips may be added if confirmed to work.
1188 */
1189 if ((nerrors == 4) &&
1190 (!cpu_is_omap3630() || (GET_OMAP_REVISION() == 0))) {
1191 printk(KERN_ERR "BCH 4-bit mode is not supported on this CPU\n");
1192 return -EINVAL;
1193 }
1194
1195 /* sanity check */
1196 if (nsectors > 8) {
1197 printk(KERN_ERR "BCH cannot process %d sectors (max is 8)\n",
1198 nsectors);
1199 return -EINVAL;
1200 }
1201
1202 return 0;
1203}
1204EXPORT_SYMBOL_GPL(gpmc_init_hwecc_bch);
1205
1206/**
1207 * gpmc_enable_hwecc_bch - enable hardware BCH ecc functionality
1208 * @cs: chip select number
1209 * @mode: read/write mode
1210 * @dev_width: device bus width(1 for x16, 0 for x8)
1211 * @nsectors: how many 512-byte sectors to process
1212 * @nerrors: how many errors to correct per sector (4 or 8)
1213 */
1214int gpmc_enable_hwecc_bch(int cs, int mode, int dev_width, int nsectors,
1215 int nerrors)
1216{
1217 unsigned int val;
1218
1219 /* check if ecc module is in use */
1220 if (gpmc_ecc_used != -EINVAL)
1221 return -EINVAL;
1222
1223 gpmc_ecc_used = cs;
1224
1225 /* clear ecc and enable bits */
1226 gpmc_write_reg(GPMC_ECC_CONTROL, 0x1);
1227
1228 /*
1229 * When using BCH, sector size is hardcoded to 512 bytes.
1230 * Here we are using wrapping mode 6 both for reading and writing, with:
1231 * size0 = 0 (no additional protected byte in spare area)
1232 * size1 = 32 (skip 32 nibbles = 16 bytes per sector in spare area)
1233 */
1234 gpmc_write_reg(GPMC_ECC_SIZE_CONFIG, (32 << 22) | (0 << 12));
1235
1236 /* BCH configuration */
1237 val = ((1 << 16) | /* enable BCH */
1238 (((nerrors == 8) ? 1 : 0) << 12) | /* 8 or 4 bits */
1239 (0x06 << 8) | /* wrap mode = 6 */
1240 (dev_width << 7) | /* bus width */
1241 (((nsectors-1) & 0x7) << 4) | /* number of sectors */
1242 (cs << 1) | /* ECC CS */
1243 (0x1)); /* enable ECC */
1244
1245 gpmc_write_reg(GPMC_ECC_CONFIG, val);
1246 gpmc_write_reg(GPMC_ECC_CONTROL, 0x101);
1247 return 0;
1248}
1249EXPORT_SYMBOL_GPL(gpmc_enable_hwecc_bch);
1250
1251/**
1252 * gpmc_calculate_ecc_bch4 - Generate 7 ecc bytes per sector of 512 data bytes
1253 * @cs: chip select number
1254 * @dat: The pointer to data on which ecc is computed
1255 * @ecc: The ecc output buffer
1256 */
1257int gpmc_calculate_ecc_bch4(int cs, const u_char *dat, u_char *ecc)
1258{
1259 int i;
1260 unsigned long nsectors, reg, val1, val2;
1261
1262 if (gpmc_ecc_used != cs)
1263 return -EINVAL;
1264
1265 nsectors = ((gpmc_read_reg(GPMC_ECC_CONFIG) >> 4) & 0x7) + 1;
1266
1267 for (i = 0; i < nsectors; i++) {
1268
1269 reg = GPMC_ECC_BCH_RESULT_0 + 16*i;
1270
1271 /* Read hw-computed remainder */
1272 val1 = gpmc_read_reg(reg + 0);
1273 val2 = gpmc_read_reg(reg + 4);
1274
1275 /*
1276 * Add constant polynomial to remainder, in order to get an ecc
1277 * sequence of 0xFFs for a buffer filled with 0xFFs; and
1278 * left-justify the resulting polynomial.
1279 */
1280 *ecc++ = 0x28 ^ ((val2 >> 12) & 0xFF);
1281 *ecc++ = 0x13 ^ ((val2 >> 4) & 0xFF);
1282 *ecc++ = 0xcc ^ (((val2 & 0xF) << 4)|((val1 >> 28) & 0xF));
1283 *ecc++ = 0x39 ^ ((val1 >> 20) & 0xFF);
1284 *ecc++ = 0x96 ^ ((val1 >> 12) & 0xFF);
1285 *ecc++ = 0xac ^ ((val1 >> 4) & 0xFF);
1286 *ecc++ = 0x7f ^ ((val1 & 0xF) << 4);
1287 }
1288
1289 gpmc_ecc_used = -EINVAL;
1290 return 0;
1291}
1292EXPORT_SYMBOL_GPL(gpmc_calculate_ecc_bch4);
1293
1294/**
1295 * gpmc_calculate_ecc_bch8 - Generate 13 ecc bytes per block of 512 data bytes
1296 * @cs: chip select number
1297 * @dat: The pointer to data on which ecc is computed
1298 * @ecc: The ecc output buffer
1299 */
1300int gpmc_calculate_ecc_bch8(int cs, const u_char *dat, u_char *ecc)
1301{
1302 int i;
1303 unsigned long nsectors, reg, val1, val2, val3, val4;
1304
1305 if (gpmc_ecc_used != cs)
1306 return -EINVAL;
1307
1308 nsectors = ((gpmc_read_reg(GPMC_ECC_CONFIG) >> 4) & 0x7) + 1;
1309
1310 for (i = 0; i < nsectors; i++) {
1311
1312 reg = GPMC_ECC_BCH_RESULT_0 + 16*i;
1313
1314 /* Read hw-computed remainder */
1315 val1 = gpmc_read_reg(reg + 0);
1316 val2 = gpmc_read_reg(reg + 4);
1317 val3 = gpmc_read_reg(reg + 8);
1318 val4 = gpmc_read_reg(reg + 12);
1319
1320 /*
1321 * Add constant polynomial to remainder, in order to get an ecc
1322 * sequence of 0xFFs for a buffer filled with 0xFFs.
1323 */
1324 *ecc++ = 0xef ^ (val4 & 0xFF);
1325 *ecc++ = 0x51 ^ ((val3 >> 24) & 0xFF);
1326 *ecc++ = 0x2e ^ ((val3 >> 16) & 0xFF);
1327 *ecc++ = 0x09 ^ ((val3 >> 8) & 0xFF);
1328 *ecc++ = 0xed ^ (val3 & 0xFF);
1329 *ecc++ = 0x93 ^ ((val2 >> 24) & 0xFF);
1330 *ecc++ = 0x9a ^ ((val2 >> 16) & 0xFF);
1331 *ecc++ = 0xc2 ^ ((val2 >> 8) & 0xFF);
1332 *ecc++ = 0x97 ^ (val2 & 0xFF);
1333 *ecc++ = 0x79 ^ ((val1 >> 24) & 0xFF);
1334 *ecc++ = 0xe5 ^ ((val1 >> 16) & 0xFF);
1335 *ecc++ = 0x24 ^ ((val1 >> 8) & 0xFF);
1336 *ecc++ = 0xb5 ^ (val1 & 0xFF);
1337 }
1338
1339 gpmc_ecc_used = -EINVAL;
1340 return 0;
1341}
1342EXPORT_SYMBOL_GPL(gpmc_calculate_ecc_bch8);
1343
1344#endif /* CONFIG_ARCH_OMAP3 */