1 /* $OpenBSD: bcm2835_mbox.c,v 1.4 2022/08/27 20:31:45 mglocker 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 bcmmbox_write(BCMMBOX_CHANPM, ( 157 (1 << VCPROP_POWER_SDCARD) | 158 (1 << VCPROP_POWER_UART0) | 159 (1 << VCPROP_POWER_USB) | 160 (1 << VCPROP_POWER_I2C0) | 161 (1 << VCPROP_POWER_I2C1) | 162 (1 << VCPROP_POWER_SPI) | 163 0) << 4); 164 165 return; 166 167 clean_dmamap: 168 bus_dmamap_destroy(sc->sc_dmat, sc->sc_dmamap); 169 170 clean_bus_space_map: 171 bus_space_unmap(sc->sc_iot, sc->sc_ioh, size); 172 } 173 174 uint32_t 175 bcmmbox_reg_read(struct bcmmbox_softc *sc, int addr) 176 { 177 return bus_space_read_4(sc->sc_iot, sc->sc_ioh, addr); 178 } 179 180 void 181 bcmmbox_reg_write(struct bcmmbox_softc *sc, int addr, uint32_t val) 182 { 183 bus_space_write_4(sc->sc_iot, sc->sc_ioh, addr, val); 184 } 185 186 void 187 bcmmbox_reg_flush(struct bcmmbox_softc *sc, int flags) 188 { 189 bus_space_barrier(sc->sc_iot, sc->sc_ioh, 0, BCMMBOX_SIZE, flags); 190 } 191 192 int 193 bcmmbox_intr(void *cookie) 194 { 195 struct bcmmbox_softc *sc = cookie; 196 int ret; 197 198 mtx_enter(&sc->sc_intr_lock); 199 ret = bcmmbox_intr_helper(sc, 1); 200 mtx_leave(&sc->sc_intr_lock); 201 202 return ret; 203 } 204 205 int 206 bcmmbox_intr_helper(struct bcmmbox_softc *sc, int broadcast) 207 { 208 uint32_t mbox, chan, data; 209 int ret = 0; 210 211 bcmmbox_reg_flush(sc, BUS_SPACE_BARRIER_READ); 212 213 while (!ISSET(bcmmbox_reg_read(sc, BCMMBOX_STATUS), BCMMBOX_STATUS_EMPTY)) { 214 mbox = bcmmbox_reg_read(sc, BCMMBOX0_READ); 215 216 chan = mbox & BCMMBOX_CHANNEL_MASK; 217 data = mbox & ~BCMMBOX_CHANNEL_MASK; 218 ret = 1; 219 220 if ((sc->sc_mbox[chan] & BCMMBOX_CHANNEL_MASK) != 0) { 221 printf("%s: chan %d overflow\n", DEVNAME(sc), chan); 222 continue; 223 } 224 225 sc->sc_mbox[chan] = data | BCMMBOX_CHANNEL_MASK; 226 227 if (broadcast) 228 wakeup(&sc->sc_chan[chan]); 229 } 230 231 return ret; 232 } 233 234 void 235 bcmmbox_read(uint8_t chan, uint32_t *data) 236 { 237 struct bcmmbox_softc *sc = bcmmbox_sc; 238 uint32_t mbox, rchan, rdata, status; 239 240 KASSERT(sc != NULL); 241 KASSERT(chan == (chan & BCMMBOX_CHANNEL_MASK)); 242 243 while (1) { 244 bcmmbox_reg_flush(sc, BUS_SPACE_BARRIER_READ); 245 status = bcmmbox_reg_read(sc, BCMMBOX0_STATUS); 246 if (ISSET(status, BCMMBOX_STATUS_EMPTY)) 247 continue; 248 249 mbox = bcmmbox_reg_read(sc, BCMMBOX0_READ); 250 251 rchan = mbox & BCMMBOX_CHANNEL_MASK; 252 rdata = mbox & ~BCMMBOX_CHANNEL_MASK; 253 254 if (rchan == chan) { 255 *data = rdata; 256 return; 257 } 258 } 259 } 260 261 void 262 bcmmbox_write(uint8_t chan, uint32_t data) 263 { 264 struct bcmmbox_softc *sc = bcmmbox_sc; 265 uint32_t rdata; 266 267 KASSERT(sc != NULL); 268 KASSERT(chan == (chan & BCMMBOX_CHANNEL_MASK)); 269 KASSERT(data == (data & ~BCMMBOX_CHANNEL_MASK)); 270 271 while (1) { 272 bcmmbox_reg_flush(sc, BUS_SPACE_BARRIER_READ); 273 rdata = bcmmbox_reg_read(sc, BCMMBOX0_STATUS); 274 if (!ISSET(rdata, BCMMBOX_STATUS_FULL)) 275 break; 276 } 277 278 bcmmbox_reg_write(sc, BCMMBOX1_WRITE, chan | data); 279 bcmmbox_reg_flush(sc, BUS_SPACE_BARRIER_WRITE); 280 } 281 282 int 283 bcmmbox_post(uint8_t chan, void *buf, size_t len, uint32_t *res) 284 { 285 struct bcmmbox_softc *sc = bcmmbox_sc; 286 bus_dmamap_t map; 287 int error; 288 289 KASSERT(sc != NULL); 290 if (sc == NULL) 291 return (ENXIO); 292 293 map = sc->sc_dmamap; 294 295 error = bus_dmamap_load(sc->sc_dmat, map, buf, len, NULL, 296 BUS_DMA_NOWAIT | BUS_DMA_READ | BUS_DMA_WRITE); 297 if (error != 0) 298 return (error); 299 300 bus_dmamap_sync(sc->sc_dmat, map, 0, len, 301 BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); 302 303 bcmmbox_write(chan, map->dm_segs[0].ds_addr); 304 bcmmbox_read(chan, res); 305 306 bus_dmamap_sync(sc->sc_dmat, map, 0, len, 307 BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE); 308 309 bus_dmamap_unload(sc->sc_dmat, map); 310 311 return (0); 312 } 313