xref: /freebsd/sys/arm/ti/ti_pruss.c (revision fe6985ef87e1aedf8e5c9b3b959c7dd54a03e2fe)
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 
58*be82b3a0SEmmanuel 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 	irqs->cnt = sc->tstamps.ctl.cnt;
1888fb0ef08SIan Lepore 	irqs->idx = sc->tstamps.ctl.idx;
1898fb0ef08SIan Lepore 
1908fb0ef08SIan Lepore 	return devfs_set_cdevpriv(irqs, ti_pruss_privdtor);
1918fb0ef08SIan Lepore }
1928fb0ef08SIan Lepore 
1938fb0ef08SIan Lepore static void
ti_pruss_privdtor(void * data)1948fb0ef08SIan Lepore ti_pruss_privdtor(void *data)
1958fb0ef08SIan Lepore {
1968fb0ef08SIan Lepore     free(data, M_DEVBUF);
1978fb0ef08SIan Lepore }
1988fb0ef08SIan Lepore 
1998fb0ef08SIan Lepore static int
ti_pruss_irq_poll(struct cdev * dev,int events,struct thread * td)2008fb0ef08SIan Lepore ti_pruss_irq_poll(struct cdev *dev, int events, struct thread *td)
2018fb0ef08SIan Lepore {
2028fb0ef08SIan Lepore 	struct ctl* irqs;
2038fb0ef08SIan Lepore 	struct ti_pruss_irqsc *sc;
2048fb0ef08SIan Lepore 	sc = dev->si_drv1;
2058fb0ef08SIan Lepore 
2068fb0ef08SIan Lepore 	devfs_get_cdevpriv((void**)&irqs);
2078fb0ef08SIan Lepore 
2088fb0ef08SIan Lepore 	if (events & (POLLIN | POLLRDNORM)) {
2098fb0ef08SIan Lepore 		if (sc->tstamps.ctl.cnt != irqs->cnt)
2108fb0ef08SIan Lepore 			return events & (POLLIN | POLLRDNORM);
2118fb0ef08SIan Lepore 		else
2128fb0ef08SIan Lepore 			selrecord(td, &sc->sc_selinfo);
2138fb0ef08SIan Lepore 	}
2148fb0ef08SIan Lepore 	return 0;
2158fb0ef08SIan Lepore }
2168fb0ef08SIan Lepore 
2178fb0ef08SIan Lepore static int
ti_pruss_irq_read(struct cdev * cdev,struct uio * uio,int ioflag)2188fb0ef08SIan Lepore ti_pruss_irq_read(struct cdev *cdev, struct uio *uio, int ioflag)
2198fb0ef08SIan Lepore {
2208fb0ef08SIan Lepore 	const size_t ts_len = sizeof(uint64_t);
2218fb0ef08SIan Lepore 	struct ti_pruss_irqsc* irq;
2228fb0ef08SIan Lepore 	struct ctl* priv;
2238fb0ef08SIan Lepore 	int error = 0;
2248fb0ef08SIan Lepore 	size_t idx;
2258fb0ef08SIan Lepore 	ssize_t level;
2268fb0ef08SIan Lepore 
2278fb0ef08SIan Lepore 	irq = cdev->si_drv1;
2288fb0ef08SIan Lepore 
2298fb0ef08SIan Lepore 	if (uio->uio_resid < ts_len)
2308fb0ef08SIan Lepore 		return (EINVAL);
2318fb0ef08SIan Lepore 
2328fb0ef08SIan Lepore 	error = devfs_get_cdevpriv((void**)&priv);
2338fb0ef08SIan Lepore 	if (error)
2348fb0ef08SIan Lepore 	    return (error);
2358fb0ef08SIan Lepore 
2368fb0ef08SIan Lepore 	mtx_lock(&irq->sc_mtx);
2378fb0ef08SIan Lepore 
2388fb0ef08SIan Lepore 	if (irq->tstamps.ctl.cnt - priv->cnt > TI_TS_ARRAY)
2398fb0ef08SIan Lepore 	{
2408fb0ef08SIan Lepore 		priv->cnt = irq->tstamps.ctl.cnt;
2418fb0ef08SIan Lepore 		priv->idx = irq->tstamps.ctl.idx;
2428fb0ef08SIan Lepore 		mtx_unlock(&irq->sc_mtx);
2438fb0ef08SIan Lepore 		return (ENXIO);
2448fb0ef08SIan Lepore 	}
2458fb0ef08SIan Lepore 
2468fb0ef08SIan Lepore 	do {
2478fb0ef08SIan Lepore 		idx = priv->idx;
2488fb0ef08SIan Lepore 		level = irq->tstamps.ctl.idx - idx;
2498fb0ef08SIan Lepore 		if (level < 0)
2508fb0ef08SIan Lepore 			level += TI_TS_ARRAY;
2518fb0ef08SIan Lepore 
2528fb0ef08SIan Lepore 		if (level == 0) {
2538fb0ef08SIan Lepore 			if (ioflag & O_NONBLOCK) {
2548fb0ef08SIan Lepore 				mtx_unlock(&irq->sc_mtx);
2558fb0ef08SIan Lepore 				return (EWOULDBLOCK);
2568fb0ef08SIan Lepore 			}
2578fb0ef08SIan Lepore 
2588fb0ef08SIan Lepore 			error = msleep(irq, &irq->sc_mtx, PCATCH | PDROP,
2598fb0ef08SIan Lepore 				"pruirq", 0);
2608fb0ef08SIan Lepore 			if (error)
2618fb0ef08SIan Lepore 				return error;
2628fb0ef08SIan Lepore 
2638fb0ef08SIan Lepore 			mtx_lock(&irq->sc_mtx);
2648fb0ef08SIan Lepore 		}
2658fb0ef08SIan Lepore 	}while(level == 0);
2668fb0ef08SIan Lepore 
2678fb0ef08SIan Lepore 	mtx_unlock(&irq->sc_mtx);
2688fb0ef08SIan Lepore 
2698fb0ef08SIan Lepore 	error = uiomove(&irq->tstamps.ts[idx], ts_len, uio);
2708fb0ef08SIan Lepore 
2718fb0ef08SIan Lepore 	if (++idx == TI_TS_ARRAY)
2728fb0ef08SIan Lepore 		idx = 0;
2738fb0ef08SIan Lepore 	priv->idx = idx;
2748fb0ef08SIan Lepore 
2758fb0ef08SIan Lepore 	atomic_add_32(&priv->cnt, 1);
2768fb0ef08SIan Lepore 
2778fb0ef08SIan Lepore 	return (error);
2788fb0ef08SIan Lepore }
2798cfbb9cfSRui Paulo 
2808cfbb9cfSRui Paulo static struct ti_pruss_irq_arg {
2818cfbb9cfSRui Paulo 	int 		       irq;
2828cfbb9cfSRui Paulo 	struct ti_pruss_softc *sc;
2838cfbb9cfSRui Paulo } ti_pruss_irq_args[TI_PRUSS_IRQS];
2848cfbb9cfSRui Paulo 
2858cfbb9cfSRui Paulo static __inline uint32_t
ti_pruss_reg_read(struct ti_pruss_softc * sc,uint32_t reg)2868cfbb9cfSRui Paulo ti_pruss_reg_read(struct ti_pruss_softc *sc, uint32_t reg)
2878cfbb9cfSRui Paulo {
2888cfbb9cfSRui Paulo 	return (bus_space_read_4(sc->sc_bt, sc->sc_bh, reg));
2898cfbb9cfSRui Paulo }
2908cfbb9cfSRui Paulo 
2918cfbb9cfSRui Paulo static __inline void
ti_pruss_reg_write(struct ti_pruss_softc * sc,uint32_t reg,uint32_t val)2928cfbb9cfSRui Paulo ti_pruss_reg_write(struct ti_pruss_softc *sc, uint32_t reg, uint32_t val)
2938cfbb9cfSRui Paulo {
2948cfbb9cfSRui Paulo 	bus_space_write_4(sc->sc_bt, sc->sc_bh, reg, val);
2958cfbb9cfSRui Paulo }
2968cfbb9cfSRui Paulo 
2978fb0ef08SIan Lepore static __inline void
ti_pruss_interrupts_clear(struct ti_pruss_softc * sc)2988fb0ef08SIan Lepore ti_pruss_interrupts_clear(struct ti_pruss_softc *sc)
2998fb0ef08SIan Lepore {
3008fb0ef08SIan Lepore 	/* disable global interrupt */
3018fb0ef08SIan Lepore 	ti_pruss_reg_write(sc, PRUSS_INTC_GER, 0 );
3028fb0ef08SIan Lepore 
3038fb0ef08SIan Lepore 	/* clear all events */
3048fb0ef08SIan Lepore 	ti_pruss_reg_write(sc, PRUSS_INTC_SECR0, 0xFFFFFFFF);
3058fb0ef08SIan Lepore 	ti_pruss_reg_write(sc, PRUSS_INTC_SECR1, 0xFFFFFFFF);
3068fb0ef08SIan Lepore 
3078fb0ef08SIan Lepore 	/* disable all host interrupts */
3088fb0ef08SIan Lepore 	ti_pruss_reg_write(sc, PRUSS_INTC_HIER, 0);
3098fb0ef08SIan Lepore }
3108fb0ef08SIan Lepore 
3118fb0ef08SIan Lepore static __inline int
ti_pruss_interrupts_enable(struct ti_pruss_softc * sc,int8_t irq,bool enable)3128fb0ef08SIan Lepore ti_pruss_interrupts_enable(struct ti_pruss_softc *sc, int8_t irq, bool enable)
3138fb0ef08SIan Lepore {
3148fb0ef08SIan Lepore 	if (enable && ((sc->sc_irq_devs[irq].channel == -1) ||
3158fb0ef08SIan Lepore 	    (sc->sc_irq_devs[irq].event== -1)))
3168fb0ef08SIan Lepore 	{
3178fb0ef08SIan Lepore 		device_printf( sc->sc_pdev->si_drv1,
3188fb0ef08SIan Lepore 			"Interrupt chain not fully configured, not possible to enable\n" );
3198fb0ef08SIan Lepore 		return (EINVAL);
3208fb0ef08SIan Lepore 	}
3218fb0ef08SIan Lepore 
3228fb0ef08SIan Lepore 	sc->sc_irq_devs[irq].enable = enable;
3238fb0ef08SIan Lepore 
3248fb0ef08SIan Lepore 	if (sc->sc_irq_devs[irq].sc_pdev) {
3258fb0ef08SIan Lepore 		destroy_dev(sc->sc_irq_devs[irq].sc_pdev);
3268fb0ef08SIan Lepore 		sc->sc_irq_devs[irq].sc_pdev = NULL;
3278fb0ef08SIan Lepore 	}
3288fb0ef08SIan Lepore 
3298fb0ef08SIan Lepore 	if (enable) {
3308fb0ef08SIan Lepore 		sc->sc_irq_devs[irq].sc_pdev = make_dev(&ti_pruss_cdevirq, 0, UID_ROOT, GID_WHEEL,
3318fb0ef08SIan Lepore 		    0600, "pruss%d.irq%d", device_get_unit(sc->sc_pdev->si_drv1), irq);
3328fb0ef08SIan Lepore 		sc->sc_irq_devs[irq].sc_pdev->si_drv1 = &sc->sc_irq_devs[irq];
3338fb0ef08SIan Lepore 
3348fb0ef08SIan Lepore 		sc->sc_irq_devs[irq].tstamps.ctl.idx = 0;
3358fb0ef08SIan Lepore 	}
3368fb0ef08SIan Lepore 
3378fb0ef08SIan Lepore 	uint32_t reg = enable ? PRUSS_INTC_HIEISR : PRUSS_INTC_HIDISR;
3388fb0ef08SIan Lepore 	ti_pruss_reg_write(sc, reg, sc->sc_irq_devs[irq].channel);
3398fb0ef08SIan Lepore 
3408fb0ef08SIan Lepore 	reg = enable ? PRUSS_INTC_EISR : PRUSS_INTC_EICR;
3418fb0ef08SIan Lepore 	ti_pruss_reg_write(sc, reg, sc->sc_irq_devs[irq].event );
3428fb0ef08SIan Lepore 
3438fb0ef08SIan Lepore 	return (0);
3448fb0ef08SIan Lepore }
3458fb0ef08SIan Lepore 
3468fb0ef08SIan Lepore static __inline void
ti_pruss_map_write(struct ti_pruss_softc * sc,uint32_t basereg,uint8_t index,uint8_t content)3478fb0ef08SIan Lepore ti_pruss_map_write(struct ti_pruss_softc *sc, uint32_t basereg, uint8_t index, uint8_t content)
3488fb0ef08SIan Lepore {
3498fb0ef08SIan Lepore 	const size_t regadr = basereg + index & ~0x03;
3508fb0ef08SIan Lepore 	const size_t bitpos = (index & 0x03) * 8;
3518fb0ef08SIan Lepore 	uint32_t rmw = ti_pruss_reg_read(sc, regadr);
3528fb0ef08SIan Lepore 	rmw = (rmw & ~( 0xF << bitpos)) | ( (content & 0xF) << bitpos);
3538fb0ef08SIan Lepore 	ti_pruss_reg_write(sc, regadr, rmw);
3548fb0ef08SIan Lepore }
3558fb0ef08SIan Lepore 
3568fb0ef08SIan Lepore static int
ti_pruss_event_map(SYSCTL_HANDLER_ARGS)3578fb0ef08SIan Lepore ti_pruss_event_map( SYSCTL_HANDLER_ARGS )
3588fb0ef08SIan Lepore {
3598fb0ef08SIan Lepore 	struct ti_pruss_softc *sc;
3608fb0ef08SIan Lepore 	const int8_t irq = arg2;
3618fb0ef08SIan Lepore 	int err;
3628fb0ef08SIan Lepore 	char event[sizeof(NOT_SET_STR)];
3638fb0ef08SIan Lepore 
3648fb0ef08SIan Lepore 	sc = arg1;
3658fb0ef08SIan Lepore 
3668fb0ef08SIan Lepore 	if(sc->sc_irq_devs[irq].event == -1)
3678fb0ef08SIan Lepore 		bcopy(NOT_SET_STR, event, sizeof(event));
3688fb0ef08SIan Lepore 	else
3698fb0ef08SIan Lepore 		snprintf(event, sizeof(event), "%d", sc->sc_irq_devs[irq].event);
3708fb0ef08SIan Lepore 
3718fb0ef08SIan Lepore 	err = sysctl_handle_string(oidp, event, sizeof(event), req);
3728fb0ef08SIan Lepore 	if(err != 0)
3738fb0ef08SIan Lepore 		return (err);
3748fb0ef08SIan Lepore 
3758fb0ef08SIan Lepore 	if (req->newptr) {  // write event
3768fb0ef08SIan Lepore 		if (strcmp(NOT_SET_STR, event) == 0) {
3778fb0ef08SIan Lepore 			ti_pruss_interrupts_enable(sc, irq, false);
3788fb0ef08SIan Lepore 			sc->sc_irq_devs[irq].event = -1;
3798fb0ef08SIan Lepore 		} else {
3808fb0ef08SIan Lepore 			if (sc->sc_irq_devs[irq].channel == -1) {
3818fb0ef08SIan Lepore 				device_printf( sc->sc_pdev->si_drv1,
3828fb0ef08SIan Lepore 					"corresponding channel not configured\n");
3838fb0ef08SIan Lepore 				return (ENXIO);
3848fb0ef08SIan Lepore 			}
3858fb0ef08SIan Lepore 
3868fb0ef08SIan Lepore 			const int8_t channelnr = sc->sc_irq_devs[irq].channel;
3878fb0ef08SIan Lepore 			const int8_t eventnr = strtol( event, NULL, 10 ); // TODO: check if strol is valid
3888fb0ef08SIan Lepore 			if (eventnr > TI_PRUSS_EVENTS || eventnr < 0) {
3898fb0ef08SIan Lepore 				device_printf( sc->sc_pdev->si_drv1,
3908fb0ef08SIan Lepore 					"Event number %d not valid (0 - %d)",
3918fb0ef08SIan Lepore 					channelnr, TI_PRUSS_EVENTS -1);
3928fb0ef08SIan Lepore 				return (EINVAL);
3938fb0ef08SIan Lepore 			}
3948fb0ef08SIan Lepore 
3958fb0ef08SIan Lepore 			sc->sc_irq_devs[irq].channel = channelnr;
3968fb0ef08SIan Lepore 			sc->sc_irq_devs[irq].event = eventnr;
3978fb0ef08SIan Lepore 
3988fb0ef08SIan Lepore 			// event[nr] <= channel
3998fb0ef08SIan Lepore 			ti_pruss_map_write(sc, PRUSS_INTC_CMR_BASE,
4008fb0ef08SIan Lepore 			    eventnr, channelnr);
4018fb0ef08SIan Lepore 		}
4028fb0ef08SIan Lepore 	}
4038fb0ef08SIan Lepore 	return (err);
4048fb0ef08SIan Lepore }
4058fb0ef08SIan Lepore 
4068fb0ef08SIan Lepore static int
ti_pruss_channel_map(SYSCTL_HANDLER_ARGS)4078fb0ef08SIan Lepore ti_pruss_channel_map(SYSCTL_HANDLER_ARGS)
4088fb0ef08SIan Lepore {
4098fb0ef08SIan Lepore 	struct ti_pruss_softc *sc;
4108fb0ef08SIan Lepore 	int err;
4118fb0ef08SIan Lepore 	char channel[sizeof(NOT_SET_STR)];
4128fb0ef08SIan Lepore 	const int8_t irq = arg2;
4138fb0ef08SIan Lepore 
4148fb0ef08SIan Lepore 	sc = arg1;
4158fb0ef08SIan Lepore 
4168fb0ef08SIan Lepore 	if (sc->sc_irq_devs[irq].channel == -1)
4178fb0ef08SIan Lepore 		bcopy(NOT_SET_STR, channel, sizeof(channel));
4188fb0ef08SIan Lepore 	else
4198fb0ef08SIan Lepore 		snprintf(channel, sizeof(channel), "%d", sc->sc_irq_devs[irq].channel);
4208fb0ef08SIan Lepore 
4218fb0ef08SIan Lepore 	err = sysctl_handle_string(oidp, channel, sizeof(channel), req);
4228fb0ef08SIan Lepore 	if (err != 0)
4238fb0ef08SIan Lepore 		return (err);
4248fb0ef08SIan Lepore 
4258fb0ef08SIan Lepore 	if (req->newptr) { // write event
4268fb0ef08SIan Lepore 		if (strcmp(NOT_SET_STR, channel) == 0) {
4278fb0ef08SIan Lepore 			ti_pruss_interrupts_enable(sc, irq, false);
4288fb0ef08SIan Lepore 			ti_pruss_reg_write(sc, PRUSS_INTC_HIDISR,
4298fb0ef08SIan Lepore 			    sc->sc_irq_devs[irq].channel);
4308fb0ef08SIan Lepore 			sc->sc_irq_devs[irq].channel = -1;
4318fb0ef08SIan Lepore 		} else {
4328fb0ef08SIan Lepore 			const int8_t channelnr = strtol(channel, NULL, 10); // TODO: check if strol is valid
4338fb0ef08SIan Lepore 			if (channelnr > TI_PRUSS_IRQS || channelnr < 0)
4348fb0ef08SIan Lepore 			{
4358fb0ef08SIan Lepore 				device_printf(sc->sc_pdev->si_drv1,
4368fb0ef08SIan Lepore 					"Channel number %d not valid (0 - %d)",
4378fb0ef08SIan Lepore 					channelnr, TI_PRUSS_IRQS-1);
4388fb0ef08SIan Lepore 				return (EINVAL);
4398fb0ef08SIan Lepore 			}
4408fb0ef08SIan Lepore 
4418fb0ef08SIan Lepore 			sc->sc_irq_devs[irq].channel = channelnr;
4428fb0ef08SIan Lepore 			sc->sc_irq_devs[irq].last = -1;
4438fb0ef08SIan Lepore 
4448fb0ef08SIan Lepore 			// channel[nr] <= irqnr
4458fb0ef08SIan Lepore 			ti_pruss_map_write(sc, PRUSS_INTC_HMR_BASE,
4468fb0ef08SIan Lepore 				irq, channelnr);
4478fb0ef08SIan Lepore 		}
4488fb0ef08SIan Lepore 	}
4498fb0ef08SIan Lepore 
4508fb0ef08SIan Lepore 	return (err);
4518fb0ef08SIan Lepore }
4528fb0ef08SIan Lepore 
4538fb0ef08SIan Lepore static int
ti_pruss_interrupt_enable(SYSCTL_HANDLER_ARGS)4548fb0ef08SIan Lepore ti_pruss_interrupt_enable(SYSCTL_HANDLER_ARGS)
4558fb0ef08SIan Lepore {
4568fb0ef08SIan Lepore 	struct ti_pruss_softc *sc;
4578fb0ef08SIan Lepore 	int err;
4588fb0ef08SIan Lepore 	bool irqenable;
4598fb0ef08SIan Lepore 	const int8_t irq = arg2;
4608fb0ef08SIan Lepore 
4618fb0ef08SIan Lepore 	sc = arg1;
4628fb0ef08SIan Lepore 	irqenable = sc->sc_irq_devs[arg2].enable;
4638fb0ef08SIan Lepore 
4648fb0ef08SIan Lepore 	err = sysctl_handle_bool(oidp, &irqenable, arg2, req);
4658fb0ef08SIan Lepore 	if (err != 0)
4668fb0ef08SIan Lepore 		return (err);
4678fb0ef08SIan Lepore 
4688fb0ef08SIan Lepore 	if (req->newptr) // write enable
4698fb0ef08SIan Lepore 		return ti_pruss_interrupts_enable(sc, irq, irqenable);
4708fb0ef08SIan Lepore 
4718fb0ef08SIan Lepore 	return (err);
4728fb0ef08SIan Lepore }
4738fb0ef08SIan Lepore 
4748fb0ef08SIan Lepore static int
ti_pruss_global_interrupt_enable(SYSCTL_HANDLER_ARGS)4758fb0ef08SIan Lepore ti_pruss_global_interrupt_enable(SYSCTL_HANDLER_ARGS)
4768fb0ef08SIan Lepore {
4778fb0ef08SIan Lepore 	struct ti_pruss_softc *sc;
4788fb0ef08SIan Lepore 	int err;
4798fb0ef08SIan Lepore 	bool glob_irqen;
4808fb0ef08SIan Lepore 
4818fb0ef08SIan Lepore 	sc = arg1;
4828fb0ef08SIan Lepore 	glob_irqen = sc->sc_glob_irqen;
4838fb0ef08SIan Lepore 
4848fb0ef08SIan Lepore 	err = sysctl_handle_bool(oidp, &glob_irqen, arg2, req);
4858fb0ef08SIan Lepore 	if (err != 0)
4868fb0ef08SIan Lepore 		return (err);
4878fb0ef08SIan Lepore 
4888fb0ef08SIan Lepore 	if (req->newptr) {
4898fb0ef08SIan Lepore 		sc->sc_glob_irqen = glob_irqen;
4908fb0ef08SIan Lepore 		ti_pruss_reg_write(sc, PRUSS_INTC_GER, glob_irqen);
4918fb0ef08SIan Lepore 	}
4928fb0ef08SIan Lepore 
4938fb0ef08SIan Lepore 	return (err);
4948fb0ef08SIan Lepore }
4958cfbb9cfSRui Paulo static int
ti_pruss_probe(device_t dev)4968cfbb9cfSRui Paulo ti_pruss_probe(device_t dev)
4978cfbb9cfSRui Paulo {
498add35ed5SIan Lepore 
499add35ed5SIan Lepore 	if (!ofw_bus_status_okay(dev))
500add35ed5SIan Lepore 		return (ENXIO);
501add35ed5SIan Lepore 
5028cfbb9cfSRui Paulo 	if (ofw_bus_is_compatible(dev, "ti,pruss-v1") ||
5038cfbb9cfSRui Paulo 	    ofw_bus_is_compatible(dev, "ti,pruss-v2")) {
5048cfbb9cfSRui Paulo 		device_set_desc(dev, "TI Programmable Realtime Unit Subsystem");
5058cfbb9cfSRui Paulo 		return (BUS_PROBE_DEFAULT);
5068cfbb9cfSRui Paulo 	}
5078cfbb9cfSRui Paulo 
5088cfbb9cfSRui Paulo 	return (ENXIO);
5098cfbb9cfSRui Paulo }
5108cfbb9cfSRui Paulo 
5118cfbb9cfSRui Paulo static int
ti_pruss_attach(device_t dev)5128cfbb9cfSRui Paulo ti_pruss_attach(device_t dev)
5138cfbb9cfSRui Paulo {
5148cfbb9cfSRui Paulo 	struct ti_pruss_softc *sc;
5150050ea24SMichal Meloun 	int rid, i, err, ncells;
5160050ea24SMichal Meloun 	phandle_t node;
5170050ea24SMichal Meloun 	clk_t l3_gclk, pruss_ocp_gclk;
5180050ea24SMichal Meloun 	phandle_t ti_prm_ref, *cells;
5190050ea24SMichal Meloun         device_t ti_prm_dev;
5208cfbb9cfSRui Paulo 
5210050ea24SMichal Meloun 	rid = 0;
5220050ea24SMichal Meloun 	sc = device_get_softc(dev);
5230050ea24SMichal Meloun 	node = ofw_bus_get_node(device_get_parent(dev));
5240050ea24SMichal Meloun 	if (node <= 0) {
5250050ea24SMichal Meloun 		device_printf(dev, "Cant get ofw node\n");
5268cfbb9cfSRui Paulo 		return (ENXIO);
5278cfbb9cfSRui Paulo 	}
5280050ea24SMichal Meloun 
5290050ea24SMichal Meloun 	/*
5300050ea24SMichal Meloun 	 * Follow activate pattern from sys/arm/ti/am335x/am335x_prcm.c
5310050ea24SMichal Meloun 	 * by Damjan Marion
5320050ea24SMichal Meloun 	 */
5330050ea24SMichal Meloun 
5340050ea24SMichal Meloun 	/* Set MODULEMODE to ENABLE(2) */
5350050ea24SMichal Meloun 	/* Wait for MODULEMODE to become ENABLE(2) */
5360050ea24SMichal Meloun 	if (ti_sysc_clock_enable(device_get_parent(dev)) != 0) {
5370050ea24SMichal Meloun 		device_printf(dev, "Could not enable PRUSS clock\n");
5380050ea24SMichal Meloun 		return (ENXIO);
5390050ea24SMichal Meloun 	}
5400050ea24SMichal Meloun 
5410050ea24SMichal Meloun 	/* Set CLKTRCTRL to SW_WKUP(2) */
5420050ea24SMichal Meloun 	/* Wait for the 200 MHz OCP clock to become active */
5430050ea24SMichal Meloun 	/* Wait for the 200 MHz IEP clock to become active */
5440050ea24SMichal Meloun 	/* Wait for the 192 MHz UART clock to become active */
5450050ea24SMichal Meloun 	/*
5460050ea24SMichal Meloun 	 * At the moment there is no reference to CM_PER_PRU_ICSS_CLKSTCTRL@140
5470050ea24SMichal Meloun 	 * in the devicetree. The register reset state are SW_WKUP(2) as default
5480050ea24SMichal Meloun 	 * so at the moment ignore setting this register.
5490050ea24SMichal Meloun 	 */
5500050ea24SMichal Meloun 
5510050ea24SMichal Meloun 	/* Select L3F as OCP clock */
5520050ea24SMichal Meloun 	/* Get the clock and set the parent */
5530050ea24SMichal Meloun 	err = clk_get_by_name(dev, "l3_gclk", &l3_gclk);
5540050ea24SMichal Meloun 	if (err) {
5550050ea24SMichal Meloun 		device_printf(dev, "Cant get l3_gclk err %d\n", err);
5560050ea24SMichal Meloun 		return (ENXIO);
5570050ea24SMichal Meloun 	}
5580050ea24SMichal Meloun 
5590050ea24SMichal Meloun 	err = clk_get_by_name(dev, "pruss_ocp_gclk@530", &pruss_ocp_gclk);
5600050ea24SMichal Meloun 	if (err) {
5610050ea24SMichal Meloun 		device_printf(dev, "Cant get pruss_ocp_gclk@530 err %d\n", err);
5620050ea24SMichal Meloun 		return (ENXIO);
5630050ea24SMichal Meloun 	}
5640050ea24SMichal Meloun 
5650050ea24SMichal Meloun 	err = clk_set_parent_by_clk(pruss_ocp_gclk, l3_gclk);
5660050ea24SMichal Meloun 	if (err) {
5670050ea24SMichal Meloun 		device_printf(dev,
5680050ea24SMichal Meloun 		    "Cant set pruss_ocp_gclk parent to l3_gclk err %d\n", err);
5690050ea24SMichal Meloun 		return (ENXIO);
5700050ea24SMichal Meloun 	}
5710050ea24SMichal Meloun 
5720050ea24SMichal Meloun 	/* Clear the RESET bit */
5730050ea24SMichal Meloun 	/* Find the ti_prm */
5740050ea24SMichal Meloun 	/* #reset-cells should not been used in this way but... */
5750050ea24SMichal Meloun 	err = ofw_bus_parse_xref_list_alloc(node, "resets", "#reset-cells", 0,
5760050ea24SMichal Meloun 	    &ti_prm_ref, &ncells, &cells);
5770050ea24SMichal Meloun 	OF_prop_free(cells);
5780050ea24SMichal Meloun 	if (err) {
5790050ea24SMichal Meloun 		device_printf(dev,
5800050ea24SMichal Meloun 		    "Cant fetch \"resets\" reference %x\n", err);
5810050ea24SMichal Meloun 		return (ENXIO);
5820050ea24SMichal Meloun 	}
5830050ea24SMichal Meloun 
5840050ea24SMichal Meloun 	ti_prm_dev = OF_device_from_xref(ti_prm_ref);
5850050ea24SMichal Meloun 	if (ti_prm_dev == NULL) {
5860050ea24SMichal Meloun 		device_printf(dev, "Cant get device from \"resets\"\n");
5870050ea24SMichal Meloun 		return (ENXIO);
5880050ea24SMichal Meloun 	}
5890050ea24SMichal Meloun 
5900050ea24SMichal Meloun 	err = ti_prm_reset(ti_prm_dev);
5910050ea24SMichal Meloun 	if (err) {
5920050ea24SMichal Meloun 		device_printf(dev, "ti_prm_reset failed %d\n", err);
5930050ea24SMichal Meloun 		return (ENXIO);
5940050ea24SMichal Meloun 	}
5950050ea24SMichal Meloun 	/* End of clock activation */
5960050ea24SMichal Meloun 
5976eebe850SJohn-Mark Gurney 	mtx_init(&sc->sc_mtx, "TI PRUSS", NULL, MTX_DEF);
5988cfbb9cfSRui Paulo 	sc->sc_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
5998cfbb9cfSRui Paulo 	    RF_ACTIVE);
6008cfbb9cfSRui Paulo 	if (sc->sc_mem_res == NULL) {
6018cfbb9cfSRui Paulo 		device_printf(dev, "could not allocate memory resource\n");
6028cfbb9cfSRui Paulo 		return (ENXIO);
6038cfbb9cfSRui Paulo 	}
6048fb0ef08SIan Lepore 
6058fb0ef08SIan Lepore 	struct sysctl_ctx_list *clist = device_get_sysctl_ctx(dev);
6068fb0ef08SIan Lepore 	if (!clist)
6078fb0ef08SIan Lepore 		return (EINVAL);
6088fb0ef08SIan Lepore 
6098fb0ef08SIan Lepore 	struct sysctl_oid *poid;
6108fb0ef08SIan Lepore 	poid = device_get_sysctl_tree( dev );
6118fb0ef08SIan Lepore 	if (!poid)
6128fb0ef08SIan Lepore 		return (EINVAL);
6138fb0ef08SIan Lepore 
6148fb0ef08SIan Lepore 	sc->sc_glob_irqen = false;
6158fb0ef08SIan Lepore 	struct sysctl_oid *irq_root = SYSCTL_ADD_NODE(clist, SYSCTL_CHILDREN(poid),
6167029da5cSPawel Biernacki 	    OID_AUTO, "irq", CTLFLAG_RD | CTLFLAG_MPSAFE, 0,
6178fb0ef08SIan Lepore 	    "PRUSS Host Interrupts");
6188fb0ef08SIan Lepore 	SYSCTL_ADD_PROC(clist, SYSCTL_CHILDREN(poid), OID_AUTO,
6197029da5cSPawel Biernacki 	    "global_interrupt_enable",
6207029da5cSPawel Biernacki 	    CTLFLAG_RW | CTLTYPE_U8 | CTLFLAG_NEEDGIANT,
6218fb0ef08SIan Lepore 	    sc, 0, ti_pruss_global_interrupt_enable,
6228fb0ef08SIan Lepore 	    "CU", "Global interrupt enable");
6238fb0ef08SIan Lepore 
6248cfbb9cfSRui Paulo 	sc->sc_bt = rman_get_bustag(sc->sc_mem_res);
6258cfbb9cfSRui Paulo 	sc->sc_bh = rman_get_bushandle(sc->sc_mem_res);
6268cfbb9cfSRui Paulo 	if (bus_alloc_resources(dev, ti_pruss_irq_spec, sc->sc_irq_res) != 0) {
6278cfbb9cfSRui Paulo 		device_printf(dev, "could not allocate interrupt resource\n");
6288cfbb9cfSRui Paulo 		ti_pruss_detach(dev);
6298cfbb9cfSRui Paulo 		return (ENXIO);
6308cfbb9cfSRui Paulo 	}
6318fb0ef08SIan Lepore 
6328fb0ef08SIan Lepore 	ti_pruss_interrupts_clear(sc);
6338fb0ef08SIan Lepore 
6348cfbb9cfSRui Paulo 	for (i = 0; i < TI_PRUSS_IRQS; i++) {
6358fb0ef08SIan Lepore 		char name[8];
6368fb0ef08SIan Lepore 		snprintf(name, sizeof(name), "%d", i);
6378fb0ef08SIan Lepore 
6388fb0ef08SIan Lepore 		struct sysctl_oid *irq_nodes = SYSCTL_ADD_NODE(clist, SYSCTL_CHILDREN(irq_root),
6397029da5cSPawel Biernacki 		    OID_AUTO, name, CTLFLAG_RD | CTLFLAG_MPSAFE, 0,
6408fb0ef08SIan Lepore 		    "PRUSS Interrupts");
6418fb0ef08SIan Lepore 		SYSCTL_ADD_PROC(clist, SYSCTL_CHILDREN(irq_nodes), OID_AUTO,
6427029da5cSPawel Biernacki 		    "channel", CTLFLAG_RW | CTLTYPE_STRING | CTLFLAG_NEEDGIANT,
6437029da5cSPawel Biernacki 		    sc, i, ti_pruss_channel_map,
6448fb0ef08SIan Lepore 		    "A", "Channel attached to this irq");
6458fb0ef08SIan Lepore 		SYSCTL_ADD_PROC(clist, SYSCTL_CHILDREN(irq_nodes), OID_AUTO,
6467029da5cSPawel Biernacki 		    "event", CTLFLAG_RW | CTLTYPE_STRING | CTLFLAG_NEEDGIANT,
6477029da5cSPawel Biernacki 		    sc, i, ti_pruss_event_map,
6488fb0ef08SIan Lepore 		    "A", "Event attached to this irq");
6498fb0ef08SIan Lepore 		SYSCTL_ADD_PROC(clist, SYSCTL_CHILDREN(irq_nodes), OID_AUTO,
6507029da5cSPawel Biernacki 		    "enable", CTLFLAG_RW | CTLTYPE_U8 | CTLFLAG_NEEDGIANT,
6517029da5cSPawel Biernacki 		    sc, i, ti_pruss_interrupt_enable,
6528fb0ef08SIan Lepore 		    "CU", "Enable/Disable interrupt");
6538fb0ef08SIan Lepore 
6548fb0ef08SIan Lepore 		sc->sc_irq_devs[i].event = -1;
6558fb0ef08SIan Lepore 		sc->sc_irq_devs[i].channel = -1;
6568fb0ef08SIan Lepore 		sc->sc_irq_devs[i].tstamps.ctl.idx = 0;
6578fb0ef08SIan Lepore 
6588fb0ef08SIan Lepore 		if (i < TI_PRUSS_HOST_IRQS) {
6598cfbb9cfSRui Paulo 			ti_pruss_irq_args[i].irq = i;
6608cfbb9cfSRui Paulo 			ti_pruss_irq_args[i].sc = sc;
6618cfbb9cfSRui Paulo 			if (bus_setup_intr(dev, sc->sc_irq_res[i],
6628cfbb9cfSRui Paulo 			    INTR_MPSAFE | INTR_TYPE_MISC,
6638cfbb9cfSRui Paulo 			    NULL, ti_pruss_intr, &ti_pruss_irq_args[i],
6648cfbb9cfSRui Paulo 			    &sc->sc_intr[i]) != 0) {
6658cfbb9cfSRui Paulo 				device_printf(dev,
6668cfbb9cfSRui Paulo 				    "unable to setup the interrupt handler\n");
6678cfbb9cfSRui Paulo 				ti_pruss_detach(dev);
6688fb0ef08SIan Lepore 
6698cfbb9cfSRui Paulo 				return (ENXIO);
6708cfbb9cfSRui Paulo 			}
6718fb0ef08SIan Lepore 			mtx_init(&sc->sc_irq_devs[i].sc_mtx, "TI PRUSS IRQ", NULL, MTX_DEF);
6728fb0ef08SIan Lepore 			knlist_init_mtx(&sc->sc_irq_devs[i].sc_selinfo.si_note, &sc->sc_irq_devs[i].sc_mtx);
6738cfbb9cfSRui Paulo 		}
6748fb0ef08SIan Lepore 	}
6758fb0ef08SIan Lepore 
6768fb0ef08SIan Lepore 	if (ti_pruss_reg_read(sc, PRUSS_AM33XX_INTC) == PRUSS_AM33XX_REV)
6778cfbb9cfSRui Paulo 		device_printf(dev, "AM33xx PRU-ICSS\n");
6788cfbb9cfSRui Paulo 
6798cfbb9cfSRui Paulo 	sc->sc_pdev = make_dev(&ti_pruss_cdevsw, 0, UID_ROOT, GID_WHEEL,
6808cfbb9cfSRui Paulo 	    0600, "pruss%d", device_get_unit(dev));
6818cfbb9cfSRui Paulo 	sc->sc_pdev->si_drv1 = dev;
6828cfbb9cfSRui Paulo 
6838fb0ef08SIan Lepore 	/*  Acc. to datasheet always write 1 to polarity registers */
6848fb0ef08SIan Lepore 	ti_pruss_reg_write(sc, PRUSS_INTC_SIPR0, 0xFFFFFFFF);
6858fb0ef08SIan Lepore 	ti_pruss_reg_write(sc, PRUSS_INTC_SIPR1, 0xFFFFFFFF);
6868fb0ef08SIan Lepore 
6878fb0ef08SIan Lepore 	/* Acc. to datasheet always write 0 to event type registers */
6888fb0ef08SIan Lepore 	ti_pruss_reg_write(sc, PRUSS_INTC_SITR0, 0);
6898fb0ef08SIan Lepore 	ti_pruss_reg_write(sc, PRUSS_INTC_SITR1, 0);
6908fb0ef08SIan Lepore 
6918cfbb9cfSRui Paulo 	return (0);
6928cfbb9cfSRui Paulo }
6938cfbb9cfSRui Paulo 
6948cfbb9cfSRui Paulo static int
ti_pruss_detach(device_t dev)6958cfbb9cfSRui Paulo ti_pruss_detach(device_t dev)
6968cfbb9cfSRui Paulo {
6978fb0ef08SIan Lepore 	struct ti_pruss_softc *sc = device_get_softc(dev);
6988cfbb9cfSRui Paulo 
6998fb0ef08SIan Lepore 	ti_pruss_interrupts_clear(sc);
7008fb0ef08SIan Lepore 
7018fb0ef08SIan Lepore 	for (int i = 0; i < TI_PRUSS_HOST_IRQS; i++) {
7028fb0ef08SIan Lepore 		ti_pruss_interrupts_enable( sc, i, false );
7038fb0ef08SIan Lepore 
7048cfbb9cfSRui Paulo 		if (sc->sc_intr[i])
7058cfbb9cfSRui Paulo 			bus_teardown_intr(dev, sc->sc_irq_res[i], sc->sc_intr[i]);
7068cfbb9cfSRui Paulo 		if (sc->sc_irq_res[i])
7078cfbb9cfSRui Paulo 			bus_release_resource(dev, SYS_RES_IRQ,
7088cfbb9cfSRui Paulo 			    rman_get_rid(sc->sc_irq_res[i]),
7098cfbb9cfSRui Paulo 			    sc->sc_irq_res[i]);
7108fb0ef08SIan Lepore 		knlist_clear(&sc->sc_irq_devs[i].sc_selinfo.si_note, 0);
7118fb0ef08SIan Lepore 		mtx_lock(&sc->sc_irq_devs[i].sc_mtx);
7128fb0ef08SIan Lepore 		if (!knlist_empty(&sc->sc_irq_devs[i].sc_selinfo.si_note))
7138fb0ef08SIan Lepore 			printf("IRQ %d KQueue not empty!\n", i );
7148fb0ef08SIan Lepore 		mtx_unlock(&sc->sc_irq_devs[i].sc_mtx);
7158fb0ef08SIan Lepore 		knlist_destroy(&sc->sc_irq_devs[i].sc_selinfo.si_note);
7168fb0ef08SIan Lepore 		mtx_destroy(&sc->sc_irq_devs[i].sc_mtx);
7178cfbb9cfSRui Paulo 	}
7188fb0ef08SIan Lepore 
719c71d7e67SRui Paulo 	mtx_destroy(&sc->sc_mtx);
7208cfbb9cfSRui Paulo 	if (sc->sc_mem_res)
7218cfbb9cfSRui Paulo 		bus_release_resource(dev, SYS_RES_MEMORY, rman_get_rid(sc->sc_mem_res),
7228cfbb9cfSRui Paulo 		    sc->sc_mem_res);
7238cfbb9cfSRui Paulo 	if (sc->sc_pdev)
7248cfbb9cfSRui Paulo 		destroy_dev(sc->sc_pdev);
7258cfbb9cfSRui Paulo 
7268cfbb9cfSRui Paulo 	return (0);
7278cfbb9cfSRui Paulo }
7288cfbb9cfSRui Paulo 
7298cfbb9cfSRui Paulo static void
ti_pruss_intr(void * arg)7308cfbb9cfSRui Paulo ti_pruss_intr(void *arg)
7318cfbb9cfSRui Paulo {
732c71d7e67SRui Paulo 	int val;
733c71d7e67SRui Paulo 	struct ti_pruss_irq_arg *iap = arg;
734c71d7e67SRui Paulo 	struct ti_pruss_softc *sc = iap->sc;
735c71d7e67SRui Paulo 	/*
736c71d7e67SRui Paulo 	 * Interrupts pr1_host_intr[0:7] are mapped to
737c71d7e67SRui Paulo 	 * Host-2 to Host-9 of PRU-ICSS IRQ-controller.
738c71d7e67SRui Paulo 	 */
7398fb0ef08SIan Lepore 	const int pru_int = iap->irq + TI_PRUSS_PRU_IRQS;
740c71d7e67SRui Paulo 	const int pru_int_mask = (1 << pru_int);
7418fb0ef08SIan Lepore 	const int pru_channel = sc->sc_irq_devs[pru_int].channel;
7428fb0ef08SIan Lepore 	const int pru_event = sc->sc_irq_devs[pru_channel].event;
7438cfbb9cfSRui Paulo 
7448fb0ef08SIan Lepore 	val = ti_pruss_reg_read(sc, PRUSS_INTC_HIER);
745c71d7e67SRui Paulo 	if (!(val & pru_int_mask))
746c71d7e67SRui Paulo 		return;
7478fb0ef08SIan Lepore 
7488fb0ef08SIan Lepore 	ti_pruss_reg_write(sc, PRUSS_INTC_HIDISR, pru_int);
7498fb0ef08SIan Lepore 	ti_pruss_reg_write(sc, PRUSS_INTC_SICR, pru_event);
7508fb0ef08SIan Lepore 	ti_pruss_reg_write(sc, PRUSS_INTC_HIEISR, pru_int);
7518fb0ef08SIan Lepore 
7528fb0ef08SIan Lepore 	struct ti_pruss_irqsc* irq = &sc->sc_irq_devs[pru_channel];
7538fb0ef08SIan Lepore 	size_t wr = irq->tstamps.ctl.idx;
7548fb0ef08SIan Lepore 
7558fb0ef08SIan Lepore 	struct timespec ts;
7568fb0ef08SIan Lepore 	nanouptime(&ts);
7578fb0ef08SIan Lepore 	irq->tstamps.ts[wr] = ts.tv_sec * 1000000000 + ts.tv_nsec;
7588fb0ef08SIan Lepore 
7598fb0ef08SIan Lepore 	if (++wr == TI_TS_ARRAY)
7608fb0ef08SIan Lepore 		wr = 0;
7618fb0ef08SIan Lepore 	atomic_add_32(&irq->tstamps.ctl.cnt, 1);
7628fb0ef08SIan Lepore 
7638fb0ef08SIan Lepore 	irq->tstamps.ctl.idx = wr;
7648fb0ef08SIan Lepore 
7658fb0ef08SIan Lepore 	KNOTE_UNLOCKED(&irq->sc_selinfo.si_note, pru_int);
7668fb0ef08SIan Lepore 	wakeup(irq);
7678fb0ef08SIan Lepore 	selwakeup(&irq->sc_selinfo);
7688cfbb9cfSRui Paulo }
7698cfbb9cfSRui Paulo 
7708cfbb9cfSRui Paulo static int
ti_pruss_open(struct cdev * cdev __unused,int oflags __unused,int devtype __unused,struct thread * td __unused)771afe2c756SRui Paulo ti_pruss_open(struct cdev *cdev __unused, int oflags __unused,
772afe2c756SRui Paulo     int devtype __unused, struct thread *td __unused)
7738cfbb9cfSRui Paulo {
7748cfbb9cfSRui Paulo 	return (0);
7758cfbb9cfSRui Paulo }
7768cfbb9cfSRui Paulo 
7778cfbb9cfSRui Paulo static int
ti_pruss_mmap(struct cdev * cdev,vm_ooffset_t offset,vm_paddr_t * paddr,int nprot,vm_memattr_t * memattr)7788cfbb9cfSRui Paulo ti_pruss_mmap(struct cdev *cdev, vm_ooffset_t offset, vm_paddr_t *paddr,
7798cfbb9cfSRui Paulo     int nprot, vm_memattr_t *memattr)
7808cfbb9cfSRui Paulo {
7818cfbb9cfSRui Paulo 	device_t dev = cdev->si_drv1;
7828cfbb9cfSRui Paulo 	struct ti_pruss_softc *sc = device_get_softc(dev);
7838cfbb9cfSRui Paulo 
784b8c20c02SKonstantin Belousov 	if (offset >= rman_get_size(sc->sc_mem_res))
7858fb0ef08SIan Lepore 		return (ENOSPC);
7868cfbb9cfSRui Paulo 	*paddr = rman_get_start(sc->sc_mem_res) + offset;
787c0a9702fSRui Paulo 	*memattr = VM_MEMATTR_UNCACHEABLE;
7888cfbb9cfSRui Paulo 
7898cfbb9cfSRui Paulo 	return (0);
7908cfbb9cfSRui Paulo }
7918cfbb9cfSRui Paulo 
7928cfbb9cfSRui Paulo static struct filterops ti_pruss_kq_read = {
7938cfbb9cfSRui Paulo 	.f_isfd = 1,
7948fb0ef08SIan Lepore 	.f_detach = ti_pruss_irq_kqread_detach,
7958fb0ef08SIan Lepore 	.f_event = ti_pruss_irq_kqevent,
7968cfbb9cfSRui Paulo };
7978cfbb9cfSRui Paulo 
7988cfbb9cfSRui Paulo static void
ti_pruss_irq_kqread_detach(struct knote * kn)7998fb0ef08SIan Lepore ti_pruss_irq_kqread_detach(struct knote *kn)
8008cfbb9cfSRui Paulo {
8018fb0ef08SIan Lepore 	struct ti_pruss_irqsc *sc = kn->kn_hook;
8028cfbb9cfSRui Paulo 
8038cfbb9cfSRui Paulo 	knlist_remove(&sc->sc_selinfo.si_note, kn, 0);
8048cfbb9cfSRui Paulo }
8058cfbb9cfSRui Paulo 
8068cfbb9cfSRui Paulo static int
ti_pruss_irq_kqevent(struct knote * kn,long hint)8078fb0ef08SIan Lepore ti_pruss_irq_kqevent(struct knote *kn, long hint)
8088cfbb9cfSRui Paulo {
8098fb0ef08SIan Lepore     struct ti_pruss_irqsc* irq_sc;
8108fb0ef08SIan Lepore     int notify;
8118cfbb9cfSRui Paulo 
8128fb0ef08SIan Lepore     irq_sc = kn->kn_hook;
8138fb0ef08SIan Lepore 
8148fb0ef08SIan Lepore     if (hint > 0)
8158fb0ef08SIan Lepore         kn->kn_data = hint - 2;
8168fb0ef08SIan Lepore 
8178fb0ef08SIan Lepore     if (hint > 0 || irq_sc->last > 0)
8188fb0ef08SIan Lepore         notify = 1;
8198fb0ef08SIan Lepore     else
8208fb0ef08SIan Lepore         notify = 0;
8218fb0ef08SIan Lepore 
8228fb0ef08SIan Lepore     irq_sc->last = hint;
8238fb0ef08SIan Lepore 
8248fb0ef08SIan Lepore     return (notify);
8258cfbb9cfSRui Paulo }
8268cfbb9cfSRui Paulo 
8278cfbb9cfSRui Paulo static int
ti_pruss_irq_kqfilter(struct cdev * cdev,struct knote * kn)8288fb0ef08SIan Lepore ti_pruss_irq_kqfilter(struct cdev *cdev, struct knote *kn)
8298cfbb9cfSRui Paulo {
8308fb0ef08SIan Lepore 	struct ti_pruss_irqsc *sc = cdev->si_drv1;
8318cfbb9cfSRui Paulo 
8328cfbb9cfSRui Paulo 	switch (kn->kn_filter) {
8338cfbb9cfSRui Paulo 	case EVFILT_READ:
8348cfbb9cfSRui Paulo 		kn->kn_hook = sc;
8358cfbb9cfSRui Paulo 		kn->kn_fop = &ti_pruss_kq_read;
836a4554c37SOleksandr Tymoshenko 		knlist_add(&sc->sc_selinfo.si_note, kn, 0);
8378cfbb9cfSRui Paulo 		break;
8388cfbb9cfSRui Paulo 	default:
8398cfbb9cfSRui Paulo 		return (EINVAL);
8408cfbb9cfSRui Paulo 	}
8418cfbb9cfSRui Paulo 
8428cfbb9cfSRui Paulo 	return (0);
8438cfbb9cfSRui Paulo }
844