dma-isa.c 5.0 KB
Newer Older
L
Linus Torvalds 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
/*
 *  linux/arch/arm/kernel/dma-isa.c
 *
 *  Copyright (C) 1999-2000 Russell King
 *
 * 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.
 *
 *  ISA DMA primitives
 *  Taken from various sources, including:
 *   linux/include/asm/dma.h: Defines for using and allocating dma channels.
 *     Written by Hennus Bergman, 1992.
 *     High DMA channel support & info by Hannu Savolainen and John Boyd,
 *     Nov. 1992.
 *   arch/arm/kernel/dma-ebsa285.c
 *   Copyright (C) 1998 Phil Blundell
 */
#include <linux/ioport.h>
#include <linux/init.h>
21
#include <linux/dma-mapping.h>
22
#include <linux/io.h>
L
Linus Torvalds 已提交
23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46

#include <asm/dma.h>
#include <asm/mach/dma.h>

#define ISA_DMA_MASK		0
#define ISA_DMA_MODE		1
#define ISA_DMA_CLRFF		2
#define ISA_DMA_PGHI		3
#define ISA_DMA_PGLO		4
#define ISA_DMA_ADDR		5
#define ISA_DMA_COUNT		6

static unsigned int isa_dma_port[8][7] = {
	/* MASK   MODE   CLRFF  PAGE_HI PAGE_LO ADDR COUNT */
	{  0x0a,  0x0b,  0x0c,  0x487,  0x087,  0x00, 0x01 },
	{  0x0a,  0x0b,  0x0c,  0x483,  0x083,  0x02, 0x03 },
	{  0x0a,  0x0b,  0x0c,  0x481,  0x081,  0x04, 0x05 },
	{  0x0a,  0x0b,  0x0c,  0x482,  0x082,  0x06, 0x07 },
	{  0xd4,  0xd6,  0xd8,  0x000,  0x000,  0xc0, 0xc2 },
	{  0xd4,  0xd6,  0xd8,  0x48b,  0x08b,  0xc4, 0xc6 },
	{  0xd4,  0xd6,  0xd8,  0x489,  0x089,  0xc8, 0xca },
	{  0xd4,  0xd6,  0xd8,  0x48a,  0x08a,  0xcc, 0xce }
};

R
Russell King 已提交
47
static int isa_get_dma_residue(unsigned int chan, dma_t *dma)
L
Linus Torvalds 已提交
48
{
R
Russell King 已提交
49
	unsigned int io_port = isa_dma_port[chan][ISA_DMA_COUNT];
L
Linus Torvalds 已提交
50 51 52 53 54
	int count;

	count = 1 + inb(io_port);
	count |= inb(io_port) << 8;

R
Russell King 已提交
55
	return chan < 4 ? count : (count << 1);
L
Linus Torvalds 已提交
56 57
}

