1/* $OpenBSD: bcm2835_mbox.c,v 1.5 2024/05/28 09:19:04 claudio Exp $ */ 2 3/* 4 * Copyright (c) 2020 Tobias Heider <tobhe@openbsd.org> 5 * Copyright (c) 2019 Neil Ashford <ashfordneil0@gmail.com> 6 * 7 * Permission to use, copy, modify, and distribute this software for any 8 * purpose with or without fee is hereby granted, provided that the above 9 * copyright notice and this permission notice appear in all copies. 10 * 11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 17 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 18 */ 19 20/*- 21 * Copyright (c) 2012 The NetBSD Foundation, Inc. 22 * All rights reserved. 23 * 24 * This code is derived from software contributed to The NetBSD Foundation 25 * by Nick Hudson 26 * 27 * Redistribution and use in source and binary forms, with or without 28 * modification, are permitted provided that the following conditions 29 * are met: 30 * 1. Redistributions of source code must retain the above copyright 31 * notice, this list of conditions and the following disclaimer. 32 * 2. Redistributions in binary form must reproduce the above copyright 33 * notice, this list of conditions and the following disclaimer in the 34 * documentation and/or other materials provided with the distribution. 35 * 36 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 37 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 38 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 39 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 40 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 41 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 42 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 43 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 44 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 45 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 46 * POSSIBILITY OF SUCH DAMAGE. 47 */ 48 49#include <sys/types.h> 50#include <sys/systm.h> 51#include <sys/mutex.h> 52 53#include <machine/bus.h> 54#include <machine/fdt.h> 55#include <machine/intr.h> 56 57#include <dev/ofw/fdt.h> 58#include <dev/ofw/openfirm.h> 59 60#include <dev/ic/bcm2835_mbox.h> 61#include <dev/ic/bcm2835_vcprop.h> 62 63#define DEVNAME(sc) ((sc)->sc_dev.dv_xname) 64 65struct cfdriver bcmmbox_cd = { NULL, "bcmmbox", DV_DULL }; 66 67struct bcmmbox_softc { 68 struct device sc_dev; 69 bus_space_tag_t sc_iot; 70 bus_space_handle_t sc_ioh; 71 72 bus_dma_tag_t sc_dmat; 73 bus_dmamap_t sc_dmamap; 74 75 void *sc_ih; 76 77 struct mutex sc_intr_lock; 78 int sc_chan[BCMMBOX_NUM_CHANNELS]; 79 uint32_t sc_mbox[BCMMBOX_NUM_CHANNELS]; 80}; 81 82static struct bcmmbox_softc *bcmmbox_sc; 83 84int bcmmbox_match(struct device *, void *, void *); 85void bcmmbox_attach(struct device *, struct device *, void *); 86 87const struct cfattach bcmmbox_ca = { 88 sizeof(struct bcmmbox_softc), 89 bcmmbox_match, 90 bcmmbox_attach, 91}; 92 93uint32_t bcmmbox_reg_read(struct bcmmbox_softc *, int); 94void bcmmbox_reg_write(struct bcmmbox_softc *, int, uint32_t); 95void bcmmbox_reg_flush(struct bcmmbox_softc *, int); 96int bcmmbox_intr(void *); 97int bcmmbox_intr_helper(struct bcmmbox_softc *, int); 98 99int 100bcmmbox_match(struct device *parent, void *match, void *aux) 101{ 102 struct fdt_attach_args *faa = aux; 103 104 return OF_is_compatible(faa->fa_node, "brcm,bcm2835-mbox"); 105} 106 107void 108bcmmbox_attach(struct device *parent, struct device *self, void *aux) 109{ 110 struct bcmmbox_softc *sc = (struct bcmmbox_softc *)self; 111 struct fdt_attach_args *faa = aux; 112 bus_addr_t addr; 113 bus_size_t size; 114 115 if (bcmmbox_sc) { 116 printf(": a similar device as already attached\n"); 117 return; 118 } 119 bcmmbox_sc = sc; 120 121 mtx_init(&sc->sc_intr_lock, IPL_VM); 122 123 if (faa->fa_nreg < 1) { 124 printf(": no registers\n"); 125 return; 126 } 127 128 addr = faa->fa_reg[0].addr; 129 size = faa->fa_reg[0].size; 130 131 sc->sc_dmat = faa->fa_dmat; 132 sc->sc_iot = faa->fa_iot; 133 if (bus_space_map(sc->sc_iot, addr, size, 0, &sc->sc_ioh)) { 134 printf(": can't map registers\n"); 135 return; 136 } 137 138 if (bus_dmamap_create(sc->sc_dmat, ~0UL, 1, ~0UL, 0, 139 BUS_DMA_NOWAIT|BUS_DMA_ALLOCNOW, &sc->sc_dmamap) != 0) { 140 printf(": unable to create dma map\n"); 141 goto clean_bus_space_map; 142 } 143 144 sc->sc_ih = fdt_intr_establish(faa->fa_node, IPL_VM, bcmmbox_intr, sc, 145 DEVNAME(sc)); 146 if (sc->sc_ih == NULL) { 147 printf(": failed to establish interrupt\n"); 148 goto clean_dmamap; 149 } 150 151 /* enable interrupt in hardware */ 152 bcmmbox_reg_write(sc, BCMMBOX_CFG, BCMMBOX_CFG_DATA_IRQ_EN); 153 154 printf("\n"); 155 156 return; 157 158 clean_dmamap: 159 bus_dmamap_destroy(sc->sc_dmat, sc->sc_dmamap); 160 161 clean_bus_space_map: 162 bus_space_unmap(sc->sc_iot, sc->sc_ioh, size); 163} 164 165uint32_t 166bcmmbox_reg_read(struct bcmmbox_softc *sc, int addr) 167{ 168 return bus_space_read_4(sc->sc_iot, sc->sc_ioh, addr); 169} 170 171void 172bcmmbox_reg_write(struct bcmmbox_softc *sc, int addr, uint32_t val) 173{ 174 bus_space_write_4(sc->sc_iot, sc->sc_ioh, addr, val); 175} 176 177void 178bcmmbox_reg_flush(struct bcmmbox_softc *sc, int flags) 179{ 180 bus_space_barrier(sc->sc_iot, sc->sc_ioh, 0, BCMMBOX_SIZE, flags); 181} 182 183int 184bcmmbox_intr(void *cookie) 185{ 186 struct bcmmbox_softc *sc = cookie; 187 int ret; 188 189 mtx_enter(&sc->sc_intr_lock); 190 ret = bcmmbox_intr_helper(sc, 1); 191 mtx_leave(&sc->sc_intr_lock); 192 193 return ret; 194} 195 196int 197bcmmbox_intr_helper(struct bcmmbox_softc *sc, int broadcast) 198{ 199 uint32_t mbox, chan, data; 200 int ret = 0; 201 202 bcmmbox_reg_flush(sc, BUS_SPACE_BARRIER_READ); 203 204 while (!ISSET(bcmmbox_reg_read(sc, BCMMBOX_STATUS), BCMMBOX_STATUS_EMPTY)) { 205 mbox = bcmmbox_reg_read(sc, BCMMBOX0_READ); 206 207 chan = mbox & BCMMBOX_CHANNEL_MASK; 208 data = mbox & ~BCMMBOX_CHANNEL_MASK; 209 ret = 1; 210 211 if ((sc->sc_mbox[chan] & BCMMBOX_CHANNEL_MASK) != 0) { 212 printf("%s: chan %d overflow\n", DEVNAME(sc), chan); 213 continue; 214 } 215 216 sc->sc_mbox[chan] = data | BCMMBOX_CHANNEL_MASK; 217 218 if (broadcast) 219 wakeup(&sc->sc_chan[chan]); 220 } 221 222 return ret; 223} 224 225void 226bcmmbox_read(uint8_t chan, uint32_t *data) 227{ 228 struct bcmmbox_softc *sc = bcmmbox_sc; 229 uint32_t mbox, rchan, rdata, status; 230 231 KASSERT(sc != NULL); 232 KASSERT(chan == (chan & BCMMBOX_CHANNEL_MASK)); 233 234 while (1) { 235 bcmmbox_reg_flush(sc, BUS_SPACE_BARRIER_READ); 236 status = bcmmbox_reg_read(sc, BCMMBOX0_STATUS); 237 if (ISSET(status, BCMMBOX_STATUS_EMPTY)) 238 continue; 239 240 mbox = bcmmbox_reg_read(sc, BCMMBOX0_READ); 241 242 rchan = mbox & BCMMBOX_CHANNEL_MASK; 243 rdata = mbox & ~BCMMBOX_CHANNEL_MASK; 244 245 if (rchan == chan) { 246 *data = rdata; 247 return; 248 } 249 } 250} 251 252void 253bcmmbox_write(uint8_t chan, uint32_t data) 254{ 255 struct bcmmbox_softc *sc = bcmmbox_sc; 256 uint32_t rdata; 257 258 KASSERT(sc != NULL); 259 KASSERT(chan == (chan & BCMMBOX_CHANNEL_MASK)); 260 KASSERT(data == (data & ~BCMMBOX_CHANNEL_MASK)); 261 262 while (1) { 263 bcmmbox_reg_flush(sc, BUS_SPACE_BARRIER_READ); 264 rdata = bcmmbox_reg_read(sc, BCMMBOX0_STATUS); 265 if (!ISSET(rdata, BCMMBOX_STATUS_FULL)) 266 break; 267 } 268 269 bcmmbox_reg_write(sc, BCMMBOX1_WRITE, chan | data); 270 bcmmbox_reg_flush(sc, BUS_SPACE_BARRIER_WRITE); 271} 272 273int 274bcmmbox_post(uint8_t chan, void *buf, size_t len, uint32_t *res) 275{ 276 struct bcmmbox_softc *sc = bcmmbox_sc; 277 bus_dmamap_t map; 278 int error; 279 280 KASSERT(sc != NULL); 281 if (sc == NULL) 282 return (ENXIO); 283 284 map = sc->sc_dmamap; 285 286 error = bus_dmamap_load(sc->sc_dmat, map, buf, len, NULL, 287 BUS_DMA_NOWAIT | BUS_DMA_READ | BUS_DMA_WRITE); 288 if (error != 0) 289 return (error); 290 291 bus_dmamap_sync(sc->sc_dmat, map, 0, len, 292 BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); 293 294 bcmmbox_write(chan, map->dm_segs[0].ds_addr); 295 bcmmbox_read(chan, res); 296 297 bus_dmamap_sync(sc->sc_dmat, map, 0, len, 298 BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE); 299 300 bus_dmamap_unload(sc->sc_dmat, map); 301 302 return (0); 303} 304