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 <asm/irq_cpu.h>
15 #include <asm/mipsregs.h>
16 #include <bcm63xx_cpu.h>
17 #include <bcm63xx_regs.h>
18 #include <bcm63xx_io.h>
19 #include <bcm63xx_irq.h>
22 * dispatch internal devices IRQ (uart, enet, watchdog, ...). do not
23 * prioritize any interrupt relatively to another. the static counter
24 * will resume the loop where it ended the last time we left this
27 static void bcm63xx_irq_dispatch_internal(void)
32 pending = bcm_perf_readl(PERF_IRQMASK_REG) &
33 bcm_perf_readl(PERF_IRQSTAT_REG);
42 if (pending & (1 << to_call)) {
43 do_IRQ(to_call + IRQ_INTERNAL_BASE);
49 asmlinkage void plat_irq_dispatch(void)
54 cause = read_c0_cause() & read_c0_status() & ST0_IM;
59 if (cause & CAUSEF_IP7)
61 if (cause & CAUSEF_IP2)
62 bcm63xx_irq_dispatch_internal();
63 if (cause & CAUSEF_IP3)
65 if (cause & CAUSEF_IP4)
67 if (cause & CAUSEF_IP5)
69 if (cause & CAUSEF_IP6)
75 * internal IRQs operations: only mask/unmask on PERF irq mask
78 static inline void bcm63xx_internal_irq_mask(unsigned int irq)
82 irq -= IRQ_INTERNAL_BASE;
83 mask = bcm_perf_readl(PERF_IRQMASK_REG);
85 bcm_perf_writel(mask, PERF_IRQMASK_REG);
88 static void bcm63xx_internal_irq_unmask(unsigned int irq)
92 irq -= IRQ_INTERNAL_BASE;
93 mask = bcm_perf_readl(PERF_IRQMASK_REG);
95 bcm_perf_writel(mask, PERF_IRQMASK_REG);
98 static unsigned int bcm63xx_internal_irq_startup(unsigned int irq)
100 bcm63xx_internal_irq_unmask(irq);
105 * external IRQs operations: mask/unmask and clear on PERF external
106 * irq control register.
108 static void bcm63xx_external_irq_mask(unsigned int irq)
113 reg = bcm_perf_readl(PERF_EXTIRQ_CFG_REG);
114 reg &= ~EXTIRQ_CFG_MASK(irq);
115 bcm_perf_writel(reg, PERF_EXTIRQ_CFG_REG);
118 static void bcm63xx_external_irq_unmask(unsigned int irq)
123 reg = bcm_perf_readl(PERF_EXTIRQ_CFG_REG);
124 reg |= EXTIRQ_CFG_MASK(irq);
125 bcm_perf_writel(reg, PERF_EXTIRQ_CFG_REG);
128 static void bcm63xx_external_irq_clear(unsigned int irq)
133 reg = bcm_perf_readl(PERF_EXTIRQ_CFG_REG);
134 reg |= EXTIRQ_CFG_CLEAR(irq);
135 bcm_perf_writel(reg, PERF_EXTIRQ_CFG_REG);
138 static unsigned int bcm63xx_external_irq_startup(unsigned int irq)
140 set_c0_status(0x100 << (irq - IRQ_MIPS_BASE));
142 bcm63xx_external_irq_unmask(irq);
146 static void bcm63xx_external_irq_shutdown(unsigned int irq)
148 bcm63xx_external_irq_mask(irq);
149 clear_c0_status(0x100 << (irq - IRQ_MIPS_BASE));
150 irq_disable_hazard();
153 static int bcm63xx_external_irq_set_type(unsigned int irq,
154 unsigned int flow_type)
157 struct irq_desc *desc = irq_desc + irq;
161 flow_type &= IRQ_TYPE_SENSE_MASK;
163 if (flow_type == IRQ_TYPE_NONE)
164 flow_type = IRQ_TYPE_LEVEL_LOW;
166 reg = bcm_perf_readl(PERF_EXTIRQ_CFG_REG);
168 case IRQ_TYPE_EDGE_BOTH:
169 reg &= ~EXTIRQ_CFG_LEVELSENSE(irq);
170 reg |= EXTIRQ_CFG_BOTHEDGE(irq);
173 case IRQ_TYPE_EDGE_RISING:
174 reg &= ~EXTIRQ_CFG_LEVELSENSE(irq);
175 reg |= EXTIRQ_CFG_SENSE(irq);
176 reg &= ~EXTIRQ_CFG_BOTHEDGE(irq);
179 case IRQ_TYPE_EDGE_FALLING:
180 reg &= ~EXTIRQ_CFG_LEVELSENSE(irq);
181 reg &= ~EXTIRQ_CFG_SENSE(irq);
182 reg &= ~EXTIRQ_CFG_BOTHEDGE(irq);
185 case IRQ_TYPE_LEVEL_HIGH:
186 reg |= EXTIRQ_CFG_LEVELSENSE(irq);
187 reg |= EXTIRQ_CFG_SENSE(irq);
190 case IRQ_TYPE_LEVEL_LOW:
191 reg |= EXTIRQ_CFG_LEVELSENSE(irq);
192 reg &= ~EXTIRQ_CFG_SENSE(irq);
196 printk(KERN_ERR "bogus flow type combination given !\n");
199 bcm_perf_writel(reg, PERF_EXTIRQ_CFG_REG);
201 if (flow_type & (IRQ_TYPE_LEVEL_LOW | IRQ_TYPE_LEVEL_HIGH)) {
202 desc->status |= IRQ_LEVEL;
203 desc->handle_irq = handle_level_irq;
205 desc->handle_irq = handle_edge_irq;
211 static struct irq_chip bcm63xx_internal_irq_chip = {
212 .name = "bcm63xx_ipic",
213 .startup = bcm63xx_internal_irq_startup,
214 .shutdown = bcm63xx_internal_irq_mask,
216 .mask = bcm63xx_internal_irq_mask,
217 .mask_ack = bcm63xx_internal_irq_mask,
218 .unmask = bcm63xx_internal_irq_unmask,
221 static struct irq_chip bcm63xx_external_irq_chip = {
222 .name = "bcm63xx_epic",
223 .startup = bcm63xx_external_irq_startup,
224 .shutdown = bcm63xx_external_irq_shutdown,
226 .ack = bcm63xx_external_irq_clear,
228 .mask = bcm63xx_external_irq_mask,
229 .unmask = bcm63xx_external_irq_unmask,
231 .set_type = bcm63xx_external_irq_set_type,
234 static struct irqaction cpu_ip2_cascade_action = {
235 .handler = no_action,
236 .name = "cascade_ip2",
239 void __init arch_init_irq(void)
244 for (i = IRQ_INTERNAL_BASE; i < NR_IRQS; ++i)
245 set_irq_chip_and_handler(i, &bcm63xx_internal_irq_chip,
248 for (i = IRQ_EXT_BASE; i < IRQ_EXT_BASE + 4; ++i)
249 set_irq_chip_and_handler(i, &bcm63xx_external_irq_chip,
252 setup_irq(IRQ_MIPS_BASE + 2, &cpu_ip2_cascade_action);