bcm2835_mbox.c revision 252450
1235537Sgber/*- 2235537Sgber * Copyright (c) 2012 Oleksandr Tymoshenko <gonzo@freebsd.org> 3235537Sgber * All rights reserved. 4235537Sgber * 5235537Sgber * Redistribution and use in source and binary forms, with or without 6235537Sgber * modification, are permitted provided that the following conditions 7235537Sgber * are met: 8235537Sgber * 1. Redistributions of source code must retain the above copyright 9235537Sgber * notice, this list of conditions and the following disclaimer. 10235537Sgber * 2. Redistributions in binary form must reproduce the above copyright 11235537Sgber * notice, this list of conditions and the following disclaimer in the 12235537Sgber * documentation and/or other materials provided with the distribution. 13235537Sgber * 14235537Sgber * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15235537Sgber * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16235537Sgber * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17235537Sgber * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18235537Sgber * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19235537Sgber * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20235537Sgber * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21235537Sgber * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22235537Sgber * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23235537Sgber * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24235537Sgber * SUCH DAMAGE. 25235537Sgber */ 26235537Sgber 27235537Sgber#include <sys/cdefs.h> 28235537Sgber__FBSDID("$FreeBSD: head/sys/arm/broadcom/bcm2835/bcm2835_mbox.c 252450 2013-07-01 06:33:35Z rpaulo $"); 29235537Sgber 30235537Sgber#include <sys/param.h> 31235537Sgber#include <sys/systm.h> 32235537Sgber#include <sys/bus.h> 33235537Sgber#include <sys/kernel.h> 34235537Sgber#include <sys/module.h> 35235537Sgber#include <sys/malloc.h> 36235537Sgber#include <sys/rman.h> 37235537Sgber#include <sys/timeet.h> 38235537Sgber#include <sys/timetc.h> 39235537Sgber#include <sys/watchdog.h> 40235537Sgber#include <machine/bus.h> 41235537Sgber#include <machine/cpu.h> 42235537Sgber#include <machine/frame.h> 43235537Sgber#include <machine/intr.h> 44235537Sgber 45235537Sgber#include <dev/fdt/fdt_common.h> 46235537Sgber#include <dev/ofw/openfirm.h> 47235537Sgber#include <dev/ofw/ofw_bus.h> 48235537Sgber#include <dev/ofw/ofw_bus_subr.h> 49235537Sgber 50235537Sgber#include <machine/bus.h> 51235537Sgber#include <machine/fdt.h> 52235537Sgber#include <arm/broadcom/bcm2835/bcm2835_mbox.h> 53235537Sgber 54235537Sgber#define REG_READ 0x00 55235537Sgber#define REG_POL 0x10 56235537Sgber#define REG_SENDER 0x14 57235537Sgber#define REG_STATUS 0x18 58235537Sgber#define STATUS_FULL 0x80000000 59235537Sgber#define STATUS_EMPTY 0x40000000 60235537Sgber#define REG_CONFIG 0x1C 61235537Sgber#define CONFIG_DATA_IRQ 0x00000001 62235537Sgber#define REG_WRITE 0x20 /* This is Mailbox 1 address */ 63235537Sgber 64235537Sgber#define MBOX_MSG(chan, data) (((data) & ~0xf) | ((chan) & 0xf)) 65235537Sgber#define MBOX_CHAN(msg) ((msg) & 0xf) 66235537Sgber#define MBOX_DATA(msg) ((msg) & ~0xf) 67235537Sgber 68235537Sgber#define MBOX_LOCK do { \ 69235537Sgber mtx_lock(&bcm_mbox_sc->lock); \ 70235537Sgber} while(0) 71235537Sgber 72235537Sgber#define MBOX_UNLOCK do { \ 73235537Sgber mtx_unlock(&bcm_mbox_sc->lock); \ 74235537Sgber} while(0) 75235537Sgber 76235537Sgber#ifdef DEBUG 77235537Sgber#define dprintf(fmt, args...) printf(fmt, ##args) 78235537Sgber#else 79235537Sgber#define dprintf(fmt, args...) 80235537Sgber#endif 81235537Sgber 82235537Sgberstruct bcm_mbox_softc { 83249743Sed struct mtx lock; 84249743Sed struct resource * mem_res; 85235537Sgber struct resource * irq_res; 86235537Sgber void* intr_hl; 87249743Sed bus_space_tag_t bst; 88249743Sed bus_space_handle_t bsh; 89249743Sed int valid[BCM2835_MBOX_CHANS]; 90249743Sed int msg[BCM2835_MBOX_CHANS]; 91249743Sed}; 92249743Sed 93249743Sedstatic struct bcm_mbox_softc *bcm_mbox_sc = NULL; 94249743Sed 95235537Sgber#define mbox_read_4(reg) \ 96235537Sgber bus_space_read_4(bcm_mbox_sc->bst, bcm_mbox_sc->bsh, reg) 97235537Sgber#define mbox_write_4(reg, val) \ 98235537Sgber bus_space_write_4(bcm_mbox_sc->bst, bcm_mbox_sc->bsh, reg, val) 99235537Sgber 100235537Sgberstatic void 101235537Sgberbcm_mbox_intr(void *arg) 102235537Sgber{ 103249743Sed struct bcm_mbox_softc *sc = arg; 104249743Sed int chan; 105235537Sgber uint32_t data; 106235537Sgber uint32_t msg; 107235537Sgber 108235537Sgber MBOX_LOCK; 109235537Sgber while (!(mbox_read_4(REG_STATUS) & STATUS_EMPTY)) { 110235537Sgber msg = mbox_read_4(REG_READ); 111235537Sgber dprintf("bcm_mbox_intr: raw data %08x\n", msg); 112235537Sgber chan = MBOX_CHAN(msg); 113249743Sed data = MBOX_DATA(msg); 114249743Sed if (sc->valid[chan]) { 115235537Sgber printf("bcm_mbox_intr: channel %d oveflow\n", chan); 116235537Sgber continue; 117235537Sgber } 118235537Sgber dprintf("bcm_mbox_intr: chan %d, data %08x\n", chan, data); 119235537Sgber sc->msg[chan] = data; 120235537Sgber sc->valid[chan] = 1; 121235537Sgber wakeup(&sc->msg[chan]); 122235537Sgber 123235537Sgber } 124235537Sgber MBOX_UNLOCK; 125235537Sgber} 126235537Sgber 127249743Sedstatic int 128235537Sgberbcm_mbox_probe(device_t dev) 129249743Sed{ 130235537Sgber 131249743Sed if (ofw_bus_is_compatible(dev, "broadcom,bcm2835-mbox")) { 132249743Sed device_set_desc(dev, "BCM2835 VideoCore Mailbox"); 133249743Sed return(BUS_PROBE_DEFAULT); 134235537Sgber } 135235537Sgber 136235537Sgber return (ENXIO); 137235537Sgber} 138235537Sgber 139235537Sgberstatic int 140235537Sgberbcm_mbox_attach(device_t dev) 141235537Sgber{ 142235537Sgber struct bcm_mbox_softc *sc = device_get_softc(dev); 143235537Sgber int i; 144235537Sgber int rid = 0; 145235537Sgber 146235537Sgber if (bcm_mbox_sc != NULL) 147235537Sgber return (EINVAL); 148235537Sgber 149235537Sgber sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); 150235537Sgber if (sc->mem_res == NULL) { 151235537Sgber device_printf(dev, "could not allocate memory resource\n"); 152235537Sgber return (ENXIO); 153235537Sgber } 154235537Sgber 155235537Sgber sc->bst = rman_get_bustag(sc->mem_res); 156235537Sgber sc->bsh = rman_get_bushandle(sc->mem_res); 157235537Sgber 158235537Sgber rid = 0; 159235537Sgber sc->irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_ACTIVE); 160235537Sgber if (sc->irq_res == NULL) { 161235537Sgber device_printf(dev, "could not allocate interrupt resource\n"); 162235537Sgber return (ENXIO); 163235537Sgber } 164235537Sgber 165235537Sgber /* Setup and enable the timer */ 166235537Sgber if (bus_setup_intr(dev, sc->irq_res, INTR_MPSAFE | INTR_TYPE_MISC, 167235537Sgber NULL, bcm_mbox_intr, sc, &sc->intr_hl) != 0) { 168235537Sgber bus_release_resource(dev, SYS_RES_IRQ, rid, sc->irq_res); 169235537Sgber device_printf(dev, "Unable to setup the clock irq handler.\n"); 170235537Sgber return (ENXIO); 171235537Sgber } 172235537Sgber 173235537Sgber mtx_init(&sc->lock, "vcio mbox", MTX_DEF, 0); 174235537Sgber for (i = 0; i < BCM2835_MBOX_CHANS; i++) { 175235537Sgber sc->valid[0] = 0; 176235537Sgber sc->msg[0] = 0; 177235537Sgber } 178235537Sgber 179235537Sgber bcm_mbox_sc = sc; 180235537Sgber /* Read all pending messages */ 181235537Sgber bcm_mbox_intr(sc); 182235537Sgber 183235537Sgber /* Should be called after bcm_mbox_sc initialization */ 184235537Sgber mbox_write_4(REG_CONFIG, CONFIG_DATA_IRQ); 185235537Sgber 186235537Sgber return (0); 187235537Sgber} 188235537Sgber 189235537Sgberstatic device_method_t bcm_mbox_methods[] = { 190235537Sgber DEVMETHOD(device_probe, bcm_mbox_probe), 191235537Sgber DEVMETHOD(device_attach, bcm_mbox_attach), 192235537Sgber { 0, 0 } 193235537Sgber}; 194235537Sgber 195235537Sgberstatic driver_t bcm_mbox_driver = { 196235537Sgber "mbox", 197235537Sgber bcm_mbox_methods, 198235537Sgber sizeof(struct bcm_mbox_softc), 199235537Sgber}; 200235537Sgber 201235537Sgberstatic devclass_t bcm_mbox_devclass; 202235537Sgber 203235537SgberDRIVER_MODULE(mbox, simplebus, bcm_mbox_driver, bcm_mbox_devclass, 0, 0); 204235537Sgber 205235537Sgber/* 206235537Sgber * Mailbox API 207235537Sgber */ 208235537Sgberint 209235537Sgberbcm_mbox_write(int chan, uint32_t data) 210235537Sgber{ 211235537Sgber int limit = 20000; 212235537Sgber 213235537Sgber dprintf("bcm_mbox_write: chan %d, data %08x\n", chan, data); 214235537Sgber MBOX_LOCK; 215235537Sgber 216235537Sgber while ((mbox_read_4(REG_STATUS) & STATUS_FULL) && limit--) { 217235537Sgber DELAY(2); 218235537Sgber } 219235537Sgber 220235537Sgber if (limit == 0) { 221235537Sgber printf("bcm_mbox_write: STATUS_FULL stuck"); 222235537Sgber MBOX_UNLOCK; 223235537Sgber return (EAGAIN); 224235537Sgber } 225235537Sgber 226235537Sgber mbox_write_4(REG_WRITE, MBOX_MSG(chan, data)); 227235537Sgber 228235537Sgber MBOX_UNLOCK; 229235537Sgber return (0); 230235537Sgber} 231235537Sgber 232235537Sgberint 233235537Sgberbcm_mbox_read(int chan, uint32_t *data) 234235537Sgber{ 235235537Sgber struct bcm_mbox_softc *sc = bcm_mbox_sc; 236235537Sgber 237235537Sgber dprintf("bcm_mbox_read: chan %d\n", chan); 238235537Sgber MBOX_LOCK; 239235537Sgber while (!sc->valid[chan]) 240235537Sgber msleep(&sc->msg[chan], &sc->lock, PZERO, "vcio mbox read", 0); 241235537Sgber *data = bcm_mbox_sc->msg[chan]; 242235537Sgber bcm_mbox_sc->valid[chan] = 0; 243235537Sgber MBOX_UNLOCK; 244235537Sgber dprintf("bcm_mbox_read: chan %d, data %08x\n", chan, *data); 245235537Sgber 246235537Sgber return (0); 247235537Sgber} 248235537Sgber