patch-2.4.9 linux/arch/arm/mach-sa1100/dma-sa1100.c
Next file: linux/arch/arm/mach-sa1100/dma-sa1111.c
Previous file: linux/arch/arm/mach-sa1100/cpu-sa1110.c
Back to the patch index
Back to the overall index
- Lines: 456
- Date:
Sun Aug 12 17:36:24 2001
- Orig file:
v2.4.8/linux/arch/arm/mach-sa1100/dma-sa1100.c
- Orig date:
Thu Apr 12 12:20:31 2001
diff -u --recursive --new-file v2.4.8/linux/arch/arm/mach-sa1100/dma-sa1100.c linux/arch/arm/mach-sa1100/dma-sa1100.c
@@ -16,7 +16,7 @@
#include <linux/init.h>
#include <linux/sched.h>
#include <linux/spinlock.h>
-#include <linux/malloc.h>
+#include <linux/slab.h>
#include <linux/errno.h>
#include <asm/system.h>
@@ -36,13 +36,6 @@
/*
- * Maximum physical DMA buffer size
- */
-#define MAX_DMA_SIZE 0x1fff
-#define MAX_DMA_ORDER 12
-
-
-/*
* DMA control register structure
*/
typedef struct {
@@ -56,24 +49,16 @@
volatile u_long DBTB;
} dma_regs_t;
-
-/*
- * DMA buffer structure
- */
-struct dma_buf_s {
- int size; /* buffer size */
- dma_addr_t dma_start; /* starting DMA address */
- dma_addr_t dma_ptr; /* current DMA pointer position */
- int ref; /* number of DMA references */
- void *id; /* to identify buffer from outside */
- struct dma_buf_s *next; /* next buffer to process */
-};
-
-
#include "dma.h"
sa1100_dma_t dma_chan[MAX_SA1100_DMA_CHANNELS];
+/*
+ * Maximum physical DMA buffer size
+ */
+#define MAX_DMA_SIZE 0x1fff
+#define MAX_DMA_ORDER 12
+
/*
* DMA processing...
@@ -130,7 +115,7 @@
for (;;) {
buf = dma->tail;
- if (!buf) {
+ if (!buf || dma->stopped) {
/* no more data available */
DPRINTK("process: no more buf (dma %s)\n",
dma->curr ? "active" : "inactive");
@@ -164,7 +149,6 @@
DPRINTK("process: b=%#x s=%d\n", (int) buf->id, buf->size);
if (start_dma(dma, buf->dma_ptr, chunksize) != 0)
break;
- dma->active = 1;
if (!dma->curr)
dma->curr = buf;
buf->ref++;
@@ -201,9 +185,10 @@
dma->spin_ref = -dma->spin_ref;
if (dma->head == buf)
dma->head = NULL;
- buf->size = buf->dma_ptr - buf->dma_start;
- if (dma->callback)
- dma->callback(buf->id, buf->size);
+ if (dma->callback) {
+ int size = buf->dma_ptr - buf->dma_start;
+ dma->callback(buf->id, size);
+ }
kfree(buf);
}
}
@@ -231,62 +216,59 @@
* DMA interface functions
*/
-/*
- * Get dma list
- * for /proc/dma
- */
-int sa1100_get_dma_list(char *buf)
-{
- int i, len = 0;
-
- for (i = 0; i < MAX_SA1100_DMA_CHANNELS; i++) {
- if (dma_chan[i].lock)
- len += sprintf(buf + len, "%2d: %s\n",
- i, dma_chan[i].device_id);
- }
- return len;
-}
+static spinlock_t dma_list_lock;
-int sa1100_request_dma(dmach_t * channel, const char *device_id)
+int sa1100_request_dma (dmach_t * channel, const char *device_id,
+ dma_device_t device)
{
sa1100_dma_t *dma = NULL;
dma_regs_t *regs;
- int ch, err;
+ int i, err;
*channel = -1; /* to be sure we catch the freeing of a misregistered channel */
- for (ch = 0; ch < SA1100_DMA_CHANNELS; ch++) {
- dma = &dma_chan[ch];
- if (xchg(&dma->lock, 1) == 0)
- break;
+ err = 0;
+ spin_lock(&dma_list_lock);
+ for (i = 0; i < SA1100_DMA_CHANNELS; i++) {
+ if (dma_chan[i].in_use) {
+ if (dma_chan[i].device == device) {
+ err = -EBUSY;
+ break;
+ }
+ } else if (!dma) {
+ dma = &dma_chan[i];
+ }
}
- if (ch >= SA1100_DMA_CHANNELS) {
- printk(KERN_ERR "%s: no free DMA channel available\n",
- device_id);
- return -EBUSY;
+ if (!err) {
+ if (dma)
+ dma->in_use = 1;
+ else
+ err = -ENOSR;
}
+ spin_unlock(&dma_list_lock);
+ if (err)
+ return err;
err = request_irq(dma->irq, dma_irq_handler, SA_INTERRUPT,
device_id, (void *) dma);
if (err) {
printk(KERN_ERR
- "%s: unable to request IRQ %d for DMA channel %d\n",
- device_id, dma->irq, ch);
- dma->lock = 0;
+ "%s: unable to request IRQ %d for DMA channel\n",
+ device_id, dma->irq);
return err;
}
- *channel = ch;
+ *channel = dma - dma_chan;
dma->device_id = device_id;
+ dma->device = device;
dma->callback = NULL;
dma->spin_size = 0;
regs = dma->regs;
regs->ClrDCSR =
- (DCSR_DONEA | DCSR_DONEB | DCSR_STRTA | DCSR_STRTB |
- DCSR_IE | DCSR_ERROR | DCSR_RUN);
- regs->DDAR = 0;
-
+ (DCSR_DONEA | DCSR_DONEB | DCSR_STRTA | DCSR_STRTB |
+ DCSR_IE | DCSR_ERROR | DCSR_RUN);
+ regs->DDAR = device;
DPRINTK("requested\n");
return 0;
}
@@ -296,24 +278,11 @@
{
sa1100_dma_t *dma = &dma_chan[channel];
- dma->callback = cb;
- DPRINTK("cb = %p\n", cb);
- return 0;
-}
-
-
-int sa1100_dma_set_device(dmach_t channel, dma_device_t device)
-{
- sa1100_dma_t *dma = &dma_chan[channel];
- dma_regs_t *regs = dma->regs;
-
- if (dma->ready)
+ if ((unsigned)channel >= MAX_SA1100_DMA_CHANNELS || !dma->in_use)
return -EINVAL;
- regs->ClrDCSR = DCSR_STRTA | DCSR_STRTB | DCSR_IE | DCSR_RUN;
- regs->DDAR = device;
- DPRINTK("DDAR = %#x\n", device);
- dma->ready = 1;
+ dma->callback = cb;
+ DPRINTK("cb = %p\n", cb);
return 0;
}
@@ -323,7 +292,7 @@
sa1100_dma_t *dma = &dma_chan[channel];
int flags;
- if (channel >= SA1100_DMA_CHANNELS)
+ if ((unsigned)channel >= MAX_SA1100_DMA_CHANNELS || !dma->in_use)
return -EINVAL;
DPRINTK("set spin %d at %#x\n", size, addr);
@@ -345,7 +314,7 @@
int flags;
dma = &dma_chan[channel];
- if (!dma->ready)
+ if ((unsigned)channel >= MAX_SA1100_DMA_CHANNELS || !dma->in_use)
return -EINVAL;
buf = kmalloc(sizeof(*buf), GFP_ATOMIC);
@@ -375,12 +344,16 @@
int sa1100_dma_get_current(dmach_t channel, void **buf_id, dma_addr_t *addr)
{
sa1100_dma_t *dma = &dma_chan[channel];
- dma_regs_t *regs = dma->regs;
+ dma_regs_t *regs;
int flags, ret;
+ if ((unsigned)channel >= MAX_SA1100_DMA_CHANNELS || !dma->in_use)
+ return -EINVAL;
+
if (channel_is_sa1111_sac(channel))
return sa1111_dma_get_current(channel, buf_id, addr);
+ regs = dma->regs;
local_irq_save(flags);
if (dma->curr && dma->spin_ref <= 0) {
dma_buf_t *buf = dma->curr;
@@ -415,11 +388,16 @@
*addr = buf->dma_ptr;
DPRINTK("curr_pos: b=%#x a=%#x\n", (int)dma->curr->id, *addr);
ret = 0;
+ } else if (dma->tail && dma->stopped) {
+ dma_buf_t *buf = dma->tail;
+ if (buf_id)
+ *buf_id = buf->id;
+ *addr = buf->dma_ptr;
+ ret = 0;
} else {
if (buf_id)
*buf_id = NULL;
*addr = 0;
- DPRINTK("curr_pos: spinning\n");
ret = -ENXIO;
}
local_irq_restore(flags);
@@ -430,12 +408,36 @@
int sa1100_dma_stop(dmach_t channel)
{
sa1100_dma_t *dma = &dma_chan[channel];
- dma_regs_t *regs = dma->regs;
+ int flags;
if (channel_is_sa1111_sac(channel))
return sa1111_dma_stop(channel);
- regs->ClrDCSR = DCSR_RUN | DCSR_IE;
+ if (dma->stopped)
+ return 0;
+ local_irq_save(flags);
+ dma->stopped = 1;
+ /*
+ * Stop DMA and tweak state variables so everything could restart
+ * from there when resume/wakeup occurs.
+ */
+ dma->regs->ClrDCSR = DCSR_RUN | DCSR_IE;
+ if (dma->curr) {
+ dma_buf_t *buf = dma->curr;
+ if (dma->spin_ref <= 0) {
+ dma_addr_t curpos;
+ sa1100_dma_get_current(channel, NULL, &curpos);
+ buf->size += buf->dma_ptr - curpos;
+ buf->dma_ptr = curpos;
+ }
+ buf->ref = 0;
+ dma->tail = buf;
+ dma->curr = NULL;
+ }
+ dma->spin_ref = 0;
+ dma->regs->ClrDCSR = DCSR_STRTA|DCSR_STRTB;
+ process_dma(dma);
+ local_irq_restore(flags);
return 0;
}
@@ -443,23 +445,35 @@
int sa1100_dma_resume(dmach_t channel)
{
sa1100_dma_t *dma = &dma_chan[channel];
- dma_regs_t *regs = dma->regs;
+
+ if ((unsigned)channel >= MAX_SA1100_DMA_CHANNELS || !dma->in_use)
+ return -EINVAL;
if (channel_is_sa1111_sac(channel))
return sa1111_dma_resume(channel);
- regs->SetDCSR = DCSR_RUN | DCSR_IE;
+ if (dma->stopped) {
+ int flags;
+ save_flags_cli(flags);
+ dma->regs->ClrDCSR = DCSR_STRTA|DCSR_STRTB|DCSR_RUN|DCSR_IE;
+ dma->stopped = 0;
+ dma->spin_ref = 0;
+ process_dma(dma);
+ restore_flags(flags);
+ }
return 0;
}
int sa1100_dma_flush_all(dmach_t channel)
{
- sa1100_dma_t *dma;
+ sa1100_dma_t *dma = &dma_chan[channel];
dma_buf_t *buf, *next_buf;
int flags;
- dma = &dma_chan[channel];
+ if ((unsigned)channel >= MAX_SA1100_DMA_CHANNELS || !dma->in_use)
+ return -EINVAL;
+
local_irq_save(flags);
if (channel_is_sa1111_sac(channel))
sa1111_reset_sac_dma(channel);
@@ -469,10 +483,9 @@
if (!buf)
buf = dma->tail;
dma->head = dma->tail = dma->curr = NULL;
- dma->active = 0;
+ dma->stopped = 0;
dma->spin_ref = 0;
- if (dma->spin_size)
- process_dma(dma);
+ process_dma(dma);
local_irq_restore(flags);
while (buf) {
next_buf = buf->next;
@@ -488,25 +501,24 @@
{
sa1100_dma_t *dma;
- if ((unsigned) channel >= MAX_SA1100_DMA_CHANNELS)
+ if ((unsigned)channel >= MAX_SA1100_DMA_CHANNELS)
return;
dma = &dma_chan[channel];
- if (!dma->lock) {
+ if (!dma->in_use) {
printk(KERN_ERR "Trying to free free DMA%d\n", channel);
return;
}
sa1100_dma_set_spin(channel, 0, 0);
sa1100_dma_flush_all(channel);
- dma->ready = 0;
if (channel_is_sa1111_sac(channel)) {
sa1111_cleanup_sac_dma(channel);
} else {
free_irq(IRQ_DMA0 + channel, (void *) dma);
}
- dma->lock = 0;
+ dma->in_use = 0;
DPRINTK("freed\n");
}
@@ -514,7 +526,6 @@
EXPORT_SYMBOL(sa1100_request_dma);
EXPORT_SYMBOL(sa1100_dma_set_callback);
-EXPORT_SYMBOL(sa1100_dma_set_device);
EXPORT_SYMBOL(sa1100_dma_set_spin);
EXPORT_SYMBOL(sa1100_dma_queue_buffer);
EXPORT_SYMBOL(sa1100_dma_get_current);
@@ -522,6 +533,66 @@
EXPORT_SYMBOL(sa1100_dma_resume);
EXPORT_SYMBOL(sa1100_dma_flush_all);
EXPORT_SYMBOL(sa1100_free_dma);
+
+
+#ifdef CONFIG_PM
+/* Drivers should call this from their PM callback function */
+
+int sa1100_dma_sleep(dmach_t channel)
+{
+ sa1100_dma_t *dma = &dma_chan[channel];
+ int orig_state;
+
+ if ((unsigned)channel >= MAX_SA1100_DMA_CHANNELS || !dma->in_use)
+ return -EINVAL;
+
+ if (channel_is_sa1111_sac(channel)) {
+ /* We'll cheat a little until someone actually
+ * write the real thing.
+ */
+ sa1111_reset_sac_dma(channel);
+ return 0;
+ }
+
+ orig_state = dma->stopped;
+ sa1100_dma_stop(channel);
+ dma->regs->ClrDCSR = DCSR_RUN | DCSR_IE | DCSR_STRTA | DCSR_STRTB;
+ dma->stopped = orig_state;
+ dma->spin_ref = 0;
+ return 0;
+}
+
+int sa1100_dma_wakeup(dmach_t channel)
+{
+ sa1100_dma_t *dma = &dma_chan[channel];
+ dma_regs_t *regs;
+ int flags;
+
+ if ((unsigned)channel >= MAX_SA1100_DMA_CHANNELS || !dma->in_use)
+ return -EINVAL;
+
+ if (channel_is_sa1111_sac(channel)) {
+ /* We'll cheat a little until someone actually
+ * write the real thing.
+ */
+ return 0;
+ }
+
+ regs = dma->regs;
+ regs->ClrDCSR =
+ (DCSR_DONEA | DCSR_DONEB | DCSR_STRTA | DCSR_STRTB |
+ DCSR_IE | DCSR_ERROR | DCSR_RUN);
+ regs->DDAR = dma->device;
+ local_irq_save(flags);
+ process_dma(dma);
+ local_irq_restore(flags);
+ return 0;
+}
+
+EXPORT_SYMBOL(sa1100_dma_sleep);
+EXPORT_SYMBOL(sa1100_dma_wakeup);
+
+#endif
static int __init sa1100_init_dma(void)
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)