diff options
author | Manuel Lauss <mano@roarinelk.homelinux.net> | 2007-05-09 04:36:15 -0400 |
---|---|---|
committer | Paul Mundt <lethal@linux-sh.org> | 2007-05-09 04:36:15 -0400 |
commit | fc467a2623029976899261d6d379779c950ddcba (patch) | |
tree | 16b1721c800bd907207723f8031fb8601811b584 /arch | |
parent | 57be2b484a417bffae66359b9b89e7239480b729 (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/Makefile | 3 | ||||
-rw-r--r-- | arch/sh/drivers/dma/Kconfig | 20 | ||||
-rw-r--r-- | arch/sh/drivers/dma/Makefile | 4 | ||||
-rw-r--r-- | arch/sh/drivers/dma/dmabrg.c | 196 |
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 | ||
5 | obj-y += dma/ | ||
6 | |||
5 | obj-$(CONFIG_PCI) += pci/ | 7 | obj-$(CONFIG_PCI) += pci/ |
6 | obj-$(CONFIG_SH_DMA) += dma/ | ||
7 | obj-$(CONFIG_SUPERHYWAY) += superhyway/ | 8 | obj-$(CONFIG_SUPERHYWAY) += superhyway/ |
8 | obj-$(CONFIG_PUSH_SWITCH) += push-switch.o | 9 | obj-$(CONFIG_PUSH_SWITCH) += push-switch.o |
9 | obj-$(CONFIG_HEARTBEAT) += heartbeat.o | 10 | obj-$(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 @@ | |||
1 | menu "DMA support" | 1 | menu "DMA support" |
2 | 2 | ||
3 | config SH_DMA | 3 | config 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. | 6 | config SH_DMA |
7 | bool "SuperH on-chip DMA controller (DMAC) support" | ||
8 | select SH_DMA_API | ||
9 | default n | ||
10 | 10 | ||
11 | config NR_ONCHIP_DMA_CHANNELS | 11 | config 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 | ||
56 | config 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 | |||
56 | endmenu | 64 | endmenu |
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 | ||
5 | obj-y += dma-api.o | 5 | obj-$(CONFIG_SH_DMA_API) += dma-api.o dma-sysfs.o |
6 | obj-$(CONFIG_ISA_DMA_API) += dma-isa.o | 6 | obj-$(CONFIG_ISA_DMA_API) += dma-isa.o |
7 | obj-$(CONFIG_SYSFS) += dma-sysfs.o | ||
8 | obj-$(CONFIG_SH_DMA) += dma-sh.o | 7 | obj-$(CONFIG_SH_DMA) += dma-sh.o |
9 | obj-$(CONFIG_SH_DREAMCAST) += dma-pvr2.o dma-g2.o | 8 | obj-$(CONFIG_SH_DREAMCAST) += dma-pvr2.o dma-g2.o |
9 | obj-$(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 | |||
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); | ||