1 /* $OpenBSD: bcm2835_mbox.c,v 1.2 2021/05/30 15:05:33 visa 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_lock; 78 struct mutex sc_intr_lock; 79 int sc_chan[BCMMBOX_NUM_CHANNELS]; 80 uint32_t sc_mbox[BCMMBOX_NUM_CHANNELS]; 81 }; 82 83 static struct bcmmbox_softc *bcmmbox_sc; 84 85 int bcmmbox_match(struct device *, void *, void *); 86 void bcmmbox_attach(struct device *, struct device *, void *); 87 88 struct cfattach bcmmbox_ca = { 89 sizeof(struct bcmmbox_softc), 90 bcmmbox_match, 91 bcmmbox_attach, 92 }; 93 94 uint32_t bcmmbox_reg_read(struct bcmmbox_softc *, int); 95 void bcmmbox_reg_write(struct bcmmbox_softc *, int, uint32_t); 96 void bcmmbox_reg_flush(struct bcmmbox_softc *, int); 97 int bcmmbox_intr(void *); 98 int bcmmbox_intr_helper(struct bcmmbox_softc *, int); 99 100 int 101 bcmmbox_match(struct device *parent, void *match, void *aux) 102 { 103 struct fdt_attach_args *faa = aux; 104 105 return OF_is_compatible(faa->fa_node, "brcm,bcm2835-mbox"); 106 } 107 108 void 109 bcmmbox_attach(struct device *parent, struct device *self, void *aux) 110 { 111 struct bcmmbox_softc *sc = (struct bcmmbox_softc *)self; 112 struct fdt_attach_args *faa = aux; 113 bus_addr_t addr; 114 bus_size_t size; 115 116 if (bcmmbox_sc) { 117 printf(": a similar device as already attached\n"); 118 return; 119 } 120 bcmmbox_sc = sc; 121 122 mtx_init(&sc->sc_lock, IPL_NONE); 123 mtx_init(&sc->sc_intr_lock, IPL_VM); 124 125 if (faa->fa_nreg < 1) { 126 printf(": no registers\n"); 127 return; 128 } 129 130 addr = faa->fa_reg[0].addr; 131 size = faa->fa_reg[0].size; 132 133 sc->sc_dmat = faa->fa_dmat; 134 sc->sc_iot = faa->fa_iot; 135 if (bus_space_map(sc->sc_iot, addr, size, 0, &sc->sc_ioh)) { 136 printf(": can't map registers\n"); 137 return; 138 } 139 140 if (bus_dmamap_create(sc->sc_dmat, ~0UL, 1, ~0UL, 0, 141 BUS_DMA_NOWAIT|BUS_DMA_ALLOCNOW, &sc->sc_dmamap) != 0) { 142 printf(": unable to create dma map\n"); 143 goto clean_bus_space_map; 144 } 145 146 sc->sc_ih = fdt_intr_establish(faa->fa_node, IPL_VM, bcmmbox_intr, sc, 147 DEVNAME(sc)); 148 if (sc->sc_ih == NULL) { 149 printf(": failed to establish interrupt\n"); 150 goto clean_dmamap; 151 } 152 153 /* enable interrupt in hardware */ 154 bcmmbox_reg_write(sc, BCMMBOX_CFG, BCMMBOX_CFG_DATA_IRQ_EN); 155 156 printf("\n"); 157 158 bcmmbox_write(BCMMBOX_CHANPM, ( 159 (1 << VCPROP_POWER_SDCARD) | 160 (1 << VCPROP_POWER_UART0) | 161 (1 << VCPROP_POWER_USB) | 162 (1 << VCPROP_POWER_I2C0) | 163 (1 << VCPROP_POWER_I2C1) | 164 (1 << VCPROP_POWER_SPI) | 165 0) << 4); 166 167 return; 168 169 clean_dmamap: 170 bus_dmamap_destroy(sc->sc_dmat, sc->sc_dmamap); 171 172 clean_bus_space_map: 173 bus_space_unmap(sc->sc_iot, sc->sc_ioh, size); 174 } 175 176 uint32_t 177 bcmmbox_reg_read(struct bcmmbox_softc *sc, int addr) 178 { 179 return bus_space_read_4(sc->sc_iot, sc->sc_ioh, addr); 180 } 181 182 void 183 bcmmbox_reg_write(struct bcmmbox_softc *sc, int addr, uint32_t val) 184 { 185 bus_space_write_4(sc->sc_iot, sc->sc_ioh, addr, val); 186 } 187 188 void 189 bcmmbox_reg_flush(struct bcmmbox_softc *sc, int flags) 190 { 191 bus_space_barrier(sc->sc_iot, sc->sc_ioh, 0, BCMMBOX_SIZE, flags); 192 } 193 194 int 195 bcmmbox_intr(void *cookie) 196 { 197 struct bcmmbox_softc *sc = cookie; 198 int ret; 199 200 mtx_enter(&sc->sc_intr_lock); 201 ret = bcmmbox_intr_helper(sc, 1); 202 mtx_leave(&sc->sc_intr_lock); 203 204 return ret; 205 } 206 207 int 208 bcmmbox_intr_helper(struct bcmmbox_softc *sc, int broadcast) 209 { 210 uint32_t mbox, chan, data; 211 int ret = 0; 212 213 bcmmbox_reg_flush(sc, BUS_SPACE_BARRIER_READ); 214 215 while (!ISSET(bcmmbox_reg_read(sc, BCMMBOX_STATUS), BCMMBOX_STATUS_EMPTY)) { 216 mbox = bcmmbox_reg_read(sc, BCMMBOX0_READ); 217 218 chan = mbox & BCMMBOX_CHANNEL_MASK; 219 data = mbox & ~BCMMBOX_CHANNEL_MASK; 220 ret = 1; 221 222 if ((sc->sc_mbox[chan] & BCMMBOX_CHANNEL_MASK) != 0) { 223 printf("%s: chan %d overflow\n", DEVNAME(sc), chan); 224 continue; 225 } 226 227 sc->sc_mbox[chan] = data | BCMMBOX_CHANNEL_MASK; 228 229 if (broadcast) 230 wakeup(&sc->sc_chan[chan]); 231 } 232 233 return ret; 234 } 235 236 void 237 bcmmbox_read(uint8_t chan, uint32_t *data) 238 { 239 struct bcmmbox_softc *sc = bcmmbox_sc; 240 uint32_t mbox, rchan, rdata, status; 241 242 KASSERT(sc != NULL); 243 KASSERT(chan == (chan & BCMMBOX_CHANNEL_MASK)); 244 245 while (1) { 246 bcmmbox_reg_flush(sc, BUS_SPACE_BARRIER_READ); 247 status = bcmmbox_reg_read(sc, BCMMBOX0_STATUS); 248 if (ISSET(status, BCMMBOX_STATUS_EMPTY)) 249 continue; 250 251 mbox = bcmmbox_reg_read(sc, BCMMBOX0_READ); 252 253 rchan = mbox & BCMMBOX_CHANNEL_MASK; 254 rdata = mbox & ~BCMMBOX_CHANNEL_MASK; 255 256 if (rchan == chan) { 257 *data = rdata; 258 return; 259 } 260 } 261 } 262 263 void 264 bcmmbox_write(uint8_t chan, uint32_t data) 265 { 266 struct bcmmbox_softc *sc = bcmmbox_sc; 267 uint32_t rdata; 268 269 KASSERT(sc != NULL); 270 KASSERT(chan == (chan & BCMMBOX_CHANNEL_MASK)); 271 KASSERT(data == (data & ~BCMMBOX_CHANNEL_MASK)); 272 273 while (1) { 274 bcmmbox_reg_flush(sc, BUS_SPACE_BARRIER_READ); 275 rdata = bcmmbox_reg_read(sc, BCMMBOX0_STATUS); 276 if (!ISSET(rdata, BCMMBOX_STATUS_FULL)) 277 break; 278 } 279 280 bcmmbox_reg_write(sc, BCMMBOX1_WRITE, chan | data); 281 bcmmbox_reg_flush(sc, BUS_SPACE_BARRIER_WRITE); 282 } 283 284 int 285 bcmmbox_post(uint8_t chan, void *buf, size_t len, uint32_t *res) 286 { 287 struct bcmmbox_softc *sc = bcmmbox_sc; 288 bus_dmamap_t map; 289 int error; 290 291 KASSERT(sc != NULL); 292 if (sc == NULL) 293 return (ENXIO); 294 295 map = sc->sc_dmamap; 296 297 error = bus_dmamap_load(sc->sc_dmat, map, buf, len, NULL, 298 BUS_DMA_NOWAIT | BUS_DMA_READ | BUS_DMA_WRITE); 299 if (error != 0) 300 return (error); 301 302 bus_dmamap_sync(sc->sc_dmat, map, 0, len, 303 BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); 304 305 bcmmbox_write(chan, map->dm_segs[0].ds_addr); 306 bcmmbox_read(chan, res); 307 308 bus_dmamap_sync(sc->sc_dmat, map, 0, len, 309 BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE); 310 311 bus_dmamap_unload(sc->sc_dmat, map); 312 313 return (0); 314 } 315