Commit | Line | Data |
---|---|---|
1da177e4 LT |
1 | /* |
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> | |
fced80c7 | 17 | #include <linux/io.h> |
1da177e4 | 18 | |
a09e64fb | 19 | #include <mach/hardware.h> |
1da177e4 | 20 | #include <asm/irq.h> |
1da177e4 LT |
21 | #include <asm/setup.h> |
22 | #include <asm/mach-types.h> | |
23 | #include <asm/pgtable.h> | |
24 | #include <asm/page.h> | |
9f97da78 | 25 | #include <asm/system_misc.h> |
1da177e4 LT |
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 | ||
bcbbf908 | 33 | #include "core.h" |
1da177e4 | 34 | |
c365e506 | 35 | static void ebsa110_mask_irq(struct irq_data *d) |
1da177e4 | 36 | { |
c365e506 | 37 | __raw_writeb(1 << d->irq, IRQ_MCLR); |
1da177e4 LT |
38 | } |
39 | ||
c365e506 | 40 | static void ebsa110_unmask_irq(struct irq_data *d) |
1da177e4 | 41 | { |
c365e506 | 42 | __raw_writeb(1 << d->irq, IRQ_MSET); |
1da177e4 LT |
43 | } |
44 | ||
10dd5ce2 | 45 | static struct irq_chip ebsa110_irq_chip = { |
c365e506 LB |
46 | .irq_ack = ebsa110_mask_irq, |
47 | .irq_mask = ebsa110_mask_irq, | |
48 | .irq_unmask = ebsa110_unmask_irq, | |
1da177e4 LT |
49 | }; |
50 | ||
51 | static void __init ebsa110_init_irq(void) | |
52 | { | |
53 | unsigned long flags; | |
54 | unsigned int irq; | |
55 | ||
56 | local_irq_save(flags); | |
57 | __raw_writeb(0xff, IRQ_MCLR); | |
58 | __raw_writeb(0x55, IRQ_MSET); | |
59 | __raw_writeb(0x00, IRQ_MSET); | |
60 | if (__raw_readb(IRQ_MASK) != 0x55) | |
61 | while (1); | |
62 | __raw_writeb(0xff, IRQ_MCLR); /* clear all interrupt enables */ | |
63 | local_irq_restore(flags); | |
64 | ||
65 | for (irq = 0; irq < NR_IRQS; irq++) { | |
f38c02f3 TG |
66 | irq_set_chip_and_handler(irq, &ebsa110_irq_chip, |
67 | handle_level_irq); | |
1da177e4 LT |
68 | set_irq_flags(irq, IRQF_VALID | IRQF_PROBE); |
69 | } | |
70 | } | |
71 | ||
72 | static struct map_desc ebsa110_io_desc[] __initdata = { | |
73 | /* | |
74 | * sparse external-decode ISAIO space | |
75 | */ | |
6cb1907c | 76 | { /* IRQ_STAT/IRQ_MCLR */ |
a21e5e28 | 77 | .virtual = (unsigned long)IRQ_STAT, |
6cb1907c | 78 | .pfn = __phys_to_pfn(TRICK4_PHYS), |
5eca8f3a | 79 | .length = TRICK4_SIZE, |
6cb1907c DS |
80 | .type = MT_DEVICE |
81 | }, { /* IRQ_MASK/IRQ_MSET */ | |
a21e5e28 | 82 | .virtual = (unsigned long)IRQ_MASK, |
6cb1907c | 83 | .pfn = __phys_to_pfn(TRICK3_PHYS), |
5eca8f3a | 84 | .length = TRICK3_SIZE, |
6cb1907c DS |
85 | .type = MT_DEVICE |
86 | }, { /* SOFT_BASE */ | |
a21e5e28 | 87 | .virtual = (unsigned long)SOFT_BASE, |
6cb1907c | 88 | .pfn = __phys_to_pfn(TRICK1_PHYS), |
5eca8f3a | 89 | .length = TRICK1_SIZE, |
6cb1907c DS |
90 | .type = MT_DEVICE |
91 | }, { /* PIT_BASE */ | |
a21e5e28 | 92 | .virtual = (unsigned long)PIT_BASE, |
6cb1907c | 93 | .pfn = __phys_to_pfn(TRICK0_PHYS), |
5eca8f3a | 94 | .length = TRICK0_SIZE, |
6cb1907c DS |
95 | .type = MT_DEVICE |
96 | }, | |
1da177e4 LT |
97 | |
98 | /* | |
99 | * self-decode ISAIO space | |
100 | */ | |
6cb1907c DS |
101 | { |
102 | .virtual = ISAIO_BASE, | |
103 | .pfn = __phys_to_pfn(ISAIO_PHYS), | |
104 | .length = ISAIO_SIZE, | |
105 | .type = MT_DEVICE | |
106 | }, { | |
107 | .virtual = ISAMEM_BASE, | |
108 | .pfn = __phys_to_pfn(ISAMEM_PHYS), | |
109 | .length = ISAMEM_SIZE, | |
110 | .type = MT_DEVICE | |
111 | } | |
1da177e4 LT |
112 | }; |
113 | ||
114 | static void __init ebsa110_map_io(void) | |
115 | { | |
116 | iotable_init(ebsa110_io_desc, ARRAY_SIZE(ebsa110_io_desc)); | |
117 | } | |
118 | ||
ed5bf6e8 RH |
119 | static void __iomem *ebsa110_ioremap_caller(unsigned long cookie, size_t size, |
120 | unsigned int flags, void *caller) | |
121 | { | |
122 | return (void __iomem *)cookie; | |
123 | } | |
124 | ||
125 | static void ebsa110_iounmap(volatile void __iomem *io_addr) | |
126 | {} | |
127 | ||
128 | static void __init ebsa110_init_early(void) | |
129 | { | |
130 | arch_ioremap_caller = ebsa110_ioremap_caller; | |
131 | arch_iounmap = ebsa110_iounmap; | |
132 | } | |
1da177e4 LT |
133 | |
134 | #define PIT_CTRL (PIT_BASE + 0x0d) | |
135 | #define PIT_T2 (PIT_BASE + 0x09) | |
136 | #define PIT_T1 (PIT_BASE + 0x05) | |
137 | #define PIT_T0 (PIT_BASE + 0x01) | |
138 | ||
139 | /* | |
140 | * This is the rate at which your MCLK signal toggles (in Hz) | |
141 | * This was measured on a 10 digit frequency counter sampling | |
142 | * over 1 second. | |
143 | */ | |
144 | #define MCLK 47894000 | |
145 | ||
146 | /* | |
147 | * This is the rate at which the PIT timers get clocked | |
148 | */ | |
149 | #define CLKBY7 (MCLK / 7) | |
150 | ||
151 | /* | |
152 | * This is the counter value. We tick at 200Hz on this platform. | |
153 | */ | |
154 | #define COUNT ((CLKBY7 + (HZ / 2)) / HZ) | |
155 | ||
156 | /* | |
157 | * Get the time offset from the system PIT. Note that if we have missed an | |
158 | * interrupt, then the PIT counter will roll over (ie, be negative). | |
159 | * This actually works out to be convenient. | |
160 | */ | |
161 | static unsigned long ebsa110_gettimeoffset(void) | |
162 | { | |
163 | unsigned long offset, count; | |
164 | ||
165 | __raw_writeb(0x40, PIT_CTRL); | |
166 | count = __raw_readb(PIT_T1); | |
167 | count |= __raw_readb(PIT_T1) << 8; | |
168 | ||
169 | /* | |
170 | * If count > COUNT, make the number negative. | |
171 | */ | |
172 | if (count > COUNT) | |
173 | count |= 0xffff0000; | |
174 | ||
175 | offset = COUNT; | |
176 | offset -= count; | |
177 | ||
178 | /* | |
179 | * `offset' is in units of timer counts. Convert | |
180 | * offset to units of microseconds. | |
181 | */ | |
182 | offset = offset * (1000000 / HZ) / COUNT; | |
183 | ||
184 | return offset; | |
185 | } | |
186 | ||
187 | static irqreturn_t | |
0cd61b68 | 188 | ebsa110_timer_interrupt(int irq, void *dev_id) |
1da177e4 LT |
189 | { |
190 | u32 count; | |
191 | ||
1da177e4 LT |
192 | /* latch and read timer 1 */ |
193 | __raw_writeb(0x40, PIT_CTRL); | |
194 | count = __raw_readb(PIT_T1); | |
195 | count |= __raw_readb(PIT_T1) << 8; | |
196 | ||
197 | count += COUNT; | |
198 | ||
199 | __raw_writeb(count & 0xff, PIT_T1); | |
200 | __raw_writeb(count >> 8, PIT_T1); | |
201 | ||
0cd61b68 | 202 | timer_tick(); |
1da177e4 | 203 | |
1da177e4 LT |
204 | return IRQ_HANDLED; |
205 | } | |
206 | ||
207 | static struct irqaction ebsa110_timer_irq = { | |
208 | .name = "EBSA110 Timer Tick", | |
b30fabad | 209 | .flags = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL, |
09b8b5f8 | 210 | .handler = ebsa110_timer_interrupt, |
1da177e4 LT |
211 | }; |
212 | ||
213 | /* | |
214 | * Set up timer interrupt. | |
215 | */ | |
216 | static void __init ebsa110_timer_init(void) | |
217 | { | |
218 | /* | |
219 | * Timer 1, mode 2, LSB/MSB | |
220 | */ | |
221 | __raw_writeb(0x70, PIT_CTRL); | |
222 | __raw_writeb(COUNT & 0xff, PIT_T1); | |
223 | __raw_writeb(COUNT >> 8, PIT_T1); | |
224 | ||
225 | setup_irq(IRQ_EBSA110_TIMER0, &ebsa110_timer_irq); | |
226 | } | |
227 | ||
228 | static struct sys_timer ebsa110_timer = { | |
229 | .init = ebsa110_timer_init, | |
230 | .offset = ebsa110_gettimeoffset, | |
231 | }; | |
232 | ||
233 | static struct plat_serial8250_port serial_platform_data[] = { | |
234 | { | |
235 | .iobase = 0x3f8, | |
236 | .irq = 1, | |
237 | .uartclk = 1843200, | |
238 | .regshift = 0, | |
239 | .iotype = UPIO_PORT, | |
240 | .flags = UPF_BOOT_AUTOCONF | UPF_SKIP_TEST, | |
241 | }, | |
242 | { | |
243 | .iobase = 0x2f8, | |
244 | .irq = 2, | |
245 | .uartclk = 1843200, | |
246 | .regshift = 0, | |
247 | .iotype = UPIO_PORT, | |
248 | .flags = UPF_BOOT_AUTOCONF | UPF_SKIP_TEST, | |
249 | }, | |
250 | { }, | |
251 | }; | |
252 | ||
253 | static struct platform_device serial_device = { | |
254 | .name = "serial8250", | |
6df29deb | 255 | .id = PLAT8250_DEV_PLATFORM, |
1da177e4 LT |
256 | .dev = { |
257 | .platform_data = serial_platform_data, | |
258 | }, | |
259 | }; | |
260 | ||
37bb30e8 RK |
261 | static struct resource am79c961_resources[] = { |
262 | { | |
263 | .start = 0x220, | |
264 | .end = 0x238, | |
265 | .flags = IORESOURCE_IO, | |
266 | }, { | |
267 | .start = IRQ_EBSA110_ETHERNET, | |
268 | .end = IRQ_EBSA110_ETHERNET, | |
269 | .flags = IORESOURCE_IRQ, | |
270 | }, | |
271 | }; | |
272 | ||
273 | static struct platform_device am79c961_device = { | |
274 | .name = "am79c961", | |
275 | .id = -1, | |
276 | .num_resources = ARRAY_SIZE(am79c961_resources), | |
277 | .resource = am79c961_resources, | |
278 | }; | |
279 | ||
280 | static struct platform_device *ebsa110_devices[] = { | |
281 | &serial_device, | |
282 | &am79c961_device, | |
283 | }; | |
284 | ||
1b7f72fc NP |
285 | /* |
286 | * EBSA110 idling methodology: | |
287 | * | |
288 | * We can not execute the "wait for interrupt" instruction since that | |
289 | * will stop our MCLK signal (which provides the clock for the glue | |
290 | * logic, and therefore the timer interrupt). | |
291 | * | |
292 | * Instead, we spin, polling the IRQ_STAT register for the occurrence | |
293 | * of any interrupt with core clock down to the memory clock. | |
294 | */ | |
295 | static void ebsa110_idle(void) | |
296 | { | |
297 | const char *irq_stat = (char *)0xff000000; | |
298 | ||
299 | /* disable clock switching */ | |
300 | asm volatile ("mcr p15, 0, ip, c15, c2, 2" : : : "cc"); | |
301 | ||
302 | /* wait for an interrupt to occur */ | |
303 | while (!*irq_stat); | |
304 | ||
305 | /* enable clock switching */ | |
306 | asm volatile ("mcr p15, 0, ip, c15, c1, 2" : : : "cc"); | |
307 | } | |
308 | ||
1da177e4 LT |
309 | static int __init ebsa110_init(void) |
310 | { | |
1b7f72fc | 311 | arm_pm_idle = ebsa110_idle; |
37bb30e8 | 312 | return platform_add_devices(ebsa110_devices, ARRAY_SIZE(ebsa110_devices)); |
1da177e4 LT |
313 | } |
314 | ||
315 | arch_initcall(ebsa110_init); | |
316 | ||
da908260 RK |
317 | static void ebsa110_restart(char mode, const char *cmd) |
318 | { | |
319 | soft_restart(0x80000000); | |
320 | } | |
321 | ||
1da177e4 | 322 | MACHINE_START(EBSA110, "EBSA110") |
e9dea0c6 | 323 | /* Maintainer: Russell King */ |
5eb980f3 | 324 | .atag_offset = 0x400, |
e9dea0c6 RK |
325 | .reserve_lp0 = 1, |
326 | .reserve_lp2 = 1, | |
b44c350d | 327 | .restart_mode = 's', |
e9dea0c6 | 328 | .map_io = ebsa110_map_io, |
ed5bf6e8 | 329 | .init_early = ebsa110_init_early, |
e9dea0c6 | 330 | .init_irq = ebsa110_init_irq, |
1da177e4 | 331 | .timer = &ebsa110_timer, |
da908260 | 332 | .restart = ebsa110_restart, |
1da177e4 | 333 | MACHINE_END |