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