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); | |
e8d36d5d | 68 | irq_clear_status_flags(irq, IRQ_NOREQUEST | IRQ_NOPROBE); |
1da177e4 LT |
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 | ||
9b97173e | 119 | static void __iomem *ebsa110_ioremap_caller(phys_addr_t cookie, size_t size, |
ed5bf6e8 RH |
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 | */ | |
23c197b7 | 161 | static u32 ebsa110_gettimeoffset(void) |
1da177e4 LT |
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 | ||
23c197b7 | 184 | return offset * 1000; |
1da177e4 LT |
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", | |
78f6db99 | 209 | .flags = IRQF_TIMER | IRQF_IRQPOLL, |
09b8b5f8 | 210 | .handler = ebsa110_timer_interrupt, |
1da177e4 LT |
211 | }; |
212 | ||
213 | /* | |
214 | * Set up timer interrupt. | |
215 | */ | |
6bb27d73 | 216 | void __init ebsa110_timer_init(void) |
1da177e4 | 217 | { |
23c197b7 SW |
218 | arch_gettimeoffset = ebsa110_gettimeoffset; |
219 | ||
1da177e4 LT |
220 | /* |
221 | * Timer 1, mode 2, LSB/MSB | |
222 | */ | |
223 | __raw_writeb(0x70, PIT_CTRL); | |
224 | __raw_writeb(COUNT & 0xff, PIT_T1); | |
225 | __raw_writeb(COUNT >> 8, PIT_T1); | |
226 | ||
227 | setup_irq(IRQ_EBSA110_TIMER0, &ebsa110_timer_irq); | |
228 | } | |
229 | ||
1da177e4 LT |
230 | static struct plat_serial8250_port serial_platform_data[] = { |
231 | { | |
232 | .iobase = 0x3f8, | |
233 | .irq = 1, | |
234 | .uartclk = 1843200, | |
235 | .regshift = 0, | |
236 | .iotype = UPIO_PORT, | |
237 | .flags = UPF_BOOT_AUTOCONF | UPF_SKIP_TEST, | |
238 | }, | |
239 | { | |
240 | .iobase = 0x2f8, | |
241 | .irq = 2, | |
242 | .uartclk = 1843200, | |
243 | .regshift = 0, | |
244 | .iotype = UPIO_PORT, | |
245 | .flags = UPF_BOOT_AUTOCONF | UPF_SKIP_TEST, | |
246 | }, | |
247 | { }, | |
248 | }; | |
249 | ||
250 | static struct platform_device serial_device = { | |
251 | .name = "serial8250", | |
6df29deb | 252 | .id = PLAT8250_DEV_PLATFORM, |
1da177e4 LT |
253 | .dev = { |
254 | .platform_data = serial_platform_data, | |
255 | }, | |
256 | }; | |
257 | ||
37bb30e8 RK |
258 | static struct resource am79c961_resources[] = { |
259 | { | |
260 | .start = 0x220, | |
261 | .end = 0x238, | |
262 | .flags = IORESOURCE_IO, | |
263 | }, { | |
264 | .start = IRQ_EBSA110_ETHERNET, | |
265 | .end = IRQ_EBSA110_ETHERNET, | |
266 | .flags = IORESOURCE_IRQ, | |
267 | }, | |
268 | }; | |
269 | ||
270 | static struct platform_device am79c961_device = { | |
271 | .name = "am79c961", | |
272 | .id = -1, | |
273 | .num_resources = ARRAY_SIZE(am79c961_resources), | |
274 | .resource = am79c961_resources, | |
275 | }; | |
276 | ||
277 | static struct platform_device *ebsa110_devices[] = { | |
278 | &serial_device, | |
279 | &am79c961_device, | |
280 | }; | |
281 | ||
1b7f72fc NP |
282 | /* |
283 | * EBSA110 idling methodology: | |
284 | * | |
285 | * We can not execute the "wait for interrupt" instruction since that | |
286 | * will stop our MCLK signal (which provides the clock for the glue | |
287 | * logic, and therefore the timer interrupt). | |
288 | * | |
289 | * Instead, we spin, polling the IRQ_STAT register for the occurrence | |
290 | * of any interrupt with core clock down to the memory clock. | |
291 | */ | |
292 | static void ebsa110_idle(void) | |
293 | { | |
294 | const char *irq_stat = (char *)0xff000000; | |
295 | ||
296 | /* disable clock switching */ | |
297 | asm volatile ("mcr p15, 0, ip, c15, c2, 2" : : : "cc"); | |
298 | ||
299 | /* wait for an interrupt to occur */ | |
300 | while (!*irq_stat); | |
301 | ||
302 | /* enable clock switching */ | |
303 | asm volatile ("mcr p15, 0, ip, c15, c1, 2" : : : "cc"); | |
304 | } | |
305 | ||
1da177e4 LT |
306 | static int __init ebsa110_init(void) |
307 | { | |
1b7f72fc | 308 | arm_pm_idle = ebsa110_idle; |
37bb30e8 | 309 | return platform_add_devices(ebsa110_devices, ARRAY_SIZE(ebsa110_devices)); |
1da177e4 LT |
310 | } |
311 | ||
312 | arch_initcall(ebsa110_init); | |
313 | ||
7b6d864b | 314 | static void ebsa110_restart(enum reboot_mode mode, const char *cmd) |
da908260 RK |
315 | { |
316 | soft_restart(0x80000000); | |
317 | } | |
318 | ||
1da177e4 | 319 | MACHINE_START(EBSA110, "EBSA110") |
e9dea0c6 | 320 | /* Maintainer: Russell King */ |
5eb980f3 | 321 | .atag_offset = 0x400, |
e9dea0c6 RK |
322 | .reserve_lp0 = 1, |
323 | .reserve_lp2 = 1, | |
e9dea0c6 | 324 | .map_io = ebsa110_map_io, |
ed5bf6e8 | 325 | .init_early = ebsa110_init_early, |
e9dea0c6 | 326 | .init_irq = ebsa110_init_irq, |
6bb27d73 | 327 | .init_time = ebsa110_timer_init, |
da908260 | 328 | .restart = ebsa110_restart, |
1da177e4 | 329 | MACHINE_END |