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> | |
17 | ||
18 | #include <asm/hardware.h> | |
19 | #include <asm/irq.h> | |
20 | #include <asm/io.h> | |
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 | ||
38 | static void ebsa110_mask_irq(unsigned int irq) | |
39 | { | |
40 | __raw_writeb(1 << irq, IRQ_MCLR); | |
41 | } | |
42 | ||
43 | static void ebsa110_unmask_irq(unsigned int irq) | |
44 | { | |
45 | __raw_writeb(1 << irq, IRQ_MSET); | |
46 | } | |
47 | ||
48 | static struct irqchip ebsa110_irq_chip = { | |
49 | .ack = ebsa110_mask_irq, | |
50 | .mask = ebsa110_mask_irq, | |
51 | .unmask = ebsa110_unmask_irq, | |
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++) { | |
69 | set_irq_chip(irq, &ebsa110_irq_chip); | |
70 | set_irq_handler(irq, do_level_IRQ); | |
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 | */ | |
79 | { IRQ_STAT, TRICK4_PHYS, PGDIR_SIZE, MT_DEVICE }, /* IRQ_STAT/IRQ_MCLR */ | |
80 | { IRQ_MASK, TRICK3_PHYS, PGDIR_SIZE, MT_DEVICE }, /* IRQ_MASK/IRQ_MSET */ | |
81 | { SOFT_BASE, TRICK1_PHYS, PGDIR_SIZE, MT_DEVICE }, /* SOFT_BASE */ | |
82 | { PIT_BASE, TRICK0_PHYS, PGDIR_SIZE, MT_DEVICE }, /* PIT_BASE */ | |
83 | ||
84 | /* | |
85 | * self-decode ISAIO space | |
86 | */ | |
87 | { ISAIO_BASE, ISAIO_PHYS, ISAIO_SIZE, MT_DEVICE }, | |
88 | { ISAMEM_BASE, ISAMEM_PHYS, ISAMEM_SIZE, MT_DEVICE } | |
89 | }; | |
90 | ||
91 | static void __init ebsa110_map_io(void) | |
92 | { | |
93 | iotable_init(ebsa110_io_desc, ARRAY_SIZE(ebsa110_io_desc)); | |
94 | } | |
95 | ||
96 | ||
97 | #define PIT_CTRL (PIT_BASE + 0x0d) | |
98 | #define PIT_T2 (PIT_BASE + 0x09) | |
99 | #define PIT_T1 (PIT_BASE + 0x05) | |
100 | #define PIT_T0 (PIT_BASE + 0x01) | |
101 | ||
102 | /* | |
103 | * This is the rate at which your MCLK signal toggles (in Hz) | |
104 | * This was measured on a 10 digit frequency counter sampling | |
105 | * over 1 second. | |
106 | */ | |
107 | #define MCLK 47894000 | |
108 | ||
109 | /* | |
110 | * This is the rate at which the PIT timers get clocked | |
111 | */ | |
112 | #define CLKBY7 (MCLK / 7) | |
113 | ||
114 | /* | |
115 | * This is the counter value. We tick at 200Hz on this platform. | |
116 | */ | |
117 | #define COUNT ((CLKBY7 + (HZ / 2)) / HZ) | |
118 | ||
119 | /* | |
120 | * Get the time offset from the system PIT. Note that if we have missed an | |
121 | * interrupt, then the PIT counter will roll over (ie, be negative). | |
122 | * This actually works out to be convenient. | |
123 | */ | |
124 | static unsigned long ebsa110_gettimeoffset(void) | |
125 | { | |
126 | unsigned long offset, count; | |
127 | ||
128 | __raw_writeb(0x40, PIT_CTRL); | |
129 | count = __raw_readb(PIT_T1); | |
130 | count |= __raw_readb(PIT_T1) << 8; | |
131 | ||
132 | /* | |
133 | * If count > COUNT, make the number negative. | |
134 | */ | |
135 | if (count > COUNT) | |
136 | count |= 0xffff0000; | |
137 | ||
138 | offset = COUNT; | |
139 | offset -= count; | |
140 | ||
141 | /* | |
142 | * `offset' is in units of timer counts. Convert | |
143 | * offset to units of microseconds. | |
144 | */ | |
145 | offset = offset * (1000000 / HZ) / COUNT; | |
146 | ||
147 | return offset; | |
148 | } | |
149 | ||
150 | static irqreturn_t | |
151 | ebsa110_timer_interrupt(int irq, void *dev_id, struct pt_regs *regs) | |
152 | { | |
153 | u32 count; | |
154 | ||
155 | write_seqlock(&xtime_lock); | |
156 | ||
157 | /* latch and read timer 1 */ | |
158 | __raw_writeb(0x40, PIT_CTRL); | |
159 | count = __raw_readb(PIT_T1); | |
160 | count |= __raw_readb(PIT_T1) << 8; | |
161 | ||
162 | count += COUNT; | |
163 | ||
164 | __raw_writeb(count & 0xff, PIT_T1); | |
165 | __raw_writeb(count >> 8, PIT_T1); | |
166 | ||
167 | timer_tick(regs); | |
168 | ||
169 | write_sequnlock(&xtime_lock); | |
170 | ||
171 | return IRQ_HANDLED; | |
172 | } | |
173 | ||
174 | static struct irqaction ebsa110_timer_irq = { | |
175 | .name = "EBSA110 Timer Tick", | |
09b8b5f8 RK |
176 | .flags = SA_INTERRUPT | SA_TIMER, |
177 | .handler = ebsa110_timer_interrupt, | |
1da177e4 LT |
178 | }; |
179 | ||
180 | /* | |
181 | * Set up timer interrupt. | |
182 | */ | |
183 | static void __init ebsa110_timer_init(void) | |
184 | { | |
185 | /* | |
186 | * Timer 1, mode 2, LSB/MSB | |
187 | */ | |
188 | __raw_writeb(0x70, PIT_CTRL); | |
189 | __raw_writeb(COUNT & 0xff, PIT_T1); | |
190 | __raw_writeb(COUNT >> 8, PIT_T1); | |
191 | ||
192 | setup_irq(IRQ_EBSA110_TIMER0, &ebsa110_timer_irq); | |
193 | } | |
194 | ||
195 | static struct sys_timer ebsa110_timer = { | |
196 | .init = ebsa110_timer_init, | |
197 | .offset = ebsa110_gettimeoffset, | |
198 | }; | |
199 | ||
200 | static struct plat_serial8250_port serial_platform_data[] = { | |
201 | { | |
202 | .iobase = 0x3f8, | |
203 | .irq = 1, | |
204 | .uartclk = 1843200, | |
205 | .regshift = 0, | |
206 | .iotype = UPIO_PORT, | |
207 | .flags = UPF_BOOT_AUTOCONF | UPF_SKIP_TEST, | |
208 | }, | |
209 | { | |
210 | .iobase = 0x2f8, | |
211 | .irq = 2, | |
212 | .uartclk = 1843200, | |
213 | .regshift = 0, | |
214 | .iotype = UPIO_PORT, | |
215 | .flags = UPF_BOOT_AUTOCONF | UPF_SKIP_TEST, | |
216 | }, | |
217 | { }, | |
218 | }; | |
219 | ||
220 | static struct platform_device serial_device = { | |
221 | .name = "serial8250", | |
222 | .id = 0, | |
223 | .dev = { | |
224 | .platform_data = serial_platform_data, | |
225 | }, | |
226 | }; | |
227 | ||
228 | static int __init ebsa110_init(void) | |
229 | { | |
230 | return platform_device_register(&serial_device); | |
231 | } | |
232 | ||
233 | arch_initcall(ebsa110_init); | |
234 | ||
235 | MACHINE_START(EBSA110, "EBSA110") | |
e9dea0c6 RK |
236 | /* Maintainer: Russell King */ |
237 | .phys_ram = 0x00000000, | |
238 | .phys_io = 0xe0000000, | |
239 | .io_pg_offst = ((0xe0000000) >> 18) & 0xfffc, | |
240 | .boot_params = 0x00000400, | |
241 | .reserve_lp0 = 1, | |
242 | .reserve_lp2 = 1, | |
243 | .soft_reboot = 1, | |
244 | .map_io = ebsa110_map_io, | |
245 | .init_irq = ebsa110_init_irq, | |
1da177e4 LT |
246 | .timer = &ebsa110_timer, |
247 | MACHINE_END |