ti_pruss.c revision 305572
1258210Srpaulo/*- 2258210Srpaulo * Copyright (c) 2013 Rui Paulo <rpaulo@FreeBSD.org> 3258210Srpaulo * All rights reserved. 4258210Srpaulo * 5258210Srpaulo * Redistribution and use in source and binary forms, with or without 6258210Srpaulo * modification, are permitted provided that the following conditions 7258210Srpaulo * are met: 8258210Srpaulo * 1. Redistributions of source code must retain the above copyright 9258210Srpaulo * notice, this list of conditions and the following disclaimer. 10258210Srpaulo * 2. Redistributions in binary form must reproduce the above copyright 11258210Srpaulo * notice, this list of conditions and the following disclaimer in the 12258210Srpaulo * documentation and/or other materials provided with the distribution. 13258210Srpaulo * 14258210Srpaulo * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 15258210Srpaulo * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 16258210Srpaulo * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17258210Srpaulo * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, 18258210Srpaulo * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 19258210Srpaulo * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 20258210Srpaulo * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21258210Srpaulo * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 22258210Srpaulo * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 23258210Srpaulo * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 24258210Srpaulo * POSSIBILITY OF SUCH DAMAGE. 25258210Srpaulo */ 26258210Srpaulo#include <sys/cdefs.h> 27258210Srpaulo__FBSDID("$FreeBSD: stable/11/sys/arm/ti/ti_pruss.c 305572 2016-09-07 23:36:46Z gonzo $"); 28258210Srpaulo 29258210Srpaulo#include <sys/param.h> 30258210Srpaulo#include <sys/systm.h> 31258210Srpaulo#include <sys/bus.h> 32258210Srpaulo#include <sys/conf.h> 33258210Srpaulo#include <sys/kernel.h> 34258210Srpaulo#include <sys/module.h> 35258210Srpaulo#include <sys/malloc.h> 36258210Srpaulo#include <sys/rman.h> 37258210Srpaulo#include <sys/event.h> 38258210Srpaulo#include <sys/selinfo.h> 39258210Srpaulo#include <machine/bus.h> 40258210Srpaulo#include <machine/cpu.h> 41258210Srpaulo#include <machine/frame.h> 42258210Srpaulo#include <machine/intr.h> 43258210Srpaulo 44258210Srpaulo#include <dev/fdt/fdt_common.h> 45258210Srpaulo#include <dev/ofw/openfirm.h> 46258210Srpaulo#include <dev/ofw/ofw_bus.h> 47258210Srpaulo#include <dev/ofw/ofw_bus_subr.h> 48258210Srpaulo 49258210Srpaulo#include <machine/bus.h> 50258210Srpaulo 51258210Srpaulo#include <arm/ti/ti_prcm.h> 52258210Srpaulo#include <arm/ti/ti_pruss.h> 53258210Srpaulo 54258210Srpaulo#ifdef DEBUG 55258210Srpaulo#define DPRINTF(fmt, ...) do { \ 56258210Srpaulo printf("%s: ", __func__); \ 57258210Srpaulo printf(fmt, __VA_ARGS__); \ 58258210Srpaulo} while (0) 59258210Srpaulo#else 60258210Srpaulo#define DPRINTF(fmt, ...) 61258210Srpaulo#endif 62258210Srpaulo 63258210Srpaulostatic device_probe_t ti_pruss_probe; 64258210Srpaulostatic device_attach_t ti_pruss_attach; 65258210Srpaulostatic device_detach_t ti_pruss_detach; 66258210Srpaulostatic void ti_pruss_intr(void *); 67258210Srpaulostatic d_open_t ti_pruss_open; 68258210Srpaulostatic d_mmap_t ti_pruss_mmap; 69258210Srpaulostatic void ti_pruss_kq_read_detach(struct knote *); 70258210Srpaulostatic int ti_pruss_kq_read_event(struct knote *, long); 71258210Srpaulostatic d_kqfilter_t ti_pruss_kqfilter; 72258210Srpaulo 73283138Srpaulo#define TI_PRUSS_IRQS 8 74283138Srpaulo 75258210Srpaulostruct ti_pruss_softc { 76258210Srpaulo struct mtx sc_mtx; 77258210Srpaulo struct resource *sc_mem_res; 78258210Srpaulo struct resource *sc_irq_res[TI_PRUSS_IRQS]; 79258210Srpaulo void *sc_intr[TI_PRUSS_IRQS]; 80258210Srpaulo bus_space_tag_t sc_bt; 81258210Srpaulo bus_space_handle_t sc_bh; 82258210Srpaulo struct cdev *sc_pdev; 83258210Srpaulo struct selinfo sc_selinfo; 84258210Srpaulo}; 85258210Srpaulo 86258210Srpaulostatic struct cdevsw ti_pruss_cdevsw = { 87258210Srpaulo .d_version = D_VERSION, 88258210Srpaulo .d_name = "ti_pruss", 89258210Srpaulo .d_open = ti_pruss_open, 90258210Srpaulo .d_mmap = ti_pruss_mmap, 91258210Srpaulo .d_kqfilter = ti_pruss_kqfilter, 92258210Srpaulo}; 93258210Srpaulo 94258210Srpaulostatic device_method_t ti_pruss_methods[] = { 95258210Srpaulo DEVMETHOD(device_probe, ti_pruss_probe), 96258210Srpaulo DEVMETHOD(device_attach, ti_pruss_attach), 97258210Srpaulo DEVMETHOD(device_detach, ti_pruss_detach), 98258210Srpaulo 99258210Srpaulo DEVMETHOD_END 100258210Srpaulo}; 101258210Srpaulo 102258210Srpaulostatic driver_t ti_pruss_driver = { 103258210Srpaulo "ti_pruss", 104258210Srpaulo ti_pruss_methods, 105258210Srpaulo sizeof(struct ti_pruss_softc) 106258210Srpaulo}; 107258210Srpaulo 108258210Srpaulostatic devclass_t ti_pruss_devclass; 109258210Srpaulo 110258210SrpauloDRIVER_MODULE(ti_pruss, simplebus, ti_pruss_driver, ti_pruss_devclass, 0, 0); 111258210Srpaulo 112258210Srpaulostatic struct resource_spec ti_pruss_irq_spec[] = { 113258210Srpaulo { SYS_RES_IRQ, 0, RF_ACTIVE }, 114258210Srpaulo { SYS_RES_IRQ, 1, RF_ACTIVE }, 115258210Srpaulo { SYS_RES_IRQ, 2, RF_ACTIVE }, 116258210Srpaulo { SYS_RES_IRQ, 3, RF_ACTIVE }, 117258210Srpaulo { SYS_RES_IRQ, 4, RF_ACTIVE }, 118258210Srpaulo { SYS_RES_IRQ, 5, RF_ACTIVE }, 119258210Srpaulo { SYS_RES_IRQ, 6, RF_ACTIVE }, 120258210Srpaulo { SYS_RES_IRQ, 7, RF_ACTIVE }, 121258210Srpaulo { -1, 0, 0 } 122258210Srpaulo}; 123283138SrpauloCTASSERT(TI_PRUSS_IRQS == nitems(ti_pruss_irq_spec) - 1); 124258210Srpaulo 125258210Srpaulostatic struct ti_pruss_irq_arg { 126258210Srpaulo int irq; 127258210Srpaulo struct ti_pruss_softc *sc; 128258210Srpaulo} ti_pruss_irq_args[TI_PRUSS_IRQS]; 129258210Srpaulo 130258210Srpaulostatic __inline uint32_t 131258210Srpauloti_pruss_reg_read(struct ti_pruss_softc *sc, uint32_t reg) 132258210Srpaulo{ 133258210Srpaulo return (bus_space_read_4(sc->sc_bt, sc->sc_bh, reg)); 134258210Srpaulo} 135258210Srpaulo 136258210Srpaulostatic __inline void 137258210Srpauloti_pruss_reg_write(struct ti_pruss_softc *sc, uint32_t reg, uint32_t val) 138258210Srpaulo{ 139258210Srpaulo bus_space_write_4(sc->sc_bt, sc->sc_bh, reg, val); 140258210Srpaulo} 141258210Srpaulo 142258210Srpaulostatic int 143258210Srpauloti_pruss_probe(device_t dev) 144258210Srpaulo{ 145261410Sian 146261410Sian if (!ofw_bus_status_okay(dev)) 147261410Sian return (ENXIO); 148261410Sian 149258210Srpaulo if (ofw_bus_is_compatible(dev, "ti,pruss-v1") || 150258210Srpaulo ofw_bus_is_compatible(dev, "ti,pruss-v2")) { 151258210Srpaulo device_set_desc(dev, "TI Programmable Realtime Unit Subsystem"); 152258210Srpaulo return (BUS_PROBE_DEFAULT); 153258210Srpaulo } 154258210Srpaulo 155258210Srpaulo return (ENXIO); 156258210Srpaulo} 157258210Srpaulo 158258210Srpaulostatic int 159258210Srpauloti_pruss_attach(device_t dev) 160258210Srpaulo{ 161258210Srpaulo struct ti_pruss_softc *sc; 162258210Srpaulo int rid, i; 163258210Srpaulo 164258210Srpaulo if (ti_prcm_clk_enable(PRUSS_CLK) != 0) { 165258210Srpaulo device_printf(dev, "could not enable PRUSS clock\n"); 166258210Srpaulo return (ENXIO); 167258210Srpaulo } 168258210Srpaulo sc = device_get_softc(dev); 169258210Srpaulo rid = 0; 170261211Sjmg mtx_init(&sc->sc_mtx, "TI PRUSS", NULL, MTX_DEF); 171283138Srpaulo knlist_init_mtx(&sc->sc_selinfo.si_note, &sc->sc_mtx); 172258210Srpaulo sc->sc_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, 173258210Srpaulo RF_ACTIVE); 174258210Srpaulo if (sc->sc_mem_res == NULL) { 175258210Srpaulo device_printf(dev, "could not allocate memory resource\n"); 176258210Srpaulo return (ENXIO); 177258210Srpaulo } 178258210Srpaulo sc->sc_bt = rman_get_bustag(sc->sc_mem_res); 179258210Srpaulo sc->sc_bh = rman_get_bushandle(sc->sc_mem_res); 180258210Srpaulo if (bus_alloc_resources(dev, ti_pruss_irq_spec, sc->sc_irq_res) != 0) { 181258210Srpaulo device_printf(dev, "could not allocate interrupt resource\n"); 182258210Srpaulo ti_pruss_detach(dev); 183258210Srpaulo return (ENXIO); 184258210Srpaulo } 185258210Srpaulo for (i = 0; i < TI_PRUSS_IRQS; i++) { 186258210Srpaulo ti_pruss_irq_args[i].irq = i; 187258210Srpaulo ti_pruss_irq_args[i].sc = sc; 188275376Srpaulo if (bus_setup_intr(dev, sc->sc_irq_res[i], 189258210Srpaulo INTR_MPSAFE | INTR_TYPE_MISC, 190275376Srpaulo NULL, ti_pruss_intr, &ti_pruss_irq_args[i], 191258210Srpaulo &sc->sc_intr[i]) != 0) { 192275376Srpaulo device_printf(dev, 193258210Srpaulo "unable to setup the interrupt handler\n"); 194258210Srpaulo ti_pruss_detach(dev); 195258210Srpaulo return (ENXIO); 196258210Srpaulo } 197258210Srpaulo } 198258210Srpaulo if (ti_pruss_reg_read(sc, PRUSS_AM18XX_INTC) == PRUSS_AM18XX_REV) 199258210Srpaulo device_printf(dev, "AM18xx PRU-ICSS\n"); 200258210Srpaulo else if (ti_pruss_reg_read(sc, PRUSS_AM33XX_INTC) == PRUSS_AM33XX_REV) 201258210Srpaulo device_printf(dev, "AM33xx PRU-ICSS\n"); 202258210Srpaulo 203258210Srpaulo sc->sc_pdev = make_dev(&ti_pruss_cdevsw, 0, UID_ROOT, GID_WHEEL, 204258210Srpaulo 0600, "pruss%d", device_get_unit(dev)); 205258210Srpaulo sc->sc_pdev->si_drv1 = dev; 206258210Srpaulo 207258210Srpaulo return (0); 208258210Srpaulo} 209258210Srpaulo 210258210Srpaulostatic int 211258210Srpauloti_pruss_detach(device_t dev) 212258210Srpaulo{ 213258210Srpaulo struct ti_pruss_softc *sc; 214258210Srpaulo int i; 215258210Srpaulo 216258210Srpaulo sc = device_get_softc(dev); 217258210Srpaulo for (i = 0; i < TI_PRUSS_IRQS; i++) { 218258210Srpaulo if (sc->sc_intr[i]) 219258210Srpaulo bus_teardown_intr(dev, sc->sc_irq_res[i], sc->sc_intr[i]); 220258210Srpaulo if (sc->sc_irq_res[i]) 221275376Srpaulo bus_release_resource(dev, SYS_RES_IRQ, 222258210Srpaulo rman_get_rid(sc->sc_irq_res[i]), 223258210Srpaulo sc->sc_irq_res[i]); 224258210Srpaulo } 225283138Srpaulo knlist_clear(&sc->sc_selinfo.si_note, 0); 226283138Srpaulo knlist_destroy(&sc->sc_selinfo.si_note); 227283138Srpaulo mtx_destroy(&sc->sc_mtx); 228258210Srpaulo if (sc->sc_mem_res) 229258210Srpaulo bus_release_resource(dev, SYS_RES_MEMORY, rman_get_rid(sc->sc_mem_res), 230258210Srpaulo sc->sc_mem_res); 231258210Srpaulo if (sc->sc_pdev) 232258210Srpaulo destroy_dev(sc->sc_pdev); 233258210Srpaulo 234258210Srpaulo return (0); 235258210Srpaulo} 236258210Srpaulo 237258210Srpaulostatic void 238258210Srpauloti_pruss_intr(void *arg) 239258210Srpaulo{ 240283138Srpaulo int val; 241283138Srpaulo struct ti_pruss_irq_arg *iap = arg; 242283138Srpaulo struct ti_pruss_softc *sc = iap->sc; 243283138Srpaulo /* 244283138Srpaulo * Interrupts pr1_host_intr[0:7] are mapped to 245283138Srpaulo * Host-2 to Host-9 of PRU-ICSS IRQ-controller. 246283138Srpaulo */ 247283138Srpaulo const int pru_int = iap->irq + 2; 248283138Srpaulo const int pru_int_mask = (1 << pru_int); 249258210Srpaulo 250283138Srpaulo val = ti_pruss_reg_read(sc, PRUSS_AM33XX_INTC + PRUSS_INTC_HIER); 251283138Srpaulo DPRINTF("interrupt %p, %d", sc, pru_int); 252283138Srpaulo if (!(val & pru_int_mask)) 253283138Srpaulo return; 254283138Srpaulo ti_pruss_reg_write(sc, PRUSS_AM33XX_INTC + PRUSS_INTC_HIDISR, 255283138Srpaulo pru_int); 256283138Srpaulo KNOTE_UNLOCKED(&sc->sc_selinfo.si_note, pru_int); 257258210Srpaulo} 258258210Srpaulo 259258210Srpaulostatic int 260275376Srpauloti_pruss_open(struct cdev *cdev __unused, int oflags __unused, 261275376Srpaulo int devtype __unused, struct thread *td __unused) 262258210Srpaulo{ 263258210Srpaulo return (0); 264258210Srpaulo} 265258210Srpaulo 266258210Srpaulostatic int 267258210Srpauloti_pruss_mmap(struct cdev *cdev, vm_ooffset_t offset, vm_paddr_t *paddr, 268258210Srpaulo int nprot, vm_memattr_t *memattr) 269258210Srpaulo{ 270258210Srpaulo device_t dev = cdev->si_drv1; 271258210Srpaulo struct ti_pruss_softc *sc = device_get_softc(dev); 272258210Srpaulo 273258210Srpaulo if (offset > rman_get_size(sc->sc_mem_res)) 274258210Srpaulo return (-1); 275258210Srpaulo *paddr = rman_get_start(sc->sc_mem_res) + offset; 276277958Srpaulo *memattr = VM_MEMATTR_UNCACHEABLE; 277258210Srpaulo 278258210Srpaulo return (0); 279258210Srpaulo} 280258210Srpaulo 281258210Srpaulostatic struct filterops ti_pruss_kq_read = { 282258210Srpaulo .f_isfd = 1, 283258210Srpaulo .f_detach = ti_pruss_kq_read_detach, 284258210Srpaulo .f_event = ti_pruss_kq_read_event, 285258210Srpaulo}; 286258210Srpaulo 287258210Srpaulostatic void 288258210Srpauloti_pruss_kq_read_detach(struct knote *kn) 289258210Srpaulo{ 290258210Srpaulo struct ti_pruss_softc *sc = kn->kn_hook; 291258210Srpaulo 292258210Srpaulo knlist_remove(&sc->sc_selinfo.si_note, kn, 0); 293258210Srpaulo} 294258210Srpaulo 295258210Srpaulostatic int 296258210Srpauloti_pruss_kq_read_event(struct knote *kn, long hint) 297258210Srpaulo{ 298258210Srpaulo kn->kn_data = hint; 299258210Srpaulo 300258210Srpaulo return (hint); 301258210Srpaulo} 302258210Srpaulo 303258210Srpaulostatic int 304258210Srpauloti_pruss_kqfilter(struct cdev *cdev, struct knote *kn) 305258210Srpaulo{ 306258210Srpaulo device_t dev = cdev->si_drv1; 307258210Srpaulo struct ti_pruss_softc *sc = device_get_softc(dev); 308258210Srpaulo 309258210Srpaulo switch (kn->kn_filter) { 310258210Srpaulo case EVFILT_READ: 311258210Srpaulo kn->kn_hook = sc; 312258210Srpaulo kn->kn_fop = &ti_pruss_kq_read; 313305572Sgonzo knlist_add(&sc->sc_selinfo.si_note, kn, 0); 314258210Srpaulo break; 315258210Srpaulo default: 316258210Srpaulo return (EINVAL); 317258210Srpaulo } 318258210Srpaulo 319258210Srpaulo return (0); 320258210Srpaulo} 321