mirror of
https://github.com/torvalds/linux
synced 2024-11-05 18:23:50 +00:00
f53f066c25
Since the number of dma channels varies between pxa25x and pxa27x, it introduces some specific code in dma.c. This patch moves the specific code to pxa25x.c and pxa27x.c and makes dma.c more generic. 1. add pxa_init_dma() for dma initialization, the number of channels are passed in by the argument 2. add a "prio" field to the "struct pxa_dma_channel" for the channel priority, and is initialized in pxa_init_dma() 3. use a general priority comparison with the channels "prio" field so to remove the processor specific pxa_for_each_dma_prio macro, this is not lightning fast as the original one, but it is acceptable as it happens when requesting dma, which is usually not so performance critical Signed-off-by: eric miao <eric.miao@marvell.com> Acked-by: Nicolas Pitre <nico@cam.org> Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
144 lines
3.3 KiB
C
144 lines
3.3 KiB
C
/*
|
|
* linux/arch/arm/mach-pxa/dma.c
|
|
*
|
|
* PXA DMA registration and IRQ dispatching
|
|
*
|
|
* Author: Nicolas Pitre
|
|
* Created: Nov 15, 2001
|
|
* Copyright: MontaVista Software Inc.
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License version 2 as
|
|
* published by the Free Software Foundation.
|
|
*/
|
|
|
|
#include <linux/module.h>
|
|
#include <linux/init.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/interrupt.h>
|
|
#include <linux/errno.h>
|
|
|
|
#include <asm/system.h>
|
|
#include <asm/irq.h>
|
|
#include <asm/hardware.h>
|
|
#include <asm/dma.h>
|
|
|
|
#include <asm/arch/pxa-regs.h>
|
|
|
|
struct dma_channel {
|
|
char *name;
|
|
pxa_dma_prio prio;
|
|
void (*irq_handler)(int, void *);
|
|
void *data;
|
|
};
|
|
|
|
static struct dma_channel *dma_channels;
|
|
static int num_dma_channels;
|
|
|
|
int pxa_request_dma (char *name, pxa_dma_prio prio,
|
|
void (*irq_handler)(int, void *),
|
|
void *data)
|
|
{
|
|
unsigned long flags;
|
|
int i, found = 0;
|
|
|
|
/* basic sanity checks */
|
|
if (!name || !irq_handler)
|
|
return -EINVAL;
|
|
|
|
local_irq_save(flags);
|
|
|
|
do {
|
|
/* try grabbing a DMA channel with the requested priority */
|
|
for (i = 0; i < num_dma_channels; i++) {
|
|
if ((dma_channels[i].prio == prio) &&
|
|
!dma_channels[i].name) {
|
|
found = 1;
|
|
break;
|
|
}
|
|
}
|
|
/* if requested prio group is full, try a hier priority */
|
|
} while (!found && prio--);
|
|
|
|
if (found) {
|
|
DCSR(i) = DCSR_STARTINTR|DCSR_ENDINTR|DCSR_BUSERR;
|
|
dma_channels[i].name = name;
|
|
dma_channels[i].irq_handler = irq_handler;
|
|
dma_channels[i].data = data;
|
|
} else {
|
|
printk (KERN_WARNING "No more available DMA channels for %s\n", name);
|
|
i = -ENODEV;
|
|
}
|
|
|
|
local_irq_restore(flags);
|
|
return i;
|
|
}
|
|
|
|
void pxa_free_dma (int dma_ch)
|
|
{
|
|
unsigned long flags;
|
|
|
|
if (!dma_channels[dma_ch].name) {
|
|
printk (KERN_CRIT
|
|
"%s: trying to free channel %d which is already freed\n",
|
|
__FUNCTION__, dma_ch);
|
|
return;
|
|
}
|
|
|
|
local_irq_save(flags);
|
|
DCSR(dma_ch) = DCSR_STARTINTR|DCSR_ENDINTR|DCSR_BUSERR;
|
|
dma_channels[dma_ch].name = NULL;
|
|
local_irq_restore(flags);
|
|
}
|
|
|
|
static irqreturn_t dma_irq_handler(int irq, void *dev_id)
|
|
{
|
|
int i, dint = DINT;
|
|
|
|
for (i = 0; i < num_dma_channels; i++) {
|
|
if (dint & (1 << i)) {
|
|
struct dma_channel *channel = &dma_channels[i];
|
|
if (channel->name && channel->irq_handler) {
|
|
channel->irq_handler(i, channel->data);
|
|
} else {
|
|
/*
|
|
* IRQ for an unregistered DMA channel:
|
|
* let's clear the interrupts and disable it.
|
|
*/
|
|
printk (KERN_WARNING "spurious IRQ for DMA channel %d\n", i);
|
|
DCSR(i) = DCSR_STARTINTR|DCSR_ENDINTR|DCSR_BUSERR;
|
|
}
|
|
}
|
|
}
|
|
return IRQ_HANDLED;
|
|
}
|
|
|
|
int __init pxa_init_dma(int num_ch)
|
|
{
|
|
int i, ret;
|
|
|
|
dma_channels = kzalloc(sizeof(struct dma_channel) * num_ch, GFP_KERNEL);
|
|
if (dma_channels == NULL)
|
|
return -ENOMEM;
|
|
|
|
ret = request_irq(IRQ_DMA, dma_irq_handler, IRQF_DISABLED, "DMA", NULL);
|
|
if (ret) {
|
|
printk (KERN_CRIT "Wow! Can't register IRQ for DMA\n");
|
|
kfree(dma_channels);
|
|
return ret;
|
|
}
|
|
|
|
/* dma channel priorities on pxa2xx processors:
|
|
* ch 0 - 3, 16 - 19 <--> (0) DMA_PRIO_HIGH
|
|
* ch 4 - 7, 20 - 23 <--> (1) DMA_PRIO_MEDIUM
|
|
* ch 8 - 15, 24 - 31 <--> (2) DMA_PRIO_LOW
|
|
*/
|
|
for (i = 0; i < num_ch; i++)
|
|
dma_channels[i].prio = min((i & 0xf) >> 2, DMA_PRIO_LOW);
|
|
|
|
num_dma_channels = num_ch;
|
|
return 0;
|
|
}
|
|
|
|
EXPORT_SYMBOL(pxa_request_dma);
|
|
EXPORT_SYMBOL(pxa_free_dma);
|