Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

Manuel Lauss and committed by
Paul Mundt
fc467a26 57be2b48

+237 -9
+2 -1
arch/sh/drivers/Makefile
··· 2 2 # Makefile for the Linux SuperH-specific device drivers. 3 3 # 4 4 5 + obj-y += dma/ 6 + 5 7 obj-$(CONFIG_PCI) += pci/ 6 - obj-$(CONFIG_SH_DMA) += dma/ 7 8 obj-$(CONFIG_SUPERHYWAY) += superhyway/ 8 9 obj-$(CONFIG_PUSH_SWITCH) += push-switch.o 9 10 obj-$(CONFIG_HEARTBEAT) += heartbeat.o
+14 -6
arch/sh/drivers/dma/Kconfig
··· 1 1 menu "DMA support" 2 2 3 - config SH_DMA 4 - bool "DMA controller (DMAC) support" 5 - help 6 - Selecting this option will provide same API as PC's Direct Memory 7 - Access Controller(8237A) for SuperH DMAC. 3 + config SH_DMA_API 4 + bool 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 11 config NR_ONCHIP_DMA_CHANNELS 12 12 depends on SH_DMA ··· 52 52 This allows the specification of the dual address dma channel, 53 53 in case channel 3 is unavailable. On the SH4, channels 1,2, and 3 54 54 are dual-address capable. 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. 55 63 56 64 endmenu
+2 -2
arch/sh/drivers/dma/Makefile
··· 2 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 6 obj-$(CONFIG_ISA_DMA_API) += dma-isa.o 7 - obj-$(CONFIG_SYSFS) += dma-sysfs.o 8 7 obj-$(CONFIG_SH_DMA) += dma-sh.o 9 8 obj-$(CONFIG_SH_DREAMCAST) += dma-pvr2.o dma-g2.o 9 + obj-$(CONFIG_SH_DMABRG) += dmabrg.o
+196
arch/sh/drivers/dma/dmabrg.c
··· 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);
+23
include/asm-sh/dmabrg.h
··· 1 + /* 2 + * SH7760 DMABRG (USB/Audio) support 3 + */ 4 + 5 + #ifndef _DMABRG_H_ 6 + #define _DMABRG_H_ 7 + 8 + /* IRQ sources */ 9 + #define DMABRGIRQ_USBDMA 0 10 + #define DMABRGIRQ_USBDMAERR 1 11 + #define DMABRGIRQ_A0TXF 2 12 + #define DMABRGIRQ_A0TXH 3 13 + #define DMABRGIRQ_A0RXF 4 14 + #define DMABRGIRQ_A0RXH 5 15 + #define DMABRGIRQ_A1TXF 6 16 + #define DMABRGIRQ_A1TXH 7 17 + #define DMABRGIRQ_A1RXF 8 18 + #define DMABRGIRQ_A1RXH 9 19 + 20 + extern int dmabrg_request_irq(unsigned int, void(*)(void *), void *); 21 + extern void dmabrg_free_irq(unsigned int); 22 + 23 + #endif