1/* $NetBSD: iomd_dma.c,v 1.9 2005/12/11 12:16:47 christos 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.9 2005/12/11 12:16:47 christos 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 61static struct dma_ctrl ctrl[6]; 62 63void dma_dumpdc(struct dma_ctrl *); 64 65void 66dma_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 79int 80dma_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 */ 96int 97dma_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 */ 123int 124dma_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 */ 133int 134dma_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 143int 144dma_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)) 204fill: 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; 251done: 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 264void 265dma_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 287struct dma_ctrl * 288dma_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