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