PageRenderTime 43ms CodeModel.GetById 14ms app.highlight 24ms RepoModel.GetById 1ms app.codeStats 0ms

/arch/arm/mach-rpc/dma.c

https://bitbucket.org/evzijst/gittest
C | 338 lines | 256 code | 60 blank | 22 comment | 32 complexity | 5b69be337b62ed969a342067c45289b8 MD5 | raw file
  1/*
  2 *  linux/arch/arm/mach-rpc/dma.c
  3 *
  4 *  Copyright (C) 1998 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 *  DMA functions specific to RiscPC architecture
 11 */
 12#include <linux/slab.h>
 13#include <linux/mman.h>
 14#include <linux/init.h>
 15#include <linux/interrupt.h>
 16#include <linux/pci.h>
 17
 18#include <asm/page.h>
 19#include <asm/dma.h>
 20#include <asm/fiq.h>
 21#include <asm/io.h>
 22#include <asm/irq.h>
 23#include <asm/hardware.h>
 24#include <asm/uaccess.h>
 25
 26#include <asm/mach/dma.h>
 27#include <asm/hardware/iomd.h>
 28
 29#if 0
 30typedef enum {
 31	dma_size_8	= 1,
 32	dma_size_16	= 2,
 33	dma_size_32	= 4,
 34	dma_size_128	= 16
 35} dma_size_t;
 36#endif
 37
 38#define TRANSFER_SIZE	2
 39
 40#define CURA	(0)
 41#define ENDA	(IOMD_IO0ENDA - IOMD_IO0CURA)
 42#define CURB	(IOMD_IO0CURB - IOMD_IO0CURA)
 43#define ENDB	(IOMD_IO0ENDB - IOMD_IO0CURA)
 44#define CR	(IOMD_IO0CR - IOMD_IO0CURA)
 45#define ST	(IOMD_IO0ST - IOMD_IO0CURA)
 46
 47static void iomd_get_next_sg(struct scatterlist *sg, dma_t *dma)
 48{
 49	unsigned long end, offset, flags = 0;
 50
 51	if (dma->sg) {
 52		sg->dma_address = dma->sg->dma_address;
 53		offset = sg->dma_address & ~PAGE_MASK;
 54
 55		end = offset + dma->sg->length;
 56
 57		if (end > PAGE_SIZE)
 58			end = PAGE_SIZE;
 59
 60		if (offset + TRANSFER_SIZE >= end)
 61			flags |= DMA_END_L;
 62
 63		sg->length = end - TRANSFER_SIZE;
 64
 65		dma->sg->length -= end - offset;
 66		dma->sg->dma_address += end - offset;
 67
 68		if (dma->sg->length == 0) {
 69			if (dma->sgcount > 1) {
 70				dma->sg++;
 71				dma->sgcount--;
 72			} else {
 73				dma->sg = NULL;
 74				flags |= DMA_END_S;
 75			}
 76		}
 77	} else {
 78		flags = DMA_END_S | DMA_END_L;
 79		sg->dma_address = 0;
 80		sg->length = 0;
 81	}
 82
 83	sg->length |= flags;
 84}
 85
 86static irqreturn_t iomd_dma_handle(int irq, void *dev_id, struct pt_regs *regs)
 87{
 88	dma_t *dma = (dma_t *)dev_id;
 89	unsigned long base = dma->dma_base;
 90
 91	do {
 92		unsigned int status;
 93
 94		status = iomd_readb(base + ST);
 95		if (!(status & DMA_ST_INT))
 96			return IRQ_HANDLED;
 97
 98		if ((dma->state ^ status) & DMA_ST_AB)
 99			iomd_get_next_sg(&dma->cur_sg, dma);
100
101		switch (status & (DMA_ST_OFL | DMA_ST_AB)) {
102		case DMA_ST_OFL:			/* OIA */
103		case DMA_ST_AB:				/* .IB */
104			iomd_writel(dma->cur_sg.dma_address, base + CURA);
105			iomd_writel(dma->cur_sg.length, base + ENDA);
106			dma->state = DMA_ST_AB;
107			break;
108
109		case DMA_ST_OFL | DMA_ST_AB:		/* OIB */
110		case 0:					/* .IA */
111			iomd_writel(dma->cur_sg.dma_address, base + CURB);
112			iomd_writel(dma->cur_sg.length, base + ENDB);
113			dma->state = 0;
114			break;
115		}
116
117		if (status & DMA_ST_OFL &&
118		    dma->cur_sg.length == (DMA_END_S|DMA_END_L))
119			break;
120	} while (1);
121
122	dma->state = ~DMA_ST_AB;
123	disable_irq(irq);
124
125	return IRQ_HANDLED;
126}
127
128static int iomd_request_dma(dmach_t channel, dma_t *dma)
129{
130	return request_irq(dma->dma_irq, iomd_dma_handle,
131			   SA_INTERRUPT, dma->device_id, dma);
132}
133
134static void iomd_free_dma(dmach_t channel, dma_t *dma)
135{
136	free_irq(dma->dma_irq, dma);
137}
138
139static void iomd_enable_dma(dmach_t channel, dma_t *dma)
140{
141	unsigned long dma_base = dma->dma_base;
142	unsigned int ctrl = TRANSFER_SIZE | DMA_CR_E;
143
144	if (dma->invalid) {
145		dma->invalid = 0;
146
147		/*
148		 * Cope with ISA-style drivers which expect cache
149		 * coherence.
150		 */
151		if (!dma->using_sg) {
152			dma->buf.dma_address = pci_map_single(NULL,
153				dma->buf.__address, dma->buf.length,
154				dma->dma_mode == DMA_MODE_READ ?
155				PCI_DMA_FROMDEVICE : PCI_DMA_TODEVICE);
156		}
157
158		iomd_writeb(DMA_CR_C, dma_base + CR);
159		dma->state = DMA_ST_AB;
160	}
161		
162	if (dma->dma_mode == DMA_MODE_READ)
163		ctrl |= DMA_CR_D;
164
165	iomd_writeb(ctrl, dma_base + CR);
166	enable_irq(dma->dma_irq);
167}
168
169static void iomd_disable_dma(dmach_t channel, dma_t *dma)
170{
171	unsigned long dma_base = dma->dma_base;
172	unsigned long flags;
173
174	local_irq_save(flags);
175	if (dma->state != ~DMA_ST_AB)
176		disable_irq(dma->dma_irq);
177	iomd_writeb(0, dma_base + CR);
178	local_irq_restore(flags);
179}
180
181static int iomd_set_dma_speed(dmach_t channel, dma_t *dma, int cycle)
182{
183	int tcr, speed;
184
185	if (cycle < 188)
186		speed = 3;
187	else if (cycle <= 250)
188		speed = 2;
189	else if (cycle < 438)
190		speed = 1;
191	else
192		speed = 0;
193
194	tcr = iomd_readb(IOMD_DMATCR);
195	speed &= 3;
196
197	switch (channel) {
198	case DMA_0:
199		tcr = (tcr & ~0x03) | speed;
200		break;
201
202	case DMA_1:
203		tcr = (tcr & ~0x0c) | (speed << 2);
204		break;
205
206	case DMA_2:
207		tcr = (tcr & ~0x30) | (speed << 4);
208		break;
209
210	case DMA_3:
211		tcr = (tcr & ~0xc0) | (speed << 6);
212		break;
213
214	default:
215		break;
216	}
217
218	iomd_writeb(tcr, IOMD_DMATCR);
219
220	return speed;
221}
222
223static struct dma_ops iomd_dma_ops = {
224	.type		= "IOMD",
225	.request	= iomd_request_dma,
226	.free		= iomd_free_dma,
227	.enable		= iomd_enable_dma,
228	.disable	= iomd_disable_dma,
229	.setspeed	= iomd_set_dma_speed,
230};
231
232static struct fiq_handler fh = {
233	.name	= "floppydma"
234};
235
236static void floppy_enable_dma(dmach_t channel, dma_t *dma)
237{
238	void *fiqhandler_start;
239	unsigned int fiqhandler_length;
240	struct pt_regs regs;
241
242	if (dma->using_sg)
243		BUG();
244
245	if (dma->dma_mode == DMA_MODE_READ) {
246		extern unsigned char floppy_fiqin_start, floppy_fiqin_end;
247		fiqhandler_start = &floppy_fiqin_start;
248		fiqhandler_length = &floppy_fiqin_end - &floppy_fiqin_start;
249	} else {
250		extern unsigned char floppy_fiqout_start, floppy_fiqout_end;
251		fiqhandler_start = &floppy_fiqout_start;
252		fiqhandler_length = &floppy_fiqout_end - &floppy_fiqout_start;
253	}
254
255	regs.ARM_r9  = dma->buf.length;
256	regs.ARM_r10 = (unsigned long)dma->buf.__address;
257	regs.ARM_fp  = (unsigned long)FLOPPYDMA_BASE;
258
259	if (claim_fiq(&fh)) {
260		printk("floppydma: couldn't claim FIQ.\n");
261		return;
262	}
263
264	set_fiq_handler(fiqhandler_start, fiqhandler_length);
265	set_fiq_regs(&regs);
266	enable_fiq(dma->dma_irq);
267}
268
269static void floppy_disable_dma(dmach_t channel, dma_t *dma)
270{
271	disable_fiq(dma->dma_irq);
272	release_fiq(&fh);
273}
274
275static int floppy_get_residue(dmach_t channel, dma_t *dma)
276{
277	struct pt_regs regs;
278	get_fiq_regs(&regs);
279	return regs.ARM_r9;
280}
281
282static struct dma_ops floppy_dma_ops = {
283	.type		= "FIQDMA",
284	.enable		= floppy_enable_dma,
285	.disable	= floppy_disable_dma,
286	.residue	= floppy_get_residue,
287};
288
289/*
290 * This is virtual DMA - we don't need anything here.
291 */
292static void sound_enable_disable_dma(dmach_t channel, dma_t *dma)
293{
294}
295
296static struct dma_ops sound_dma_ops = {
297	.type		= "VIRTUAL",
298	.enable		= sound_enable_disable_dma,
299	.disable	= sound_enable_disable_dma,
300};
301
302void __init arch_dma_init(dma_t *dma)
303{
304	iomd_writeb(0, IOMD_IO0CR);
305	iomd_writeb(0, IOMD_IO1CR);
306	iomd_writeb(0, IOMD_IO2CR);
307	iomd_writeb(0, IOMD_IO3CR);
308
309	iomd_writeb(0xa0, IOMD_DMATCR);
310
311	dma[DMA_0].dma_base		= IOMD_IO0CURA;
312	dma[DMA_0].dma_irq		= IRQ_DMA0;
313	dma[DMA_0].d_ops		= &iomd_dma_ops;
314	dma[DMA_1].dma_base		= IOMD_IO1CURA;
315	dma[DMA_1].dma_irq		= IRQ_DMA1;
316	dma[DMA_1].d_ops		= &iomd_dma_ops;
317	dma[DMA_2].dma_base		= IOMD_IO2CURA;
318	dma[DMA_2].dma_irq		= IRQ_DMA2;
319	dma[DMA_2].d_ops		= &iomd_dma_ops;
320	dma[DMA_3].dma_base		= IOMD_IO3CURA;
321	dma[DMA_3].dma_irq		= IRQ_DMA3;
322	dma[DMA_3].d_ops		= &iomd_dma_ops;
323	dma[DMA_S0].dma_base		= IOMD_SD0CURA;
324	dma[DMA_S0].dma_irq		= IRQ_DMAS0;
325	dma[DMA_S0].d_ops		= &iomd_dma_ops;
326	dma[DMA_S1].dma_base		= IOMD_SD1CURA;
327	dma[DMA_S1].dma_irq		= IRQ_DMAS1;
328	dma[DMA_S1].d_ops		= &iomd_dma_ops;
329	dma[DMA_VIRTUAL_FLOPPY].dma_irq	= FIQ_FLOPPYDATA;
330	dma[DMA_VIRTUAL_FLOPPY].d_ops	= &floppy_dma_ops;
331	dma[DMA_VIRTUAL_SOUND].d_ops	= &sound_dma_ops;
332
333	/*
334	 * Setup DMA channels 2,3 to be for podules
335	 * and channels 0,1 for internal devices
336	 */
337	iomd_writeb(DMA_EXT_IO3|DMA_EXT_IO2, IOMD_DMAEXT);
338}