11b1a53cfSOleksandr Tymoshenko /*- 2af3dc4a7SPedro F. Giffuni * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3af3dc4a7SPedro F. Giffuni * 41b1a53cfSOleksandr Tymoshenko * Copyright (c) 2012 Oleksandr Tymoshenko <gonzo@freebsd.org> 51b1a53cfSOleksandr Tymoshenko * All rights reserved. 61b1a53cfSOleksandr Tymoshenko * 71b1a53cfSOleksandr Tymoshenko * Redistribution and use in source and binary forms, with or without 81b1a53cfSOleksandr Tymoshenko * modification, are permitted provided that the following conditions 91b1a53cfSOleksandr Tymoshenko * are met: 101b1a53cfSOleksandr Tymoshenko * 1. Redistributions of source code must retain the above copyright 111b1a53cfSOleksandr Tymoshenko * notice, this list of conditions and the following disclaimer. 121b1a53cfSOleksandr Tymoshenko * 2. Redistributions in binary form must reproduce the above copyright 131b1a53cfSOleksandr Tymoshenko * notice, this list of conditions and the following disclaimer in the 141b1a53cfSOleksandr Tymoshenko * documentation and/or other materials provided with the distribution. 151b1a53cfSOleksandr Tymoshenko * 161b1a53cfSOleksandr Tymoshenko * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 171b1a53cfSOleksandr Tymoshenko * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 181b1a53cfSOleksandr Tymoshenko * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 191b1a53cfSOleksandr Tymoshenko * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 201b1a53cfSOleksandr Tymoshenko * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 211b1a53cfSOleksandr Tymoshenko * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 221b1a53cfSOleksandr Tymoshenko * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 231b1a53cfSOleksandr Tymoshenko * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 241b1a53cfSOleksandr Tymoshenko * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 251b1a53cfSOleksandr Tymoshenko * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 261b1a53cfSOleksandr Tymoshenko * SUCH DAMAGE. 271b1a53cfSOleksandr Tymoshenko */ 281b1a53cfSOleksandr Tymoshenko 291b1a53cfSOleksandr Tymoshenko #include <sys/cdefs.h> 301b1a53cfSOleksandr Tymoshenko __FBSDID("$FreeBSD$"); 311b1a53cfSOleksandr Tymoshenko 321b1a53cfSOleksandr Tymoshenko #include <sys/param.h> 331b1a53cfSOleksandr Tymoshenko #include <sys/systm.h> 341b1a53cfSOleksandr Tymoshenko #include <sys/bus.h> 351b1a53cfSOleksandr Tymoshenko #include <sys/kernel.h> 3609df4bfdSLuiz Otavio O Souza #include <sys/lock.h> 371b1a53cfSOleksandr Tymoshenko #include <sys/module.h> 3809df4bfdSLuiz Otavio O Souza #include <sys/mutex.h> 39e597fdb8SOleksandr Tymoshenko #include <sys/sx.h> 401b1a53cfSOleksandr Tymoshenko #include <sys/rman.h> 411b1a53cfSOleksandr Tymoshenko #include <machine/bus.h> 421b1a53cfSOleksandr Tymoshenko 431b1a53cfSOleksandr Tymoshenko #include <dev/ofw/ofw_bus.h> 441b1a53cfSOleksandr Tymoshenko #include <dev/ofw/ofw_bus_subr.h> 451b1a53cfSOleksandr Tymoshenko 46*ce69217cSAndrew Turner #include <arm/broadcom/bcm2835/bcm2835_firmware.h> 471b1a53cfSOleksandr Tymoshenko #include <arm/broadcom/bcm2835/bcm2835_mbox.h> 48b812d0adSAndrew Turner #include <arm/broadcom/bcm2835/bcm2835_mbox_prop.h> 49b812d0adSAndrew Turner #include <arm/broadcom/bcm2835/bcm2835_vcbus.h> 501b1a53cfSOleksandr Tymoshenko 51279cd101SRui Paulo #include "mbox_if.h" 52279cd101SRui Paulo 531b1a53cfSOleksandr Tymoshenko #define REG_READ 0x00 541b1a53cfSOleksandr Tymoshenko #define REG_POL 0x10 551b1a53cfSOleksandr Tymoshenko #define REG_SENDER 0x14 561b1a53cfSOleksandr Tymoshenko #define REG_STATUS 0x18 571b1a53cfSOleksandr Tymoshenko #define STATUS_FULL 0x80000000 581b1a53cfSOleksandr Tymoshenko #define STATUS_EMPTY 0x40000000 591b1a53cfSOleksandr Tymoshenko #define REG_CONFIG 0x1C 601b1a53cfSOleksandr Tymoshenko #define CONFIG_DATA_IRQ 0x00000001 611b1a53cfSOleksandr Tymoshenko #define REG_WRITE 0x20 /* This is Mailbox 1 address */ 621b1a53cfSOleksandr Tymoshenko 631b1a53cfSOleksandr Tymoshenko #define MBOX_MSG(chan, data) (((data) & ~0xf) | ((chan) & 0xf)) 641b1a53cfSOleksandr Tymoshenko #define MBOX_CHAN(msg) ((msg) & 0xf) 651b1a53cfSOleksandr Tymoshenko #define MBOX_DATA(msg) ((msg) & ~0xf) 661b1a53cfSOleksandr Tymoshenko 67279cd101SRui Paulo #define MBOX_LOCK(sc) do { \ 68279cd101SRui Paulo mtx_lock(&(sc)->lock); \ 691b1a53cfSOleksandr Tymoshenko } while(0) 701b1a53cfSOleksandr Tymoshenko 71279cd101SRui Paulo #define MBOX_UNLOCK(sc) do { \ 72279cd101SRui Paulo mtx_unlock(&(sc)->lock); \ 731b1a53cfSOleksandr Tymoshenko } while(0) 741b1a53cfSOleksandr Tymoshenko 751b1a53cfSOleksandr Tymoshenko #ifdef DEBUG 761b1a53cfSOleksandr Tymoshenko #define dprintf(fmt, args...) printf(fmt, ##args) 771b1a53cfSOleksandr Tymoshenko #else 781b1a53cfSOleksandr Tymoshenko #define dprintf(fmt, args...) 791b1a53cfSOleksandr Tymoshenko #endif 801b1a53cfSOleksandr Tymoshenko 811b1a53cfSOleksandr Tymoshenko struct bcm_mbox_softc { 821b1a53cfSOleksandr Tymoshenko struct mtx lock; 831b1a53cfSOleksandr Tymoshenko struct resource * mem_res; 841b1a53cfSOleksandr Tymoshenko struct resource * irq_res; 851b1a53cfSOleksandr Tymoshenko void* intr_hl; 861b1a53cfSOleksandr Tymoshenko bus_space_tag_t bst; 871b1a53cfSOleksandr Tymoshenko bus_space_handle_t bsh; 881b1a53cfSOleksandr Tymoshenko int msg[BCM2835_MBOX_CHANS]; 89b805a8c3SOleksandr Tymoshenko int have_message[BCM2835_MBOX_CHANS]; 90e597fdb8SOleksandr Tymoshenko struct sx property_chan_lock; 911b1a53cfSOleksandr Tymoshenko }; 921b1a53cfSOleksandr Tymoshenko 93279cd101SRui Paulo #define mbox_read_4(sc, reg) \ 94279cd101SRui Paulo bus_space_read_4((sc)->bst, (sc)->bsh, reg) 95279cd101SRui Paulo #define mbox_write_4(sc, reg, val) \ 96279cd101SRui Paulo bus_space_write_4((sc)->bst, (sc)->bsh, reg, val) 971b1a53cfSOleksandr Tymoshenko 9838e62ae5SOleksandr Tymoshenko static struct ofw_compat_data compat_data[] = { 9938e62ae5SOleksandr Tymoshenko {"broadcom,bcm2835-mbox", 1}, 10038e62ae5SOleksandr Tymoshenko {"brcm,bcm2835-mbox", 1}, 10138e62ae5SOleksandr Tymoshenko {NULL, 0} 10238e62ae5SOleksandr Tymoshenko }; 10338e62ae5SOleksandr Tymoshenko 104d792cd92SAndrew Turner static int 105d792cd92SAndrew Turner bcm_mbox_read_msg(struct bcm_mbox_softc *sc, int *ochan) 1061b1a53cfSOleksandr Tymoshenko { 107151ba793SAlexander Kabaev #ifdef DEBUG 1081b1a53cfSOleksandr Tymoshenko uint32_t data; 109151ba793SAlexander Kabaev #endif 1101b1a53cfSOleksandr Tymoshenko uint32_t msg; 111d792cd92SAndrew Turner int chan; 1121b1a53cfSOleksandr Tymoshenko 113279cd101SRui Paulo msg = mbox_read_4(sc, REG_READ); 1141b1a53cfSOleksandr Tymoshenko dprintf("bcm_mbox_intr: raw data %08x\n", msg); 1151b1a53cfSOleksandr Tymoshenko chan = MBOX_CHAN(msg); 116151ba793SAlexander Kabaev #ifdef DEBUG 1171b1a53cfSOleksandr Tymoshenko data = MBOX_DATA(msg); 118151ba793SAlexander Kabaev #endif 119b9450e43SRui Paulo if (sc->msg[chan]) { 1201b1a53cfSOleksandr Tymoshenko printf("bcm_mbox_intr: channel %d oveflow\n", chan); 121d792cd92SAndrew Turner return (1); 1221b1a53cfSOleksandr Tymoshenko } 1231b1a53cfSOleksandr Tymoshenko dprintf("bcm_mbox_intr: chan %d, data %08x\n", chan, data); 1241432fa20SLuiz Otavio O Souza sc->msg[chan] = msg; 125d792cd92SAndrew Turner 126d792cd92SAndrew Turner if (ochan != NULL) 127d792cd92SAndrew Turner *ochan = chan; 128d792cd92SAndrew Turner 129d792cd92SAndrew Turner return (0); 1301b1a53cfSOleksandr Tymoshenko } 131d792cd92SAndrew Turner 132d792cd92SAndrew Turner static void 133d792cd92SAndrew Turner bcm_mbox_intr(void *arg) 134d792cd92SAndrew Turner { 135d792cd92SAndrew Turner struct bcm_mbox_softc *sc = arg; 136d792cd92SAndrew Turner int chan; 137d792cd92SAndrew Turner 138b805a8c3SOleksandr Tymoshenko MBOX_LOCK(sc); 139d792cd92SAndrew Turner while (!(mbox_read_4(sc, REG_STATUS) & STATUS_EMPTY)) 140e597fdb8SOleksandr Tymoshenko if (bcm_mbox_read_msg(sc, &chan) == 0) { 141e597fdb8SOleksandr Tymoshenko sc->have_message[chan] = 1; 142b805a8c3SOleksandr Tymoshenko wakeup(&sc->have_message[chan]); 143e597fdb8SOleksandr Tymoshenko } 144b805a8c3SOleksandr Tymoshenko MBOX_UNLOCK(sc); 1451b1a53cfSOleksandr Tymoshenko } 1461b1a53cfSOleksandr Tymoshenko 1471b1a53cfSOleksandr Tymoshenko static int 1481b1a53cfSOleksandr Tymoshenko bcm_mbox_probe(device_t dev) 1491b1a53cfSOleksandr Tymoshenko { 1501b1a53cfSOleksandr Tymoshenko 151add35ed5SIan Lepore if (!ofw_bus_status_okay(dev)) 152add35ed5SIan Lepore return (ENXIO); 153add35ed5SIan Lepore 15438e62ae5SOleksandr Tymoshenko if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0) 1551b1a53cfSOleksandr Tymoshenko return (ENXIO); 15638e62ae5SOleksandr Tymoshenko 15738e62ae5SOleksandr Tymoshenko device_set_desc(dev, "BCM2835 VideoCore Mailbox"); 15838e62ae5SOleksandr Tymoshenko 15938e62ae5SOleksandr Tymoshenko return (BUS_PROBE_DEFAULT); 1601b1a53cfSOleksandr Tymoshenko } 1611b1a53cfSOleksandr Tymoshenko 1621b1a53cfSOleksandr Tymoshenko static int 1631b1a53cfSOleksandr Tymoshenko bcm_mbox_attach(device_t dev) 1641b1a53cfSOleksandr Tymoshenko { 1651b1a53cfSOleksandr Tymoshenko struct bcm_mbox_softc *sc = device_get_softc(dev); 1661b1a53cfSOleksandr Tymoshenko int i; 1671b1a53cfSOleksandr Tymoshenko int rid = 0; 1681b1a53cfSOleksandr Tymoshenko 1691b1a53cfSOleksandr Tymoshenko sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); 1701b1a53cfSOleksandr Tymoshenko if (sc->mem_res == NULL) { 1711b1a53cfSOleksandr Tymoshenko device_printf(dev, "could not allocate memory resource\n"); 1721b1a53cfSOleksandr Tymoshenko return (ENXIO); 1731b1a53cfSOleksandr Tymoshenko } 1741b1a53cfSOleksandr Tymoshenko 1751b1a53cfSOleksandr Tymoshenko sc->bst = rman_get_bustag(sc->mem_res); 1761b1a53cfSOleksandr Tymoshenko sc->bsh = rman_get_bushandle(sc->mem_res); 1771b1a53cfSOleksandr Tymoshenko 1781b1a53cfSOleksandr Tymoshenko rid = 0; 1791b1a53cfSOleksandr Tymoshenko sc->irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_ACTIVE); 1801b1a53cfSOleksandr Tymoshenko if (sc->irq_res == NULL) { 1811b1a53cfSOleksandr Tymoshenko device_printf(dev, "could not allocate interrupt resource\n"); 1821b1a53cfSOleksandr Tymoshenko return (ENXIO); 1831b1a53cfSOleksandr Tymoshenko } 1841b1a53cfSOleksandr Tymoshenko 1851b1a53cfSOleksandr Tymoshenko /* Setup and enable the timer */ 18695506b81SRui Paulo if (bus_setup_intr(dev, sc->irq_res, INTR_MPSAFE | INTR_TYPE_MISC, 18795506b81SRui Paulo NULL, bcm_mbox_intr, sc, &sc->intr_hl) != 0) { 18895506b81SRui Paulo bus_release_resource(dev, SYS_RES_IRQ, rid, sc->irq_res); 1891b1a53cfSOleksandr Tymoshenko device_printf(dev, "Unable to setup the clock irq handler.\n"); 1901b1a53cfSOleksandr Tymoshenko return (ENXIO); 1911b1a53cfSOleksandr Tymoshenko } 1921b1a53cfSOleksandr Tymoshenko 193d01195e3SChristian Brueffer mtx_init(&sc->lock, "vcio mbox", NULL, MTX_DEF); 1941b1a53cfSOleksandr Tymoshenko for (i = 0; i < BCM2835_MBOX_CHANS; i++) { 195b9450e43SRui Paulo sc->msg[i] = 0; 196b805a8c3SOleksandr Tymoshenko sc->have_message[i] = 0; 1971b1a53cfSOleksandr Tymoshenko } 1981b1a53cfSOleksandr Tymoshenko 199e597fdb8SOleksandr Tymoshenko sx_init(&sc->property_chan_lock, "mboxprop"); 200e597fdb8SOleksandr Tymoshenko 2011b1a53cfSOleksandr Tymoshenko /* Read all pending messages */ 2021432fa20SLuiz Otavio O Souza while ((mbox_read_4(sc, REG_STATUS) & STATUS_EMPTY) == 0) 2031432fa20SLuiz Otavio O Souza (void)mbox_read_4(sc, REG_READ); 2041b1a53cfSOleksandr Tymoshenko 205279cd101SRui Paulo mbox_write_4(sc, REG_CONFIG, CONFIG_DATA_IRQ); 206279cd101SRui Paulo 207279cd101SRui Paulo return (0); 208279cd101SRui Paulo } 209279cd101SRui Paulo 210279cd101SRui Paulo /* 211279cd101SRui Paulo * Mailbox API 212279cd101SRui Paulo */ 213279cd101SRui Paulo static int 214279cd101SRui Paulo bcm_mbox_write(device_t dev, int chan, uint32_t data) 215279cd101SRui Paulo { 21609df4bfdSLuiz Otavio O Souza int limit = 1000; 217279cd101SRui Paulo struct bcm_mbox_softc *sc = device_get_softc(dev); 218279cd101SRui Paulo 219279cd101SRui Paulo dprintf("bcm_mbox_write: chan %d, data %08x\n", chan, data); 220279cd101SRui Paulo MBOX_LOCK(sc); 221b805a8c3SOleksandr Tymoshenko sc->have_message[chan] = 0; 22209df4bfdSLuiz Otavio O Souza while ((mbox_read_4(sc, REG_STATUS) & STATUS_FULL) && --limit) 22309df4bfdSLuiz Otavio O Souza DELAY(5); 224279cd101SRui Paulo if (limit == 0) { 225279cd101SRui Paulo printf("bcm_mbox_write: STATUS_FULL stuck"); 226279cd101SRui Paulo MBOX_UNLOCK(sc); 227279cd101SRui Paulo return (EAGAIN); 228279cd101SRui Paulo } 229279cd101SRui Paulo mbox_write_4(sc, REG_WRITE, MBOX_MSG(chan, data)); 230279cd101SRui Paulo MBOX_UNLOCK(sc); 23109df4bfdSLuiz Otavio O Souza 232279cd101SRui Paulo return (0); 233279cd101SRui Paulo } 234279cd101SRui Paulo 235279cd101SRui Paulo static int 236279cd101SRui Paulo bcm_mbox_read(device_t dev, int chan, uint32_t *data) 237279cd101SRui Paulo { 238279cd101SRui Paulo struct bcm_mbox_softc *sc = device_get_softc(dev); 239d792cd92SAndrew Turner int err, read_chan; 240279cd101SRui Paulo 241279cd101SRui Paulo dprintf("bcm_mbox_read: chan %d\n", chan); 242d792cd92SAndrew Turner 243d792cd92SAndrew Turner err = 0; 244279cd101SRui Paulo MBOX_LOCK(sc); 245d792cd92SAndrew Turner if (!cold) { 246b805a8c3SOleksandr Tymoshenko if (sc->have_message[chan] == 0) { 247b805a8c3SOleksandr Tymoshenko if (mtx_sleep(&sc->have_message[chan], &sc->lock, 0, 248b805a8c3SOleksandr Tymoshenko "mbox", 10*hz) != 0) { 249b805a8c3SOleksandr Tymoshenko device_printf(dev, "timeout waiting for message on chan %d\n", chan); 250b805a8c3SOleksandr Tymoshenko err = ETIMEDOUT; 251b805a8c3SOleksandr Tymoshenko } 252b9450e43SRui Paulo } 253d792cd92SAndrew Turner } else { 254d792cd92SAndrew Turner do { 255d792cd92SAndrew Turner /* Wait for a message */ 256d792cd92SAndrew Turner while ((mbox_read_4(sc, REG_STATUS) & STATUS_EMPTY)) 257d792cd92SAndrew Turner ; 258d792cd92SAndrew Turner /* Read the message */ 259d792cd92SAndrew Turner if (bcm_mbox_read_msg(sc, &read_chan) != 0) { 260d792cd92SAndrew Turner err = EINVAL; 261d792cd92SAndrew Turner goto out; 262d792cd92SAndrew Turner } 263d792cd92SAndrew Turner } while (read_chan != chan); 264d792cd92SAndrew Turner } 265b9450e43SRui Paulo /* 266b9450e43SRui Paulo * get data from intr handler, the same channel is never coming 267b9450e43SRui Paulo * because of holding sc lock. 268b9450e43SRui Paulo */ 269b9450e43SRui Paulo *data = MBOX_DATA(sc->msg[chan]); 270b9450e43SRui Paulo sc->msg[chan] = 0; 271b805a8c3SOleksandr Tymoshenko sc->have_message[chan] = 0; 272d792cd92SAndrew Turner out: 273279cd101SRui Paulo MBOX_UNLOCK(sc); 274279cd101SRui Paulo dprintf("bcm_mbox_read: chan %d, data %08x\n", chan, *data); 2751b1a53cfSOleksandr Tymoshenko 276d792cd92SAndrew Turner return (err); 2771b1a53cfSOleksandr Tymoshenko } 2781b1a53cfSOleksandr Tymoshenko 2791b1a53cfSOleksandr Tymoshenko static device_method_t bcm_mbox_methods[] = { 2801b1a53cfSOleksandr Tymoshenko DEVMETHOD(device_probe, bcm_mbox_probe), 2811b1a53cfSOleksandr Tymoshenko DEVMETHOD(device_attach, bcm_mbox_attach), 282279cd101SRui Paulo 283279cd101SRui Paulo DEVMETHOD(mbox_read, bcm_mbox_read), 284279cd101SRui Paulo DEVMETHOD(mbox_write, bcm_mbox_write), 285279cd101SRui Paulo 286279cd101SRui Paulo DEVMETHOD_END 2871b1a53cfSOleksandr Tymoshenko }; 2881b1a53cfSOleksandr Tymoshenko 2891b1a53cfSOleksandr Tymoshenko static driver_t bcm_mbox_driver = { 2901b1a53cfSOleksandr Tymoshenko "mbox", 2911b1a53cfSOleksandr Tymoshenko bcm_mbox_methods, 2921b1a53cfSOleksandr Tymoshenko sizeof(struct bcm_mbox_softc), 2931b1a53cfSOleksandr Tymoshenko }; 2941b1a53cfSOleksandr Tymoshenko 2951b1a53cfSOleksandr Tymoshenko static devclass_t bcm_mbox_devclass; 2961b1a53cfSOleksandr Tymoshenko 2974b24f9a0SAndrew Turner EARLY_DRIVER_MODULE(mbox, simplebus, bcm_mbox_driver, bcm_mbox_devclass, 0, 0, 2984b24f9a0SAndrew Turner BUS_PASS_INTERRUPT + BUS_PASS_ORDER_LAST); 299b812d0adSAndrew Turner 300b812d0adSAndrew Turner static void 301b812d0adSAndrew Turner bcm2835_mbox_dma_cb(void *arg, bus_dma_segment_t *segs, int nseg, int err) 302b812d0adSAndrew Turner { 303b812d0adSAndrew Turner bus_addr_t *addr; 304b812d0adSAndrew Turner 305b812d0adSAndrew Turner if (err) 306b812d0adSAndrew Turner return; 307b812d0adSAndrew Turner addr = (bus_addr_t *)arg; 30840084ac3SKyle Evans *addr = ARMC_TO_VCBUS(segs[0].ds_addr); 309b812d0adSAndrew Turner } 310b812d0adSAndrew Turner 31127eb3304SAndrew Turner static void * 31227eb3304SAndrew Turner bcm2835_mbox_init_dma(device_t dev, size_t len, bus_dma_tag_t *tag, 31327eb3304SAndrew Turner bus_dmamap_t *map, bus_addr_t *phys) 31427eb3304SAndrew Turner { 31527eb3304SAndrew Turner void *buf; 31627eb3304SAndrew Turner int err; 31727eb3304SAndrew Turner 31827eb3304SAndrew Turner err = bus_dma_tag_create(bus_get_dma_tag(dev), 16, 0, 31940084ac3SKyle Evans bcm283x_dmabus_peripheral_lowaddr(), BUS_SPACE_MAXADDR, NULL, NULL, 32027eb3304SAndrew Turner len, 1, len, 0, NULL, NULL, tag); 32127eb3304SAndrew Turner if (err != 0) { 32227eb3304SAndrew Turner device_printf(dev, "can't create DMA tag\n"); 32327eb3304SAndrew Turner return (NULL); 32427eb3304SAndrew Turner } 32527eb3304SAndrew Turner 32627eb3304SAndrew Turner err = bus_dmamem_alloc(*tag, &buf, 0, map); 32727eb3304SAndrew Turner if (err != 0) { 32827eb3304SAndrew Turner bus_dma_tag_destroy(*tag); 32927eb3304SAndrew Turner device_printf(dev, "can't allocate dmamem\n"); 33027eb3304SAndrew Turner return (NULL); 33127eb3304SAndrew Turner } 33227eb3304SAndrew Turner 333723af368SLuiz Otavio O Souza err = bus_dmamap_load(*tag, *map, buf, len, bcm2835_mbox_dma_cb, 33427eb3304SAndrew Turner phys, 0); 33527eb3304SAndrew Turner if (err != 0) { 33627eb3304SAndrew Turner bus_dmamem_free(*tag, buf, *map); 33727eb3304SAndrew Turner bus_dma_tag_destroy(*tag); 33827eb3304SAndrew Turner device_printf(dev, "can't load DMA map\n"); 33927eb3304SAndrew Turner return (NULL); 34027eb3304SAndrew Turner } 34127eb3304SAndrew Turner 34227eb3304SAndrew Turner return (buf); 34327eb3304SAndrew Turner } 34427eb3304SAndrew Turner 345ae29e2d8SLuiz Otavio O Souza static int 346ae29e2d8SLuiz Otavio O Souza bcm2835_mbox_err(device_t dev, bus_addr_t msg_phys, uint32_t resp_phys, 347ae29e2d8SLuiz Otavio O Souza struct bcm2835_mbox_hdr *msg, size_t len) 348ae29e2d8SLuiz Otavio O Souza { 349ae29e2d8SLuiz Otavio O Souza int idx; 350ae29e2d8SLuiz Otavio O Souza struct bcm2835_mbox_tag_hdr *tag; 351ae29e2d8SLuiz Otavio O Souza uint8_t *last; 352ae29e2d8SLuiz Otavio O Souza 353ae29e2d8SLuiz Otavio O Souza if ((uint32_t)msg_phys != resp_phys) { 354ae29e2d8SLuiz Otavio O Souza device_printf(dev, "response channel mismatch\n"); 355ae29e2d8SLuiz Otavio O Souza return (EIO); 356ae29e2d8SLuiz Otavio O Souza } 357ae29e2d8SLuiz Otavio O Souza if (msg->code != BCM2835_MBOX_CODE_RESP_SUCCESS) { 358ae29e2d8SLuiz Otavio O Souza device_printf(dev, "mbox response error\n"); 359ae29e2d8SLuiz Otavio O Souza return (EIO); 360ae29e2d8SLuiz Otavio O Souza } 361ae29e2d8SLuiz Otavio O Souza 362ae29e2d8SLuiz Otavio O Souza /* Loop until the end tag. */ 363ae29e2d8SLuiz Otavio O Souza tag = (struct bcm2835_mbox_tag_hdr *)(msg + 1); 364ae29e2d8SLuiz Otavio O Souza last = (uint8_t *)msg + len; 365ae29e2d8SLuiz Otavio O Souza for (idx = 0; tag->tag != 0; idx++) { 366*ce69217cSAndrew Turner /* 367*ce69217cSAndrew Turner * When setting the GPIO config or state the firmware doesn't 368*ce69217cSAndrew Turner * set tag->val_len correctly. 369*ce69217cSAndrew Turner */ 370*ce69217cSAndrew Turner if ((tag->tag == BCM2835_FIRMWARE_TAG_SET_GPIO_CONFIG || 371*ce69217cSAndrew Turner tag->tag == BCM2835_FIRMWARE_TAG_SET_GPIO_STATE) && 372*ce69217cSAndrew Turner tag->val_len == 0) { 373*ce69217cSAndrew Turner tag->val_len = BCM2835_MBOX_TAG_VAL_LEN_RESPONSE | 374*ce69217cSAndrew Turner tag->val_buf_size; 375*ce69217cSAndrew Turner } 376ae29e2d8SLuiz Otavio O Souza if ((tag->val_len & BCM2835_MBOX_TAG_VAL_LEN_RESPONSE) == 0) { 377ae29e2d8SLuiz Otavio O Souza device_printf(dev, "tag %d response error\n", idx); 378ae29e2d8SLuiz Otavio O Souza return (EIO); 379ae29e2d8SLuiz Otavio O Souza } 380ae29e2d8SLuiz Otavio O Souza /* Clear the response bit. */ 381ae29e2d8SLuiz Otavio O Souza tag->val_len &= ~BCM2835_MBOX_TAG_VAL_LEN_RESPONSE; 382ae29e2d8SLuiz Otavio O Souza 383ae29e2d8SLuiz Otavio O Souza /* Next tag. */ 384ae29e2d8SLuiz Otavio O Souza tag = (struct bcm2835_mbox_tag_hdr *)((uint8_t *)tag + 385ae29e2d8SLuiz Otavio O Souza sizeof(*tag) + tag->val_buf_size); 386ae29e2d8SLuiz Otavio O Souza 387ae29e2d8SLuiz Otavio O Souza if ((uint8_t *)tag > last) { 388ae29e2d8SLuiz Otavio O Souza device_printf(dev, "mbox buffer size error\n"); 389ae29e2d8SLuiz Otavio O Souza return (EIO); 390ae29e2d8SLuiz Otavio O Souza } 391ae29e2d8SLuiz Otavio O Souza } 392ae29e2d8SLuiz Otavio O Souza 393ae29e2d8SLuiz Otavio O Souza return (0); 394ae29e2d8SLuiz Otavio O Souza } 395ae29e2d8SLuiz Otavio O Souza 396b812d0adSAndrew Turner int 3978826550bSOleksandr Tymoshenko bcm2835_mbox_property(void *msg, size_t msg_size) 398b812d0adSAndrew Turner { 399e597fdb8SOleksandr Tymoshenko struct bcm_mbox_softc *sc; 4008826550bSOleksandr Tymoshenko struct msg_set_power_state *buf; 401b812d0adSAndrew Turner bus_dma_tag_t msg_tag; 402b812d0adSAndrew Turner bus_dmamap_t msg_map; 403b812d0adSAndrew Turner bus_addr_t msg_phys; 404b812d0adSAndrew Turner uint32_t reg; 405b812d0adSAndrew Turner device_t mbox; 406ae29e2d8SLuiz Otavio O Souza int err; 407ae29e2d8SLuiz Otavio O Souza 408ae29e2d8SLuiz Otavio O Souza /* get mbox device */ 409ae29e2d8SLuiz Otavio O Souza mbox = devclass_get_device(devclass_find("mbox"), 0); 4108826550bSOleksandr Tymoshenko if (mbox == NULL) 411ae29e2d8SLuiz Otavio O Souza return (ENXIO); 412ae29e2d8SLuiz Otavio O Souza 413e597fdb8SOleksandr Tymoshenko sc = device_get_softc(mbox); 414e597fdb8SOleksandr Tymoshenko sx_xlock(&sc->property_chan_lock); 415e597fdb8SOleksandr Tymoshenko 416ae29e2d8SLuiz Otavio O Souza /* Allocate memory for the message */ 4178826550bSOleksandr Tymoshenko buf = bcm2835_mbox_init_dma(mbox, msg_size, &msg_tag, &msg_map, 418ae29e2d8SLuiz Otavio O Souza &msg_phys); 419e597fdb8SOleksandr Tymoshenko if (buf == NULL) { 420e597fdb8SOleksandr Tymoshenko err = ENOMEM; 421e597fdb8SOleksandr Tymoshenko goto out; 422e597fdb8SOleksandr Tymoshenko } 423ae29e2d8SLuiz Otavio O Souza 4248826550bSOleksandr Tymoshenko memcpy(buf, msg, msg_size); 425ae29e2d8SLuiz Otavio O Souza 4268826550bSOleksandr Tymoshenko bus_dmamap_sync(msg_tag, msg_map, 4278826550bSOleksandr Tymoshenko BUS_DMASYNC_PREWRITE); 4288826550bSOleksandr Tymoshenko 429ae29e2d8SLuiz Otavio O Souza MBOX_WRITE(mbox, BCM2835_MBOX_CHAN_PROP, (uint32_t)msg_phys); 430ae29e2d8SLuiz Otavio O Souza MBOX_READ(mbox, BCM2835_MBOX_CHAN_PROP, ®); 431ae29e2d8SLuiz Otavio O Souza 4328826550bSOleksandr Tymoshenko bus_dmamap_sync(msg_tag, msg_map, 4338826550bSOleksandr Tymoshenko BUS_DMASYNC_PREREAD); 4348826550bSOleksandr Tymoshenko 4358826550bSOleksandr Tymoshenko memcpy(msg, buf, msg_size); 4368826550bSOleksandr Tymoshenko 4378826550bSOleksandr Tymoshenko err = bcm2835_mbox_err(mbox, msg_phys, reg, 4388826550bSOleksandr Tymoshenko (struct bcm2835_mbox_hdr *)msg, msg_size); 439ae29e2d8SLuiz Otavio O Souza 440ae29e2d8SLuiz Otavio O Souza bus_dmamap_unload(msg_tag, msg_map); 4418826550bSOleksandr Tymoshenko bus_dmamem_free(msg_tag, buf, msg_map); 442ae29e2d8SLuiz Otavio O Souza bus_dma_tag_destroy(msg_tag); 443e597fdb8SOleksandr Tymoshenko out: 444e597fdb8SOleksandr Tymoshenko sx_xunlock(&sc->property_chan_lock); 445ae29e2d8SLuiz Otavio O Souza return (err); 446ae29e2d8SLuiz Otavio O Souza } 447ae29e2d8SLuiz Otavio O Souza 448ae29e2d8SLuiz Otavio O Souza int 4498826550bSOleksandr Tymoshenko bcm2835_mbox_set_power_state(uint32_t device_id, boolean_t on) 450ae29e2d8SLuiz Otavio O Souza { 4518826550bSOleksandr Tymoshenko struct msg_set_power_state msg; 452ae29e2d8SLuiz Otavio O Souza int err; 453ae29e2d8SLuiz Otavio O Souza 4548826550bSOleksandr Tymoshenko memset(&msg, 0, sizeof(msg)); 4558826550bSOleksandr Tymoshenko msg.hdr.buf_size = sizeof(msg); 4568826550bSOleksandr Tymoshenko msg.hdr.code = BCM2835_MBOX_CODE_REQ; 4578826550bSOleksandr Tymoshenko msg.tag_hdr.tag = BCM2835_MBOX_TAG_SET_POWER_STATE; 4588826550bSOleksandr Tymoshenko msg.tag_hdr.val_buf_size = sizeof(msg.body); 4598826550bSOleksandr Tymoshenko msg.tag_hdr.val_len = sizeof(msg.body.req); 4608826550bSOleksandr Tymoshenko msg.body.req.device_id = device_id; 4618826550bSOleksandr Tymoshenko msg.body.req.state = (on ? BCM2835_MBOX_POWER_ON : 0) | 4628826550bSOleksandr Tymoshenko BCM2835_MBOX_POWER_WAIT; 4638826550bSOleksandr Tymoshenko msg.end_tag = 0; 4648826550bSOleksandr Tymoshenko 4658826550bSOleksandr Tymoshenko err = bcm2835_mbox_property(&msg, sizeof(msg)); 4668826550bSOleksandr Tymoshenko 4678826550bSOleksandr Tymoshenko return (err); 468ae29e2d8SLuiz Otavio O Souza } 469ae29e2d8SLuiz Otavio O Souza 4708826550bSOleksandr Tymoshenko int 4718826550bSOleksandr Tymoshenko bcm2835_mbox_get_clock_rate(uint32_t clock_id, uint32_t *hz) 4728826550bSOleksandr Tymoshenko { 4738826550bSOleksandr Tymoshenko struct msg_get_clock_rate msg; 4748826550bSOleksandr Tymoshenko int err; 475ae29e2d8SLuiz Otavio O Souza 4768826550bSOleksandr Tymoshenko memset(&msg, 0, sizeof(msg)); 4778826550bSOleksandr Tymoshenko msg.hdr.buf_size = sizeof(msg); 4788826550bSOleksandr Tymoshenko msg.hdr.code = BCM2835_MBOX_CODE_REQ; 4798826550bSOleksandr Tymoshenko msg.tag_hdr.tag = BCM2835_MBOX_TAG_GET_CLOCK_RATE; 4808826550bSOleksandr Tymoshenko msg.tag_hdr.val_buf_size = sizeof(msg.body); 4818826550bSOleksandr Tymoshenko msg.tag_hdr.val_len = sizeof(msg.body.req); 4828826550bSOleksandr Tymoshenko msg.body.req.clock_id = clock_id; 4838826550bSOleksandr Tymoshenko msg.end_tag = 0; 484ae29e2d8SLuiz Otavio O Souza 4858826550bSOleksandr Tymoshenko err = bcm2835_mbox_property(&msg, sizeof(msg)); 4868826550bSOleksandr Tymoshenko *hz = msg.body.resp.rate_hz; 487ae29e2d8SLuiz Otavio O Souza 4888826550bSOleksandr Tymoshenko return (err); 4898826550bSOleksandr Tymoshenko } 490ae29e2d8SLuiz Otavio O Souza 4918826550bSOleksandr Tymoshenko int 4928826550bSOleksandr Tymoshenko bcm2835_mbox_fb_get_w_h(struct bcm2835_fb_config *fb) 4938826550bSOleksandr Tymoshenko { 4948826550bSOleksandr Tymoshenko int err; 4958826550bSOleksandr Tymoshenko struct msg_fb_get_w_h msg; 4968826550bSOleksandr Tymoshenko 4978826550bSOleksandr Tymoshenko memset(&msg, 0, sizeof(msg)); 4988826550bSOleksandr Tymoshenko msg.hdr.buf_size = sizeof(msg); 4998826550bSOleksandr Tymoshenko msg.hdr.code = BCM2835_MBOX_CODE_REQ; 5008826550bSOleksandr Tymoshenko BCM2835_MBOX_INIT_TAG(&msg.physical_w_h, GET_PHYSICAL_W_H); 5018826550bSOleksandr Tymoshenko msg.physical_w_h.tag_hdr.val_len = 0; 5028826550bSOleksandr Tymoshenko msg.end_tag = 0; 5038826550bSOleksandr Tymoshenko 5048826550bSOleksandr Tymoshenko err = bcm2835_mbox_property(&msg, sizeof(msg)); 505ae29e2d8SLuiz Otavio O Souza if (err == 0) { 5068826550bSOleksandr Tymoshenko fb->xres = msg.physical_w_h.body.resp.width; 5078826550bSOleksandr Tymoshenko fb->yres = msg.physical_w_h.body.resp.height; 508ae29e2d8SLuiz Otavio O Souza } 509ae29e2d8SLuiz Otavio O Souza 5108826550bSOleksandr Tymoshenko return (err); 5118826550bSOleksandr Tymoshenko } 5128826550bSOleksandr Tymoshenko 5138826550bSOleksandr Tymoshenko int 514c344416eSOleksandr Tymoshenko bcm2835_mbox_fb_get_bpp(struct bcm2835_fb_config *fb) 515c344416eSOleksandr Tymoshenko { 516c344416eSOleksandr Tymoshenko int err; 517c344416eSOleksandr Tymoshenko struct msg_fb_get_bpp msg; 518c344416eSOleksandr Tymoshenko 519c344416eSOleksandr Tymoshenko memset(&msg, 0, sizeof(msg)); 520c344416eSOleksandr Tymoshenko msg.hdr.buf_size = sizeof(msg); 521c344416eSOleksandr Tymoshenko msg.hdr.code = BCM2835_MBOX_CODE_REQ; 522c344416eSOleksandr Tymoshenko BCM2835_MBOX_INIT_TAG(&msg.bpp, GET_DEPTH); 523c344416eSOleksandr Tymoshenko msg.bpp.tag_hdr.val_len = 0; 524c344416eSOleksandr Tymoshenko msg.end_tag = 0; 525c344416eSOleksandr Tymoshenko 526c344416eSOleksandr Tymoshenko err = bcm2835_mbox_property(&msg, sizeof(msg)); 527c344416eSOleksandr Tymoshenko if (err == 0) 528c344416eSOleksandr Tymoshenko fb->bpp = msg.bpp.body.resp.bpp; 529c344416eSOleksandr Tymoshenko 530c344416eSOleksandr Tymoshenko return (err); 531c344416eSOleksandr Tymoshenko } 532c344416eSOleksandr Tymoshenko 533c344416eSOleksandr Tymoshenko int 5348826550bSOleksandr Tymoshenko bcm2835_mbox_fb_init(struct bcm2835_fb_config *fb) 5358826550bSOleksandr Tymoshenko { 5368826550bSOleksandr Tymoshenko int err; 5378826550bSOleksandr Tymoshenko struct msg_fb_setup msg; 5388826550bSOleksandr Tymoshenko 5398826550bSOleksandr Tymoshenko memset(&msg, 0, sizeof(msg)); 5408826550bSOleksandr Tymoshenko msg.hdr.buf_size = sizeof(msg); 5418826550bSOleksandr Tymoshenko msg.hdr.code = BCM2835_MBOX_CODE_REQ; 5428826550bSOleksandr Tymoshenko BCM2835_MBOX_INIT_TAG(&msg.physical_w_h, SET_PHYSICAL_W_H); 5438826550bSOleksandr Tymoshenko msg.physical_w_h.body.req.width = fb->xres; 5448826550bSOleksandr Tymoshenko msg.physical_w_h.body.req.height = fb->yres; 5458826550bSOleksandr Tymoshenko BCM2835_MBOX_INIT_TAG(&msg.virtual_w_h, SET_VIRTUAL_W_H); 5468826550bSOleksandr Tymoshenko msg.virtual_w_h.body.req.width = fb->vxres; 5478826550bSOleksandr Tymoshenko msg.virtual_w_h.body.req.height = fb->vyres; 548658b482eSOleksandr Tymoshenko BCM2835_MBOX_INIT_TAG(&msg.offset, SET_VIRTUAL_OFFSET); 5498826550bSOleksandr Tymoshenko msg.offset.body.req.x = fb->xoffset; 5508826550bSOleksandr Tymoshenko msg.offset.body.req.y = fb->yoffset; 5518826550bSOleksandr Tymoshenko BCM2835_MBOX_INIT_TAG(&msg.depth, SET_DEPTH); 5528826550bSOleksandr Tymoshenko msg.depth.body.req.bpp = fb->bpp; 5538826550bSOleksandr Tymoshenko BCM2835_MBOX_INIT_TAG(&msg.alpha, SET_ALPHA_MODE); 5548826550bSOleksandr Tymoshenko msg.alpha.body.req.alpha = BCM2835_MBOX_ALPHA_MODE_IGNORED; 5558826550bSOleksandr Tymoshenko BCM2835_MBOX_INIT_TAG(&msg.buffer, ALLOCATE_BUFFER); 5568826550bSOleksandr Tymoshenko msg.buffer.body.req.alignment = PAGE_SIZE; 5578826550bSOleksandr Tymoshenko BCM2835_MBOX_INIT_TAG(&msg.pitch, GET_PITCH); 5588826550bSOleksandr Tymoshenko msg.end_tag = 0; 5598826550bSOleksandr Tymoshenko 5608826550bSOleksandr Tymoshenko err = bcm2835_mbox_property(&msg, sizeof(msg)); 5618826550bSOleksandr Tymoshenko if (err == 0) { 5628826550bSOleksandr Tymoshenko fb->xres = msg.physical_w_h.body.resp.width; 5638826550bSOleksandr Tymoshenko fb->yres = msg.physical_w_h.body.resp.height; 5648826550bSOleksandr Tymoshenko fb->vxres = msg.virtual_w_h.body.resp.width; 5658826550bSOleksandr Tymoshenko fb->vyres = msg.virtual_w_h.body.resp.height; 5668826550bSOleksandr Tymoshenko fb->xoffset = msg.offset.body.resp.x; 5678826550bSOleksandr Tymoshenko fb->yoffset = msg.offset.body.resp.y; 5688826550bSOleksandr Tymoshenko fb->pitch = msg.pitch.body.resp.pitch; 56940084ac3SKyle Evans fb->base = VCBUS_TO_ARMC(msg.buffer.body.resp.fb_address); 5708826550bSOleksandr Tymoshenko fb->size = msg.buffer.body.resp.fb_size; 5718826550bSOleksandr Tymoshenko } 572ae29e2d8SLuiz Otavio O Souza 573ae29e2d8SLuiz Otavio O Souza return (err); 574ae29e2d8SLuiz Otavio O Souza } 575