aboutsummaryrefslogtreecommitdiffstats
path: root/arch/arm/mach-ixp23xx/core.c
diff options
context:
space:
mode:
authorLennert Buytenhek <buytenh@wantstofly.org>2006-03-28 15:18:54 -0500
committerRussell King <rmk+kernel@arm.linux.org.uk>2006-03-28 15:18:54 -0500
commitc4713074375c61f939310b04e92090afe29810dc (patch)
treef09441eac8bd29a4a48f0360e1207d6def04b5fd /arch/arm/mach-ixp23xx/core.c
parente9937d4b0a9382c4c78411d1c53e62be396ee9a9 (diff)
[ARM] 3388/1: ixp23xx: add core ixp23xx support
Patch from Lennert Buytenhek This patch adds support for the Intel ixp23xx series of CPUs. The ixp23xx is an XSC3 based CPU with 512K of L2 cache, a 64bit 66MHz PCI interface, two DDR RAM interfaces, QDR RAM interfaces, two gigabit MACs, two 10/100 MACs, expansion bus, four microengines, a Media and Switch Fabric unit almost identical to the one on the ixp2400, two xscale (8250ish) UARTs and a bunch of other stuff. This patch adds the core ixp23xx support code, and support for the ADI Engineering Roadrunner, Intel IXDP2351, and IP Fabrics Double Espresso platforms. Signed-off-by: Deepak Saxena <dsaxena@plexity.net> Signed-off-by: Lennert Buytenhek <buytenh@wantstofly.org> Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
Diffstat (limited to 'arch/arm/mach-ixp23xx/core.c')
-rw-r--r--arch/arm/mach-ixp23xx/core.c431
1 files changed, 431 insertions, 0 deletions
diff --git a/arch/arm/mach-ixp23xx/core.c b/arch/arm/mach-ixp23xx/core.c
new file mode 100644
index 00000000000..092ee12ced4
--- /dev/null
+++ b/arch/arm/mach-ixp23xx/core.c
@@ -0,0 +1,431 @@
1/*
2 * arch/arm/mach-ixp23xx/core.c
3 *
4 * Core routines for IXP23xx chips
5 *
6 * Author: Deepak Saxena <dsaxena@plexity.net>
7 *
8 * Copyright 2005 (c) MontaVista Software, Inc.
9 *
10 * Based on 2.4 code Copyright 2004 (c) 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.h>
27#include <linux/serial_8250.h>
28#include <linux/serial_core.h>
29#include <linux/device.h>
30#include <linux/mm.h>
31#include <linux/time.h>
32#include <linux/timex.h>
33
34#include <asm/types.h>
35#include <asm/setup.h>
36#include <asm/memory.h>
37#include <asm/hardware.h>
38#include <asm/mach-types.h>
39#include <asm/irq.h>
40#include <asm/system.h>
41#include <asm/tlbflush.h>
42#include <asm/pgtable.h>
43
44#include <asm/mach/map.h>
45#include <asm/mach/time.h>
46#include <asm/mach/irq.h>
47#include <asm/mach/arch.h>
48
49
50/*************************************************************************
51 * Chip specific mappings shared by all IXP23xx systems
52 *************************************************************************/
53static struct map_desc ixp23xx_io_desc[] __initdata = {
54 { /* XSI-CPP CSRs */
55 .virtual = IXP23XX_XSI2CPP_CSR_VIRT,
56 .pfn = __phys_to_pfn(IXP23XX_XSI2CPP_CSR_PHYS),
57 .length = IXP23XX_XSI2CPP_CSR_SIZE,
58 .type = MT_DEVICE,
59 }, { /* Expansion Bus Config */
60 .virtual = IXP23XX_EXP_CFG_VIRT,
61 .pfn = __phys_to_pfn(IXP23XX_EXP_CFG_PHYS),
62 .length = IXP23XX_EXP_CFG_SIZE,
63 .type = MT_DEVICE,
64 }, { /* UART, Interrupt ctrl, GPIO, timers, NPEs, MACS,.... */
65 .virtual = IXP23XX_PERIPHERAL_VIRT,
66 .pfn = __phys_to_pfn(IXP23XX_PERIPHERAL_PHYS),
67 .length = IXP23XX_PERIPHERAL_SIZE,
68 .type = MT_DEVICE,
69 }, { /* CAP CSRs */
70 .virtual = IXP23XX_CAP_CSR_VIRT,
71 .pfn = __phys_to_pfn(IXP23XX_CAP_CSR_PHYS),
72 .length = IXP23XX_CAP_CSR_SIZE,
73 .type = MT_DEVICE,
74 }, { /* MSF CSRs */
75 .virtual = IXP23XX_MSF_CSR_VIRT,
76 .pfn = __phys_to_pfn(IXP23XX_MSF_CSR_PHYS),
77 .length = IXP23XX_MSF_CSR_SIZE,
78 .type = MT_DEVICE,
79 }, { /* PCI I/O Space */
80 .virtual = IXP23XX_PCI_IO_VIRT,
81 .pfn = __phys_to_pfn(IXP23XX_PCI_IO_PHYS),
82 .length = IXP23XX_PCI_IO_SIZE,
83 .type = MT_DEVICE,
84 }, { /* PCI Config Space */
85 .virtual = IXP23XX_PCI_CFG_VIRT,
86 .pfn = __phys_to_pfn(IXP23XX_PCI_CFG_PHYS),
87 .length = IXP23XX_PCI_CFG_SIZE,
88 .type = MT_DEVICE,
89 }, { /* PCI local CFG CSRs */
90 .virtual = IXP23XX_PCI_CREG_VIRT,
91 .pfn = __phys_to_pfn(IXP23XX_PCI_CREG_PHYS),
92 .length = IXP23XX_PCI_CREG_SIZE,
93 .type = MT_DEVICE,
94 }, { /* PCI MEM Space */
95 .virtual = IXP23XX_PCI_MEM_VIRT,
96 .pfn = __phys_to_pfn(IXP23XX_PCI_MEM_PHYS),
97 .length = IXP23XX_PCI_MEM_SIZE,
98 .type = MT_DEVICE,
99 }
100};
101
102void __init ixp23xx_map_io(void)
103{
104 iotable_init(ixp23xx_io_desc, ARRAY_SIZE(ixp23xx_io_desc));
105}
106
107
108/***************************************************************************
109 * IXP23xx Interrupt Handling
110 ***************************************************************************/
111enum ixp23xx_irq_type {
112 IXP23XX_IRQ_LEVEL, IXP23XX_IRQ_EDGE
113};
114
115static void ixp23xx_config_irq(unsigned int, enum ixp23xx_irq_type);
116
117static int ixp23xx_irq_set_type(unsigned int irq, unsigned int type)
118{
119 int line = irq - IRQ_IXP23XX_GPIO6 + 6;
120 u32 int_style;
121 enum ixp23xx_irq_type irq_type;
122 volatile u32 *int_reg;
123
124 /*
125 * Only GPIOs 6-15 are wired to interrupts on IXP23xx
126 */
127 if (line < 6 || line > 15)
128 return -EINVAL;
129
130 switch (type) {
131 case IRQT_BOTHEDGE:
132 int_style = IXP23XX_GPIO_STYLE_TRANSITIONAL;
133 irq_type = IXP23XX_IRQ_EDGE;
134 break;
135 case IRQT_RISING:
136 int_style = IXP23XX_GPIO_STYLE_RISING_EDGE;
137 irq_type = IXP23XX_IRQ_EDGE;
138 break;
139 case IRQT_FALLING:
140 int_style = IXP23XX_GPIO_STYLE_FALLING_EDGE;
141 irq_type = IXP23XX_IRQ_EDGE;
142 break;
143 case IRQT_HIGH:
144 int_style = IXP23XX_GPIO_STYLE_ACTIVE_HIGH;
145 irq_type = IXP23XX_IRQ_LEVEL;
146 break;
147 case IRQT_LOW:
148 int_style = IXP23XX_GPIO_STYLE_ACTIVE_LOW;
149 irq_type = IXP23XX_IRQ_LEVEL;
150 break;
151 default:
152 return -EINVAL;
153 }
154
155 ixp23xx_config_irq(irq, irq_type);
156
157 if (line >= 8) { /* pins 8-15 */
158 line -= 8;
159 int_reg = (volatile u32 *)IXP23XX_GPIO_GPIT2R;
160 } else { /* pins 0-7 */
161 int_reg = (volatile u32 *)IXP23XX_GPIO_GPIT1R;
162 }
163
164 /*
165 * Clear pending interrupts
166 */
167 *IXP23XX_GPIO_GPISR = (1 << line);
168
169 /* Clear the style for the appropriate pin */
170 *int_reg &= ~(IXP23XX_GPIO_STYLE_MASK <<
171 (line * IXP23XX_GPIO_STYLE_SIZE));
172
173 /* Set the new style */
174 *int_reg |= (int_style << (line * IXP23XX_GPIO_STYLE_SIZE));
175
176 return 0;
177}
178
179static void ixp23xx_irq_mask(unsigned int irq)
180{
181 volatile unsigned long *intr_reg = IXP23XX_INTR_EN1 + (irq / 32);
182
183 *intr_reg &= ~(1 << (irq % 32));
184}
185
186static void ixp23xx_irq_ack(unsigned int irq)
187{
188 int line = irq - IRQ_IXP23XX_GPIO6 + 6;
189
190 if ((line < 6) || (line > 15))
191 return;
192
193 *IXP23XX_GPIO_GPISR = (1 << line);
194}
195
196/*
197 * Level triggered interrupts on GPIO lines can only be cleared when the
198 * interrupt condition disappears.
199 */
200static void ixp23xx_irq_level_unmask(unsigned int irq)
201{
202 volatile unsigned long *intr_reg = IXP23XX_INTR_EN1 + (irq / 32);
203
204 ixp23xx_irq_ack(irq);
205
206 *intr_reg |= (1 << (irq % 32));
207}
208
209static void ixp23xx_irq_edge_unmask(unsigned int irq)
210{
211 volatile unsigned long *intr_reg = IXP23XX_INTR_EN1 + (irq / 32);
212
213 *intr_reg |= (1 << (irq % 32));
214}
215
216static struct irqchip ixp23xx_irq_level_chip = {
217 .ack = ixp23xx_irq_mask,
218 .mask = ixp23xx_irq_mask,
219 .unmask = ixp23xx_irq_level_unmask,
220 .set_type = ixp23xx_irq_set_type
221};
222
223static struct irqchip ixp23xx_irq_edge_chip = {
224 .ack = ixp23xx_irq_ack,
225 .mask = ixp23xx_irq_mask,
226 .unmask = ixp23xx_irq_edge_unmask,
227 .set_type = ixp23xx_irq_set_type
228};
229
230static void ixp23xx_pci_irq_mask(unsigned int irq)
231{
232 *IXP23XX_PCI_XSCALE_INT_ENABLE &= ~(1 << (IRQ_IXP23XX_INTA + 27 - irq));
233}
234
235static void ixp23xx_pci_irq_unmask(unsigned int irq)
236{
237 *IXP23XX_PCI_XSCALE_INT_ENABLE |= (1 << (IRQ_IXP23XX_INTA + 27 - irq));
238}
239
240/*
241 * TODO: Should this just be done at ASM level?
242 */
243static void pci_handler(unsigned int irq, struct irqdesc *desc, struct pt_regs *regs)
244{
245 u32 pci_interrupt;
246 unsigned int irqno;
247 struct irqdesc *int_desc;
248
249 pci_interrupt = *IXP23XX_PCI_XSCALE_INT_STATUS;
250
251 desc->chip->ack(irq);
252
253 /* See which PCI_INTA, or PCI_INTB interrupted */
254 if (pci_interrupt & (1 << 26)) {
255 irqno = IRQ_IXP23XX_INTB;
256 } else if (pci_interrupt & (1 << 27)) {
257 irqno = IRQ_IXP23XX_INTA;
258 } else {
259 BUG();
260 }
261
262 int_desc = irq_desc + irqno;
263 int_desc->handle(irqno, int_desc, regs);
264
265 desc->chip->unmask(irq);
266}
267
268static struct irqchip ixp23xx_pci_irq_chip = {
269 .ack = ixp23xx_pci_irq_mask,
270 .mask = ixp23xx_pci_irq_mask,
271 .unmask = ixp23xx_pci_irq_unmask
272};
273
274static void ixp23xx_config_irq(unsigned int irq, enum ixp23xx_irq_type type)
275{
276 switch (type) {
277 case IXP23XX_IRQ_LEVEL:
278 set_irq_chip(irq, &ixp23xx_irq_level_chip);
279 set_irq_handler(irq, do_level_IRQ);
280 break;
281 case IXP23XX_IRQ_EDGE:
282 set_irq_chip(irq, &ixp23xx_irq_edge_chip);
283 set_irq_handler(irq, do_edge_IRQ);
284 break;
285 }
286 set_irq_flags(irq, IRQF_VALID);
287}
288
289void __init ixp23xx_init_irq(void)
290{
291 int irq;
292
293 /* Route everything to IRQ */
294 *IXP23XX_INTR_SEL1 = 0x0;
295 *IXP23XX_INTR_SEL2 = 0x0;
296 *IXP23XX_INTR_SEL3 = 0x0;
297 *IXP23XX_INTR_SEL4 = 0x0;
298
299 /* Mask all sources */
300 *IXP23XX_INTR_EN1 = 0x0;
301 *IXP23XX_INTR_EN2 = 0x0;
302 *IXP23XX_INTR_EN3 = 0x0;
303 *IXP23XX_INTR_EN4 = 0x0;
304
305 /*
306 * Configure all IRQs for level-sensitive operation
307 */
308 for (irq = 0; irq <= NUM_IXP23XX_RAW_IRQS; irq++) {
309 ixp23xx_config_irq(irq, IXP23XX_IRQ_LEVEL);
310 }
311
312 for (irq = IRQ_IXP23XX_INTA; irq <= IRQ_IXP23XX_INTB; irq++) {
313 set_irq_chip(irq, &ixp23xx_pci_irq_chip);
314 set_irq_handler(irq, do_level_IRQ);
315 set_irq_flags(irq, IRQF_VALID);
316 }
317
318 set_irq_chained_handler(IRQ_IXP23XX_PCI_INT_RPH, pci_handler);
319}
320
321
322/*************************************************************************
323 * Timer-tick functions for IXP23xx
324 *************************************************************************/
325#define CLOCK_TICKS_PER_USEC CLOCK_TICK_RATE / (USEC_PER_SEC)
326
327static unsigned long next_jiffy_time;
328
329static unsigned long
330ixp23xx_gettimeoffset(void)
331{
332 unsigned long elapsed;
333
334 elapsed = *IXP23XX_TIMER_CONT - (next_jiffy_time - LATCH);
335
336 return elapsed / CLOCK_TICKS_PER_USEC;
337}
338
339static irqreturn_t
340ixp23xx_timer_interrupt(int irq, void *dev_id, struct pt_regs *regs)
341{
342 /* Clear Pending Interrupt by writing '1' to it */
343 *IXP23XX_TIMER_STATUS = IXP23XX_TIMER1_INT_PEND;
344 while ((*IXP23XX_TIMER_CONT - next_jiffy_time) > LATCH) {
345 timer_tick(regs);
346 next_jiffy_time += LATCH;
347 }
348
349 return IRQ_HANDLED;
350}
351
352static struct irqaction ixp23xx_timer_irq = {
353 .name = "IXP23xx Timer Tick",
354 .handler = ixp23xx_timer_interrupt,
355 .flags = SA_INTERRUPT | SA_TIMER,
356};
357
358void __init ixp23xx_init_timer(void)
359{
360 /* Clear Pending Interrupt by writing '1' to it */
361 *IXP23XX_TIMER_STATUS = IXP23XX_TIMER1_INT_PEND;
362
363 /* Setup the Timer counter value */
364 *IXP23XX_TIMER1_RELOAD =
365 (LATCH & ~IXP23XX_TIMER_RELOAD_MASK) | IXP23XX_TIMER_ENABLE;
366
367 *IXP23XX_TIMER_CONT = 0;
368 next_jiffy_time = LATCH;
369
370 /* Connect the interrupt handler and enable the interrupt */
371 setup_irq(IRQ_IXP23XX_TIMER1, &ixp23xx_timer_irq);
372}
373
374struct sys_timer ixp23xx_timer = {
375 .init = ixp23xx_init_timer,
376 .offset = ixp23xx_gettimeoffset,
377};
378
379
380/*************************************************************************
381 * IXP23xx Platform Initializaion
382 *************************************************************************/
383static struct resource ixp23xx_uart_resources[] = {
384 {
385 .start = IXP23XX_UART1_PHYS,
386 .end = IXP23XX_UART1_PHYS + 0x0fff,
387 .flags = IORESOURCE_MEM
388 }, {
389 .start = IXP23XX_UART2_PHYS,
390 .end = IXP23XX_UART2_PHYS + 0x0fff,
391 .flags = IORESOURCE_MEM
392 }
393};
394
395static struct plat_serial8250_port ixp23xx_uart_data[] = {
396 {
397 .mapbase = IXP23XX_UART1_PHYS,
398 .membase = (char *)(IXP23XX_UART1_VIRT + 3),
399 .irq = IRQ_IXP23XX_UART1,
400 .flags = UPF_BOOT_AUTOCONF | UPF_SKIP_TEST,
401 .iotype = UPIO_MEM,
402 .regshift = 2,
403 .uartclk = IXP23XX_UART_XTAL,
404 }, {
405 .mapbase = IXP23XX_UART2_PHYS,
406 .membase = (char *)(IXP23XX_UART2_VIRT + 3),
407 .irq = IRQ_IXP23XX_UART2,
408 .flags = UPF_BOOT_AUTOCONF | UPF_SKIP_TEST,
409 .iotype = UPIO_MEM,
410 .regshift = 2,
411 .uartclk = IXP23XX_UART_XTAL,
412 },
413 { },
414};
415
416static struct platform_device ixp23xx_uart = {
417 .name = "serial8250",
418 .id = 0,
419 .dev.platform_data = ixp23xx_uart_data,
420 .num_resources = 2,
421 .resource = ixp23xx_uart_resources,
422};
423
424static struct platform_device *ixp23xx_devices[] __initdata = {
425 &ixp23xx_uart,
426};
427
428void __init ixp23xx_sys_init(void)
429{
430 platform_add_devices(ixp23xx_devices, ARRAY_SIZE(ixp23xx_devices));
431}