diff options
Diffstat (limited to 'arch/sh/drivers/dma/dmabrg.c')
-rw-r--r-- | arch/sh/drivers/dma/dmabrg.c | 196 |
1 files changed, 196 insertions, 0 deletions
diff --git a/arch/sh/drivers/dma/dmabrg.c b/arch/sh/drivers/dma/dmabrg.c new file mode 100644 index 000000000000..9d0a29370f21 --- /dev/null +++ b/arch/sh/drivers/dma/dmabrg.c | |||
@@ -0,0 +1,196 @@ | |||
1 | /* | ||
2 | * SH7760 DMABRG IRQ handling | ||
3 | * | ||
4 | * (c) 2007 MSC Vertriebsges.m.b.H, Manuel Lauss <mlau@msc-ge.com> | ||
5 | * licensed under the GPLv2. | ||
6 | * | ||
7 | */ | ||
8 | |||
9 | #include <linux/interrupt.h> | ||
10 | #include <linux/kernel.h> | ||
11 | #include <asm/dma.h> | ||
12 | #include <asm/dmabrg.h> | ||
13 | #include <asm/io.h> | ||
14 | |||
15 | /* | ||
16 | * The DMABRG is a special DMA unit within the SH7760. It does transfers | ||
17 | * from USB-SRAM/Audio units to main memory (and also the LCDC; but that | ||
18 | * part is sensibly placed in the LCDC registers and requires no irqs) | ||
19 | * It has 3 IRQ lines which trigger 10 events, and works independently | ||
20 | * from the traditional SH DMAC (although it blocks usage of DMAC 0) | ||
21 | * | ||
22 | * BRGIRQID | component | dir | meaning | source | ||
23 | * ----------------------------------------------------- | ||
24 | * 0 | USB-DMA | ... | xfer done | DMABRGI1 | ||
25 | * 1 | USB-UAE | ... | USB addr err.| DMABRGI0 | ||
26 | * 2 | HAC0/SSI0 | play| all done | DMABRGI1 | ||
27 | * 3 | HAC0/SSI0 | play| half done | DMABRGI2 | ||
28 | * 4 | HAC0/SSI0 | rec | all done | DMABRGI1 | ||
29 | * 5 | HAC0/SSI0 | rec | half done | DMABRGI2 | ||
30 | * 6 | HAC1/SSI1 | play| all done | DMABRGI1 | ||
31 | * 7 | HAC1/SSI1 | play| half done | DMABRGI2 | ||
32 | * 8 | HAC1/SSI1 | rec | all done | DMABRGI1 | ||
33 | * 9 | HAC1/SSI1 | rec | half done | DMABRGI2 | ||
34 | * | ||
35 | * all can be enabled/disabled in the DMABRGCR register, | ||
36 | * as well as checked if they occured. | ||
37 | * | ||
38 | * DMABRGI0 services USB DMA Address errors, but it still must be | ||
39 | * enabled/acked in the DMABRGCR register. USB-DMA complete indicator | ||
40 | * is grouped together with the audio buffer end indicators, too bad... | ||
41 | * | ||
42 | * DMABRGCR: Bits 31-24: audio-dma ENABLE flags, | ||
43 | * Bits 23-16: audio-dma STATUS flags, | ||
44 | * Bits 9-8: USB error/xfer ENABLE, | ||
45 | * Bits 1-0: USB error/xfer STATUS. | ||
46 | * Ack an IRQ by writing 0 to the STATUS flag. | ||
47 | * Mask IRQ by writing 0 to ENABLE flag. | ||
48 | * | ||
49 | * Usage is almost like with any other IRQ: | ||
50 | * dmabrg_request_irq(BRGIRQID, handler, data) | ||
51 | * dmabrg_free_irq(BRGIRQID) | ||
52 | * | ||
53 | * handler prototype: void brgirqhandler(void *data) | ||
54 | */ | ||
55 | |||
56 | #define DMARSRA 0xfe090000 | ||
57 | #define DMAOR 0xffa00040 | ||
58 | #define DMACHCR0 0xffa0000c | ||
59 | #define DMABRGCR 0xfe3c0000 | ||
60 | |||
61 | #define DMAOR_BRG 0x0000c000 | ||
62 | #define DMAOR_DMEN 0x00000001 | ||
63 | |||
64 | #define DMABRGI0 68 | ||
65 | #define DMABRGI1 69 | ||
66 | #define DMABRGI2 70 | ||
67 | |||
68 | struct dmabrg_handler { | ||
69 | void (*handler)(void *); | ||
70 | void *data; | ||
71 | } *dmabrg_handlers; | ||
72 | |||
73 | static inline void dmabrg_call_handler(int i) | ||
74 | { | ||
75 | dmabrg_handlers[i].handler(dmabrg_handlers[i].data); | ||
76 | } | ||
77 | |||
78 | /* | ||
79 | * main DMABRG irq handler. It acks irqs and then | ||
80 | * handles every set and unmasked bit sequentially. | ||
81 | * No locking and no validity checks; it should be | ||
82 | * as fast as possible (audio!) | ||
83 | */ | ||
84 | static irqreturn_t dmabrg_irq(int irq, void *data) | ||
85 | { | ||
86 | unsigned long dcr; | ||
87 | unsigned int i; | ||
88 | |||
89 | dcr = ctrl_inl(DMABRGCR); | ||
90 | ctrl_outl(dcr & ~0x00ff0003, DMABRGCR); /* ack all */ | ||
91 | dcr &= dcr >> 8; /* ignore masked */ | ||
92 | |||
93 | /* USB stuff, get it out of the way first */ | ||
94 | if (dcr & 1) | ||
95 | dmabrg_call_handler(DMABRGIRQ_USBDMA); | ||
96 | if (dcr & 2) | ||
97 | dmabrg_call_handler(DMABRGIRQ_USBDMAERR); | ||
98 | |||
99 | /* Audio */ | ||
100 | dcr >>= 16; | ||
101 | while (dcr) { | ||
102 | i = __ffs(dcr); | ||
103 | dcr &= dcr - 1; | ||
104 | dmabrg_call_handler(i + DMABRGIRQ_A0TXF); | ||
105 | } | ||
106 | return IRQ_HANDLED; | ||
107 | } | ||
108 | |||
109 | static void dmabrg_disable_irq(unsigned int dmairq) | ||
110 | { | ||
111 | unsigned long dcr; | ||
112 | dcr = ctrl_inl(DMABRGCR); | ||
113 | dcr &= ~(1 << ((dmairq > 1) ? dmairq + 22 : dmairq + 8)); | ||
114 | ctrl_outl(dcr, DMABRGCR); | ||
115 | } | ||
116 | |||
117 | static void dmabrg_enable_irq(unsigned int dmairq) | ||
118 | { | ||
119 | unsigned long dcr; | ||
120 | dcr = ctrl_inl(DMABRGCR); | ||
121 | dcr |= (1 << ((dmairq > 1) ? dmairq + 22 : dmairq + 8)); | ||
122 | ctrl_outl(dcr, DMABRGCR); | ||
123 | } | ||
124 | |||
125 | int dmabrg_request_irq(unsigned int dmairq, void(*handler)(void*), | ||
126 | void *data) | ||
127 | { | ||
128 | if ((dmairq > 9) || !handler) | ||
129 | return -ENOENT; | ||
130 | if (dmabrg_handlers[dmairq].handler) | ||
131 | return -EBUSY; | ||
132 | |||
133 | dmabrg_handlers[dmairq].handler = handler; | ||
134 | dmabrg_handlers[dmairq].data = data; | ||
135 | |||
136 | dmabrg_enable_irq(dmairq); | ||
137 | return 0; | ||
138 | } | ||
139 | EXPORT_SYMBOL_GPL(dmabrg_request_irq); | ||
140 | |||
141 | void dmabrg_free_irq(unsigned int dmairq) | ||
142 | { | ||
143 | if (likely(dmairq < 10)) { | ||
144 | dmabrg_disable_irq(dmairq); | ||
145 | dmabrg_handlers[dmairq].handler = NULL; | ||
146 | dmabrg_handlers[dmairq].data = NULL; | ||
147 | } | ||
148 | } | ||
149 | EXPORT_SYMBOL_GPL(dmabrg_free_irq); | ||
150 | |||
151 | static int __init dmabrg_init(void) | ||
152 | { | ||
153 | unsigned long or; | ||
154 | int ret; | ||
155 | |||
156 | dmabrg_handlers = kzalloc(10 * sizeof(struct dmabrg_handler), | ||
157 | GFP_KERNEL); | ||
158 | if (!dmabrg_handlers) | ||
159 | return -ENOMEM; | ||
160 | |||
161 | #ifdef CONFIG_SH_DMA | ||
162 | /* request DMAC channel 0 before anyone else can get it */ | ||
163 | ret = request_dma(0, "DMAC 0 (DMABRG)"); | ||
164 | if (ret < 0) | ||
165 | printk(KERN_INFO "DMABRG: DMAC ch0 not reserved!\n"); | ||
166 | #endif | ||
167 | |||
168 | ctrl_outl(0, DMABRGCR); | ||
169 | ctrl_outl(0, DMACHCR0); | ||
170 | ctrl_outl(0x94000000, DMARSRA); /* enable DMABRG in DMAC 0 */ | ||
171 | |||
172 | /* enable DMABRG mode, enable the DMAC */ | ||
173 | or = ctrl_inl(DMAOR); | ||
174 | ctrl_outl(or | DMAOR_BRG | DMAOR_DMEN, DMAOR); | ||
175 | |||
176 | ret = request_irq(DMABRGI0, dmabrg_irq, IRQF_DISABLED, | ||
177 | "DMABRG USB address error", NULL); | ||
178 | if (ret) | ||
179 | goto out0; | ||
180 | |||
181 | ret = request_irq(DMABRGI1, dmabrg_irq, IRQF_DISABLED, | ||
182 | "DMABRG Transfer End", NULL); | ||
183 | if (ret) | ||
184 | goto out1; | ||
185 | |||
186 | ret = request_irq(DMABRGI2, dmabrg_irq, IRQF_DISABLED, | ||
187 | "DMABRG Transfer Half", NULL); | ||
188 | if (ret == 0) | ||
189 | return ret; | ||
190 | |||
191 | free_irq(DMABRGI1, 0); | ||
192 | out1: free_irq(DMABRGI0, 0); | ||
193 | out0: kfree(dmabrg_handlers); | ||
194 | return ret; | ||
195 | } | ||
196 | subsys_initcall(dmabrg_init); | ||