File: dma.c

package info (click to toggle)
kernel-source-2.4.14 2.4.14-1
  • links: PTS
  • area: main
  • in suites: woody
  • size: 139,160 kB
  • ctags: 428,423
  • sloc: ansic: 2,435,554; asm: 141,119; makefile: 8,258; sh: 3,099; perl: 2,561; yacc: 1,177; cpp: 755; tcl: 577; lex: 352; awk: 251; lisp: 218; sed: 72
file content (142 lines) | stat: -rw-r--r-- 3,522 bytes parent folder | download | duplicates (8)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
/*
 * arch/sh/kernel/dma.c
 *
 * Copyright (C) 2000 Takashi YOSHII
 *
 * PC like DMA API for SuperH's DMAC.
 */

#include <linux/config.h>
#include <linux/init.h>
#include <linux/irq.h>
#include <linux/interrupt.h>

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

static struct dma_info_t *dma_info[MAX_DMA_CHANNELS];
static struct dma_info_t *autoinit_info[SH_MAX_DMA_CHANNELS] = {0};
static spinlock_t  dma_spin_lock;

static unsigned int calc_chcr(struct dma_info_t *info)
{
	unsigned int chcr;

	chcr = ( info->mode & DMA_MODE_WRITE )? info->mode_write : info->mode_read;
	if( info->mode & DMA_AUTOINIT )
		chcr |= CHCR_IE;
	return chcr;
}

static __inline__ int ts_shift(unsigned long chcr)
{
	return ((int[]){3,0,1,2,5,0,0,0})[(chcr>>4)&0x000007];
}

static void dma_tei(int irq, void *dev_id, struct pt_regs *regs)
{
	int chan = irq - DMTE_IRQ[0];
	struct dma_info_t *info = autoinit_info[chan];

	if( info->mode & DMA_MODE_WRITE )
		ctrl_outl(info->mem_addr, SAR[info->chan]);
	else
		ctrl_outl(info->mem_addr, DAR[info->chan]);

	ctrl_outl(info->count>>ts_shift(calc_chcr(info)), DMATCR[info->chan]);
	ctrl_outl(ctrl_inl(CHCR[info->chan])&~CHCR_TE, CHCR[info->chan]);
}

static struct irqaction irq_tei = { dma_tei, SA_INTERRUPT, 0, "dma_tei", NULL, NULL};

void setup_dma(unsigned int dmanr, struct dma_info_t *info)
{
	make_ipr_irq(DMTE_IRQ[info->chan], DMA_IPR_ADDR, DMA_IPR_POS, DMA_PRIORITY);
	setup_irq(DMTE_IRQ[info->chan], &irq_tei);
	dma_info[dmanr] = info;
}

unsigned long claim_dma_lock(void)
{
	unsigned long flags;
	spin_lock_irqsave(&dma_spin_lock, flags);
	return flags;
}

void release_dma_lock(unsigned long flags)
{
	spin_unlock_irqrestore(&dma_spin_lock, flags);
}

void enable_dma(unsigned int dmanr)
{
	struct dma_info_t *info = dma_info[dmanr];
	ctrl_outl(calc_chcr(info)|CHCR_DE, CHCR[info->chan]);
}

void disable_dma(unsigned int dmanr)
{
	struct dma_info_t *info = dma_info[dmanr];
	ctrl_outl(calc_chcr(info)&~CHCR_DE, CHCR[info->chan]);
}

void set_dma_mode(unsigned int dmanr, char mode)
{
	struct dma_info_t *info = dma_info[dmanr];

	info->mode = mode;
	set_dma_addr(dmanr, info->mem_addr);
	set_dma_count(dmanr, info->count);
	autoinit_info[info->chan] = info;
}

void set_dma_addr(unsigned int dmanr, unsigned int a)
{
	struct dma_info_t *info = dma_info[dmanr];
	unsigned long sar, dar;

	info->mem_addr = a;
	sar = (info->mode & DMA_MODE_WRITE)? info->mem_addr: info->dev_addr;
	dar = (info->mode & DMA_MODE_WRITE)? info->dev_addr: info->mem_addr;
	ctrl_outl(sar, SAR[info->chan]);
	ctrl_outl(dar, DAR[info->chan]);
}

void set_dma_count(unsigned int dmanr, unsigned int count)
{
	struct dma_info_t *info = dma_info[dmanr];
	info->count = count;
	ctrl_outl(count>>ts_shift(calc_chcr(info)), DMATCR[info->chan]);
}

int get_dma_residue(unsigned int dmanr)
{
	struct dma_info_t *info = dma_info[dmanr];
	return ctrl_inl(DMATCR[info->chan])<<ts_shift(calc_chcr(info));
}

#if defined(__SH4__)
static void dma_err(int irq, void *dev_id, struct pt_regs *regs)
{
	printk(KERN_WARNING "DMAE: DMAOR=%lx\n",ctrl_inl(DMAOR));
	ctrl_outl(ctrl_inl(DMAOR)&~DMAOR_NMIF, DMAOR);
	ctrl_outl(ctrl_inl(DMAOR)&~DMAOR_AE, DMAOR);
	ctrl_outl(ctrl_inl(DMAOR)|DMAOR_DME, DMAOR);
}
static struct irqaction irq_err = { dma_err, SA_INTERRUPT, 0, "dma_err", NULL, NULL};
#endif

int __init init_dma(void)
{
#if defined(__SH4__)
	make_ipr_irq(DMAE_IRQ, DMA_IPR_ADDR, DMA_IPR_POS, DMA_PRIORITY);
	setup_irq(DMAE_IRQ, &irq_err);
#endif

	ctrl_outl(DMAOR_DME, DMAOR);
	return 0;
}

module_init(init_dma);

/**/