xref: /freebsd/sys/arm/ti/ti_pruss.c (revision be82b3a0)
18cfbb9cfSRui Paulo /*-
24d846d26SWarner Losh  * SPDX-License-Identifier: BSD-2-Clause
3af3dc4a7SPedro F. Giffuni  *
48cfbb9cfSRui Paulo  * Copyright (c) 2013 Rui Paulo <rpaulo@FreeBSD.org>
58fb0ef08SIan Lepore  * Copyright (c) 2017 Manuel Stuehn
68cfbb9cfSRui Paulo  * All rights reserved.
78cfbb9cfSRui Paulo  *
88cfbb9cfSRui Paulo  * Redistribution and use in source and binary forms, with or without
98cfbb9cfSRui Paulo  * modification, are permitted provided that the following conditions
108cfbb9cfSRui Paulo  * are met:
118cfbb9cfSRui Paulo  * 1. Redistributions of source code must retain the above copyright
128cfbb9cfSRui Paulo  *    notice, this list of conditions and the following disclaimer.
138cfbb9cfSRui Paulo  * 2. Redistributions in binary form must reproduce the above copyright
148cfbb9cfSRui Paulo  *    notice, this list of conditions and the following disclaimer in the
158cfbb9cfSRui Paulo  *    documentation and/or other materials provided with the distribution.
168cfbb9cfSRui Paulo  *
178cfbb9cfSRui Paulo  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
188cfbb9cfSRui Paulo  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
198cfbb9cfSRui Paulo  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
208cfbb9cfSRui Paulo  * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
218cfbb9cfSRui Paulo  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
228cfbb9cfSRui Paulo  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
238cfbb9cfSRui Paulo  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
248cfbb9cfSRui Paulo  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
258cfbb9cfSRui Paulo  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
268cfbb9cfSRui Paulo  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
278cfbb9cfSRui Paulo  * POSSIBILITY OF SUCH DAMAGE.
288cfbb9cfSRui Paulo  */
298cfbb9cfSRui Paulo #include <sys/cdefs.h>
308fb0ef08SIan Lepore #include <sys/poll.h>
318fb0ef08SIan Lepore #include <sys/time.h>
328fb0ef08SIan Lepore #include <sys/uio.h>
338cfbb9cfSRui Paulo #include <sys/param.h>
348cfbb9cfSRui Paulo #include <sys/systm.h>
358fb0ef08SIan Lepore #include <sys/fcntl.h>
368cfbb9cfSRui Paulo #include <sys/bus.h>
378cfbb9cfSRui Paulo #include <sys/conf.h>
388cfbb9cfSRui Paulo #include <sys/kernel.h>
39e2e050c8SConrad Meyer #include <sys/lock.h>
408cfbb9cfSRui Paulo #include <sys/module.h>
418cfbb9cfSRui Paulo #include <sys/malloc.h>
42e2e050c8SConrad Meyer #include <sys/mutex.h>
438cfbb9cfSRui Paulo #include <sys/rman.h>
448fb0ef08SIan Lepore #include <sys/types.h>
458fb0ef08SIan Lepore #include <sys/sysctl.h>
468cfbb9cfSRui Paulo #include <sys/event.h>
478cfbb9cfSRui Paulo #include <sys/selinfo.h>
488cfbb9cfSRui Paulo #include <machine/bus.h>
498cfbb9cfSRui Paulo #include <machine/cpu.h>
508cfbb9cfSRui Paulo #include <machine/frame.h>
518cfbb9cfSRui Paulo #include <machine/intr.h>
528fb0ef08SIan Lepore #include <machine/atomic.h>
538cfbb9cfSRui Paulo 
548cfbb9cfSRui Paulo #include <dev/ofw/openfirm.h>
558cfbb9cfSRui Paulo #include <dev/ofw/ofw_bus.h>
568cfbb9cfSRui Paulo #include <dev/ofw/ofw_bus_subr.h>
578cfbb9cfSRui Paulo 
58be82b3a0SEmmanuel Vadot #include <dev/clk/clk.h>
590050ea24SMichal Meloun 
600050ea24SMichal Meloun #include <arm/ti/ti_sysc.h>
618cfbb9cfSRui Paulo #include <arm/ti/ti_pruss.h>
620050ea24SMichal Meloun #include <arm/ti/ti_prm.h>
638cfbb9cfSRui Paulo 
648cfbb9cfSRui Paulo #ifdef DEBUG
658cfbb9cfSRui Paulo #define	DPRINTF(fmt, ...)	do {	\
668cfbb9cfSRui Paulo 	printf("%s: ", __func__);	\
678cfbb9cfSRui Paulo 	printf(fmt, __VA_ARGS__);	\
688cfbb9cfSRui Paulo } while (0)
698cfbb9cfSRui Paulo #else
708cfbb9cfSRui Paulo #define	DPRINTF(fmt, ...)
718cfbb9cfSRui Paulo #endif
728cfbb9cfSRui Paulo 
738fb0ef08SIan Lepore static d_open_t			ti_pruss_irq_open;
748fb0ef08SIan Lepore static d_read_t			ti_pruss_irq_read;
758fb0ef08SIan Lepore static d_poll_t			ti_pruss_irq_poll;
768fb0ef08SIan Lepore 
778cfbb9cfSRui Paulo static device_probe_t		ti_pruss_probe;
788cfbb9cfSRui Paulo static device_attach_t		ti_pruss_attach;
798cfbb9cfSRui Paulo static device_detach_t		ti_pruss_detach;
808cfbb9cfSRui Paulo static void			ti_pruss_intr(void *);
818cfbb9cfSRui Paulo static d_open_t			ti_pruss_open;
828cfbb9cfSRui Paulo static d_mmap_t			ti_pruss_mmap;
838fb0ef08SIan Lepore static void 			ti_pruss_irq_kqread_detach(struct knote *);
848fb0ef08SIan Lepore static int 			ti_pruss_irq_kqevent(struct knote *, long);
858fb0ef08SIan Lepore static d_kqfilter_t		ti_pruss_irq_kqfilter;
868fb0ef08SIan Lepore static void			ti_pruss_privdtor(void *data);
878cfbb9cfSRui Paulo 
888fb0ef08SIan Lepore #define	TI_PRUSS_PRU_IRQS 2
898fb0ef08SIan Lepore #define	TI_PRUSS_HOST_IRQS 8
908fb0ef08SIan Lepore #define	TI_PRUSS_IRQS (TI_PRUSS_HOST_IRQS+TI_PRUSS_PRU_IRQS)
918fb0ef08SIan Lepore #define	TI_PRUSS_EVENTS 64
928fb0ef08SIan Lepore #define	NOT_SET_STR "NONE"
938fb0ef08SIan Lepore #define	TI_TS_ARRAY 16
948fb0ef08SIan Lepore 
958fb0ef08SIan Lepore struct ctl
968fb0ef08SIan Lepore {
978fb0ef08SIan Lepore 	size_t cnt;
988fb0ef08SIan Lepore 	size_t idx;
998fb0ef08SIan Lepore };
1008fb0ef08SIan Lepore 
1018fb0ef08SIan Lepore struct ts_ring_buf
1028fb0ef08SIan Lepore {
1038fb0ef08SIan Lepore 	struct ctl ctl;
1048fb0ef08SIan Lepore 	uint64_t ts[TI_TS_ARRAY];
1058fb0ef08SIan Lepore };
1068fb0ef08SIan Lepore 
1078fb0ef08SIan Lepore struct ti_pruss_irqsc
1088fb0ef08SIan Lepore {
1098fb0ef08SIan Lepore 	struct mtx		sc_mtx;
1108fb0ef08SIan Lepore 	struct cdev		*sc_pdev;
1118fb0ef08SIan Lepore 	struct selinfo		sc_selinfo;
1128fb0ef08SIan Lepore 	int8_t			channel;
1138fb0ef08SIan Lepore 	int8_t			last;
1148fb0ef08SIan Lepore 	int8_t			event;
1158fb0ef08SIan Lepore 	bool			enable;
1168fb0ef08SIan Lepore 	struct ts_ring_buf	tstamps;
1178fb0ef08SIan Lepore };
1188fb0ef08SIan Lepore 
1198fb0ef08SIan Lepore static struct cdevsw ti_pruss_cdevirq = {
1208fb0ef08SIan Lepore 	.d_version =	D_VERSION,
1218fb0ef08SIan Lepore 	.d_name =	"ti_pruss_irq",
1228fb0ef08SIan Lepore 	.d_open =	ti_pruss_irq_open,
1238fb0ef08SIan Lepore 	.d_read =	ti_pruss_irq_read,
1248fb0ef08SIan Lepore 	.d_poll =	ti_pruss_irq_poll,
1258fb0ef08SIan Lepore 	.d_kqfilter =	ti_pruss_irq_kqfilter,
1268fb0ef08SIan Lepore };
127c71d7e67SRui Paulo 
1288cfbb9cfSRui Paulo struct ti_pruss_softc {
1298cfbb9cfSRui Paulo 	struct mtx		sc_mtx;
1308cfbb9cfSRui Paulo 	struct resource 	*sc_mem_res;
1318fb0ef08SIan Lepore 	struct resource 	*sc_irq_res[TI_PRUSS_HOST_IRQS];
1328fb0ef08SIan Lepore 	void            	*sc_intr[TI_PRUSS_HOST_IRQS];
1338fb0ef08SIan Lepore 	struct ti_pruss_irqsc	sc_irq_devs[TI_PRUSS_IRQS];
1348cfbb9cfSRui Paulo 	bus_space_tag_t		sc_bt;
1358cfbb9cfSRui Paulo 	bus_space_handle_t	sc_bh;
1368cfbb9cfSRui Paulo 	struct cdev		*sc_pdev;
1378cfbb9cfSRui Paulo 	struct selinfo		sc_selinfo;
1388fb0ef08SIan Lepore 	bool			sc_glob_irqen;
1398cfbb9cfSRui Paulo };
1408cfbb9cfSRui Paulo 
1418cfbb9cfSRui Paulo static struct cdevsw ti_pruss_cdevsw = {
1428cfbb9cfSRui Paulo 	.d_version =	D_VERSION,
1438cfbb9cfSRui Paulo 	.d_name =	"ti_pruss",
1448cfbb9cfSRui Paulo 	.d_open =	ti_pruss_open,
1458cfbb9cfSRui Paulo 	.d_mmap =	ti_pruss_mmap,
1468cfbb9cfSRui Paulo };
1478cfbb9cfSRui Paulo 
1488cfbb9cfSRui Paulo static device_method_t ti_pruss_methods[] = {
1498cfbb9cfSRui Paulo 	DEVMETHOD(device_probe,		ti_pruss_probe),
1508cfbb9cfSRui Paulo 	DEVMETHOD(device_attach,	ti_pruss_attach),
1518cfbb9cfSRui Paulo 	DEVMETHOD(device_detach,	ti_pruss_detach),
1528cfbb9cfSRui Paulo 
1538cfbb9cfSRui Paulo 	DEVMETHOD_END
1548cfbb9cfSRui Paulo };
1558cfbb9cfSRui Paulo 
1568cfbb9cfSRui Paulo static driver_t ti_pruss_driver = {
1578cfbb9cfSRui Paulo 	"ti_pruss",
1588cfbb9cfSRui Paulo 	ti_pruss_methods,
1598cfbb9cfSRui Paulo 	sizeof(struct ti_pruss_softc)
1608cfbb9cfSRui Paulo };
1618cfbb9cfSRui Paulo 
1628537e671SJohn Baldwin DRIVER_MODULE(ti_pruss, simplebus, ti_pruss_driver, 0, 0);
1630050ea24SMichal Meloun MODULE_DEPEND(ti_pruss, ti_sysc, 1, 1, 1);
1640050ea24SMichal Meloun MODULE_DEPEND(ti_pruss, ti_prm, 1, 1, 1);
1658cfbb9cfSRui Paulo 
1668cfbb9cfSRui Paulo static struct resource_spec ti_pruss_irq_spec[] = {
1678cfbb9cfSRui Paulo 	{ SYS_RES_IRQ,	    0,  RF_ACTIVE },
1688cfbb9cfSRui Paulo 	{ SYS_RES_IRQ,	    1,  RF_ACTIVE },
1698cfbb9cfSRui Paulo 	{ SYS_RES_IRQ,	    2,  RF_ACTIVE },
1708cfbb9cfSRui Paulo 	{ SYS_RES_IRQ,	    3,  RF_ACTIVE },
1718cfbb9cfSRui Paulo 	{ SYS_RES_IRQ,	    4,  RF_ACTIVE },
1728cfbb9cfSRui Paulo 	{ SYS_RES_IRQ,	    5,  RF_ACTIVE },
1738cfbb9cfSRui Paulo 	{ SYS_RES_IRQ,	    6,  RF_ACTIVE },
1748cfbb9cfSRui Paulo 	{ SYS_RES_IRQ,	    7,  RF_ACTIVE },
1758cfbb9cfSRui Paulo 	{ -1,               0,  0 }
1768cfbb9cfSRui Paulo };
1778fb0ef08SIan Lepore CTASSERT(TI_PRUSS_HOST_IRQS == nitems(ti_pruss_irq_spec) - 1);
1788fb0ef08SIan Lepore 
1798fb0ef08SIan Lepore static int
ti_pruss_irq_open(struct cdev * dev,int oflags,int devtype,struct thread * td)1808fb0ef08SIan Lepore ti_pruss_irq_open(struct cdev *dev, int oflags, int devtype, struct thread *td)
1818fb0ef08SIan Lepore {
1828fb0ef08SIan Lepore 	struct ctl* irqs;
1838fb0ef08SIan Lepore 	struct ti_pruss_irqsc *sc;
1848fb0ef08SIan Lepore 	sc = dev->si_drv1;
1858fb0ef08SIan Lepore 
1868fb0ef08SIan Lepore 	irqs = malloc(sizeof(struct ctl), M_DEVBUF, M_WAITOK);
1878fb0ef08SIan Lepore 	if (!irqs)
1888fb0ef08SIan Lepore 	    return (ENOMEM);
1898fb0ef08SIan Lepore 
1908fb0ef08SIan Lepore 	irqs->cnt = sc->tstamps.ctl.cnt;
1918fb0ef08SIan Lepore 	irqs->idx = sc->tstamps.ctl.idx;
1928fb0ef08SIan Lepore 
1938fb0ef08SIan Lepore 	return devfs_set_cdevpriv(irqs, ti_pruss_privdtor);
1948fb0ef08SIan Lepore }
1958fb0ef08SIan Lepore 
1968fb0ef08SIan Lepore static void
ti_pruss_privdtor(void * data)1978fb0ef08SIan Lepore ti_pruss_privdtor(void *data)
1988fb0ef08SIan Lepore {
1998fb0ef08SIan Lepore     free(data, M_DEVBUF);
2008fb0ef08SIan Lepore }
2018fb0ef08SIan Lepore 
2028fb0ef08SIan Lepore static int
ti_pruss_irq_poll(struct cdev * dev,int events,struct thread * td)2038fb0ef08SIan Lepore ti_pruss_irq_poll(struct cdev *dev, int events, struct thread *td)
2048fb0ef08SIan Lepore {
2058fb0ef08SIan Lepore 	struct ctl* irqs;
2068fb0ef08SIan Lepore 	struct ti_pruss_irqsc *sc;
2078fb0ef08SIan Lepore 	sc = dev->si_drv1;
2088fb0ef08SIan Lepore 
2098fb0ef08SIan Lepore 	devfs_get_cdevpriv((void**)&irqs);
2108fb0ef08SIan Lepore 
2118fb0ef08SIan Lepore 	if (events & (POLLIN | POLLRDNORM)) {
2128fb0ef08SIan Lepore 		if (sc->tstamps.ctl.cnt != irqs->cnt)
2138fb0ef08SIan Lepore 			return events & (POLLIN | POLLRDNORM);
2148fb0ef08SIan Lepore 		else
2158fb0ef08SIan Lepore 			selrecord(td, &sc->sc_selinfo);
2168fb0ef08SIan Lepore 	}
2178fb0ef08SIan Lepore 	return 0;
2188fb0ef08SIan Lepore }
2198fb0ef08SIan Lepore 
2208fb0ef08SIan Lepore static int
ti_pruss_irq_read(struct cdev * cdev,struct uio * uio,int ioflag)2218fb0ef08SIan Lepore ti_pruss_irq_read(struct cdev *cdev, struct uio *uio, int ioflag)
2228fb0ef08SIan Lepore {
2238fb0ef08SIan Lepore 	const size_t ts_len = sizeof(uint64_t);
2248fb0ef08SIan Lepore 	struct ti_pruss_irqsc* irq;
2258fb0ef08SIan Lepore 	struct ctl* priv;
2268fb0ef08SIan Lepore 	int error = 0;
2278fb0ef08SIan Lepore 	size_t idx;
2288fb0ef08SIan Lepore 	ssize_t level;
2298fb0ef08SIan Lepore 
2308fb0ef08SIan Lepore 	irq = cdev->si_drv1;
2318fb0ef08SIan Lepore 
2328fb0ef08SIan Lepore 	if (uio->uio_resid < ts_len)
2338fb0ef08SIan Lepore 		return (EINVAL);
2348fb0ef08SIan Lepore 
2358fb0ef08SIan Lepore 	error = devfs_get_cdevpriv((void**)&priv);
2368fb0ef08SIan Lepore 	if (error)
2378fb0ef08SIan Lepore 	    return (error);
2388fb0ef08SIan Lepore 
2398fb0ef08SIan Lepore 	mtx_lock(&irq->sc_mtx);
2408fb0ef08SIan Lepore 
2418fb0ef08SIan Lepore 	if (irq->tstamps.ctl.cnt - priv->cnt > TI_TS_ARRAY)
2428fb0ef08SIan Lepore 	{
2438fb0ef08SIan Lepore 		priv->cnt = irq->tstamps.ctl.cnt;
2448fb0ef08SIan Lepore 		priv->idx = irq->tstamps.ctl.idx;
2458fb0ef08SIan Lepore 		mtx_unlock(&irq->sc_mtx);
2468fb0ef08SIan Lepore 		return (ENXIO);
2478fb0ef08SIan Lepore 	}
2488fb0ef08SIan Lepore 
2498fb0ef08SIan Lepore 	do {
2508fb0ef08SIan Lepore 		idx = priv->idx;
2518fb0ef08SIan Lepore 		level = irq->tstamps.ctl.idx - idx;
2528fb0ef08SIan Lepore 		if (level < 0)
2538fb0ef08SIan Lepore 			level += TI_TS_ARRAY;
2548fb0ef08SIan Lepore 
2558fb0ef08SIan Lepore 		if (level == 0) {
2568fb0ef08SIan Lepore 			if (ioflag & O_NONBLOCK) {
2578fb0ef08SIan Lepore 				mtx_unlock(&irq->sc_mtx);
2588fb0ef08SIan Lepore 				return (EWOULDBLOCK);
2598fb0ef08SIan Lepore 			}
2608fb0ef08SIan Lepore 
2618fb0ef08SIan Lepore 			error = msleep(irq, &irq->sc_mtx, PCATCH | PDROP,
2628fb0ef08SIan Lepore 				"pruirq", 0);
2638fb0ef08SIan Lepore 			if (error)
2648fb0ef08SIan Lepore 				return error;
2658fb0ef08SIan Lepore 
2668fb0ef08SIan Lepore 			mtx_lock(&irq->sc_mtx);
2678fb0ef08SIan Lepore 		}
2688fb0ef08SIan Lepore 	}while(level == 0);
2698fb0ef08SIan Lepore 
2708fb0ef08SIan Lepore 	mtx_unlock(&irq->sc_mtx);
2718fb0ef08SIan Lepore 
2728fb0ef08SIan Lepore 	error = uiomove(&irq->tstamps.ts[idx], ts_len, uio);
2738fb0ef08SIan Lepore 
2748fb0ef08SIan Lepore 	if (++idx == TI_TS_ARRAY)
2758fb0ef08SIan Lepore 		idx = 0;
2768fb0ef08SIan Lepore 	priv->idx = idx;
2778fb0ef08SIan Lepore 
2788fb0ef08SIan Lepore 	atomic_add_32(&priv->cnt, 1);
2798fb0ef08SIan Lepore 
2808fb0ef08SIan Lepore 	return (error);
2818fb0ef08SIan Lepore }
2828cfbb9cfSRui Paulo 
2838cfbb9cfSRui Paulo static struct ti_pruss_irq_arg {
2848cfbb9cfSRui Paulo 	int 		       irq;
2858cfbb9cfSRui Paulo 	struct ti_pruss_softc *sc;
2868cfbb9cfSRui Paulo } ti_pruss_irq_args[TI_PRUSS_IRQS];
2878cfbb9cfSRui Paulo 
2888cfbb9cfSRui Paulo static __inline uint32_t
ti_pruss_reg_read(struct ti_pruss_softc * sc,uint32_t reg)2898cfbb9cfSRui Paulo ti_pruss_reg_read(struct ti_pruss_softc *sc, uint32_t reg)
2908cfbb9cfSRui Paulo {
2918cfbb9cfSRui Paulo 	return (bus_space_read_4(sc->sc_bt, sc->sc_bh, reg));
2928cfbb9cfSRui Paulo }
2938cfbb9cfSRui Paulo 
2948cfbb9cfSRui Paulo static __inline void
ti_pruss_reg_write(struct ti_pruss_softc * sc,uint32_t reg,uint32_t val)2958cfbb9cfSRui Paulo ti_pruss_reg_write(struct ti_pruss_softc *sc, uint32_t reg, uint32_t val)
2968cfbb9cfSRui Paulo {
2978cfbb9cfSRui Paulo 	bus_space_write_4(sc->sc_bt, sc->sc_bh, reg, val);
2988cfbb9cfSRui Paulo }
2998cfbb9cfSRui Paulo 
3008fb0ef08SIan Lepore static __inline void
ti_pruss_interrupts_clear(struct ti_pruss_softc * sc)3018fb0ef08SIan Lepore ti_pruss_interrupts_clear(struct ti_pruss_softc *sc)
3028fb0ef08SIan Lepore {
3038fb0ef08SIan Lepore 	/* disable global interrupt */
3048fb0ef08SIan Lepore 	ti_pruss_reg_write(sc, PRUSS_INTC_GER, 0 );
3058fb0ef08SIan Lepore 
3068fb0ef08SIan Lepore 	/* clear all events */
3078fb0ef08SIan Lepore 	ti_pruss_reg_write(sc, PRUSS_INTC_SECR0, 0xFFFFFFFF);
3088fb0ef08SIan Lepore 	ti_pruss_reg_write(sc, PRUSS_INTC_SECR1, 0xFFFFFFFF);
3098fb0ef08SIan Lepore 
3108fb0ef08SIan Lepore 	/* disable all host interrupts */
3118fb0ef08SIan Lepore 	ti_pruss_reg_write(sc, PRUSS_INTC_HIER, 0);
3128fb0ef08SIan Lepore }
3138fb0ef08SIan Lepore 
3148fb0ef08SIan Lepore static __inline int
ti_pruss_interrupts_enable(struct ti_pruss_softc * sc,int8_t irq,bool enable)3158fb0ef08SIan Lepore ti_pruss_interrupts_enable(struct ti_pruss_softc *sc, int8_t irq, bool enable)
3168fb0ef08SIan Lepore {
3178fb0ef08SIan Lepore 	if (enable && ((sc->sc_irq_devs[irq].channel == -1) ||
3188fb0ef08SIan Lepore 	    (sc->sc_irq_devs[irq].event== -1)))
3198fb0ef08SIan Lepore 	{
3208fb0ef08SIan Lepore 		device_printf( sc->sc_pdev->si_drv1,
3218fb0ef08SIan Lepore 			"Interrupt chain not fully configured, not possible to enable\n" );
3228fb0ef08SIan Lepore 		return (EINVAL);
3238fb0ef08SIan Lepore 	}
3248fb0ef08SIan Lepore 
3258fb0ef08SIan Lepore 	sc->sc_irq_devs[irq].enable = enable;
3268fb0ef08SIan Lepore 
3278fb0ef08SIan Lepore 	if (sc->sc_irq_devs[irq].sc_pdev) {
3288fb0ef08SIan Lepore 		destroy_dev(sc->sc_irq_devs[irq].sc_pdev);
3298fb0ef08SIan Lepore 		sc->sc_irq_devs[irq].sc_pdev = NULL;
3308fb0ef08SIan Lepore 	}
3318fb0ef08SIan Lepore 
3328fb0ef08SIan Lepore 	if (enable) {
3338fb0ef08SIan Lepore 		sc->sc_irq_devs[irq].sc_pdev = make_dev(&ti_pruss_cdevirq, 0, UID_ROOT, GID_WHEEL,
3348fb0ef08SIan Lepore 		    0600, "pruss%d.irq%d", device_get_unit(sc->sc_pdev->si_drv1), irq);
3358fb0ef08SIan Lepore 		sc->sc_irq_devs[irq].sc_pdev->si_drv1 = &sc->sc_irq_devs[irq];
3368fb0ef08SIan Lepore 
3378fb0ef08SIan Lepore 		sc->sc_irq_devs[irq].tstamps.ctl.idx = 0;
3388fb0ef08SIan Lepore 	}
3398fb0ef08SIan Lepore 
3408fb0ef08SIan Lepore 	uint32_t reg = enable ? PRUSS_INTC_HIEISR : PRUSS_INTC_HIDISR;
3418fb0ef08SIan Lepore 	ti_pruss_reg_write(sc, reg, sc->sc_irq_devs[irq].channel);
3428fb0ef08SIan Lepore 
3438fb0ef08SIan Lepore 	reg = enable ? PRUSS_INTC_EISR : PRUSS_INTC_EICR;
3448fb0ef08SIan Lepore 	ti_pruss_reg_write(sc, reg, sc->sc_irq_devs[irq].event );
3458fb0ef08SIan Lepore 
3468fb0ef08SIan Lepore 	return (0);
3478fb0ef08SIan Lepore }
3488fb0ef08SIan Lepore 
3498fb0ef08SIan Lepore static __inline void
ti_pruss_map_write(struct ti_pruss_softc * sc,uint32_t basereg,uint8_t index,uint8_t content)3508fb0ef08SIan Lepore ti_pruss_map_write(struct ti_pruss_softc *sc, uint32_t basereg, uint8_t index, uint8_t content)
3518fb0ef08SIan Lepore {
3528fb0ef08SIan Lepore 	const size_t regadr = basereg + index & ~0x03;
3538fb0ef08SIan Lepore 	const size_t bitpos = (index & 0x03) * 8;
3548fb0ef08SIan Lepore 	uint32_t rmw = ti_pruss_reg_read(sc, regadr);
3558fb0ef08SIan Lepore 	rmw = (rmw & ~( 0xF << bitpos)) | ( (content & 0xF) << bitpos);
3568fb0ef08SIan Lepore 	ti_pruss_reg_write(sc, regadr, rmw);
3578fb0ef08SIan Lepore }
3588fb0ef08SIan Lepore 
3598fb0ef08SIan Lepore static int
ti_pruss_event_map(SYSCTL_HANDLER_ARGS)3608fb0ef08SIan Lepore ti_pruss_event_map( SYSCTL_HANDLER_ARGS )
3618fb0ef08SIan Lepore {
3628fb0ef08SIan Lepore 	struct ti_pruss_softc *sc;
3638fb0ef08SIan Lepore 	const int8_t irq = arg2;
3648fb0ef08SIan Lepore 	int err;
3658fb0ef08SIan Lepore 	char event[sizeof(NOT_SET_STR)];
3668fb0ef08SIan Lepore 
3678fb0ef08SIan Lepore 	sc = arg1;
3688fb0ef08SIan Lepore 
3698fb0ef08SIan Lepore 	if(sc->sc_irq_devs[irq].event == -1)
3708fb0ef08SIan Lepore 		bcopy(NOT_SET_STR, event, sizeof(event));
3718fb0ef08SIan Lepore 	else
3728fb0ef08SIan Lepore 		snprintf(event, sizeof(event), "%d", sc->sc_irq_devs[irq].event);
3738fb0ef08SIan Lepore 
3748fb0ef08SIan Lepore 	err = sysctl_handle_string(oidp, event, sizeof(event), req);
3758fb0ef08SIan Lepore 	if(err != 0)
3768fb0ef08SIan Lepore 		return (err);
3778fb0ef08SIan Lepore 
3788fb0ef08SIan Lepore 	if (req->newptr) {  // write event
3798fb0ef08SIan Lepore 		if (strcmp(NOT_SET_STR, event) == 0) {
3808fb0ef08SIan Lepore 			ti_pruss_interrupts_enable(sc, irq, false);
3818fb0ef08SIan Lepore 			sc->sc_irq_devs[irq].event = -1;
3828fb0ef08SIan Lepore 		} else {
3838fb0ef08SIan Lepore 			if (sc->sc_irq_devs[irq].channel == -1) {
3848fb0ef08SIan Lepore 				device_printf( sc->sc_pdev->si_drv1,
3858fb0ef08SIan Lepore 					"corresponding channel not configured\n");
3868fb0ef08SIan Lepore 				return (ENXIO);
3878fb0ef08SIan Lepore 			}
3888fb0ef08SIan Lepore 
3898fb0ef08SIan Lepore 			const int8_t channelnr = sc->sc_irq_devs[irq].channel;
3908fb0ef08SIan Lepore 			const int8_t eventnr = strtol( event, NULL, 10 ); // TODO: check if strol is valid
3918fb0ef08SIan Lepore 			if (eventnr > TI_PRUSS_EVENTS || eventnr < 0) {
3928fb0ef08SIan Lepore 				device_printf( sc->sc_pdev->si_drv1,
3938fb0ef08SIan Lepore 					"Event number %d not valid (0 - %d)",
3948fb0ef08SIan Lepore 					channelnr, TI_PRUSS_EVENTS -1);
3958fb0ef08SIan Lepore 				return (EINVAL);
3968fb0ef08SIan Lepore 			}
3978fb0ef08SIan Lepore 
3988fb0ef08SIan Lepore 			sc->sc_irq_devs[irq].channel = channelnr;
3998fb0ef08SIan Lepore 			sc->sc_irq_devs[irq].event = eventnr;
4008fb0ef08SIan Lepore 
4018fb0ef08SIan Lepore 			// event[nr] <= channel
4028fb0ef08SIan Lepore 			ti_pruss_map_write(sc, PRUSS_INTC_CMR_BASE,
4038fb0ef08SIan Lepore 			    eventnr, channelnr);
4048fb0ef08SIan Lepore 		}
4058fb0ef08SIan Lepore 	}
4068fb0ef08SIan Lepore 	return (err);
4078fb0ef08SIan Lepore }
4088fb0ef08SIan Lepore 
4098fb0ef08SIan Lepore static int
ti_pruss_channel_map(SYSCTL_HANDLER_ARGS)4108fb0ef08SIan Lepore ti_pruss_channel_map(SYSCTL_HANDLER_ARGS)
4118fb0ef08SIan Lepore {
4128fb0ef08SIan Lepore 	struct ti_pruss_softc *sc;
4138fb0ef08SIan Lepore 	int err;
4148fb0ef08SIan Lepore 	char channel[sizeof(NOT_SET_STR)];
4158fb0ef08SIan Lepore 	const int8_t irq = arg2;
4168fb0ef08SIan Lepore 
4178fb0ef08SIan Lepore 	sc = arg1;
4188fb0ef08SIan Lepore 
4198fb0ef08SIan Lepore 	if (sc->sc_irq_devs[irq].channel == -1)
4208fb0ef08SIan Lepore 		bcopy(NOT_SET_STR, channel, sizeof(channel));
4218fb0ef08SIan Lepore 	else
4228fb0ef08SIan Lepore 		snprintf(channel, sizeof(channel), "%d", sc->sc_irq_devs[irq].channel);
4238fb0ef08SIan Lepore 
4248fb0ef08SIan Lepore 	err = sysctl_handle_string(oidp, channel, sizeof(channel), req);
4258fb0ef08SIan Lepore 	if (err != 0)
4268fb0ef08SIan Lepore 		return (err);
4278fb0ef08SIan Lepore 
4288fb0ef08SIan Lepore 	if (req->newptr) { // write event
4298fb0ef08SIan Lepore 		if (strcmp(NOT_SET_STR, channel) == 0) {
4308fb0ef08SIan Lepore 			ti_pruss_interrupts_enable(sc, irq, false);
4318fb0ef08SIan Lepore 			ti_pruss_reg_write(sc, PRUSS_INTC_HIDISR,
4328fb0ef08SIan Lepore 			    sc->sc_irq_devs[irq].channel);
4338fb0ef08SIan Lepore 			sc->sc_irq_devs[irq].channel = -1;
4348fb0ef08SIan Lepore 		} else {
4358fb0ef08SIan Lepore 			const int8_t channelnr = strtol(channel, NULL, 10); // TODO: check if strol is valid
4368fb0ef08SIan Lepore 			if (channelnr > TI_PRUSS_IRQS || channelnr < 0)
4378fb0ef08SIan Lepore 			{
4388fb0ef08SIan Lepore 				device_printf(sc->sc_pdev->si_drv1,
4398fb0ef08SIan Lepore 					"Channel number %d not valid (0 - %d)",
4408fb0ef08SIan Lepore 					channelnr, TI_PRUSS_IRQS-1);
4418fb0ef08SIan Lepore 				return (EINVAL);
4428fb0ef08SIan Lepore 			}
4438fb0ef08SIan Lepore 
4448fb0ef08SIan Lepore 			sc->sc_irq_devs[irq].channel = channelnr;
4458fb0ef08SIan Lepore 			sc->sc_irq_devs[irq].last = -1;
4468fb0ef08SIan Lepore 
4478fb0ef08SIan Lepore 			// channel[nr] <= irqnr
4488fb0ef08SIan Lepore 			ti_pruss_map_write(sc, PRUSS_INTC_HMR_BASE,
4498fb0ef08SIan Lepore 				irq, channelnr);
4508fb0ef08SIan Lepore 		}
4518fb0ef08SIan Lepore 	}
4528fb0ef08SIan Lepore 
4538fb0ef08SIan Lepore 	return (err);
4548fb0ef08SIan Lepore }
4558fb0ef08SIan Lepore 
4568fb0ef08SIan Lepore static int
ti_pruss_interrupt_enable(SYSCTL_HANDLER_ARGS)4578fb0ef08SIan Lepore ti_pruss_interrupt_enable(SYSCTL_HANDLER_ARGS)
4588fb0ef08SIan Lepore {
4598fb0ef08SIan Lepore 	struct ti_pruss_softc *sc;
4608fb0ef08SIan Lepore 	int err;
4618fb0ef08SIan Lepore 	bool irqenable;
4628fb0ef08SIan Lepore 	const int8_t irq = arg2;
4638fb0ef08SIan Lepore 
4648fb0ef08SIan Lepore 	sc = arg1;
4658fb0ef08SIan Lepore 	irqenable = sc->sc_irq_devs[arg2].enable;
4668fb0ef08SIan Lepore 
4678fb0ef08SIan Lepore 	err = sysctl_handle_bool(oidp, &irqenable, arg2, req);
4688fb0ef08SIan Lepore 	if (err != 0)
4698fb0ef08SIan Lepore 		return (err);
4708fb0ef08SIan Lepore 
4718fb0ef08SIan Lepore 	if (req->newptr) // write enable
4728fb0ef08SIan Lepore 		return ti_pruss_interrupts_enable(sc, irq, irqenable);
4738fb0ef08SIan Lepore 
4748fb0ef08SIan Lepore 	return (err);
4758fb0ef08SIan Lepore }
4768fb0ef08SIan Lepore 
4778fb0ef08SIan Lepore static int
ti_pruss_global_interrupt_enable(SYSCTL_HANDLER_ARGS)4788fb0ef08SIan Lepore ti_pruss_global_interrupt_enable(SYSCTL_HANDLER_ARGS)
4798fb0ef08SIan Lepore {
4808fb0ef08SIan Lepore 	struct ti_pruss_softc *sc;
4818fb0ef08SIan Lepore 	int err;
4828fb0ef08SIan Lepore 	bool glob_irqen;
4838fb0ef08SIan Lepore 
4848fb0ef08SIan Lepore 	sc = arg1;
4858fb0ef08SIan Lepore 	glob_irqen = sc->sc_glob_irqen;
4868fb0ef08SIan Lepore 
4878fb0ef08SIan Lepore 	err = sysctl_handle_bool(oidp, &glob_irqen, arg2, req);
4888fb0ef08SIan Lepore 	if (err != 0)
4898fb0ef08SIan Lepore 		return (err);
4908fb0ef08SIan Lepore 
4918fb0ef08SIan Lepore 	if (req->newptr) {
4928fb0ef08SIan Lepore 		sc->sc_glob_irqen = glob_irqen;
4938fb0ef08SIan Lepore 		ti_pruss_reg_write(sc, PRUSS_INTC_GER, glob_irqen);
4948fb0ef08SIan Lepore 	}
4958fb0ef08SIan Lepore 
4968fb0ef08SIan Lepore 	return (err);
4978fb0ef08SIan Lepore }
4988cfbb9cfSRui Paulo static int
ti_pruss_probe(device_t dev)4998cfbb9cfSRui Paulo ti_pruss_probe(device_t dev)
5008cfbb9cfSRui Paulo {
501add35ed5SIan Lepore 
502add35ed5SIan Lepore 	if (!ofw_bus_status_okay(dev))
503add35ed5SIan Lepore 		return (ENXIO);
504add35ed5SIan Lepore 
5058cfbb9cfSRui Paulo 	if (ofw_bus_is_compatible(dev, "ti,pruss-v1") ||
5068cfbb9cfSRui Paulo 	    ofw_bus_is_compatible(dev, "ti,pruss-v2")) {
5078cfbb9cfSRui Paulo 		device_set_desc(dev, "TI Programmable Realtime Unit Subsystem");
5088cfbb9cfSRui Paulo 		return (BUS_PROBE_DEFAULT);
5098cfbb9cfSRui Paulo 	}
5108cfbb9cfSRui Paulo 
5118cfbb9cfSRui Paulo 	return (ENXIO);
5128cfbb9cfSRui Paulo }
5138cfbb9cfSRui Paulo 
5148cfbb9cfSRui Paulo static int
ti_pruss_attach(device_t dev)5158cfbb9cfSRui Paulo ti_pruss_attach(device_t dev)
5168cfbb9cfSRui Paulo {
5178cfbb9cfSRui Paulo 	struct ti_pruss_softc *sc;
5180050ea24SMichal Meloun 	int rid, i, err, ncells;
5190050ea24SMichal Meloun 	phandle_t node;
5200050ea24SMichal Meloun 	clk_t l3_gclk, pruss_ocp_gclk;
5210050ea24SMichal Meloun 	phandle_t ti_prm_ref, *cells;
5220050ea24SMichal Meloun         device_t ti_prm_dev;
5238cfbb9cfSRui Paulo 
5240050ea24SMichal Meloun 	rid = 0;
5250050ea24SMichal Meloun 	sc = device_get_softc(dev);
5260050ea24SMichal Meloun 	node = ofw_bus_get_node(device_get_parent(dev));
5270050ea24SMichal Meloun 	if (node <= 0) {
5280050ea24SMichal Meloun 		device_printf(dev, "Cant get ofw node\n");
5298cfbb9cfSRui Paulo 		return (ENXIO);
5308cfbb9cfSRui Paulo 	}
5310050ea24SMichal Meloun 
5320050ea24SMichal Meloun 	/*
5330050ea24SMichal Meloun 	 * Follow activate pattern from sys/arm/ti/am335x/am335x_prcm.c
5340050ea24SMichal Meloun 	 * by Damjan Marion
5350050ea24SMichal Meloun 	 */
5360050ea24SMichal Meloun 
5370050ea24SMichal Meloun 	/* Set MODULEMODE to ENABLE(2) */
5380050ea24SMichal Meloun 	/* Wait for MODULEMODE to become ENABLE(2) */
5390050ea24SMichal Meloun 	if (ti_sysc_clock_enable(device_get_parent(dev)) != 0) {
5400050ea24SMichal Meloun 		device_printf(dev, "Could not enable PRUSS clock\n");
5410050ea24SMichal Meloun 		return (ENXIO);
5420050ea24SMichal Meloun 	}
5430050ea24SMichal Meloun 
5440050ea24SMichal Meloun 	/* Set CLKTRCTRL to SW_WKUP(2) */
5450050ea24SMichal Meloun 	/* Wait for the 200 MHz OCP clock to become active */
5460050ea24SMichal Meloun 	/* Wait for the 200 MHz IEP clock to become active */
5470050ea24SMichal Meloun 	/* Wait for the 192 MHz UART clock to become active */
5480050ea24SMichal Meloun 	/*
5490050ea24SMichal Meloun 	 * At the moment there is no reference to CM_PER_PRU_ICSS_CLKSTCTRL@140
5500050ea24SMichal Meloun 	 * in the devicetree. The register reset state are SW_WKUP(2) as default
5510050ea24SMichal Meloun 	 * so at the moment ignore setting this register.
5520050ea24SMichal Meloun 	 */
5530050ea24SMichal Meloun 
5540050ea24SMichal Meloun 	/* Select L3F as OCP clock */
5550050ea24SMichal Meloun 	/* Get the clock and set the parent */
5560050ea24SMichal Meloun 	err = clk_get_by_name(dev, "l3_gclk", &l3_gclk);
5570050ea24SMichal Meloun 	if (err) {
5580050ea24SMichal Meloun 		device_printf(dev, "Cant get l3_gclk err %d\n", err);
5590050ea24SMichal Meloun 		return (ENXIO);
5600050ea24SMichal Meloun 	}
5610050ea24SMichal Meloun 
5620050ea24SMichal Meloun 	err = clk_get_by_name(dev, "pruss_ocp_gclk@530", &pruss_ocp_gclk);
5630050ea24SMichal Meloun 	if (err) {
5640050ea24SMichal Meloun 		device_printf(dev, "Cant get pruss_ocp_gclk@530 err %d\n", err);
5650050ea24SMichal Meloun 		return (ENXIO);
5660050ea24SMichal Meloun 	}
5670050ea24SMichal Meloun 
5680050ea24SMichal Meloun 	err = clk_set_parent_by_clk(pruss_ocp_gclk, l3_gclk);
5690050ea24SMichal Meloun 	if (err) {
5700050ea24SMichal Meloun 		device_printf(dev,
5710050ea24SMichal Meloun 		    "Cant set pruss_ocp_gclk parent to l3_gclk err %d\n", err);
5720050ea24SMichal Meloun 		return (ENXIO);
5730050ea24SMichal Meloun 	}
5740050ea24SMichal Meloun 
5750050ea24SMichal Meloun 	/* Clear the RESET bit */
5760050ea24SMichal Meloun 	/* Find the ti_prm */
5770050ea24SMichal Meloun 	/* #reset-cells should not been used in this way but... */
5780050ea24SMichal Meloun 	err = ofw_bus_parse_xref_list_alloc(node, "resets", "#reset-cells", 0,
5790050ea24SMichal Meloun 	    &ti_prm_ref, &ncells, &cells);
5800050ea24SMichal Meloun 	OF_prop_free(cells);
5810050ea24SMichal Meloun 	if (err) {
5820050ea24SMichal Meloun 		device_printf(dev,
5830050ea24SMichal Meloun 		    "Cant fetch \"resets\" reference %x\n", err);
5840050ea24SMichal Meloun 		return (ENXIO);
5850050ea24SMichal Meloun 	}
5860050ea24SMichal Meloun 
5870050ea24SMichal Meloun 	ti_prm_dev = OF_device_from_xref(ti_prm_ref);
5880050ea24SMichal Meloun 	if (ti_prm_dev == NULL) {
5890050ea24SMichal Meloun 		device_printf(dev, "Cant get device from \"resets\"\n");
5900050ea24SMichal Meloun 		return (ENXIO);
5910050ea24SMichal Meloun 	}
5920050ea24SMichal Meloun 
5930050ea24SMichal Meloun 	err = ti_prm_reset(ti_prm_dev);
5940050ea24SMichal Meloun 	if (err) {
5950050ea24SMichal Meloun 		device_printf(dev, "ti_prm_reset failed %d\n", err);
5960050ea24SMichal Meloun 		return (ENXIO);
5970050ea24SMichal Meloun 	}
5980050ea24SMichal Meloun 	/* End of clock activation */
5990050ea24SMichal Meloun 
6006eebe850SJohn-Mark Gurney 	mtx_init(&sc->sc_mtx, "TI PRUSS", NULL, MTX_DEF);
6018cfbb9cfSRui Paulo 	sc->sc_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
6028cfbb9cfSRui Paulo 	    RF_ACTIVE);
6038cfbb9cfSRui Paulo 	if (sc->sc_mem_res == NULL) {
6048cfbb9cfSRui Paulo 		device_printf(dev, "could not allocate memory resource\n");
6058cfbb9cfSRui Paulo 		return (ENXIO);
6068cfbb9cfSRui Paulo 	}
6078fb0ef08SIan Lepore 
6088fb0ef08SIan Lepore 	struct sysctl_ctx_list *clist = device_get_sysctl_ctx(dev);
6098fb0ef08SIan Lepore 	if (!clist)
6108fb0ef08SIan Lepore 		return (EINVAL);
6118fb0ef08SIan Lepore 
6128fb0ef08SIan Lepore 	struct sysctl_oid *poid;
6138fb0ef08SIan Lepore 	poid = device_get_sysctl_tree( dev );
6148fb0ef08SIan Lepore 	if (!poid)
6158fb0ef08SIan Lepore 		return (EINVAL);
6168fb0ef08SIan Lepore 
6178fb0ef08SIan Lepore 	sc->sc_glob_irqen = false;
6188fb0ef08SIan Lepore 	struct sysctl_oid *irq_root = SYSCTL_ADD_NODE(clist, SYSCTL_CHILDREN(poid),
6197029da5cSPawel Biernacki 	    OID_AUTO, "irq", CTLFLAG_RD | CTLFLAG_MPSAFE, 0,
6208fb0ef08SIan Lepore 	    "PRUSS Host Interrupts");
6218fb0ef08SIan Lepore 	SYSCTL_ADD_PROC(clist, SYSCTL_CHILDREN(poid), OID_AUTO,
6227029da5cSPawel Biernacki 	    "global_interrupt_enable",
6237029da5cSPawel Biernacki 	    CTLFLAG_RW | CTLTYPE_U8 | CTLFLAG_NEEDGIANT,
6248fb0ef08SIan Lepore 	    sc, 0, ti_pruss_global_interrupt_enable,
6258fb0ef08SIan Lepore 	    "CU", "Global interrupt enable");
6268fb0ef08SIan Lepore 
6278cfbb9cfSRui Paulo 	sc->sc_bt = rman_get_bustag(sc->sc_mem_res);
6288cfbb9cfSRui Paulo 	sc->sc_bh = rman_get_bushandle(sc->sc_mem_res);
6298cfbb9cfSRui Paulo 	if (bus_alloc_resources(dev, ti_pruss_irq_spec, sc->sc_irq_res) != 0) {
6308cfbb9cfSRui Paulo 		device_printf(dev, "could not allocate interrupt resource\n");
6318cfbb9cfSRui Paulo 		ti_pruss_detach(dev);
6328cfbb9cfSRui Paulo 		return (ENXIO);
6338cfbb9cfSRui Paulo 	}
6348fb0ef08SIan Lepore 
6358fb0ef08SIan Lepore 	ti_pruss_interrupts_clear(sc);
6368fb0ef08SIan Lepore 
6378cfbb9cfSRui Paulo 	for (i = 0; i < TI_PRUSS_IRQS; i++) {
6388fb0ef08SIan Lepore 		char name[8];
6398fb0ef08SIan Lepore 		snprintf(name, sizeof(name), "%d", i);
6408fb0ef08SIan Lepore 
6418fb0ef08SIan Lepore 		struct sysctl_oid *irq_nodes = SYSCTL_ADD_NODE(clist, SYSCTL_CHILDREN(irq_root),
6427029da5cSPawel Biernacki 		    OID_AUTO, name, CTLFLAG_RD | CTLFLAG_MPSAFE, 0,
6438fb0ef08SIan Lepore 		    "PRUSS Interrupts");
6448fb0ef08SIan Lepore 		SYSCTL_ADD_PROC(clist, SYSCTL_CHILDREN(irq_nodes), OID_AUTO,
6457029da5cSPawel Biernacki 		    "channel", CTLFLAG_RW | CTLTYPE_STRING | CTLFLAG_NEEDGIANT,
6467029da5cSPawel Biernacki 		    sc, i, ti_pruss_channel_map,
6478fb0ef08SIan Lepore 		    "A", "Channel attached to this irq");
6488fb0ef08SIan Lepore 		SYSCTL_ADD_PROC(clist, SYSCTL_CHILDREN(irq_nodes), OID_AUTO,
6497029da5cSPawel Biernacki 		    "event", CTLFLAG_RW | CTLTYPE_STRING | CTLFLAG_NEEDGIANT,
6507029da5cSPawel Biernacki 		    sc, i, ti_pruss_event_map,
6518fb0ef08SIan Lepore 		    "A", "Event attached to this irq");
6528fb0ef08SIan Lepore 		SYSCTL_ADD_PROC(clist, SYSCTL_CHILDREN(irq_nodes), OID_AUTO,
6537029da5cSPawel Biernacki 		    "enable", CTLFLAG_RW | CTLTYPE_U8 | CTLFLAG_NEEDGIANT,
6547029da5cSPawel Biernacki 		    sc, i, ti_pruss_interrupt_enable,
6558fb0ef08SIan Lepore 		    "CU", "Enable/Disable interrupt");
6568fb0ef08SIan Lepore 
6578fb0ef08SIan Lepore 		sc->sc_irq_devs[i].event = -1;
6588fb0ef08SIan Lepore 		sc->sc_irq_devs[i].channel = -1;
6598fb0ef08SIan Lepore 		sc->sc_irq_devs[i].tstamps.ctl.idx = 0;
6608fb0ef08SIan Lepore 
6618fb0ef08SIan Lepore 		if (i < TI_PRUSS_HOST_IRQS) {
6628cfbb9cfSRui Paulo 			ti_pruss_irq_args[i].irq = i;
6638cfbb9cfSRui Paulo 			ti_pruss_irq_args[i].sc = sc;
6648cfbb9cfSRui Paulo 			if (bus_setup_intr(dev, sc->sc_irq_res[i],
6658cfbb9cfSRui Paulo 			    INTR_MPSAFE | INTR_TYPE_MISC,
6668cfbb9cfSRui Paulo 			    NULL, ti_pruss_intr, &ti_pruss_irq_args[i],
6678cfbb9cfSRui Paulo 			    &sc->sc_intr[i]) != 0) {
6688cfbb9cfSRui Paulo 				device_printf(dev,
6698cfbb9cfSRui Paulo 				    "unable to setup the interrupt handler\n");
6708cfbb9cfSRui Paulo 				ti_pruss_detach(dev);
6718fb0ef08SIan Lepore 
6728cfbb9cfSRui Paulo 				return (ENXIO);
6738cfbb9cfSRui Paulo 			}
6748fb0ef08SIan Lepore 			mtx_init(&sc->sc_irq_devs[i].sc_mtx, "TI PRUSS IRQ", NULL, MTX_DEF);
6758fb0ef08SIan Lepore 			knlist_init_mtx(&sc->sc_irq_devs[i].sc_selinfo.si_note, &sc->sc_irq_devs[i].sc_mtx);
6768cfbb9cfSRui Paulo 		}
6778fb0ef08SIan Lepore 	}
6788fb0ef08SIan Lepore 
6798fb0ef08SIan Lepore 	if (ti_pruss_reg_read(sc, PRUSS_AM33XX_INTC) == PRUSS_AM33XX_REV)
6808cfbb9cfSRui Paulo 		device_printf(dev, "AM33xx PRU-ICSS\n");
6818cfbb9cfSRui Paulo 
6828cfbb9cfSRui Paulo 	sc->sc_pdev = make_dev(&ti_pruss_cdevsw, 0, UID_ROOT, GID_WHEEL,
6838cfbb9cfSRui Paulo 	    0600, "pruss%d", device_get_unit(dev));
6848cfbb9cfSRui Paulo 	sc->sc_pdev->si_drv1 = dev;
6858cfbb9cfSRui Paulo 
6868fb0ef08SIan Lepore 	/*  Acc. to datasheet always write 1 to polarity registers */
6878fb0ef08SIan Lepore 	ti_pruss_reg_write(sc, PRUSS_INTC_SIPR0, 0xFFFFFFFF);
6888fb0ef08SIan Lepore 	ti_pruss_reg_write(sc, PRUSS_INTC_SIPR1, 0xFFFFFFFF);
6898fb0ef08SIan Lepore 
6908fb0ef08SIan Lepore 	/* Acc. to datasheet always write 0 to event type registers */
6918fb0ef08SIan Lepore 	ti_pruss_reg_write(sc, PRUSS_INTC_SITR0, 0);
6928fb0ef08SIan Lepore 	ti_pruss_reg_write(sc, PRUSS_INTC_SITR1, 0);
6938fb0ef08SIan Lepore 
6948cfbb9cfSRui Paulo 	return (0);
6958cfbb9cfSRui Paulo }
6968cfbb9cfSRui Paulo 
6978cfbb9cfSRui Paulo static int
ti_pruss_detach(device_t dev)6988cfbb9cfSRui Paulo ti_pruss_detach(device_t dev)
6998cfbb9cfSRui Paulo {
7008fb0ef08SIan Lepore 	struct ti_pruss_softc *sc = device_get_softc(dev);
7018cfbb9cfSRui Paulo 
7028fb0ef08SIan Lepore 	ti_pruss_interrupts_clear(sc);
7038fb0ef08SIan Lepore 
7048fb0ef08SIan Lepore 	for (int i = 0; i < TI_PRUSS_HOST_IRQS; i++) {
7058fb0ef08SIan Lepore 		ti_pruss_interrupts_enable( sc, i, false );
7068fb0ef08SIan Lepore 
7078cfbb9cfSRui Paulo 		if (sc->sc_intr[i])
7088cfbb9cfSRui Paulo 			bus_teardown_intr(dev, sc->sc_irq_res[i], sc->sc_intr[i]);
7098cfbb9cfSRui Paulo 		if (sc->sc_irq_res[i])
7108cfbb9cfSRui Paulo 			bus_release_resource(dev, SYS_RES_IRQ,
7118cfbb9cfSRui Paulo 			    rman_get_rid(sc->sc_irq_res[i]),
7128cfbb9cfSRui Paulo 			    sc->sc_irq_res[i]);
7138fb0ef08SIan Lepore 		knlist_clear(&sc->sc_irq_devs[i].sc_selinfo.si_note, 0);
7148fb0ef08SIan Lepore 		mtx_lock(&sc->sc_irq_devs[i].sc_mtx);
7158fb0ef08SIan Lepore 		if (!knlist_empty(&sc->sc_irq_devs[i].sc_selinfo.si_note))
7168fb0ef08SIan Lepore 			printf("IRQ %d KQueue not empty!\n", i );
7178fb0ef08SIan Lepore 		mtx_unlock(&sc->sc_irq_devs[i].sc_mtx);
7188fb0ef08SIan Lepore 		knlist_destroy(&sc->sc_irq_devs[i].sc_selinfo.si_note);
7198fb0ef08SIan Lepore 		mtx_destroy(&sc->sc_irq_devs[i].sc_mtx);
7208cfbb9cfSRui Paulo 	}
7218fb0ef08SIan Lepore 
722c71d7e67SRui Paulo 	mtx_destroy(&sc->sc_mtx);
7238cfbb9cfSRui Paulo 	if (sc->sc_mem_res)
7248cfbb9cfSRui Paulo 		bus_release_resource(dev, SYS_RES_MEMORY, rman_get_rid(sc->sc_mem_res),
7258cfbb9cfSRui Paulo 		    sc->sc_mem_res);
7268cfbb9cfSRui Paulo 	if (sc->sc_pdev)
7278cfbb9cfSRui Paulo 		destroy_dev(sc->sc_pdev);
7288cfbb9cfSRui Paulo 
7298cfbb9cfSRui Paulo 	return (0);
7308cfbb9cfSRui Paulo }
7318cfbb9cfSRui Paulo 
7328cfbb9cfSRui Paulo static void
ti_pruss_intr(void * arg)7338cfbb9cfSRui Paulo ti_pruss_intr(void *arg)
7348cfbb9cfSRui Paulo {
735c71d7e67SRui Paulo 	int val;
736c71d7e67SRui Paulo 	struct ti_pruss_irq_arg *iap = arg;
737c71d7e67SRui Paulo 	struct ti_pruss_softc *sc = iap->sc;
738c71d7e67SRui Paulo 	/*
739c71d7e67SRui Paulo 	 * Interrupts pr1_host_intr[0:7] are mapped to
740c71d7e67SRui Paulo 	 * Host-2 to Host-9 of PRU-ICSS IRQ-controller.
741c71d7e67SRui Paulo 	 */
7428fb0ef08SIan Lepore 	const int pru_int = iap->irq + TI_PRUSS_PRU_IRQS;
743c71d7e67SRui Paulo 	const int pru_int_mask = (1 << pru_int);
7448fb0ef08SIan Lepore 	const int pru_channel = sc->sc_irq_devs[pru_int].channel;
7458fb0ef08SIan Lepore 	const int pru_event = sc->sc_irq_devs[pru_channel].event;
7468cfbb9cfSRui Paulo 
7478fb0ef08SIan Lepore 	val = ti_pruss_reg_read(sc, PRUSS_INTC_HIER);
748c71d7e67SRui Paulo 	if (!(val & pru_int_mask))
749c71d7e67SRui Paulo 		return;
7508fb0ef08SIan Lepore 
7518fb0ef08SIan Lepore 	ti_pruss_reg_write(sc, PRUSS_INTC_HIDISR, pru_int);
7528fb0ef08SIan Lepore 	ti_pruss_reg_write(sc, PRUSS_INTC_SICR, pru_event);
7538fb0ef08SIan Lepore 	ti_pruss_reg_write(sc, PRUSS_INTC_HIEISR, pru_int);
7548fb0ef08SIan Lepore 
7558fb0ef08SIan Lepore 	struct ti_pruss_irqsc* irq = &sc->sc_irq_devs[pru_channel];
7568fb0ef08SIan Lepore 	size_t wr = irq->tstamps.ctl.idx;
7578fb0ef08SIan Lepore 
7588fb0ef08SIan Lepore 	struct timespec ts;
7598fb0ef08SIan Lepore 	nanouptime(&ts);
7608fb0ef08SIan Lepore 	irq->tstamps.ts[wr] = ts.tv_sec * 1000000000 + ts.tv_nsec;
7618fb0ef08SIan Lepore 
7628fb0ef08SIan Lepore 	if (++wr == TI_TS_ARRAY)
7638fb0ef08SIan Lepore 		wr = 0;
7648fb0ef08SIan Lepore 	atomic_add_32(&irq->tstamps.ctl.cnt, 1);
7658fb0ef08SIan Lepore 
7668fb0ef08SIan Lepore 	irq->tstamps.ctl.idx = wr;
7678fb0ef08SIan Lepore 
7688fb0ef08SIan Lepore 	KNOTE_UNLOCKED(&irq->sc_selinfo.si_note, pru_int);
7698fb0ef08SIan Lepore 	wakeup(irq);
7708fb0ef08SIan Lepore 	selwakeup(&irq->sc_selinfo);
7718cfbb9cfSRui Paulo }
7728cfbb9cfSRui Paulo 
7738cfbb9cfSRui Paulo static int
ti_pruss_open(struct cdev * cdev __unused,int oflags __unused,int devtype __unused,struct thread * td __unused)774afe2c756SRui Paulo ti_pruss_open(struct cdev *cdev __unused, int oflags __unused,
775afe2c756SRui Paulo     int devtype __unused, struct thread *td __unused)
7768cfbb9cfSRui Paulo {
7778cfbb9cfSRui Paulo 	return (0);
7788cfbb9cfSRui Paulo }
7798cfbb9cfSRui Paulo 
7808cfbb9cfSRui Paulo static int
ti_pruss_mmap(struct cdev * cdev,vm_ooffset_t offset,vm_paddr_t * paddr,int nprot,vm_memattr_t * memattr)7818cfbb9cfSRui Paulo ti_pruss_mmap(struct cdev *cdev, vm_ooffset_t offset, vm_paddr_t *paddr,
7828cfbb9cfSRui Paulo     int nprot, vm_memattr_t *memattr)
7838cfbb9cfSRui Paulo {
7848cfbb9cfSRui Paulo 	device_t dev = cdev->si_drv1;
7858cfbb9cfSRui Paulo 	struct ti_pruss_softc *sc = device_get_softc(dev);
7868cfbb9cfSRui Paulo 
787b8c20c02SKonstantin Belousov 	if (offset >= rman_get_size(sc->sc_mem_res))
7888fb0ef08SIan Lepore 		return (ENOSPC);
7898cfbb9cfSRui Paulo 	*paddr = rman_get_start(sc->sc_mem_res) + offset;
790c0a9702fSRui Paulo 	*memattr = VM_MEMATTR_UNCACHEABLE;
7918cfbb9cfSRui Paulo 
7928cfbb9cfSRui Paulo 	return (0);
7938cfbb9cfSRui Paulo }
7948cfbb9cfSRui Paulo 
7958cfbb9cfSRui Paulo static struct filterops ti_pruss_kq_read = {
7968cfbb9cfSRui Paulo 	.f_isfd = 1,
7978fb0ef08SIan Lepore 	.f_detach = ti_pruss_irq_kqread_detach,
7988fb0ef08SIan Lepore 	.f_event = ti_pruss_irq_kqevent,
7998cfbb9cfSRui Paulo };
8008cfbb9cfSRui Paulo 
8018cfbb9cfSRui Paulo static void
ti_pruss_irq_kqread_detach(struct knote * kn)8028fb0ef08SIan Lepore ti_pruss_irq_kqread_detach(struct knote *kn)
8038cfbb9cfSRui Paulo {
8048fb0ef08SIan Lepore 	struct ti_pruss_irqsc *sc = kn->kn_hook;
8058cfbb9cfSRui Paulo 
8068cfbb9cfSRui Paulo 	knlist_remove(&sc->sc_selinfo.si_note, kn, 0);
8078cfbb9cfSRui Paulo }
8088cfbb9cfSRui Paulo 
8098cfbb9cfSRui Paulo static int
ti_pruss_irq_kqevent(struct knote * kn,long hint)8108fb0ef08SIan Lepore ti_pruss_irq_kqevent(struct knote *kn, long hint)
8118cfbb9cfSRui Paulo {
8128fb0ef08SIan Lepore     struct ti_pruss_irqsc* irq_sc;
8138fb0ef08SIan Lepore     int notify;
8148cfbb9cfSRui Paulo 
8158fb0ef08SIan Lepore     irq_sc = kn->kn_hook;
8168fb0ef08SIan Lepore 
8178fb0ef08SIan Lepore     if (hint > 0)
8188fb0ef08SIan Lepore         kn->kn_data = hint - 2;
8198fb0ef08SIan Lepore 
8208fb0ef08SIan Lepore     if (hint > 0 || irq_sc->last > 0)
8218fb0ef08SIan Lepore         notify = 1;
8228fb0ef08SIan Lepore     else
8238fb0ef08SIan Lepore         notify = 0;
8248fb0ef08SIan Lepore 
8258fb0ef08SIan Lepore     irq_sc->last = hint;
8268fb0ef08SIan Lepore 
8278fb0ef08SIan Lepore     return (notify);
8288cfbb9cfSRui Paulo }
8298cfbb9cfSRui Paulo 
8308cfbb9cfSRui Paulo static int
ti_pruss_irq_kqfilter(struct cdev * cdev,struct knote * kn)8318fb0ef08SIan Lepore ti_pruss_irq_kqfilter(struct cdev *cdev, struct knote *kn)
8328cfbb9cfSRui Paulo {
8338fb0ef08SIan Lepore 	struct ti_pruss_irqsc *sc = cdev->si_drv1;
8348cfbb9cfSRui Paulo 
8358cfbb9cfSRui Paulo 	switch (kn->kn_filter) {
8368cfbb9cfSRui Paulo 	case EVFILT_READ:
8378cfbb9cfSRui Paulo 		kn->kn_hook = sc;
8388cfbb9cfSRui Paulo 		kn->kn_fop = &ti_pruss_kq_read;
839a4554c37SOleksandr Tymoshenko 		knlist_add(&sc->sc_selinfo.si_note, kn, 0);
8408cfbb9cfSRui Paulo 		break;
8418cfbb9cfSRui Paulo 	default:
8428cfbb9cfSRui Paulo 		return (EINVAL);
8438cfbb9cfSRui Paulo 	}
8448cfbb9cfSRui Paulo 
8458cfbb9cfSRui Paulo 	return (0);
8468cfbb9cfSRui Paulo }
847