1/* $NetBSD: dmac3.c,v 1.12 2008/06/13 12:26:35 cegger 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.12 2008/06/13 12:26:35 cegger 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 64int dmac3_match(device_t, cfdata_t, void *); 65void dmac3_attach(device_t, device_t, void *); 66 67extern paddr_t kvtophys(vaddr_t); 68 69CFATTACH_DECL_NEW(dmac, sizeof(struct dmac3_softc), 70 dmac3_match, dmac3_attach, NULL, NULL); 71 72int 73dmac3_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 83void 84dmac3_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 111struct dmac3_softc * 112dmac3_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 127void 128dmac3_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 138void 139dmac3_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 170int 171dmac3_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 203void 204dmac3_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