linux/arch/arm/mach-pxa/dma.c
<<
>>
Prefs
   1/*
   2 *  linux/arch/arm/mach-pxa/dma.c
   3 *
   4 *  PXA DMA registration and IRQ dispatching
   5 *
   6 *  Author:     Nicolas Pitre
   7 *  Created:    Nov 15, 2001
   8 *  Copyright:  MontaVista Software Inc.
   9 *
  10 *  This program is free software; you can redistribute it and/or modify
  11 *  it under the terms of the GNU General Public License version 2 as
  12 *  published by the Free Software Foundation.
  13 */
  14
  15#include <linux/module.h>
  16#include <linux/init.h>
  17#include <linux/kernel.h>
  18#include <linux/interrupt.h>
  19#include <linux/errno.h>
  20
  21#include <asm/system.h>
  22#include <asm/irq.h>
  23#include <mach/hardware.h>
  24#include <asm/dma.h>
  25
  26#include <mach/pxa-regs.h>
  27
  28struct dma_channel {
  29        char *name;
  30        pxa_dma_prio prio;
  31        void (*irq_handler)(int, void *);
  32        void *data;
  33};
  34
  35static struct dma_channel *dma_channels;
  36static int num_dma_channels;
  37
  38int pxa_request_dma (char *name, pxa_dma_prio prio,
  39                         void (*irq_handler)(int, void *),
  40                         void *data)
  41{
  42        unsigned long flags;
  43        int i, found = 0;
  44
  45        /* basic sanity checks */
  46        if (!name || !irq_handler)
  47                return -EINVAL;
  48
  49        local_irq_save(flags);
  50
  51        do {
  52                /* try grabbing a DMA channel with the requested priority */
  53                for (i = 0; i < num_dma_channels; i++) {
  54                        if ((dma_channels[i].prio == prio) &&
  55                            !dma_channels[i].name) {
  56                                found = 1;
  57                                break;
  58                        }
  59                }
  60                /* if requested prio group is full, try a hier priority */
  61        } while (!found && prio--);
  62
  63        if (found) {
  64                DCSR(i) = DCSR_STARTINTR|DCSR_ENDINTR|DCSR_BUSERR;
  65                dma_channels[i].name = name;
  66                dma_channels[i].irq_handler = irq_handler;
  67                dma_channels[i].data = data;
  68        } else {
  69                printk (KERN_WARNING "No more available DMA channels for %s\n", name);
  70                i = -ENODEV;
  71        }
  72
  73        local_irq_restore(flags);
  74        return i;
  75}
  76
  77void pxa_free_dma (int dma_ch)
  78{
  79        unsigned long flags;
  80
  81        if (!dma_channels[dma_ch].name) {
  82                printk (KERN_CRIT
  83                        "%s: trying to free channel %d which is already freed\n",
  84                        __func__, dma_ch);
  85                return;
  86        }
  87
  88        local_irq_save(flags);
  89        DCSR(dma_ch) = DCSR_STARTINTR|DCSR_ENDINTR|DCSR_BUSERR;
  90        dma_channels[dma_ch].name = NULL;
  91        local_irq_restore(flags);
  92}
  93
  94static irqreturn_t dma_irq_handler(int irq, void *dev_id)
  95{
  96        int i, dint = DINT;
  97
  98        for (i = 0; i < num_dma_channels; i++) {
  99                if (dint & (1 << i)) {
 100                        struct dma_channel *channel = &dma_channels[i];
 101                        if (channel->name && channel->irq_handler) {
 102                                channel->irq_handler(i, channel->data);
 103                        } else {
 104                                /*
 105                                 * IRQ for an unregistered DMA channel:
 106                                 * let's clear the interrupts and disable it.
 107                                 */
 108                                printk (KERN_WARNING "spurious IRQ for DMA channel %d\n", i);
 109                                DCSR(i) = DCSR_STARTINTR|DCSR_ENDINTR|DCSR_BUSERR;
 110                        }
 111                }
 112        }
 113        return IRQ_HANDLED;
 114}
 115
 116int __init pxa_init_dma(int num_ch)
 117{
 118        int i, ret;
 119
 120        dma_channels = kzalloc(sizeof(struct dma_channel) * num_ch, GFP_KERNEL);
 121        if (dma_channels == NULL)
 122                return -ENOMEM;
 123
 124        ret = request_irq(IRQ_DMA, dma_irq_handler, IRQF_DISABLED, "DMA", NULL);
 125        if (ret) {
 126                printk (KERN_CRIT "Wow!  Can't register IRQ for DMA\n");
 127                kfree(dma_channels);
 128                return ret;
 129        }
 130
 131        /* dma channel priorities on pxa2xx processors:
 132         * ch 0 - 3,  16 - 19  <--> (0) DMA_PRIO_HIGH
 133         * ch 4 - 7,  20 - 23  <--> (1) DMA_PRIO_MEDIUM
 134         * ch 8 - 15, 24 - 31  <--> (2) DMA_PRIO_LOW
 135         */
 136        for (i = 0; i < num_ch; i++)
 137                dma_channels[i].prio = min((i & 0xf) >> 2, DMA_PRIO_LOW);
 138
 139        num_dma_channels = num_ch;
 140        return 0;
 141}
 142
 143EXPORT_SYMBOL(pxa_request_dma);
 144EXPORT_SYMBOL(pxa_free_dma);
 145
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.