blob: b049edbdf152627c321732b35e79115f7ca4d2f5 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
2 * linux/arch/arm/mach-ebsa110/core.c
3 *
4 * Copyright (C) 1998-2001 Russell King
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License version 2 as
8 * published by the Free Software Foundation.
9 *
10 * Extra MM routines for the EBSA-110 architecture
11 */
12#include <linux/kernel.h>
13#include <linux/mm.h>
14#include <linux/interrupt.h>
15#include <linux/serial_8250.h>
16#include <linux/init.h>
Russell Kingfced80c2008-09-06 12:10:45 +010017#include <linux/io.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070018
Russell Kinga09e64f2008-08-05 16:14:15 +010019#include <mach/hardware.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070020#include <asm/irq.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070021#include <asm/setup.h>
22#include <asm/mach-types.h>
23#include <asm/pgtable.h>
24#include <asm/page.h>
25#include <asm/system.h>
26
27#include <asm/mach/arch.h>
28#include <asm/mach/irq.h>
29#include <asm/mach/map.h>
30
31#include <asm/mach/time.h>
32
33#define IRQ_MASK 0xfe000000 /* read */
34#define IRQ_MSET 0xfe000000 /* write */
35#define IRQ_STAT 0xff000000 /* read */
36#define IRQ_MCLR 0xff000000 /* write */
37
Lennert Buytenhekc365e502010-11-29 10:29:25 +010038static void ebsa110_mask_irq(struct irq_data *d)
Linus Torvalds1da177e2005-04-16 15:20:36 -070039{
Lennert Buytenhekc365e502010-11-29 10:29:25 +010040 __raw_writeb(1 << d->irq, IRQ_MCLR);
Linus Torvalds1da177e2005-04-16 15:20:36 -070041}
42
Lennert Buytenhekc365e502010-11-29 10:29:25 +010043static void ebsa110_unmask_irq(struct irq_data *d)
Linus Torvalds1da177e2005-04-16 15:20:36 -070044{
Lennert Buytenhekc365e502010-11-29 10:29:25 +010045 __raw_writeb(1 << d->irq, IRQ_MSET);
Linus Torvalds1da177e2005-04-16 15:20:36 -070046}
47
Russell King10dd5ce2006-11-23 11:41:32 +000048static struct irq_chip ebsa110_irq_chip = {
Lennert Buytenhekc365e502010-11-29 10:29:25 +010049 .irq_ack = ebsa110_mask_irq,
50 .irq_mask = ebsa110_mask_irq,
51 .irq_unmask = ebsa110_unmask_irq,
Linus Torvalds1da177e2005-04-16 15:20:36 -070052};
53
54static void __init ebsa110_init_irq(void)
55{
56 unsigned long flags;
57 unsigned int irq;
58
59 local_irq_save(flags);
60 __raw_writeb(0xff, IRQ_MCLR);
61 __raw_writeb(0x55, IRQ_MSET);
62 __raw_writeb(0x00, IRQ_MSET);
63 if (__raw_readb(IRQ_MASK) != 0x55)
64 while (1);
65 __raw_writeb(0xff, IRQ_MCLR); /* clear all interrupt enables */
66 local_irq_restore(flags);
67
68 for (irq = 0; irq < NR_IRQS; irq++) {
Thomas Gleixnerf38c02f2011-03-24 13:35:09 +010069 irq_set_chip_and_handler(irq, &ebsa110_irq_chip,
70 handle_level_irq);
Linus Torvalds1da177e2005-04-16 15:20:36 -070071 set_irq_flags(irq, IRQF_VALID | IRQF_PROBE);
72 }
73}
74
75static struct map_desc ebsa110_io_desc[] __initdata = {
76 /*
77 * sparse external-decode ISAIO space
78 */
Deepak Saxena6cb19072005-10-28 15:19:08 +010079 { /* IRQ_STAT/IRQ_MCLR */
80 .virtual = IRQ_STAT,
81 .pfn = __phys_to_pfn(TRICK4_PHYS),
82 .length = PGDIR_SIZE,
83 .type = MT_DEVICE
84 }, { /* IRQ_MASK/IRQ_MSET */
85 .virtual = IRQ_MASK,
86 .pfn = __phys_to_pfn(TRICK3_PHYS),
87 .length = PGDIR_SIZE,
88 .type = MT_DEVICE
89 }, { /* SOFT_BASE */
90 .virtual = SOFT_BASE,
91 .pfn = __phys_to_pfn(TRICK1_PHYS),
92 .length = PGDIR_SIZE,
93 .type = MT_DEVICE
94 }, { /* PIT_BASE */
95 .virtual = PIT_BASE,
96 .pfn = __phys_to_pfn(TRICK0_PHYS),
97 .length = PGDIR_SIZE,
98 .type = MT_DEVICE
99 },
Linus Torvalds1da177e2005-04-16 15:20:36 -0700100
101 /*
102 * self-decode ISAIO space
103 */
Deepak Saxena6cb19072005-10-28 15:19:08 +0100104 {
105 .virtual = ISAIO_BASE,
106 .pfn = __phys_to_pfn(ISAIO_PHYS),
107 .length = ISAIO_SIZE,
108 .type = MT_DEVICE
109 }, {
110 .virtual = ISAMEM_BASE,
111 .pfn = __phys_to_pfn(ISAMEM_PHYS),
112 .length = ISAMEM_SIZE,
113 .type = MT_DEVICE
114 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700115};
116
117static void __init ebsa110_map_io(void)
118{
119 iotable_init(ebsa110_io_desc, ARRAY_SIZE(ebsa110_io_desc));
120}
121
Rob Herringed5bf6e2012-03-06 21:34:19 -0600122static void __iomem *ebsa110_ioremap_caller(unsigned long cookie, size_t size,
123 unsigned int flags, void *caller)
124{
125 return (void __iomem *)cookie;
126}
127
128static void ebsa110_iounmap(volatile void __iomem *io_addr)
129{}
130
131static void __init ebsa110_init_early(void)
132{
133 arch_ioremap_caller = ebsa110_ioremap_caller;
134 arch_iounmap = ebsa110_iounmap;
135}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700136
137#define PIT_CTRL (PIT_BASE + 0x0d)
138#define PIT_T2 (PIT_BASE + 0x09)
139#define PIT_T1 (PIT_BASE + 0x05)
140#define PIT_T0 (PIT_BASE + 0x01)
141
142/*
143 * This is the rate at which your MCLK signal toggles (in Hz)
144 * This was measured on a 10 digit frequency counter sampling
145 * over 1 second.
146 */
147#define MCLK 47894000
148
149/*
150 * This is the rate at which the PIT timers get clocked
151 */
152#define CLKBY7 (MCLK / 7)
153
154/*
155 * This is the counter value. We tick at 200Hz on this platform.
156 */
157#define COUNT ((CLKBY7 + (HZ / 2)) / HZ)
158
159/*
160 * Get the time offset from the system PIT. Note that if we have missed an
161 * interrupt, then the PIT counter will roll over (ie, be negative).
162 * This actually works out to be convenient.
163 */
164static unsigned long ebsa110_gettimeoffset(void)
165{
166 unsigned long offset, count;
167
168 __raw_writeb(0x40, PIT_CTRL);
169 count = __raw_readb(PIT_T1);
170 count |= __raw_readb(PIT_T1) << 8;
171
172 /*
173 * If count > COUNT, make the number negative.
174 */
175 if (count > COUNT)
176 count |= 0xffff0000;
177
178 offset = COUNT;
179 offset -= count;
180
181 /*
182 * `offset' is in units of timer counts. Convert
183 * offset to units of microseconds.
184 */
185 offset = offset * (1000000 / HZ) / COUNT;
186
187 return offset;
188}
189
190static irqreturn_t
Linus Torvalds0cd61b62006-10-06 10:53:39 -0700191ebsa110_timer_interrupt(int irq, void *dev_id)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700192{
193 u32 count;
194
Linus Torvalds1da177e2005-04-16 15:20:36 -0700195 /* latch and read timer 1 */
196 __raw_writeb(0x40, PIT_CTRL);
197 count = __raw_readb(PIT_T1);
198 count |= __raw_readb(PIT_T1) << 8;
199
200 count += COUNT;
201
202 __raw_writeb(count & 0xff, PIT_T1);
203 __raw_writeb(count >> 8, PIT_T1);
204
Linus Torvalds0cd61b62006-10-06 10:53:39 -0700205 timer_tick();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700206
Linus Torvalds1da177e2005-04-16 15:20:36 -0700207 return IRQ_HANDLED;
208}
209
210static struct irqaction ebsa110_timer_irq = {
211 .name = "EBSA110 Timer Tick",
Bernhard Walleb30faba2007-05-08 00:35:39 -0700212 .flags = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL,
Russell King09b8b5f2005-06-26 17:06:36 +0100213 .handler = ebsa110_timer_interrupt,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700214};
215
216/*
217 * Set up timer interrupt.
218 */
219static void __init ebsa110_timer_init(void)
220{
221 /*
222 * Timer 1, mode 2, LSB/MSB
223 */
224 __raw_writeb(0x70, PIT_CTRL);
225 __raw_writeb(COUNT & 0xff, PIT_T1);
226 __raw_writeb(COUNT >> 8, PIT_T1);
227
228 setup_irq(IRQ_EBSA110_TIMER0, &ebsa110_timer_irq);
229}
230
231static struct sys_timer ebsa110_timer = {
232 .init = ebsa110_timer_init,
233 .offset = ebsa110_gettimeoffset,
234};
235
236static struct plat_serial8250_port serial_platform_data[] = {
237 {
238 .iobase = 0x3f8,
239 .irq = 1,
240 .uartclk = 1843200,
241 .regshift = 0,
242 .iotype = UPIO_PORT,
243 .flags = UPF_BOOT_AUTOCONF | UPF_SKIP_TEST,
244 },
245 {
246 .iobase = 0x2f8,
247 .irq = 2,
248 .uartclk = 1843200,
249 .regshift = 0,
250 .iotype = UPIO_PORT,
251 .flags = UPF_BOOT_AUTOCONF | UPF_SKIP_TEST,
252 },
253 { },
254};
255
256static struct platform_device serial_device = {
257 .name = "serial8250",
Russell King6df29de2005-09-08 16:04:41 +0100258 .id = PLAT8250_DEV_PLATFORM,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700259 .dev = {
260 .platform_data = serial_platform_data,
261 },
262};
263
Russell King37bb30e2005-10-31 17:14:57 +0000264static struct resource am79c961_resources[] = {
265 {
266 .start = 0x220,
267 .end = 0x238,
268 .flags = IORESOURCE_IO,
269 }, {
270 .start = IRQ_EBSA110_ETHERNET,
271 .end = IRQ_EBSA110_ETHERNET,
272 .flags = IORESOURCE_IRQ,
273 },
274};
275
276static struct platform_device am79c961_device = {
277 .name = "am79c961",
278 .id = -1,
279 .num_resources = ARRAY_SIZE(am79c961_resources),
280 .resource = am79c961_resources,
281};
282
283static struct platform_device *ebsa110_devices[] = {
284 &serial_device,
285 &am79c961_device,
286};
287
Nicolas Pitre1b7f72f2011-08-02 12:52:48 -0400288/*
289 * EBSA110 idling methodology:
290 *
291 * We can not execute the "wait for interrupt" instruction since that
292 * will stop our MCLK signal (which provides the clock for the glue
293 * logic, and therefore the timer interrupt).
294 *
295 * Instead, we spin, polling the IRQ_STAT register for the occurrence
296 * of any interrupt with core clock down to the memory clock.
297 */
298static void ebsa110_idle(void)
299{
300 const char *irq_stat = (char *)0xff000000;
301
302 /* disable clock switching */
303 asm volatile ("mcr p15, 0, ip, c15, c2, 2" : : : "cc");
304
305 /* wait for an interrupt to occur */
306 while (!*irq_stat);
307
308 /* enable clock switching */
309 asm volatile ("mcr p15, 0, ip, c15, c1, 2" : : : "cc");
310}
311
Linus Torvalds1da177e2005-04-16 15:20:36 -0700312static int __init ebsa110_init(void)
313{
Nicolas Pitre1b7f72f2011-08-02 12:52:48 -0400314 arm_pm_idle = ebsa110_idle;
Russell King37bb30e2005-10-31 17:14:57 +0000315 return platform_add_devices(ebsa110_devices, ARRAY_SIZE(ebsa110_devices));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700316}
317
318arch_initcall(ebsa110_init);
319
Russell Kingda908262011-11-03 19:50:44 +0000320static void ebsa110_restart(char mode, const char *cmd)
321{
322 soft_restart(0x80000000);
323}
324
Linus Torvalds1da177e2005-04-16 15:20:36 -0700325MACHINE_START(EBSA110, "EBSA110")
Russell Kinge9dea0c2005-07-03 17:38:58 +0100326 /* Maintainer: Russell King */
Nicolas Pitre5eb980f2011-07-05 22:38:11 -0400327 .atag_offset = 0x400,
Russell Kinge9dea0c2005-07-03 17:38:58 +0100328 .reserve_lp0 = 1,
329 .reserve_lp2 = 1,
Russell Kingb44c3502011-11-01 14:27:33 +0000330 .restart_mode = 's',
Russell Kinge9dea0c2005-07-03 17:38:58 +0100331 .map_io = ebsa110_map_io,
Rob Herringed5bf6e2012-03-06 21:34:19 -0600332 .init_early = ebsa110_init_early,
Russell Kinge9dea0c2005-07-03 17:38:58 +0100333 .init_irq = ebsa110_init_irq,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700334 .timer = &ebsa110_timer,
Russell Kingda908262011-11-03 19:50:44 +0000335 .restart = ebsa110_restart,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700336MACHINE_END