ce303ec74a7b89dc7546c40d88341a10bce884dd
[openwrt/openwrt.git] / target / linux / adm5120 / files-2.6.26 / arch / mips / adm5120 / common / irq.c
1 /*
2 * ADM5120 specific interrupt handlers
3 *
4 * Copyright (C) 2007-2008 Gabor Juhos <juhosg@openwrt.org>
5 *
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License version 2 as published
8 * by the Free Software Foundation.
9 *
10 */
11
12 #include <linux/init.h>
13 #include <linux/kernel.h>
14 #include <linux/version.h>
15 #include <linux/interrupt.h>
16 #include <linux/ioport.h>
17 #include <linux/io.h>
18
19 #include <asm/irq.h>
20 #include <asm/irq_cpu.h>
21 #include <asm/mipsregs.h>
22 #include <asm/bitops.h>
23
24 #include <asm/mach-adm5120/adm5120_defs.h>
25 #include <asm/mach-adm5120/adm5120_irq.h>
26
27 static void adm5120_intc_irq_unmask(unsigned int irq);
28 static void adm5120_intc_irq_mask(unsigned int irq);
29 static int adm5120_intc_irq_set_type(unsigned int irq, unsigned int flow_type);
30
31 static inline void intc_write_reg(unsigned int reg, u32 val)
32 {
33 void __iomem *base = (void __iomem *)KSEG1ADDR(ADM5120_INTC_BASE);
34
35 __raw_writel(val, base + reg);
36 }
37
38 static inline u32 intc_read_reg(unsigned int reg)
39 {
40 void __iomem *base = (void __iomem *)KSEG1ADDR(ADM5120_INTC_BASE);
41
42 return __raw_readl(base + reg);
43 }
44
45 static struct irq_chip adm5120_intc_irq_chip = {
46 .name = "INTC",
47 .unmask = adm5120_intc_irq_unmask,
48 .mask = adm5120_intc_irq_mask,
49 .mask_ack = adm5120_intc_irq_mask,
50 .set_type = adm5120_intc_irq_set_type
51 };
52
53 static struct irqaction adm5120_intc_irq_action = {
54 .handler = no_action,
55 .name = "cascade [INTC]"
56 };
57
58 static void adm5120_intc_irq_unmask(unsigned int irq)
59 {
60 irq -= ADM5120_INTC_IRQ_BASE;
61 intc_write_reg(INTC_REG_IRQ_ENABLE, 1 << irq);
62 }
63
64 static void adm5120_intc_irq_mask(unsigned int irq)
65 {
66 irq -= ADM5120_INTC_IRQ_BASE;
67 intc_write_reg(INTC_REG_IRQ_DISABLE, 1 << irq);
68 }
69
70 static int adm5120_intc_irq_set_type(unsigned int irq, unsigned int flow_type)
71 {
72 unsigned int sense;
73 unsigned long mode;
74 int err = 0;
75
76 sense = flow_type & (IRQ_TYPE_SENSE_MASK);
77 switch (sense) {
78 case IRQ_TYPE_NONE:
79 case IRQ_TYPE_LEVEL_HIGH:
80 break;
81 case IRQ_TYPE_LEVEL_LOW:
82 switch (irq) {
83 case ADM5120_IRQ_GPIO2:
84 case ADM5120_IRQ_GPIO4:
85 break;
86 default:
87 err = -EINVAL;
88 break;
89 }
90 break;
91 default:
92 err = -EINVAL;
93 break;
94 }
95
96 if (err)
97 return err;
98
99 switch (irq) {
100 case ADM5120_IRQ_GPIO2:
101 case ADM5120_IRQ_GPIO4:
102 mode = intc_read_reg(INTC_REG_INT_MODE);
103 if (sense == IRQ_TYPE_LEVEL_LOW)
104 mode |= (1 << (irq - ADM5120_INTC_IRQ_BASE));
105 else
106 mode &= ~(1 << (irq - ADM5120_INTC_IRQ_BASE));
107
108 intc_write_reg(INTC_REG_INT_MODE, mode);
109 /* fallthrough */
110 default:
111 irq_desc[irq].status &= ~IRQ_TYPE_SENSE_MASK;
112 irq_desc[irq].status |= sense;
113 break;
114 }
115
116 return 0;
117 }
118
119 static void adm5120_intc_irq_dispatch(void)
120 {
121 unsigned long status;
122 int irq;
123
124 /* dispatch only one IRQ at a time */
125 status = intc_read_reg(INTC_REG_IRQ_STATUS) & INTC_INT_ALL;
126
127 if (status) {
128 irq = ADM5120_INTC_IRQ_BASE + fls(status) - 1;
129 do_IRQ(irq);
130 } else
131 spurious_interrupt();
132 }
133
134 asmlinkage void plat_irq_dispatch(void)
135 {
136 unsigned long pending;
137
138 pending = read_c0_status() & read_c0_cause() & ST0_IM;
139
140 if (pending & STATUSF_IP7)
141 do_IRQ(ADM5120_IRQ_COUNTER);
142 else if (pending & STATUSF_IP2)
143 adm5120_intc_irq_dispatch();
144 else
145 spurious_interrupt();
146 }
147
148 #define INTC_IRQ_STATUS (IRQ_LEVEL | IRQ_TYPE_LEVEL_HIGH | IRQ_DISABLED)
149 static void __init adm5120_intc_irq_init(void)
150 {
151 int i;
152
153 /* disable all interrupts */
154 intc_write_reg(INTC_REG_IRQ_DISABLE, INTC_INT_ALL);
155
156 /* setup all interrupts to generate IRQ instead of FIQ */
157 intc_write_reg(INTC_REG_INT_MODE, 0);
158
159 /* set active level for all external interrupts to HIGH */
160 intc_write_reg(INTC_REG_INT_LEVEL, 0);
161
162 /* disable usage of the TEST_SOURCE register */
163 intc_write_reg(INTC_REG_IRQ_SOURCE_SELECT, 0);
164
165 for (i = ADM5120_INTC_IRQ_BASE;
166 i <= ADM5120_INTC_IRQ_BASE + INTC_IRQ_LAST;
167 i++) {
168 irq_desc[i].status = INTC_IRQ_STATUS;
169 set_irq_chip_and_handler(i, &adm5120_intc_irq_chip,
170 handle_level_irq);
171 }
172
173 setup_irq(ADM5120_IRQ_INTC, &adm5120_intc_irq_action);
174 }
175
176 void __init arch_init_irq(void) {
177 mips_cpu_irq_init();
178 adm5120_intc_irq_init();
179 }