diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /arch/arm/mach-ixp2000/core.c |
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'arch/arm/mach-ixp2000/core.c')
-rw-r--r-- | arch/arm/mach-ixp2000/core.c | 390 |
1 files changed, 390 insertions, 0 deletions
diff --git a/arch/arm/mach-ixp2000/core.c b/arch/arm/mach-ixp2000/core.c new file mode 100644 index 000000000000..4f3c3d5c781c --- /dev/null +++ b/arch/arm/mach-ixp2000/core.c | |||
@@ -0,0 +1,390 @@ | |||
1 | /* | ||
2 | * arch/arm/mach-ixp2000/common.c | ||
3 | * | ||
4 | * Common routines used by all IXP2400/2800 based platforms. | ||
5 | * | ||
6 | * Author: Deepak Saxena <dsaxena@plexity.net> | ||
7 | * | ||
8 | * Copyright 2004 (C) MontaVista Software, Inc. | ||
9 | * | ||
10 | * Based on work Copyright (C) 2002-2003 Intel Corporation | ||
11 | * | ||
12 | * This file is licensed under the terms of the GNU General Public | ||
13 | * License version 2. This program is licensed "as is" without any | ||
14 | * warranty of any kind, whether express or implied. | ||
15 | */ | ||
16 | |||
17 | #include <linux/config.h> | ||
18 | #include <linux/kernel.h> | ||
19 | #include <linux/init.h> | ||
20 | #include <linux/spinlock.h> | ||
21 | #include <linux/sched.h> | ||
22 | #include <linux/interrupt.h> | ||
23 | #include <linux/serial.h> | ||
24 | #include <linux/tty.h> | ||
25 | #include <linux/bitops.h> | ||
26 | #include <linux/serial_core.h> | ||
27 | #include <linux/mm.h> | ||
28 | |||
29 | #include <asm/types.h> | ||
30 | #include <asm/setup.h> | ||
31 | #include <asm/memory.h> | ||
32 | #include <asm/hardware.h> | ||
33 | #include <asm/mach-types.h> | ||
34 | #include <asm/irq.h> | ||
35 | #include <asm/system.h> | ||
36 | #include <asm/tlbflush.h> | ||
37 | #include <asm/pgtable.h> | ||
38 | |||
39 | #include <asm/mach/map.h> | ||
40 | #include <asm/mach/time.h> | ||
41 | #include <asm/mach/irq.h> | ||
42 | |||
43 | static DEFINE_SPINLOCK(ixp2000_slowport_lock); | ||
44 | static unsigned long ixp2000_slowport_irq_flags; | ||
45 | |||
46 | /************************************************************************* | ||
47 | * Slowport access routines | ||
48 | *************************************************************************/ | ||
49 | void ixp2000_acquire_slowport(struct slowport_cfg *new_cfg, struct slowport_cfg *old_cfg) | ||
50 | { | ||
51 | |||
52 | spin_lock_irqsave(&ixp2000_slowport_lock, ixp2000_slowport_irq_flags); | ||
53 | |||
54 | old_cfg->CCR = *IXP2000_SLOWPORT_CCR; | ||
55 | old_cfg->WTC = *IXP2000_SLOWPORT_WTC2; | ||
56 | old_cfg->RTC = *IXP2000_SLOWPORT_RTC2; | ||
57 | old_cfg->PCR = *IXP2000_SLOWPORT_PCR; | ||
58 | old_cfg->ADC = *IXP2000_SLOWPORT_ADC; | ||
59 | |||
60 | ixp2000_reg_write(IXP2000_SLOWPORT_CCR, new_cfg->CCR); | ||
61 | ixp2000_reg_write(IXP2000_SLOWPORT_WTC2, new_cfg->WTC); | ||
62 | ixp2000_reg_write(IXP2000_SLOWPORT_RTC2, new_cfg->RTC); | ||
63 | ixp2000_reg_write(IXP2000_SLOWPORT_PCR, new_cfg->PCR); | ||
64 | ixp2000_reg_write(IXP2000_SLOWPORT_ADC, new_cfg->ADC); | ||
65 | } | ||
66 | |||
67 | void ixp2000_release_slowport(struct slowport_cfg *old_cfg) | ||
68 | { | ||
69 | ixp2000_reg_write(IXP2000_SLOWPORT_CCR, old_cfg->CCR); | ||
70 | ixp2000_reg_write(IXP2000_SLOWPORT_WTC2, old_cfg->WTC); | ||
71 | ixp2000_reg_write(IXP2000_SLOWPORT_RTC2, old_cfg->RTC); | ||
72 | ixp2000_reg_write(IXP2000_SLOWPORT_PCR, old_cfg->PCR); | ||
73 | ixp2000_reg_write(IXP2000_SLOWPORT_ADC, old_cfg->ADC); | ||
74 | |||
75 | spin_unlock_irqrestore(&ixp2000_slowport_lock, | ||
76 | ixp2000_slowport_irq_flags); | ||
77 | } | ||
78 | |||
79 | /************************************************************************* | ||
80 | * Chip specific mappings shared by all IXP2000 systems | ||
81 | *************************************************************************/ | ||
82 | static struct map_desc ixp2000_io_desc[] __initdata = { | ||
83 | { | ||
84 | .virtual = IXP2000_CAP_VIRT_BASE, | ||
85 | .physical = IXP2000_CAP_PHYS_BASE, | ||
86 | .length = IXP2000_CAP_SIZE, | ||
87 | .type = MT_DEVICE | ||
88 | }, { | ||
89 | .virtual = IXP2000_INTCTL_VIRT_BASE, | ||
90 | .physical = IXP2000_INTCTL_PHYS_BASE, | ||
91 | .length = IXP2000_INTCTL_SIZE, | ||
92 | .type = MT_DEVICE | ||
93 | }, { | ||
94 | .virtual = IXP2000_PCI_CREG_VIRT_BASE, | ||
95 | .physical = IXP2000_PCI_CREG_PHYS_BASE, | ||
96 | .length = IXP2000_PCI_CREG_SIZE, | ||
97 | .type = MT_DEVICE | ||
98 | }, { | ||
99 | .virtual = IXP2000_PCI_CSR_VIRT_BASE, | ||
100 | .physical = IXP2000_PCI_CSR_PHYS_BASE, | ||
101 | .length = IXP2000_PCI_CSR_SIZE, | ||
102 | .type = MT_DEVICE | ||
103 | }, { | ||
104 | .virtual = IXP2000_PCI_IO_VIRT_BASE, | ||
105 | .physical = IXP2000_PCI_IO_PHYS_BASE, | ||
106 | .length = IXP2000_PCI_IO_SIZE, | ||
107 | .type = MT_DEVICE | ||
108 | }, { | ||
109 | .virtual = IXP2000_PCI_CFG0_VIRT_BASE, | ||
110 | .physical = IXP2000_PCI_CFG0_PHYS_BASE, | ||
111 | .length = IXP2000_PCI_CFG0_SIZE, | ||
112 | .type = MT_DEVICE | ||
113 | }, { | ||
114 | .virtual = IXP2000_PCI_CFG1_VIRT_BASE, | ||
115 | .physical = IXP2000_PCI_CFG1_PHYS_BASE, | ||
116 | .length = IXP2000_PCI_CFG1_SIZE, | ||
117 | .type = MT_DEVICE | ||
118 | } | ||
119 | }; | ||
120 | |||
121 | static struct uart_port ixp2000_serial_port = { | ||
122 | .membase = (char *)(IXP2000_UART_VIRT_BASE + 3), | ||
123 | .mapbase = IXP2000_UART_PHYS_BASE + 3, | ||
124 | .irq = IRQ_IXP2000_UART, | ||
125 | .flags = UPF_SKIP_TEST, | ||
126 | .iotype = UPIO_MEM, | ||
127 | .regshift = 2, | ||
128 | .uartclk = 50000000, | ||
129 | .line = 0, | ||
130 | .type = PORT_XSCALE, | ||
131 | .fifosize = 16 | ||
132 | }; | ||
133 | |||
134 | void __init ixp2000_map_io(void) | ||
135 | { | ||
136 | extern unsigned int processor_id; | ||
137 | |||
138 | /* | ||
139 | * On IXP2400 CPUs we need to use MT_IXP2000_DEVICE for | ||
140 | * tweaking the PMDs so XCB=101. On IXP2800s we use the normal | ||
141 | * PMD flags. | ||
142 | */ | ||
143 | if ((processor_id & 0xfffffff0) == 0x69054190) { | ||
144 | int i; | ||
145 | |||
146 | printk(KERN_INFO "Enabling IXP2400 erratum #66 workaround\n"); | ||
147 | |||
148 | for(i=0;i<ARRAY_SIZE(ixp2000_io_desc);i++) | ||
149 | ixp2000_io_desc[i].type = MT_IXP2000_DEVICE; | ||
150 | } | ||
151 | |||
152 | iotable_init(ixp2000_io_desc, ARRAY_SIZE(ixp2000_io_desc)); | ||
153 | early_serial_setup(&ixp2000_serial_port); | ||
154 | |||
155 | /* Set slowport to 8-bit mode. */ | ||
156 | ixp2000_reg_write(IXP2000_SLOWPORT_FRM, 1); | ||
157 | } | ||
158 | |||
159 | /************************************************************************* | ||
160 | * Timer-tick functions for IXP2000 | ||
161 | *************************************************************************/ | ||
162 | static unsigned ticks_per_jiffy; | ||
163 | static unsigned ticks_per_usec; | ||
164 | static unsigned next_jiffy_time; | ||
165 | |||
166 | unsigned long ixp2000_gettimeoffset (void) | ||
167 | { | ||
168 | unsigned long offset; | ||
169 | |||
170 | offset = next_jiffy_time - *IXP2000_T4_CSR; | ||
171 | |||
172 | return offset / ticks_per_usec; | ||
173 | } | ||
174 | |||
175 | static int ixp2000_timer_interrupt(int irq, void *dev_id, struct pt_regs *regs) | ||
176 | { | ||
177 | write_seqlock(&xtime_lock); | ||
178 | |||
179 | /* clear timer 1 */ | ||
180 | ixp2000_reg_write(IXP2000_T1_CLR, 1); | ||
181 | |||
182 | while ((next_jiffy_time - *IXP2000_T4_CSR) > ticks_per_jiffy) { | ||
183 | timer_tick(regs); | ||
184 | next_jiffy_time -= ticks_per_jiffy; | ||
185 | } | ||
186 | |||
187 | write_sequnlock(&xtime_lock); | ||
188 | |||
189 | return IRQ_HANDLED; | ||
190 | } | ||
191 | |||
192 | static struct irqaction ixp2000_timer_irq = { | ||
193 | .name = "IXP2000 Timer Tick", | ||
194 | .flags = SA_INTERRUPT, | ||
195 | .handler = ixp2000_timer_interrupt | ||
196 | }; | ||
197 | |||
198 | void __init ixp2000_init_time(unsigned long tick_rate) | ||
199 | { | ||
200 | ixp2000_reg_write(IXP2000_T1_CLR, 0); | ||
201 | ixp2000_reg_write(IXP2000_T4_CLR, 0); | ||
202 | |||
203 | ticks_per_jiffy = (tick_rate + HZ/2) / HZ; | ||
204 | ticks_per_usec = tick_rate / 1000000; | ||
205 | |||
206 | ixp2000_reg_write(IXP2000_T1_CLD, ticks_per_jiffy - 1); | ||
207 | ixp2000_reg_write(IXP2000_T1_CTL, (1 << 7)); | ||
208 | |||
209 | /* | ||
210 | * We use T4 as a monotonic counter to track missed jiffies | ||
211 | */ | ||
212 | ixp2000_reg_write(IXP2000_T4_CLD, -1); | ||
213 | ixp2000_reg_write(IXP2000_T4_CTL, (1 << 7)); | ||
214 | next_jiffy_time = 0xffffffff; | ||
215 | |||
216 | /* register for interrupt */ | ||
217 | setup_irq(IRQ_IXP2000_TIMER1, &ixp2000_timer_irq); | ||
218 | } | ||
219 | |||
220 | /************************************************************************* | ||
221 | * GPIO helpers | ||
222 | *************************************************************************/ | ||
223 | static unsigned long GPIO_IRQ_rising_edge; | ||
224 | static unsigned long GPIO_IRQ_falling_edge; | ||
225 | static unsigned long GPIO_IRQ_level_low; | ||
226 | static unsigned long GPIO_IRQ_level_high; | ||
227 | |||
228 | void gpio_line_config(int line, int style) | ||
229 | { | ||
230 | unsigned long flags; | ||
231 | |||
232 | local_irq_save(flags); | ||
233 | |||
234 | if(style == GPIO_OUT) { | ||
235 | /* if it's an output, it ain't an interrupt anymore */ | ||
236 | ixp2000_reg_write(IXP2000_GPIO_PDSR, (1 << line)); | ||
237 | GPIO_IRQ_falling_edge &= ~(1 << line); | ||
238 | GPIO_IRQ_rising_edge &= ~(1 << line); | ||
239 | GPIO_IRQ_level_low &= ~(1 << line); | ||
240 | GPIO_IRQ_level_high &= ~(1 << line); | ||
241 | ixp2000_reg_write(IXP2000_GPIO_FEDR, GPIO_IRQ_falling_edge); | ||
242 | ixp2000_reg_write(IXP2000_GPIO_REDR, GPIO_IRQ_rising_edge); | ||
243 | ixp2000_reg_write(IXP2000_GPIO_LSHR, GPIO_IRQ_level_high); | ||
244 | ixp2000_reg_write(IXP2000_GPIO_LSLR, GPIO_IRQ_level_low); | ||
245 | irq_desc[line+IRQ_IXP2000_GPIO0].valid = 0; | ||
246 | } else if(style == GPIO_IN) { | ||
247 | ixp2000_reg_write(IXP2000_GPIO_PDCR, (1 << line)); | ||
248 | } | ||
249 | |||
250 | local_irq_restore(flags); | ||
251 | } | ||
252 | |||
253 | |||
254 | /************************************************************************* | ||
255 | * IRQ handling IXP2000 | ||
256 | *************************************************************************/ | ||
257 | static void ixp2000_GPIO_irq_handler(unsigned int irq, struct irqdesc *desc, struct pt_regs *regs) | ||
258 | { | ||
259 | int i; | ||
260 | unsigned long status = *IXP2000_GPIO_INST; | ||
261 | |||
262 | for (i = 0; i <= 7; i++) { | ||
263 | if (status & (1<<i)) { | ||
264 | desc = irq_desc + i + IRQ_IXP2000_GPIO0; | ||
265 | desc->handle(i + IRQ_IXP2000_GPIO0, desc, regs); | ||
266 | } | ||
267 | } | ||
268 | } | ||
269 | |||
270 | static void ixp2000_GPIO_irq_mask_ack(unsigned int irq) | ||
271 | { | ||
272 | ixp2000_reg_write(IXP2000_GPIO_INCR, (1 << (irq - IRQ_IXP2000_GPIO0))); | ||
273 | ixp2000_reg_write(IXP2000_GPIO_INST, (1 << (irq - IRQ_IXP2000_GPIO0))); | ||
274 | } | ||
275 | |||
276 | static void ixp2000_GPIO_irq_mask(unsigned int irq) | ||
277 | { | ||
278 | ixp2000_reg_write(IXP2000_GPIO_INCR, (1 << (irq - IRQ_IXP2000_GPIO0))); | ||
279 | } | ||
280 | |||
281 | static void ixp2000_GPIO_irq_unmask(unsigned int irq) | ||
282 | { | ||
283 | ixp2000_reg_write(IXP2000_GPIO_INSR, (1 << (irq - IRQ_IXP2000_GPIO0))); | ||
284 | } | ||
285 | |||
286 | static struct irqchip ixp2000_GPIO_irq_chip = { | ||
287 | .ack = ixp2000_GPIO_irq_mask_ack, | ||
288 | .mask = ixp2000_GPIO_irq_mask, | ||
289 | .unmask = ixp2000_GPIO_irq_unmask | ||
290 | }; | ||
291 | |||
292 | static void ixp2000_pci_irq_mask(unsigned int irq) | ||
293 | { | ||
294 | unsigned long temp = *IXP2000_PCI_XSCALE_INT_ENABLE; | ||
295 | if (irq == IRQ_IXP2000_PCIA) | ||
296 | ixp2000_reg_write(IXP2000_PCI_XSCALE_INT_ENABLE, (temp & ~(1 << 26))); | ||
297 | else if (irq == IRQ_IXP2000_PCIB) | ||
298 | ixp2000_reg_write(IXP2000_PCI_XSCALE_INT_ENABLE, (temp & ~(1 << 27))); | ||
299 | } | ||
300 | |||
301 | static void ixp2000_pci_irq_unmask(unsigned int irq) | ||
302 | { | ||
303 | unsigned long temp = *IXP2000_PCI_XSCALE_INT_ENABLE; | ||
304 | if (irq == IRQ_IXP2000_PCIA) | ||
305 | ixp2000_reg_write(IXP2000_PCI_XSCALE_INT_ENABLE, (temp | (1 << 26))); | ||
306 | else if (irq == IRQ_IXP2000_PCIB) | ||
307 | ixp2000_reg_write(IXP2000_PCI_XSCALE_INT_ENABLE, (temp | (1 << 27))); | ||
308 | } | ||
309 | |||
310 | static struct irqchip ixp2000_pci_irq_chip = { | ||
311 | .ack = ixp2000_pci_irq_mask, | ||
312 | .mask = ixp2000_pci_irq_mask, | ||
313 | .unmask = ixp2000_pci_irq_unmask | ||
314 | }; | ||
315 | |||
316 | static void ixp2000_irq_mask(unsigned int irq) | ||
317 | { | ||
318 | ixp2000_reg_write(IXP2000_IRQ_ENABLE_CLR, (1 << irq)); | ||
319 | } | ||
320 | |||
321 | static void ixp2000_irq_unmask(unsigned int irq) | ||
322 | { | ||
323 | ixp2000_reg_write(IXP2000_IRQ_ENABLE_SET, (1 << irq)); | ||
324 | } | ||
325 | |||
326 | static struct irqchip ixp2000_irq_chip = { | ||
327 | .ack = ixp2000_irq_mask, | ||
328 | .mask = ixp2000_irq_mask, | ||
329 | .unmask = ixp2000_irq_unmask | ||
330 | }; | ||
331 | |||
332 | void __init ixp2000_init_irq(void) | ||
333 | { | ||
334 | int irq; | ||
335 | |||
336 | /* | ||
337 | * Mask all sources | ||
338 | */ | ||
339 | ixp2000_reg_write(IXP2000_IRQ_ENABLE_CLR, 0xffffffff); | ||
340 | ixp2000_reg_write(IXP2000_FIQ_ENABLE_CLR, 0xffffffff); | ||
341 | |||
342 | /* clear all GPIO edge/level detects */ | ||
343 | ixp2000_reg_write(IXP2000_GPIO_REDR, 0); | ||
344 | ixp2000_reg_write(IXP2000_GPIO_FEDR, 0); | ||
345 | ixp2000_reg_write(IXP2000_GPIO_LSHR, 0); | ||
346 | ixp2000_reg_write(IXP2000_GPIO_LSLR, 0); | ||
347 | ixp2000_reg_write(IXP2000_GPIO_INCR, -1); | ||
348 | |||
349 | /* clear PCI interrupt sources */ | ||
350 | ixp2000_reg_write(IXP2000_PCI_XSCALE_INT_ENABLE, 0); | ||
351 | |||
352 | /* | ||
353 | * Certain bits in the IRQ status register of the | ||
354 | * IXP2000 are reserved. Instead of trying to map | ||
355 | * things non 1:1 from bit position to IRQ number, | ||
356 | * we mark the reserved IRQs as invalid. This makes | ||
357 | * our mask/unmask code much simpler. | ||
358 | */ | ||
359 | for (irq = IRQ_IXP2000_SOFT_INT; irq <= IRQ_IXP2000_THDB3; irq++) { | ||
360 | if((1 << irq) & IXP2000_VALID_IRQ_MASK) { | ||
361 | set_irq_chip(irq, &ixp2000_irq_chip); | ||
362 | set_irq_handler(irq, do_level_IRQ); | ||
363 | set_irq_flags(irq, IRQF_VALID); | ||
364 | } else set_irq_flags(irq, 0); | ||
365 | } | ||
366 | |||
367 | /* | ||
368 | * GPIO IRQs are invalid until someone sets the interrupt mode | ||
369 | * by calling gpio_line_set(); | ||
370 | */ | ||
371 | for (irq = IRQ_IXP2000_GPIO0; irq <= IRQ_IXP2000_GPIO7; irq++) { | ||
372 | set_irq_chip(irq, &ixp2000_GPIO_irq_chip); | ||
373 | set_irq_handler(irq, do_level_IRQ); | ||
374 | set_irq_flags(irq, 0); | ||
375 | } | ||
376 | set_irq_chained_handler(IRQ_IXP2000_GPIO, ixp2000_GPIO_irq_handler); | ||
377 | |||
378 | /* | ||
379 | * Enable PCI irqs. The actual PCI[AB] decoding is done in | ||
380 | * entry-macro.S, so we don't need a chained handler for the | ||
381 | * PCI interrupt source. | ||
382 | */ | ||
383 | ixp2000_reg_write(IXP2000_IRQ_ENABLE_SET, (1 << IRQ_IXP2000_PCI)); | ||
384 | for (irq = IRQ_IXP2000_PCIA; irq <= IRQ_IXP2000_PCIB; irq++) { | ||
385 | set_irq_chip(irq, &ixp2000_pci_irq_chip); | ||
386 | set_irq_handler(irq, do_level_IRQ); | ||
387 | set_irq_flags(irq, IRQF_VALID); | ||
388 | } | ||
389 | } | ||
390 | |||