xref: /openbmc/linux/drivers/sh/intc/handle.c (revision 2be6bb0c79c7fbda3425b65ee51c558bbaf4cf91)
1*2be6bb0cSPaul Mundt /*
2*2be6bb0cSPaul Mundt  * Shared interrupt handling code for IPR and INTC2 types of IRQs.
3*2be6bb0cSPaul Mundt  *
4*2be6bb0cSPaul Mundt  * Copyright (C) 2007, 2008 Magnus Damm
5*2be6bb0cSPaul Mundt  * Copyright (C) 2009, 2010 Paul Mundt
6*2be6bb0cSPaul Mundt  *
7*2be6bb0cSPaul Mundt  * This file is subject to the terms and conditions of the GNU General Public
8*2be6bb0cSPaul Mundt  * License.  See the file "COPYING" in the main directory of this archive
9*2be6bb0cSPaul Mundt  * for more details.
10*2be6bb0cSPaul Mundt  */
11*2be6bb0cSPaul Mundt #include <linux/init.h>
12*2be6bb0cSPaul Mundt #include <linux/irq.h>
13*2be6bb0cSPaul Mundt #include <linux/spinlock.h>
14*2be6bb0cSPaul Mundt #include "internals.h"
15*2be6bb0cSPaul Mundt 
16*2be6bb0cSPaul Mundt static unsigned long ack_handle[NR_IRQS];
17*2be6bb0cSPaul Mundt 
18*2be6bb0cSPaul Mundt static intc_enum __init intc_grp_id(struct intc_desc *desc,
19*2be6bb0cSPaul Mundt 				    intc_enum enum_id)
20*2be6bb0cSPaul Mundt {
21*2be6bb0cSPaul Mundt 	struct intc_group *g = desc->hw.groups;
22*2be6bb0cSPaul Mundt 	unsigned int i, j;
23*2be6bb0cSPaul Mundt 
24*2be6bb0cSPaul Mundt 	for (i = 0; g && enum_id && i < desc->hw.nr_groups; i++) {
25*2be6bb0cSPaul Mundt 		g = desc->hw.groups + i;
26*2be6bb0cSPaul Mundt 
27*2be6bb0cSPaul Mundt 		for (j = 0; g->enum_ids[j]; j++) {
28*2be6bb0cSPaul Mundt 			if (g->enum_ids[j] != enum_id)
29*2be6bb0cSPaul Mundt 				continue;
30*2be6bb0cSPaul Mundt 
31*2be6bb0cSPaul Mundt 			return g->enum_id;
32*2be6bb0cSPaul Mundt 		}
33*2be6bb0cSPaul Mundt 	}
34*2be6bb0cSPaul Mundt 
35*2be6bb0cSPaul Mundt 	return 0;
36*2be6bb0cSPaul Mundt }
37*2be6bb0cSPaul Mundt 
38*2be6bb0cSPaul Mundt static unsigned int __init _intc_mask_data(struct intc_desc *desc,
39*2be6bb0cSPaul Mundt 					   struct intc_desc_int *d,
40*2be6bb0cSPaul Mundt 					   intc_enum enum_id,
41*2be6bb0cSPaul Mundt 					   unsigned int *reg_idx,
42*2be6bb0cSPaul Mundt 					   unsigned int *fld_idx)
43*2be6bb0cSPaul Mundt {
44*2be6bb0cSPaul Mundt 	struct intc_mask_reg *mr = desc->hw.mask_regs;
45*2be6bb0cSPaul Mundt 	unsigned int fn, mode;
46*2be6bb0cSPaul Mundt 	unsigned long reg_e, reg_d;
47*2be6bb0cSPaul Mundt 
48*2be6bb0cSPaul Mundt 	while (mr && enum_id && *reg_idx < desc->hw.nr_mask_regs) {
49*2be6bb0cSPaul Mundt 		mr = desc->hw.mask_regs + *reg_idx;
50*2be6bb0cSPaul Mundt 
51*2be6bb0cSPaul Mundt 		for (; *fld_idx < ARRAY_SIZE(mr->enum_ids); (*fld_idx)++) {
52*2be6bb0cSPaul Mundt 			if (mr->enum_ids[*fld_idx] != enum_id)
53*2be6bb0cSPaul Mundt 				continue;
54*2be6bb0cSPaul Mundt 
55*2be6bb0cSPaul Mundt 			if (mr->set_reg && mr->clr_reg) {
56*2be6bb0cSPaul Mundt 				fn = REG_FN_WRITE_BASE;
57*2be6bb0cSPaul Mundt 				mode = MODE_DUAL_REG;
58*2be6bb0cSPaul Mundt 				reg_e = mr->clr_reg;
59*2be6bb0cSPaul Mundt 				reg_d = mr->set_reg;
60*2be6bb0cSPaul Mundt 			} else {
61*2be6bb0cSPaul Mundt 				fn = REG_FN_MODIFY_BASE;
62*2be6bb0cSPaul Mundt 				if (mr->set_reg) {
63*2be6bb0cSPaul Mundt 					mode = MODE_ENABLE_REG;
64*2be6bb0cSPaul Mundt 					reg_e = mr->set_reg;
65*2be6bb0cSPaul Mundt 					reg_d = mr->set_reg;
66*2be6bb0cSPaul Mundt 				} else {
67*2be6bb0cSPaul Mundt 					mode = MODE_MASK_REG;
68*2be6bb0cSPaul Mundt 					reg_e = mr->clr_reg;
69*2be6bb0cSPaul Mundt 					reg_d = mr->clr_reg;
70*2be6bb0cSPaul Mundt 				}
71*2be6bb0cSPaul Mundt 			}
72*2be6bb0cSPaul Mundt 
73*2be6bb0cSPaul Mundt 			fn += (mr->reg_width >> 3) - 1;
74*2be6bb0cSPaul Mundt 			return _INTC_MK(fn, mode,
75*2be6bb0cSPaul Mundt 					intc_get_reg(d, reg_e),
76*2be6bb0cSPaul Mundt 					intc_get_reg(d, reg_d),
77*2be6bb0cSPaul Mundt 					1,
78*2be6bb0cSPaul Mundt 					(mr->reg_width - 1) - *fld_idx);
79*2be6bb0cSPaul Mundt 		}
80*2be6bb0cSPaul Mundt 
81*2be6bb0cSPaul Mundt 		*fld_idx = 0;
82*2be6bb0cSPaul Mundt 		(*reg_idx)++;
83*2be6bb0cSPaul Mundt 	}
84*2be6bb0cSPaul Mundt 
85*2be6bb0cSPaul Mundt 	return 0;
86*2be6bb0cSPaul Mundt }
87*2be6bb0cSPaul Mundt 
88*2be6bb0cSPaul Mundt unsigned int __init
89*2be6bb0cSPaul Mundt intc_get_mask_handle(struct intc_desc *desc, struct intc_desc_int *d,
90*2be6bb0cSPaul Mundt 		     intc_enum enum_id, int do_grps)
91*2be6bb0cSPaul Mundt {
92*2be6bb0cSPaul Mundt 	unsigned int i = 0;
93*2be6bb0cSPaul Mundt 	unsigned int j = 0;
94*2be6bb0cSPaul Mundt 	unsigned int ret;
95*2be6bb0cSPaul Mundt 
96*2be6bb0cSPaul Mundt 	ret = _intc_mask_data(desc, d, enum_id, &i, &j);
97*2be6bb0cSPaul Mundt 	if (ret)
98*2be6bb0cSPaul Mundt 		return ret;
99*2be6bb0cSPaul Mundt 
100*2be6bb0cSPaul Mundt 	if (do_grps)
101*2be6bb0cSPaul Mundt 		return intc_get_mask_handle(desc, d, intc_grp_id(desc, enum_id), 0);
102*2be6bb0cSPaul Mundt 
103*2be6bb0cSPaul Mundt 	return 0;
104*2be6bb0cSPaul Mundt }
105*2be6bb0cSPaul Mundt 
106*2be6bb0cSPaul Mundt static unsigned int __init _intc_prio_data(struct intc_desc *desc,
107*2be6bb0cSPaul Mundt 					   struct intc_desc_int *d,
108*2be6bb0cSPaul Mundt 					   intc_enum enum_id,
109*2be6bb0cSPaul Mundt 					   unsigned int *reg_idx,
110*2be6bb0cSPaul Mundt 					   unsigned int *fld_idx)
111*2be6bb0cSPaul Mundt {
112*2be6bb0cSPaul Mundt 	struct intc_prio_reg *pr = desc->hw.prio_regs;
113*2be6bb0cSPaul Mundt 	unsigned int fn, n, mode, bit;
114*2be6bb0cSPaul Mundt 	unsigned long reg_e, reg_d;
115*2be6bb0cSPaul Mundt 
116*2be6bb0cSPaul Mundt 	while (pr && enum_id && *reg_idx < desc->hw.nr_prio_regs) {
117*2be6bb0cSPaul Mundt 		pr = desc->hw.prio_regs + *reg_idx;
118*2be6bb0cSPaul Mundt 
119*2be6bb0cSPaul Mundt 		for (; *fld_idx < ARRAY_SIZE(pr->enum_ids); (*fld_idx)++) {
120*2be6bb0cSPaul Mundt 			if (pr->enum_ids[*fld_idx] != enum_id)
121*2be6bb0cSPaul Mundt 				continue;
122*2be6bb0cSPaul Mundt 
123*2be6bb0cSPaul Mundt 			if (pr->set_reg && pr->clr_reg) {
124*2be6bb0cSPaul Mundt 				fn = REG_FN_WRITE_BASE;
125*2be6bb0cSPaul Mundt 				mode = MODE_PCLR_REG;
126*2be6bb0cSPaul Mundt 				reg_e = pr->set_reg;
127*2be6bb0cSPaul Mundt 				reg_d = pr->clr_reg;
128*2be6bb0cSPaul Mundt 			} else {
129*2be6bb0cSPaul Mundt 				fn = REG_FN_MODIFY_BASE;
130*2be6bb0cSPaul Mundt 				mode = MODE_PRIO_REG;
131*2be6bb0cSPaul Mundt 				if (!pr->set_reg)
132*2be6bb0cSPaul Mundt 					BUG();
133*2be6bb0cSPaul Mundt 				reg_e = pr->set_reg;
134*2be6bb0cSPaul Mundt 				reg_d = pr->set_reg;
135*2be6bb0cSPaul Mundt 			}
136*2be6bb0cSPaul Mundt 
137*2be6bb0cSPaul Mundt 			fn += (pr->reg_width >> 3) - 1;
138*2be6bb0cSPaul Mundt 			n = *fld_idx + 1;
139*2be6bb0cSPaul Mundt 
140*2be6bb0cSPaul Mundt 			BUG_ON(n * pr->field_width > pr->reg_width);
141*2be6bb0cSPaul Mundt 
142*2be6bb0cSPaul Mundt 			bit = pr->reg_width - (n * pr->field_width);
143*2be6bb0cSPaul Mundt 
144*2be6bb0cSPaul Mundt 			return _INTC_MK(fn, mode,
145*2be6bb0cSPaul Mundt 					intc_get_reg(d, reg_e),
146*2be6bb0cSPaul Mundt 					intc_get_reg(d, reg_d),
147*2be6bb0cSPaul Mundt 					pr->field_width, bit);
148*2be6bb0cSPaul Mundt 		}
149*2be6bb0cSPaul Mundt 
150*2be6bb0cSPaul Mundt 		*fld_idx = 0;
151*2be6bb0cSPaul Mundt 		(*reg_idx)++;
152*2be6bb0cSPaul Mundt 	}
153*2be6bb0cSPaul Mundt 
154*2be6bb0cSPaul Mundt 	return 0;
155*2be6bb0cSPaul Mundt }
156*2be6bb0cSPaul Mundt 
157*2be6bb0cSPaul Mundt unsigned int __init
158*2be6bb0cSPaul Mundt intc_get_prio_handle(struct intc_desc *desc, struct intc_desc_int *d,
159*2be6bb0cSPaul Mundt 		     intc_enum enum_id, int do_grps)
160*2be6bb0cSPaul Mundt {
161*2be6bb0cSPaul Mundt 	unsigned int i = 0;
162*2be6bb0cSPaul Mundt 	unsigned int j = 0;
163*2be6bb0cSPaul Mundt 	unsigned int ret;
164*2be6bb0cSPaul Mundt 
165*2be6bb0cSPaul Mundt 	ret = _intc_prio_data(desc, d, enum_id, &i, &j);
166*2be6bb0cSPaul Mundt 	if (ret)
167*2be6bb0cSPaul Mundt 		return ret;
168*2be6bb0cSPaul Mundt 
169*2be6bb0cSPaul Mundt 	if (do_grps)
170*2be6bb0cSPaul Mundt 		return intc_get_prio_handle(desc, d, intc_grp_id(desc, enum_id), 0);
171*2be6bb0cSPaul Mundt 
172*2be6bb0cSPaul Mundt 	return 0;
173*2be6bb0cSPaul Mundt }
174*2be6bb0cSPaul Mundt 
175*2be6bb0cSPaul Mundt static unsigned int __init intc_ack_data(struct intc_desc *desc,
176*2be6bb0cSPaul Mundt 					  struct intc_desc_int *d,
177*2be6bb0cSPaul Mundt 					  intc_enum enum_id)
178*2be6bb0cSPaul Mundt {
179*2be6bb0cSPaul Mundt 	struct intc_mask_reg *mr = desc->hw.ack_regs;
180*2be6bb0cSPaul Mundt 	unsigned int i, j, fn, mode;
181*2be6bb0cSPaul Mundt 	unsigned long reg_e, reg_d;
182*2be6bb0cSPaul Mundt 
183*2be6bb0cSPaul Mundt 	for (i = 0; mr && enum_id && i < desc->hw.nr_ack_regs; i++) {
184*2be6bb0cSPaul Mundt 		mr = desc->hw.ack_regs + i;
185*2be6bb0cSPaul Mundt 
186*2be6bb0cSPaul Mundt 		for (j = 0; j < ARRAY_SIZE(mr->enum_ids); j++) {
187*2be6bb0cSPaul Mundt 			if (mr->enum_ids[j] != enum_id)
188*2be6bb0cSPaul Mundt 				continue;
189*2be6bb0cSPaul Mundt 
190*2be6bb0cSPaul Mundt 			fn = REG_FN_MODIFY_BASE;
191*2be6bb0cSPaul Mundt 			mode = MODE_ENABLE_REG;
192*2be6bb0cSPaul Mundt 			reg_e = mr->set_reg;
193*2be6bb0cSPaul Mundt 			reg_d = mr->set_reg;
194*2be6bb0cSPaul Mundt 
195*2be6bb0cSPaul Mundt 			fn += (mr->reg_width >> 3) - 1;
196*2be6bb0cSPaul Mundt 			return _INTC_MK(fn, mode,
197*2be6bb0cSPaul Mundt 					intc_get_reg(d, reg_e),
198*2be6bb0cSPaul Mundt 					intc_get_reg(d, reg_d),
199*2be6bb0cSPaul Mundt 					1,
200*2be6bb0cSPaul Mundt 					(mr->reg_width - 1) - j);
201*2be6bb0cSPaul Mundt 		}
202*2be6bb0cSPaul Mundt 	}
203*2be6bb0cSPaul Mundt 
204*2be6bb0cSPaul Mundt 	return 0;
205*2be6bb0cSPaul Mundt }
206*2be6bb0cSPaul Mundt 
207*2be6bb0cSPaul Mundt static void intc_enable_disable(struct intc_desc_int *d,
208*2be6bb0cSPaul Mundt 				unsigned long handle, int do_enable)
209*2be6bb0cSPaul Mundt {
210*2be6bb0cSPaul Mundt 	unsigned long addr;
211*2be6bb0cSPaul Mundt 	unsigned int cpu;
212*2be6bb0cSPaul Mundt 	unsigned long (*fn)(unsigned long, unsigned long,
213*2be6bb0cSPaul Mundt 		   unsigned long (*)(unsigned long, unsigned long,
214*2be6bb0cSPaul Mundt 				     unsigned long),
215*2be6bb0cSPaul Mundt 		   unsigned int);
216*2be6bb0cSPaul Mundt 
217*2be6bb0cSPaul Mundt 	if (do_enable) {
218*2be6bb0cSPaul Mundt 		for (cpu = 0; cpu < SMP_NR(d, _INTC_ADDR_E(handle)); cpu++) {
219*2be6bb0cSPaul Mundt 			addr = INTC_REG(d, _INTC_ADDR_E(handle), cpu);
220*2be6bb0cSPaul Mundt 			fn = intc_enable_noprio_fns[_INTC_MODE(handle)];
221*2be6bb0cSPaul Mundt 			fn(addr, handle, intc_reg_fns[_INTC_FN(handle)], 0);
222*2be6bb0cSPaul Mundt 		}
223*2be6bb0cSPaul Mundt 	} else {
224*2be6bb0cSPaul Mundt 		for (cpu = 0; cpu < SMP_NR(d, _INTC_ADDR_D(handle)); cpu++) {
225*2be6bb0cSPaul Mundt 			addr = INTC_REG(d, _INTC_ADDR_D(handle), cpu);
226*2be6bb0cSPaul Mundt 			fn = intc_disable_fns[_INTC_MODE(handle)];
227*2be6bb0cSPaul Mundt 			fn(addr, handle, intc_reg_fns[_INTC_FN(handle)], 0);
228*2be6bb0cSPaul Mundt 		}
229*2be6bb0cSPaul Mundt 	}
230*2be6bb0cSPaul Mundt }
231*2be6bb0cSPaul Mundt 
232*2be6bb0cSPaul Mundt void __init intc_enable_disable_enum(struct intc_desc *desc,
233*2be6bb0cSPaul Mundt 				     struct intc_desc_int *d,
234*2be6bb0cSPaul Mundt 				     intc_enum enum_id, int enable)
235*2be6bb0cSPaul Mundt {
236*2be6bb0cSPaul Mundt 	unsigned int i, j, data;
237*2be6bb0cSPaul Mundt 
238*2be6bb0cSPaul Mundt 	/* go through and enable/disable all mask bits */
239*2be6bb0cSPaul Mundt 	i = j = 0;
240*2be6bb0cSPaul Mundt 	do {
241*2be6bb0cSPaul Mundt 		data = _intc_mask_data(desc, d, enum_id, &i, &j);
242*2be6bb0cSPaul Mundt 		if (data)
243*2be6bb0cSPaul Mundt 			intc_enable_disable(d, data, enable);
244*2be6bb0cSPaul Mundt 		j++;
245*2be6bb0cSPaul Mundt 	} while (data);
246*2be6bb0cSPaul Mundt 
247*2be6bb0cSPaul Mundt 	/* go through and enable/disable all priority fields */
248*2be6bb0cSPaul Mundt 	i = j = 0;
249*2be6bb0cSPaul Mundt 	do {
250*2be6bb0cSPaul Mundt 		data = _intc_prio_data(desc, d, enum_id, &i, &j);
251*2be6bb0cSPaul Mundt 		if (data)
252*2be6bb0cSPaul Mundt 			intc_enable_disable(d, data, enable);
253*2be6bb0cSPaul Mundt 
254*2be6bb0cSPaul Mundt 		j++;
255*2be6bb0cSPaul Mundt 	} while (data);
256*2be6bb0cSPaul Mundt }
257*2be6bb0cSPaul Mundt 
258*2be6bb0cSPaul Mundt unsigned int __init
259*2be6bb0cSPaul Mundt intc_get_sense_handle(struct intc_desc *desc, struct intc_desc_int *d,
260*2be6bb0cSPaul Mundt 		      intc_enum enum_id)
261*2be6bb0cSPaul Mundt {
262*2be6bb0cSPaul Mundt 	struct intc_sense_reg *sr = desc->hw.sense_regs;
263*2be6bb0cSPaul Mundt 	unsigned int i, j, fn, bit;
264*2be6bb0cSPaul Mundt 
265*2be6bb0cSPaul Mundt 	for (i = 0; sr && enum_id && i < desc->hw.nr_sense_regs; i++) {
266*2be6bb0cSPaul Mundt 		sr = desc->hw.sense_regs + i;
267*2be6bb0cSPaul Mundt 
268*2be6bb0cSPaul Mundt 		for (j = 0; j < ARRAY_SIZE(sr->enum_ids); j++) {
269*2be6bb0cSPaul Mundt 			if (sr->enum_ids[j] != enum_id)
270*2be6bb0cSPaul Mundt 				continue;
271*2be6bb0cSPaul Mundt 
272*2be6bb0cSPaul Mundt 			fn = REG_FN_MODIFY_BASE;
273*2be6bb0cSPaul Mundt 			fn += (sr->reg_width >> 3) - 1;
274*2be6bb0cSPaul Mundt 
275*2be6bb0cSPaul Mundt 			BUG_ON((j + 1) * sr->field_width > sr->reg_width);
276*2be6bb0cSPaul Mundt 
277*2be6bb0cSPaul Mundt 			bit = sr->reg_width - ((j + 1) * sr->field_width);
278*2be6bb0cSPaul Mundt 
279*2be6bb0cSPaul Mundt 			return _INTC_MK(fn, 0, intc_get_reg(d, sr->reg),
280*2be6bb0cSPaul Mundt 					0, sr->field_width, bit);
281*2be6bb0cSPaul Mundt 		}
282*2be6bb0cSPaul Mundt 	}
283*2be6bb0cSPaul Mundt 
284*2be6bb0cSPaul Mundt 	return 0;
285*2be6bb0cSPaul Mundt }
286*2be6bb0cSPaul Mundt 
287*2be6bb0cSPaul Mundt 
288*2be6bb0cSPaul Mundt void intc_set_ack_handle(unsigned int irq, struct intc_desc *desc,
289*2be6bb0cSPaul Mundt 			 struct intc_desc_int *d, intc_enum id)
290*2be6bb0cSPaul Mundt {
291*2be6bb0cSPaul Mundt 	unsigned long flags;
292*2be6bb0cSPaul Mundt 
293*2be6bb0cSPaul Mundt 	/*
294*2be6bb0cSPaul Mundt 	 * Nothing to do for this IRQ.
295*2be6bb0cSPaul Mundt 	 */
296*2be6bb0cSPaul Mundt 	if (!desc->hw.ack_regs)
297*2be6bb0cSPaul Mundt 		return;
298*2be6bb0cSPaul Mundt 
299*2be6bb0cSPaul Mundt 	raw_spin_lock_irqsave(&intc_big_lock, flags);
300*2be6bb0cSPaul Mundt 	ack_handle[irq] = intc_ack_data(desc, d, id);
301*2be6bb0cSPaul Mundt 	raw_spin_unlock_irqrestore(&intc_big_lock, flags);
302*2be6bb0cSPaul Mundt }
303*2be6bb0cSPaul Mundt 
304*2be6bb0cSPaul Mundt unsigned long intc_get_ack_handle(unsigned int irq)
305*2be6bb0cSPaul Mundt {
306*2be6bb0cSPaul Mundt 	return ack_handle[irq];
307*2be6bb0cSPaul Mundt }
308