xref: /linux/arch/mips/bcm63xx/irq.c (revision 765532c8aaac624b5f8687af6d319c6a1138a257)
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>
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>
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  */
28 static 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 
50 asmlinkage 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  */
79 static inline void bcm63xx_internal_irq_mask(unsigned int irq)
80 {
81 	u32 mask;
82 
83 	irq -= IRQ_INTERNAL_BASE;
84 	mask = bcm_perf_readl(PERF_IRQMASK_REG);
85 	mask &= ~(1 << irq);
86 	bcm_perf_writel(mask, PERF_IRQMASK_REG);
87 }
88 
89 static void bcm63xx_internal_irq_unmask(unsigned int irq)
90 {
91 	u32 mask;
92 
93 	irq -= IRQ_INTERNAL_BASE;
94 	mask = bcm_perf_readl(PERF_IRQMASK_REG);
95 	mask |= (1 << irq);
96 	bcm_perf_writel(mask, PERF_IRQMASK_REG);
97 }
98 
99 static unsigned int bcm63xx_internal_irq_startup(unsigned int irq)
100 {
101 	bcm63xx_internal_irq_unmask(irq);
102 	return 0;
103 }
104 
105 /*
106  * external IRQs operations: mask/unmask and clear on PERF external
107  * irq control register.
108  */
109 static void bcm63xx_external_irq_mask(unsigned int irq)
110 {
111 	u32 reg;
112 
113 	irq -= IRQ_EXT_BASE;
114 	reg = bcm_perf_readl(PERF_EXTIRQ_CFG_REG);
115 	reg &= ~EXTIRQ_CFG_MASK(irq);
116 	bcm_perf_writel(reg, PERF_EXTIRQ_CFG_REG);
117 }
118 
119 static void bcm63xx_external_irq_unmask(unsigned int irq)
120 {
121 	u32 reg;
122 
123 	irq -= IRQ_EXT_BASE;
124 	reg = bcm_perf_readl(PERF_EXTIRQ_CFG_REG);
125 	reg |= EXTIRQ_CFG_MASK(irq);
126 	bcm_perf_writel(reg, PERF_EXTIRQ_CFG_REG);
127 }
128 
129 static void bcm63xx_external_irq_clear(unsigned int irq)
130 {
131 	u32 reg;
132 
133 	irq -= IRQ_EXT_BASE;
134 	reg = bcm_perf_readl(PERF_EXTIRQ_CFG_REG);
135 	reg |= EXTIRQ_CFG_CLEAR(irq);
136 	bcm_perf_writel(reg, PERF_EXTIRQ_CFG_REG);
137 }
138 
139 static unsigned int bcm63xx_external_irq_startup(unsigned int irq)
140 {
141 	set_c0_status(0x100 << (irq - IRQ_MIPS_BASE));
142 	irq_enable_hazard();
143 	bcm63xx_external_irq_unmask(irq);
144 	return 0;
145 }
146 
147 static void bcm63xx_external_irq_shutdown(unsigned int irq)
148 {
149 	bcm63xx_external_irq_mask(irq);
150 	clear_c0_status(0x100 << (irq - IRQ_MIPS_BASE));
151 	irq_disable_hazard();
152 }
153 
154 static int bcm63xx_external_irq_set_type(unsigned int irq,
155 					 unsigned int flow_type)
156 {
157 	u32 reg;
158 	struct irq_desc *desc = irq_desc + irq;
159 
160 	irq -= IRQ_EXT_BASE;
161 
162 	flow_type &= IRQ_TYPE_SENSE_MASK;
163 
164 	if (flow_type == IRQ_TYPE_NONE)
165 		flow_type = IRQ_TYPE_LEVEL_LOW;
166 
167 	reg = bcm_perf_readl(PERF_EXTIRQ_CFG_REG);
168 	switch (flow_type) {
169 	case IRQ_TYPE_EDGE_BOTH:
170 		reg &= ~EXTIRQ_CFG_LEVELSENSE(irq);
171 		reg |= EXTIRQ_CFG_BOTHEDGE(irq);
172 		break;
173 
174 	case IRQ_TYPE_EDGE_RISING:
175 		reg &= ~EXTIRQ_CFG_LEVELSENSE(irq);
176 		reg |= EXTIRQ_CFG_SENSE(irq);
177 		reg &= ~EXTIRQ_CFG_BOTHEDGE(irq);
178 		break;
179 
180 	case IRQ_TYPE_EDGE_FALLING:
181 		reg &= ~EXTIRQ_CFG_LEVELSENSE(irq);
182 		reg &= ~EXTIRQ_CFG_SENSE(irq);
183 		reg &= ~EXTIRQ_CFG_BOTHEDGE(irq);
184 		break;
185 
186 	case IRQ_TYPE_LEVEL_HIGH:
187 		reg |= EXTIRQ_CFG_LEVELSENSE(irq);
188 		reg |= EXTIRQ_CFG_SENSE(irq);
189 		break;
190 
191 	case IRQ_TYPE_LEVEL_LOW:
192 		reg |= EXTIRQ_CFG_LEVELSENSE(irq);
193 		reg &= ~EXTIRQ_CFG_SENSE(irq);
194 		break;
195 
196 	default:
197 		printk(KERN_ERR "bogus flow type combination given !\n");
198 		return -EINVAL;
199 	}
200 	bcm_perf_writel(reg, PERF_EXTIRQ_CFG_REG);
201 
202 	if (flow_type & (IRQ_TYPE_LEVEL_LOW | IRQ_TYPE_LEVEL_HIGH))  {
203 		desc->status |= IRQ_LEVEL;
204 		desc->handle_irq = handle_level_irq;
205 	} else {
206 		desc->handle_irq = handle_edge_irq;
207 	}
208 
209 	return 0;
210 }
211 
212 static struct irq_chip bcm63xx_internal_irq_chip = {
213 	.name		= "bcm63xx_ipic",
214 	.startup	= bcm63xx_internal_irq_startup,
215 	.shutdown	= bcm63xx_internal_irq_mask,
216 
217 	.mask		= bcm63xx_internal_irq_mask,
218 	.mask_ack	= bcm63xx_internal_irq_mask,
219 	.unmask		= bcm63xx_internal_irq_unmask,
220 };
221 
222 static struct irq_chip bcm63xx_external_irq_chip = {
223 	.name		= "bcm63xx_epic",
224 	.startup	= bcm63xx_external_irq_startup,
225 	.shutdown	= bcm63xx_external_irq_shutdown,
226 
227 	.ack		= bcm63xx_external_irq_clear,
228 
229 	.mask		= bcm63xx_external_irq_mask,
230 	.unmask		= bcm63xx_external_irq_unmask,
231 
232 	.set_type	= bcm63xx_external_irq_set_type,
233 };
234 
235 static struct irqaction cpu_ip2_cascade_action = {
236 	.handler	= no_action,
237 	.name		= "cascade_ip2",
238 };
239 
240 void __init arch_init_irq(void)
241 {
242 	int i;
243 
244 	mips_cpu_irq_init();
245 	for (i = IRQ_INTERNAL_BASE; i < NR_IRQS; ++i)
246 		set_irq_chip_and_handler(i, &bcm63xx_internal_irq_chip,
247 					 handle_level_irq);
248 
249 	for (i = IRQ_EXT_BASE; i < IRQ_EXT_BASE + 4; ++i)
250 		set_irq_chip_and_handler(i, &bcm63xx_external_irq_chip,
251 					 handle_edge_irq);
252 
253 	setup_irq(IRQ_MIPS_BASE + 2, &cpu_ip2_cascade_action);
254 }
255