patch-2.3.7 linux/arch/arm/kernel/dma-isa.c

Next file: linux/arch/arm/kernel/dma-isa.h
Previous file: linux/arch/arm/kernel/dma-footbridge.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.3.6/linux/arch/arm/kernel/dma-isa.c linux/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 @@
 
 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 @@
 	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 @@
 		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]);
@@ -104,4 +123,57 @@
 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;
 }

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)