1 /* $NetBSD: dmac3.c,v 1.13 2009/12/14 00:46:09 matt Exp $ */ 2 3 /*- 4 * Copyright (c) 2000 Tsubai Masanari. All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 3. The name of the author may not be used to endorse or promote products 15 * derived from this software without specific prior written permission. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 26 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29 #include <sys/cdefs.h> 30 __KERNEL_RCSID(0, "$NetBSD: dmac3.c,v 1.13 2009/12/14 00:46:09 matt Exp $"); 31 32 #include <sys/param.h> 33 #include <sys/device.h> 34 #include <sys/kernel.h> 35 #include <sys/systm.h> 36 37 #include <uvm/uvm_extern.h> 38 39 #include <machine/locore.h> 40 41 #include <newsmips/apbus/apbusvar.h> 42 #include <newsmips/apbus/dmac3reg.h> 43 #include <newsmips/apbus/dmac3var.h> 44 45 #include <mips/cache.h> 46 47 #include "ioconf.h" 48 49 #define DMA_BURST 50 #define DMA_APAD_OFF 51 52 #ifdef DMA_APAD_OFF 53 # define APAD_MODE 0 54 #else 55 # define APAD_MODE DMAC3_CSR_APAD 56 #endif 57 58 #ifdef DMA_BURST 59 # define BURST_MODE (DMAC3_CSR_DBURST | DMAC3_CSR_MBURST) 60 #else 61 # define BURST_MODE 0 62 #endif 63 64 int dmac3_match(device_t, cfdata_t, void *); 65 void dmac3_attach(device_t, device_t, void *); 66 67 extern paddr_t kvtophys(vaddr_t); 68 69 CFATTACH_DECL_NEW(dmac, sizeof(struct dmac3_softc), 70 dmac3_match, dmac3_attach, NULL, NULL); 71 72 int 73 dmac3_match(device_t parent, cfdata_t cf, void *aux) 74 { 75 struct apbus_attach_args *apa = aux; 76 77 if (strcmp(apa->apa_name, "dmac3") == 0) 78 return 1; 79 80 return 0; 81 } 82 83 void 84 dmac3_attach(device_t parent, device_t self, void *aux) 85 { 86 struct dmac3_softc *sc = device_private(self); 87 struct apbus_attach_args *apa = aux; 88 struct dmac3reg *reg; 89 static paddr_t dmamap = DMAC3_PAGEMAP; 90 static vaddr_t dmaaddr = 0; 91 92 sc->sc_dev = self; 93 reg = (void *)apa->apa_hwbase; 94 sc->sc_reg = reg; 95 sc->sc_ctlnum = apa->apa_ctlnum; 96 sc->sc_dmamap = (uint32_t *)dmamap; 97 sc->sc_dmaaddr = dmaaddr; 98 dmamap += 0x1000; 99 dmaaddr += 0x200000; 100 101 sc->sc_conf = DMAC3_CONF_PCEN | DMAC3_CONF_DCEN | DMAC3_CONF_FASTACCESS; 102 103 dmac3_reset(sc); 104 105 aprint_normal(" slot%d addr 0x%lx", apa->apa_slotno, apa->apa_hwbase); 106 aprint_normal(": ctlnum = %d, map = %p, va = %#"PRIxVADDR, 107 apa->apa_ctlnum, sc->sc_dmamap, sc->sc_dmaaddr); 108 aprint_normal("\n"); 109 } 110 111 struct dmac3_softc * 112 dmac3_link(int ctlnum) 113 { 114 struct dmac3_softc *sc; 115 int unit; 116 117 for (unit = 0; unit < dmac_cd.cd_ndevs; unit++) { 118 sc = device_lookup_private(&dmac_cd, unit); 119 if (sc == NULL) 120 continue; 121 if (sc->sc_ctlnum == ctlnum) 122 return sc; 123 } 124 return NULL; 125 } 126 127 void 128 dmac3_reset(struct dmac3_softc *sc) 129 { 130 struct dmac3reg *reg = sc->sc_reg; 131 132 reg->csr = DMAC3_CSR_RESET; 133 reg->csr = 0; 134 reg->intr = DMAC3_INTR_EOPIE | DMAC3_INTR_INTEN; 135 reg->conf = sc->sc_conf; 136 } 137 138 void 139 dmac3_start(struct dmac3_softc *sc, vaddr_t addr, int len, int direction) 140 { 141 struct dmac3reg *reg = sc->sc_reg; 142 paddr_t pa; 143 vaddr_t start, end, v; 144 volatile uint32_t *p; 145 146 if (reg->csr & DMAC3_CSR_ENABLE) 147 dmac3_reset(sc); 148 149 start = mips_trunc_page(addr); 150 end = mips_round_page(addr + len); 151 p = sc->sc_dmamap; 152 for (v = start; v < end; v += PAGE_SIZE) { 153 pa = kvtophys(v); 154 mips_dcache_wbinv_range(MIPS_PHYS_TO_KSEG0(pa), PAGE_SIZE); 155 *p++ = 0; 156 *p++ = (pa >> PGSHIFT) | 0xc0000000; 157 } 158 *p++ = 0; 159 *p++ = 0x003fffff; 160 161 addr &= PGOFSET; 162 addr += sc->sc_dmaaddr; 163 164 reg->len = len; 165 reg->addr = addr; 166 reg->intr = DMAC3_INTR_EOPIE | DMAC3_INTR_INTEN; 167 reg->csr = DMAC3_CSR_ENABLE | direction | BURST_MODE | APAD_MODE; 168 } 169 170 int 171 dmac3_intr(void *v) 172 { 173 struct dmac3_softc *sc = v; 174 struct dmac3reg *reg = sc->sc_reg; 175 int intr, conf, rv = 1; 176 177 intr = reg->intr; 178 if ((intr & DMAC3_INTR_INT) == 0) 179 return 0; 180 181 /* clear interrupt */ 182 conf = reg->conf; 183 reg->conf = conf; 184 reg->intr = intr; 185 186 if (intr & DMAC3_INTR_PERR) { 187 printf("%s: intr = 0x%x\n", device_xname(sc->sc_dev), intr); 188 rv = -1; 189 } 190 191 if (conf & (DMAC3_CONF_IPER | DMAC3_CONF_MPER | DMAC3_CONF_DERR)) { 192 printf("%s: conf = 0x%x\n", device_xname(sc->sc_dev), conf); 193 if (conf & DMAC3_CONF_DERR) { 194 printf("DMA address = 0x%x\n", reg->addr); 195 printf("resetting DMA...\n"); 196 dmac3_reset(sc); 197 } 198 } 199 200 return rv; 201 } 202 203 void 204 dmac3_misc(struct dmac3_softc *sc, int cmd) 205 { 206 struct dmac3reg *reg = sc->sc_reg; 207 int conf; 208 209 conf = DMAC3_CONF_PCEN | DMAC3_CONF_DCEN | cmd; 210 sc->sc_conf = conf; 211 reg->conf = conf; 212 } 213