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