R
Russell King 已提交
58
static void isa_enable_dma(unsigned int chan, dma_t *dma)
L
Linus Torvalds 已提交
59 60 61
{
	if (dma->invalid) {
		unsigned long address, length;
62 63
		unsigned int mode;
		enum dma_data_direction direction;
L
Linus Torvalds 已提交
64

65
		mode = (chan & 3) | dma->dma_mode;
L
Linus Torvalds 已提交
66 67
		switch (dma->dma_mode & DMA_MODE_MASK) {
		case DMA_MODE_READ:
68
			direction = DMA_FROM_DEVICE;
L
Linus Torvalds 已提交
69 70 71
			break;

		case DMA_MODE_WRITE:
72
			direction = DMA_TO_DEVICE;
L
Linus Torvalds 已提交
73 74 75
			break;

		case DMA_MODE_CASCADE:
76
			direction = DMA_BIDIRECTIONAL;
L
Linus Torvalds 已提交
77 78 79
			break;

		default:
80
			direction = DMA_NONE;
L
Linus Torvalds 已提交
81 82 83
			break;
		}

84
		if (!dma->sg) {
L
Linus Torvalds 已提交
85 86 87 88
			/*
			 * Cope with ISA-style drivers which expect cache
			 * coherence.
			 */
89 90 91 92 93
			dma->sg = &dma->buf;
			dma->sgcount = 1;
			dma->buf.length = dma->count;
			dma->buf.dma_address = dma_map_single(NULL,
				dma->addr, dma->count,
L
Linus Torvalds 已提交
94 95 96 97 98 99
				direction);
		}

		address = dma->buf.dma_address;
		length  = dma->buf.length - 1;

R
Russell King 已提交
100 101
		outb(address >> 16, isa_dma_port[chan][ISA_DMA_PGLO]);
		outb(address >> 24, isa_dma_port[chan][ISA_DMA_PGHI]);
L
Linus Torvalds 已提交
102

R
Russell King 已提交
103
		if (chan >= 4) {
L
Linus Torvalds 已提交
104 105 106 107
			address >>= 1;
			length >>= 1;
		}

R
Russell King 已提交
108
		outb(0, isa_dma_port[chan][ISA_DMA_CLRFF]);
L
Linus Torvalds 已提交
109

R
Russell King 已提交
110 111
		outb(address, isa_dma_port[chan][ISA_DMA_ADDR]);
		outb(address >> 8, isa_dma_port[chan][ISA_DMA_ADDR]);
L
Linus Torvalds 已提交
112

R
Russell King 已提交
113 114
		outb(length, isa_dma_port[chan][ISA_DMA_COUNT]);
		outb(length >> 8, isa_dma_port[chan][ISA_DMA_COUNT]);
L
Linus Torvalds 已提交
115

R
Russell King 已提交
116
		outb(mode, isa_dma_port[chan][ISA_DMA_MODE]);
L
Linus Torvalds 已提交
117 118
		dma->invalid = 0;
	}
R
Russell King 已提交
119
	outb(chan & 3, isa_dma_port[chan][ISA_DMA_MASK]);
L
Linus Torvalds 已提交
120 121
}

R
Russell King 已提交
122
static void isa_disable_dma(unsigned int chan, dma_t *dma)
L
Linus Torvalds 已提交
123
{
R
Russell King 已提交
124
	outb(chan | 4, isa_dma_port[chan][ISA_DMA_MASK]);
L
Linus Torvalds 已提交
125 126 127 128 129 130 131 132 133
}

static struct dma_ops isa_dma_ops = {
	.type		= "ISA",
	.enable		= isa_enable_dma,
	.disable	= isa_disable_dma,
	.residue	= isa_get_dma_residue,
};

134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150
static struct resource dma_resources[] = { {
	.name	= "dma1",
	.start	= 0x0000,
	.end	= 0x000f
}, {
	.name	= "dma low page",
	.start	= 0x0080,
	.end 	= 0x008f
}, {
	.name	= "dma2",
	.start	= 0x00c0,
	.end	= 0x00df
}, {
	.name	= "dma high page",
	.start	= 0x0480,
	.end	= 0x048f
} };
L
Linus Torvalds 已提交
151

R
Russell King 已提交
152 153 154 155 156 157
static dma_t isa_dma[8];

/*
 * ISA DMA always starts at channel 0
 */
void __init isa_init_dma(void)
L
Linus Torvalds 已提交
158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174
{
	/*
	 * Try to autodetect presence of an ISA DMA controller.
	 * We do some minimal initialisation, and check that
	 * channel 0's DMA address registers are writeable.
	 */
	outb(0xff, 0x0d);
	outb(0xff, 0xda);

	/*
	 * Write high and low address, and then read them back
	 * in the same order.
	 */
	outb(0x55, 0x00);
	outb(0xaa, 0x00);

	if (inb(0) == 0x55 && inb(0) == 0xaa) {
R
Russell King 已提交
175
		unsigned int chan, i;
L
Linus Torvalds 已提交
176

R
Russell King 已提交
177
		for (chan = 0; chan < 8; chan++) {
R
Russell King 已提交
178
			isa_dma[chan].d_ops = &isa_dma_ops;
R
Russell King 已提交
179
			isa_disable_dma(chan, NULL);
L
Linus Torvalds 已提交
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
		}

		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");

212
		for (i = 0; i < ARRAY_SIZE(dma_resources); i++)
L
Linus Torvalds 已提交
213
			request_resource(&ioport_resource, dma_resources + i);
R
Russell King 已提交
214 215 216 217 218 219 220

		for (chan = 0; chan < 8; chan++) {
			int ret = isa_dma_add(chan, &isa_dma[chan]);
			if (ret)
				printk(KERN_ERR "ISADMA%u: unable to register: %d\n",
					chan, ret);
		}
L
Linus Torvalds 已提交
221 222
	}
}