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> | |
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 | ||
c365e506 | 38 | static void ebsa110_mask_irq(struct irq_data *d) |
1da177e4 | 39 | { |
c365e506 | 40 | __raw_writeb(1 << d->irq, IRQ_MCLR); |
1da177e4 LT |
41 | } |
42 | ||
c365e506 | 43 | static void ebsa110_unmask_irq(struct irq_data *d) |
1da177e4 | 44 | { |
c365e506 | 45 | __raw_writeb(1 << d->irq, IRQ_MSET); |
1da177e4 LT |
46 | } |
47 | ||
10dd5ce2 | 48 | static struct irq_chip ebsa110_irq_chip = { |
c365e506 LB |
49 | .irq_ack = ebsa110_mask_irq, |
50 | .irq_mask = ebsa110_mask_irq, | |
51 | .irq_unmask = ebsa110_unmask_irq, | |
1da177e4 LT |
52 | }; |
53 | ||
54 | static 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++) { | |
f38c02f3 TG |
69 | irq_set_chip_and_handler(irq, &ebsa110_irq_chip, |
70 | handle_level_irq); | |
1da177e4 LT |
71 | set_irq_flags(irq, IRQF_VALID | IRQF_PROBE); |
72 | } | |
73 | } | |
74 | ||
75 | static struct map_desc ebsa110_io_desc[] __initdata = { | |
76 | /* | |
77 | * sparse external-decode ISAIO space | |
78 | */ | |
6cb1907c DS |
79 | { /* 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 | }, | |
1da177e4 LT |
100 | |
101 | /* | |
102 | * self-decode ISAIO space | |
103 | */ | |
6cb1907c DS |
104 | { |
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 | } | |
1da177e4 LT |
115 | }; |
116 | ||
117 | static void __init ebsa110_map_io(void) | |
118 | { | |
119 | iotable_init(ebsa110_io_desc, ARRAY_SIZE(ebsa110_io_desc)); | |
120 | } | |
121 | ||
122 | ||
123 | #define PIT_CTRL (PIT_BASE + 0x0d) | |
124 | #define PIT_T2 (PIT_BASE + 0x09) | |
125 | #define PIT_T1 (PIT_BASE + 0x05) | |
126 | #define PIT_T0 (PIT_BASE + 0x01) | |
127 | ||
128 | /* | |
129 | * This is the rate at which your MCLK signal toggles (in Hz) | |
130 | * This was measured on a 10 digit frequency counter sampling | |
131 | * over 1 second. | |
132 | */ | |
133 | #define MCLK 47894000 | |
134 | ||
135 | /* | |
136 | * This is the rate at which the PIT timers get clocked | |
137 | */ | |
138 | #define CLKBY7 (MCLK / 7) | |
139 | ||
140 | /* | |
141 | * This is the counter value. We tick at 200Hz on this platform. | |
142 | */ | |
143 | #define COUNT ((CLKBY7 + (HZ / 2)) / HZ) | |
144 | ||
145 | /* | |
146 | * Get the time offset from the system PIT. Note that if we have missed an | |
147 | * interrupt, then the PIT counter will roll over (ie, be negative). | |
148 | * This actually works out to be convenient. | |
149 | */ | |
150 | static unsigned long ebsa110_gettimeoffset(void) | |
151 | { | |
152 | unsigned long offset, count; | |
153 | ||
154 | __raw_writeb(0x40, PIT_CTRL); | |
155 | count = __raw_readb(PIT_T1); | |
156 | count |= __raw_readb(PIT_T1) << 8; | |
157 | ||
158 | /* | |
159 | * If count > COUNT, make the number negative. | |
160 | */ | |
161 | if (count > COUNT) | |
162 | count |= 0xffff0000; | |
163 | ||
164 | offset = COUNT; | |
165 | offset -= count; | |
166 | ||
167 | /* | |
168 | * `offset' is in units of timer counts. Convert | |
169 | * offset to units of microseconds. | |
170 | */ | |
171 | offset = offset * (1000000 / HZ) / COUNT; | |
172 | ||
173 | return offset; | |
174 | } | |
175 | ||
176 | static irqreturn_t | |
0cd61b68 | 177 | ebsa110_timer_interrupt(int irq, void *dev_id) |
1da177e4 LT |
178 | { |
179 | u32 count; | |
180 | ||
1da177e4 LT |
181 | /* latch and read timer 1 */ |
182 | __raw_writeb(0x40, PIT_CTRL); | |
183 | count = __raw_readb(PIT_T1); | |
184 | count |= __raw_readb(PIT_T1) << 8; | |
185 | ||
186 | count += COUNT; | |
187 | ||
188 | __raw_writeb(count & 0xff, PIT_T1); | |
189 | __raw_writeb(count >> 8, PIT_T1); | |
190 | ||
0cd61b68 | 191 | timer_tick(); |
1da177e4 | 192 | |
1da177e4 LT |
193 | return IRQ_HANDLED; |
194 | } | |
195 | ||
196 | static struct irqaction ebsa110_timer_irq = { | |
197 | .name = "EBSA110 Timer Tick", | |
b30fabad | 198 | .flags = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL, |
09b8b5f8 | 199 | .handler = ebsa110_timer_interrupt, |
1da177e4 LT |
200 | }; |
201 | ||
202 | /* | |
203 | * Set up timer interrupt. | |
204 | */ | |
205 | static void __init ebsa110_timer_init(void) | |
206 | { | |
207 | /* | |
208 | * Timer 1, mode 2, LSB/MSB | |
209 | */ | |
210 | __raw_writeb(0x70, PIT_CTRL); | |
211 | __raw_writeb(COUNT & 0xff, PIT_T1); | |
212 | __raw_writeb(COUNT >> 8, PIT_T1); | |
213 | ||
214 | setup_irq(IRQ_EBSA110_TIMER0, &ebsa110_timer_irq); | |
215 | } | |
216 | ||
217 | static struct sys_timer ebsa110_timer = { | |
218 | .init = ebsa110_timer_init, | |
219 | .offset = ebsa110_gettimeoffset, | |
220 | }; | |
221 | ||
222 | static struct plat_serial8250_port serial_platform_data[] = { | |
223 | { | |
224 | .iobase = 0x3f8, | |
225 | .irq = 1, | |
226 | .uartclk = 1843200, | |
227 | .regshift = 0, | |
228 | .iotype = UPIO_PORT, | |
229 | .flags = UPF_BOOT_AUTOCONF | UPF_SKIP_TEST, | |
230 | }, | |
231 | { | |
232 | .iobase = 0x2f8, | |
233 | .irq = 2, | |
234 | .uartclk = 1843200, | |
235 | .regshift = 0, | |
236 | .iotype = UPIO_PORT, | |
237 | .flags = UPF_BOOT_AUTOCONF | UPF_SKIP_TEST, | |
238 | }, | |
239 | { }, | |
240 | }; | |
241 | ||
242 | static struct platform_device serial_device = { | |
243 | .name = "serial8250", | |
6df29deb | 244 | .id = PLAT8250_DEV_PLATFORM, |
1da177e4 LT |
245 | .dev = { |
246 | .platform_data = serial_platform_data, | |
247 | }, | |
248 | }; | |
249 | ||
37bb30e8 RK |
250 | static struct resource am79c961_resources[] = { |
251 | { | |
252 | .start = 0x220, | |
253 | .end = 0x238, | |
254 | .flags = IORESOURCE_IO, | |
255 | }, { | |
256 | .start = IRQ_EBSA110_ETHERNET, | |
257 | .end = IRQ_EBSA110_ETHERNET, | |
258 | .flags = IORESOURCE_IRQ, | |
259 | }, | |
260 | }; | |
261 | ||
262 | static struct platform_device am79c961_device = { | |
263 | .name = "am79c961", | |
264 | .id = -1, | |
265 | .num_resources = ARRAY_SIZE(am79c961_resources), | |
266 | .resource = am79c961_resources, | |
267 | }; | |
268 | ||
269 | static struct platform_device *ebsa110_devices[] = { | |
270 | &serial_device, | |
271 | &am79c961_device, | |
272 | }; | |
273 | ||
1da177e4 LT |
274 | static int __init ebsa110_init(void) |
275 | { | |
37bb30e8 | 276 | return platform_add_devices(ebsa110_devices, ARRAY_SIZE(ebsa110_devices)); |
1da177e4 LT |
277 | } |
278 | ||
279 | arch_initcall(ebsa110_init); | |
280 | ||
281 | MACHINE_START(EBSA110, "EBSA110") | |
e9dea0c6 | 282 | /* Maintainer: Russell King */ |
e9dea0c6 RK |
283 | .boot_params = 0x00000400, |
284 | .reserve_lp0 = 1, | |
285 | .reserve_lp2 = 1, | |
286 | .soft_reboot = 1, | |
287 | .map_io = ebsa110_map_io, | |
288 | .init_irq = ebsa110_init_irq, | |
1da177e4 LT |
289 | .timer = &ebsa110_timer, |
290 | MACHINE_END |