xref: /freebsd/sys/arm/ti/ti_pruss.c (revision ca987d4641cdcd7f27e153db17c5bf064934faf5)
1 /*-
2  * Copyright (c) 2013 Rui Paulo <rpaulo@FreeBSD.org>
3  * Copyright (c) 2017 Manuel Stuehn
4  * All rights reserved.
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 ``AS IS'' AND ANY EXPRESS OR
16  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
17  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
18  * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
19  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
20  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
21  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
23  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
24  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
25  * POSSIBILITY OF SUCH DAMAGE.
26  */
27 #include <sys/cdefs.h>
28 __FBSDID("$FreeBSD$");
29 
30 #include <sys/poll.h>
31 #include <sys/time.h>
32 #include <sys/uio.h>
33 #include <sys/param.h>
34 #include <sys/systm.h>
35 #include <sys/fcntl.h>
36 #include <sys/bus.h>
37 #include <sys/conf.h>
38 #include <sys/kernel.h>
39 #include <sys/module.h>
40 #include <sys/malloc.h>
41 #include <sys/rman.h>
42 #include <sys/types.h>
43 #include <sys/sysctl.h>
44 #include <sys/event.h>
45 #include <sys/selinfo.h>
46 #include <machine/bus.h>
47 #include <machine/cpu.h>
48 #include <machine/frame.h>
49 #include <machine/intr.h>
50 #include <machine/atomic.h>
51 
52 #include <dev/ofw/openfirm.h>
53 #include <dev/ofw/ofw_bus.h>
54 #include <dev/ofw/ofw_bus_subr.h>
55 
56 #include <arm/ti/ti_prcm.h>
57 #include <arm/ti/ti_pruss.h>
58 
59 #ifdef DEBUG
60 #define	DPRINTF(fmt, ...)	do {	\
61 	printf("%s: ", __func__);	\
62 	printf(fmt, __VA_ARGS__);	\
63 } while (0)
64 #else
65 #define	DPRINTF(fmt, ...)
66 #endif
67 
68 static d_open_t			ti_pruss_irq_open;
69 static d_read_t			ti_pruss_irq_read;
70 static d_poll_t			ti_pruss_irq_poll;
71 
72 static device_probe_t		ti_pruss_probe;
73 static device_attach_t		ti_pruss_attach;
74 static device_detach_t		ti_pruss_detach;
75 static void			ti_pruss_intr(void *);
76 static d_open_t			ti_pruss_open;
77 static d_mmap_t			ti_pruss_mmap;
78 static void 			ti_pruss_irq_kqread_detach(struct knote *);
79 static int 			ti_pruss_irq_kqevent(struct knote *, long);
80 static d_kqfilter_t		ti_pruss_irq_kqfilter;
81 static void			ti_pruss_privdtor(void *data);
82 
83 #define	TI_PRUSS_PRU_IRQS 2
84 #define	TI_PRUSS_HOST_IRQS 8
85 #define	TI_PRUSS_IRQS (TI_PRUSS_HOST_IRQS+TI_PRUSS_PRU_IRQS)
86 #define	TI_PRUSS_EVENTS 64
87 #define	NOT_SET_STR "NONE"
88 #define	TI_TS_ARRAY 16
89 
90 struct ctl
91 {
92 	size_t cnt;
93 	size_t idx;
94 };
95 
96 struct ts_ring_buf
97 {
98 	struct ctl ctl;
99 	uint64_t ts[TI_TS_ARRAY];
100 };
101 
102 struct ti_pruss_irqsc
103 {
104 	struct mtx		sc_mtx;
105 	struct cdev		*sc_pdev;
106 	struct selinfo		sc_selinfo;
107 	int8_t			channel;
108 	int8_t			last;
109 	int8_t			event;
110 	bool			enable;
111 	struct ts_ring_buf	tstamps;
112 };
113 
114 static struct cdevsw ti_pruss_cdevirq = {
115 	.d_version =	D_VERSION,
116 	.d_name =	"ti_pruss_irq",
117 	.d_open =	ti_pruss_irq_open,
118 	.d_read =	ti_pruss_irq_read,
119 	.d_poll =	ti_pruss_irq_poll,
120 	.d_kqfilter =	ti_pruss_irq_kqfilter,
121 };
122 
123 struct ti_pruss_softc {
124 	struct mtx		sc_mtx;
125 	struct resource 	*sc_mem_res;
126 	struct resource 	*sc_irq_res[TI_PRUSS_HOST_IRQS];
127 	void            	*sc_intr[TI_PRUSS_HOST_IRQS];
128 	struct ti_pruss_irqsc	sc_irq_devs[TI_PRUSS_IRQS];
129 	bus_space_tag_t		sc_bt;
130 	bus_space_handle_t	sc_bh;
131 	struct cdev		*sc_pdev;
132 	struct selinfo		sc_selinfo;
133 	bool			sc_glob_irqen;
134 };
135 
136 static struct cdevsw ti_pruss_cdevsw = {
137 	.d_version =	D_VERSION,
138 	.d_name =	"ti_pruss",
139 	.d_open =	ti_pruss_open,
140 	.d_mmap =	ti_pruss_mmap,
141 };
142 
143 static device_method_t ti_pruss_methods[] = {
144 	DEVMETHOD(device_probe,		ti_pruss_probe),
145 	DEVMETHOD(device_attach,	ti_pruss_attach),
146 	DEVMETHOD(device_detach,	ti_pruss_detach),
147 
148 	DEVMETHOD_END
149 };
150 
151 static driver_t ti_pruss_driver = {
152 	"ti_pruss",
153 	ti_pruss_methods,
154 	sizeof(struct ti_pruss_softc)
155 };
156 
157 static devclass_t ti_pruss_devclass;
158 
159 DRIVER_MODULE(ti_pruss, simplebus, ti_pruss_driver, ti_pruss_devclass, 0, 0);
160 MODULE_DEPEND(ti_pruss, ti_prcm, 1, 1, 1);
161 
162 static struct resource_spec ti_pruss_irq_spec[] = {
163 	{ SYS_RES_IRQ,	    0,  RF_ACTIVE },
164 	{ SYS_RES_IRQ,	    1,  RF_ACTIVE },
165 	{ SYS_RES_IRQ,	    2,  RF_ACTIVE },
166 	{ SYS_RES_IRQ,	    3,  RF_ACTIVE },
167 	{ SYS_RES_IRQ,	    4,  RF_ACTIVE },
168 	{ SYS_RES_IRQ,	    5,  RF_ACTIVE },
169 	{ SYS_RES_IRQ,	    6,  RF_ACTIVE },
170 	{ SYS_RES_IRQ,	    7,  RF_ACTIVE },
171 	{ -1,               0,  0 }
172 };
173 CTASSERT(TI_PRUSS_HOST_IRQS == nitems(ti_pruss_irq_spec) - 1);
174 
175 static int
176 ti_pruss_irq_open(struct cdev *dev, int oflags, int devtype, struct thread *td)
177 {
178 	struct ctl* irqs;
179 	struct ti_pruss_irqsc *sc;
180 	sc = dev->si_drv1;
181 
182 	irqs = malloc(sizeof(struct ctl), M_DEVBUF, M_WAITOK);
183 	if (!irqs)
184 	    return (ENOMEM);
185 
186 	irqs->cnt = sc->tstamps.ctl.cnt;
187 	irqs->idx = sc->tstamps.ctl.idx;
188 
189 	return devfs_set_cdevpriv(irqs, ti_pruss_privdtor);
190 }
191 
192 static void
193 ti_pruss_privdtor(void *data)
194 {
195     free(data, M_DEVBUF);
196 }
197 
198 static int
199 ti_pruss_irq_poll(struct cdev *dev, int events, struct thread *td)
200 {
201 	struct ctl* irqs;
202 	struct ti_pruss_irqsc *sc;
203 	sc = dev->si_drv1;
204 
205 	devfs_get_cdevpriv((void**)&irqs);
206 
207 	if (events & (POLLIN | POLLRDNORM)) {
208 		if (sc->tstamps.ctl.cnt != irqs->cnt)
209 			return events & (POLLIN | POLLRDNORM);
210 		else
211 			selrecord(td, &sc->sc_selinfo);
212 	}
213 	return 0;
214 }
215 
216 static int
217 ti_pruss_irq_read(struct cdev *cdev, struct uio *uio, int ioflag)
218 {
219 	const size_t ts_len = sizeof(uint64_t);
220 	struct ti_pruss_irqsc* irq;
221 	struct ctl* priv;
222 	int error = 0;
223 	size_t idx;
224 	ssize_t level;
225 
226 	irq = cdev->si_drv1;
227 
228 	if (uio->uio_resid < ts_len)
229 		return (EINVAL);
230 
231 	error = devfs_get_cdevpriv((void**)&priv);
232 	if (error)
233 	    return (error);
234 
235 	mtx_lock(&irq->sc_mtx);
236 
237 	if (irq->tstamps.ctl.cnt - priv->cnt > TI_TS_ARRAY)
238 	{
239 		priv->cnt = irq->tstamps.ctl.cnt;
240 		priv->idx = irq->tstamps.ctl.idx;
241 		mtx_unlock(&irq->sc_mtx);
242 		return (ENXIO);
243 	}
244 
245 	do {
246 		idx = priv->idx;
247 		level = irq->tstamps.ctl.idx - idx;
248 		if (level < 0)
249 			level += TI_TS_ARRAY;
250 
251 		if (level == 0) {
252 			if (ioflag & O_NONBLOCK) {
253 				mtx_unlock(&irq->sc_mtx);
254 				return (EWOULDBLOCK);
255 			}
256 
257 			error = msleep(irq, &irq->sc_mtx, PCATCH | PDROP,
258 				"pruirq", 0);
259 			if (error)
260 				return error;
261 
262 			mtx_lock(&irq->sc_mtx);
263 		}
264 	}while(level == 0);
265 
266 	mtx_unlock(&irq->sc_mtx);
267 
268 	error = uiomove(&irq->tstamps.ts[idx], ts_len, uio);
269 
270 	if (++idx == TI_TS_ARRAY)
271 		idx = 0;
272 	priv->idx = idx;
273 
274 	atomic_add_32(&priv->cnt, 1);
275 
276 	return (error);
277 }
278 
279 static struct ti_pruss_irq_arg {
280 	int 		       irq;
281 	struct ti_pruss_softc *sc;
282 } ti_pruss_irq_args[TI_PRUSS_IRQS];
283 
284 static __inline uint32_t
285 ti_pruss_reg_read(struct ti_pruss_softc *sc, uint32_t reg)
286 {
287 	return (bus_space_read_4(sc->sc_bt, sc->sc_bh, reg));
288 }
289 
290 static __inline void
291 ti_pruss_reg_write(struct ti_pruss_softc *sc, uint32_t reg, uint32_t val)
292 {
293 	bus_space_write_4(sc->sc_bt, sc->sc_bh, reg, val);
294 }
295 
296 static __inline void
297 ti_pruss_interrupts_clear(struct ti_pruss_softc *sc)
298 {
299 	/* disable global interrupt */
300 	ti_pruss_reg_write(sc, PRUSS_INTC_GER, 0 );
301 
302 	/* clear all events */
303 	ti_pruss_reg_write(sc, PRUSS_INTC_SECR0, 0xFFFFFFFF);
304 	ti_pruss_reg_write(sc, PRUSS_INTC_SECR1, 0xFFFFFFFF);
305 
306 	/* disable all host interrupts */
307 	ti_pruss_reg_write(sc, PRUSS_INTC_HIER, 0);
308 }
309 
310 static __inline int
311 ti_pruss_interrupts_enable(struct ti_pruss_softc *sc, int8_t irq, bool enable)
312 {
313 	if (enable && ((sc->sc_irq_devs[irq].channel == -1) ||
314 	    (sc->sc_irq_devs[irq].event== -1)))
315 	{
316 		device_printf( sc->sc_pdev->si_drv1,
317 			"Interrupt chain not fully configured, not possible to enable\n" );
318 		return (EINVAL);
319 	}
320 
321 	sc->sc_irq_devs[irq].enable = enable;
322 
323 	if (sc->sc_irq_devs[irq].sc_pdev) {
324 		destroy_dev(sc->sc_irq_devs[irq].sc_pdev);
325 		sc->sc_irq_devs[irq].sc_pdev = NULL;
326 	}
327 
328 	if (enable) {
329 		sc->sc_irq_devs[irq].sc_pdev = make_dev(&ti_pruss_cdevirq, 0, UID_ROOT, GID_WHEEL,
330 		    0600, "pruss%d.irq%d", device_get_unit(sc->sc_pdev->si_drv1), irq);
331 		sc->sc_irq_devs[irq].sc_pdev->si_drv1 = &sc->sc_irq_devs[irq];
332 
333 		sc->sc_irq_devs[irq].tstamps.ctl.idx = 0;
334 	}
335 
336 	uint32_t reg = enable ? PRUSS_INTC_HIEISR : PRUSS_INTC_HIDISR;
337 	ti_pruss_reg_write(sc, reg, sc->sc_irq_devs[irq].channel);
338 
339 	reg = enable ? PRUSS_INTC_EISR : PRUSS_INTC_EICR;
340 	ti_pruss_reg_write(sc, reg, sc->sc_irq_devs[irq].event );
341 
342 	return (0);
343 }
344 
345 static __inline void
346 ti_pruss_map_write(struct ti_pruss_softc *sc, uint32_t basereg, uint8_t index, uint8_t content)
347 {
348 	const size_t regadr = basereg + index & ~0x03;
349 	const size_t bitpos = (index & 0x03) * 8;
350 	uint32_t rmw = ti_pruss_reg_read(sc, regadr);
351 	rmw = (rmw & ~( 0xF << bitpos)) | ( (content & 0xF) << bitpos);
352 	ti_pruss_reg_write(sc, regadr, rmw);
353 }
354 
355 static int
356 ti_pruss_event_map( SYSCTL_HANDLER_ARGS )
357 {
358 	struct ti_pruss_softc *sc;
359 	const int8_t irq = arg2;
360 	int err;
361 	char event[sizeof(NOT_SET_STR)];
362 
363 	sc = arg1;
364 
365 	if(sc->sc_irq_devs[irq].event == -1)
366 		bcopy(NOT_SET_STR, event, sizeof(event));
367 	else
368 		snprintf(event, sizeof(event), "%d", sc->sc_irq_devs[irq].event);
369 
370 	err = sysctl_handle_string(oidp, event, sizeof(event), req);
371 	if(err != 0)
372 		return (err);
373 
374 	if (req->newptr) {  // write event
375 		if (strcmp(NOT_SET_STR, event) == 0) {
376 			ti_pruss_interrupts_enable(sc, irq, false);
377 			sc->sc_irq_devs[irq].event = -1;
378 		} else {
379 			if (sc->sc_irq_devs[irq].channel == -1) {
380 				device_printf( sc->sc_pdev->si_drv1,
381 					"corresponding channel not configured\n");
382 				return (ENXIO);
383 			}
384 
385 			const int8_t channelnr = sc->sc_irq_devs[irq].channel;
386 			const int8_t eventnr = strtol( event, NULL, 10 ); // TODO: check if strol is valid
387 			if (eventnr > TI_PRUSS_EVENTS || eventnr < 0) {
388 				device_printf( sc->sc_pdev->si_drv1,
389 					"Event number %d not valid (0 - %d)",
390 					channelnr, TI_PRUSS_EVENTS -1);
391 				return (EINVAL);
392 			}
393 
394 			sc->sc_irq_devs[irq].channel = channelnr;
395 			sc->sc_irq_devs[irq].event = eventnr;
396 
397 			// event[nr] <= channel
398 			ti_pruss_map_write(sc, PRUSS_INTC_CMR_BASE,
399 			    eventnr, channelnr);
400 		}
401 	}
402 	return (err);
403 }
404 
405 static int
406 ti_pruss_channel_map(SYSCTL_HANDLER_ARGS)
407 {
408 	struct ti_pruss_softc *sc;
409 	int err;
410 	char channel[sizeof(NOT_SET_STR)];
411 	const int8_t irq = arg2;
412 
413 	sc = arg1;
414 
415 	if (sc->sc_irq_devs[irq].channel == -1)
416 		bcopy(NOT_SET_STR, channel, sizeof(channel));
417 	else
418 		snprintf(channel, sizeof(channel), "%d", sc->sc_irq_devs[irq].channel);
419 
420 	err = sysctl_handle_string(oidp, channel, sizeof(channel), req);
421 	if (err != 0)
422 		return (err);
423 
424 	if (req->newptr) { // write event
425 		if (strcmp(NOT_SET_STR, channel) == 0) {
426 			ti_pruss_interrupts_enable(sc, irq, false);
427 			ti_pruss_reg_write(sc, PRUSS_INTC_HIDISR,
428 			    sc->sc_irq_devs[irq].channel);
429 			sc->sc_irq_devs[irq].channel = -1;
430 		} else {
431 			const int8_t channelnr = strtol(channel, NULL, 10); // TODO: check if strol is valid
432 			if (channelnr > TI_PRUSS_IRQS || channelnr < 0)
433 			{
434 				device_printf(sc->sc_pdev->si_drv1,
435 					"Channel number %d not valid (0 - %d)",
436 					channelnr, TI_PRUSS_IRQS-1);
437 				return (EINVAL);
438 			}
439 
440 			sc->sc_irq_devs[irq].channel = channelnr;
441 			sc->sc_irq_devs[irq].last = -1;
442 
443 			// channel[nr] <= irqnr
444 			ti_pruss_map_write(sc, PRUSS_INTC_HMR_BASE,
445 				irq, channelnr);
446 		}
447 	}
448 
449 	return (err);
450 }
451 
452 static int
453 ti_pruss_interrupt_enable(SYSCTL_HANDLER_ARGS)
454 {
455 	struct ti_pruss_softc *sc;
456 	int err;
457 	bool irqenable;
458 	const int8_t irq = arg2;
459 
460 	sc = arg1;
461 	irqenable = sc->sc_irq_devs[arg2].enable;
462 
463 	err = sysctl_handle_bool(oidp, &irqenable, arg2, req);
464 	if (err != 0)
465 		return (err);
466 
467 	if (req->newptr) // write enable
468 		return ti_pruss_interrupts_enable(sc, irq, irqenable);
469 
470 	return (err);
471 }
472 
473 static int
474 ti_pruss_global_interrupt_enable(SYSCTL_HANDLER_ARGS)
475 {
476 	struct ti_pruss_softc *sc;
477 	int err;
478 	bool glob_irqen;
479 
480 	sc = arg1;
481 	glob_irqen = sc->sc_glob_irqen;
482 
483 	err = sysctl_handle_bool(oidp, &glob_irqen, arg2, req);
484 	if (err != 0)
485 		return (err);
486 
487 	if (req->newptr) {
488 		sc->sc_glob_irqen = glob_irqen;
489 		ti_pruss_reg_write(sc, PRUSS_INTC_GER, glob_irqen);
490 	}
491 
492 	return (err);
493 }
494 static int
495 ti_pruss_probe(device_t dev)
496 {
497 
498 	if (!ofw_bus_status_okay(dev))
499 		return (ENXIO);
500 
501 	if (ofw_bus_is_compatible(dev, "ti,pruss-v1") ||
502 	    ofw_bus_is_compatible(dev, "ti,pruss-v2")) {
503 		device_set_desc(dev, "TI Programmable Realtime Unit Subsystem");
504 		return (BUS_PROBE_DEFAULT);
505 	}
506 
507 	return (ENXIO);
508 }
509 
510 static int
511 ti_pruss_attach(device_t dev)
512 {
513 	struct ti_pruss_softc *sc;
514 	int rid, i;
515 
516 	if (ti_prcm_clk_enable(PRUSS_CLK) != 0) {
517 		device_printf(dev, "could not enable PRUSS clock\n");
518 		return (ENXIO);
519 	}
520 	sc = device_get_softc(dev);
521 	rid = 0;
522 	mtx_init(&sc->sc_mtx, "TI PRUSS", NULL, MTX_DEF);
523 	sc->sc_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
524 	    RF_ACTIVE);
525 	if (sc->sc_mem_res == NULL) {
526 		device_printf(dev, "could not allocate memory resource\n");
527 		return (ENXIO);
528 	}
529 
530 	struct sysctl_ctx_list *clist = device_get_sysctl_ctx(dev);
531 	if (!clist)
532 		return (EINVAL);
533 
534 	struct sysctl_oid *poid;
535 	poid = device_get_sysctl_tree( dev );
536 	if (!poid)
537 		return (EINVAL);
538 
539 	sc->sc_glob_irqen = false;
540 	struct sysctl_oid *irq_root = SYSCTL_ADD_NODE(clist, SYSCTL_CHILDREN(poid),
541 	    OID_AUTO, "irq", CTLFLAG_RD, 0,
542 	    "PRUSS Host Interrupts");
543 	SYSCTL_ADD_PROC(clist, SYSCTL_CHILDREN(poid), OID_AUTO,
544 	    "global_interrupt_enable", CTLFLAG_RW | CTLTYPE_U8,
545 	    sc, 0, ti_pruss_global_interrupt_enable,
546 	    "CU", "Global interrupt enable");
547 
548 	sc->sc_bt = rman_get_bustag(sc->sc_mem_res);
549 	sc->sc_bh = rman_get_bushandle(sc->sc_mem_res);
550 	if (bus_alloc_resources(dev, ti_pruss_irq_spec, sc->sc_irq_res) != 0) {
551 		device_printf(dev, "could not allocate interrupt resource\n");
552 		ti_pruss_detach(dev);
553 		return (ENXIO);
554 	}
555 
556 	ti_pruss_interrupts_clear(sc);
557 
558 	for (i = 0; i < TI_PRUSS_IRQS; i++) {
559 		char name[8];
560 		snprintf(name, sizeof(name), "%d", i);
561 
562 		struct sysctl_oid *irq_nodes = SYSCTL_ADD_NODE(clist, SYSCTL_CHILDREN(irq_root),
563 		    OID_AUTO, name, CTLFLAG_RD, 0,
564 		    "PRUSS Interrupts");
565 		SYSCTL_ADD_PROC(clist, SYSCTL_CHILDREN(irq_nodes), OID_AUTO,
566 		    "channel", CTLFLAG_RW | CTLTYPE_STRING, sc, i, ti_pruss_channel_map,
567 		    "A", "Channel attached to this irq");
568 		SYSCTL_ADD_PROC(clist, SYSCTL_CHILDREN(irq_nodes), OID_AUTO,
569 		    "event", CTLFLAG_RW | CTLTYPE_STRING, sc, i, ti_pruss_event_map,
570 		    "A", "Event attached to this irq");
571 		SYSCTL_ADD_PROC(clist, SYSCTL_CHILDREN(irq_nodes), OID_AUTO,
572 		    "enable", CTLFLAG_RW | CTLTYPE_U8, sc, i, ti_pruss_interrupt_enable,
573 		    "CU", "Enable/Disable interrupt");
574 
575 		sc->sc_irq_devs[i].event = -1;
576 		sc->sc_irq_devs[i].channel = -1;
577 		sc->sc_irq_devs[i].tstamps.ctl.idx = 0;
578 
579 		if (i < TI_PRUSS_HOST_IRQS) {
580 			ti_pruss_irq_args[i].irq = i;
581 			ti_pruss_irq_args[i].sc = sc;
582 			if (bus_setup_intr(dev, sc->sc_irq_res[i],
583 			    INTR_MPSAFE | INTR_TYPE_MISC,
584 			    NULL, ti_pruss_intr, &ti_pruss_irq_args[i],
585 			    &sc->sc_intr[i]) != 0) {
586 				device_printf(dev,
587 				    "unable to setup the interrupt handler\n");
588 				ti_pruss_detach(dev);
589 
590 				return (ENXIO);
591 			}
592 			mtx_init(&sc->sc_irq_devs[i].sc_mtx, "TI PRUSS IRQ", NULL, MTX_DEF);
593 			knlist_init_mtx(&sc->sc_irq_devs[i].sc_selinfo.si_note, &sc->sc_irq_devs[i].sc_mtx);
594 		}
595 	}
596 
597 	if (ti_pruss_reg_read(sc, PRUSS_AM33XX_INTC) == PRUSS_AM33XX_REV)
598 		device_printf(dev, "AM33xx PRU-ICSS\n");
599 
600 	sc->sc_pdev = make_dev(&ti_pruss_cdevsw, 0, UID_ROOT, GID_WHEEL,
601 	    0600, "pruss%d", device_get_unit(dev));
602 	sc->sc_pdev->si_drv1 = dev;
603 
604 	/*  Acc. to datasheet always write 1 to polarity registers */
605 	ti_pruss_reg_write(sc, PRUSS_INTC_SIPR0, 0xFFFFFFFF);
606 	ti_pruss_reg_write(sc, PRUSS_INTC_SIPR1, 0xFFFFFFFF);
607 
608 	/* Acc. to datasheet always write 0 to event type registers */
609 	ti_pruss_reg_write(sc, PRUSS_INTC_SITR0, 0);
610 	ti_pruss_reg_write(sc, PRUSS_INTC_SITR1, 0);
611 
612 	return (0);
613 }
614 
615 static int
616 ti_pruss_detach(device_t dev)
617 {
618 	struct ti_pruss_softc *sc = device_get_softc(dev);
619 
620 	ti_pruss_interrupts_clear(sc);
621 
622 	for (int i = 0; i < TI_PRUSS_HOST_IRQS; i++) {
623 		ti_pruss_interrupts_enable( sc, i, false );
624 
625 		if (sc->sc_intr[i])
626 			bus_teardown_intr(dev, sc->sc_irq_res[i], sc->sc_intr[i]);
627 		if (sc->sc_irq_res[i])
628 			bus_release_resource(dev, SYS_RES_IRQ,
629 			    rman_get_rid(sc->sc_irq_res[i]),
630 			    sc->sc_irq_res[i]);
631 		knlist_clear(&sc->sc_irq_devs[i].sc_selinfo.si_note, 0);
632 		mtx_lock(&sc->sc_irq_devs[i].sc_mtx);
633 		if (!knlist_empty(&sc->sc_irq_devs[i].sc_selinfo.si_note))
634 			printf("IRQ %d KQueue not empty!\n", i );
635 		mtx_unlock(&sc->sc_irq_devs[i].sc_mtx);
636 		knlist_destroy(&sc->sc_irq_devs[i].sc_selinfo.si_note);
637 		mtx_destroy(&sc->sc_irq_devs[i].sc_mtx);
638 	}
639 
640 	mtx_destroy(&sc->sc_mtx);
641 	if (sc->sc_mem_res)
642 		bus_release_resource(dev, SYS_RES_MEMORY, rman_get_rid(sc->sc_mem_res),
643 		    sc->sc_mem_res);
644 	if (sc->sc_pdev)
645 		destroy_dev(sc->sc_pdev);
646 
647 	return (0);
648 }
649 
650 static void
651 ti_pruss_intr(void *arg)
652 {
653 	int val;
654 	struct ti_pruss_irq_arg *iap = arg;
655 	struct ti_pruss_softc *sc = iap->sc;
656 	/*
657 	 * Interrupts pr1_host_intr[0:7] are mapped to
658 	 * Host-2 to Host-9 of PRU-ICSS IRQ-controller.
659 	 */
660 	const int pru_int = iap->irq + TI_PRUSS_PRU_IRQS;
661 	const int pru_int_mask = (1 << pru_int);
662 	const int pru_channel = sc->sc_irq_devs[pru_int].channel;
663 	const int pru_event = sc->sc_irq_devs[pru_channel].event;
664 
665 	val = ti_pruss_reg_read(sc, PRUSS_INTC_HIER);
666 	if (!(val & pru_int_mask))
667 		return;
668 
669 	ti_pruss_reg_write(sc, PRUSS_INTC_HIDISR, pru_int);
670 	ti_pruss_reg_write(sc, PRUSS_INTC_SICR, pru_event);
671 	ti_pruss_reg_write(sc, PRUSS_INTC_HIEISR, pru_int);
672 
673 	struct ti_pruss_irqsc* irq = &sc->sc_irq_devs[pru_channel];
674 	size_t wr = irq->tstamps.ctl.idx;
675 
676 	struct timespec ts;
677 	nanouptime(&ts);
678 	irq->tstamps.ts[wr] = ts.tv_sec * 1000000000 + ts.tv_nsec;
679 
680 	if (++wr == TI_TS_ARRAY)
681 		wr = 0;
682 	atomic_add_32(&irq->tstamps.ctl.cnt, 1);
683 
684 	irq->tstamps.ctl.idx = wr;
685 
686 	KNOTE_UNLOCKED(&irq->sc_selinfo.si_note, pru_int);
687 	wakeup(irq);
688 	selwakeup(&irq->sc_selinfo);
689 }
690 
691 static int
692 ti_pruss_open(struct cdev *cdev __unused, int oflags __unused,
693     int devtype __unused, struct thread *td __unused)
694 {
695 	return (0);
696 }
697 
698 static int
699 ti_pruss_mmap(struct cdev *cdev, vm_ooffset_t offset, vm_paddr_t *paddr,
700     int nprot, vm_memattr_t *memattr)
701 {
702 	device_t dev = cdev->si_drv1;
703 	struct ti_pruss_softc *sc = device_get_softc(dev);
704 
705 	if (offset > rman_get_size(sc->sc_mem_res))
706 		return (ENOSPC);
707 	*paddr = rman_get_start(sc->sc_mem_res) + offset;
708 	*memattr = VM_MEMATTR_UNCACHEABLE;
709 
710 	return (0);
711 }
712 
713 static struct filterops ti_pruss_kq_read = {
714 	.f_isfd = 1,
715 	.f_detach = ti_pruss_irq_kqread_detach,
716 	.f_event = ti_pruss_irq_kqevent,
717 };
718 
719 static void
720 ti_pruss_irq_kqread_detach(struct knote *kn)
721 {
722 	struct ti_pruss_irqsc *sc = kn->kn_hook;
723 
724 	knlist_remove(&sc->sc_selinfo.si_note, kn, 0);
725 }
726 
727 static int
728 ti_pruss_irq_kqevent(struct knote *kn, long hint)
729 {
730     struct ti_pruss_irqsc* irq_sc;
731     int notify;
732 
733     irq_sc = kn->kn_hook;
734 
735     if (hint > 0)
736         kn->kn_data = hint - 2;
737 
738     if (hint > 0 || irq_sc->last > 0)
739         notify = 1;
740     else
741         notify = 0;
742 
743     irq_sc->last = hint;
744 
745     return (notify);
746 }
747 
748 static int
749 ti_pruss_irq_kqfilter(struct cdev *cdev, struct knote *kn)
750 {
751 	struct ti_pruss_irqsc *sc = cdev->si_drv1;
752 
753 	switch (kn->kn_filter) {
754 	case EVFILT_READ:
755 		kn->kn_hook = sc;
756 		kn->kn_fop = &ti_pruss_kq_read;
757 		knlist_add(&sc->sc_selinfo.si_note, kn, 0);
758 		break;
759 	default:
760 		return (EINVAL);
761 	}
762 
763 	return (0);
764 }
765