xref: /openbsd-src/sys/arch/arm64/dev/aplintc.c (revision de8cc8edbc71bd3e3bc7fbffa27ba0e564c37d8b)
1 /*	$OpenBSD: aplintc.c,v 1.1 2021/02/23 17:01:17 kettenis Exp $	*/
2 /*
3  * Copyright (c) 2021 Mark Kettenis
4  *
5  * Permission to use, copy, modify, and distribute this software for any
6  * purpose with or without fee is hereby granted, provided that the above
7  * copyright notice and this permission notice appear in all copies.
8  *
9  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16  */
17 
18 #include <sys/param.h>
19 #include <sys/device.h>
20 #include <sys/evcount.h>
21 #include <sys/malloc.h>
22 #include <sys/systm.h>
23 
24 #include <machine/bus.h>
25 #include <machine/fdt.h>
26 #include <machine/intr.h>
27 
28 #include <dev/ofw/openfirm.h>
29 #include <dev/ofw/fdt.h>
30 
31 #include <ddb/db_output.h>
32 
33 #define CNTV_CTL_IMASK		(1 << 1)
34 
35 #define AIC_INFO		0x0004
36 #define  AIC_INFO_NIRQ(val)	((val) & 0xffff)
37 #define AIC_WHOAMI		0x2000
38 #define AIC_EVENT		0x2004
39 #define  AIC_EVENT_TYPE(val)	((val) >> 16)
40 #define  AIC_EVENT_TYPE_IRQ	1
41 #define  AIC_EVENT_IRQ(val)	((val) & 0xffff)
42 #define AIC_TARGET_CPU(irq)	(0x3000 + ((irq) << 2))
43 #define AIC_SW_SET(irq)		(0x4000 + (((irq) >> 5) << 2))
44 #define AIC_SW_CLR(irq)		(0x4080 + (((irq) >> 5) << 2))
45 #define  AIC_SW_BIT(irq)	(1U << ((irq) & 0x1f))
46 #define AIC_MASK_SET(irq)	(0x4100 + (((irq) >> 5) << 2))
47 #define AIC_MASK_CLR(irq)	(0x4180 + (((irq) >> 5) << 2))
48 #define  AIC_MASK_BIT(irq)	(1U << ((irq) & 0x1f))
49 
50 #define HREAD4(sc, reg)							\
51 	(bus_space_read_4((sc)->sc_iot, (sc)->sc_ioh, (reg)))
52 #define HWRITE4(sc, reg, val)						\
53 	bus_space_write_4((sc)->sc_iot, (sc)->sc_ioh, (reg), (val))
54 
55 struct intrhand {
56 	TAILQ_ENTRY(intrhand) ih_list;
57 	int		(*ih_func)(void *);
58 	void		*ih_arg;
59 	int		ih_ipl;
60 	int		ih_flags;
61 	int		ih_irq;
62 	struct evcount	ih_count;
63 	const char	*ih_name;
64 	struct cpu_info *ih_ci;
65 };
66 
67 struct aplintc_softc {
68 	struct device		sc_dev;
69 	bus_space_tag_t		sc_iot;
70 	bus_space_handle_t	sc_ioh;
71 
72 	struct interrupt_controller sc_ic;
73 
74 	struct intrhand		*sc_fiq_handler;
75 	int			sc_fiq_pending;
76 	struct intrhand		**sc_irq_handler;
77 	int 			sc_nirq;
78 	TAILQ_HEAD(, intrhand)	sc_irq_list[NIPL];
79 };
80 
81 struct aplintc_softc *aplintc_sc;
82 
83 int	aplintc_match(struct device *, void *, void *);
84 void	aplintc_attach(struct device *, struct device *, void *);
85 
86 struct cfattach	aplintc_ca = {
87 	sizeof (struct aplintc_softc), aplintc_match, aplintc_attach
88 };
89 
90 struct cfdriver aplintc_cd = {
91 	NULL, "aplintc", DV_DULL
92 };
93 
94 void	aplintc_irq_handler(void *);
95 void	aplintc_fiq_handler(void *);
96 void	aplintc_intr_barrier(void *);
97 int	aplintc_splraise(int);
98 int	aplintc_spllower(int);
99 void	aplintc_splx(int);
100 void	aplintc_setipl(int);
101 
102 void 	*aplintc_intr_establish(void *, int *, int, struct cpu_info *,
103 	    int (*)(void *), void *, char *);
104 void	aplintc_intr_disestablish(void *);
105 
106 int
107 aplintc_match(struct device *parent, void *match, void *aux)
108 {
109 	struct fdt_attach_args *faa = aux;
110 
111 	return OF_is_compatible(faa->fa_node, "apple,aic");
112 }
113 
114 void
115 aplintc_attach(struct device *parent, struct device *self, void *aux)
116 {
117 	struct aplintc_softc *sc = (struct aplintc_softc *)self;
118 	struct fdt_attach_args *faa = aux;
119 	uint32_t info;
120 	int ipl;
121 
122 	if (faa->fa_nreg < 1) {
123 		printf(": no registers\n");
124 		return;
125 	}
126 
127 	sc->sc_iot = faa->fa_iot;
128 	if (bus_space_map(sc->sc_iot, faa->fa_reg[0].addr,
129 	    faa->fa_reg[0].size, 0, &sc->sc_ioh)) {
130 		printf(": can't map registers\n");
131 		return;
132 	}
133 
134 	info = HREAD4(sc, AIC_INFO);
135 	sc->sc_nirq = AIC_INFO_NIRQ(info);
136 	sc->sc_irq_handler = mallocarray(sc->sc_nirq,
137 	    sizeof(*sc->sc_irq_handler), M_DEVBUF, M_WAITOK | M_ZERO);
138 	for (ipl = 0; ipl < NIPL; ipl++)
139 		TAILQ_INIT(&sc->sc_irq_list[ipl]);
140 
141 	printf(" nirq %d\n", sc->sc_nirq);
142 
143 	arm_init_smask();
144 
145 	aplintc_sc = sc;
146 	arm_set_intr_handler(aplintc_splraise, aplintc_spllower, aplintc_splx,
147 	    aplintc_setipl, aplintc_irq_handler, aplintc_fiq_handler);
148 
149 	sc->sc_ic.ic_node = faa->fa_node;
150 	sc->sc_ic.ic_cookie = self;
151 	sc->sc_ic.ic_establish = aplintc_intr_establish;
152 	sc->sc_ic.ic_disestablish = aplintc_intr_disestablish;
153 	sc->sc_ic.ic_barrier = aplintc_intr_barrier;
154 	arm_intr_register_fdt(&sc->sc_ic);
155 }
156 
157 void
158 aplintc_irq_handler(void *frame)
159 {
160 	struct aplintc_softc *sc = aplintc_sc;
161 	struct cpu_info *ci = curcpu();
162 	struct intrhand *ih;
163 	uint32_t event;
164 	uint32_t irq, type;
165 	int handled;
166 	int s;
167 
168 	event = HREAD4(sc, AIC_EVENT);
169 	irq = AIC_EVENT_IRQ(event);
170 	type = AIC_EVENT_TYPE(event);
171 
172 	if (type != AIC_EVENT_TYPE_IRQ)
173 		panic("%s: unexpected event type %d\n", __func__, type);
174 
175 	if (irq >= sc->sc_nirq)
176 		panic("%s: unexpected irq %d\n", __func__, irq);
177 
178 	if (sc->sc_irq_handler[irq] == NULL)
179 		return;
180 
181 	HWRITE4(sc, AIC_SW_CLR(irq), AIC_SW_BIT(irq));
182 
183 	ih = sc->sc_irq_handler[irq];
184 
185 	if (ci->ci_cpl >= ih->ih_ipl) {
186 		/* Queue interrupt as pending. */
187 		TAILQ_INSERT_TAIL(&sc->sc_irq_list[ih->ih_ipl], ih, ih_list);
188 	} else {
189 		s = aplintc_splraise(ih->ih_ipl);
190 		intr_enable();
191 		handled = ih->ih_func(ih->ih_arg);
192 		intr_disable();
193 		if (handled)
194 			ih->ih_count.ec_count++;
195 		aplintc_splx(s);
196 
197 		HWRITE4(sc, AIC_MASK_CLR(irq), AIC_MASK_BIT(irq));
198 	}
199 }
200 
201 void
202 aplintc_fiq_handler(void *frame)
203 {
204 	struct aplintc_softc *sc = aplintc_sc;
205 	struct cpu_info *ci = curcpu();
206 	uint64_t reg;
207 	int s;
208 
209 	if (ci->ci_cpl >= IPL_CLOCK) {
210 		/* Mask timer interrupt and mark as pending. */
211 		reg = READ_SPECIALREG(cntv_ctl_el0);
212 		WRITE_SPECIALREG(cntv_ctl_el0, reg | CNTV_CTL_IMASK);
213 		sc->sc_fiq_pending = 1;
214 		return;
215 	}
216 
217 	s = aplintc_splraise(IPL_CLOCK);
218 	sc->sc_fiq_handler->ih_func(frame);
219 	sc->sc_fiq_handler->ih_count.ec_count++;
220 	aplintc_splx(s);
221 }
222 
223 void
224 aplintc_intr_barrier(void *cookie)
225 {
226 	struct intrhand	*ih = cookie;
227 
228 	sched_barrier(ih->ih_ci);
229 }
230 
231 int
232 aplintc_splraise(int new)
233 {
234 	struct cpu_info *ci = curcpu();
235 	int old = ci->ci_cpl;
236 
237 	if (old > new)
238 		new = old;
239 
240 	aplintc_setipl(new);
241 	return old;
242 }
243 
244 int
245 aplintc_spllower(int new)
246 {
247 	struct cpu_info *ci = curcpu();
248 	int old = ci->ci_cpl;
249 
250 	aplintc_splx(new);
251 	return old;
252 }
253 
254 void
255 aplintc_splx(int new)
256 {
257 	struct aplintc_softc *sc = aplintc_sc;
258 	struct cpu_info *ci = curcpu();
259 	struct intrhand *ih;
260 	uint64_t reg;
261 	u_long daif;
262 	int ipl;
263 
264 	daif = intr_disable();
265 
266 	/* Process pending FIQs. */
267 	if (sc->sc_fiq_pending && new < IPL_CLOCK) {
268 		sc->sc_fiq_pending = 0;
269 		reg = READ_SPECIALREG(cntv_ctl_el0);
270 		WRITE_SPECIALREG(cntv_ctl_el0, reg & ~CNTV_CTL_IMASK);
271 	}
272 
273 	/* Process pending IRQs. */
274 	for (ipl = ci->ci_cpl; ipl > new; ipl--) {
275 		while (!TAILQ_EMPTY(&sc->sc_irq_list[ipl])) {
276 			ih = TAILQ_FIRST(&sc->sc_irq_list[ipl]);
277 			TAILQ_REMOVE(&sc->sc_irq_list[ipl], ih, ih_list);
278 
279 			HWRITE4(sc, AIC_SW_SET(ih->ih_irq),
280 			    AIC_SW_BIT(ih->ih_irq));
281 			HWRITE4(sc, AIC_MASK_CLR(ih->ih_irq),
282 			    AIC_MASK_BIT(ih->ih_irq));
283 		}
284 	}
285 
286 	aplintc_setipl(new);
287 	intr_restore(daif);
288 
289 	if (ci->ci_ipending & arm_smask[new])
290 		arm_do_pending_intr(new);
291 }
292 
293 void
294 aplintc_setipl(int ipl)
295 {
296 	struct cpu_info *ci = curcpu();
297 
298 	ci->ci_cpl = ipl;
299 }
300 
301 void *
302 aplintc_intr_establish(void *cookie, int *cell, int level,
303     struct cpu_info *ci, int (*func)(void *), void *arg, char *name)
304 {
305 	struct aplintc_softc *sc = cookie;
306 	struct intrhand *ih;
307 	uint32_t type = cell[0];
308 	uint32_t irq = cell[1];
309 
310 	if (type == 0) {
311 		KASSERT(level != (IPL_CLOCK | IPL_MPSAFE));
312 		if (irq >= sc->sc_nirq) {
313 			panic("%s: bogus irq number %d\n",
314 			    sc->sc_dev.dv_xname, irq);
315 		}
316 	} else if (type == 1) {
317 		KASSERT(level == (IPL_CLOCK | IPL_MPSAFE));
318 		if (irq >= 4)
319 			panic("%s: bogus fiq number %d\n",
320 			    sc->sc_dev.dv_xname, irq);
321 	} else {
322 		panic("%s: bogus irq type %d",
323 		    sc->sc_dev.dv_xname, cell[0]);
324 	}
325 
326 	ih = malloc(sizeof(*ih), M_DEVBUF, M_WAITOK);
327 	ih->ih_func = func;
328 	ih->ih_arg = arg;
329 	ih->ih_ipl = level & IPL_IRQMASK;
330 	ih->ih_flags = level & IPL_FLAGMASK;
331 	ih->ih_irq = irq;
332 	ih->ih_name = name;
333 	ih->ih_ci = ci;
334 
335 	if (name != NULL)
336 		evcount_attach(&ih->ih_count, name, &ih->ih_irq);
337 
338 	if (type == 0) {
339 		sc->sc_irq_handler[irq] = ih;
340 		HWRITE4(sc, AIC_TARGET_CPU(irq), 1);
341 		HWRITE4(sc, AIC_MASK_CLR(irq), AIC_MASK_BIT(irq));
342 	} else
343 		sc->sc_fiq_handler = ih;
344 
345 	return ih;
346 }
347 
348 void
349 aplintc_intr_disestablish(void *cookie)
350 {
351 	struct aplintc_softc *sc = aplintc_sc;
352 	struct intrhand *ih = cookie;
353 	struct intrhand *tmp;
354 	u_long daif;
355 
356 	KASSERT(ih->ih_ipl < IPL_CLOCK);
357 
358 	daif = intr_disable();
359 
360 	HWRITE4(sc, AIC_SW_CLR(ih->ih_irq), AIC_SW_BIT(ih->ih_irq));
361 	HWRITE4(sc, AIC_MASK_SET(ih->ih_irq), AIC_MASK_BIT(ih->ih_irq));
362 
363 	/* Remove ourselves from the list of pending IRQs. */
364 	TAILQ_FOREACH(tmp, &sc->sc_irq_list[ih->ih_ipl], ih_list) {
365 		if (tmp == ih) {
366 			TAILQ_REMOVE(&sc->sc_irq_list[ih->ih_ipl],
367 			    ih, ih_list);
368 			break;
369 		}
370 	}
371 
372 	sc->sc_irq_handler[ih->ih_irq] = NULL;
373 	if (ih->ih_name)
374 		evcount_detach(&ih->ih_count);
375 
376 	intr_restore(daif);
377 
378 	free(ih, M_DEVBUF, sizeof(*ih));
379 }
380