xref: /linux/arch/mips/bcm63xx/irq.c (revision f2ee442115c9b6219083c019939a9cc0c9abb2f8)
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(struct irq_data *d)
80 {
81 	unsigned int irq = d->irq - IRQ_INTERNAL_BASE;
82 	u32 mask;
83 
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(struct irq_data *d)
90 {
91 	unsigned int irq = d->irq - IRQ_INTERNAL_BASE;
92 	u32 mask;
93 
94 	mask = bcm_perf_readl(PERF_IRQMASK_REG);
95 	mask |= (1 << irq);
96 	bcm_perf_writel(mask, PERF_IRQMASK_REG);
97 }
98 
99 /*
100  * external IRQs operations: mask/unmask and clear on PERF external
101  * irq control register.
102  */
103 static void bcm63xx_external_irq_mask(struct irq_data *d)
104 {
105 	unsigned int irq = d->irq - IRQ_EXT_BASE;
106 	u32 reg;
107 
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 
113 static void bcm63xx_external_irq_unmask(struct irq_data *d)
114 {
115 	unsigned int irq = d->irq - IRQ_EXT_BASE;
116 	u32 reg;
117 
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 
123 static void bcm63xx_external_irq_clear(struct irq_data *d)
124 {
125 	unsigned int irq = d->irq - IRQ_EXT_BASE;
126 	u32 reg;
127 
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 
133 static unsigned int bcm63xx_external_irq_startup(struct irq_data *d)
134 {
135 	set_c0_status(0x100 << (d->irq - IRQ_MIPS_BASE));
136 	irq_enable_hazard();
137 	bcm63xx_external_irq_unmask(d);
138 	return 0;
139 }
140 
141 static void bcm63xx_external_irq_shutdown(struct irq_data *d)
142 {
143 	bcm63xx_external_irq_mask(d);
144 	clear_c0_status(0x100 << (d->irq - IRQ_MIPS_BASE));
145 	irq_disable_hazard();
146 }
147 
148 static int bcm63xx_external_irq_set_type(struct irq_data *d,
149 					 unsigned int flow_type)
150 {
151 	unsigned int irq = d->irq - IRQ_EXT_BASE;
152 	u32 reg;
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 
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);
199 
200 	return IRQ_SET_MASK_OK_NOCOPY;
201 }
202 
203 static struct irq_chip bcm63xx_internal_irq_chip = {
204 	.name		= "bcm63xx_ipic",
205 	.irq_mask	= bcm63xx_internal_irq_mask,
206 	.irq_unmask	= bcm63xx_internal_irq_unmask,
207 };
208 
209 static struct irq_chip bcm63xx_external_irq_chip = {
210 	.name		= "bcm63xx_epic",
211 	.irq_startup	= bcm63xx_external_irq_startup,
212 	.irq_shutdown	= bcm63xx_external_irq_shutdown,
213 
214 	.irq_ack	= bcm63xx_external_irq_clear,
215 
216 	.irq_mask	= bcm63xx_external_irq_mask,
217 	.irq_unmask	= bcm63xx_external_irq_unmask,
218 
219 	.irq_set_type	= bcm63xx_external_irq_set_type,
220 };
221 
222 static struct irqaction cpu_ip2_cascade_action = {
223 	.handler	= no_action,
224 	.name		= "cascade_ip2",
225 	.flags		= IRQF_NO_THREAD,
226 };
227 
228 void __init arch_init_irq(void)
229 {
230 	int i;
231 
232 	mips_cpu_irq_init();
233 	for (i = IRQ_INTERNAL_BASE; i < NR_IRQS; ++i)
234 		irq_set_chip_and_handler(i, &bcm63xx_internal_irq_chip,
235 					 handle_level_irq);
236 
237 	for (i = IRQ_EXT_BASE; i < IRQ_EXT_BASE + 4; ++i)
238 		irq_set_chip_and_handler(i, &bcm63xx_external_irq_chip,
239 					 handle_edge_irq);
240 
241 	setup_irq(IRQ_MIPS_BASE + 2, &cpu_ip2_cascade_action);
242 }
243