aboutsummaryrefslogtreecommitdiffstats
path: root/arch
diff options
context:
space:
mode:
authorManuel Lauss <mano@roarinelk.homelinux.net>2007-05-09 04:36:15 -0400
committerPaul Mundt <lethal@linux-sh.org>2007-05-09 04:36:15 -0400
commitfc467a2623029976899261d6d379779c950ddcba (patch)
tree16b1721c800bd907207723f8031fb8601811b584 /arch
parent57be2b484a417bffae66359b9b89e7239480b729 (diff)
sh: SH7760 DMABRG support.
The DMABRG is a special DMA unit within the SH7760 which does data transfers from main memory to Audio units and USB shared memory. It has 3 IRQ lines which generate 10 events, which have to be masked unmasked and acked in a single 32bit register. It works independently from the tradition SH DMAC, but blocks usage of DMAC channel 0. This patch adds 2 functions to associate callbacks with DMABRG events and initialization. Signed-off-by: Manuel Lauss <mano@roarinelk.homelinux.net> Signed-off-by: Paul Mundt <lethal@linux-sh.org>
Diffstat (limited to 'arch')
-rw-r--r--arch/sh/drivers/Makefile3
-rw-r--r--arch/sh/drivers/dma/Kconfig20
-rw-r--r--arch/sh/drivers/dma/Makefile4
-rw-r--r--arch/sh/drivers/dma/dmabrg.c196
4 files changed, 214 insertions, 9 deletions
diff --git a/arch/sh/drivers/Makefile b/arch/sh/drivers/Makefile
index 6cb92676c5fc..e13f06bebd92 100644
--- a/arch/sh/drivers/Makefile
+++ b/arch/sh/drivers/Makefile
@@ -2,8 +2,9 @@
2# Makefile for the Linux SuperH-specific device drivers. 2# Makefile for the Linux SuperH-specific device drivers.
3# 3#
4 4
5obj-y += dma/
6
5obj-$(CONFIG_PCI) += pci/ 7obj-$(CONFIG_PCI) += pci/
6obj-$(CONFIG_SH_DMA) += dma/
7obj-$(CONFIG_SUPERHYWAY) += superhyway/ 8obj-$(CONFIG_SUPERHYWAY) += superhyway/
8obj-$(CONFIG_PUSH_SWITCH) += push-switch.o 9obj-$(CONFIG_PUSH_SWITCH) += push-switch.o
9obj-$(CONFIG_HEARTBEAT) += heartbeat.o 10obj-$(CONFIG_HEARTBEAT) += heartbeat.o
diff --git a/arch/sh/drivers/dma/Kconfig b/arch/sh/drivers/dma/Kconfig
index defc13c37d48..99935f9daf4b 100644
--- a/arch/sh/drivers/dma/Kconfig
+++ b/arch/sh/drivers/dma/Kconfig
@@ -1,12 +1,12 @@
1menu "DMA support" 1menu "DMA support"
2 2
3config SH_DMA 3config SH_DMA_API
4 bool "DMA controller (DMAC) support" 4 bool
5 help
6 Selecting this option will provide same API as PC's Direct Memory
7 Access Controller(8237A) for SuperH DMAC.
8 5
9 If unsure, say N. 6config SH_DMA
7 bool "SuperH on-chip DMA controller (DMAC) support"
8 select SH_DMA_API
9 default n
10 10
11config NR_ONCHIP_DMA_CHANNELS 11config NR_ONCHIP_DMA_CHANNELS
12 depends on SH_DMA 12 depends on SH_DMA
@@ -53,4 +53,12 @@ config DMA_PAGE_OPS_CHANNEL
53 in case channel 3 is unavailable. On the SH4, channels 1,2, and 3 53 in case channel 3 is unavailable. On the SH4, channels 1,2, and 3
54 are dual-address capable. 54 are dual-address capable.
55 55
56config SH_DMABRG
57 bool "SH7760 DMABRG support"
58 depends on CPU_SUBTYPE_SH7760
59 help
60 The DMABRG does data transfers from main memory to Audio/USB units
61 of the SH7760.
62 Say Y if you want to use Audio/USB DMA on your SH7760 board.
63
56endmenu 64endmenu
diff --git a/arch/sh/drivers/dma/Makefile b/arch/sh/drivers/dma/Makefile
index db1295d32268..1ac812d24488 100644
--- a/arch/sh/drivers/dma/Makefile
+++ b/arch/sh/drivers/dma/Makefile
@@ -2,8 +2,8 @@
2# Makefile for the SuperH DMA specific kernel interface routines under Linux. 2# Makefile for the SuperH DMA specific kernel interface routines under Linux.
3# 3#
4 4
5obj-y += dma-api.o 5obj-$(CONFIG_SH_DMA_API) += dma-api.o dma-sysfs.o
6obj-$(CONFIG_ISA_DMA_API) += dma-isa.o 6obj-$(CONFIG_ISA_DMA_API) += dma-isa.o
7obj-$(CONFIG_SYSFS) += dma-sysfs.o
8obj-$(CONFIG_SH_DMA) += dma-sh.o 7obj-$(CONFIG_SH_DMA) += dma-sh.o
9obj-$(CONFIG_SH_DREAMCAST) += dma-pvr2.o dma-g2.o 8obj-$(CONFIG_SH_DREAMCAST) += dma-pvr2.o dma-g2.o
9obj-$(CONFIG_SH_DMABRG) += dmabrg.o
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
68struct dmabrg_handler {
69 void (*handler)(void *);
70 void *data;
71} *dmabrg_handlers;
72
73static 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 */
84static 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
109static 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
117static 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
125int 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}
139EXPORT_SYMBOL_GPL(dmabrg_request_irq);
140
141void 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}
149EXPORT_SYMBOL_GPL(dmabrg_free_irq);
150
151static 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);
192out1: free_irq(DMABRGI0, 0);
193out0: kfree(dmabrg_handlers);
194 return ret;
195}
196subsys_initcall(dmabrg_init);