xref: /freebsd/sys/arm/mv/mv_ap806_sei.c (revision 2ed3236082a4473c1da8f72c1ebc071a7b54321f)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3  *
4  * Copyright (c) 2019 Michal Meloun <mmel@FreeBSD.org>
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25  * SUCH DAMAGE.
26  *
27  */
28 
29 #include <sys/cdefs.h>
30 __FBSDID("$FreeBSD$");
31 
32 #include <sys/param.h>
33 #include <sys/systm.h>
34 #include <sys/bus.h>
35 #include <sys/kernel.h>
36 #include <sys/proc.h>
37 #include <sys/rman.h>
38 #include <sys/lock.h>
39 #include <sys/module.h>
40 #include <sys/mutex.h>
41 
42 #include <machine/bus.h>
43 #include <machine/intr.h>
44 #include <machine/resource.h>
45 
46 #include <dev/fdt/simplebus.h>
47 
48 #include <dev/ofw/ofw_bus.h>
49 #include <dev/ofw/ofw_bus_subr.h>
50 
51 #include "pic_if.h"
52 
53 #define	MV_AP806_SEI_LOCK(_sc)		mtx_lock(&(_sc)->mtx)
54 #define	MV_AP806_SEI_UNLOCK(_sc)	mtx_unlock(&(_sc)->mtx)
55 #define	MV_AP806_SEI_LOCK_INIT(_sc)	mtx_init(&_sc->mtx, 			\
56 	    device_get_nameunit(_sc->dev), "mv_ap806_sei", MTX_DEF)
57 #define	MV_AP806_SEI_LOCK_DESTROY(_sc)	mtx_destroy(&_sc->mtx);
58 #define	MV_AP806_SEI_ASSERT_LOCKED(_sc)	mtx_assert(&_sc->mtx, MA_OWNED);
59 #define	MV_AP806_SEI_ASSERT_UNLOCKED(_sc) mtx_assert(&_sc->mtx, MA_NOTOWNED);
60 
61 #define	MV_AP806_SEI_MAX_NIRQS	64
62 #define GICP_SECR0		0x00
63 #define GICP_SECR1		0x04
64 #define GICP_SECR(i)		(0x00  + (((i)/32) * 0x4))
65 #define GICP_SECR_BIT(i)	((i) % 32)
66 #define GICP_SEMR0		0x20
67 #define GICP_SEMR1		0x24
68 #define GICP_SEMR(i)		(0x20  + (((i)/32) * 0x4))
69 #define GICP_SEMR_BIT(i)	((i) % 32)
70 
71 struct mv_ap806_sei_irqsrc {
72 	struct intr_irqsrc	isrc;
73 	u_int			irq;
74 };
75 
76 struct mv_ap806_sei_softc {
77 	device_t		dev;
78 	struct resource		*mem_res;
79 	struct resource		*irq_res;
80 	void			*irq_ih;
81 	struct mtx		mtx;
82 
83 	struct mv_ap806_sei_irqsrc *isrcs;
84 };
85 
86 static struct ofw_compat_data compat_data[] = {
87 	{"marvell,ap806-sei", 1},
88 	{NULL,             0}
89 };
90 
91 #define	RD4(sc, reg)		bus_read_4((sc)->mem_res, (reg))
92 #define	WR4(sc, reg, val)	bus_write_4((sc)->mem_res, (reg), (val))
93 
94 static inline void
95 mv_ap806_sei_isrc_mask(struct mv_ap806_sei_softc *sc,
96      struct mv_ap806_sei_irqsrc *sisrc, uint32_t val)
97 {
98 	uint32_t tmp;
99 	int bit;
100 
101 	bit = GICP_SEMR_BIT(sisrc->irq);
102 	MV_AP806_SEI_LOCK(sc);
103 	tmp = RD4(sc, GICP_SEMR(sisrc->irq));
104 	if (val != 0)
105 		tmp |= 1 << bit;
106 	else
107 		tmp &= ~(1 << bit);
108 	WR4(sc, GICP_SEMR(sisrc->irq), tmp);
109 	MV_AP806_SEI_UNLOCK(sc);
110 }
111 
112 static inline void
113 mv_ap806_sei_isrc_eoi(struct mv_ap806_sei_softc *sc,
114      struct mv_ap806_sei_irqsrc *sisrc)
115 {
116 
117 	WR4(sc, GICP_SECR(sisrc->irq), GICP_SECR_BIT(sisrc->irq));
118 }
119 
120 static void
121 mv_ap806_sei_enable_intr(device_t dev, struct intr_irqsrc *isrc)
122 {
123 	struct mv_ap806_sei_softc *sc;
124 	struct mv_ap806_sei_irqsrc *sisrc;
125 
126 	sc = device_get_softc(dev);
127 	sisrc = (struct mv_ap806_sei_irqsrc *)isrc;
128 	mv_ap806_sei_isrc_mask(sc, sisrc, 0);
129 }
130 
131 static void
132 mv_ap806_sei_disable_intr(device_t dev, struct intr_irqsrc *isrc)
133 {
134 	struct mv_ap806_sei_softc *sc;
135 	struct mv_ap806_sei_irqsrc *sisrc;
136 
137 	sc = device_get_softc(dev);
138 	sisrc = (struct mv_ap806_sei_irqsrc *)isrc;
139 	mv_ap806_sei_isrc_mask(sc, sisrc, 1);
140 }
141 
142 static int
143 mv_ap806_sei_map(device_t dev, struct intr_map_data *data, u_int *irqp)
144 {
145 	struct mv_ap806_sei_softc *sc;
146 	struct intr_map_data_fdt *daf;
147 	u_int irq;
148 
149 	sc = device_get_softc(dev);
150 
151 	if (data->type != INTR_MAP_DATA_FDT)
152 		return (ENOTSUP);
153 
154 	daf = (struct intr_map_data_fdt *)data;
155 	if (daf->ncells != 1 || daf->cells[0] >= MV_AP806_SEI_MAX_NIRQS)
156 		return (EINVAL);
157 	irq = daf->cells[0];
158 	if (irqp != NULL)
159 		*irqp = irq;
160 
161 	return(0);
162 }
163 
164 static int
165 mv_ap806_sei_map_intr(device_t dev, struct intr_map_data *data,
166     struct intr_irqsrc **isrcp)
167 {
168 	struct mv_ap806_sei_softc *sc;
169 	u_int irq;
170 	int rv;
171 
172 	sc = device_get_softc(dev);
173 	rv = mv_ap806_sei_map(dev, data, &irq);
174 	if (rv == 0)
175 		*isrcp = &sc->isrcs[irq].isrc;
176 
177 	return (rv);
178 }
179 
180 static int
181 mv_ap806_sei_setup_intr(device_t dev, struct intr_irqsrc *isrc,
182     struct resource *res, struct intr_map_data *data)
183 {
184 	struct mv_ap806_sei_softc *sc;
185 	struct mv_ap806_sei_irqsrc *sisrc;
186 	u_int irq;
187 	int rv;
188 
189 	sc = device_get_softc(dev);
190 	sisrc = (struct mv_ap806_sei_irqsrc *)isrc;
191 	if (data == NULL)
192 		return (ENOTSUP);
193 	rv = mv_ap806_sei_map(dev, data, &irq);
194 	if (rv != 0)
195 		return (rv);
196 	if (irq != sisrc->irq)
197 		return (EINVAL);
198 	mv_ap806_sei_isrc_mask(sc, sisrc, 0);
199 	return (0);
200 }
201 
202 static int
203 mv_ap806_sei_teardown_intr(device_t dev, struct intr_irqsrc *isrc,
204     struct resource *res, struct intr_map_data *data)
205 {
206 	struct mv_ap806_sei_softc *sc;
207 	struct mv_ap806_sei_irqsrc *sisrc;
208 
209 	sc = device_get_softc(dev);
210 	sisrc = (struct mv_ap806_sei_irqsrc *)isrc;
211 
212 	mv_ap806_sei_isrc_mask(sc, sisrc, 1);
213 	return (0);
214 }
215 
216 static void
217 mv_ap806_sei_pre_ithread(device_t dev, struct intr_irqsrc *isrc)
218 {
219 	struct mv_ap806_sei_softc *sc;
220 	struct mv_ap806_sei_irqsrc *sisrc;
221 
222 	sc = device_get_softc(dev);
223 	sisrc = (struct mv_ap806_sei_irqsrc *)isrc;
224 
225 	mv_ap806_sei_isrc_mask(sc, sisrc, 1);
226 	mv_ap806_sei_isrc_eoi(sc, sisrc);
227 }
228 
229 static void
230 mv_ap806_sei_post_ithread(device_t dev, struct intr_irqsrc *isrc)
231 {
232 	struct mv_ap806_sei_softc *sc;
233 	struct mv_ap806_sei_irqsrc *sisrc;
234 
235 	sc = device_get_softc(dev);
236 	sisrc = (struct mv_ap806_sei_irqsrc *)isrc;
237 
238 	mv_ap806_sei_isrc_mask(sc, sisrc, 1);
239 }
240 
241 static void
242 mv_ap806_sei_post_filter(device_t dev, struct intr_irqsrc *isrc)
243 {
244 	struct mv_ap806_sei_softc *sc;
245 	struct mv_ap806_sei_irqsrc *sisrc;
246 
247 	sc = device_get_softc(dev);
248 	sisrc = (struct mv_ap806_sei_irqsrc *)isrc;
249 
250 	mv_ap806_sei_isrc_mask(sc, sisrc, 1);
251 	mv_ap806_sei_isrc_eoi(sc, sisrc);
252 }
253 
254 /* ----------------------------------------------------------------------------
255  *
256  *		B u s    i n t e r f a c e
257  */
258 static int
259 mv_ap806_sei_intr(void *arg)
260 {
261 	struct mv_ap806_sei_softc *sc;
262 	struct mv_ap806_sei_irqsrc *sirq;
263 	struct trapframe *tf;
264 	uint64_t cause;
265 	u_int irq;
266 
267 	sc = (struct mv_ap806_sei_softc *)arg;
268 	tf = curthread->td_intr_frame;
269 	while (1) {
270 		cause = RD4(sc, GICP_SECR1);
271 		cause <<= 32;
272 		cause |= RD4(sc, GICP_SECR0);
273 
274 		irq = ffsll(cause);
275 		if (irq == 0) break;
276 		irq--;
277 		sirq = &sc->isrcs[irq];
278 		if (intr_isrc_dispatch(&sirq->isrc, tf) != 0) {
279 			mv_ap806_sei_isrc_mask(sc, sirq, 0);
280 			mv_ap806_sei_isrc_eoi(sc, sirq);
281 			device_printf(sc->dev,
282 			    "Stray irq %u disabled\n", irq);
283 		}
284 	}
285 
286 	return (FILTER_HANDLED);
287 }
288 
289 static int
290 mv_ap806_sei_probe(device_t dev)
291 {
292 
293 	if (!ofw_bus_status_okay(dev))
294 		return (ENXIO);
295 
296 	if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
297 		return (ENXIO);
298 
299 	device_set_desc(dev, "Marvell SEI");
300 	return (BUS_PROBE_DEFAULT);
301 }
302 
303 static int
304 mv_ap806_sei_attach(device_t dev)
305 {
306 	struct mv_ap806_sei_softc *sc;
307 	phandle_t xref, node;
308 	uint32_t irq;
309 	const char *name;
310 	int rv, rid;
311 
312 	sc = device_get_softc(dev);
313 	sc->dev = dev;
314 	node = ofw_bus_get_node(dev);
315 	MV_AP806_SEI_LOCK_INIT(sc);
316 
317 	/* Allocate resources. */
318 	rid = 0;
319 	sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
320 	    RF_ACTIVE);
321 	if (sc->mem_res == NULL) {
322 		device_printf(dev, "Cannot allocate memory resources\n");
323 		rv = ENXIO;
324 		goto fail;
325 	}
326 
327 	rid = 0;
328 	sc->irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_ACTIVE);
329 	if (sc->irq_res == NULL) {
330 		device_printf(dev, "Cannot allocate IRQ resources\n");
331 		rv = ENXIO;
332 		goto fail;
333 	}
334 
335 	/* Mask all interrupts) */
336 	WR4(sc, GICP_SEMR0, 0xFFFFFFFF);
337 	WR4(sc, GICP_SEMR1, 0xFFFFFFFF);
338 
339 	/* Create all interrupt sources */
340 	sc->isrcs = malloc(sizeof(*sc->isrcs) * MV_AP806_SEI_MAX_NIRQS,
341 	    M_DEVBUF, M_WAITOK | M_ZERO);
342 	name = device_get_nameunit(sc->dev);
343 	for (irq = 0; irq < MV_AP806_SEI_MAX_NIRQS; irq++) {
344 		sc->isrcs[irq].irq = irq;
345 		rv = intr_isrc_register(&sc->isrcs[irq].isrc,
346 		    sc->dev, 0, "%s,%u", name, irq);
347 		if (rv != 0)
348 			goto fail; /* XXX deregister ISRCs */
349 	}
350 	xref = OF_xref_from_node(node);;
351 	if (intr_pic_register(dev, xref) == NULL) {
352 		device_printf(dev, "Cannot register SEI\n");
353 		rv = ENXIO;
354 		goto fail;
355 	}
356 	if (bus_setup_intr(dev, sc->irq_res,INTR_TYPE_MISC | INTR_MPSAFE,
357 	    mv_ap806_sei_intr, NULL, sc, &sc->irq_ih)) {
358 		device_printf(dev,
359 		    "Unable to register interrupt handler\n");
360 		rv = ENXIO;
361 		goto fail;
362 	}
363 
364 	OF_device_register_xref(xref, dev);
365 	return (0);
366 
367 fail:
368 	if (sc->irq_ih != NULL)
369 		bus_teardown_intr(dev, sc->irq_res, sc->irq_ih);
370 	if (sc->irq_res != NULL)
371 		bus_release_resource(dev, SYS_RES_IRQ, 0, sc->irq_res);
372 	if (sc->mem_res != NULL)
373 		bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->mem_res);
374 	MV_AP806_SEI_LOCK_DESTROY(sc);
375 	return (ENXIO);
376 }
377 
378 static int
379 mv_ap806_sei_detach(device_t dev)
380 {
381 
382 	return (EBUSY);
383 }
384 
385 static device_method_t mv_ap806_sei_methods[] = {
386 	/* Device interface */
387 	DEVMETHOD(device_probe,		mv_ap806_sei_probe),
388 	DEVMETHOD(device_attach,	mv_ap806_sei_attach),
389 	DEVMETHOD(device_detach,	mv_ap806_sei_detach),
390 
391 	/* Interrupt controller interface */
392 	DEVMETHOD(pic_disable_intr,	mv_ap806_sei_disable_intr),
393 	DEVMETHOD(pic_enable_intr,	mv_ap806_sei_enable_intr),
394 	DEVMETHOD(pic_map_intr,		mv_ap806_sei_map_intr),
395 	DEVMETHOD(pic_setup_intr,	mv_ap806_sei_setup_intr),
396 	DEVMETHOD(pic_teardown_intr,	mv_ap806_sei_teardown_intr),
397 	DEVMETHOD(pic_post_filter,	mv_ap806_sei_post_filter),
398 	DEVMETHOD(pic_post_ithread,	mv_ap806_sei_post_ithread),
399 	DEVMETHOD(pic_pre_ithread,	mv_ap806_sei_pre_ithread),
400 
401 	DEVMETHOD_END
402 };
403 
404 static devclass_t mv_ap806_sei_devclass;
405 
406 static driver_t mv_ap806_sei_driver = {
407 	"mv_ap806_sei",
408 	mv_ap806_sei_methods,
409 	sizeof(struct mv_ap806_sei_softc),
410 };
411 
412 EARLY_DRIVER_MODULE(mv_ap806_sei, simplebus, mv_ap806_sei_driver,
413     mv_ap806_sei_devclass, 0, 0, BUS_PASS_INTERRUPT + BUS_PASS_ORDER_MIDDLE);
414