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
6 * Copyright (C) 2008 Maxime Bizon <mbizon@freebox.fr>
7 * Copyright (C) 2008 Nicolas Schichan <nschichan@freebox.fr>
10 #include <linux/kernel.h>
11 #include <linux/init.h>
12 #include <linux/interrupt.h>
13 #include <linux/module.h>
14 #include <linux/irq.h>
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>
22 static void __dispatch_internal(void) __maybe_unused;
24 #ifndef BCMCPU_RUNTIME_DETECT
25 #ifdef CONFIG_BCM63XX_CPU_6338
26 #define irq_stat_reg PERF_IRQSTAT_6338_REG
27 #define irq_mask_reg PERF_IRQMASK_6338_REG
29 #ifdef CONFIG_BCM63XX_CPU_6345
30 #define irq_stat_reg PERF_IRQSTAT_6345_REG
31 #define irq_mask_reg PERF_IRQMASK_6345_REG
33 #ifdef CONFIG_BCM63XX_CPU_6348
34 #define irq_stat_reg PERF_IRQSTAT_6348_REG
35 #define irq_mask_reg PERF_IRQMASK_6348_REG
37 #ifdef CONFIG_BCM63XX_CPU_6358
38 #define irq_stat_reg PERF_IRQSTAT_6358_REG
39 #define irq_mask_reg PERF_IRQMASK_6358_REG
42 #define dispatch_internal __dispatch_internal
44 #define irq_stat_addr (bcm63xx_regset_address(RSET_PERF) + irq_stat_reg)
45 #define irq_mask_addr (bcm63xx_regset_address(RSET_PERF) + irq_mask_reg)
47 static inline void bcm63xx_init_irq(void)
50 #else /* ! BCMCPU_RUNTIME_DETECT */
52 static u32 irq_stat_addr, irq_mask_addr;
53 static void (*dispatch_internal)(void);
55 static void bcm63xx_init_irq(void)
57 irq_stat_addr = bcm63xx_regset_address(RSET_PERF);
58 irq_mask_addr = bcm63xx_regset_address(RSET_PERF);
60 switch (bcm63xx_get_cpu_id()) {
62 irq_stat_addr += PERF_IRQSTAT_6338_REG;
63 irq_mask_addr += PERF_IRQMASK_6338_REG;
66 irq_stat_addr += PERF_IRQSTAT_6345_REG;
67 irq_mask_addr += PERF_IRQMASK_6345_REG;
70 irq_stat_addr += PERF_IRQSTAT_6348_REG;
71 irq_mask_addr += PERF_IRQMASK_6348_REG;
74 irq_stat_addr += PERF_IRQSTAT_6358_REG;
75 irq_mask_addr += PERF_IRQMASK_6358_REG;
81 dispatch_internal = __dispatch_internal;
83 #endif /* ! BCMCPU_RUNTIME_DETECT */
85 static inline void handle_internal(int intbit)
87 do_IRQ(intbit + IRQ_INTERNAL_BASE);
91 * dispatch internal devices IRQ (uart, enet, watchdog, ...). do not
92 * prioritize any interrupt relatively to another. the static counter
93 * will resume the loop where it ended the last time we left this
96 static void __dispatch_internal(void)
101 pending = bcm_readl(irq_stat_addr) & bcm_readl(irq_mask_addr);
110 if (pending & (1 << to_call)) {
111 handle_internal(to_call);
117 asmlinkage void plat_irq_dispatch(void)
122 cause = read_c0_cause() & read_c0_status() & ST0_IM;
127 if (cause & CAUSEF_IP7)
129 if (cause & CAUSEF_IP2)
131 if (cause & CAUSEF_IP3)
133 if (cause & CAUSEF_IP4)
135 if (cause & CAUSEF_IP5)
137 if (cause & CAUSEF_IP6)
143 * internal IRQs operations: only mask/unmask on PERF irq mask
146 static inline void bcm63xx_internal_irq_mask(struct irq_data *d)
148 unsigned int irq = d->irq - IRQ_INTERNAL_BASE;
151 mask = bcm_readl(irq_mask_addr);
153 bcm_writel(mask, irq_mask_addr);
156 static void bcm63xx_internal_irq_unmask(struct irq_data *d)
158 unsigned int irq = d->irq - IRQ_INTERNAL_BASE;
161 mask = bcm_readl(irq_mask_addr);
163 bcm_writel(mask, irq_mask_addr);
167 * external IRQs operations: mask/unmask and clear on PERF external
168 * irq control register.
170 static void bcm63xx_external_irq_mask(struct irq_data *d)
172 unsigned int irq = d->irq - IRQ_EXT_BASE;
175 reg = bcm_perf_readl(PERF_EXTIRQ_CFG_REG);
176 reg &= ~EXTIRQ_CFG_MASK(irq);
177 bcm_perf_writel(reg, PERF_EXTIRQ_CFG_REG);
180 static void bcm63xx_external_irq_unmask(struct irq_data *d)
182 unsigned int irq = d->irq - IRQ_EXT_BASE;
185 reg = bcm_perf_readl(PERF_EXTIRQ_CFG_REG);
186 reg |= EXTIRQ_CFG_MASK(irq);
187 bcm_perf_writel(reg, PERF_EXTIRQ_CFG_REG);
190 static void bcm63xx_external_irq_clear(struct irq_data *d)
192 unsigned int irq = d->irq - IRQ_EXT_BASE;
195 reg = bcm_perf_readl(PERF_EXTIRQ_CFG_REG);
196 reg |= EXTIRQ_CFG_CLEAR(irq);
197 bcm_perf_writel(reg, PERF_EXTIRQ_CFG_REG);
200 static unsigned int bcm63xx_external_irq_startup(struct irq_data *d)
202 set_c0_status(0x100 << (d->irq - IRQ_MIPS_BASE));
204 bcm63xx_external_irq_unmask(d);
208 static void bcm63xx_external_irq_shutdown(struct irq_data *d)
210 bcm63xx_external_irq_mask(d);
211 clear_c0_status(0x100 << (d->irq - IRQ_MIPS_BASE));
212 irq_disable_hazard();
215 static int bcm63xx_external_irq_set_type(struct irq_data *d,
216 unsigned int flow_type)
218 unsigned int irq = d->irq - IRQ_EXT_BASE;
221 flow_type &= IRQ_TYPE_SENSE_MASK;
223 if (flow_type == IRQ_TYPE_NONE)
224 flow_type = IRQ_TYPE_LEVEL_LOW;
226 reg = bcm_perf_readl(PERF_EXTIRQ_CFG_REG);
228 case IRQ_TYPE_EDGE_BOTH:
229 reg &= ~EXTIRQ_CFG_LEVELSENSE(irq);
230 reg |= EXTIRQ_CFG_BOTHEDGE(irq);
233 case IRQ_TYPE_EDGE_RISING:
234 reg &= ~EXTIRQ_CFG_LEVELSENSE(irq);
235 reg |= EXTIRQ_CFG_SENSE(irq);
236 reg &= ~EXTIRQ_CFG_BOTHEDGE(irq);
239 case IRQ_TYPE_EDGE_FALLING:
240 reg &= ~EXTIRQ_CFG_LEVELSENSE(irq);
241 reg &= ~EXTIRQ_CFG_SENSE(irq);
242 reg &= ~EXTIRQ_CFG_BOTHEDGE(irq);
245 case IRQ_TYPE_LEVEL_HIGH:
246 reg |= EXTIRQ_CFG_LEVELSENSE(irq);
247 reg |= EXTIRQ_CFG_SENSE(irq);
250 case IRQ_TYPE_LEVEL_LOW:
251 reg |= EXTIRQ_CFG_LEVELSENSE(irq);
252 reg &= ~EXTIRQ_CFG_SENSE(irq);
256 printk(KERN_ERR "bogus flow type combination given !\n");
259 bcm_perf_writel(reg, PERF_EXTIRQ_CFG_REG);
261 irqd_set_trigger_type(d, flow_type);
262 if (flow_type & (IRQ_TYPE_LEVEL_LOW | IRQ_TYPE_LEVEL_HIGH))
263 __irq_set_handler_locked(d->irq, handle_level_irq);
265 __irq_set_handler_locked(d->irq, handle_edge_irq);
267 return IRQ_SET_MASK_OK_NOCOPY;
270 static struct irq_chip bcm63xx_internal_irq_chip = {
271 .name = "bcm63xx_ipic",
272 .irq_mask = bcm63xx_internal_irq_mask,
273 .irq_unmask = bcm63xx_internal_irq_unmask,
276 static struct irq_chip bcm63xx_external_irq_chip = {
277 .name = "bcm63xx_epic",
278 .irq_startup = bcm63xx_external_irq_startup,
279 .irq_shutdown = bcm63xx_external_irq_shutdown,
281 .irq_ack = bcm63xx_external_irq_clear,
283 .irq_mask = bcm63xx_external_irq_mask,
284 .irq_unmask = bcm63xx_external_irq_unmask,
286 .irq_set_type = bcm63xx_external_irq_set_type,
289 static struct irqaction cpu_ip2_cascade_action = {
290 .handler = no_action,
291 .name = "cascade_ip2",
292 .flags = IRQF_NO_THREAD,
295 void __init arch_init_irq(void)
301 for (i = IRQ_INTERNAL_BASE; i < NR_IRQS; ++i)
302 irq_set_chip_and_handler(i, &bcm63xx_internal_irq_chip,
305 for (i = IRQ_EXT_BASE; i < IRQ_EXT_BASE + 4; ++i)
306 irq_set_chip_and_handler(i, &bcm63xx_external_irq_chip,
309 setup_irq(IRQ_MIPS_BASE + 2, &cpu_ip2_cascade_action);