]> git.proxmox.com Git - mirror_ubuntu-jammy-kernel.git/blame - arch/mips/bcm63xx/irq.c
Merge branch 'for-linus-1' of git://git.infradead.org/mtd-2.6
[mirror_ubuntu-jammy-kernel.git] / arch / mips / bcm63xx / irq.c
CommitLineData
e7300d04
MB
1/*
2 * This file is subject to the terms and conditions of the GNU General Public
3 * License. See the file "COPYING" in the main directory of this archive
4 * for more details.
5 *
6 * Copyright (C) 2008 Maxime Bizon <mbizon@freebox.fr>
7 * Copyright (C) 2008 Nicolas Schichan <nschichan@freebox.fr>
8 */
9
10#include <linux/kernel.h>
11#include <linux/init.h>
12#include <linux/interrupt.h>
13#include <linux/module.h>
ca4d3e67 14#include <linux/irq.h>
e7300d04
MB
15#include <asm/irq_cpu.h>
16#include <asm/mipsregs.h>
17#include <bcm63xx_cpu.h>
18#include <bcm63xx_regs.h>
19#include <bcm63xx_io.h>
20#include <bcm63xx_irq.h>
21
22/*
23 * dispatch internal devices IRQ (uart, enet, watchdog, ...). do not
24 * prioritize any interrupt relatively to another. the static counter
25 * will resume the loop where it ended the last time we left this
26 * function.
27 */
28static void bcm63xx_irq_dispatch_internal(void)
29{
30 u32 pending;
31 static int i;
32
33 pending = bcm_perf_readl(PERF_IRQMASK_REG) &
34 bcm_perf_readl(PERF_IRQSTAT_REG);
35
36 if (!pending)
37 return ;
38
39 while (1) {
40 int to_call = i;
41
42 i = (i + 1) & 0x1f;
43 if (pending & (1 << to_call)) {
44 do_IRQ(to_call + IRQ_INTERNAL_BASE);
45 break;
46 }
47 }
48}
49
50asmlinkage void plat_irq_dispatch(void)
51{
52 u32 cause;
53
54 do {
55 cause = read_c0_cause() & read_c0_status() & ST0_IM;
56
57 if (!cause)
58 break;
59
60 if (cause & CAUSEF_IP7)
61 do_IRQ(7);
62 if (cause & CAUSEF_IP2)
63 bcm63xx_irq_dispatch_internal();
64 if (cause & CAUSEF_IP3)
65 do_IRQ(IRQ_EXT_0);
66 if (cause & CAUSEF_IP4)
67 do_IRQ(IRQ_EXT_1);
68 if (cause & CAUSEF_IP5)
69 do_IRQ(IRQ_EXT_2);
70 if (cause & CAUSEF_IP6)
71 do_IRQ(IRQ_EXT_3);
72 } while (1);
73}
74
75/*
76 * internal IRQs operations: only mask/unmask on PERF irq mask
77 * register.
78 */
93f29361 79static inline void bcm63xx_internal_irq_mask(struct irq_data *d)
e7300d04 80{
93f29361 81 unsigned int irq = d->irq - IRQ_INTERNAL_BASE;
e7300d04
MB
82 u32 mask;
83
e7300d04
MB
84 mask = bcm_perf_readl(PERF_IRQMASK_REG);
85 mask &= ~(1 << irq);
86 bcm_perf_writel(mask, PERF_IRQMASK_REG);
87}
88
93f29361 89static void bcm63xx_internal_irq_unmask(struct irq_data *d)
e7300d04 90{
93f29361 91 unsigned int irq = d->irq - IRQ_INTERNAL_BASE;
e7300d04
MB
92 u32 mask;
93
e7300d04
MB
94 mask = bcm_perf_readl(PERF_IRQMASK_REG);
95 mask |= (1 << irq);
96 bcm_perf_writel(mask, PERF_IRQMASK_REG);
97}
98
e7300d04
MB
99/*
100 * external IRQs operations: mask/unmask and clear on PERF external
101 * irq control register.
102 */
93f29361 103static void bcm63xx_external_irq_mask(struct irq_data *d)
e7300d04 104{
93f29361 105 unsigned int irq = d->irq - IRQ_EXT_BASE;
e7300d04
MB
106 u32 reg;
107
e7300d04
MB
108 reg = bcm_perf_readl(PERF_EXTIRQ_CFG_REG);
109 reg &= ~EXTIRQ_CFG_MASK(irq);
110 bcm_perf_writel(reg, PERF_EXTIRQ_CFG_REG);
111}
112
93f29361 113static void bcm63xx_external_irq_unmask(struct irq_data *d)
e7300d04 114{
93f29361 115 unsigned int irq = d->irq - IRQ_EXT_BASE;
e7300d04
MB
116 u32 reg;
117
e7300d04
MB
118 reg = bcm_perf_readl(PERF_EXTIRQ_CFG_REG);
119 reg |= EXTIRQ_CFG_MASK(irq);
120 bcm_perf_writel(reg, PERF_EXTIRQ_CFG_REG);
121}
122
93f29361 123static void bcm63xx_external_irq_clear(struct irq_data *d)
e7300d04 124{
93f29361 125 unsigned int irq = d->irq - IRQ_EXT_BASE;
e7300d04
MB
126 u32 reg;
127
e7300d04
MB
128 reg = bcm_perf_readl(PERF_EXTIRQ_CFG_REG);
129 reg |= EXTIRQ_CFG_CLEAR(irq);
130 bcm_perf_writel(reg, PERF_EXTIRQ_CFG_REG);
131}
132
93f29361 133static unsigned int bcm63xx_external_irq_startup(struct irq_data *d)
e7300d04 134{
93f29361 135 set_c0_status(0x100 << (d->irq - IRQ_MIPS_BASE));
e7300d04 136 irq_enable_hazard();
93f29361 137 bcm63xx_external_irq_unmask(d);
e7300d04
MB
138 return 0;
139}
140
93f29361 141static void bcm63xx_external_irq_shutdown(struct irq_data *d)
e7300d04 142{
93f29361
TG
143 bcm63xx_external_irq_mask(d);
144 clear_c0_status(0x100 << (d->irq - IRQ_MIPS_BASE));
e7300d04
MB
145 irq_disable_hazard();
146}
147
93f29361 148static int bcm63xx_external_irq_set_type(struct irq_data *d,
e7300d04
MB
149 unsigned int flow_type)
150{
93f29361 151 unsigned int irq = d->irq - IRQ_EXT_BASE;
e7300d04 152 u32 reg;
e7300d04
MB
153
154 flow_type &= IRQ_TYPE_SENSE_MASK;
155
156 if (flow_type == IRQ_TYPE_NONE)
157 flow_type = IRQ_TYPE_LEVEL_LOW;
158
159 reg = bcm_perf_readl(PERF_EXTIRQ_CFG_REG);
160 switch (flow_type) {
161 case IRQ_TYPE_EDGE_BOTH:
162 reg &= ~EXTIRQ_CFG_LEVELSENSE(irq);
163 reg |= EXTIRQ_CFG_BOTHEDGE(irq);
164 break;
165
166 case IRQ_TYPE_EDGE_RISING:
167 reg &= ~EXTIRQ_CFG_LEVELSENSE(irq);
168 reg |= EXTIRQ_CFG_SENSE(irq);
169 reg &= ~EXTIRQ_CFG_BOTHEDGE(irq);
170 break;
171
172 case IRQ_TYPE_EDGE_FALLING:
173 reg &= ~EXTIRQ_CFG_LEVELSENSE(irq);
174 reg &= ~EXTIRQ_CFG_SENSE(irq);
175 reg &= ~EXTIRQ_CFG_BOTHEDGE(irq);
176 break;
177
178 case IRQ_TYPE_LEVEL_HIGH:
179 reg |= EXTIRQ_CFG_LEVELSENSE(irq);
180 reg |= EXTIRQ_CFG_SENSE(irq);
181 break;
182
183 case IRQ_TYPE_LEVEL_LOW:
184 reg |= EXTIRQ_CFG_LEVELSENSE(irq);
185 reg &= ~EXTIRQ_CFG_SENSE(irq);
186 break;
187
188 default:
189 printk(KERN_ERR "bogus flow type combination given !\n");
190 return -EINVAL;
191 }
192 bcm_perf_writel(reg, PERF_EXTIRQ_CFG_REG);
193
93f29361
TG
194 irqd_set_trigger_type(d, flow_type);
195 if (flow_type & (IRQ_TYPE_LEVEL_LOW | IRQ_TYPE_LEVEL_HIGH))
196 __irq_set_handler_locked(d->irq, handle_level_irq);
197 else
198 __irq_set_handler_locked(d->irq, handle_edge_irq);
e7300d04 199
93f29361 200 return IRQ_SET_MASK_OK_NOCOPY;
e7300d04
MB
201}
202
203static struct irq_chip bcm63xx_internal_irq_chip = {
204 .name = "bcm63xx_ipic",
93f29361
TG
205 .irq_mask = bcm63xx_internal_irq_mask,
206 .irq_unmask = bcm63xx_internal_irq_unmask,
e7300d04
MB
207};
208
209static struct irq_chip bcm63xx_external_irq_chip = {
210 .name = "bcm63xx_epic",
93f29361
TG
211 .irq_startup = bcm63xx_external_irq_startup,
212 .irq_shutdown = bcm63xx_external_irq_shutdown,
e7300d04 213
93f29361 214 .irq_ack = bcm63xx_external_irq_clear,
e7300d04 215
93f29361
TG
216 .irq_mask = bcm63xx_external_irq_mask,
217 .irq_unmask = bcm63xx_external_irq_unmask,
e7300d04 218
93f29361 219 .irq_set_type = bcm63xx_external_irq_set_type,
e7300d04
MB
220};
221
222static struct irqaction cpu_ip2_cascade_action = {
223 .handler = no_action,
224 .name = "cascade_ip2",
225};
226
227void __init arch_init_irq(void)
228{
229 int i;
230
231 mips_cpu_irq_init();
232 for (i = IRQ_INTERNAL_BASE; i < NR_IRQS; ++i)
233 set_irq_chip_and_handler(i, &bcm63xx_internal_irq_chip,
234 handle_level_irq);
235
236 for (i = IRQ_EXT_BASE; i < IRQ_EXT_BASE + 4; ++i)
237 set_irq_chip_and_handler(i, &bcm63xx_external_irq_chip,
238 handle_edge_irq);
239
240 setup_irq(IRQ_MIPS_BASE + 2, &cpu_ip2_cascade_action);
241}