Loading...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 | /*
* linux/arch/arm/mach-sa1100/dma-sa1111.c
*
* Copyright (C) 2000 John Dorsey
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* 4 September 2000 - created.
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/sched.h>
#include <linux/delay.h>
#include <linux/spinlock.h>
#include <linux/errno.h>
#include <asm/system.h>
#include <asm/irq.h>
#include <asm/hardware.h>
#include <asm/io.h>
#include <asm/dma.h>
// #define DEBUG
#ifdef DEBUG
#define DPRINTK( s, arg... ) printk( "dma<%s>: " s, dma->device_id , ##arg )
#else
#define DPRINTK( x... )
#endif
/*
* Control register structure for the SA1111 SAC DMA
*/
typedef struct {
volatile u_long SAD_CS;
volatile dma_addr_t SAD_SA;
volatile u_long SAD_CA;
volatile dma_addr_t SAD_SB;
volatile u_long SAD_CB;
} dma_regs_t;
#include "dma.h"
void sa1111_reset_sac_dma(dmach_t channel)
{
sa1100_dma_t *dma = &dma_chan[channel];
dma->regs->SAD_CS = 0;
mdelay(1);
dma->dma_a = dma->dma_b = 0;
}
int start_sa1111_sac_dma(sa1100_dma_t *dma, dma_addr_t dma_ptr, size_t size)
{
dma_regs_t *sac_regs = dma->regs;
DPRINTK(" SAC DMA %cCS %02x at %08x (%d)\n",
(sac_regs==&SADTCS)?'T':'R', sac_regs->SAD_CS, dma_ptr, size);
/* The minimum transfer length requirement has not yet been
* verified:
*/
if( size < SA1111_SAC_DMA_MIN_XFER )
printk(KERN_WARNING "Warning: SAC xfers below %u bytes may be buggy!"
" (%u bytes)\n", SA1111_SAC_DMA_MIN_XFER, size);
if( dma->dma_a && dma->dma_b ){
DPRINTK(" neither engine available! (A %d, B %d)\n",
dma->dma_a, dma->dma_b);
return -1;
}
if( sa1111_check_dma_bug(dma_ptr) )
printk(KERN_WARNING "Warning: DMA address %08x is buggy!\n",
dma_ptr);
if( (dma->last_dma || dma->dma_b) && dma->dma_a == 0 ){
if( sac_regs->SAD_CS & SAD_CS_DBDB ){
DPRINTK(" awaiting \"done B\" interrupt, not starting\n");
return -1;
}
sac_regs->SAD_SA = SA1111_DMA_ADDR((u_int)dma_ptr);
sac_regs->SAD_CA = size;
sac_regs->SAD_CS = SAD_CS_DSTA | SAD_CS_DEN;
++dma->dma_a;
DPRINTK(" with A [%02lx %08lx %04lx]\n", sac_regs->SAD_CS,
sac_regs->SAD_SA, sac_regs->SAD_CA);
} else {
if( sac_regs->SAD_CS & SAD_CS_DBDA ){
DPRINTK(" awaiting \"done A\" interrupt, not starting\n");
return -1;
}
sac_regs->SAD_SB = SA1111_DMA_ADDR((u_int)dma_ptr);
sac_regs->SAD_CB = size;
sac_regs->SAD_CS = SAD_CS_DSTB | SAD_CS_DEN;
++dma->dma_b;
DPRINTK(" with B [%02lx %08lx %04lx]\n", sac_regs->SAD_CS,
sac_regs->SAD_SB, sac_regs->SAD_CB);
}
/* Additional delay to avoid DMA engine lockup during record: */
if( sac_regs == (dma_regs_t*)&SADRCS )
mdelay(1); /* NP : wouuuh! ugly... */
return 0;
}
static void sa1111_sac_dma_irq(int irq, void *dev_id, struct pt_regs *regs)
{
sa1100_dma_t *dma = (sa1100_dma_t *) dev_id;
DPRINTK("irq %d, last DMA serviced was %c, CS %02x\n", irq,
dma->last_dma?'B':'A', dma->regs->SAD_CS);
/* Occasionally, one of the DMA engines (A or B) will
* lock up. We try to deal with this by quietly kicking
* the control register for the afflicted transfer
* direction.
*
* Note for the debugging-inclined: we currently aren't
* properly flushing the DMA engines during channel
* shutdown. A slight hiccup at the beginning of playback
* after a channel has been stopped can be heard as
* evidence of this. Programmatically, this shows up
* as either a locked engine, or a spurious interrupt. -jd
*/
if(irq==AUDXMTDMADONEA || irq==AUDRCVDMADONEA){
if(dma->last_dma == 0){
DPRINTK("DMA B has locked up!\n");
dma->regs->SAD_CS = 0;
mdelay(1);
dma->dma_a = dma->dma_b = 0;
} else {
if(dma->dma_a == 0)
DPRINTK("spurious SAC IRQ %d\n", irq);
else {
--dma->dma_a;
/* Servicing the SAC DMA engines too quickly
* after they issue a DONE interrupt causes
* them to lock up.
*/
if(irq==AUDRCVDMADONEA || irq==AUDRCVDMADONEB)
mdelay(1);
}
}
dma->regs->SAD_CS = SAD_CS_DBDA | SAD_CS_DEN; /* w1c */
dma->last_dma = 0;
} else {
if(dma->last_dma == 1){
DPRINTK("DMA A has locked up!\n");
dma->regs->SAD_CS = 0;
mdelay(1);
dma->dma_a = dma->dma_b = 0;
} else {
if(dma->dma_b == 0)
DPRINTK("spurious SAC IRQ %d\n", irq);
else {
--dma->dma_b;
/* See lock-up note above. */
if(irq==AUDRCVDMADONEA || irq==AUDRCVDMADONEB)
mdelay(1);
}
}
dma->regs->SAD_CS = SAD_CS_DBDB | SAD_CS_DEN; /* w1c */
dma->last_dma = 1;
}
/* NP: maybe this shouldn't be called in all cases? */
sa1100_dma_done (dma);
}
int sa1111_sac_request_dma(dmach_t *channel, const char *device_id,
unsigned int direction)
{
sa1100_dma_t *dma = NULL;
int ch, irq, err;
*channel = -1; /* to be sure we catch the freeing of a misregistered channel */
ch = SA1111_SAC_DMA_BASE + direction;
if (!channel_is_sa1111_sac(ch)) {
printk(KERN_ERR "%s: invalid SA-1111 SAC DMA channel (%d)\n",
device_id, ch);
return -1;
}
dma = &dma_chan[ch];
if (xchg(&dma->in_use, 1) == 1) {
printk(KERN_ERR "%s: SA-1111 SAC DMA channel %d in use\n",
device_id, ch);
return -EBUSY;
}
irq = AUDXMTDMADONEA + direction;
err = request_irq(irq, sa1111_sac_dma_irq, SA_INTERRUPT,
device_id, (void *) dma);
if (err) {
printk(KERN_ERR
"%s: unable to request IRQ %d for DMA channel %d (A)\n",
device_id, irq, ch);
dma->in_use = 0;
return err;
}
irq = AUDXMTDMADONEB + direction;
err = request_irq(irq, sa1111_sac_dma_irq, SA_INTERRUPT,
device_id, (void *) dma);
if (err) {
printk(KERN_ERR
"%s: unable to request IRQ %d for DMA channel %d (B)\n",
device_id, irq, ch);
dma->in_use = 0;
return err;
}
*channel = ch;
dma->device_id = device_id;
dma->callback = NULL;
dma->spin_size = 0;
return 0;
}
/* FIXME: need to complete the three following functions */
int sa1111_dma_get_current(dmach_t channel, void **buf_id, dma_addr_t *addr)
{
sa1100_dma_t *dma = &dma_chan[channel];
int flags, ret;
local_irq_save(flags);
if (dma->curr && dma->spin_ref <= 0) {
dma_buf_t *buf = dma->curr;
if (buf_id)
*buf_id = buf->id;
/* not fully accurate but still... */
*addr = buf->dma_ptr;
ret = 0;
} else {
if (buf_id)
*buf_id = NULL;
*addr = 0;
ret = -ENXIO;
}
local_irq_restore(flags);
return ret;
}
int sa1111_dma_stop(dmach_t channel)
{
return 0;
}
int sa1111_dma_resume(dmach_t channel)
{
return 0;
}
void sa1111_cleanup_sac_dma(dmach_t channel)
{
sa1100_dma_t *dma = &dma_chan[channel];
free_irq(AUDXMTDMADONEA + (channel - SA1111_SAC_DMA_BASE), (void*) dma);
free_irq(AUDXMTDMADONEB + (channel - SA1111_SAC_DMA_BASE), (void*) dma);
}
/* According to the "Intel StrongARM SA-1111 Microprocessor Companion
* Chip Specification Update" (June 2000), erratum #7, there is a
* significant bug in Serial Audio Controller DMA. If the SAC is
* accessing a region of memory above 1MB relative to the bank base,
* it is important that address bit 10 _NOT_ be asserted. Depending
* on the configuration of the RAM, bit 10 may correspond to one
* of several different (processor-relative) address bits.
*
* This routine only identifies whether or not a given DMA address
* is susceptible to the bug.
*/
int sa1111_check_dma_bug(dma_addr_t addr){
unsigned int physaddr=SA1111_DMA_ADDR((unsigned int)addr);
/* Section 4.6 of the "Intel StrongARM SA-1111 Development Module
* User's Guide" mentions that jumpers R51 and R52 control the
* target of SA-1111 DMA (either SDRAM bank 0 on Assabet, or
* SDRAM bank 1 on Neponset). The default configuration selects
* Assabet, so any address in bank 1 is necessarily invalid.
*/
if((machine_is_assabet() || machine_is_pfs168()) && addr >= _DRAMBnk1)
return -1;
/* The bug only applies to buffers located more than one megabyte
* above the start of the target bank:
*/
if(physaddr<(1<<20))
return 0;
switch(FExtr(SMCR, SMCR_DRAC)){
case 01: /* 10 row + bank address bits, A<20> must not be set */
if(physaddr & (1<<20))
return -1;
break;
case 02: /* 11 row + bank address bits, A<23> must not be set */
if(physaddr & (1<<23))
return -1;
break;
case 03: /* 12 row + bank address bits, A<24> must not be set */
if(physaddr & (1<<24))
return -1;
break;
case 04: /* 13 row + bank address bits, A<25> must not be set */
if(physaddr & (1<<25))
return -1;
break;
case 05: /* 14 row + bank address bits, A<20> must not be set */
if(physaddr & (1<<20))
return -1;
break;
case 06: /* 15 row + bank address bits, A<20> must not be set */
if(physaddr & (1<<20))
return -1;
break;
default:
printk(KERN_ERR "%s(): invalid SMCR DRAC value 0%o\n",
__FUNCTION__, FExtr(SMCR, SMCR_DRAC));
return -1;
}
return 0;
}
EXPORT_SYMBOL(sa1111_sac_request_dma);
EXPORT_SYMBOL(sa1111_check_dma_bug);
static int __init sa1111_init_sac_dma(void)
{
int channel = SA1111_SAC_DMA_BASE;
dma_chan[channel++].regs = (dma_regs_t *) &SADTCS;
dma_chan[channel++].regs = (dma_regs_t *) &SADRCS;
return 0;
}
__initcall(sa1111_init_sac_dma);
|