/*1* SH7760 DMABRG IRQ handling2*3* (c) 2007 MSC Vertriebsges.m.b.H, Manuel Lauss <[email protected]>4* licensed under the GPLv2.5*6*/78#include <linux/interrupt.h>9#include <linux/kernel.h>10#include <linux/slab.h>11#include <asm/dma.h>12#include <asm/dmabrg.h>13#include <asm/io.h>1415/*16* The DMABRG is a special DMA unit within the SH7760. It does transfers17* from USB-SRAM/Audio units to main memory (and also the LCDC; but that18* part is sensibly placed in the LCDC registers and requires no irqs)19* It has 3 IRQ lines which trigger 10 events, and works independently20* from the traditional SH DMAC (although it blocks usage of DMAC 0)21*22* BRGIRQID | component | dir | meaning | source23* -----------------------------------------------------24* 0 | USB-DMA | ... | xfer done | DMABRGI125* 1 | USB-UAE | ... | USB addr err.| DMABRGI026* 2 | HAC0/SSI0 | play| all done | DMABRGI127* 3 | HAC0/SSI0 | play| half done | DMABRGI228* 4 | HAC0/SSI0 | rec | all done | DMABRGI129* 5 | HAC0/SSI0 | rec | half done | DMABRGI230* 6 | HAC1/SSI1 | play| all done | DMABRGI131* 7 | HAC1/SSI1 | play| half done | DMABRGI232* 8 | HAC1/SSI1 | rec | all done | DMABRGI133* 9 | HAC1/SSI1 | rec | half done | DMABRGI234*35* all can be enabled/disabled in the DMABRGCR register,36* as well as checked if they occurred.37*38* DMABRGI0 services USB DMA Address errors, but it still must be39* enabled/acked in the DMABRGCR register. USB-DMA complete indicator40* 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*/5556#define DMARSRA 0xfe09000057#define DMAOR 0xffa0004058#define DMACHCR0 0xffa0000c59#define DMABRGCR 0xfe3c00006061#define DMAOR_BRG 0x0000c00062#define DMAOR_DMEN 0x000000016364#define DMABRGI0 6865#define DMABRGI1 6966#define DMABRGI2 706768struct dmabrg_handler {69void (*handler)(void *);70void *data;71} *dmabrg_handlers;7273static inline void dmabrg_call_handler(int i)74{75dmabrg_handlers[i].handler(dmabrg_handlers[i].data);76}7778/*79* main DMABRG irq handler. It acks irqs and then80* handles every set and unmasked bit sequentially.81* No locking and no validity checks; it should be82* as fast as possible (audio!)83*/84static irqreturn_t dmabrg_irq(int irq, void *data)85{86unsigned long dcr;87unsigned int i;8889dcr = __raw_readl(DMABRGCR);90__raw_writel(dcr & ~0x00ff0003, DMABRGCR); /* ack all */91dcr &= dcr >> 8; /* ignore masked */9293/* USB stuff, get it out of the way first */94if (dcr & 1)95dmabrg_call_handler(DMABRGIRQ_USBDMA);96if (dcr & 2)97dmabrg_call_handler(DMABRGIRQ_USBDMAERR);9899/* Audio */100dcr >>= 16;101while (dcr) {102i = __ffs(dcr);103dcr &= dcr - 1;104dmabrg_call_handler(i + DMABRGIRQ_A0TXF);105}106return IRQ_HANDLED;107}108109static void dmabrg_disable_irq(unsigned int dmairq)110{111unsigned long dcr;112dcr = __raw_readl(DMABRGCR);113dcr &= ~(1 << ((dmairq > 1) ? dmairq + 22 : dmairq + 8));114__raw_writel(dcr, DMABRGCR);115}116117static void dmabrg_enable_irq(unsigned int dmairq)118{119unsigned long dcr;120dcr = __raw_readl(DMABRGCR);121dcr |= (1 << ((dmairq > 1) ? dmairq + 22 : dmairq + 8));122__raw_writel(dcr, DMABRGCR);123}124125int dmabrg_request_irq(unsigned int dmairq, void(*handler)(void*),126void *data)127{128if ((dmairq > 9) || !handler)129return -ENOENT;130if (dmabrg_handlers[dmairq].handler)131return -EBUSY;132133dmabrg_handlers[dmairq].handler = handler;134dmabrg_handlers[dmairq].data = data;135136dmabrg_enable_irq(dmairq);137return 0;138}139EXPORT_SYMBOL_GPL(dmabrg_request_irq);140141void dmabrg_free_irq(unsigned int dmairq)142{143if (likely(dmairq < 10)) {144dmabrg_disable_irq(dmairq);145dmabrg_handlers[dmairq].handler = NULL;146dmabrg_handlers[dmairq].data = NULL;147}148}149EXPORT_SYMBOL_GPL(dmabrg_free_irq);150151static int __init dmabrg_init(void)152{153unsigned long or;154int ret;155156dmabrg_handlers = kzalloc(10 * sizeof(struct dmabrg_handler),157GFP_KERNEL);158if (!dmabrg_handlers)159return -ENOMEM;160161#ifdef CONFIG_SH_DMA162/* request DMAC channel 0 before anyone else can get it */163ret = request_dma(0, "DMAC 0 (DMABRG)");164if (ret < 0)165printk(KERN_INFO "DMABRG: DMAC ch0 not reserved!\n");166#endif167168__raw_writel(0, DMABRGCR);169__raw_writel(0, DMACHCR0);170__raw_writel(0x94000000, DMARSRA); /* enable DMABRG in DMAC 0 */171172/* enable DMABRG mode, enable the DMAC */173or = __raw_readl(DMAOR);174__raw_writel(or | DMAOR_BRG | DMAOR_DMEN, DMAOR);175176ret = request_irq(DMABRGI0, dmabrg_irq, IRQF_DISABLED,177"DMABRG USB address error", NULL);178if (ret)179goto out0;180181ret = request_irq(DMABRGI1, dmabrg_irq, IRQF_DISABLED,182"DMABRG Transfer End", NULL);183if (ret)184goto out1;185186ret = request_irq(DMABRGI2, dmabrg_irq, IRQF_DISABLED,187"DMABRG Transfer Half", NULL);188if (ret == 0)189return ret;190191free_irq(DMABRGI1, 0);192out1: free_irq(DMABRGI0, 0);193out0: kfree(dmabrg_handlers);194return ret;195}196subsys_initcall(dmabrg_init);197198199