1 /* $NetBSD: iomd_dma.c,v 1.5 2002/04/10 19:35:23 thorpej Exp $ */ 2 3 /* 4 * Copyright (c) 1995 Scott Stevens 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 * 3. All advertising materials mentioning features or use of this software 16 * must display the following acknowledgement: 17 * This product includes software developed by Scott Stevens. 18 * 4. The name of the author may not be used to endorse or promote products 19 * derived from this software without specific prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 22 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 23 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 24 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 25 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 26 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 28 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 30 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 * 32 * RiscBSD kernel project 33 * 34 * dma.c 35 * 36 * Created : 15/03/97 37 */ 38 39 #define DMA_DEBUG 40 #include <sys/param.h> 41 #include <sys/systm.h> 42 #include <sys/kernel.h> 43 44 #include <uvm/uvm_extern.h> 45 46 #include <machine/intr.h> 47 #include <machine/pmap.h> 48 #include <arm/iomd/iomdreg.h> 49 #include <arm/iomd/iomdvar.h> 50 #include <arm/iomd/iomd_dma.h> 51 52 53 /* 54 * Only for non ARM7500 machines but the kernel could be booted on a different machine 55 */ 56 57 static struct dma_ctrl ctrl[6]; 58 59 void dma_dumpdc __P((struct dma_ctrl *)); 60 61 void 62 dma_go(dp) 63 struct dma_ctrl *dp; 64 { 65 #ifdef DMA_DEBUG 66 printf("dma_go()\n"); 67 #endif 68 if(dp->dc_flags & DMA_FL_READY) { 69 dp->dc_flags = DMA_FL_ACTIVE; 70 enable_irq(IRQ_DMACH0 + dp->dc_channel); 71 } 72 else 73 panic("dma not ready"); 74 } 75 76 int 77 dma_reset(dp) 78 struct dma_ctrl *dp; 79 { 80 #ifdef DMA_DEBUG 81 printf("dma_reset()\n"); 82 dma_dumpdc(dp); 83 #endif 84 *dp->dc_cr = DMA_CR_CLEAR; 85 dp->dc_flags = 0; 86 disable_irq(IRQ_DMACH0 + dp->dc_channel); 87 return(0); 88 } 89 90 /* 91 * Setup dma transfer, prior to the dma_go call 92 */ 93 int 94 dma_setup(dp, start, len, readp) 95 struct dma_ctrl *dp; 96 int readp; 97 u_char *start; 98 int len; 99 { 100 #ifdef DMA_DEBUG 101 printf("dma_setup(start = %p, len = 0x%08x, readp = %d\n", start, len, readp); 102 #endif 103 if(((u_int)start & (dp->dc_dmasize - 1)) || 104 (len & (dp->dc_dmasize - 1))) { 105 printf("dma_setup: unaligned DMA, %p (0x%08x)\n", 106 start, len); 107 } 108 *dp->dc_cr = DMA_CR_CLEAR | DMA_CR_ENABLE | (readp?DMA_CR_DIR:0) | dp->dc_dmasize; 109 *dp->dc_cr = DMA_CR_ENABLE | (readp?DMA_CR_DIR:0) | dp->dc_dmasize; 110 111 dp->dc_nextaddr = start; 112 dp->dc_len = len; 113 114 dp->dc_flags = DMA_FL_READY; 115 return(0); 116 } 117 118 /* 119 * return true if DMA is active 120 */ 121 int 122 dma_isactive(dp) 123 struct dma_ctrl *dp; 124 { 125 return(dp->dc_flags & DMA_FL_ACTIVE); 126 } 127 128 /* 129 * return true if interrupt pending 130 */ 131 int 132 dma_isintr(dp) 133 struct dma_ctrl *dp; 134 { 135 #ifdef DMA_DEBUG 136 /* printf("dma_isintr() returning %d\n", *dp->dc_st & DMA_ST_INT);*/ 137 #endif 138 return(*dp->dc_st & DMA_ST_INT); 139 } 140 141 int 142 dma_intr(cookie) 143 void *cookie; 144 { 145 struct dma_ctrl *dp = cookie; 146 u_char status = (*dp->dc_st) & DMA_ST_MASK; 147 paddr_t cur; 148 int len; 149 int bufap = 0; 150 151 #ifdef DMA_DEBUG 152 printf("dma_intr() status = 0x%02x\n", status); 153 #endif 154 155 if(!(dp->dc_flags & DMA_FL_ACTIVE)) { 156 /* interrupt whilst not active */ 157 /* ie. last buffer programmed */ 158 dma_reset(dp); 159 return(0); 160 } 161 162 switch(status) { 163 case DMA_ST_OVER | DMA_ST_INT: 164 case DMA_ST_OVER | DMA_ST_INT | DMA_ST_CHAN: 165 /* idle, either first buffer or finished */ 166 if(status & DMA_ST_CHAN) { 167 /* fill buffer B */ 168 bufap = 0; 169 goto fill; 170 } 171 else { 172 /* fill buffer A */ 173 bufap = 1; 174 goto fill; 175 } 176 break; 177 case DMA_ST_INT: 178 case DMA_ST_INT | DMA_ST_CHAN: 179 /* buffer ready */ 180 if(status & DMA_ST_CHAN) { 181 /* fill buffer A */ 182 bufap = 1; 183 goto fill; 184 } 185 else { 186 /* fill buffer B */ 187 bufap = 0; 188 goto fill; 189 } 190 break; 191 default: 192 /* Shouldn't be here */ 193 #ifdef DMA_DEBUG 194 printf("DMA ch %d bad status [%x]\n", dp->dc_channel, status); 195 dma_dumpdc(dp); 196 #endif 197 break; 198 } 199 200 /* return(0);*/ 201 /* XXX */ 202 #define PHYS(x, y) pmap_extract(pmap_kernel(), (vaddr_t)x, (paddr_t *)(y)) 203 fill: 204 #ifdef DMA_DEBUG 205 printf("fill:\n"); 206 #endif 207 if (dp->dc_len == 0) goto done; 208 PHYS(dp->dc_nextaddr, &cur); 209 len = NBPG - (cur & PGOFSET); 210 if (len > dp->dc_len) { 211 /* Last buffer */ 212 len = dp->dc_len; 213 } 214 215 #ifdef DMA_DEBUG 216 dma_dumpdc(dp); 217 /* ptsc_dump_mem(dp->dc_nextaddr, len);*/ 218 #endif 219 /* 220 * Flush the cache for this address 221 */ 222 cpu_dcache_wbinv_range((vaddr_t)dp->dc_nextaddr, len); 223 224 dp->dc_nextaddr += len; 225 dp->dc_len -= len; 226 227 if(bufap) { 228 *dp->dc_cura = (u_int)cur; 229 *dp->dc_enda = ((u_int)cur + len - dp->dc_dmasize) | 230 (dp->dc_len == 0 ? DMA_END_STOP : 0); 231 if (dp->dc_len == 0) { 232 /* Last buffer, fill other buffer with garbage */ 233 *dp->dc_endb = (u_int)cur; 234 } 235 } 236 else { 237 *dp->dc_curb = (u_int)cur; 238 *dp->dc_endb = ((u_int)cur + len - dp->dc_dmasize) | 239 (dp->dc_len == 0 ? DMA_END_STOP : 0); 240 if (dp->dc_len == 0) { 241 /* Last buffer, fill other buffer with garbage */ 242 *dp->dc_enda = (u_int)cur; 243 } 244 } 245 #ifdef DMA_DEBUG 246 dma_dumpdc(dp); 247 /* ptsc_dump_mem(dp->dc_nextaddr - len, len);*/ 248 printf("about to return\n"); 249 #endif 250 return(1); 251 done: 252 #ifdef DMA_DEBUG 253 printf("done:\n"); 254 #endif 255 dp->dc_flags = 0; 256 *dp->dc_cr = 0; 257 disable_irq(IRQ_DMACH0 + dp->dc_channel); 258 #ifdef DMA_DEBUG 259 printf("about to return\n"); 260 #endif 261 return(1); 262 } 263 264 void 265 dma_dumpdc(dc) 266 struct dma_ctrl *dc; 267 { 268 printf("\ndc:\t%p\n" 269 "dc_channel:\t%p=0x%08x\tdc_flags:\t%p=0x%08x\n" 270 "dc_cura:\t%p=0x%08x\tdc_enda:\t%p=0x%08x\n" 271 "dc_curb:\t%p=0x%08x\tdc_endb:\t%p=0x%08x\n" 272 "dc_cr:\t%p=0x%02x\t\tdc_st:\t%p=0x%02x\n" 273 "dc_nextaddr:\t%p=0x%08x\tdc_len:\t%p=0x%08x\n", 274 dc, 275 &dc->dc_channel, (int)dc->dc_channel, 276 &dc->dc_flags, (int)dc->dc_flags, 277 dc->dc_cura, (int)*dc->dc_cura, 278 dc->dc_enda, (int)*dc->dc_enda, 279 dc->dc_curb, (int)*dc->dc_curb, 280 dc->dc_endb, (int)*dc->dc_endb, 281 dc->dc_cr, (int)*dc->dc_cr, 282 dc->dc_st, (int)(*dc->dc_st) & DMA_ST_MASK, 283 &dc->dc_nextaddr, (int)dc->dc_nextaddr, 284 &dc->dc_len, dc->dc_len); 285 } 286 287 struct dma_ctrl * 288 dma_init(ch, extp, dmasize, ipl) 289 int ch; 290 int extp; 291 int dmasize; 292 int ipl; 293 { 294 struct dma_ctrl *dp = &ctrl[ch]; 295 int offset = ch * 0x20; 296 volatile u_char *dmaext = (volatile u_char *)(IOMD_ADDRESS(IOMD_DMAEXT)); 297 298 printf("Initialising DMA channel %d\n", ch); 299 300 dp->dc_channel = ch; 301 dp->dc_flags = 0; 302 dp->dc_dmasize = dmasize; 303 dp->dc_cura = (volatile u_int *)(IOMD_ADDRESS(IOMD_IO0CURA) + offset); 304 dp->dc_enda = (volatile u_int *)(IOMD_ADDRESS(IOMD_IO0ENDA) + offset); 305 dp->dc_curb = (volatile u_int *)(IOMD_ADDRESS(IOMD_IO0CURB) + offset); 306 dp->dc_endb = (volatile u_int *)(IOMD_ADDRESS(IOMD_IO0ENDB) + offset); 307 dp->dc_cr = (volatile u_char *)(IOMD_ADDRESS(IOMD_IO0CR) + offset); 308 dp->dc_st = (volatile u_char *)(IOMD_ADDRESS(IOMD_IO0ST) + offset); 309 310 if (extp) 311 *dmaext |= (1 << ch); 312 313 printf("about to claim interrupt\n"); 314 315 dp->dc_ih.ih_func = dma_intr; 316 dp->dc_ih.ih_arg = dp; 317 dp->dc_ih.ih_level = ipl; 318 dp->dc_ih.ih_name = "dma"; 319 dp->dc_ih.ih_maskaddr = (u_int) IOMD_ADDRESS(IOMD_DMARQ); 320 dp->dc_ih.ih_maskbits = (1 << ch); 321 322 if (irq_claim(IRQ_DMACH0 + ch, &dp->dc_ih)) 323 panic("Cannot install DMA IRQ handler\n"); 324 325 return(dp); 326 } 327 328