/*
* linux/arch/arm/mach-pnx4008/dma.c
*
* PNX4008 DMA registration and IRQ dispatching
*
* Author: Vitaly Wool
* Copyright: MontaVista Software Inc. (c) 2005
*
* Based on the code from Nicolas Pitre
*
* 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 <linux/err.h>
#include <linux/dma-mapping.h>
#include <linux/clk.h>
#include <asm/system.h>
#include <mach/hardware.h>
#include <asm/dma.h>
#include <asm/dma-mapping.h>
#include <asm/io.h>
#include <asm/mach/dma.h>
#include <mach/clock.h>
static struct dma_channel {
char *name;
void (*irq_handler) (int, int, void *);
void *data;
struct pnx4008_dma_ll *ll;
u32 ll_dma;
void *target_addr;
int target_id;
} dma_channels[MAX_DMA_CHANNELS];
static struct ll_pool {
void *vaddr;
void *cur;
dma_addr_t dma_addr;
int count;
} ll_pool;
static DEFINE_SPINLOCK(ll_lock);
struct pnx4008_dma_ll *pnx4008_alloc_ll_entry(dma_addr_t * ll_dma)
{
struct pnx4008_dma_ll *ll = NULL;
unsigned long flags;
spin_lock_irqsave(&ll_lock, flags);
if (ll_pool.count > 4) { /* can give one more */
ll = *(struct pnx4008_dma_ll **) ll_pool.cur;
*ll_dma = ll_pool.dma_addr + ((void *)ll - ll_pool.vaddr);
*(void **)ll_pool.cur = **(void ***)ll_pool.cur;
memset(ll, 0, sizeof(*ll));
ll_pool.count--;
}
spin_unlock_irqrestore(&ll_lock, flags);
return ll;
}
EXPORT_SYMBOL_GPL(pnx4008_alloc_ll_entry);
void pnx4008_free_ll_entry(struct pnx4008_dma_ll * ll, dma_addr_t ll_dma)
{
unsigned long flags;
if (ll) {
if ((unsigned long)((long)ll - (long)ll_pool.vaddr) > 0x4000) {
printk(KERN_ERR "Trying to free entry not allocated by DMA\n");
BUG();
}
if (ll->flags & DMA_BUFFER_ALLOCATED)
ll->free(ll->alloc_data);
spin_lock_irqsave(&ll_lock, flags);
*(long *)ll = *(long *)ll_pool.cur;
*(long *)ll_pool.cur = (long)ll;
ll_pool.count++;
spin_unlock_irqrestore(&ll_lock, flags);
}
}
EXPORT_SYMBOL_GPL(pnx4008_free_ll_entry);
void pnx4008_free_ll(u32 ll_dma, struct pnx4008_dma_ll * ll)
{
struct pnx4008_dma_ll *ptr;
u32 dma;
while (ll) {
dma = ll->next_dma;
ptr = ll->next;
pnx4008_free_ll_entry(ll, ll_dma);
ll_dma = dma;
ll = ptr;
}
}
EXPORT_SYMBOL_GPL(pnx4008_free_ll);
static int dma_channels_requested = 0;
static inline void dma_increment_usage(void)
{
if (!dma_channels_requested++) {
struct clk *clk = clk_get(0, "dma_ck");
if (!IS_ERR(clk)) {
clk_set_rate(clk, 1);
clk_put(clk);
}
pnx4008_config_dma(-1, -1, 1);
}
}
static inline void dma_decrement_usage(void)
{
if (!--dma_channels_requested) {
struct clk *clk