1 /* $NetBSD: iomd_dma.c,v 1.2 2001/11/27 01:03:52 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(dp) 143 struct dma_ctrl *dp; 144 { 145 u_char status = (*dp->dc_st) & DMA_ST_MASK; 146 vm_offset_t cur; 147 int len; 148 int bufap = 0; 149 150 #ifdef DMA_DEBUG 151 printf("dma_intr() status = 0x%02x\n", status); 152 #endif 153 154 if(!(dp->dc_flags & DMA_FL_ACTIVE)) { 155 /* interrupt whilst not active */ 156 /* ie. last buffer programmed */ 157 dma_reset(dp); 158 return(0); 159 } 160 161 switch(status) { 162 case DMA_ST_OVER | DMA_ST_INT: 163 case DMA_ST_OVER | DMA_ST_INT | DMA_ST_CHAN: 164 /* idle, either first buffer or finished */ 165 if(status & DMA_ST_CHAN) { 166 /* fill buffer B */ 167 bufap = 0; 168 goto fill; 169 } 170 else { 171 /* fill buffer A */ 172 bufap = 1; 173 goto fill; 174 } 175 break; 176 case DMA_ST_INT: 177 case DMA_ST_INT | DMA_ST_CHAN: 178 /* buffer ready */ 179 if(status & DMA_ST_CHAN) { 180 /* fill buffer A */ 181 bufap = 1; 182 goto fill; 183 } 184 else { 185 /* fill buffer B */ 186 bufap = 0; 187 goto fill; 188 } 189 break; 190 default: 191 /* Shouldn't be here */ 192 #ifdef DMA_DEBUG 193 printf("DMA ch %d bad status [%x]\n", dp->dc_channel, status); 194 dma_dumpdc(dp); 195 #endif 196 break; 197 } 198 199 /* return(0);*/ 200 /* XXX */ 201 #define PHYS(x, y) pmap_extract(pmap_kernel(), (vaddr_t)x, (paddr_t *)(y)) 202 fill: 203 #ifdef DMA_DEBUG 204 printf("fill:\n"); 205 #endif 206 if (dp->dc_len == 0) goto done; 207 PHYS(dp->dc_nextaddr, &cur); 208 len = NBPG - (cur & PGOFSET); 209 if (len > dp->dc_len) { 210 /* Last buffer */ 211 len = dp->dc_len; 212 } 213 214 #ifdef DMA_DEBUG 215 dma_dumpdc(dp); 216 /* ptsc_dump_mem(dp->dc_nextaddr, len);*/ 217 #endif 218 /* 219 * Flush the cache for this address 220 */ 221 cpu_cache_purgeD_rng((vm_offset_t)dp->dc_nextaddr, len); 222 223 dp->dc_nextaddr += len; 224 dp->dc_len -= len; 225 226 if(bufap) { 227 *dp->dc_cura = (u_int)cur; 228 *dp->dc_enda = ((u_int)cur + len - dp->dc_dmasize) | 229 (dp->dc_len == 0 ? DMA_END_STOP : 0); 230 if (dp->dc_len == 0) { 231 /* Last buffer, fill other buffer with garbage */ 232 *dp->dc_endb = (u_int)cur; 233 } 234 } 235 else { 236 *dp->dc_curb = (u_int)cur; 237 *dp->dc_endb = ((u_int)cur + len - dp->dc_dmasize) | 238 (dp->dc_len == 0 ? DMA_END_STOP : 0); 239 if (dp->dc_len == 0) { 240 /* Last buffer, fill other buffer with garbage */ 241 *dp->dc_enda = (u_int)cur; 242 } 243 } 244 #ifdef DMA_DEBUG 245 dma_dumpdc(dp); 246 /* ptsc_dump_mem(dp->dc_nextaddr - len, len);*/ 247 printf("about to return\n"); 248 #endif 249 return(1); 250 done: 251 #ifdef DMA_DEBUG 252 printf("done:\n"); 253 #endif 254 dp->dc_flags = 0; 255 *dp->dc_cr = 0; 256 disable_irq(IRQ_DMACH0 + dp->dc_channel); 257 #ifdef DMA_DEBUG 258 printf("about to return\n"); 259 #endif 260 return(1); 261 } 262 263 void 264 dma_dumpdc(dc) 265 struct dma_ctrl *dc; 266 { 267 printf("\ndc:\t%p\n" 268 "dc_channel:\t%p=0x%08x\tdc_flags:\t%p=0x%08x\n" 269 "dc_cura:\t%p=0x%08x\tdc_enda:\t%p=0x%08x\n" 270 "dc_curb:\t%p=0x%08x\tdc_endb:\t%p=0x%08x\n" 271 "dc_cr:\t%p=0x%02x\t\tdc_st:\t%p=0x%02x\n" 272 "dc_nextaddr:\t%p=0x%08x\tdc_len:\t%p=0x%08x\n", 273 dc, 274 &dc->dc_channel, (int)dc->dc_channel, 275 &dc->dc_flags, (int)dc->dc_flags, 276 dc->dc_cura, (int)*dc->dc_cura, 277 dc->dc_enda, (int)*dc->dc_enda, 278 dc->dc_curb, (int)*dc->dc_curb, 279 dc->dc_endb, (int)*dc->dc_endb, 280 dc->dc_cr, (int)*dc->dc_cr, 281 dc->dc_st, (int)(*dc->dc_st) & DMA_ST_MASK, 282 &dc->dc_nextaddr, (int)dc->dc_nextaddr, 283 &dc->dc_len, dc->dc_len); 284 } 285 286 struct dma_ctrl * 287 dma_init(ch, extp, dmasize, ipl) 288 int ch; 289 int extp; 290 int dmasize; 291 int ipl; 292 { 293 struct dma_ctrl *dp = &ctrl[ch]; 294 int offset = ch * 0x20; 295 volatile u_char *dmaext = (volatile u_char *)(IOMD_ADDRESS(IOMD_DMAEXT)); 296 297 printf("Initialising DMA channel %d\n", ch); 298 299 dp->dc_channel = ch; 300 dp->dc_flags = 0; 301 dp->dc_dmasize = dmasize; 302 dp->dc_cura = (volatile u_int *)(IOMD_ADDRESS(IOMD_IO0CURA) + offset); 303 dp->dc_enda = (volatile u_int *)(IOMD_ADDRESS(IOMD_IO0ENDA) + offset); 304 dp->dc_curb = (volatile u_int *)(IOMD_ADDRESS(IOMD_IO0CURB) + offset); 305 dp->dc_endb = (volatile u_int *)(IOMD_ADDRESS(IOMD_IO0ENDB) + offset); 306 dp->dc_cr = (volatile u_char *)(IOMD_ADDRESS(IOMD_IO0CR) + offset); 307 dp->dc_st = (volatile u_char *)(IOMD_ADDRESS(IOMD_IO0ST) + offset); 308 309 if (extp) 310 *dmaext |= (1 << ch); 311 312 printf("about to claim interrupt\n"); 313 314 dp->dc_ih.ih_func = dma_intr; 315 dp->dc_ih.ih_arg = dp; 316 dp->dc_ih.ih_level = ipl; 317 dp->dc_ih.ih_name = "dma"; 318 dp->dc_ih.ih_maskaddr = (u_int) IOMD_ADDRESS(IOMD_DMARQ); 319 dp->dc_ih.ih_maskbits = (1 << ch); 320 321 if (irq_claim(IRQ_DMACH0 + ch, &dp->dc_ih)) 322 panic("Cannot install DMA IRQ handler\n"); 323 324 return(dp); 325 } 326 327