11b1a53cfSOleksandr Tymoshenko /*- 21b1a53cfSOleksandr Tymoshenko * Copyright (c) 2012 Oleksandr Tymoshenko <gonzo@freebsd.org> 31b1a53cfSOleksandr Tymoshenko * All rights reserved. 41b1a53cfSOleksandr Tymoshenko * 51b1a53cfSOleksandr Tymoshenko * Redistribution and use in source and binary forms, with or without 61b1a53cfSOleksandr Tymoshenko * modification, are permitted provided that the following conditions 71b1a53cfSOleksandr Tymoshenko * are met: 81b1a53cfSOleksandr Tymoshenko * 1. Redistributions of source code must retain the above copyright 91b1a53cfSOleksandr Tymoshenko * notice, this list of conditions and the following disclaimer. 101b1a53cfSOleksandr Tymoshenko * 2. Redistributions in binary form must reproduce the above copyright 111b1a53cfSOleksandr Tymoshenko * notice, this list of conditions and the following disclaimer in the 121b1a53cfSOleksandr Tymoshenko * documentation and/or other materials provided with the distribution. 131b1a53cfSOleksandr Tymoshenko * 141b1a53cfSOleksandr Tymoshenko * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 151b1a53cfSOleksandr Tymoshenko * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 161b1a53cfSOleksandr Tymoshenko * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 171b1a53cfSOleksandr Tymoshenko * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 181b1a53cfSOleksandr Tymoshenko * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 191b1a53cfSOleksandr Tymoshenko * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 201b1a53cfSOleksandr Tymoshenko * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 211b1a53cfSOleksandr Tymoshenko * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 221b1a53cfSOleksandr Tymoshenko * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 231b1a53cfSOleksandr Tymoshenko * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 241b1a53cfSOleksandr Tymoshenko * SUCH DAMAGE. 251b1a53cfSOleksandr Tymoshenko */ 261b1a53cfSOleksandr Tymoshenko 271b1a53cfSOleksandr Tymoshenko #include <sys/cdefs.h> 281b1a53cfSOleksandr Tymoshenko __FBSDID("$FreeBSD$"); 291b1a53cfSOleksandr Tymoshenko 301b1a53cfSOleksandr Tymoshenko #include <sys/param.h> 311b1a53cfSOleksandr Tymoshenko #include <sys/systm.h> 321b1a53cfSOleksandr Tymoshenko #include <sys/bus.h> 331b1a53cfSOleksandr Tymoshenko #include <sys/kernel.h> 3409df4bfdSLuiz Otavio O Souza #include <sys/lock.h> 351b1a53cfSOleksandr Tymoshenko #include <sys/module.h> 3609df4bfdSLuiz Otavio O Souza #include <sys/mutex.h> 371b1a53cfSOleksandr Tymoshenko #include <sys/rman.h> 381b1a53cfSOleksandr Tymoshenko #include <machine/bus.h> 391b1a53cfSOleksandr Tymoshenko 401b1a53cfSOleksandr Tymoshenko #include <dev/ofw/ofw_bus.h> 411b1a53cfSOleksandr Tymoshenko #include <dev/ofw/ofw_bus_subr.h> 421b1a53cfSOleksandr Tymoshenko 431b1a53cfSOleksandr Tymoshenko #include <arm/broadcom/bcm2835/bcm2835_mbox.h> 44b812d0adSAndrew Turner #include <arm/broadcom/bcm2835/bcm2835_mbox_prop.h> 45b812d0adSAndrew Turner #include <arm/broadcom/bcm2835/bcm2835_vcbus.h> 461b1a53cfSOleksandr Tymoshenko 47279cd101SRui Paulo #include "mbox_if.h" 48279cd101SRui Paulo 491b1a53cfSOleksandr Tymoshenko #define REG_READ 0x00 501b1a53cfSOleksandr Tymoshenko #define REG_POL 0x10 511b1a53cfSOleksandr Tymoshenko #define REG_SENDER 0x14 521b1a53cfSOleksandr Tymoshenko #define REG_STATUS 0x18 531b1a53cfSOleksandr Tymoshenko #define STATUS_FULL 0x80000000 541b1a53cfSOleksandr Tymoshenko #define STATUS_EMPTY 0x40000000 551b1a53cfSOleksandr Tymoshenko #define REG_CONFIG 0x1C 561b1a53cfSOleksandr Tymoshenko #define CONFIG_DATA_IRQ 0x00000001 571b1a53cfSOleksandr Tymoshenko #define REG_WRITE 0x20 /* This is Mailbox 1 address */ 581b1a53cfSOleksandr Tymoshenko 591b1a53cfSOleksandr Tymoshenko #define MBOX_MSG(chan, data) (((data) & ~0xf) | ((chan) & 0xf)) 601b1a53cfSOleksandr Tymoshenko #define MBOX_CHAN(msg) ((msg) & 0xf) 611b1a53cfSOleksandr Tymoshenko #define MBOX_DATA(msg) ((msg) & ~0xf) 621b1a53cfSOleksandr Tymoshenko 63279cd101SRui Paulo #define MBOX_LOCK(sc) do { \ 64279cd101SRui Paulo mtx_lock(&(sc)->lock); \ 651b1a53cfSOleksandr Tymoshenko } while(0) 661b1a53cfSOleksandr Tymoshenko 67279cd101SRui Paulo #define MBOX_UNLOCK(sc) do { \ 68279cd101SRui Paulo mtx_unlock(&(sc)->lock); \ 691b1a53cfSOleksandr Tymoshenko } while(0) 701b1a53cfSOleksandr Tymoshenko 711b1a53cfSOleksandr Tymoshenko #ifdef DEBUG 721b1a53cfSOleksandr Tymoshenko #define dprintf(fmt, args...) printf(fmt, ##args) 731b1a53cfSOleksandr Tymoshenko #else 741b1a53cfSOleksandr Tymoshenko #define dprintf(fmt, args...) 751b1a53cfSOleksandr Tymoshenko #endif 761b1a53cfSOleksandr Tymoshenko 771b1a53cfSOleksandr Tymoshenko struct bcm_mbox_softc { 781b1a53cfSOleksandr Tymoshenko struct mtx lock; 791b1a53cfSOleksandr Tymoshenko struct resource * mem_res; 801b1a53cfSOleksandr Tymoshenko struct resource * irq_res; 811b1a53cfSOleksandr Tymoshenko void* intr_hl; 821b1a53cfSOleksandr Tymoshenko bus_space_tag_t bst; 831b1a53cfSOleksandr Tymoshenko bus_space_handle_t bsh; 841b1a53cfSOleksandr Tymoshenko int msg[BCM2835_MBOX_CHANS]; 85*b805a8c3SOleksandr Tymoshenko int have_message[BCM2835_MBOX_CHANS]; 861b1a53cfSOleksandr Tymoshenko }; 871b1a53cfSOleksandr Tymoshenko 88279cd101SRui Paulo #define mbox_read_4(sc, reg) \ 89279cd101SRui Paulo bus_space_read_4((sc)->bst, (sc)->bsh, reg) 90279cd101SRui Paulo #define mbox_write_4(sc, reg, val) \ 91279cd101SRui Paulo bus_space_write_4((sc)->bst, (sc)->bsh, reg, val) 921b1a53cfSOleksandr Tymoshenko 93d792cd92SAndrew Turner static int 94d792cd92SAndrew Turner bcm_mbox_read_msg(struct bcm_mbox_softc *sc, int *ochan) 951b1a53cfSOleksandr Tymoshenko { 961b1a53cfSOleksandr Tymoshenko uint32_t data; 971b1a53cfSOleksandr Tymoshenko uint32_t msg; 98d792cd92SAndrew Turner int chan; 991b1a53cfSOleksandr Tymoshenko 100279cd101SRui Paulo msg = mbox_read_4(sc, REG_READ); 1011b1a53cfSOleksandr Tymoshenko dprintf("bcm_mbox_intr: raw data %08x\n", msg); 1021b1a53cfSOleksandr Tymoshenko chan = MBOX_CHAN(msg); 1031b1a53cfSOleksandr Tymoshenko data = MBOX_DATA(msg); 104b9450e43SRui Paulo if (sc->msg[chan]) { 1051b1a53cfSOleksandr Tymoshenko printf("bcm_mbox_intr: channel %d oveflow\n", chan); 106d792cd92SAndrew Turner return (1); 1071b1a53cfSOleksandr Tymoshenko } 1081b1a53cfSOleksandr Tymoshenko dprintf("bcm_mbox_intr: chan %d, data %08x\n", chan, data); 1091432fa20SLuiz Otavio O Souza sc->msg[chan] = msg; 110d792cd92SAndrew Turner 111d792cd92SAndrew Turner if (ochan != NULL) 112d792cd92SAndrew Turner *ochan = chan; 113d792cd92SAndrew Turner 114d792cd92SAndrew Turner return (0); 1151b1a53cfSOleksandr Tymoshenko } 116d792cd92SAndrew Turner 117d792cd92SAndrew Turner static void 118d792cd92SAndrew Turner bcm_mbox_intr(void *arg) 119d792cd92SAndrew Turner { 120d792cd92SAndrew Turner struct bcm_mbox_softc *sc = arg; 121d792cd92SAndrew Turner int chan; 122d792cd92SAndrew Turner 123*b805a8c3SOleksandr Tymoshenko MBOX_LOCK(sc); 124d792cd92SAndrew Turner while (!(mbox_read_4(sc, REG_STATUS) & STATUS_EMPTY)) 125d792cd92SAndrew Turner if (bcm_mbox_read_msg(sc, &chan) == 0) 126*b805a8c3SOleksandr Tymoshenko wakeup(&sc->have_message[chan]); 127*b805a8c3SOleksandr Tymoshenko MBOX_UNLOCK(sc); 1281b1a53cfSOleksandr Tymoshenko } 1291b1a53cfSOleksandr Tymoshenko 1301b1a53cfSOleksandr Tymoshenko static int 1311b1a53cfSOleksandr Tymoshenko bcm_mbox_probe(device_t dev) 1321b1a53cfSOleksandr Tymoshenko { 1331b1a53cfSOleksandr Tymoshenko 134add35ed5SIan Lepore if (!ofw_bus_status_okay(dev)) 135add35ed5SIan Lepore return (ENXIO); 136add35ed5SIan Lepore 1371b1a53cfSOleksandr Tymoshenko if (ofw_bus_is_compatible(dev, "broadcom,bcm2835-mbox")) { 1381b1a53cfSOleksandr Tymoshenko device_set_desc(dev, "BCM2835 VideoCore Mailbox"); 1391b1a53cfSOleksandr Tymoshenko return(BUS_PROBE_DEFAULT); 1401b1a53cfSOleksandr Tymoshenko } 1411b1a53cfSOleksandr Tymoshenko 1421b1a53cfSOleksandr Tymoshenko return (ENXIO); 1431b1a53cfSOleksandr Tymoshenko } 1441b1a53cfSOleksandr Tymoshenko 1451b1a53cfSOleksandr Tymoshenko static int 1461b1a53cfSOleksandr Tymoshenko bcm_mbox_attach(device_t dev) 1471b1a53cfSOleksandr Tymoshenko { 1481b1a53cfSOleksandr Tymoshenko struct bcm_mbox_softc *sc = device_get_softc(dev); 1491b1a53cfSOleksandr Tymoshenko int i; 1501b1a53cfSOleksandr Tymoshenko int rid = 0; 1511b1a53cfSOleksandr Tymoshenko 1521b1a53cfSOleksandr Tymoshenko sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); 1531b1a53cfSOleksandr Tymoshenko if (sc->mem_res == NULL) { 1541b1a53cfSOleksandr Tymoshenko device_printf(dev, "could not allocate memory resource\n"); 1551b1a53cfSOleksandr Tymoshenko return (ENXIO); 1561b1a53cfSOleksandr Tymoshenko } 1571b1a53cfSOleksandr Tymoshenko 1581b1a53cfSOleksandr Tymoshenko sc->bst = rman_get_bustag(sc->mem_res); 1591b1a53cfSOleksandr Tymoshenko sc->bsh = rman_get_bushandle(sc->mem_res); 1601b1a53cfSOleksandr Tymoshenko 1611b1a53cfSOleksandr Tymoshenko rid = 0; 1621b1a53cfSOleksandr Tymoshenko sc->irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_ACTIVE); 1631b1a53cfSOleksandr Tymoshenko if (sc->irq_res == NULL) { 1641b1a53cfSOleksandr Tymoshenko device_printf(dev, "could not allocate interrupt resource\n"); 1651b1a53cfSOleksandr Tymoshenko return (ENXIO); 1661b1a53cfSOleksandr Tymoshenko } 1671b1a53cfSOleksandr Tymoshenko 1681b1a53cfSOleksandr Tymoshenko /* Setup and enable the timer */ 16995506b81SRui Paulo if (bus_setup_intr(dev, sc->irq_res, INTR_MPSAFE | INTR_TYPE_MISC, 17095506b81SRui Paulo NULL, bcm_mbox_intr, sc, &sc->intr_hl) != 0) { 17195506b81SRui Paulo bus_release_resource(dev, SYS_RES_IRQ, rid, sc->irq_res); 1721b1a53cfSOleksandr Tymoshenko device_printf(dev, "Unable to setup the clock irq handler.\n"); 1731b1a53cfSOleksandr Tymoshenko return (ENXIO); 1741b1a53cfSOleksandr Tymoshenko } 1751b1a53cfSOleksandr Tymoshenko 176d01195e3SChristian Brueffer mtx_init(&sc->lock, "vcio mbox", NULL, MTX_DEF); 1771b1a53cfSOleksandr Tymoshenko for (i = 0; i < BCM2835_MBOX_CHANS; i++) { 178b9450e43SRui Paulo sc->msg[i] = 0; 179*b805a8c3SOleksandr Tymoshenko sc->have_message[i] = 0; 1801b1a53cfSOleksandr Tymoshenko } 1811b1a53cfSOleksandr Tymoshenko 1821b1a53cfSOleksandr Tymoshenko /* Read all pending messages */ 1831432fa20SLuiz Otavio O Souza while ((mbox_read_4(sc, REG_STATUS) & STATUS_EMPTY) == 0) 1841432fa20SLuiz Otavio O Souza (void)mbox_read_4(sc, REG_READ); 1851b1a53cfSOleksandr Tymoshenko 186279cd101SRui Paulo mbox_write_4(sc, REG_CONFIG, CONFIG_DATA_IRQ); 187279cd101SRui Paulo 188279cd101SRui Paulo return (0); 189279cd101SRui Paulo } 190279cd101SRui Paulo 191279cd101SRui Paulo /* 192279cd101SRui Paulo * Mailbox API 193279cd101SRui Paulo */ 194279cd101SRui Paulo static int 195279cd101SRui Paulo bcm_mbox_write(device_t dev, int chan, uint32_t data) 196279cd101SRui Paulo { 19709df4bfdSLuiz Otavio O Souza int limit = 1000; 198279cd101SRui Paulo struct bcm_mbox_softc *sc = device_get_softc(dev); 199279cd101SRui Paulo 200279cd101SRui Paulo dprintf("bcm_mbox_write: chan %d, data %08x\n", chan, data); 201279cd101SRui Paulo MBOX_LOCK(sc); 202*b805a8c3SOleksandr Tymoshenko sc->have_message[chan] = 0; 20309df4bfdSLuiz Otavio O Souza while ((mbox_read_4(sc, REG_STATUS) & STATUS_FULL) && --limit) 20409df4bfdSLuiz Otavio O Souza DELAY(5); 205279cd101SRui Paulo if (limit == 0) { 206279cd101SRui Paulo printf("bcm_mbox_write: STATUS_FULL stuck"); 207279cd101SRui Paulo MBOX_UNLOCK(sc); 208279cd101SRui Paulo return (EAGAIN); 209279cd101SRui Paulo } 210279cd101SRui Paulo mbox_write_4(sc, REG_WRITE, MBOX_MSG(chan, data)); 211279cd101SRui Paulo MBOX_UNLOCK(sc); 21209df4bfdSLuiz Otavio O Souza 213279cd101SRui Paulo return (0); 214279cd101SRui Paulo } 215279cd101SRui Paulo 216279cd101SRui Paulo static int 217279cd101SRui Paulo bcm_mbox_read(device_t dev, int chan, uint32_t *data) 218279cd101SRui Paulo { 219279cd101SRui Paulo struct bcm_mbox_softc *sc = device_get_softc(dev); 220d792cd92SAndrew Turner int err, read_chan; 221279cd101SRui Paulo 222279cd101SRui Paulo dprintf("bcm_mbox_read: chan %d\n", chan); 223d792cd92SAndrew Turner 224d792cd92SAndrew Turner err = 0; 225279cd101SRui Paulo MBOX_LOCK(sc); 226d792cd92SAndrew Turner if (!cold) { 227*b805a8c3SOleksandr Tymoshenko if (sc->have_message[chan] == 0) { 228*b805a8c3SOleksandr Tymoshenko if (mtx_sleep(&sc->have_message[chan], &sc->lock, 0, 229*b805a8c3SOleksandr Tymoshenko "mbox", 10*hz) != 0) { 230*b805a8c3SOleksandr Tymoshenko device_printf(dev, "timeout waiting for message on chan %d\n", chan); 231*b805a8c3SOleksandr Tymoshenko err = ETIMEDOUT; 232*b805a8c3SOleksandr Tymoshenko } 233b9450e43SRui Paulo } 234d792cd92SAndrew Turner } else { 235d792cd92SAndrew Turner do { 236d792cd92SAndrew Turner /* Wait for a message */ 237d792cd92SAndrew Turner while ((mbox_read_4(sc, REG_STATUS) & STATUS_EMPTY)) 238d792cd92SAndrew Turner ; 239d792cd92SAndrew Turner /* Read the message */ 240d792cd92SAndrew Turner if (bcm_mbox_read_msg(sc, &read_chan) != 0) { 241d792cd92SAndrew Turner err = EINVAL; 242d792cd92SAndrew Turner goto out; 243d792cd92SAndrew Turner } 244d792cd92SAndrew Turner } while (read_chan != chan); 245d792cd92SAndrew Turner } 246b9450e43SRui Paulo /* 247b9450e43SRui Paulo * get data from intr handler, the same channel is never coming 248b9450e43SRui Paulo * because of holding sc lock. 249b9450e43SRui Paulo */ 250b9450e43SRui Paulo *data = MBOX_DATA(sc->msg[chan]); 251b9450e43SRui Paulo sc->msg[chan] = 0; 252*b805a8c3SOleksandr Tymoshenko sc->have_message[chan] = 0; 253d792cd92SAndrew Turner out: 254279cd101SRui Paulo MBOX_UNLOCK(sc); 255279cd101SRui Paulo dprintf("bcm_mbox_read: chan %d, data %08x\n", chan, *data); 2561b1a53cfSOleksandr Tymoshenko 257d792cd92SAndrew Turner return (err); 2581b1a53cfSOleksandr Tymoshenko } 2591b1a53cfSOleksandr Tymoshenko 2601b1a53cfSOleksandr Tymoshenko static device_method_t bcm_mbox_methods[] = { 2611b1a53cfSOleksandr Tymoshenko DEVMETHOD(device_probe, bcm_mbox_probe), 2621b1a53cfSOleksandr Tymoshenko DEVMETHOD(device_attach, bcm_mbox_attach), 263279cd101SRui Paulo 264279cd101SRui Paulo DEVMETHOD(mbox_read, bcm_mbox_read), 265279cd101SRui Paulo DEVMETHOD(mbox_write, bcm_mbox_write), 266279cd101SRui Paulo 267279cd101SRui Paulo DEVMETHOD_END 2681b1a53cfSOleksandr Tymoshenko }; 2691b1a53cfSOleksandr Tymoshenko 2701b1a53cfSOleksandr Tymoshenko static driver_t bcm_mbox_driver = { 2711b1a53cfSOleksandr Tymoshenko "mbox", 2721b1a53cfSOleksandr Tymoshenko bcm_mbox_methods, 2731b1a53cfSOleksandr Tymoshenko sizeof(struct bcm_mbox_softc), 2741b1a53cfSOleksandr Tymoshenko }; 2751b1a53cfSOleksandr Tymoshenko 2761b1a53cfSOleksandr Tymoshenko static devclass_t bcm_mbox_devclass; 2771b1a53cfSOleksandr Tymoshenko 2781b1a53cfSOleksandr Tymoshenko DRIVER_MODULE(mbox, simplebus, bcm_mbox_driver, bcm_mbox_devclass, 0, 0); 279b812d0adSAndrew Turner 280b812d0adSAndrew Turner static void 281b812d0adSAndrew Turner bcm2835_mbox_dma_cb(void *arg, bus_dma_segment_t *segs, int nseg, int err) 282b812d0adSAndrew Turner { 283b812d0adSAndrew Turner bus_addr_t *addr; 284b812d0adSAndrew Turner 285b812d0adSAndrew Turner if (err) 286b812d0adSAndrew Turner return; 287b812d0adSAndrew Turner addr = (bus_addr_t *)arg; 288b812d0adSAndrew Turner *addr = PHYS_TO_VCBUS(segs[0].ds_addr); 289b812d0adSAndrew Turner } 290b812d0adSAndrew Turner 29127eb3304SAndrew Turner static void * 29227eb3304SAndrew Turner bcm2835_mbox_init_dma(device_t dev, size_t len, bus_dma_tag_t *tag, 29327eb3304SAndrew Turner bus_dmamap_t *map, bus_addr_t *phys) 29427eb3304SAndrew Turner { 29527eb3304SAndrew Turner void *buf; 29627eb3304SAndrew Turner int err; 29727eb3304SAndrew Turner 29827eb3304SAndrew Turner err = bus_dma_tag_create(bus_get_dma_tag(dev), 16, 0, 29927eb3304SAndrew Turner BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, 30027eb3304SAndrew Turner len, 1, len, 0, NULL, NULL, tag); 30127eb3304SAndrew Turner if (err != 0) { 30227eb3304SAndrew Turner device_printf(dev, "can't create DMA tag\n"); 30327eb3304SAndrew Turner return (NULL); 30427eb3304SAndrew Turner } 30527eb3304SAndrew Turner 30627eb3304SAndrew Turner err = bus_dmamem_alloc(*tag, &buf, 0, map); 30727eb3304SAndrew Turner if (err != 0) { 30827eb3304SAndrew Turner bus_dma_tag_destroy(*tag); 30927eb3304SAndrew Turner device_printf(dev, "can't allocate dmamem\n"); 31027eb3304SAndrew Turner return (NULL); 31127eb3304SAndrew Turner } 31227eb3304SAndrew Turner 313723af368SLuiz Otavio O Souza err = bus_dmamap_load(*tag, *map, buf, len, bcm2835_mbox_dma_cb, 31427eb3304SAndrew Turner phys, 0); 31527eb3304SAndrew Turner if (err != 0) { 31627eb3304SAndrew Turner bus_dmamem_free(*tag, buf, *map); 31727eb3304SAndrew Turner bus_dma_tag_destroy(*tag); 31827eb3304SAndrew Turner device_printf(dev, "can't load DMA map\n"); 31927eb3304SAndrew Turner return (NULL); 32027eb3304SAndrew Turner } 32127eb3304SAndrew Turner 32227eb3304SAndrew Turner return (buf); 32327eb3304SAndrew Turner } 32427eb3304SAndrew Turner 325ae29e2d8SLuiz Otavio O Souza static int 326ae29e2d8SLuiz Otavio O Souza bcm2835_mbox_err(device_t dev, bus_addr_t msg_phys, uint32_t resp_phys, 327ae29e2d8SLuiz Otavio O Souza struct bcm2835_mbox_hdr *msg, size_t len) 328ae29e2d8SLuiz Otavio O Souza { 329ae29e2d8SLuiz Otavio O Souza int idx; 330ae29e2d8SLuiz Otavio O Souza struct bcm2835_mbox_tag_hdr *tag; 331ae29e2d8SLuiz Otavio O Souza uint8_t *last; 332ae29e2d8SLuiz Otavio O Souza 333ae29e2d8SLuiz Otavio O Souza if ((uint32_t)msg_phys != resp_phys) { 334ae29e2d8SLuiz Otavio O Souza device_printf(dev, "response channel mismatch\n"); 335ae29e2d8SLuiz Otavio O Souza return (EIO); 336ae29e2d8SLuiz Otavio O Souza } 337ae29e2d8SLuiz Otavio O Souza if (msg->code != BCM2835_MBOX_CODE_RESP_SUCCESS) { 338ae29e2d8SLuiz Otavio O Souza device_printf(dev, "mbox response error\n"); 339ae29e2d8SLuiz Otavio O Souza return (EIO); 340ae29e2d8SLuiz Otavio O Souza } 341ae29e2d8SLuiz Otavio O Souza 342ae29e2d8SLuiz Otavio O Souza /* Loop until the end tag. */ 343ae29e2d8SLuiz Otavio O Souza tag = (struct bcm2835_mbox_tag_hdr *)(msg + 1); 344ae29e2d8SLuiz Otavio O Souza last = (uint8_t *)msg + len; 345ae29e2d8SLuiz Otavio O Souza for (idx = 0; tag->tag != 0; idx++) { 346ae29e2d8SLuiz Otavio O Souza if ((tag->val_len & BCM2835_MBOX_TAG_VAL_LEN_RESPONSE) == 0) { 347ae29e2d8SLuiz Otavio O Souza device_printf(dev, "tag %d response error\n", idx); 348ae29e2d8SLuiz Otavio O Souza return (EIO); 349ae29e2d8SLuiz Otavio O Souza } 350ae29e2d8SLuiz Otavio O Souza /* Clear the response bit. */ 351ae29e2d8SLuiz Otavio O Souza tag->val_len &= ~BCM2835_MBOX_TAG_VAL_LEN_RESPONSE; 352ae29e2d8SLuiz Otavio O Souza 353ae29e2d8SLuiz Otavio O Souza /* Next tag. */ 354ae29e2d8SLuiz Otavio O Souza tag = (struct bcm2835_mbox_tag_hdr *)((uint8_t *)tag + 355ae29e2d8SLuiz Otavio O Souza sizeof(*tag) + tag->val_buf_size); 356ae29e2d8SLuiz Otavio O Souza 357ae29e2d8SLuiz Otavio O Souza if ((uint8_t *)tag > last) { 358ae29e2d8SLuiz Otavio O Souza device_printf(dev, "mbox buffer size error\n"); 359ae29e2d8SLuiz Otavio O Souza return (EIO); 360ae29e2d8SLuiz Otavio O Souza } 361ae29e2d8SLuiz Otavio O Souza } 362ae29e2d8SLuiz Otavio O Souza 363ae29e2d8SLuiz Otavio O Souza return (0); 364ae29e2d8SLuiz Otavio O Souza } 365ae29e2d8SLuiz Otavio O Souza 366b812d0adSAndrew Turner int 3678826550bSOleksandr Tymoshenko bcm2835_mbox_property(void *msg, size_t msg_size) 368b812d0adSAndrew Turner { 3698826550bSOleksandr Tymoshenko struct msg_set_power_state *buf; 370b812d0adSAndrew Turner bus_dma_tag_t msg_tag; 371b812d0adSAndrew Turner bus_dmamap_t msg_map; 372b812d0adSAndrew Turner bus_addr_t msg_phys; 373b812d0adSAndrew Turner uint32_t reg; 374b812d0adSAndrew Turner device_t mbox; 375ae29e2d8SLuiz Otavio O Souza int err; 376ae29e2d8SLuiz Otavio O Souza 377ae29e2d8SLuiz Otavio O Souza /* get mbox device */ 378ae29e2d8SLuiz Otavio O Souza mbox = devclass_get_device(devclass_find("mbox"), 0); 3798826550bSOleksandr Tymoshenko if (mbox == NULL) 380ae29e2d8SLuiz Otavio O Souza return (ENXIO); 381ae29e2d8SLuiz Otavio O Souza 382ae29e2d8SLuiz Otavio O Souza /* Allocate memory for the message */ 3838826550bSOleksandr Tymoshenko buf = bcm2835_mbox_init_dma(mbox, msg_size, &msg_tag, &msg_map, 384ae29e2d8SLuiz Otavio O Souza &msg_phys); 3858826550bSOleksandr Tymoshenko if (buf == NULL) 386ae29e2d8SLuiz Otavio O Souza return (ENOMEM); 387ae29e2d8SLuiz Otavio O Souza 3888826550bSOleksandr Tymoshenko memcpy(buf, msg, msg_size); 389ae29e2d8SLuiz Otavio O Souza 3908826550bSOleksandr Tymoshenko bus_dmamap_sync(msg_tag, msg_map, 3918826550bSOleksandr Tymoshenko BUS_DMASYNC_PREWRITE); 3928826550bSOleksandr Tymoshenko 393ae29e2d8SLuiz Otavio O Souza MBOX_WRITE(mbox, BCM2835_MBOX_CHAN_PROP, (uint32_t)msg_phys); 394ae29e2d8SLuiz Otavio O Souza MBOX_READ(mbox, BCM2835_MBOX_CHAN_PROP, ®); 395ae29e2d8SLuiz Otavio O Souza 3968826550bSOleksandr Tymoshenko bus_dmamap_sync(msg_tag, msg_map, 3978826550bSOleksandr Tymoshenko BUS_DMASYNC_PREREAD); 3988826550bSOleksandr Tymoshenko 3998826550bSOleksandr Tymoshenko memcpy(msg, buf, msg_size); 4008826550bSOleksandr Tymoshenko 4018826550bSOleksandr Tymoshenko err = bcm2835_mbox_err(mbox, msg_phys, reg, 4028826550bSOleksandr Tymoshenko (struct bcm2835_mbox_hdr *)msg, msg_size); 403ae29e2d8SLuiz Otavio O Souza 404ae29e2d8SLuiz Otavio O Souza bus_dmamap_unload(msg_tag, msg_map); 4058826550bSOleksandr Tymoshenko bus_dmamem_free(msg_tag, buf, msg_map); 406ae29e2d8SLuiz Otavio O Souza bus_dma_tag_destroy(msg_tag); 407ae29e2d8SLuiz Otavio O Souza 408ae29e2d8SLuiz Otavio O Souza return (err); 409ae29e2d8SLuiz Otavio O Souza } 410ae29e2d8SLuiz Otavio O Souza 411ae29e2d8SLuiz Otavio O Souza int 4128826550bSOleksandr Tymoshenko bcm2835_mbox_set_power_state(uint32_t device_id, boolean_t on) 413ae29e2d8SLuiz Otavio O Souza { 4148826550bSOleksandr Tymoshenko struct msg_set_power_state msg; 415ae29e2d8SLuiz Otavio O Souza int err; 416ae29e2d8SLuiz Otavio O Souza 4178826550bSOleksandr Tymoshenko memset(&msg, 0, sizeof(msg)); 4188826550bSOleksandr Tymoshenko msg.hdr.buf_size = sizeof(msg); 4198826550bSOleksandr Tymoshenko msg.hdr.code = BCM2835_MBOX_CODE_REQ; 4208826550bSOleksandr Tymoshenko msg.tag_hdr.tag = BCM2835_MBOX_TAG_SET_POWER_STATE; 4218826550bSOleksandr Tymoshenko msg.tag_hdr.val_buf_size = sizeof(msg.body); 4228826550bSOleksandr Tymoshenko msg.tag_hdr.val_len = sizeof(msg.body.req); 4238826550bSOleksandr Tymoshenko msg.body.req.device_id = device_id; 4248826550bSOleksandr Tymoshenko msg.body.req.state = (on ? BCM2835_MBOX_POWER_ON : 0) | 4258826550bSOleksandr Tymoshenko BCM2835_MBOX_POWER_WAIT; 4268826550bSOleksandr Tymoshenko msg.end_tag = 0; 4278826550bSOleksandr Tymoshenko 4288826550bSOleksandr Tymoshenko err = bcm2835_mbox_property(&msg, sizeof(msg)); 4298826550bSOleksandr Tymoshenko 4308826550bSOleksandr Tymoshenko return (err); 431ae29e2d8SLuiz Otavio O Souza } 432ae29e2d8SLuiz Otavio O Souza 4338826550bSOleksandr Tymoshenko int 4348826550bSOleksandr Tymoshenko bcm2835_mbox_get_clock_rate(uint32_t clock_id, uint32_t *hz) 4358826550bSOleksandr Tymoshenko { 4368826550bSOleksandr Tymoshenko struct msg_get_clock_rate msg; 4378826550bSOleksandr Tymoshenko int err; 438ae29e2d8SLuiz Otavio O Souza 4398826550bSOleksandr Tymoshenko memset(&msg, 0, sizeof(msg)); 4408826550bSOleksandr Tymoshenko msg.hdr.buf_size = sizeof(msg); 4418826550bSOleksandr Tymoshenko msg.hdr.code = BCM2835_MBOX_CODE_REQ; 4428826550bSOleksandr Tymoshenko msg.tag_hdr.tag = BCM2835_MBOX_TAG_GET_CLOCK_RATE; 4438826550bSOleksandr Tymoshenko msg.tag_hdr.val_buf_size = sizeof(msg.body); 4448826550bSOleksandr Tymoshenko msg.tag_hdr.val_len = sizeof(msg.body.req); 4458826550bSOleksandr Tymoshenko msg.body.req.clock_id = clock_id; 4468826550bSOleksandr Tymoshenko msg.end_tag = 0; 447ae29e2d8SLuiz Otavio O Souza 4488826550bSOleksandr Tymoshenko err = bcm2835_mbox_property(&msg, sizeof(msg)); 4498826550bSOleksandr Tymoshenko *hz = msg.body.resp.rate_hz; 450ae29e2d8SLuiz Otavio O Souza 4518826550bSOleksandr Tymoshenko return (err); 4528826550bSOleksandr Tymoshenko } 453ae29e2d8SLuiz Otavio O Souza 4548826550bSOleksandr Tymoshenko int 4558826550bSOleksandr Tymoshenko bcm2835_mbox_fb_get_w_h(struct bcm2835_fb_config *fb) 4568826550bSOleksandr Tymoshenko { 4578826550bSOleksandr Tymoshenko int err; 4588826550bSOleksandr Tymoshenko struct msg_fb_get_w_h msg; 4598826550bSOleksandr Tymoshenko 4608826550bSOleksandr Tymoshenko memset(&msg, 0, sizeof(msg)); 4618826550bSOleksandr Tymoshenko msg.hdr.buf_size = sizeof(msg); 4628826550bSOleksandr Tymoshenko msg.hdr.code = BCM2835_MBOX_CODE_REQ; 4638826550bSOleksandr Tymoshenko BCM2835_MBOX_INIT_TAG(&msg.physical_w_h, GET_PHYSICAL_W_H); 4648826550bSOleksandr Tymoshenko msg.physical_w_h.tag_hdr.val_len = 0; 4658826550bSOleksandr Tymoshenko BCM2835_MBOX_INIT_TAG(&msg.virtual_w_h, GET_VIRTUAL_W_H); 4668826550bSOleksandr Tymoshenko msg.virtual_w_h.tag_hdr.val_len = 0; 4678826550bSOleksandr Tymoshenko BCM2835_MBOX_INIT_TAG(&msg.offset, GET_VIRTUAL_OFFSET); 4688826550bSOleksandr Tymoshenko msg.offset.tag_hdr.val_len = 0; 4698826550bSOleksandr Tymoshenko msg.end_tag = 0; 4708826550bSOleksandr Tymoshenko 4718826550bSOleksandr Tymoshenko err = bcm2835_mbox_property(&msg, sizeof(msg)); 472ae29e2d8SLuiz Otavio O Souza if (err == 0) { 4738826550bSOleksandr Tymoshenko fb->xres = msg.physical_w_h.body.resp.width; 4748826550bSOleksandr Tymoshenko fb->yres = msg.physical_w_h.body.resp.height; 4758826550bSOleksandr Tymoshenko fb->vxres = msg.virtual_w_h.body.resp.width; 4768826550bSOleksandr Tymoshenko fb->vyres = msg.virtual_w_h.body.resp.height; 4778826550bSOleksandr Tymoshenko fb->xoffset = msg.offset.body.resp.x; 4788826550bSOleksandr Tymoshenko fb->yoffset = msg.offset.body.resp.y; 479ae29e2d8SLuiz Otavio O Souza } 480ae29e2d8SLuiz Otavio O Souza 4818826550bSOleksandr Tymoshenko return (err); 4828826550bSOleksandr Tymoshenko } 4838826550bSOleksandr Tymoshenko 4848826550bSOleksandr Tymoshenko int 4858826550bSOleksandr Tymoshenko bcm2835_mbox_fb_init(struct bcm2835_fb_config *fb) 4868826550bSOleksandr Tymoshenko { 4878826550bSOleksandr Tymoshenko int err; 4888826550bSOleksandr Tymoshenko struct msg_fb_setup msg; 4898826550bSOleksandr Tymoshenko 4908826550bSOleksandr Tymoshenko memset(&msg, 0, sizeof(msg)); 4918826550bSOleksandr Tymoshenko msg.hdr.buf_size = sizeof(msg); 4928826550bSOleksandr Tymoshenko msg.hdr.code = BCM2835_MBOX_CODE_REQ; 4938826550bSOleksandr Tymoshenko BCM2835_MBOX_INIT_TAG(&msg.physical_w_h, SET_PHYSICAL_W_H); 4948826550bSOleksandr Tymoshenko msg.physical_w_h.body.req.width = fb->xres; 4958826550bSOleksandr Tymoshenko msg.physical_w_h.body.req.height = fb->yres; 4968826550bSOleksandr Tymoshenko BCM2835_MBOX_INIT_TAG(&msg.virtual_w_h, SET_VIRTUAL_W_H); 4978826550bSOleksandr Tymoshenko msg.virtual_w_h.body.req.width = fb->vxres; 4988826550bSOleksandr Tymoshenko msg.virtual_w_h.body.req.height = fb->vyres; 4998826550bSOleksandr Tymoshenko BCM2835_MBOX_INIT_TAG(&msg.offset, GET_VIRTUAL_OFFSET); 5008826550bSOleksandr Tymoshenko msg.offset.body.req.x = fb->xoffset; 5018826550bSOleksandr Tymoshenko msg.offset.body.req.y = fb->yoffset; 5028826550bSOleksandr Tymoshenko BCM2835_MBOX_INIT_TAG(&msg.depth, SET_DEPTH); 5038826550bSOleksandr Tymoshenko msg.depth.body.req.bpp = fb->bpp; 5048826550bSOleksandr Tymoshenko BCM2835_MBOX_INIT_TAG(&msg.alpha, SET_ALPHA_MODE); 5058826550bSOleksandr Tymoshenko msg.alpha.body.req.alpha = BCM2835_MBOX_ALPHA_MODE_IGNORED; 5068826550bSOleksandr Tymoshenko BCM2835_MBOX_INIT_TAG(&msg.buffer, ALLOCATE_BUFFER); 5078826550bSOleksandr Tymoshenko msg.buffer.body.req.alignment = PAGE_SIZE; 5088826550bSOleksandr Tymoshenko BCM2835_MBOX_INIT_TAG(&msg.pitch, GET_PITCH); 5098826550bSOleksandr Tymoshenko msg.end_tag = 0; 5108826550bSOleksandr Tymoshenko 5118826550bSOleksandr Tymoshenko err = bcm2835_mbox_property(&msg, sizeof(msg)); 5128826550bSOleksandr Tymoshenko if (err == 0) { 5138826550bSOleksandr Tymoshenko fb->xres = msg.physical_w_h.body.resp.width; 5148826550bSOleksandr Tymoshenko fb->yres = msg.physical_w_h.body.resp.height; 5158826550bSOleksandr Tymoshenko fb->vxres = msg.virtual_w_h.body.resp.width; 5168826550bSOleksandr Tymoshenko fb->vyres = msg.virtual_w_h.body.resp.height; 5178826550bSOleksandr Tymoshenko fb->xoffset = msg.offset.body.resp.x; 5188826550bSOleksandr Tymoshenko fb->yoffset = msg.offset.body.resp.y; 5198826550bSOleksandr Tymoshenko fb->pitch = msg.pitch.body.resp.pitch; 5208826550bSOleksandr Tymoshenko fb->base = VCBUS_TO_PHYS(msg.buffer.body.resp.fb_address); 5218826550bSOleksandr Tymoshenko fb->size = msg.buffer.body.resp.fb_size; 5228826550bSOleksandr Tymoshenko } 523ae29e2d8SLuiz Otavio O Souza 524ae29e2d8SLuiz Otavio O Souza return (err); 525ae29e2d8SLuiz Otavio O Souza } 526