xref: /openbmc/linux/arch/mips/bcm63xx/irq.c (revision a09d2831)
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 <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>
20 
21 /*
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
25  * function.
26  */
27 static void bcm63xx_irq_dispatch_internal(void)
28 {
29 	u32 pending;
30 	static int i;
31 
32 	pending = bcm_perf_readl(PERF_IRQMASK_REG) &
33 		bcm_perf_readl(PERF_IRQSTAT_REG);
34 
35 	if (!pending)
36 		return ;
37 
38 	while (1) {
39 		int to_call = i;
40 
41 		i = (i + 1) & 0x1f;
42 		if (pending & (1 << to_call)) {
43 			do_IRQ(to_call + IRQ_INTERNAL_BASE);
44 			break;
45 		}
46 	}
47 }
48 
49 asmlinkage void plat_irq_dispatch(void)
50 {
51 	u32 cause;
52 
53 	do {
54 		cause = read_c0_cause() & read_c0_status() & ST0_IM;
55 
56 		if (!cause)
57 			break;
58 
59 		if (cause & CAUSEF_IP7)
60 			do_IRQ(7);
61 		if (cause & CAUSEF_IP2)
62 			bcm63xx_irq_dispatch_internal();
63 		if (cause & CAUSEF_IP3)
64 			do_IRQ(IRQ_EXT_0);
65 		if (cause & CAUSEF_IP4)
66 			do_IRQ(IRQ_EXT_1);
67 		if (cause & CAUSEF_IP5)
68 			do_IRQ(IRQ_EXT_2);
69 		if (cause & CAUSEF_IP6)
70 			do_IRQ(IRQ_EXT_3);
71 	} while (1);
72 }
73 
74 /*
75  * internal IRQs operations: only mask/unmask on PERF irq mask
76  * register.
77  */
78 static inline void bcm63xx_internal_irq_mask(unsigned int irq)
79 {
80 	u32 mask;
81 
82 	irq -= IRQ_INTERNAL_BASE;
83 	mask = bcm_perf_readl(PERF_IRQMASK_REG);
84 	mask &= ~(1 << irq);
85 	bcm_perf_writel(mask, PERF_IRQMASK_REG);
86 }
87 
88 static void bcm63xx_internal_irq_unmask(unsigned int irq)
89 {
90 	u32 mask;
91 
92 	irq -= IRQ_INTERNAL_BASE;
93 	mask = bcm_perf_readl(PERF_IRQMASK_REG);
94 	mask |= (1 << irq);
95 	bcm_perf_writel(mask, PERF_IRQMASK_REG);
96 }
97 
98 static unsigned int bcm63xx_internal_irq_startup(unsigned int irq)
99 {
100 	bcm63xx_internal_irq_unmask(irq);
101 	return 0;
102 }
103 
104 /*
105  * external IRQs operations: mask/unmask and clear on PERF external
106  * irq control register.
107  */
108 static void bcm63xx_external_irq_mask(unsigned int irq)
109 {
110 	u32 reg;
111 
112 	irq -= IRQ_EXT_BASE;
113 	reg = bcm_perf_readl(PERF_EXTIRQ_CFG_REG);
114 	reg &= ~EXTIRQ_CFG_MASK(irq);
115 	bcm_perf_writel(reg, PERF_EXTIRQ_CFG_REG);
116 }
117 
118 static void bcm63xx_external_irq_unmask(unsigned int irq)
119 {
120 	u32 reg;
121 
122 	irq -= IRQ_EXT_BASE;
123 	reg = bcm_perf_readl(PERF_EXTIRQ_CFG_REG);
124 	reg |= EXTIRQ_CFG_MASK(irq);
125 	bcm_perf_writel(reg, PERF_EXTIRQ_CFG_REG);
126 }
127 
128 static void bcm63xx_external_irq_clear(unsigned int irq)
129 {
130 	u32 reg;
131 
132 	irq -= IRQ_EXT_BASE;
133 	reg = bcm_perf_readl(PERF_EXTIRQ_CFG_REG);
134 	reg |= EXTIRQ_CFG_CLEAR(irq);
135 	bcm_perf_writel(reg, PERF_EXTIRQ_CFG_REG);
136 }
137 
138 static unsigned int bcm63xx_external_irq_startup(unsigned int irq)
139 {
140 	set_c0_status(0x100 << (irq - IRQ_MIPS_BASE));
141 	irq_enable_hazard();
142 	bcm63xx_external_irq_unmask(irq);
143 	return 0;
144 }
145 
146 static void bcm63xx_external_irq_shutdown(unsigned int irq)
147 {
148 	bcm63xx_external_irq_mask(irq);
149 	clear_c0_status(0x100 << (irq - IRQ_MIPS_BASE));
150 	irq_disable_hazard();
151 }
152 
153 static int bcm63xx_external_irq_set_type(unsigned int irq,
154 					 unsigned int flow_type)
155 {
156 	u32 reg;
157 	struct irq_desc *desc = irq_desc + irq;
158 
159 	irq -= IRQ_EXT_BASE;
160 
161 	flow_type &= IRQ_TYPE_SENSE_MASK;
162 
163 	if (flow_type == IRQ_TYPE_NONE)
164 		flow_type = IRQ_TYPE_LEVEL_LOW;
165 
166 	reg = bcm_perf_readl(PERF_EXTIRQ_CFG_REG);
167 	switch (flow_type) {
168 	case IRQ_TYPE_EDGE_BOTH:
169 		reg &= ~EXTIRQ_CFG_LEVELSENSE(irq);
170 		reg |= EXTIRQ_CFG_BOTHEDGE(irq);
171 		break;
172 
173 	case IRQ_TYPE_EDGE_RISING:
174 		reg &= ~EXTIRQ_CFG_LEVELSENSE(irq);
175 		reg |= EXTIRQ_CFG_SENSE(irq);
176 		reg &= ~EXTIRQ_CFG_BOTHEDGE(irq);
177 		break;
178 
179 	case IRQ_TYPE_EDGE_FALLING:
180 		reg &= ~EXTIRQ_CFG_LEVELSENSE(irq);
181 		reg &= ~EXTIRQ_CFG_SENSE(irq);
182 		reg &= ~EXTIRQ_CFG_BOTHEDGE(irq);
183 		break;
184 
185 	case IRQ_TYPE_LEVEL_HIGH:
186 		reg |= EXTIRQ_CFG_LEVELSENSE(irq);
187 		reg |= EXTIRQ_CFG_SENSE(irq);
188 		break;
189 
190 	case IRQ_TYPE_LEVEL_LOW:
191 		reg |= EXTIRQ_CFG_LEVELSENSE(irq);
192 		reg &= ~EXTIRQ_CFG_SENSE(irq);
193 		break;
194 
195 	default:
196 		printk(KERN_ERR "bogus flow type combination given !\n");
197 		return -EINVAL;
198 	}
199 	bcm_perf_writel(reg, PERF_EXTIRQ_CFG_REG);
200 
201 	if (flow_type & (IRQ_TYPE_LEVEL_LOW | IRQ_TYPE_LEVEL_HIGH))  {
202 		desc->status |= IRQ_LEVEL;
203 		desc->handle_irq = handle_level_irq;
204 	} else {
205 		desc->handle_irq = handle_edge_irq;
206 	}
207 
208 	return 0;
209 }
210 
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,
215 
216 	.mask		= bcm63xx_internal_irq_mask,
217 	.mask_ack	= bcm63xx_internal_irq_mask,
218 	.unmask		= bcm63xx_internal_irq_unmask,
219 };
220 
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,
225 
226 	.ack		= bcm63xx_external_irq_clear,
227 
228 	.mask		= bcm63xx_external_irq_mask,
229 	.unmask		= bcm63xx_external_irq_unmask,
230 
231 	.set_type	= bcm63xx_external_irq_set_type,
232 };
233 
234 static struct irqaction cpu_ip2_cascade_action = {
235 	.handler	= no_action,
236 	.name		= "cascade_ip2",
237 };
238 
239 void __init arch_init_irq(void)
240 {
241 	int i;
242 
243 	mips_cpu_irq_init();
244 	for (i = IRQ_INTERNAL_BASE; i < NR_IRQS; ++i)
245 		set_irq_chip_and_handler(i, &bcm63xx_internal_irq_chip,
246 					 handle_level_irq);
247 
248 	for (i = IRQ_EXT_BASE; i < IRQ_EXT_BASE + 4; ++i)
249 		set_irq_chip_and_handler(i, &bcm63xx_external_irq_chip,
250 					 handle_edge_irq);
251 
252 	setup_irq(IRQ_MIPS_BASE + 2, &cpu_ip2_cascade_action);
253 }
254