1 /* $NetBSD: bcm2835_dmac.c,v 1.16 2017/12/10 21:38:26 skrll Exp $ */ 2 3 /*- 4 * Copyright (c) 2014 Jared D. McNeill <jmcneill@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.16 2017/12/10 21:38:26 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 int 100 bcm_dmac_match(device_t parent, cfdata_t cf, void *aux) 101 { 102 const char * const compatible[] = { 103 "brcm,bcm2835-dma", 104 NULL 105 }; 106 struct fdt_attach_args * const faa = aux; 107 108 return of_match_compatible(faa->faa_phandle, compatible); 109 } 110 111 static void 112 bcm_dmac_attach(device_t parent, device_t self, void *aux) 113 { 114 struct bcm_dmac_softc *sc = device_private(self); 115 const prop_dictionary_t cfg = device_properties(self); 116 struct fdt_attach_args * const faa = aux; 117 struct bcm_dmac_channel *ch; 118 uint32_t val; 119 int index; 120 121 const int phandle = faa->faa_phandle; 122 123 sc->sc_dev = self; 124 sc->sc_iot = faa->faa_bst; 125 sc->sc_phandle = phandle; 126 127 bus_addr_t addr; 128 bus_size_t size; 129 130 if (fdtbus_get_reg(phandle, 0, &addr, &size) != 0) { 131 aprint_error(": missing 'reg' property\n"); 132 return; 133 } 134 135 if (bus_space_map(sc->sc_iot, addr, size, 0, &sc->sc_ioh)) { 136 aprint_error(": unable to map device\n"); 137 return; 138 } 139 140 prop_dictionary_get_uint32(cfg, "chanmask", &sc->sc_channelmask); 141 sc->sc_channelmask &= BCM_DMAC_CHANNELMASK; 142 143 mutex_init(&sc->sc_lock, MUTEX_DEFAULT, IPL_SCHED); 144 145 sc->sc_nchannels = 31 - __builtin_clz(sc->sc_channelmask); 146 sc->sc_channels = kmem_alloc( 147 sizeof(*sc->sc_channels) * sc->sc_nchannels, KM_SLEEP); 148 149 aprint_normal(":"); 150 for (index = 0; index < sc->sc_nchannels; index++) { 151 ch = &sc->sc_channels[index]; 152 ch->ch_sc = sc; 153 ch->ch_index = index; 154 ch->ch_callback = NULL; 155 ch->ch_callbackarg = NULL; 156 ch->ch_ih = NULL; 157 if ((__BIT(index) & sc->sc_channelmask) == 0) 158 continue; 159 160 aprint_normal(" DMA%d", index); 161 162 ch->ch_debug = DMAC_READ(sc, DMAC_DEBUG(index)); 163 164 val = DMAC_READ(sc, DMAC_CS(index)); 165 val |= DMAC_CS_RESET; 166 DMAC_WRITE(sc, DMAC_CS(index), val); 167 } 168 aprint_normal("\n"); 169 aprint_naive("\n"); 170 } 171 172 static int 173 bcm_dmac_intr(void *priv) 174 { 175 struct bcm_dmac_channel *ch = priv; 176 struct bcm_dmac_softc *sc = ch->ch_sc; 177 uint32_t cs, ce; 178 179 cs = DMAC_READ(sc, DMAC_CS(ch->ch_index)); 180 DMAC_WRITE(sc, DMAC_CS(ch->ch_index), cs); 181 cs &= DMAC_CS_INT | DMAC_CS_END | DMAC_CS_ERROR; 182 183 ce = DMAC_READ(sc, DMAC_DEBUG(ch->ch_index)); 184 ce &= DMAC_DEBUG_READ_ERROR | DMAC_DEBUG_FIFO_ERROR 185 | DMAC_DEBUG_READ_LAST_NOT_SET_ERROR; 186 DMAC_WRITE(sc, DMAC_DEBUG(ch->ch_index), ce); 187 188 if (ch->ch_callback) 189 ch->ch_callback(cs, ce, ch->ch_callbackarg); 190 191 return 1; 192 } 193 194 struct bcm_dmac_channel * 195 bcm_dmac_alloc(enum bcm_dmac_type type, int ipl, 196 void (*cb)(uint32_t, uint32_t, void *), void *cbarg) 197 { 198 struct bcm_dmac_softc *sc; 199 struct bcm_dmac_channel *ch = NULL; 200 device_t dev; 201 int index; 202 203 dev = device_find_by_driver_unit("bcmdmac", 0); 204 if (dev == NULL) 205 return NULL; 206 sc = device_private(dev); 207 208 mutex_enter(&sc->sc_lock); 209 for (index = 0; index < sc->sc_nchannels; index++) { 210 if ((sc->sc_channelmask & __BIT(index)) == 0) 211 continue; 212 if (DMAC_CHANNEL_TYPE(&sc->sc_channels[index]) != type) 213 continue; 214 if (DMAC_CHANNEL_USED(&sc->sc_channels[index])) 215 continue; 216 217 ch = &sc->sc_channels[index]; 218 ch->ch_callback = cb; 219 ch->ch_callbackarg = cbarg; 220 break; 221 } 222 mutex_exit(&sc->sc_lock); 223 224 if (ch == NULL) 225 return NULL; 226 227 KASSERT(ch->ch_ih == NULL); 228 229 const int phandle = sc->sc_phandle; 230 char intrstr[128]; 231 232 if (!fdtbus_intr_str(phandle, ch->ch_index, intrstr, sizeof(intrstr))) { 233 aprint_error(": failed to decode interrupt\n"); 234 return NULL; 235 } 236 237 ch->ch_ih = fdtbus_intr_establish(phandle, ch->ch_index, ipl, 0, 238 bcm_dmac_intr, ch); 239 if (ch->ch_ih == NULL) { 240 aprint_error_dev(sc->sc_dev, 241 "failed to establish interrupt for DMA%d and %s\n", ch->ch_index, 242 intrstr); 243 ch->ch_callback = NULL; 244 ch->ch_callbackarg = NULL; 245 ch = NULL; 246 } 247 248 return ch; 249 } 250 251 void 252 bcm_dmac_free(struct bcm_dmac_channel *ch) 253 { 254 struct bcm_dmac_softc *sc = ch->ch_sc; 255 uint32_t val; 256 257 bcm_dmac_halt(ch); 258 259 /* reset chip */ 260 val = DMAC_READ(sc, DMAC_CS(ch->ch_index)); 261 val |= DMAC_CS_RESET; 262 val &= ~DMAC_CS_ACTIVE; 263 DMAC_WRITE(sc, DMAC_CS(ch->ch_index), val); 264 265 mutex_enter(&sc->sc_lock); 266 fdtbus_intr_disestablish(sc->sc_phandle, ch->ch_ih); 267 ch->ch_ih = NULL; 268 ch->ch_callback = NULL; 269 ch->ch_callbackarg = NULL; 270 mutex_exit(&sc->sc_lock); 271 } 272 273 void 274 bcm_dmac_set_conblk_addr(struct bcm_dmac_channel *ch, bus_addr_t addr) 275 { 276 struct bcm_dmac_softc *sc = ch->ch_sc; 277 278 DMAC_WRITE(sc, DMAC_CONBLK_AD(ch->ch_index), addr); 279 } 280 281 int 282 bcm_dmac_transfer(struct bcm_dmac_channel *ch) 283 { 284 struct bcm_dmac_softc *sc = ch->ch_sc; 285 uint32_t val; 286 287 val = DMAC_READ(sc, DMAC_CS(ch->ch_index)); 288 if (val & DMAC_CS_ACTIVE) 289 return EBUSY; 290 291 val |= DMAC_CS_ACTIVE; 292 DMAC_WRITE(sc, DMAC_CS(ch->ch_index), val); 293 294 return 0; 295 } 296 297 void 298 bcm_dmac_halt(struct bcm_dmac_channel *ch) 299 { 300 struct bcm_dmac_softc *sc = ch->ch_sc; 301 uint32_t val; 302 303 /* pause DMA */ 304 val = DMAC_READ(sc, DMAC_CS(ch->ch_index)); 305 val &= ~DMAC_CS_ACTIVE; 306 DMAC_WRITE(sc, DMAC_CS(ch->ch_index), val); 307 308 /* wait for paused state ? */ 309 310 /* end descriptor chain */ 311 DMAC_WRITE(sc, DMAC_NEXTCONBK(ch->ch_index), 0); 312 313 /* resume DMA that then stops */ 314 val |= DMAC_CS_ACTIVE | DMAC_CS_ABORT; 315 DMAC_WRITE(sc, DMAC_CS(ch->ch_index), val); 316 } 317 318 #if defined(DDB) 319 void 320 bcm_dmac_dump_regs(void) 321 { 322 struct bcm_dmac_softc *sc; 323 device_t dev; 324 int index; 325 326 dev = device_find_by_driver_unit("bcmdmac", 0); 327 if (dev == NULL) 328 return; 329 sc = device_private(dev); 330 331 for (index = 0; index < sc->sc_nchannels; index++) { 332 if ((sc->sc_channelmask & __BIT(index)) == 0) 333 continue; 334 printf("%d_CS: %08X\n", index, 335 DMAC_READ(sc, DMAC_CS(index))); 336 printf("%d_CONBLK_AD: %08X\n", index, 337 DMAC_READ(sc, DMAC_CONBLK_AD(index))); 338 printf("%d_DEBUG: %08X\n", index, 339 DMAC_READ(sc, DMAC_DEBUG(index))); 340 } 341 } 342 #endif 343