1 /* $OpenBSD: mvmpic.c,v 1.7 2023/04/10 04:21:20 jsg Exp $ */
2 /*
3 * Copyright (c) 2007,2009,2011 Dale Rahn <drahn@openbsd.org>
4 * Copyright (c) 2015 Patrick Wildt <patrick@blueri.se>
5 *
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18
19 #include <sys/param.h>
20 #include <sys/systm.h>
21 #include <sys/malloc.h>
22 #include <sys/device.h>
23 #include <sys/evcount.h>
24
25 #include <arm/cpufunc.h>
26 #include <machine/bus.h>
27 #include <machine/fdt.h>
28
29 #include <dev/ofw/openfirm.h>
30 #include <dev/ofw/fdt.h>
31
32 #define MPIC_CTRL 0x000 /* control register */
33 #define MPIC_CTRL_PRIO_EN 0x1
34 #define MPIC_SOFTINT 0x004 /* software triggered interrupt register */
35 #define MPIC_INTERR 0x020 /* SOC main interrupt error cause register */
36 #define MPIC_ISE 0x030 /* interrupt set enable */
37 #define MPIC_ICE 0x034 /* interrupt clear enable */
38 #define MPIC_ISCR(x) (0x100 + (4 * x)) /* interrupt x source control register */
39 #define MPIC_ISCR_PRIO_SHIFT 24
40 #define MPIC_ISCR_INTEN (1 << 28)
41
42 #define MPIC_DOORBELL_CAUSE 0x008
43 #define MPIC_CTP 0x040 /* current task priority */
44 #define MPIC_CTP_SHIFT 28
45 #define MPIC_IACK 0x044 /* interrupt acknowledge */
46 #define MPIC_ISM 0x048 /* set mask */
47 #define MPIC_ICM 0x04c /* clear mask */
48
49 struct mpic_softc {
50 struct device sc_dev;
51 bus_space_tag_t sc_iot;
52 bus_space_handle_t sc_m_ioh, sc_c_ioh;
53 int sc_node;
54
55 struct intrhand **sc_handlers;
56 int sc_ipl;
57 int sc_nintr;
58 struct evcount sc_spur;
59 struct interrupt_controller sc_intc;
60 void *sc_ih;
61 };
62
63 struct intrhand {
64 int (*ih_func)(void *); /* handler */
65 void *ih_arg; /* arg for handler */
66 int ih_ipl; /* IPL_* */
67 int ih_irq; /* IRQ number */
68 struct evcount ih_count;
69 char *ih_name;
70 void *ih_sc;
71 };
72
73 int mpic_match(struct device *, void *, void *);
74 void mpic_attach(struct device *, struct device *, void *);
75 void mpic_calc_mask(struct mpic_softc *);
76 void *mpic_intr_establish(void *, int *, int, struct cpu_info *,
77 int (*)(void *), void *, char *);
78 void mpic_intr_disestablish(void *);
79 int mpic_intr(void *);
80 void mpic_set_priority(struct mpic_softc *, int, int);
81 void mpic_intr_enable(struct mpic_softc *, int);
82 void mpic_intr_disable(struct mpic_softc *, int);
83
84 const struct cfattach mvmpic_ca = {
85 sizeof (struct mpic_softc), mpic_match, mpic_attach
86 };
87
88 struct cfdriver mvmpic_cd = {
89 NULL, "mvmpic", DV_DULL
90 };
91
92 int
mpic_match(struct device * parent,void * cfdata,void * aux)93 mpic_match(struct device *parent, void *cfdata, void *aux)
94 {
95 struct fdt_attach_args *faa = aux;
96
97 return OF_is_compatible(faa->fa_node, "marvell,mpic");
98 }
99
100 void
mpic_attach(struct device * parent,struct device * self,void * args)101 mpic_attach(struct device *parent, struct device *self, void *args)
102 {
103 struct mpic_softc *sc = (struct mpic_softc *)self;
104 struct fdt_attach_args *faa = args;
105 int i;
106
107 sc->sc_node = faa->fa_node;
108 sc->sc_iot = faa->fa_iot;
109 if (bus_space_map(sc->sc_iot, faa->fa_reg[0].addr,
110 faa->fa_reg[0].size, 0, &sc->sc_m_ioh))
111 panic("%s: main bus_space_map failed!", __func__);
112
113 if (bus_space_map(sc->sc_iot, faa->fa_reg[1].addr,
114 faa->fa_reg[1].size, 0, &sc->sc_c_ioh))
115 panic("%s: cpu bus_space_map failed!", __func__);
116
117 evcount_attach(&sc->sc_spur, "irq1023/spur", NULL);
118
119 sc->sc_nintr = (bus_space_read_4(sc->sc_iot, sc->sc_m_ioh,
120 MPIC_CTRL) >> 2) & 0x3ff;
121 printf(" nirq %d\n", sc->sc_nintr);
122
123 /* Disable all interrupts */
124 for (i = 0; i < sc->sc_nintr; i++) {
125 bus_space_write_4(sc->sc_iot, sc->sc_m_ioh, MPIC_ICE, i);
126 bus_space_write_4(sc->sc_iot, sc->sc_c_ioh, MPIC_ISM, i);
127 }
128
129 /* Clear pending IPIs */
130 bus_space_write_4(sc->sc_iot, sc->sc_c_ioh, MPIC_DOORBELL_CAUSE, 0);
131
132 /* Enable hardware prioritization selection */
133 bus_space_write_4(sc->sc_iot, sc->sc_m_ioh, MPIC_CTRL,
134 MPIC_CTRL_PRIO_EN);
135
136 /* Always allow everything. */
137 bus_space_write_4(sc->sc_iot, sc->sc_c_ioh, MPIC_CTP,
138 (bus_space_read_4(sc->sc_iot, sc->sc_c_ioh, MPIC_CTP) &
139 ~(0xf << MPIC_CTP_SHIFT)) | (IPL_NONE << MPIC_CTP_SHIFT));
140
141 sc->sc_handlers = mallocarray(sc->sc_nintr,
142 sizeof(*sc->sc_handlers), M_DEVBUF, M_ZERO | M_NOWAIT);
143
144 sc->sc_ipl = IPL_NONE;
145 mpic_calc_mask(sc);
146
147 sc->sc_intc.ic_node = faa->fa_node;
148 sc->sc_intc.ic_cookie = sc;
149 sc->sc_intc.ic_establish = mpic_intr_establish;
150 arm_intr_register_fdt(&sc->sc_intc);
151 }
152
153 void
mpic_set_priority(struct mpic_softc * sc,int irq,int pri)154 mpic_set_priority(struct mpic_softc *sc, int irq, int pri)
155 {
156 bus_space_write_4(sc->sc_iot, sc->sc_m_ioh, MPIC_ISCR(irq),
157 (bus_space_read_4(sc->sc_iot, sc->sc_m_ioh, MPIC_ISCR(irq)) &
158 ~(0xf << MPIC_ISCR_PRIO_SHIFT)) | (pri << MPIC_ISCR_PRIO_SHIFT));
159 }
160
161 void
mpic_intr_enable(struct mpic_softc * sc,int irq)162 mpic_intr_enable(struct mpic_softc *sc, int irq)
163 {
164 bus_space_write_4(sc->sc_iot, sc->sc_m_ioh, MPIC_ISE, irq);
165 bus_space_write_4(sc->sc_iot, sc->sc_c_ioh, MPIC_ICM, irq);
166 }
167
168 void
mpic_intr_disable(struct mpic_softc * sc,int irq)169 mpic_intr_disable(struct mpic_softc *sc, int irq)
170 {
171 bus_space_write_4(sc->sc_iot, sc->sc_m_ioh, MPIC_ICE, irq);
172 bus_space_write_4(sc->sc_iot, sc->sc_c_ioh, MPIC_ISM, irq);
173 }
174
175 void
mpic_calc_mask(struct mpic_softc * sc)176 mpic_calc_mask(struct mpic_softc *sc)
177 {
178 struct intrhand *ih;
179 int irq;
180 int max = IPL_NONE;
181 int min = IPL_HIGH;
182
183 for (irq = 0; irq < sc->sc_nintr; irq++) {
184 ih = sc->sc_handlers[irq];
185 if (ih == NULL)
186 continue;
187
188 if (ih->ih_ipl > max)
189 max = ih->ih_ipl;
190
191 if (ih->ih_ipl < min)
192 min = ih->ih_ipl;
193 }
194
195 if (max == IPL_NONE)
196 min = IPL_NONE;
197
198 if (sc->sc_ipl != min) {
199 sc->sc_ipl = min;
200
201 if (sc->sc_ih != NULL)
202 arm_intr_disestablish_fdt(sc->sc_ih);
203
204 if (sc->sc_ipl != IPL_NONE)
205 sc->sc_ih = arm_intr_establish_fdt(sc->sc_node,
206 sc->sc_ipl, mpic_intr, sc, sc->sc_dev.dv_xname);
207 }
208 }
209
210 int
mpic_intr(void * cookie)211 mpic_intr(void *cookie)
212 {
213 struct mpic_softc *sc = cookie;
214 struct intrhand *ih;
215 int irq, s;
216
217 irq = bus_space_read_4(sc->sc_iot, sc->sc_c_ioh, MPIC_IACK) & 0x3ff;
218
219 if (irq == 1023) {
220 sc->sc_spur.ec_count++;
221 return 1;
222 }
223
224 if (irq >= sc->sc_nintr)
225 return 1;
226
227 if ((ih = sc->sc_handlers[irq]) != NULL) {
228 s = splraise(ih->ih_ipl);
229 if (ih->ih_func(ih->ih_arg))
230 ih->ih_count.ec_count++;
231 splx(s);
232 }
233
234 return 1;
235 }
236
237 void *
mpic_intr_establish(void * cookie,int * cells,int level,struct cpu_info * ci,int (* func)(void *),void * arg,char * name)238 mpic_intr_establish(void *cookie, int *cells, int level,
239 struct cpu_info *ci, int (*func)(void *), void *arg, char *name)
240 {
241 struct mpic_softc *sc = cookie;
242 struct intrhand *ih;
243 int psw;
244 int irqno = cells[0];
245
246 if (irqno < 0 || irqno >= sc->sc_nintr)
247 panic("%s: bogus irqnumber %d: %s", __func__,
248 irqno, name);
249
250 if (sc->sc_handlers[irqno] != NULL)
251 panic("%s: irq %d already registered" , __func__,
252 irqno);
253
254 if (ci != NULL && !CPU_IS_PRIMARY(ci))
255 return NULL;
256
257 ih = malloc(sizeof(*ih), M_DEVBUF, M_WAITOK);
258 ih->ih_func = func;
259 ih->ih_arg = arg;
260 ih->ih_ipl = level & IPL_IRQMASK;
261 ih->ih_irq = irqno;
262 ih->ih_name = name;
263 ih->ih_sc = sc;
264
265 psw = disable_interrupts(PSR_I);
266
267 sc->sc_handlers[irqno] = ih;
268
269 if (name != NULL)
270 evcount_attach(&ih->ih_count, name, &ih->ih_irq);
271
272 #ifdef DEBUG_INTC
273 printf("%s: irq %d level %d [%s]\n", __func__, irqno, level, name);
274 #endif
275
276 mpic_calc_mask(sc);
277 mpic_set_priority(sc, irqno, level);
278 mpic_intr_enable(sc, irqno);
279
280 restore_interrupts(psw);
281 return (ih);
282 }
283
284 void
mpic_intr_disestablish(void * cookie)285 mpic_intr_disestablish(void *cookie)
286 {
287 struct intrhand *ih = cookie;
288 struct mpic_softc *sc = ih->ih_sc;
289 int psw;
290
291 psw = disable_interrupts(PSR_I);
292
293 #ifdef DEBUG_INTC
294 printf("%s: irq %d ipl %d [%s]\n", __func__, ih->ih_irq, ih->ih_ipl,
295 ih->ih_name);
296 #endif
297
298 mpic_intr_disable(sc, ih->ih_irq);
299
300 sc->sc_handlers[ih->ih_irq] = NULL;
301 if (ih->ih_name != NULL)
302 evcount_detach(&ih->ih_count);
303 free(ih, M_DEVBUF, sizeof(*ih));
304
305 mpic_calc_mask(sc);
306
307 restore_interrupts(psw);
308 }
309