summaryrefslogtreecommitdiffstats
path: root/arch/arm/kernel/dma-isa.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/arm/kernel/dma-isa.c')
-rw-r--r--arch/arm/kernel/dma-isa.c94
1 files changed, 83 insertions, 11 deletions
diff --git a/arch/arm/kernel/dma-isa.c b/arch/arm/kernel/dma-isa.c
index bdf7c6147..19be50433 100644
--- a/arch/arm/kernel/dma-isa.c
+++ b/arch/arm/kernel/dma-isa.c
@@ -11,6 +11,7 @@
* Copyright (C) 1998 Phil Blundell
*/
#include <linux/sched.h>
+#include <linux/init.h>
#include <asm/dma.h>
#include <asm/io.h>
@@ -18,6 +19,11 @@
#include "dma.h"
#include "dma-isa.h"
+#define ISA_DMA_MODE_READ 0x44
+#define ISA_DMA_MODE_WRITE 0x48
+#define ISA_DMA_MODE_CASCADE 0xc0
+#define ISA_DMA_AUTOINIT 0x10
+
#define ISA_DMA_MASK 0
#define ISA_DMA_MODE 1
#define ISA_DMA_CLRFF 2
@@ -40,10 +46,7 @@ static unsigned int isa_dma_port[8][7] = {
int isa_request_dma(int channel, dma_t *dma, const char *dev_name)
{
- if (channel != 4)
- return 0;
-
- return -EINVAL;
+ return 0;
}
void isa_free_dma(int channel, dma_t *dma)
@@ -56,25 +59,27 @@ int isa_get_dma_residue(int channel, dma_t *dma)
unsigned int io_port = isa_dma_port[channel][ISA_DMA_COUNT];
int count;
- count = 1 + inb(io_port) + (inb(io_port) << 8);
+ count = 1 + inb(io_port);
+ count |= inb(io_port) << 8;
return channel < 4 ? count : (count << 1);
}
void isa_enable_dma(int channel, dma_t *dma)
{
- unsigned long address, length;
-
if (dma->invalid) {
+ unsigned long address, length;
+ unsigned int mode;
+
address = dma->buf.address;
length = dma->buf.length - 1;
- outb(address >> 24, isa_dma_port[channel][ISA_DMA_PGHI]);
outb(address >> 16, isa_dma_port[channel][ISA_DMA_PGLO]);
+ outb(address >> 24, isa_dma_port[channel][ISA_DMA_PGHI]);
if (channel >= 4) {
address >>= 1;
- length = (length >> 1) & 0xfe; /* why &0xfe? */
+ length >>= 1;
}
outb(0, isa_dma_port[channel][ISA_DMA_CLRFF]);
@@ -85,17 +90,31 @@ void isa_enable_dma(int channel, dma_t *dma)
outb(length, isa_dma_port[channel][ISA_DMA_COUNT]);
outb(length >> 8, isa_dma_port[channel][ISA_DMA_COUNT]);
- outb(dma->dma_mode | (channel & 3), isa_dma_port[channel][ISA_DMA_MODE]);
+ mode = channel & 3;
- switch (dma->dma_mode) {
+ switch (dma->dma_mode & DMA_MODE_MASK) {
case DMA_MODE_READ:
+ mode |= ISA_DMA_MODE_READ;
dma_cache_inv(__bus_to_virt(dma->buf.address), dma->buf.length);
break;
case DMA_MODE_WRITE:
+ mode |= ISA_DMA_MODE_WRITE;
dma_cache_wback(__bus_to_virt(dma->buf.address), dma->buf.length);
break;
+
+ case DMA_MODE_CASCADE:
+ mode |= ISA_DMA_MODE_CASCADE;
+ break;
+
+ default:
+ break;
}
+
+ if (dma->dma_mode & DMA_AUTOINIT)
+ mode |= ISA_DMA_AUTOINIT;
+
+ outb(mode, isa_dma_port[channel][ISA_DMA_MODE]);
dma->invalid = 0;
}
outb(channel & 3, isa_dma_port[channel][ISA_DMA_MASK]);
@@ -105,3 +124,56 @@ void isa_disable_dma(int channel, dma_t *dma)
{
outb(channel | 4, isa_dma_port[channel][ISA_DMA_MASK]);
}
+
+__initfunc(int isa_init_dma(void))
+{
+ int dmac_found;
+
+ outb(0xff, 0x0d);
+ outb(0xff, 0xda);
+
+ outb(0x55, 0x00);
+ outb(0xaa, 0x00);
+
+ dmac_found = inb(0x00) == 0x55 && inb(0x00) == 0xaa;
+
+ if (dmac_found) {
+ int channel;
+
+ for (channel = 0; channel < 8; channel++)
+ isa_disable_dma(channel, NULL);
+
+ outb(0x40, 0x0b);
+ outb(0x41, 0x0b);
+ outb(0x42, 0x0b);
+ outb(0x43, 0x0b);
+
+ outb(0xc0, 0xd6);
+ outb(0x41, 0xd6);
+ outb(0x42, 0xd6);
+ outb(0x43, 0xd6);
+
+ outb(0, 0xd4);
+
+ outb(0x10, 0x08);
+ outb(0x10, 0xd0);
+
+ /*
+ * Is this correct? According to
+ * my documentation, it doesn't
+ * appear to be. It should be
+ * outb(0x3f, 0x40b); outb(0x3f, 0x4d6);
+ */
+ outb(0x30, 0x40b);
+ outb(0x31, 0x40b);
+ outb(0x32, 0x40b);
+ outb(0x33, 0x40b);
+ outb(0x31, 0x4d6);
+ outb(0x32, 0x4d6);
+ outb(0x33, 0x4d6);
+
+ request_dma(DMA_ISA_CASCADE, "cascade");
+ }
+
+ return dmac_found;
+}