1*1b1a53cfSOleksandr Tymoshenko /*- 2*1b1a53cfSOleksandr Tymoshenko * Copyright (c) 2012 Oleksandr Tymoshenko <gonzo@freebsd.org> 3*1b1a53cfSOleksandr Tymoshenko * All rights reserved. 4*1b1a53cfSOleksandr Tymoshenko * 5*1b1a53cfSOleksandr Tymoshenko * Redistribution and use in source and binary forms, with or without 6*1b1a53cfSOleksandr Tymoshenko * modification, are permitted provided that the following conditions 7*1b1a53cfSOleksandr Tymoshenko * are met: 8*1b1a53cfSOleksandr Tymoshenko * 1. Redistributions of source code must retain the above copyright 9*1b1a53cfSOleksandr Tymoshenko * notice, this list of conditions and the following disclaimer. 10*1b1a53cfSOleksandr Tymoshenko * 2. Redistributions in binary form must reproduce the above copyright 11*1b1a53cfSOleksandr Tymoshenko * notice, this list of conditions and the following disclaimer in the 12*1b1a53cfSOleksandr Tymoshenko * documentation and/or other materials provided with the distribution. 13*1b1a53cfSOleksandr Tymoshenko * 14*1b1a53cfSOleksandr Tymoshenko * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15*1b1a53cfSOleksandr Tymoshenko * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16*1b1a53cfSOleksandr Tymoshenko * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17*1b1a53cfSOleksandr Tymoshenko * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18*1b1a53cfSOleksandr Tymoshenko * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19*1b1a53cfSOleksandr Tymoshenko * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20*1b1a53cfSOleksandr Tymoshenko * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21*1b1a53cfSOleksandr Tymoshenko * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22*1b1a53cfSOleksandr Tymoshenko * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23*1b1a53cfSOleksandr Tymoshenko * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24*1b1a53cfSOleksandr Tymoshenko * SUCH DAMAGE. 25*1b1a53cfSOleksandr Tymoshenko */ 26*1b1a53cfSOleksandr Tymoshenko 27*1b1a53cfSOleksandr Tymoshenko #include <sys/cdefs.h> 28*1b1a53cfSOleksandr Tymoshenko __FBSDID("$FreeBSD$"); 29*1b1a53cfSOleksandr Tymoshenko 30*1b1a53cfSOleksandr Tymoshenko #include <sys/param.h> 31*1b1a53cfSOleksandr Tymoshenko #include <sys/systm.h> 32*1b1a53cfSOleksandr Tymoshenko #include <sys/bus.h> 33*1b1a53cfSOleksandr Tymoshenko #include <sys/kernel.h> 34*1b1a53cfSOleksandr Tymoshenko #include <sys/module.h> 35*1b1a53cfSOleksandr Tymoshenko #include <sys/malloc.h> 36*1b1a53cfSOleksandr Tymoshenko #include <sys/rman.h> 37*1b1a53cfSOleksandr Tymoshenko #include <sys/timeet.h> 38*1b1a53cfSOleksandr Tymoshenko #include <sys/timetc.h> 39*1b1a53cfSOleksandr Tymoshenko #include <sys/watchdog.h> 40*1b1a53cfSOleksandr Tymoshenko #include <machine/bus.h> 41*1b1a53cfSOleksandr Tymoshenko #include <machine/cpu.h> 42*1b1a53cfSOleksandr Tymoshenko #include <machine/frame.h> 43*1b1a53cfSOleksandr Tymoshenko #include <machine/intr.h> 44*1b1a53cfSOleksandr Tymoshenko 45*1b1a53cfSOleksandr Tymoshenko #include <dev/fdt/fdt_common.h> 46*1b1a53cfSOleksandr Tymoshenko #include <dev/ofw/openfirm.h> 47*1b1a53cfSOleksandr Tymoshenko #include <dev/ofw/ofw_bus.h> 48*1b1a53cfSOleksandr Tymoshenko #include <dev/ofw/ofw_bus_subr.h> 49*1b1a53cfSOleksandr Tymoshenko 50*1b1a53cfSOleksandr Tymoshenko #include <machine/bus.h> 51*1b1a53cfSOleksandr Tymoshenko #include <machine/fdt.h> 52*1b1a53cfSOleksandr Tymoshenko #include <arm/broadcom/bcm2835/bcm2835_mbox.h> 53*1b1a53cfSOleksandr Tymoshenko 54*1b1a53cfSOleksandr Tymoshenko #define REG_READ 0x00 55*1b1a53cfSOleksandr Tymoshenko #define REG_POL 0x10 56*1b1a53cfSOleksandr Tymoshenko #define REG_SENDER 0x14 57*1b1a53cfSOleksandr Tymoshenko #define REG_STATUS 0x18 58*1b1a53cfSOleksandr Tymoshenko #define STATUS_FULL 0x80000000 59*1b1a53cfSOleksandr Tymoshenko #define STATUS_EMPTY 0x40000000 60*1b1a53cfSOleksandr Tymoshenko #define REG_CONFIG 0x1C 61*1b1a53cfSOleksandr Tymoshenko #define CONFIG_DATA_IRQ 0x00000001 62*1b1a53cfSOleksandr Tymoshenko #define REG_WRITE 0x20 /* This is Mailbox 1 address */ 63*1b1a53cfSOleksandr Tymoshenko 64*1b1a53cfSOleksandr Tymoshenko #define MBOX_MSG(chan, data) (((data) & ~0xf) | ((chan) & 0xf)) 65*1b1a53cfSOleksandr Tymoshenko #define MBOX_CHAN(msg) ((msg) & 0xf) 66*1b1a53cfSOleksandr Tymoshenko #define MBOX_DATA(msg) ((msg) & ~0xf) 67*1b1a53cfSOleksandr Tymoshenko 68*1b1a53cfSOleksandr Tymoshenko #define MBOX_LOCK do { \ 69*1b1a53cfSOleksandr Tymoshenko mtx_lock(&bcm_mbox_sc->lock); \ 70*1b1a53cfSOleksandr Tymoshenko } while(0) 71*1b1a53cfSOleksandr Tymoshenko 72*1b1a53cfSOleksandr Tymoshenko #define MBOX_UNLOCK do { \ 73*1b1a53cfSOleksandr Tymoshenko mtx_unlock(&bcm_mbox_sc->lock); \ 74*1b1a53cfSOleksandr Tymoshenko } while(0) 75*1b1a53cfSOleksandr Tymoshenko 76*1b1a53cfSOleksandr Tymoshenko #ifdef DEBUG 77*1b1a53cfSOleksandr Tymoshenko #define dprintf(fmt, args...) printf(fmt, ##args) 78*1b1a53cfSOleksandr Tymoshenko #else 79*1b1a53cfSOleksandr Tymoshenko #define dprintf(fmt, args...) 80*1b1a53cfSOleksandr Tymoshenko #endif 81*1b1a53cfSOleksandr Tymoshenko 82*1b1a53cfSOleksandr Tymoshenko struct bcm_mbox_softc { 83*1b1a53cfSOleksandr Tymoshenko struct mtx lock; 84*1b1a53cfSOleksandr Tymoshenko struct resource * mem_res; 85*1b1a53cfSOleksandr Tymoshenko struct resource * irq_res; 86*1b1a53cfSOleksandr Tymoshenko void* intr_hl; 87*1b1a53cfSOleksandr Tymoshenko bus_space_tag_t bst; 88*1b1a53cfSOleksandr Tymoshenko bus_space_handle_t bsh; 89*1b1a53cfSOleksandr Tymoshenko int valid[BCM2835_MBOX_CHANS]; 90*1b1a53cfSOleksandr Tymoshenko int msg[BCM2835_MBOX_CHANS]; 91*1b1a53cfSOleksandr Tymoshenko }; 92*1b1a53cfSOleksandr Tymoshenko 93*1b1a53cfSOleksandr Tymoshenko static struct bcm_mbox_softc *bcm_mbox_sc = NULL; 94*1b1a53cfSOleksandr Tymoshenko 95*1b1a53cfSOleksandr Tymoshenko #define mbox_read_4(reg) \ 96*1b1a53cfSOleksandr Tymoshenko bus_space_read_4(bcm_mbox_sc->bst, bcm_mbox_sc->bsh, reg) 97*1b1a53cfSOleksandr Tymoshenko #define mbox_write_4(reg, val) \ 98*1b1a53cfSOleksandr Tymoshenko bus_space_write_4(bcm_mbox_sc->bst, bcm_mbox_sc->bsh, reg, val) 99*1b1a53cfSOleksandr Tymoshenko 100*1b1a53cfSOleksandr Tymoshenko static void 101*1b1a53cfSOleksandr Tymoshenko bcm_mbox_intr(void *arg) 102*1b1a53cfSOleksandr Tymoshenko { 103*1b1a53cfSOleksandr Tymoshenko struct bcm_mbox_softc *sc = arg; 104*1b1a53cfSOleksandr Tymoshenko int chan; 105*1b1a53cfSOleksandr Tymoshenko uint32_t data; 106*1b1a53cfSOleksandr Tymoshenko uint32_t msg; 107*1b1a53cfSOleksandr Tymoshenko 108*1b1a53cfSOleksandr Tymoshenko MBOX_LOCK; 109*1b1a53cfSOleksandr Tymoshenko while (!(mbox_read_4(REG_STATUS) & STATUS_EMPTY)) { 110*1b1a53cfSOleksandr Tymoshenko msg = mbox_read_4(REG_READ); 111*1b1a53cfSOleksandr Tymoshenko dprintf("bcm_mbox_intr: raw data %08x\n", msg); 112*1b1a53cfSOleksandr Tymoshenko chan = MBOX_CHAN(msg); 113*1b1a53cfSOleksandr Tymoshenko data = MBOX_DATA(msg); 114*1b1a53cfSOleksandr Tymoshenko if (sc->valid[chan]) { 115*1b1a53cfSOleksandr Tymoshenko printf("bcm_mbox_intr: channel %d oveflow\n", chan); 116*1b1a53cfSOleksandr Tymoshenko continue; 117*1b1a53cfSOleksandr Tymoshenko } 118*1b1a53cfSOleksandr Tymoshenko dprintf("bcm_mbox_intr: chan %d, data %08x\n", chan, data); 119*1b1a53cfSOleksandr Tymoshenko sc->msg[chan] = data; 120*1b1a53cfSOleksandr Tymoshenko sc->valid[chan] = 1; 121*1b1a53cfSOleksandr Tymoshenko wakeup(&sc->msg[chan]); 122*1b1a53cfSOleksandr Tymoshenko 123*1b1a53cfSOleksandr Tymoshenko } 124*1b1a53cfSOleksandr Tymoshenko MBOX_UNLOCK; 125*1b1a53cfSOleksandr Tymoshenko } 126*1b1a53cfSOleksandr Tymoshenko 127*1b1a53cfSOleksandr Tymoshenko static int 128*1b1a53cfSOleksandr Tymoshenko bcm_mbox_probe(device_t dev) 129*1b1a53cfSOleksandr Tymoshenko { 130*1b1a53cfSOleksandr Tymoshenko 131*1b1a53cfSOleksandr Tymoshenko if (ofw_bus_is_compatible(dev, "broadcom,bcm2835-mbox")) { 132*1b1a53cfSOleksandr Tymoshenko device_set_desc(dev, "BCM2835 VideoCore Mailbox"); 133*1b1a53cfSOleksandr Tymoshenko return(BUS_PROBE_DEFAULT); 134*1b1a53cfSOleksandr Tymoshenko } 135*1b1a53cfSOleksandr Tymoshenko 136*1b1a53cfSOleksandr Tymoshenko return (ENXIO); 137*1b1a53cfSOleksandr Tymoshenko } 138*1b1a53cfSOleksandr Tymoshenko 139*1b1a53cfSOleksandr Tymoshenko static int 140*1b1a53cfSOleksandr Tymoshenko bcm_mbox_attach(device_t dev) 141*1b1a53cfSOleksandr Tymoshenko { 142*1b1a53cfSOleksandr Tymoshenko struct bcm_mbox_softc *sc = device_get_softc(dev); 143*1b1a53cfSOleksandr Tymoshenko int i; 144*1b1a53cfSOleksandr Tymoshenko int rid = 0; 145*1b1a53cfSOleksandr Tymoshenko 146*1b1a53cfSOleksandr Tymoshenko if (bcm_mbox_sc != NULL) 147*1b1a53cfSOleksandr Tymoshenko return (EINVAL); 148*1b1a53cfSOleksandr Tymoshenko 149*1b1a53cfSOleksandr Tymoshenko sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); 150*1b1a53cfSOleksandr Tymoshenko if (sc->mem_res == NULL) { 151*1b1a53cfSOleksandr Tymoshenko device_printf(dev, "could not allocate memory resource\n"); 152*1b1a53cfSOleksandr Tymoshenko return (ENXIO); 153*1b1a53cfSOleksandr Tymoshenko } 154*1b1a53cfSOleksandr Tymoshenko 155*1b1a53cfSOleksandr Tymoshenko sc->bst = rman_get_bustag(sc->mem_res); 156*1b1a53cfSOleksandr Tymoshenko sc->bsh = rman_get_bushandle(sc->mem_res); 157*1b1a53cfSOleksandr Tymoshenko 158*1b1a53cfSOleksandr Tymoshenko rid = 0; 159*1b1a53cfSOleksandr Tymoshenko sc->irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_ACTIVE); 160*1b1a53cfSOleksandr Tymoshenko if (sc->irq_res == NULL) { 161*1b1a53cfSOleksandr Tymoshenko device_printf(dev, "could not allocate interrupt resource\n"); 162*1b1a53cfSOleksandr Tymoshenko return (ENXIO); 163*1b1a53cfSOleksandr Tymoshenko } 164*1b1a53cfSOleksandr Tymoshenko 165*1b1a53cfSOleksandr Tymoshenko /* Setup and enable the timer */ 166*1b1a53cfSOleksandr Tymoshenko if (bus_setup_intr(dev, sc->irq_res, INTR_TYPE_MISC, 167*1b1a53cfSOleksandr Tymoshenko NULL, bcm_mbox_intr, sc, 168*1b1a53cfSOleksandr Tymoshenko &sc->intr_hl) != 0) { 169*1b1a53cfSOleksandr Tymoshenko bus_release_resource(dev, SYS_RES_IRQ, rid, 170*1b1a53cfSOleksandr Tymoshenko sc->irq_res); 171*1b1a53cfSOleksandr Tymoshenko device_printf(dev, "Unable to setup the clock irq handler.\n"); 172*1b1a53cfSOleksandr Tymoshenko return (ENXIO); 173*1b1a53cfSOleksandr Tymoshenko } 174*1b1a53cfSOleksandr Tymoshenko 175*1b1a53cfSOleksandr Tymoshenko mtx_init(&sc->lock, "vcio mbox", MTX_DEF, 0); 176*1b1a53cfSOleksandr Tymoshenko for (i = 0; i < BCM2835_MBOX_CHANS; i++) { 177*1b1a53cfSOleksandr Tymoshenko sc->valid[0] = 0; 178*1b1a53cfSOleksandr Tymoshenko sc->msg[0] = 0; 179*1b1a53cfSOleksandr Tymoshenko } 180*1b1a53cfSOleksandr Tymoshenko 181*1b1a53cfSOleksandr Tymoshenko bcm_mbox_sc = sc; 182*1b1a53cfSOleksandr Tymoshenko /* Read all pending messages */ 183*1b1a53cfSOleksandr Tymoshenko bcm_mbox_intr(sc); 184*1b1a53cfSOleksandr Tymoshenko 185*1b1a53cfSOleksandr Tymoshenko /* Should be called after bcm_mbox_sc initialization */ 186*1b1a53cfSOleksandr Tymoshenko mbox_write_4(REG_CONFIG, CONFIG_DATA_IRQ); 187*1b1a53cfSOleksandr Tymoshenko 188*1b1a53cfSOleksandr Tymoshenko return (0); 189*1b1a53cfSOleksandr Tymoshenko } 190*1b1a53cfSOleksandr Tymoshenko 191*1b1a53cfSOleksandr Tymoshenko static device_method_t bcm_mbox_methods[] = { 192*1b1a53cfSOleksandr Tymoshenko DEVMETHOD(device_probe, bcm_mbox_probe), 193*1b1a53cfSOleksandr Tymoshenko DEVMETHOD(device_attach, bcm_mbox_attach), 194*1b1a53cfSOleksandr Tymoshenko { 0, 0 } 195*1b1a53cfSOleksandr Tymoshenko }; 196*1b1a53cfSOleksandr Tymoshenko 197*1b1a53cfSOleksandr Tymoshenko static driver_t bcm_mbox_driver = { 198*1b1a53cfSOleksandr Tymoshenko "mbox", 199*1b1a53cfSOleksandr Tymoshenko bcm_mbox_methods, 200*1b1a53cfSOleksandr Tymoshenko sizeof(struct bcm_mbox_softc), 201*1b1a53cfSOleksandr Tymoshenko }; 202*1b1a53cfSOleksandr Tymoshenko 203*1b1a53cfSOleksandr Tymoshenko static devclass_t bcm_mbox_devclass; 204*1b1a53cfSOleksandr Tymoshenko 205*1b1a53cfSOleksandr Tymoshenko DRIVER_MODULE(mbox, simplebus, bcm_mbox_driver, bcm_mbox_devclass, 0, 0); 206*1b1a53cfSOleksandr Tymoshenko 207*1b1a53cfSOleksandr Tymoshenko /* 208*1b1a53cfSOleksandr Tymoshenko * Mailbox API 209*1b1a53cfSOleksandr Tymoshenko */ 210*1b1a53cfSOleksandr Tymoshenko int 211*1b1a53cfSOleksandr Tymoshenko bcm_mbox_write(int chan, uint32_t data) 212*1b1a53cfSOleksandr Tymoshenko { 213*1b1a53cfSOleksandr Tymoshenko int limit = 20000; 214*1b1a53cfSOleksandr Tymoshenko 215*1b1a53cfSOleksandr Tymoshenko dprintf("bcm_mbox_write: chan %d, data %08x\n", chan, data); 216*1b1a53cfSOleksandr Tymoshenko MBOX_LOCK; 217*1b1a53cfSOleksandr Tymoshenko 218*1b1a53cfSOleksandr Tymoshenko while ((mbox_read_4(REG_STATUS) & STATUS_FULL) && limit--) { 219*1b1a53cfSOleksandr Tymoshenko DELAY(2); 220*1b1a53cfSOleksandr Tymoshenko } 221*1b1a53cfSOleksandr Tymoshenko 222*1b1a53cfSOleksandr Tymoshenko if (limit == 0) { 223*1b1a53cfSOleksandr Tymoshenko printf("bcm_mbox_write: STATUS_FULL stuck"); 224*1b1a53cfSOleksandr Tymoshenko MBOX_UNLOCK; 225*1b1a53cfSOleksandr Tymoshenko return (EAGAIN); 226*1b1a53cfSOleksandr Tymoshenko } 227*1b1a53cfSOleksandr Tymoshenko 228*1b1a53cfSOleksandr Tymoshenko mbox_write_4(REG_WRITE, MBOX_MSG(chan, data)); 229*1b1a53cfSOleksandr Tymoshenko 230*1b1a53cfSOleksandr Tymoshenko MBOX_UNLOCK; 231*1b1a53cfSOleksandr Tymoshenko return (0); 232*1b1a53cfSOleksandr Tymoshenko } 233*1b1a53cfSOleksandr Tymoshenko 234*1b1a53cfSOleksandr Tymoshenko int 235*1b1a53cfSOleksandr Tymoshenko bcm_mbox_read(int chan, uint32_t *data) 236*1b1a53cfSOleksandr Tymoshenko { 237*1b1a53cfSOleksandr Tymoshenko struct bcm_mbox_softc *sc = bcm_mbox_sc; 238*1b1a53cfSOleksandr Tymoshenko 239*1b1a53cfSOleksandr Tymoshenko dprintf("bcm_mbox_read: chan %d\n", chan); 240*1b1a53cfSOleksandr Tymoshenko MBOX_LOCK; 241*1b1a53cfSOleksandr Tymoshenko while (!sc->valid[chan]) 242*1b1a53cfSOleksandr Tymoshenko msleep(&sc->msg[chan], &sc->lock, PZERO, "vcio mbox read", 0); 243*1b1a53cfSOleksandr Tymoshenko *data = bcm_mbox_sc->msg[chan]; 244*1b1a53cfSOleksandr Tymoshenko bcm_mbox_sc->valid[chan] = 0; 245*1b1a53cfSOleksandr Tymoshenko MBOX_UNLOCK; 246*1b1a53cfSOleksandr Tymoshenko dprintf("bcm_mbox_read: chan %d, data %08x\n", chan, *data); 247*1b1a53cfSOleksandr Tymoshenko 248*1b1a53cfSOleksandr Tymoshenko return (0); 249*1b1a53cfSOleksandr Tymoshenko } 250