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