1258210Srpaulo/*-
2258210Srpaulo * Copyright (c) 2013 Rui Paulo <rpaulo@FreeBSD.org>
3327595Sian * Copyright (c) 2017 Manuel Stuehn
4258210Srpaulo * All rights reserved.
5258210Srpaulo *
6258210Srpaulo * Redistribution and use in source and binary forms, with or without
7258210Srpaulo * modification, are permitted provided that the following conditions
8258210Srpaulo * are met:
9258210Srpaulo * 1. Redistributions of source code must retain the above copyright
10258210Srpaulo *    notice, this list of conditions and the following disclaimer.
11258210Srpaulo * 2. Redistributions in binary form must reproduce the above copyright
12258210Srpaulo *    notice, this list of conditions and the following disclaimer in the
13258210Srpaulo *    documentation and/or other materials provided with the distribution.
14258210Srpaulo *
15258210Srpaulo * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16258210Srpaulo * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
17258210Srpaulo * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
18258210Srpaulo * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
19258210Srpaulo * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
20258210Srpaulo * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
21258210Srpaulo * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22258210Srpaulo * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
23258210Srpaulo * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
24258210Srpaulo * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
25258210Srpaulo * POSSIBILITY OF SUCH DAMAGE.
26258210Srpaulo */
27258210Srpaulo#include <sys/cdefs.h>
28258210Srpaulo__FBSDID("$FreeBSD: stable/11/sys/arm/ti/ti_pruss.c 327595 2018-01-05 19:28:13Z ian $");
29258210Srpaulo
30327595Sian#include <sys/poll.h>
31327595Sian#include <sys/time.h>
32327595Sian#include <sys/uio.h>
33258210Srpaulo#include <sys/param.h>
34258210Srpaulo#include <sys/systm.h>
35327595Sian#include <sys/fcntl.h>
36258210Srpaulo#include <sys/bus.h>
37258210Srpaulo#include <sys/conf.h>
38258210Srpaulo#include <sys/kernel.h>
39258210Srpaulo#include <sys/module.h>
40258210Srpaulo#include <sys/malloc.h>
41258210Srpaulo#include <sys/rman.h>
42327595Sian#include <sys/types.h>
43327595Sian#include <sys/sysctl.h>
44258210Srpaulo#include <sys/event.h>
45258210Srpaulo#include <sys/selinfo.h>
46258210Srpaulo#include <machine/bus.h>
47258210Srpaulo#include <machine/cpu.h>
48258210Srpaulo#include <machine/frame.h>
49258210Srpaulo#include <machine/intr.h>
50327595Sian#include <machine/atomic.h>
51258210Srpaulo
52258210Srpaulo#include <dev/fdt/fdt_common.h>
53258210Srpaulo#include <dev/ofw/openfirm.h>
54258210Srpaulo#include <dev/ofw/ofw_bus.h>
55258210Srpaulo#include <dev/ofw/ofw_bus_subr.h>
56258210Srpaulo
57258210Srpaulo#include <arm/ti/ti_prcm.h>
58258210Srpaulo#include <arm/ti/ti_pruss.h>
59258210Srpaulo
60258210Srpaulo#ifdef DEBUG
61258210Srpaulo#define	DPRINTF(fmt, ...)	do {	\
62258210Srpaulo	printf("%s: ", __func__);	\
63258210Srpaulo	printf(fmt, __VA_ARGS__);	\
64258210Srpaulo} while (0)
65258210Srpaulo#else
66258210Srpaulo#define	DPRINTF(fmt, ...)
67258210Srpaulo#endif
68258210Srpaulo
69327595Sianstatic d_open_t			ti_pruss_irq_open;
70327595Sianstatic d_read_t			ti_pruss_irq_read;
71327595Sianstatic d_poll_t			ti_pruss_irq_poll;
72327595Sian
73258210Srpaulostatic device_probe_t		ti_pruss_probe;
74258210Srpaulostatic device_attach_t		ti_pruss_attach;
75258210Srpaulostatic device_detach_t		ti_pruss_detach;
76258210Srpaulostatic void			ti_pruss_intr(void *);
77258210Srpaulostatic d_open_t			ti_pruss_open;
78258210Srpaulostatic d_mmap_t			ti_pruss_mmap;
79327595Sianstatic void 			ti_pruss_irq_kqread_detach(struct knote *);
80327595Sianstatic int 			ti_pruss_irq_kqevent(struct knote *, long);
81327595Sianstatic d_kqfilter_t		ti_pruss_irq_kqfilter;
82327595Sianstatic void			ti_pruss_privdtor(void *data);
83258210Srpaulo
84327595Sian#define	TI_PRUSS_PRU_IRQS 2
85327595Sian#define	TI_PRUSS_HOST_IRQS 8
86327595Sian#define	TI_PRUSS_IRQS (TI_PRUSS_HOST_IRQS+TI_PRUSS_PRU_IRQS)
87327595Sian#define	TI_PRUSS_EVENTS 64
88327595Sian#define	NOT_SET_STR "NONE"
89327595Sian#define	TI_TS_ARRAY 16
90283138Srpaulo
91327595Sianstruct ctl
92327595Sian{
93327595Sian	size_t cnt;
94327595Sian	size_t idx;
95327595Sian};
96327595Sian
97327595Sianstruct ts_ring_buf
98327595Sian{
99327595Sian	struct ctl ctl;
100327595Sian	uint64_t ts[TI_TS_ARRAY];
101327595Sian};
102327595Sian
103327595Sianstruct ti_pruss_irqsc
104327595Sian{
105327595Sian	struct mtx		sc_mtx;
106327595Sian	struct cdev		*sc_pdev;
107327595Sian	struct selinfo		sc_selinfo;
108327595Sian	int8_t			channel;
109327595Sian	int8_t			last;
110327595Sian	int8_t			event;
111327595Sian	bool			enable;
112327595Sian	struct ts_ring_buf	tstamps;
113327595Sian};
114327595Sian
115327595Sianstatic struct cdevsw ti_pruss_cdevirq = {
116327595Sian	.d_version =	D_VERSION,
117327595Sian	.d_name =	"ti_pruss_irq",
118327595Sian	.d_open =	ti_pruss_irq_open,
119327595Sian	.d_read =	ti_pruss_irq_read,
120327595Sian	.d_poll =	ti_pruss_irq_poll,
121327595Sian	.d_kqfilter =	ti_pruss_irq_kqfilter,
122327595Sian};
123327595Sian
124258210Srpaulostruct ti_pruss_softc {
125258210Srpaulo	struct mtx		sc_mtx;
126258210Srpaulo	struct resource 	*sc_mem_res;
127327595Sian	struct resource 	*sc_irq_res[TI_PRUSS_HOST_IRQS];
128327595Sian	void            	*sc_intr[TI_PRUSS_HOST_IRQS];
129327595Sian	struct ti_pruss_irqsc	sc_irq_devs[TI_PRUSS_IRQS];
130258210Srpaulo	bus_space_tag_t		sc_bt;
131258210Srpaulo	bus_space_handle_t	sc_bh;
132258210Srpaulo	struct cdev		*sc_pdev;
133258210Srpaulo	struct selinfo		sc_selinfo;
134327595Sian	bool			sc_glob_irqen;
135258210Srpaulo};
136258210Srpaulo
137258210Srpaulostatic struct cdevsw ti_pruss_cdevsw = {
138258210Srpaulo	.d_version =	D_VERSION,
139258210Srpaulo	.d_name =	"ti_pruss",
140258210Srpaulo	.d_open =	ti_pruss_open,
141258210Srpaulo	.d_mmap =	ti_pruss_mmap,
142258210Srpaulo};
143258210Srpaulo
144258210Srpaulostatic device_method_t ti_pruss_methods[] = {
145258210Srpaulo	DEVMETHOD(device_probe,		ti_pruss_probe),
146258210Srpaulo	DEVMETHOD(device_attach,	ti_pruss_attach),
147258210Srpaulo	DEVMETHOD(device_detach,	ti_pruss_detach),
148258210Srpaulo
149258210Srpaulo	DEVMETHOD_END
150258210Srpaulo};
151258210Srpaulo
152258210Srpaulostatic driver_t ti_pruss_driver = {
153258210Srpaulo	"ti_pruss",
154258210Srpaulo	ti_pruss_methods,
155258210Srpaulo	sizeof(struct ti_pruss_softc)
156258210Srpaulo};
157258210Srpaulo
158258210Srpaulostatic devclass_t ti_pruss_devclass;
159258210Srpaulo
160258210SrpauloDRIVER_MODULE(ti_pruss, simplebus, ti_pruss_driver, ti_pruss_devclass, 0, 0);
161327595SianMODULE_DEPEND(ti_pruss, ti_prcm, 1, 1, 1);
162258210Srpaulo
163258210Srpaulostatic struct resource_spec ti_pruss_irq_spec[] = {
164258210Srpaulo	{ SYS_RES_IRQ,	    0,  RF_ACTIVE },
165258210Srpaulo	{ SYS_RES_IRQ,	    1,  RF_ACTIVE },
166258210Srpaulo	{ SYS_RES_IRQ,	    2,  RF_ACTIVE },
167258210Srpaulo	{ SYS_RES_IRQ,	    3,  RF_ACTIVE },
168258210Srpaulo	{ SYS_RES_IRQ,	    4,  RF_ACTIVE },
169258210Srpaulo	{ SYS_RES_IRQ,	    5,  RF_ACTIVE },
170258210Srpaulo	{ SYS_RES_IRQ,	    6,  RF_ACTIVE },
171258210Srpaulo	{ SYS_RES_IRQ,	    7,  RF_ACTIVE },
172258210Srpaulo	{ -1,               0,  0 }
173258210Srpaulo};
174327595SianCTASSERT(TI_PRUSS_HOST_IRQS == nitems(ti_pruss_irq_spec) - 1);
175258210Srpaulo
176327595Sianstatic int
177327595Sianti_pruss_irq_open(struct cdev *dev, int oflags, int devtype, struct thread *td)
178327595Sian{
179327595Sian	struct ctl* irqs;
180327595Sian	struct ti_pruss_irqsc *sc;
181327595Sian	sc = dev->si_drv1;
182327595Sian
183327595Sian	irqs = malloc(sizeof(struct ctl), M_DEVBUF, M_WAITOK);
184327595Sian	if (!irqs)
185327595Sian	    return (ENOMEM);
186327595Sian
187327595Sian	irqs->cnt = sc->tstamps.ctl.cnt;
188327595Sian	irqs->idx = sc->tstamps.ctl.idx;
189327595Sian
190327595Sian	return devfs_set_cdevpriv(irqs, ti_pruss_privdtor);
191327595Sian}
192327595Sian
193327595Sianstatic void
194327595Sianti_pruss_privdtor(void *data)
195327595Sian{
196327595Sian    free(data, M_DEVBUF);
197327595Sian}
198327595Sian
199327595Sianstatic int
200327595Sianti_pruss_irq_poll(struct cdev *dev, int events, struct thread *td)
201327595Sian{
202327595Sian	struct ctl* irqs;
203327595Sian	struct ti_pruss_irqsc *sc;
204327595Sian	sc = dev->si_drv1;
205327595Sian
206327595Sian	devfs_get_cdevpriv((void**)&irqs);
207327595Sian
208327595Sian	if (events & (POLLIN | POLLRDNORM)) {
209327595Sian		if (sc->tstamps.ctl.cnt != irqs->cnt)
210327595Sian			return events & (POLLIN | POLLRDNORM);
211327595Sian		else
212327595Sian			selrecord(td, &sc->sc_selinfo);
213327595Sian	}
214327595Sian	return 0;
215327595Sian}
216327595Sian
217327595Sianstatic int
218327595Sianti_pruss_irq_read(struct cdev *cdev, struct uio *uio, int ioflag)
219327595Sian{
220327595Sian	const size_t ts_len = sizeof(uint64_t);
221327595Sian	struct ti_pruss_irqsc* irq;
222327595Sian	struct ctl* priv;
223327595Sian	int error = 0;
224327595Sian	size_t idx;
225327595Sian	ssize_t level;
226327595Sian
227327595Sian	irq = cdev->si_drv1;
228327595Sian
229327595Sian	if (uio->uio_resid < ts_len)
230327595Sian		return (EINVAL);
231327595Sian
232327595Sian	error = devfs_get_cdevpriv((void**)&priv);
233327595Sian	if (error)
234327595Sian	    return (error);
235327595Sian
236327595Sian	mtx_lock(&irq->sc_mtx);
237327595Sian
238327595Sian	if (irq->tstamps.ctl.cnt - priv->cnt > TI_TS_ARRAY)
239327595Sian	{
240327595Sian		priv->cnt = irq->tstamps.ctl.cnt;
241327595Sian		priv->idx = irq->tstamps.ctl.idx;
242327595Sian		mtx_unlock(&irq->sc_mtx);
243327595Sian		return (ENXIO);
244327595Sian	}
245327595Sian
246327595Sian	do {
247327595Sian		idx = priv->idx;
248327595Sian		level = irq->tstamps.ctl.idx - idx;
249327595Sian		if (level < 0)
250327595Sian			level += TI_TS_ARRAY;
251327595Sian
252327595Sian		if (level == 0) {
253327595Sian			if (ioflag & O_NONBLOCK) {
254327595Sian				mtx_unlock(&irq->sc_mtx);
255327595Sian				return (EWOULDBLOCK);
256327595Sian			}
257327595Sian
258327595Sian			error = msleep(irq, &irq->sc_mtx, PCATCH | PDROP,
259327595Sian				"pruirq", 0);
260327595Sian			if (error)
261327595Sian				return error;
262327595Sian
263327595Sian			mtx_lock(&irq->sc_mtx);
264327595Sian		}
265327595Sian	}while(level == 0);
266327595Sian
267327595Sian	mtx_unlock(&irq->sc_mtx);
268327595Sian
269327595Sian	error = uiomove(&irq->tstamps.ts[idx], ts_len, uio);
270327595Sian
271327595Sian	if (++idx == TI_TS_ARRAY)
272327595Sian		idx = 0;
273327595Sian	priv->idx = idx;
274327595Sian
275327595Sian	atomic_add_32(&priv->cnt, 1);
276327595Sian
277327595Sian	return (error);
278327595Sian}
279327595Sian
280258210Srpaulostatic struct ti_pruss_irq_arg {
281258210Srpaulo	int 		       irq;
282258210Srpaulo	struct ti_pruss_softc *sc;
283258210Srpaulo} ti_pruss_irq_args[TI_PRUSS_IRQS];
284258210Srpaulo
285258210Srpaulostatic __inline uint32_t
286258210Srpauloti_pruss_reg_read(struct ti_pruss_softc *sc, uint32_t reg)
287258210Srpaulo{
288258210Srpaulo	return (bus_space_read_4(sc->sc_bt, sc->sc_bh, reg));
289258210Srpaulo}
290258210Srpaulo
291258210Srpaulostatic __inline void
292258210Srpauloti_pruss_reg_write(struct ti_pruss_softc *sc, uint32_t reg, uint32_t val)
293258210Srpaulo{
294258210Srpaulo	bus_space_write_4(sc->sc_bt, sc->sc_bh, reg, val);
295258210Srpaulo}
296258210Srpaulo
297327595Sianstatic __inline void
298327595Sianti_pruss_interrupts_clear(struct ti_pruss_softc *sc)
299327595Sian{
300327595Sian	/* disable global interrupt */
301327595Sian	ti_pruss_reg_write(sc, PRUSS_INTC_GER, 0 );
302327595Sian
303327595Sian	/* clear all events */
304327595Sian	ti_pruss_reg_write(sc, PRUSS_INTC_SECR0, 0xFFFFFFFF);
305327595Sian	ti_pruss_reg_write(sc, PRUSS_INTC_SECR1, 0xFFFFFFFF);
306327595Sian
307327595Sian	/* disable all host interrupts */
308327595Sian	ti_pruss_reg_write(sc, PRUSS_INTC_HIER, 0);
309327595Sian}
310327595Sian
311327595Sianstatic __inline int
312327595Sianti_pruss_interrupts_enable(struct ti_pruss_softc *sc, int8_t irq, bool enable)
313327595Sian{
314327595Sian	if (enable && ((sc->sc_irq_devs[irq].channel == -1) ||
315327595Sian	    (sc->sc_irq_devs[irq].event== -1)))
316327595Sian	{
317327595Sian		device_printf( sc->sc_pdev->si_drv1,
318327595Sian			"Interrupt chain not fully configured, not possible to enable\n" );
319327595Sian		return (EINVAL);
320327595Sian	}
321327595Sian
322327595Sian	sc->sc_irq_devs[irq].enable = enable;
323327595Sian
324327595Sian	if (sc->sc_irq_devs[irq].sc_pdev) {
325327595Sian		destroy_dev(sc->sc_irq_devs[irq].sc_pdev);
326327595Sian		sc->sc_irq_devs[irq].sc_pdev = NULL;
327327595Sian	}
328327595Sian
329327595Sian	if (enable) {
330327595Sian		sc->sc_irq_devs[irq].sc_pdev = make_dev(&ti_pruss_cdevirq, 0, UID_ROOT, GID_WHEEL,
331327595Sian		    0600, "pruss%d.irq%d", device_get_unit(sc->sc_pdev->si_drv1), irq);
332327595Sian		sc->sc_irq_devs[irq].sc_pdev->si_drv1 = &sc->sc_irq_devs[irq];
333327595Sian
334327595Sian		sc->sc_irq_devs[irq].tstamps.ctl.idx = 0;
335327595Sian	}
336327595Sian
337327595Sian	uint32_t reg = enable ? PRUSS_INTC_HIEISR : PRUSS_INTC_HIDISR;
338327595Sian	ti_pruss_reg_write(sc, reg, sc->sc_irq_devs[irq].channel);
339327595Sian
340327595Sian	reg = enable ? PRUSS_INTC_EISR : PRUSS_INTC_EICR;
341327595Sian	ti_pruss_reg_write(sc, reg, sc->sc_irq_devs[irq].event );
342327595Sian
343327595Sian	return (0);
344327595Sian}
345327595Sian
346327595Sianstatic __inline void
347327595Sianti_pruss_map_write(struct ti_pruss_softc *sc, uint32_t basereg, uint8_t index, uint8_t content)
348327595Sian{
349327595Sian	const size_t regadr = basereg + index & ~0x03;
350327595Sian	const size_t bitpos = (index & 0x03) * 8;
351327595Sian	uint32_t rmw = ti_pruss_reg_read(sc, regadr);
352327595Sian	rmw = (rmw & ~( 0xF << bitpos)) | ( (content & 0xF) << bitpos);
353327595Sian	ti_pruss_reg_write(sc, regadr, rmw);
354327595Sian}
355327595Sian
356258210Srpaulostatic int
357327595Sianti_pruss_event_map( SYSCTL_HANDLER_ARGS )
358327595Sian{
359327595Sian	struct ti_pruss_softc *sc;
360327595Sian	const int8_t irq = arg2;
361327595Sian	int err;
362327595Sian	char event[sizeof(NOT_SET_STR)];
363327595Sian
364327595Sian	sc = arg1;
365327595Sian
366327595Sian	if(sc->sc_irq_devs[irq].event == -1)
367327595Sian		bcopy(NOT_SET_STR, event, sizeof(event));
368327595Sian	else
369327595Sian		snprintf(event, sizeof(event), "%d", sc->sc_irq_devs[irq].event);
370327595Sian
371327595Sian	err = sysctl_handle_string(oidp, event, sizeof(event), req);
372327595Sian	if(err != 0)
373327595Sian		return (err);
374327595Sian
375327595Sian	if (req->newptr) {  // write event
376327595Sian		if (strcmp(NOT_SET_STR, event) == 0) {
377327595Sian			ti_pruss_interrupts_enable(sc, irq, false);
378327595Sian			sc->sc_irq_devs[irq].event = -1;
379327595Sian		} else {
380327595Sian			if (sc->sc_irq_devs[irq].channel == -1) {
381327595Sian				device_printf( sc->sc_pdev->si_drv1,
382327595Sian					"corresponding channel not configured\n");
383327595Sian				return (ENXIO);
384327595Sian			}
385327595Sian
386327595Sian			const int8_t channelnr = sc->sc_irq_devs[irq].channel;
387327595Sian			const int8_t eventnr = strtol( event, NULL, 10 ); // TODO: check if strol is valid
388327595Sian			if (eventnr > TI_PRUSS_EVENTS || eventnr < 0) {
389327595Sian				device_printf( sc->sc_pdev->si_drv1,
390327595Sian					"Event number %d not valid (0 - %d)",
391327595Sian					channelnr, TI_PRUSS_EVENTS -1);
392327595Sian				return (EINVAL);
393327595Sian			}
394327595Sian
395327595Sian			sc->sc_irq_devs[irq].channel = channelnr;
396327595Sian			sc->sc_irq_devs[irq].event = eventnr;
397327595Sian
398327595Sian			// event[nr] <= channel
399327595Sian			ti_pruss_map_write(sc, PRUSS_INTC_CMR_BASE,
400327595Sian			    eventnr, channelnr);
401327595Sian		}
402327595Sian	}
403327595Sian	return (err);
404327595Sian}
405327595Sian
406327595Sianstatic int
407327595Sianti_pruss_channel_map(SYSCTL_HANDLER_ARGS)
408327595Sian{
409327595Sian	struct ti_pruss_softc *sc;
410327595Sian	int err;
411327595Sian	char channel[sizeof(NOT_SET_STR)];
412327595Sian	const int8_t irq = arg2;
413327595Sian
414327595Sian	sc = arg1;
415327595Sian
416327595Sian	if (sc->sc_irq_devs[irq].channel == -1)
417327595Sian		bcopy(NOT_SET_STR, channel, sizeof(channel));
418327595Sian	else
419327595Sian		snprintf(channel, sizeof(channel), "%d", sc->sc_irq_devs[irq].channel);
420327595Sian
421327595Sian	err = sysctl_handle_string(oidp, channel, sizeof(channel), req);
422327595Sian	if (err != 0)
423327595Sian		return (err);
424327595Sian
425327595Sian	if (req->newptr) { // write event
426327595Sian		if (strcmp(NOT_SET_STR, channel) == 0) {
427327595Sian			ti_pruss_interrupts_enable(sc, irq, false);
428327595Sian			ti_pruss_reg_write(sc, PRUSS_INTC_HIDISR,
429327595Sian			    sc->sc_irq_devs[irq].channel);
430327595Sian			sc->sc_irq_devs[irq].channel = -1;
431327595Sian		} else {
432327595Sian			const int8_t channelnr = strtol(channel, NULL, 10); // TODO: check if strol is valid
433327595Sian			if (channelnr > TI_PRUSS_IRQS || channelnr < 0)
434327595Sian			{
435327595Sian				device_printf(sc->sc_pdev->si_drv1,
436327595Sian					"Channel number %d not valid (0 - %d)",
437327595Sian					channelnr, TI_PRUSS_IRQS-1);
438327595Sian				return (EINVAL);
439327595Sian			}
440327595Sian
441327595Sian			sc->sc_irq_devs[irq].channel = channelnr;
442327595Sian			sc->sc_irq_devs[irq].last = -1;
443327595Sian
444327595Sian			// channel[nr] <= irqnr
445327595Sian			ti_pruss_map_write(sc, PRUSS_INTC_HMR_BASE,
446327595Sian				irq, channelnr);
447327595Sian		}
448327595Sian	}
449327595Sian
450327595Sian	return (err);
451327595Sian}
452327595Sian
453327595Sianstatic int
454327595Sianti_pruss_interrupt_enable(SYSCTL_HANDLER_ARGS)
455327595Sian{
456327595Sian	struct ti_pruss_softc *sc;
457327595Sian	int err;
458327595Sian	bool irqenable;
459327595Sian	const int8_t irq = arg2;
460327595Sian
461327595Sian	sc = arg1;
462327595Sian	irqenable = sc->sc_irq_devs[arg2].enable;
463327595Sian
464327595Sian	err = sysctl_handle_bool(oidp, &irqenable, arg2, req);
465327595Sian	if (err != 0)
466327595Sian		return (err);
467327595Sian
468327595Sian	if (req->newptr) // write enable
469327595Sian		return ti_pruss_interrupts_enable(sc, irq, irqenable);
470327595Sian
471327595Sian	return (err);
472327595Sian}
473327595Sian
474327595Sianstatic int
475327595Sianti_pruss_global_interrupt_enable(SYSCTL_HANDLER_ARGS)
476327595Sian{
477327595Sian	struct ti_pruss_softc *sc;
478327595Sian	int err;
479327595Sian	bool glob_irqen;
480327595Sian
481327595Sian	sc = arg1;
482327595Sian	glob_irqen = sc->sc_glob_irqen;
483327595Sian
484327595Sian	err = sysctl_handle_bool(oidp, &glob_irqen, arg2, req);
485327595Sian	if (err != 0)
486327595Sian		return (err);
487327595Sian
488327595Sian	if (req->newptr) {
489327595Sian		sc->sc_glob_irqen = glob_irqen;
490327595Sian		ti_pruss_reg_write(sc, PRUSS_INTC_GER, glob_irqen);
491327595Sian	}
492327595Sian
493327595Sian	return (err);
494327595Sian}
495327595Sianstatic int
496258210Srpauloti_pruss_probe(device_t dev)
497258210Srpaulo{
498261410Sian
499261410Sian	if (!ofw_bus_status_okay(dev))
500261410Sian		return (ENXIO);
501261410Sian
502258210Srpaulo	if (ofw_bus_is_compatible(dev, "ti,pruss-v1") ||
503258210Srpaulo	    ofw_bus_is_compatible(dev, "ti,pruss-v2")) {
504258210Srpaulo		device_set_desc(dev, "TI Programmable Realtime Unit Subsystem");
505258210Srpaulo		return (BUS_PROBE_DEFAULT);
506258210Srpaulo	}
507258210Srpaulo
508258210Srpaulo	return (ENXIO);
509258210Srpaulo}
510258210Srpaulo
511258210Srpaulostatic int
512258210Srpauloti_pruss_attach(device_t dev)
513258210Srpaulo{
514258210Srpaulo	struct ti_pruss_softc *sc;
515258210Srpaulo	int rid, i;
516258210Srpaulo
517258210Srpaulo	if (ti_prcm_clk_enable(PRUSS_CLK) != 0) {
518258210Srpaulo		device_printf(dev, "could not enable PRUSS clock\n");
519258210Srpaulo		return (ENXIO);
520258210Srpaulo	}
521258210Srpaulo	sc = device_get_softc(dev);
522258210Srpaulo	rid = 0;
523261211Sjmg	mtx_init(&sc->sc_mtx, "TI PRUSS", NULL, MTX_DEF);
524258210Srpaulo	sc->sc_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
525258210Srpaulo	    RF_ACTIVE);
526258210Srpaulo	if (sc->sc_mem_res == NULL) {
527258210Srpaulo		device_printf(dev, "could not allocate memory resource\n");
528258210Srpaulo		return (ENXIO);
529258210Srpaulo	}
530327595Sian
531327595Sian	struct sysctl_ctx_list *clist = device_get_sysctl_ctx(dev);
532327595Sian	if (!clist)
533327595Sian		return (EINVAL);
534327595Sian
535327595Sian	struct sysctl_oid *poid;
536327595Sian	poid = device_get_sysctl_tree( dev );
537327595Sian	if (!poid)
538327595Sian		return (EINVAL);
539327595Sian
540327595Sian	sc->sc_glob_irqen = false;
541327595Sian	struct sysctl_oid *irq_root = SYSCTL_ADD_NODE(clist, SYSCTL_CHILDREN(poid),
542327595Sian	    OID_AUTO, "irq", CTLFLAG_RD, 0,
543327595Sian	    "PRUSS Host Interrupts");
544327595Sian	SYSCTL_ADD_PROC(clist, SYSCTL_CHILDREN(poid), OID_AUTO,
545327595Sian	    "global_interrupt_enable", CTLFLAG_RW | CTLTYPE_U8,
546327595Sian	    sc, 0, ti_pruss_global_interrupt_enable,
547327595Sian	    "CU", "Global interrupt enable");
548327595Sian
549258210Srpaulo	sc->sc_bt = rman_get_bustag(sc->sc_mem_res);
550258210Srpaulo	sc->sc_bh = rman_get_bushandle(sc->sc_mem_res);
551258210Srpaulo	if (bus_alloc_resources(dev, ti_pruss_irq_spec, sc->sc_irq_res) != 0) {
552258210Srpaulo		device_printf(dev, "could not allocate interrupt resource\n");
553258210Srpaulo		ti_pruss_detach(dev);
554258210Srpaulo		return (ENXIO);
555258210Srpaulo	}
556327595Sian
557327595Sian	ti_pruss_interrupts_clear(sc);
558327595Sian
559258210Srpaulo	for (i = 0; i < TI_PRUSS_IRQS; i++) {
560327595Sian		char name[8];
561327595Sian		snprintf(name, sizeof(name), "%d", i);
562327595Sian
563327595Sian		struct sysctl_oid *irq_nodes = SYSCTL_ADD_NODE(clist, SYSCTL_CHILDREN(irq_root),
564327595Sian		    OID_AUTO, name, CTLFLAG_RD, 0,
565327595Sian		    "PRUSS Interrupts");
566327595Sian		SYSCTL_ADD_PROC(clist, SYSCTL_CHILDREN(irq_nodes), OID_AUTO,
567327595Sian		    "channel", CTLFLAG_RW | CTLTYPE_STRING, sc, i, ti_pruss_channel_map,
568327595Sian		    "A", "Channel attached to this irq");
569327595Sian		SYSCTL_ADD_PROC(clist, SYSCTL_CHILDREN(irq_nodes), OID_AUTO,
570327595Sian		    "event", CTLFLAG_RW | CTLTYPE_STRING, sc, i, ti_pruss_event_map,
571327595Sian		    "A", "Event attached to this irq");
572327595Sian		SYSCTL_ADD_PROC(clist, SYSCTL_CHILDREN(irq_nodes), OID_AUTO,
573327595Sian		    "enable", CTLFLAG_RW | CTLTYPE_U8, sc, i, ti_pruss_interrupt_enable,
574327595Sian		    "CU", "Enable/Disable interrupt");
575327595Sian
576327595Sian		sc->sc_irq_devs[i].event = -1;
577327595Sian		sc->sc_irq_devs[i].channel = -1;
578327595Sian		sc->sc_irq_devs[i].tstamps.ctl.idx = 0;
579327595Sian
580327595Sian		if (i < TI_PRUSS_HOST_IRQS) {
581327595Sian			ti_pruss_irq_args[i].irq = i;
582327595Sian			ti_pruss_irq_args[i].sc = sc;
583327595Sian			if (bus_setup_intr(dev, sc->sc_irq_res[i],
584327595Sian			    INTR_MPSAFE | INTR_TYPE_MISC,
585327595Sian			    NULL, ti_pruss_intr, &ti_pruss_irq_args[i],
586327595Sian			    &sc->sc_intr[i]) != 0) {
587327595Sian				device_printf(dev,
588327595Sian				    "unable to setup the interrupt handler\n");
589327595Sian				ti_pruss_detach(dev);
590327595Sian
591327595Sian				return (ENXIO);
592327595Sian			}
593327595Sian			mtx_init(&sc->sc_irq_devs[i].sc_mtx, "TI PRUSS IRQ", NULL, MTX_DEF);
594327595Sian			knlist_init_mtx(&sc->sc_irq_devs[i].sc_selinfo.si_note, &sc->sc_irq_devs[i].sc_mtx);
595258210Srpaulo		}
596258210Srpaulo	}
597327595Sian
598327595Sian	if (ti_pruss_reg_read(sc, PRUSS_AM33XX_INTC) == PRUSS_AM33XX_REV)
599258210Srpaulo		device_printf(dev, "AM33xx PRU-ICSS\n");
600258210Srpaulo
601258210Srpaulo	sc->sc_pdev = make_dev(&ti_pruss_cdevsw, 0, UID_ROOT, GID_WHEEL,
602258210Srpaulo	    0600, "pruss%d", device_get_unit(dev));
603258210Srpaulo	sc->sc_pdev->si_drv1 = dev;
604258210Srpaulo
605327595Sian	/*  Acc. to datasheet always write 1 to polarity registers */
606327595Sian	ti_pruss_reg_write(sc, PRUSS_INTC_SIPR0, 0xFFFFFFFF);
607327595Sian	ti_pruss_reg_write(sc, PRUSS_INTC_SIPR1, 0xFFFFFFFF);
608327595Sian
609327595Sian	/* Acc. to datasheet always write 0 to event type registers */
610327595Sian	ti_pruss_reg_write(sc, PRUSS_INTC_SITR0, 0);
611327595Sian	ti_pruss_reg_write(sc, PRUSS_INTC_SITR1, 0);
612327595Sian
613258210Srpaulo	return (0);
614258210Srpaulo}
615258210Srpaulo
616258210Srpaulostatic int
617258210Srpauloti_pruss_detach(device_t dev)
618258210Srpaulo{
619327595Sian	struct ti_pruss_softc *sc = device_get_softc(dev);
620258210Srpaulo
621327595Sian	ti_pruss_interrupts_clear(sc);
622327595Sian
623327595Sian	for (int i = 0; i < TI_PRUSS_HOST_IRQS; i++) {
624327595Sian		ti_pruss_interrupts_enable( sc, i, false );
625327595Sian
626258210Srpaulo		if (sc->sc_intr[i])
627258210Srpaulo			bus_teardown_intr(dev, sc->sc_irq_res[i], sc->sc_intr[i]);
628258210Srpaulo		if (sc->sc_irq_res[i])
629275376Srpaulo			bus_release_resource(dev, SYS_RES_IRQ,
630258210Srpaulo			    rman_get_rid(sc->sc_irq_res[i]),
631258210Srpaulo			    sc->sc_irq_res[i]);
632327595Sian		knlist_clear(&sc->sc_irq_devs[i].sc_selinfo.si_note, 0);
633327595Sian		mtx_lock(&sc->sc_irq_devs[i].sc_mtx);
634327595Sian		if (!knlist_empty(&sc->sc_irq_devs[i].sc_selinfo.si_note))
635327595Sian			printf("IRQ %d KQueue not empty!\n", i );
636327595Sian		mtx_unlock(&sc->sc_irq_devs[i].sc_mtx);
637327595Sian		knlist_destroy(&sc->sc_irq_devs[i].sc_selinfo.si_note);
638327595Sian		mtx_destroy(&sc->sc_irq_devs[i].sc_mtx);
639258210Srpaulo	}
640327595Sian
641283138Srpaulo	mtx_destroy(&sc->sc_mtx);
642258210Srpaulo	if (sc->sc_mem_res)
643258210Srpaulo		bus_release_resource(dev, SYS_RES_MEMORY, rman_get_rid(sc->sc_mem_res),
644258210Srpaulo		    sc->sc_mem_res);
645258210Srpaulo	if (sc->sc_pdev)
646258210Srpaulo		destroy_dev(sc->sc_pdev);
647258210Srpaulo
648258210Srpaulo	return (0);
649258210Srpaulo}
650258210Srpaulo
651258210Srpaulostatic void
652258210Srpauloti_pruss_intr(void *arg)
653258210Srpaulo{
654283138Srpaulo	int val;
655283138Srpaulo	struct ti_pruss_irq_arg *iap = arg;
656283138Srpaulo	struct ti_pruss_softc *sc = iap->sc;
657283138Srpaulo	/*
658327595Sian	 * Interrupts pr1_host_intr[0:7] are mapped to
659283138Srpaulo	 * Host-2 to Host-9 of PRU-ICSS IRQ-controller.
660283138Srpaulo	 */
661327595Sian	const int pru_int = iap->irq + TI_PRUSS_PRU_IRQS;
662283138Srpaulo	const int pru_int_mask = (1 << pru_int);
663327595Sian	const int pru_channel = sc->sc_irq_devs[pru_int].channel;
664327595Sian	const int pru_event = sc->sc_irq_devs[pru_channel].event;
665258210Srpaulo
666327595Sian	val = ti_pruss_reg_read(sc, PRUSS_INTC_HIER);
667283138Srpaulo	if (!(val & pru_int_mask))
668283138Srpaulo		return;
669327595Sian
670327595Sian	ti_pruss_reg_write(sc, PRUSS_INTC_HIDISR, pru_int);
671327595Sian	ti_pruss_reg_write(sc, PRUSS_INTC_SICR, pru_event);
672327595Sian	ti_pruss_reg_write(sc, PRUSS_INTC_HIEISR, pru_int);
673327595Sian
674327595Sian	struct ti_pruss_irqsc* irq = &sc->sc_irq_devs[pru_channel];
675327595Sian	size_t wr = irq->tstamps.ctl.idx;
676327595Sian
677327595Sian	struct timespec ts;
678327595Sian	nanouptime(&ts);
679327595Sian	irq->tstamps.ts[wr] = ts.tv_sec * 1000000000 + ts.tv_nsec;
680327595Sian
681327595Sian	if (++wr == TI_TS_ARRAY)
682327595Sian		wr = 0;
683327595Sian	atomic_add_32(&irq->tstamps.ctl.cnt, 1);
684327595Sian
685327595Sian	irq->tstamps.ctl.idx = wr;
686327595Sian
687327595Sian	KNOTE_UNLOCKED(&irq->sc_selinfo.si_note, pru_int);
688327595Sian	wakeup(irq);
689327595Sian	selwakeup(&irq->sc_selinfo);
690258210Srpaulo}
691258210Srpaulo
692258210Srpaulostatic int
693275376Srpauloti_pruss_open(struct cdev *cdev __unused, int oflags __unused,
694275376Srpaulo    int devtype __unused, struct thread *td __unused)
695258210Srpaulo{
696258210Srpaulo	return (0);
697258210Srpaulo}
698258210Srpaulo
699258210Srpaulostatic int
700258210Srpauloti_pruss_mmap(struct cdev *cdev, vm_ooffset_t offset, vm_paddr_t *paddr,
701258210Srpaulo    int nprot, vm_memattr_t *memattr)
702258210Srpaulo{
703258210Srpaulo	device_t dev = cdev->si_drv1;
704258210Srpaulo	struct ti_pruss_softc *sc = device_get_softc(dev);
705258210Srpaulo
706258210Srpaulo	if (offset > rman_get_size(sc->sc_mem_res))
707327595Sian		return (ENOSPC);
708258210Srpaulo	*paddr = rman_get_start(sc->sc_mem_res) + offset;
709277958Srpaulo	*memattr = VM_MEMATTR_UNCACHEABLE;
710258210Srpaulo
711258210Srpaulo	return (0);
712258210Srpaulo}
713258210Srpaulo
714258210Srpaulostatic struct filterops ti_pruss_kq_read = {
715258210Srpaulo	.f_isfd = 1,
716327595Sian	.f_detach = ti_pruss_irq_kqread_detach,
717327595Sian	.f_event = ti_pruss_irq_kqevent,
718258210Srpaulo};
719258210Srpaulo
720258210Srpaulostatic void
721327595Sianti_pruss_irq_kqread_detach(struct knote *kn)
722258210Srpaulo{
723327595Sian	struct ti_pruss_irqsc *sc = kn->kn_hook;
724258210Srpaulo
725258210Srpaulo	knlist_remove(&sc->sc_selinfo.si_note, kn, 0);
726258210Srpaulo}
727258210Srpaulo
728258210Srpaulostatic int
729327595Sianti_pruss_irq_kqevent(struct knote *kn, long hint)
730258210Srpaulo{
731327595Sian    struct ti_pruss_irqsc* irq_sc;
732327595Sian    int notify;
733258210Srpaulo
734327595Sian    irq_sc = kn->kn_hook;
735327595Sian
736327595Sian    if (hint > 0)
737327595Sian        kn->kn_data = hint - 2;
738327595Sian
739327595Sian    if (hint > 0 || irq_sc->last > 0)
740327595Sian        notify = 1;
741327595Sian    else
742327595Sian        notify = 0;
743327595Sian
744327595Sian    irq_sc->last = hint;
745327595Sian
746327595Sian    return (notify);
747258210Srpaulo}
748258210Srpaulo
749258210Srpaulostatic int
750327595Sianti_pruss_irq_kqfilter(struct cdev *cdev, struct knote *kn)
751258210Srpaulo{
752327595Sian	struct ti_pruss_irqsc *sc = cdev->si_drv1;
753258210Srpaulo
754258210Srpaulo	switch (kn->kn_filter) {
755258210Srpaulo	case EVFILT_READ:
756258210Srpaulo		kn->kn_hook = sc;
757258210Srpaulo		kn->kn_fop = &ti_pruss_kq_read;
758305572Sgonzo		knlist_add(&sc->sc_selinfo.si_note, kn, 0);
759258210Srpaulo		break;
760258210Srpaulo	default:
761258210Srpaulo		return (EINVAL);
762258210Srpaulo	}
763258210Srpaulo
764258210Srpaulo	return (0);
765258210Srpaulo}
766