1 /* $NetBSD: iomd_dma.c,v 1.10 2006/08/05 18:22:57 bjh21 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 #include <sys/cdefs.h> 40 __KERNEL_RCSID(0, "$NetBSD: iomd_dma.c,v 1.10 2006/08/05 18:22:57 bjh21 Exp $"); 41 42 #define DMA_DEBUG 43 #include <sys/param.h> 44 #include <sys/systm.h> 45 #include <sys/kernel.h> 46 47 #include <uvm/uvm_extern.h> 48 49 #include <machine/intr.h> 50 #include <machine/pmap.h> 51 #include <arm/iomd/iomdreg.h> 52 #include <arm/iomd/iomdvar.h> 53 #include <arm/iomd/iomd_dma.h> 54 55 56 /* 57 * Only for non ARM7500 machines but the kernel could be booted on a 58 * different machine 59 */ 60 61 static struct dma_ctrl ctrl[6]; 62 63 void dma_dumpdc(struct dma_ctrl *); 64 65 void 66 dma_go(struct dma_ctrl *dp) 67 { 68 69 #ifdef DMA_DEBUG 70 printf("dma_go()\n"); 71 #endif 72 if (dp->dc_flags & DMA_FL_READY) { 73 dp->dc_flags = DMA_FL_ACTIVE; 74 enable_irq(IRQ_DMACH0 + dp->dc_channel); 75 } else 76 panic("dma not ready"); 77 } 78 79 int 80 dma_reset(struct dma_ctrl *dp) 81 { 82 83 #ifdef DMA_DEBUG 84 printf("dma_reset()\n"); 85 dma_dumpdc(dp); 86 #endif 87 *dp->dc_cr = DMA_CR_CLEAR; 88 dp->dc_flags = 0; 89 disable_irq(IRQ_DMACH0 + dp->dc_channel); 90 return 0; 91 } 92 93 /* 94 * Setup dma transfer, prior to the dma_go call 95 */ 96 int 97 dma_setup(struct dma_ctrl *dp, u_char *start, int len, int readp) 98 { 99 100 #ifdef DMA_DEBUG 101 printf("dma_setup(start = %p, len = 0x%08x, readp = %d\n", 102 start, len, readp); 103 #endif 104 if (((u_int)start & (dp->dc_dmasize - 1)) || 105 (len & (dp->dc_dmasize - 1))) { 106 printf("dma_setup: unaligned DMA, %p (0x%08x)\n", 107 start, len); 108 } 109 *dp->dc_cr = DMA_CR_CLEAR | DMA_CR_ENABLE | (readp?DMA_CR_DIR:0) | 110 dp->dc_dmasize; 111 *dp->dc_cr = DMA_CR_ENABLE | (readp?DMA_CR_DIR:0) | dp->dc_dmasize; 112 113 dp->dc_nextaddr = start; 114 dp->dc_len = len; 115 116 dp->dc_flags = DMA_FL_READY; 117 return 0; 118 } 119 120 /* 121 * return true if DMA is active 122 */ 123 int 124 dma_isactive(struct dma_ctrl *dp) 125 { 126 127 return dp->dc_flags & DMA_FL_ACTIVE; 128 } 129 130 /* 131 * return true if interrupt pending 132 */ 133 int 134 dma_isintr(struct dma_ctrl *dp) 135 { 136 137 #ifdef DMA_DEBUG 138 /* printf("dma_isintr() returning %d\n", *dp->dc_st & DMA_ST_INT);*/ 139 #endif 140 return *dp->dc_st & DMA_ST_INT; 141 } 142 143 int 144 dma_intr(void *cookie) 145 { 146 struct dma_ctrl *dp = cookie; 147 u_char status = (*dp->dc_st) & DMA_ST_MASK; 148 paddr_t cur; 149 int len; 150 int bufap = 0; 151 152 #ifdef DMA_DEBUG 153 printf("dma_intr() status = 0x%02x\n", status); 154 #endif 155 156 if (!(dp->dc_flags & DMA_FL_ACTIVE)) { 157 /* interrupt whilst not active */ 158 /* ie. last buffer programmed */ 159 dma_reset(dp); 160 return 0; 161 } 162 163 switch (status) { 164 case DMA_ST_OVER | DMA_ST_INT: 165 case DMA_ST_OVER | DMA_ST_INT | DMA_ST_CHAN: 166 /* idle, either first buffer or finished */ 167 if (status & DMA_ST_CHAN) { 168 /* fill buffer B */ 169 bufap = 0; 170 goto fill; 171 } 172 else { 173 /* fill buffer A */ 174 bufap = 1; 175 goto fill; 176 } 177 break; 178 case DMA_ST_INT: 179 case DMA_ST_INT | DMA_ST_CHAN: 180 /* buffer ready */ 181 if (status & DMA_ST_CHAN) { 182 /* fill buffer A */ 183 bufap = 1; 184 goto fill; 185 } 186 else { 187 /* fill buffer B */ 188 bufap = 0; 189 goto fill; 190 } 191 break; 192 default: 193 /* Shouldn't be here */ 194 #ifdef DMA_DEBUG 195 printf("DMA ch %d bad status [%x]\n", dp->dc_channel, status); 196 dma_dumpdc(dp); 197 #endif 198 break; 199 } 200 201 /* return(0);*/ 202 /* XXX */ 203 #define PHYS(x, y) pmap_extract(pmap_kernel(), (vaddr_t)x, (paddr_t *)(y)) 204 fill: 205 #ifdef DMA_DEBUG 206 printf("fill:\n"); 207 #endif 208 if (dp->dc_len == 0) goto done; 209 PHYS(dp->dc_nextaddr, &cur); 210 len = PAGE_SIZE - (cur & PGOFSET); 211 if (len > dp->dc_len) { 212 /* Last buffer */ 213 len = dp->dc_len; 214 } 215 216 #ifdef DMA_DEBUG 217 dma_dumpdc(dp); 218 /* ptsc_dump_mem(dp->dc_nextaddr, len);*/ 219 #endif 220 /* 221 * Flush the cache for this address 222 */ 223 cpu_dcache_wbinv_range((vaddr_t)dp->dc_nextaddr, len); 224 225 dp->dc_nextaddr += len; 226 dp->dc_len -= len; 227 228 if (bufap) { 229 *dp->dc_cura = (u_int)cur; 230 *dp->dc_enda = ((u_int)cur + len - dp->dc_dmasize) | 231 (dp->dc_len == 0 ? DMA_END_STOP : 0); 232 if (dp->dc_len == 0) { 233 /* Last buffer, fill other buffer with garbage */ 234 *dp->dc_endb = (u_int)cur; 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(struct dma_ctrl *dc) 266 { 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(int ch, int extp, int dmasize, int ipl) 289 { 290 struct dma_ctrl *dp = &ctrl[ch]; 291 int offset = ch * 0x20; 292 volatile u_char *dmaext = (volatile u_char *)(IOMD_ADDRESS(IOMD_DMAEXT)); 293 294 printf("Initialising DMA channel %d\n", ch); 295 296 dp->dc_channel = ch; 297 dp->dc_flags = 0; 298 dp->dc_dmasize = dmasize; 299 dp->dc_cura = (volatile u_int *)(IOMD_ADDRESS(IOMD_IO0CURA) + offset); 300 dp->dc_enda = (volatile u_int *)(IOMD_ADDRESS(IOMD_IO0ENDA) + offset); 301 dp->dc_curb = (volatile u_int *)(IOMD_ADDRESS(IOMD_IO0CURB) + offset); 302 dp->dc_endb = (volatile u_int *)(IOMD_ADDRESS(IOMD_IO0ENDB) + offset); 303 dp->dc_cr = (volatile u_char *)(IOMD_ADDRESS(IOMD_IO0CR) + offset); 304 dp->dc_st = (volatile u_char *)(IOMD_ADDRESS(IOMD_IO0ST) + offset); 305 306 if (extp) 307 *dmaext |= (1 << ch); 308 309 printf("about to claim interrupt\n"); 310 311 dp->dc_ih.ih_func = dma_intr; 312 dp->dc_ih.ih_arg = dp; 313 dp->dc_ih.ih_level = ipl; 314 dp->dc_ih.ih_name = "dma"; 315 dp->dc_ih.ih_maskaddr = (u_int) IOMD_ADDRESS(IOMD_DMARQ); 316 dp->dc_ih.ih_maskbits = (1 << ch); 317 318 if (irq_claim(IRQ_DMACH0 + ch, &dp->dc_ih)) 319 panic("Cannot install DMA IRQ handler"); 320 321 return dp; 322 } 323 324