1 /* $NetBSD: bcm2835_dmac.c,v 1.19 2021/01/29 14:11:14 skrll Exp $ */ 2 3 /*- 4 * Copyright (c) 2014 Jared D. McNeill <jmcneill (at) invisible.ca> 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 18 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 19 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 21 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 23 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 24 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * SUCH DAMAGE. 27 */ 28 29 #include "opt_ddb.h" 30 31 #include <sys/cdefs.h> 32 __KERNEL_RCSID(0, "$NetBSD: bcm2835_dmac.c,v 1.19 2021/01/29 14:11:14 skrll Exp $"); 33 34 #include <sys/param.h> 35 #include <sys/bus.h> 36 #include <sys/device.h> 37 #include <sys/intr.h> 38 #include <sys/systm.h> 39 #include <sys/kmem.h> 40 #include <sys/mutex.h> 41 42 #include <arm/broadcom/bcm2835reg.h> 43 #include <arm/broadcom/bcm2835_intr.h> 44 45 #include <arm/broadcom/bcm2835_dmac.h> 46 47 #include <dev/fdt/fdtvar.h> 48 49 #include <arm/fdt/arm_fdtvar.h> 50 51 #define BCM_DMAC_CHANNELMASK 0x00000fff 52 53 struct bcm_dmac_softc; 54 55 struct bcm_dmac_channel { 56 struct bcm_dmac_softc *ch_sc; 57 void *ch_ih; 58 uint8_t ch_index; 59 void (*ch_callback)(uint32_t, uint32_t, void *); 60 void *ch_callbackarg; 61 uint32_t ch_debug; 62 }; 63 64 #define DMAC_CHANNEL_TYPE(ch) \ 65 (((ch)->ch_debug & DMAC_DEBUG_LITE) ? \ 66 BCM_DMAC_TYPE_LITE : BCM_DMAC_TYPE_NORMAL) 67 #define DMAC_CHANNEL_USED(ch) \ 68 ((ch)->ch_callback != NULL) 69 70 struct bcm_dmac_softc { 71 device_t sc_dev; 72 bus_space_tag_t sc_iot; 73 bus_space_handle_t sc_ioh; 74 int sc_phandle; 75 76 kmutex_t sc_lock; 77 struct bcm_dmac_channel *sc_channels; 78 int sc_nchannels; 79 uint32_t sc_channelmask; 80 }; 81 82 #define DMAC_READ(sc, reg) \ 83 bus_space_read_4((sc)->sc_iot, (sc)->sc_ioh, (reg)) 84 #define DMAC_WRITE(sc, reg, val) \ 85 bus_space_write_4((sc)->sc_iot, (sc)->sc_ioh, (reg), (val)) 86 87 static int bcm_dmac_match(device_t, cfdata_t, void *); 88 static void bcm_dmac_attach(device_t, device_t, void *); 89 90 static int bcm_dmac_intr(void *); 91 92 #if defined(DDB) 93 void bcm_dmac_dump_regs(void); 94 #endif 95 96 CFATTACH_DECL_NEW(bcmdmac_fdt, sizeof(struct bcm_dmac_softc), 97 bcm_dmac_match, bcm_dmac_attach, NULL, NULL); 98 99 static const struct device_compatible_entry compat_data[] = { 100 { .compat = "brcm,bcm2835-dma" }, 101 DEVICE_COMPAT_EOL 102 }; 103 104 static int 105 bcm_dmac_match(device_t parent, cfdata_t cf, void *aux) 106 { 107 struct fdt_attach_args * const faa = aux; 108 109 return of_compatible_match(faa->faa_phandle, compat_data); 110 } 111 112 static void 113 bcm_dmac_attach(device_t parent, device_t self, void *aux) 114 { 115 struct bcm_dmac_softc *sc = device_private(self); 116 const prop_dictionary_t cfg = device_properties(self); 117 struct fdt_attach_args * const faa = aux; 118 struct bcm_dmac_channel *ch; 119 uint32_t val; 120 int index; 121 122 const int phandle = faa->faa_phandle; 123 124 sc->sc_dev = self; 125 sc->sc_iot = faa->faa_bst; 126 sc->sc_phandle = phandle; 127 128 bus_addr_t addr; 129 bus_size_t size; 130 131 if (fdtbus_get_reg(phandle, 0, &addr, &size) != 0) { 132 aprint_error(": missing 'reg' property\n"); 133 return; 134 } 135 136 if (bus_space_map(sc->sc_iot, addr, size, 0, &sc->sc_ioh)) { 137 aprint_error(": unable to map device\n"); 138 return; 139 } 140 141 prop_dictionary_get_uint32(cfg, "chanmask", &sc->sc_channelmask); 142 sc->sc_channelmask &= BCM_DMAC_CHANNELMASK; 143 144 mutex_init(&sc->sc_lock, MUTEX_DEFAULT, IPL_SCHED); 145 146 sc->sc_nchannels = 32 - __builtin_clz(sc->sc_channelmask); 147 sc->sc_channels = kmem_alloc( 148 sizeof(*sc->sc_channels) * sc->sc_nchannels, KM_SLEEP); 149 150 aprint_normal(":"); 151 for (index = 0; index < sc->sc_nchannels; index++) { 152 ch = &sc->sc_channels[index]; 153 ch->ch_sc = sc; 154 ch->ch_index = index; 155 ch->ch_callback = NULL; 156 ch->ch_callbackarg = NULL; 157 ch->ch_ih = NULL; 158 if ((__BIT(index) & sc->sc_channelmask) == 0) 159 continue; 160 161 aprint_normal(" DMA%d", index); 162 163 ch->ch_debug = DMAC_READ(sc, DMAC_DEBUG(index)); 164 165 val = DMAC_READ(sc, DMAC_CS(index)); 166 val |= DMAC_CS_RESET; 167 DMAC_WRITE(sc, DMAC_CS(index), val); 168 } 169 aprint_normal("\n"); 170 aprint_naive("\n"); 171 } 172 173 static int 174 bcm_dmac_intr(void *priv) 175 { 176 struct bcm_dmac_channel *ch = priv; 177 struct bcm_dmac_softc *sc = ch->ch_sc; 178 uint32_t cs, ce; 179 180 cs = DMAC_READ(sc, DMAC_CS(ch->ch_index)); 181 DMAC_WRITE(sc, DMAC_CS(ch->ch_index), cs); 182 cs &= DMAC_CS_INT | DMAC_CS_END | DMAC_CS_ERROR; 183 184 ce = DMAC_READ(sc, DMAC_DEBUG(ch->ch_index)); 185 ce &= DMAC_DEBUG_READ_ERROR | DMAC_DEBUG_FIFO_ERROR 186 | DMAC_DEBUG_READ_LAST_NOT_SET_ERROR; 187 DMAC_WRITE(sc, DMAC_DEBUG(ch->ch_index), ce); 188 189 if (ch->ch_callback) 190 ch->ch_callback(cs, ce, ch->ch_callbackarg); 191 192 return 1; 193 } 194 195 struct bcm_dmac_channel * 196 bcm_dmac_alloc(enum bcm_dmac_type type, int ipl, 197 void (*cb)(uint32_t, uint32_t, void *), void *cbarg) 198 { 199 struct bcm_dmac_softc *sc; 200 struct bcm_dmac_channel *ch = NULL; 201 device_t dev; 202 int index; 203 204 dev = device_find_by_driver_unit("bcmdmac", 0); 205 if (dev == NULL) 206 return NULL; 207 sc = device_private(dev); 208 209 mutex_enter(&sc->sc_lock); 210 for (index = 0; index < sc->sc_nchannels; index++) { 211 if ((sc->sc_channelmask & __BIT(index)) == 0) 212 continue; 213 if (DMAC_CHANNEL_TYPE(&sc->sc_channels[index]) != type) 214 continue; 215 if (DMAC_CHANNEL_USED(&sc->sc_channels[index])) 216 continue; 217 218 ch = &sc->sc_channels[index]; 219 ch->ch_callback = cb; 220 ch->ch_callbackarg = cbarg; 221 break; 222 } 223 mutex_exit(&sc->sc_lock); 224 225 if (ch == NULL) 226 return NULL; 227 228 KASSERT(ch->ch_ih == NULL); 229 230 const int phandle = sc->sc_phandle; 231 char intrstr[128]; 232 233 if (!fdtbus_intr_str(phandle, ch->ch_index, intrstr, sizeof(intrstr))) { 234 aprint_error(": failed to decode interrupt\n"); 235 return NULL; 236 } 237 238 char xname[16]; 239 snprintf(xname, sizeof(xname), "%s #%u", device_xname(sc->sc_dev), 240 ch->ch_index); 241 ch->ch_ih = fdtbus_intr_establish_xname(phandle, ch->ch_index, ipl, 0, 242 bcm_dmac_intr, ch, xname); 243 if (ch->ch_ih == NULL) { 244 aprint_error_dev(sc->sc_dev, 245 "failed to establish interrupt for DMA%d and %s\n", ch->ch_index, 246 intrstr); 247 ch->ch_callback = NULL; 248 ch->ch_callbackarg = NULL; 249 ch = NULL; 250 } 251 252 return ch; 253 } 254 255 void 256 bcm_dmac_free(struct bcm_dmac_channel *ch) 257 { 258 struct bcm_dmac_softc *sc = ch->ch_sc; 259 uint32_t val; 260 261 bcm_dmac_halt(ch); 262 263 /* reset chip */ 264 val = DMAC_READ(sc, DMAC_CS(ch->ch_index)); 265 val |= DMAC_CS_RESET; 266 val &= ~DMAC_CS_ACTIVE; 267 DMAC_WRITE(sc, DMAC_CS(ch->ch_index), val); 268 269 mutex_enter(&sc->sc_lock); 270 fdtbus_intr_disestablish(sc->sc_phandle, ch->ch_ih); 271 ch->ch_ih = NULL; 272 ch->ch_callback = NULL; 273 ch->ch_callbackarg = NULL; 274 mutex_exit(&sc->sc_lock); 275 } 276 277 void 278 bcm_dmac_set_conblk_addr(struct bcm_dmac_channel *ch, bus_addr_t addr) 279 { 280 struct bcm_dmac_softc *sc = ch->ch_sc; 281 282 DMAC_WRITE(sc, DMAC_CONBLK_AD(ch->ch_index), addr); 283 } 284 285 int 286 bcm_dmac_transfer(struct bcm_dmac_channel *ch) 287 { 288 struct bcm_dmac_softc *sc = ch->ch_sc; 289 uint32_t val; 290 291 val = DMAC_READ(sc, DMAC_CS(ch->ch_index)); 292 if (val & DMAC_CS_ACTIVE) 293 return EBUSY; 294 295 val |= DMAC_CS_ACTIVE; 296 DMAC_WRITE(sc, DMAC_CS(ch->ch_index), val); 297 298 return 0; 299 } 300 301 void 302 bcm_dmac_halt(struct bcm_dmac_channel *ch) 303 { 304 struct bcm_dmac_softc *sc = ch->ch_sc; 305 uint32_t val; 306 307 /* pause DMA */ 308 val = DMAC_READ(sc, DMAC_CS(ch->ch_index)); 309 val &= ~DMAC_CS_ACTIVE; 310 DMAC_WRITE(sc, DMAC_CS(ch->ch_index), val); 311 312 /* wait for paused state ? */ 313 314 /* end descriptor chain */ 315 DMAC_WRITE(sc, DMAC_NEXTCONBK(ch->ch_index), 0); 316 317 /* resume DMA that then stops */ 318 val |= DMAC_CS_ACTIVE | DMAC_CS_ABORT; 319 DMAC_WRITE(sc, DMAC_CS(ch->ch_index), val); 320 } 321 322 #if defined(DDB) 323 void 324 bcm_dmac_dump_regs(void) 325 { 326 struct bcm_dmac_softc *sc; 327 device_t dev; 328 int index; 329 330 dev = device_find_by_driver_unit("bcmdmac", 0); 331 if (dev == NULL) 332 return; 333 sc = device_private(dev); 334 335 for (index = 0; index < sc->sc_nchannels; index++) { 336 if ((sc->sc_channelmask & __BIT(index)) == 0) 337 continue; 338 printf("%d_CS: %08X\n", index, 339 DMAC_READ(sc, DMAC_CS(index))); 340 printf("%d_CONBLK_AD: %08X\n", index, 341 DMAC_READ(sc, DMAC_CONBLK_AD(index))); 342 printf("%d_DEBUG: %08X\n", index, 343 DMAC_READ(sc, DMAC_DEBUG(index))); 344 } 345 } 346 #endif 347