• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  *  linux/arch/arm/kernel/dma-isa.c
3  *
4  *  Copyright (C) 1999-2000 Russell King
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License version 2 as
8  * published by the Free Software Foundation.
9  *
10  *  ISA DMA primitives
11  *  Taken from various sources, including:
12  *   linux/include/asm/dma.h: Defines for using and allocating dma channels.
13  *     Written by Hennus Bergman, 1992.
14  *     High DMA channel support & info by Hannu Savolainen and John Boyd,
15  *     Nov. 1992.
16  *   arch/arm/kernel/dma-ebsa285.c
17  *   Copyright (C) 1998 Phil Blundell
18  */
19 #include <linux/ioport.h>
20 #include <linux/init.h>
21 #include <linux/dma-mapping.h>
22 #include <linux/io.h>
23 
24 #include <asm/dma.h>
25 #include <asm/mach/dma.h>
26 
27 #define ISA_DMA_MODE_READ	0x44
28 #define ISA_DMA_MODE_WRITE	0x48
29 #define ISA_DMA_MODE_CASCADE	0xc0
30 #define ISA_DMA_AUTOINIT	0x10
31 
32 #define ISA_DMA_MASK		0
33 #define ISA_DMA_MODE		1
34 #define ISA_DMA_CLRFF		2
35 #define ISA_DMA_PGHI		3
36 #define ISA_DMA_PGLO		4
37 #define ISA_DMA_ADDR		5
38 #define ISA_DMA_COUNT		6
39 
40 static unsigned int isa_dma_port[8][7] = {
41 	/* MASK   MODE   CLRFF  PAGE_HI PAGE_LO ADDR COUNT */
42 	{  0x0a,  0x0b,  0x0c,  0x487,  0x087,  0x00, 0x01 },
43 	{  0x0a,  0x0b,  0x0c,  0x483,  0x083,  0x02, 0x03 },
44 	{  0x0a,  0x0b,  0x0c,  0x481,  0x081,  0x04, 0x05 },
45 	{  0x0a,  0x0b,  0x0c,  0x482,  0x082,  0x06, 0x07 },
46 	{  0xd4,  0xd6,  0xd8,  0x000,  0x000,  0xc0, 0xc2 },
47 	{  0xd4,  0xd6,  0xd8,  0x48b,  0x08b,  0xc4, 0xc6 },
48 	{  0xd4,  0xd6,  0xd8,  0x489,  0x089,  0xc8, 0xca },
49 	{  0xd4,  0xd6,  0xd8,  0x48a,  0x08a,  0xcc, 0xce }
50 };
51 
isa_get_dma_residue(dmach_t channel,dma_t * dma)52 static int isa_get_dma_residue(dmach_t channel, dma_t *dma)
53 {
54 	unsigned int io_port = isa_dma_port[channel][ISA_DMA_COUNT];
55 	int count;
56 
57 	count = 1 + inb(io_port);
58 	count |= inb(io_port) << 8;
59 
60 	return channel < 4 ? count : (count << 1);
61 }
62 
isa_enable_dma(dmach_t channel,dma_t * dma)63 static void isa_enable_dma(dmach_t channel, dma_t *dma)
64 {
65 	if (dma->invalid) {
66 		unsigned long address, length;
67 		unsigned int mode;
68 		enum dma_data_direction direction;
69 
70 		mode = channel & 3;
71 		switch (dma->dma_mode & DMA_MODE_MASK) {
72 		case DMA_MODE_READ:
73 			mode |= ISA_DMA_MODE_READ;
74 			direction = DMA_FROM_DEVICE;
75 			break;
76 
77 		case DMA_MODE_WRITE:
78 			mode |= ISA_DMA_MODE_WRITE;
79 			direction = DMA_TO_DEVICE;
80 			break;
81 
82 		case DMA_MODE_CASCADE:
83 			mode |= ISA_DMA_MODE_CASCADE;
84 			direction = DMA_BIDIRECTIONAL;
85 			break;
86 
87 		default:
88 			direction = DMA_NONE;
89 			break;
90 		}
91 
92 		if (!dma->sg) {
93 			/*
94 			 * Cope with ISA-style drivers which expect cache
95 			 * coherence.
96 			 */
97 			dma->sg = &dma->buf;
98 			dma->sgcount = 1;
99 			dma->buf.length = dma->count;
100 			dma->buf.dma_address = dma_map_single(NULL,
101 				dma->addr, dma->count,
102 				direction);
103 		}
104 
105 		address = dma->buf.dma_address;
106 		length  = dma->buf.length - 1;
107 
108 		outb(address >> 16, isa_dma_port[channel][ISA_DMA_PGLO]);
109 		outb(address >> 24, isa_dma_port[channel][ISA_DMA_PGHI]);
110 
111 		if (channel >= 4) {
112 			address >>= 1;
113 			length >>= 1;
114 		}
115 
116 		outb(0, isa_dma_port[channel][ISA_DMA_CLRFF]);
117 
118 		outb(address, isa_dma_port[channel][ISA_DMA_ADDR]);
119 		outb(address >> 8, isa_dma_port[channel][ISA_DMA_ADDR]);
120 
121 		outb(length, isa_dma_port[channel][ISA_DMA_COUNT]);
122 		outb(length >> 8, isa_dma_port[channel][ISA_DMA_COUNT]);
123 
124 		if (dma->dma_mode & DMA_AUTOINIT)
125 			mode |= ISA_DMA_AUTOINIT;
126 
127 		outb(mode, isa_dma_port[channel][ISA_DMA_MODE]);
128 		dma->invalid = 0;
129 	}
130 	outb(channel & 3, isa_dma_port[channel][ISA_DMA_MASK]);
131 }
132 
isa_disable_dma(dmach_t channel,dma_t * dma)133 static void isa_disable_dma(dmach_t channel, dma_t *dma)
134 {
135 	outb(channel | 4, isa_dma_port[channel][ISA_DMA_MASK]);
136 }
137 
138 static struct dma_ops isa_dma_ops = {
139 	.type		= "ISA",
140 	.enable		= isa_enable_dma,
141 	.disable	= isa_disable_dma,
142 	.residue	= isa_get_dma_residue,
143 };
144 
145 static struct resource dma_resources[] = { {
146 	.name	= "dma1",
147 	.start	= 0x0000,
148 	.end	= 0x000f
149 }, {
150 	.name	= "dma low page",
151 	.start	= 0x0080,
152 	.end 	= 0x008f
153 }, {
154 	.name	= "dma2",
155 	.start	= 0x00c0,
156 	.end	= 0x00df
157 }, {
158 	.name	= "dma high page",
159 	.start	= 0x0480,
160 	.end	= 0x048f
161 } };
162 
isa_init_dma(dma_t * dma)163 void __init isa_init_dma(dma_t *dma)
164 {
165 	/*
166 	 * Try to autodetect presence of an ISA DMA controller.
167 	 * We do some minimal initialisation, and check that
168 	 * channel 0's DMA address registers are writeable.
169 	 */
170 	outb(0xff, 0x0d);
171 	outb(0xff, 0xda);
172 
173 	/*
174 	 * Write high and low address, and then read them back
175 	 * in the same order.
176 	 */
177 	outb(0x55, 0x00);
178 	outb(0xaa, 0x00);
179 
180 	if (inb(0) == 0x55 && inb(0) == 0xaa) {
181 		int channel, i;
182 
183 		for (channel = 0; channel < 8; channel++) {
184 			dma[channel].d_ops = &isa_dma_ops;
185 			isa_disable_dma(channel, NULL);
186 		}
187 
188 		outb(0x40, 0x0b);
189 		outb(0x41, 0x0b);
190 		outb(0x42, 0x0b);
191 		outb(0x43, 0x0b);
192 
193 		outb(0xc0, 0xd6);
194 		outb(0x41, 0xd6);
195 		outb(0x42, 0xd6);
196 		outb(0x43, 0xd6);
197 
198 		outb(0, 0xd4);
199 
200 		outb(0x10, 0x08);
201 		outb(0x10, 0xd0);
202 
203 		/*
204 		 * Is this correct?  According to my documentation, it
205 		 * doesn't appear to be.  It should be:
206 		 *  outb(0x3f, 0x40b); outb(0x3f, 0x4d6);
207 		 */
208 		outb(0x30, 0x40b);
209 		outb(0x31, 0x40b);
210 		outb(0x32, 0x40b);
211 		outb(0x33, 0x40b);
212 		outb(0x31, 0x4d6);
213 		outb(0x32, 0x4d6);
214 		outb(0x33, 0x4d6);
215 
216 		request_dma(DMA_ISA_CASCADE, "cascade");
217 
218 		for (i = 0; i < ARRAY_SIZE(dma_resources); i++)
219 			request_resource(&ioport_resource, dma_resources + i);
220 	}
221 }
222