Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
awilliam
GitHub Repository: awilliam/linux-vfio
Path: blob/master/arch/unicore32/kernel/dma.c
10817 views
1
/*
2
* linux/arch/unicore32/kernel/dma.c
3
*
4
* Code specific to PKUnity SoC and UniCore ISA
5
*
6
* Maintained by GUAN Xue-tao <[email protected]>
7
* Copyright (C) 2001-2010 Guan Xuetao
8
*
9
* This program is free software; you can redistribute it and/or modify
10
* it under the terms of the GNU General Public License version 2 as
11
* published by the Free Software Foundation.
12
*/
13
14
#include <linux/module.h>
15
#include <linux/init.h>
16
#include <linux/kernel.h>
17
#include <linux/interrupt.h>
18
#include <linux/errno.h>
19
#include <linux/io.h>
20
21
#include <asm/system.h>
22
#include <asm/irq.h>
23
#include <mach/hardware.h>
24
#include <mach/dma.h>
25
26
struct dma_channel {
27
char *name;
28
puv3_dma_prio prio;
29
void (*irq_handler)(int, void *);
30
void (*err_handler)(int, void *);
31
void *data;
32
};
33
34
static struct dma_channel dma_channels[MAX_DMA_CHANNELS];
35
36
int puv3_request_dma(char *name, puv3_dma_prio prio,
37
void (*irq_handler)(int, void *),
38
void (*err_handler)(int, void *),
39
void *data)
40
{
41
unsigned long flags;
42
int i, found = 0;
43
44
/* basic sanity checks */
45
if (!name)
46
return -EINVAL;
47
48
local_irq_save(flags);
49
50
do {
51
/* try grabbing a DMA channel with the requested priority */
52
for (i = 0; i < MAX_DMA_CHANNELS; i++) {
53
if ((dma_channels[i].prio == prio) &&
54
!dma_channels[i].name) {
55
found = 1;
56
break;
57
}
58
}
59
/* if requested prio group is full, try a hier priority */
60
} while (!found && prio--);
61
62
if (found) {
63
dma_channels[i].name = name;
64
dma_channels[i].irq_handler = irq_handler;
65
dma_channels[i].err_handler = err_handler;
66
dma_channels[i].data = data;
67
} else {
68
printk(KERN_WARNING "No more available DMA channels for %s\n",
69
name);
70
i = -ENODEV;
71
}
72
73
local_irq_restore(flags);
74
return i;
75
}
76
EXPORT_SYMBOL(puv3_request_dma);
77
78
void puv3_free_dma(int dma_ch)
79
{
80
unsigned long flags;
81
82
if (!dma_channels[dma_ch].name) {
83
printk(KERN_CRIT
84
"%s: trying to free channel %d which is already freed\n",
85
__func__, dma_ch);
86
return;
87
}
88
89
local_irq_save(flags);
90
dma_channels[dma_ch].name = NULL;
91
dma_channels[dma_ch].err_handler = NULL;
92
local_irq_restore(flags);
93
}
94
EXPORT_SYMBOL(puv3_free_dma);
95
96
static irqreturn_t dma_irq_handler(int irq, void *dev_id)
97
{
98
int i, dint;
99
100
dint = readl(DMAC_ITCSR);
101
for (i = 0; i < MAX_DMA_CHANNELS; i++) {
102
if (dint & DMAC_CHANNEL(i)) {
103
struct dma_channel *channel = &dma_channels[i];
104
105
/* Clear TC interrupt of channel i */
106
writel(DMAC_CHANNEL(i), DMAC_ITCCR);
107
writel(0, DMAC_ITCCR);
108
109
if (channel->name && channel->irq_handler) {
110
channel->irq_handler(i, channel->data);
111
} else {
112
/*
113
* IRQ for an unregistered DMA channel:
114
* let's clear the interrupts and disable it.
115
*/
116
printk(KERN_WARNING "spurious IRQ for"
117
" DMA channel %d\n", i);
118
}
119
}
120
}
121
return IRQ_HANDLED;
122
}
123
124
static irqreturn_t dma_err_handler(int irq, void *dev_id)
125
{
126
int i, dint;
127
128
dint = readl(DMAC_IESR);
129
for (i = 0; i < MAX_DMA_CHANNELS; i++) {
130
if (dint & DMAC_CHANNEL(i)) {
131
struct dma_channel *channel = &dma_channels[i];
132
133
/* Clear Err interrupt of channel i */
134
writel(DMAC_CHANNEL(i), DMAC_IECR);
135
writel(0, DMAC_IECR);
136
137
if (channel->name && channel->err_handler) {
138
channel->err_handler(i, channel->data);
139
} else {
140
/*
141
* IRQ for an unregistered DMA channel:
142
* let's clear the interrupts and disable it.
143
*/
144
printk(KERN_WARNING "spurious IRQ for"
145
" DMA channel %d\n", i);
146
}
147
}
148
}
149
return IRQ_HANDLED;
150
}
151
152
int __init puv3_init_dma(void)
153
{
154
int i, ret;
155
156
/* dma channel priorities on v8 processors:
157
* ch 0 - 1 <--> (0) DMA_PRIO_HIGH
158
* ch 2 - 3 <--> (1) DMA_PRIO_MEDIUM
159
* ch 4 - 5 <--> (2) DMA_PRIO_LOW
160
*/
161
for (i = 0; i < MAX_DMA_CHANNELS; i++) {
162
puv3_stop_dma(i);
163
dma_channels[i].name = NULL;
164
dma_channels[i].prio = min((i & 0x7) >> 1, DMA_PRIO_LOW);
165
}
166
167
ret = request_irq(IRQ_DMA, dma_irq_handler, 0, "DMA", NULL);
168
if (ret) {
169
printk(KERN_CRIT "Can't register IRQ for DMA\n");
170
return ret;
171
}
172
173
ret = request_irq(IRQ_DMAERR, dma_err_handler, 0, "DMAERR", NULL);
174
if (ret) {
175
printk(KERN_CRIT "Can't register IRQ for DMAERR\n");
176
free_irq(IRQ_DMA, "DMA");
177
return ret;
178
}
179
180
return 0;
181
}
182
183
postcore_initcall(puv3_init_dma);
184
185