ti_pruss.c revision 261410
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: head/sys/arm/ti/ti_pruss.c 261410 2014-02-02 19:17:28Z ian $"); 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#include <machine/fdt.h> 51258210Srpaulo 52258210Srpaulo#include <arm/ti/ti_prcm.h> 53258210Srpaulo#include <arm/ti/ti_pruss.h> 54258210Srpaulo 55258210Srpaulo#define DEBUG 56258210Srpaulo#ifdef DEBUG 57258210Srpaulo#define DPRINTF(fmt, ...) do { \ 58258210Srpaulo printf("%s: ", __func__); \ 59258210Srpaulo printf(fmt, __VA_ARGS__); \ 60258210Srpaulo} while (0) 61258210Srpaulo#else 62258210Srpaulo#define DPRINTF(fmt, ...) 63258210Srpaulo#endif 64258210Srpaulo 65258210Srpaulostatic device_probe_t ti_pruss_probe; 66258210Srpaulostatic device_attach_t ti_pruss_attach; 67258210Srpaulostatic device_detach_t ti_pruss_detach; 68258210Srpaulostatic void ti_pruss_intr(void *); 69258210Srpaulostatic d_open_t ti_pruss_open; 70258210Srpaulostatic d_close_t ti_pruss_close; 71258210Srpaulostatic d_mmap_t ti_pruss_mmap; 72258210Srpaulostatic void ti_pruss_kq_read_detach(struct knote *); 73258210Srpaulostatic int ti_pruss_kq_read_event(struct knote *, long); 74258210Srpaulostatic d_kqfilter_t ti_pruss_kqfilter; 75258210Srpaulo 76258210Srpaulo#define TI_PRUSS_IRQS 8 77258210Srpaulostruct ti_pruss_softc { 78258210Srpaulo struct mtx sc_mtx; 79258210Srpaulo struct resource *sc_mem_res; 80258210Srpaulo struct resource *sc_irq_res[TI_PRUSS_IRQS]; 81258210Srpaulo void *sc_intr[TI_PRUSS_IRQS]; 82258210Srpaulo bus_space_tag_t sc_bt; 83258210Srpaulo bus_space_handle_t sc_bh; 84258210Srpaulo struct cdev *sc_pdev; 85258210Srpaulo struct selinfo sc_selinfo; 86258210Srpaulo uint32_t sc_inuse; 87258210Srpaulo}; 88258210Srpaulo 89258210Srpaulostatic struct cdevsw ti_pruss_cdevsw = { 90258210Srpaulo .d_version = D_VERSION, 91258210Srpaulo .d_name = "ti_pruss", 92258210Srpaulo .d_open = ti_pruss_open, 93258210Srpaulo .d_close = ti_pruss_close, 94258210Srpaulo .d_mmap = ti_pruss_mmap, 95258210Srpaulo .d_kqfilter = ti_pruss_kqfilter, 96258210Srpaulo}; 97258210Srpaulo 98258210Srpaulostatic device_method_t ti_pruss_methods[] = { 99258210Srpaulo DEVMETHOD(device_probe, ti_pruss_probe), 100258210Srpaulo DEVMETHOD(device_attach, ti_pruss_attach), 101258210Srpaulo DEVMETHOD(device_detach, ti_pruss_detach), 102258210Srpaulo 103258210Srpaulo DEVMETHOD_END 104258210Srpaulo}; 105258210Srpaulo 106258210Srpaulostatic driver_t ti_pruss_driver = { 107258210Srpaulo "ti_pruss", 108258210Srpaulo ti_pruss_methods, 109258210Srpaulo sizeof(struct ti_pruss_softc) 110258210Srpaulo}; 111258210Srpaulo 112258210Srpaulostatic devclass_t ti_pruss_devclass; 113258210Srpaulo 114258210SrpauloDRIVER_MODULE(ti_pruss, simplebus, ti_pruss_driver, ti_pruss_devclass, 0, 0); 115258210Srpaulo 116258210Srpaulostatic struct resource_spec ti_pruss_irq_spec[] = { 117258210Srpaulo { SYS_RES_IRQ, 0, RF_ACTIVE }, 118258210Srpaulo { SYS_RES_IRQ, 1, RF_ACTIVE }, 119258210Srpaulo { SYS_RES_IRQ, 2, RF_ACTIVE }, 120258210Srpaulo { SYS_RES_IRQ, 3, RF_ACTIVE }, 121258210Srpaulo { SYS_RES_IRQ, 4, RF_ACTIVE }, 122258210Srpaulo { SYS_RES_IRQ, 5, RF_ACTIVE }, 123258210Srpaulo { SYS_RES_IRQ, 6, RF_ACTIVE }, 124258210Srpaulo { SYS_RES_IRQ, 7, RF_ACTIVE }, 125258210Srpaulo { -1, 0, 0 } 126258210Srpaulo}; 127258210Srpaulo 128258210Srpaulostatic struct ti_pruss_irq_arg { 129258210Srpaulo int irq; 130258210Srpaulo struct ti_pruss_softc *sc; 131258210Srpaulo} ti_pruss_irq_args[TI_PRUSS_IRQS]; 132258210Srpaulo 133258210Srpaulostatic __inline uint32_t 134258210Srpauloti_pruss_reg_read(struct ti_pruss_softc *sc, uint32_t reg) 135258210Srpaulo{ 136258210Srpaulo return (bus_space_read_4(sc->sc_bt, sc->sc_bh, reg)); 137258210Srpaulo} 138258210Srpaulo 139258210Srpaulostatic __inline void 140258210Srpauloti_pruss_reg_write(struct ti_pruss_softc *sc, uint32_t reg, uint32_t val) 141258210Srpaulo{ 142258210Srpaulo bus_space_write_4(sc->sc_bt, sc->sc_bh, reg, val); 143258210Srpaulo} 144258210Srpaulo 145258210Srpaulostatic int 146258210Srpauloti_pruss_probe(device_t dev) 147258210Srpaulo{ 148261410Sian 149261410Sian if (!ofw_bus_status_okay(dev)) 150261410Sian return (ENXIO); 151261410Sian 152258210Srpaulo if (ofw_bus_is_compatible(dev, "ti,pruss-v1") || 153258210Srpaulo ofw_bus_is_compatible(dev, "ti,pruss-v2")) { 154258210Srpaulo device_set_desc(dev, "TI Programmable Realtime Unit Subsystem"); 155258210Srpaulo return (BUS_PROBE_DEFAULT); 156258210Srpaulo } 157258210Srpaulo 158258210Srpaulo return (ENXIO); 159258210Srpaulo} 160258210Srpaulo 161258210Srpaulostatic int 162258210Srpauloti_pruss_attach(device_t dev) 163258210Srpaulo{ 164258210Srpaulo struct ti_pruss_softc *sc; 165258210Srpaulo int rid, i; 166258210Srpaulo 167258210Srpaulo if (ti_prcm_clk_enable(PRUSS_CLK) != 0) { 168258210Srpaulo device_printf(dev, "could not enable PRUSS clock\n"); 169258210Srpaulo return (ENXIO); 170258210Srpaulo } 171258210Srpaulo sc = device_get_softc(dev); 172258210Srpaulo rid = 0; 173261211Sjmg mtx_init(&sc->sc_mtx, "TI PRUSS", NULL, MTX_DEF); 174258210Srpaulo sc->sc_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, 175258210Srpaulo RF_ACTIVE); 176258210Srpaulo if (sc->sc_mem_res == NULL) { 177258210Srpaulo device_printf(dev, "could not allocate memory resource\n"); 178258210Srpaulo return (ENXIO); 179258210Srpaulo } 180258210Srpaulo sc->sc_bt = rman_get_bustag(sc->sc_mem_res); 181258210Srpaulo sc->sc_bh = rman_get_bushandle(sc->sc_mem_res); 182258210Srpaulo if (bus_alloc_resources(dev, ti_pruss_irq_spec, sc->sc_irq_res) != 0) { 183258210Srpaulo device_printf(dev, "could not allocate interrupt resource\n"); 184258210Srpaulo ti_pruss_detach(dev); 185258210Srpaulo return (ENXIO); 186258210Srpaulo } 187258210Srpaulo for (i = 0; i < TI_PRUSS_IRQS; i++) { 188258210Srpaulo ti_pruss_irq_args[i].irq = i; 189258210Srpaulo ti_pruss_irq_args[i].sc = sc; 190258210Srpaulo if (bus_setup_intr(dev, sc->sc_irq_res[i], 191258210Srpaulo INTR_MPSAFE | INTR_TYPE_MISC, 192258210Srpaulo NULL, ti_pruss_intr, &ti_pruss_irq_args[i], 193258210Srpaulo &sc->sc_intr[i]) != 0) { 194258210Srpaulo device_printf(dev, 195258210Srpaulo "unable to setup the interrupt handler\n"); 196258210Srpaulo ti_pruss_detach(dev); 197258210Srpaulo return (ENXIO); 198258210Srpaulo } 199258210Srpaulo } 200258210Srpaulo if (ti_pruss_reg_read(sc, PRUSS_AM18XX_INTC) == PRUSS_AM18XX_REV) 201258210Srpaulo device_printf(dev, "AM18xx PRU-ICSS\n"); 202258210Srpaulo else if (ti_pruss_reg_read(sc, PRUSS_AM33XX_INTC) == PRUSS_AM33XX_REV) 203258210Srpaulo device_printf(dev, "AM33xx PRU-ICSS\n"); 204258210Srpaulo 205258210Srpaulo sc->sc_pdev = make_dev(&ti_pruss_cdevsw, 0, UID_ROOT, GID_WHEEL, 206258210Srpaulo 0600, "pruss%d", device_get_unit(dev)); 207258210Srpaulo sc->sc_pdev->si_drv1 = dev; 208258210Srpaulo 209258210Srpaulo return (0); 210258210Srpaulo} 211258210Srpaulo 212258210Srpaulostatic int 213258210Srpauloti_pruss_detach(device_t dev) 214258210Srpaulo{ 215258210Srpaulo struct ti_pruss_softc *sc; 216258210Srpaulo int i; 217258210Srpaulo 218258210Srpaulo sc = device_get_softc(dev); 219258210Srpaulo for (i = 0; i < TI_PRUSS_IRQS; i++) { 220258210Srpaulo if (sc->sc_intr[i]) 221258210Srpaulo bus_teardown_intr(dev, sc->sc_irq_res[i], sc->sc_intr[i]); 222258210Srpaulo if (sc->sc_irq_res[i]) 223258210Srpaulo bus_release_resource(dev, SYS_RES_IRQ, 224258210Srpaulo rman_get_rid(sc->sc_irq_res[i]), 225258210Srpaulo sc->sc_irq_res[i]); 226258210Srpaulo } 227258210Srpaulo if (sc->sc_mem_res) 228258210Srpaulo bus_release_resource(dev, SYS_RES_MEMORY, rman_get_rid(sc->sc_mem_res), 229258210Srpaulo sc->sc_mem_res); 230258210Srpaulo if (sc->sc_pdev) 231258210Srpaulo destroy_dev(sc->sc_pdev); 232258210Srpaulo 233258210Srpaulo return (0); 234258210Srpaulo} 235258210Srpaulo 236258210Srpaulostatic void 237258210Srpauloti_pruss_intr(void *arg) 238258210Srpaulo{ 239258210Srpaulo struct ti_pruss_irq_arg *iap; 240258210Srpaulo struct ti_pruss_softc *sc; 241258210Srpaulo 242258210Srpaulo iap = arg; 243258210Srpaulo sc = iap->sc; 244258210Srpaulo DPRINTF("interrupt %p", sc); 245258210Srpaulo KNOTE_UNLOCKED(&sc->sc_selinfo.si_note, iap->irq); 246258210Srpaulo} 247258210Srpaulo 248258210Srpaulostatic int 249258210Srpauloti_pruss_open(struct cdev *cdev, int oflags, int devtype, struct thread *td) 250258210Srpaulo{ 251258210Srpaulo device_t dev = cdev->si_drv1; 252258210Srpaulo struct ti_pruss_softc *sc = device_get_softc(dev); 253258210Srpaulo 254258210Srpaulo if (atomic_cmpset_32(&sc->sc_inuse, 0, 1) == 0) 255258210Srpaulo return (EBUSY); 256258210Srpaulo else 257258210Srpaulo return (0); 258258210Srpaulo} 259258210Srpaulo 260258210Srpaulostatic int 261258210Srpauloti_pruss_close(struct cdev *cdev, int fflag, int devtype, struct thread *td) 262258210Srpaulo{ 263258210Srpaulo device_t dev = cdev->si_drv1; 264258210Srpaulo struct ti_pruss_softc *sc = device_get_softc(dev); 265258210Srpaulo 266258210Srpaulo sc->sc_inuse = 0; 267258210Srpaulo 268258210Srpaulo return (0); 269258210Srpaulo} 270258210Srpaulo 271258210Srpaulostatic int 272258210Srpauloti_pruss_mmap(struct cdev *cdev, vm_ooffset_t offset, vm_paddr_t *paddr, 273258210Srpaulo int nprot, vm_memattr_t *memattr) 274258210Srpaulo{ 275258210Srpaulo device_t dev = cdev->si_drv1; 276258210Srpaulo struct ti_pruss_softc *sc = device_get_softc(dev); 277258210Srpaulo 278258210Srpaulo if (offset > rman_get_size(sc->sc_mem_res)) 279258210Srpaulo return (-1); 280258210Srpaulo *paddr = rman_get_start(sc->sc_mem_res) + offset; 281258210Srpaulo 282258210Srpaulo return (0); 283258210Srpaulo} 284258210Srpaulo 285258210Srpaulostatic struct filterops ti_pruss_kq_read = { 286258210Srpaulo .f_isfd = 1, 287258210Srpaulo .f_detach = ti_pruss_kq_read_detach, 288258210Srpaulo .f_event = ti_pruss_kq_read_event, 289258210Srpaulo}; 290258210Srpaulo 291258210Srpaulostatic void 292258210Srpauloti_pruss_kq_read_detach(struct knote *kn) 293258210Srpaulo{ 294258210Srpaulo struct ti_pruss_softc *sc = kn->kn_hook; 295258210Srpaulo 296258210Srpaulo knlist_remove(&sc->sc_selinfo.si_note, kn, 0); 297258210Srpaulo} 298258210Srpaulo 299258210Srpaulostatic int 300258210Srpauloti_pruss_kq_read_event(struct knote *kn, long hint) 301258210Srpaulo{ 302258210Srpaulo kn->kn_data = hint; 303258210Srpaulo 304258210Srpaulo return (hint); 305258210Srpaulo} 306258210Srpaulo 307258210Srpaulostatic int 308258210Srpauloti_pruss_kqfilter(struct cdev *cdev, struct knote *kn) 309258210Srpaulo{ 310258210Srpaulo device_t dev = cdev->si_drv1; 311258210Srpaulo struct ti_pruss_softc *sc = device_get_softc(dev); 312258210Srpaulo 313258210Srpaulo switch (kn->kn_filter) { 314258210Srpaulo case EVFILT_READ: 315258210Srpaulo kn->kn_hook = sc; 316258210Srpaulo kn->kn_fop = &ti_pruss_kq_read; 317258210Srpaulo knlist_add(&sc->sc_selinfo.si_note, kn, 1); 318258210Srpaulo break; 319258210Srpaulo default: 320258210Srpaulo return (EINVAL); 321258210Srpaulo } 322258210Srpaulo 323258210Srpaulo return (0); 324258210Srpaulo} 325