1 /* $OpenBSD: bcm2835_mbox.c,v 1.1 2020/04/19 14:51:52 tobhe 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 52 #include <machine/bus.h> 53 #include <machine/fdt.h> 54 #include <machine/intr.h> 55 56 #include <dev/ofw/fdt.h> 57 #include <dev/ofw/openfirm.h> 58 59 #include <dev/ic/bcm2835_mbox.h> 60 #include <dev/ic/bcm2835_vcprop.h> 61 62 #define DEVNAME(sc) ((sc)->sc_dev.dv_xname) 63 64 struct cfdriver bcmmbox_cd = { NULL, "bcmmbox", DV_DULL }; 65 66 struct bcmmbox_softc { 67 struct device sc_dev; 68 bus_space_tag_t sc_iot; 69 bus_space_handle_t sc_ioh; 70 71 bus_dma_tag_t sc_dmat; 72 bus_dmamap_t sc_dmamap; 73 74 void *sc_ih; 75 76 struct mutex sc_lock; 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 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_lock, IPL_NONE); 122 mtx_init(&sc->sc_intr_lock, IPL_VM); 123 124 if (faa->fa_nreg < 1) { 125 printf(": no registers\n"); 126 return; 127 } 128 129 addr = faa->fa_reg[0].addr; 130 size = faa->fa_reg[0].size; 131 132 sc->sc_dmat = faa->fa_dmat; 133 sc->sc_iot = faa->fa_iot; 134 if (bus_space_map(sc->sc_iot, addr, size, 0, &sc->sc_ioh)) { 135 printf(": can't map registers\n"); 136 return; 137 } 138 139 if (bus_dmamap_create(sc->sc_dmat, ~0UL, 1, ~0UL, 0, 140 BUS_DMA_NOWAIT|BUS_DMA_ALLOCNOW, &sc->sc_dmamap) != 0) { 141 printf(": unable to create dma map\n"); 142 goto clean_bus_space_map; 143 } 144 145 sc->sc_ih = fdt_intr_establish(faa->fa_node, IPL_VM, bcmmbox_intr, sc, 146 DEVNAME(sc)); 147 if (sc->sc_ih == NULL) { 148 printf(": failed to establish interrupt\n"); 149 goto clean_dmamap; 150 } 151 152 /* enable interrupt in hardware */ 153 bcmmbox_reg_write(sc, BCMMBOX_CFG, BCMMBOX_CFG_DATA_IRQ_EN); 154 155 printf("\n"); 156 157 bcmmbox_write(BCMMBOX_CHANPM, ( 158 (1 << VCPROP_POWER_SDCARD) | 159 (1 << VCPROP_POWER_UART0) | 160 (1 << VCPROP_POWER_USB) | 161 (1 << VCPROP_POWER_I2C0) | 162 (1 << VCPROP_POWER_I2C1) | 163 (1 << VCPROP_POWER_SPI) | 164 0) << 4); 165 166 return; 167 168 clean_dmamap: 169 bus_dmamap_destroy(sc->sc_dmat, sc->sc_dmamap); 170 171 clean_bus_space_map: 172 bus_space_unmap(sc->sc_iot, sc->sc_ioh, size); 173 } 174 175 uint32_t 176 bcmmbox_reg_read(struct bcmmbox_softc *sc, int addr) 177 { 178 return bus_space_read_4(sc->sc_iot, sc->sc_ioh, addr); 179 } 180 181 void 182 bcmmbox_reg_write(struct bcmmbox_softc *sc, int addr, uint32_t val) 183 { 184 bus_space_write_4(sc->sc_iot, sc->sc_ioh, addr, val); 185 } 186 187 void 188 bcmmbox_reg_flush(struct bcmmbox_softc *sc, int flags) 189 { 190 bus_space_barrier(sc->sc_iot, sc->sc_ioh, 0, BCMMBOX_SIZE, flags); 191 } 192 193 int 194 bcmmbox_intr(void *cookie) 195 { 196 struct bcmmbox_softc *sc = cookie; 197 int ret; 198 199 mtx_enter(&sc->sc_intr_lock); 200 ret = bcmmbox_intr_helper(sc, 1); 201 mtx_leave(&sc->sc_intr_lock); 202 203 return ret; 204 } 205 206 int 207 bcmmbox_intr_helper(struct bcmmbox_softc *sc, int broadcast) 208 { 209 uint32_t mbox, chan, data; 210 int ret = 0; 211 212 bcmmbox_reg_flush(sc, BUS_SPACE_BARRIER_READ); 213 214 while (!ISSET(bcmmbox_reg_read(sc, BCMMBOX_STATUS), BCMMBOX_STATUS_EMPTY)) { 215 mbox = bcmmbox_reg_read(sc, BCMMBOX0_READ); 216 217 chan = mbox & BCMMBOX_CHANNEL_MASK; 218 data = mbox & ~BCMMBOX_CHANNEL_MASK; 219 ret = 1; 220 221 if ((sc->sc_mbox[chan] & BCMMBOX_CHANNEL_MASK) != 0) { 222 printf("%s: chan %d overflow\n", DEVNAME(sc), chan); 223 continue; 224 } 225 226 sc->sc_mbox[chan] = data | BCMMBOX_CHANNEL_MASK; 227 228 if (broadcast) 229 wakeup(&sc->sc_chan[chan]); 230 } 231 232 return ret; 233 } 234 235 void 236 bcmmbox_read(uint8_t chan, uint32_t *data) 237 { 238 struct bcmmbox_softc *sc = bcmmbox_sc; 239 uint32_t mbox, rchan, rdata, status; 240 241 KASSERT(sc != NULL); 242 KASSERT(chan == (chan & BCMMBOX_CHANNEL_MASK)); 243 244 while (1) { 245 bcmmbox_reg_flush(sc, BUS_SPACE_BARRIER_READ); 246 status = bcmmbox_reg_read(sc, BCMMBOX0_STATUS); 247 if (ISSET(status, BCMMBOX_STATUS_EMPTY)) 248 continue; 249 250 mbox = bcmmbox_reg_read(sc, BCMMBOX0_READ); 251 252 rchan = mbox & BCMMBOX_CHANNEL_MASK; 253 rdata = mbox & ~BCMMBOX_CHANNEL_MASK; 254 255 if (rchan == chan) { 256 *data = rdata; 257 return; 258 } 259 } 260 } 261 262 void 263 bcmmbox_write(uint8_t chan, uint32_t data) 264 { 265 struct bcmmbox_softc *sc = bcmmbox_sc; 266 uint32_t rdata; 267 268 KASSERT(sc != NULL); 269 KASSERT(chan == (chan & BCMMBOX_CHANNEL_MASK)); 270 KASSERT(data == (data & ~BCMMBOX_CHANNEL_MASK)); 271 272 while (1) { 273 bcmmbox_reg_flush(sc, BUS_SPACE_BARRIER_READ); 274 rdata = bcmmbox_reg_read(sc, BCMMBOX0_STATUS); 275 if (!ISSET(rdata, BCMMBOX_STATUS_FULL)) 276 break; 277 } 278 279 bcmmbox_reg_write(sc, BCMMBOX1_WRITE, chan | data); 280 bcmmbox_reg_flush(sc, BUS_SPACE_BARRIER_WRITE); 281 } 282 283 int 284 bcmmbox_post(uint8_t chan, void *buf, size_t len, uint32_t *res) 285 { 286 struct bcmmbox_softc *sc = bcmmbox_sc; 287 bus_dmamap_t map; 288 int error; 289 290 KASSERT(sc != NULL); 291 if (sc == NULL) 292 return (ENXIO); 293 294 map = sc->sc_dmamap; 295 296 error = bus_dmamap_load(sc->sc_dmat, map, buf, len, NULL, 297 BUS_DMA_NOWAIT | BUS_DMA_READ | BUS_DMA_WRITE); 298 if (error != 0) 299 return (error); 300 301 bus_dmamap_sync(sc->sc_dmat, map, 0, len, 302 BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); 303 304 bcmmbox_write(chan, map->dm_segs[0].ds_addr); 305 bcmmbox_read(chan, res); 306 307 bus_dmamap_sync(sc->sc_dmat, map, 0, len, 308 BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE); 309 310 bus_dmamap_unload(sc->sc_dmat, map); 311 312 return (0); 313 } 314