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 65 struct cfdriver bcmmbox_cd = { NULL, "bcmmbox", DV_DULL }; 66 67 struct 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 82 static struct bcmmbox_softc *bcmmbox_sc; 83 84 int bcmmbox_match(struct device *, void *, void *); 85 void bcmmbox_attach(struct device *, struct device *, void *); 86 87 const struct cfattach bcmmbox_ca = { 88 sizeof(struct bcmmbox_softc), 89 bcmmbox_match, 90 bcmmbox_attach, 91 }; 92 93 uint32_t bcmmbox_reg_read(struct bcmmbox_softc *, int); 94 void bcmmbox_reg_write(struct bcmmbox_softc *, int, uint32_t); 95 void bcmmbox_reg_flush(struct bcmmbox_softc *, int); 96 int bcmmbox_intr(void *); 97 int bcmmbox_intr_helper(struct bcmmbox_softc *, int); 98 99 int 100 bcmmbox_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 107 void 108 bcmmbox_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 165 uint32_t 166 bcmmbox_reg_read(struct bcmmbox_softc *sc, int addr) 167 { 168 return bus_space_read_4(sc->sc_iot, sc->sc_ioh, addr); 169 } 170 171 void 172 bcmmbox_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 177 void 178 bcmmbox_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 183 int 184 bcmmbox_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 196 int 197 bcmmbox_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 225 void 226 bcmmbox_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 252 void 253 bcmmbox_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 273 int 274 bcmmbox_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