summaryrefslogtreecommitdiff
path: root/packages/linux/linux-2.6.18/at32ap7000-dmac-driver.patch
diff options
context:
space:
mode:
Diffstat (limited to 'packages/linux/linux-2.6.18/at32ap7000-dmac-driver.patch')
-rw-r--r--packages/linux/linux-2.6.18/at32ap7000-dmac-driver.patch855
1 files changed, 0 insertions, 855 deletions
diff --git a/packages/linux/linux-2.6.18/at32ap7000-dmac-driver.patch b/packages/linux/linux-2.6.18/at32ap7000-dmac-driver.patch
deleted file mode 100644
index dfe5f6abd5..0000000000
--- a/packages/linux/linux-2.6.18/at32ap7000-dmac-driver.patch
+++ /dev/null
@@ -1,855 +0,0 @@
-From nobody Mon Sep 17 00:00:00 2001
-From: Håvard Skinnemoen <hskinnemoen@atmel.com>
-Date: Fri Dec 2 13:24:24 2005 +0100
-Subject: [PATCH] AVR32: DesignWare DMA Controller
-
-This patch adds a driver for the Synopsys DesignWare DMA Controller.
-
----
-
- arch/avr32/Kconfig | 4
- arch/avr32/Makefile | 1
- arch/avr32/drivers/Makefile | 1
- arch/avr32/drivers/dw-dmac.c | 754 +++++++++++++++++++++++++++++++++++++++++++
- arch/avr32/drivers/dw-dmac.h | 42 ++
- 5 files changed, 802 insertions(+)
-
-Index: linux-2.6.18-avr32/arch/avr32/Makefile
-===================================================================
---- linux-2.6.18-avr32.orig/arch/avr32/Makefile 2006-11-02 14:17:29.000000000 +0100
-+++ linux-2.6.18-avr32/arch/avr32/Makefile 2006-11-02 15:53:13.000000000 +0100
-@@ -30,6 +30,7 @@ core-$(CONFIG_BOARD_ATSTK1000) += arch/
- core-$(CONFIG_LOADER_U_BOOT) += arch/avr32/boot/u-boot/
- core-y += arch/avr32/kernel/
- core-y += arch/avr32/mm/
-+drivers-y += arch/avr32/drivers/
- drivers-$(CONFIG_OPROFILE) += arch/avr32/oprofile/
- libs-y += arch/avr32/lib/
-
-Index: linux-2.6.18-avr32/arch/avr32/drivers/Makefile
-===================================================================
---- /dev/null 1970-01-01 00:00:00.000000000 +0000
-+++ linux-2.6.18-avr32/arch/avr32/drivers/Makefile 2006-11-02 14:17:29.000000000 +0100
-@@ -0,0 +1 @@
-+obj-$(CONFIG_DW_DMAC) += dw-dmac.o
-Index: linux-2.6.18-avr32/arch/avr32/drivers/dw-dmac.c
-===================================================================
---- /dev/null 1970-01-01 00:00:00.000000000 +0000
-+++ linux-2.6.18-avr32/arch/avr32/drivers/dw-dmac.c 2006-11-02 15:55:35.000000000 +0100
-@@ -0,0 +1,754 @@
-+/*
-+ * Driver for the Synopsys DesignWare DMA Controller
-+ *
-+ * Copyright (C) 2005-2006 Atmel Corporation
-+ *
-+ * 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/clk.h>
-+#include <linux/device.h>
-+#include <linux/dma-mapping.h>
-+#include <linux/dmapool.h>
-+#include <linux/init.h>
-+#include <linux/interrupt.h>
-+#include <linux/module.h>
-+#include <linux/platform_device.h>
-+
-+#include <asm/dma-controller.h>
-+#include <asm/io.h>
-+
-+#include "dw-dmac.h"
-+
-+#define DMAC_NR_CHANNELS 3
-+#define DMAC_MAX_BLOCKSIZE 4095
-+
-+enum {
-+ CH_STATE_FREE = 0,
-+ CH_STATE_ALLOCATED,
-+ CH_STATE_BUSY,
-+};
-+
-+struct dw_dma_lli {
-+ dma_addr_t sar;
-+ dma_addr_t dar;
-+ dma_addr_t llp;
-+ u32 ctllo;
-+ u32 ctlhi;
-+ u32 sstat;
-+ u32 dstat;
-+};
-+
-+struct dw_dma_block {
-+ struct dw_dma_lli *lli_vaddr;
-+ dma_addr_t lli_dma_addr;
-+};
-+
-+struct dw_dma_channel {
-+ unsigned int state;
-+ int is_cyclic;
-+ struct dma_request_sg *req_sg;
-+ struct dma_request_cyclic *req_cyclic;
-+ unsigned int nr_blocks;
-+ int direction;
-+ struct dw_dma_block *block;
-+};
-+
-+struct dw_dma_controller {
-+ spinlock_t lock;
-+ void * __iomem regs;
-+ struct dma_pool *lli_pool;
-+ struct clk *hclk;
-+ struct dma_controller dma;
-+ struct dw_dma_channel channel[DMAC_NR_CHANNELS];
-+};
-+#define to_dw_dmac(dmac) container_of(dmac, struct dw_dma_controller, dma)
-+
-+#define dmac_writel_hi(dmac, reg, value) \
-+ __raw_writel((value), (dmac)->regs + DW_DMAC_##reg + 4)
-+#define dmac_readl_hi(dmac, reg) \
-+ __raw_readl((dmac)->regs + DW_DMAC_##reg + 4)
-+#define dmac_writel_lo(dmac, reg, value) \
-+ __raw_writel((value), (dmac)->regs + DW_DMAC_##reg)
-+#define dmac_readl_lo(dmac, reg) \
-+ __raw_readl((dmac)->regs + DW_DMAC_##reg)
-+#define dmac_chan_writel_hi(dmac, chan, reg, value) \
-+ __raw_writel((value), ((dmac)->regs + 0x58 * (chan) \
-+ + DW_DMAC_CHAN_##reg + 4))
-+#define dmac_chan_readl_hi(dmac, chan, reg) \
-+ __raw_readl((dmac)->regs + 0x58 * (chan) + DW_DMAC_CHAN_##reg + 4)
-+#define dmac_chan_writel_lo(dmac, chan, reg, value) \
-+ __raw_writel((value), (dmac)->regs + 0x58 * (chan) + DW_DMAC_CHAN_##reg)
-+#define dmac_chan_readl_lo(dmac, chan, reg) \
-+ __raw_readl((dmac)->regs + 0x58 * (chan) + DW_DMAC_CHAN_##reg)
-+#define set_channel_bit(dmac, reg, chan) \
-+ dmac_writel_lo(dmac, reg, (1 << (chan)) | (1 << ((chan) + 8)))
-+#define clear_channel_bit(dmac, reg, chan) \
-+ dmac_writel_lo(dmac, reg, (0 << (chan)) | (1 << ((chan) + 8)))
-+
-+static int dmac_alloc_channel(struct dma_controller *_dmac)
-+{
-+ struct dw_dma_controller *dmac = to_dw_dmac(_dmac);
-+ struct dw_dma_channel *chan;
-+ unsigned long flags;
-+ int i;
-+
-+ spin_lock_irqsave(&dmac->lock, flags);
-+ for (i = 0; i < DMAC_NR_CHANNELS; i++)
-+ if (dmac->channel[i].state == CH_STATE_FREE)
-+ break;
-+
-+ if (i < DMAC_NR_CHANNELS) {
-+ chan = &dmac->channel[i];
-+ chan->state = CH_STATE_ALLOCATED;
-+ } else {
-+ i = -EBUSY;
-+ }
-+
-+ spin_unlock_irqrestore(&dmac->lock, flags);
-+
-+ return i;
-+}
-+
-+static void dmac_release_channel(struct dma_controller *_dmac, int channel)
-+{
-+ struct dw_dma_controller *dmac = to_dw_dmac(_dmac);
-+
-+ BUG_ON(channel >= DMAC_NR_CHANNELS
-+ || dmac->channel[channel].state != CH_STATE_ALLOCATED);
-+
-+ dmac->channel[channel].state = CH_STATE_FREE;
-+}
-+
-+static struct dw_dma_block *allocate_blocks(struct dw_dma_controller *dmac,
-+ unsigned int nr_blocks)
-+{
-+ struct dw_dma_block *block;
-+ void *p;
-+ unsigned int i;
-+
-+ block = kmalloc(nr_blocks * sizeof(*block),
-+ GFP_KERNEL);
-+ if (unlikely(!block))
-+ return NULL;
-+
-+ for (i = 0; i < nr_blocks; i++) {
-+ p = dma_pool_alloc(dmac->lli_pool, GFP_KERNEL,
-+ &block[i].lli_dma_addr);
-+ block[i].lli_vaddr = p;
-+ if (unlikely(!p))
-+ goto fail;
-+ }
-+
-+ return block;
-+
-+fail:
-+ for (i = 0; i < nr_blocks; i++) {
-+ if (!block[i].lli_vaddr)
-+ break;
-+ dma_pool_free(dmac->lli_pool, block[i].lli_vaddr,
-+ block[i].lli_dma_addr);
-+ }
-+ kfree(block);
-+ return NULL;
-+}
-+
-+static int dmac_prepare_request_sg(struct dma_controller *_dmac,
-+ struct dma_request_sg *req)
-+{
-+ struct dw_dma_controller *dmac = to_dw_dmac(_dmac);
-+ struct dw_dma_channel *chan;
-+ unsigned long ctlhi, ctllo, cfghi, cfglo;
-+ unsigned long block_size;
-+ int ret, i, nr_blocks, direction;
-+ unsigned long flags;
-+
-+ spin_lock_irqsave(&dmac->lock, flags);
-+
-+ ret = -EINVAL;
-+ if (req->req.channel >= DMAC_NR_CHANNELS
-+ || dmac->channel[req->req.channel].state != CH_STATE_ALLOCATED
-+ || req->block_size > DMAC_MAX_BLOCKSIZE) {
-+ spin_unlock_irqrestore(&dmac->lock, flags);
-+ return -EINVAL;
-+ }
-+
-+ chan = &dmac->channel[req->req.channel];
-+ chan->state = CH_STATE_BUSY;
-+ chan->req_sg = req;
-+ chan->is_cyclic = 0;
-+
-+ /*
-+ * We have marked the channel as busy, so no need to keep the
-+ * lock as long as we only touch the channel-specific
-+ * registers
-+ */
-+ spin_unlock_irqrestore(&dmac->lock, flags);
-+
-+ /*
-+ * There may be limitations in the driver and/or the DMA
-+ * controller that prevents us from sending a whole
-+ * scatterlist item in one go. Taking this into account,
-+ * calculate the number of block transfers we need to set up.
-+ *
-+ * FIXME: Let the peripheral driver know about the maximum
-+ * block size we support. We really don't want to use a
-+ * different block size than what was suggested by the
-+ * peripheral.
-+ *
-+ * Each block will get its own Linked List Item (LLI) below.
-+ */
-+ block_size = req->block_size;
-+ pr_debug("block_size = %lu, nr_sg = %u\n", block_size, req->nr_sg);
-+ for (i = 0, nr_blocks = 0; i < req->nr_sg; i++) {
-+ pr_debug("sg[i].length = %u\n", req->sg[i].length);
-+ BUG_ON(req->sg[i].length % block_size);
-+ nr_blocks += req->sg[i].length / block_size;
-+ }
-+
-+ BUG_ON(nr_blocks == 0);
-+ chan->nr_blocks = nr_blocks;
-+
-+ ret = -EINVAL;
-+ cfglo = cfghi = 0;
-+ switch (req->direction) {
-+ case DMA_DIR_MEM_TO_PERIPH:
-+ direction = DMA_TO_DEVICE;
-+ cfghi = req->periph_id << (43 - 32);
-+ break;
-+
-+ case DMA_DIR_PERIPH_TO_MEM:
-+ direction = DMA_FROM_DEVICE;
-+ cfghi = req->periph_id << (39 - 32);
-+ break;
-+ default:
-+ goto out_unclaim_channel;
-+ }
-+
-+ chan->direction = direction;
-+
-+ dmac_chan_writel_hi(dmac, req->req.channel, CFG, cfghi);
-+ dmac_chan_writel_lo(dmac, req->req.channel, CFG, cfglo);
-+
-+ ctlhi = block_size >> req->width;
-+ ctllo = ((req->direction << 20)
-+ // | (1 << 14) | (1 << 11) // source/dest burst trans len
-+ | (req->width << 4) | (req->width << 1)
-+ | (1 << 0)); // interrupt enable
-+
-+ if (nr_blocks == 1) {
-+ /* Only one block: No need to use block chaining */
-+ if (direction == DMA_TO_DEVICE) {
-+ dmac_chan_writel_lo(dmac, req->req.channel, SAR,
-+ req->sg->dma_address);
-+ dmac_chan_writel_lo(dmac, req->req.channel, DAR,
-+ req->data_reg);
-+ ctllo |= 2 << 7; // no dst increment
-+ } else {
-+ dmac_chan_writel_lo(dmac, req->req.channel, SAR,
-+ req->data_reg);
-+ dmac_chan_writel_lo(dmac, req->req.channel, DAR,
-+ req->sg->dma_address);
-+ ctllo |= 2 << 9; // no src increment
-+ }
-+ dmac_chan_writel_lo(dmac, req->req.channel, CTL, ctllo);
-+ dmac_chan_writel_hi(dmac, req->req.channel, CTL, ctlhi);
-+ } else {
-+ struct dw_dma_lli *lli, *lli_prev = NULL;
-+ int j = 0, offset = 0;
-+
-+ ret = -ENOMEM;
-+ chan->block = allocate_blocks(dmac, nr_blocks);
-+ if (!chan->block)
-+ goto out_unclaim_channel;
-+
-+ if (direction == DMA_TO_DEVICE)
-+ ctllo |= 1 << 28 | 1 << 27 | 2 << 7;
-+ else
-+ ctllo |= 1 << 28 | 1 << 27 | 2 << 9;
-+
-+ /*
-+ * Map scatterlist items to blocks. One scatterlist
-+ * item may need more than one block for the reasons
-+ * mentioned above.
-+ */
-+ for (i = 0; i < nr_blocks; i++) {
-+ lli = chan->block[i].lli_vaddr;
-+ if (lli_prev) {
-+ lli_prev->llp = chan->block[i].lli_dma_addr;
-+ pr_debug("lli[%d] (0x%p/0x%x): 0x%x 0x%x 0x%x 0x%x 0x%x\n",
-+ i - 1, chan->block[i - 1].lli_vaddr,
-+ chan->block[i - 1].lli_dma_addr,
-+ lli_prev->sar, lli_prev->dar, lli_prev->llp,
-+ lli_prev->ctllo, lli_prev->ctlhi);
-+ }
-+ lli->llp = 0;
-+ lli->ctllo = ctllo;
-+ lli->ctlhi = ctlhi;
-+ if (direction == DMA_TO_DEVICE) {
-+ lli->sar = req->sg[j].dma_address + offset;
-+ lli->dar = req->data_reg;
-+ } else {
-+ lli->sar = req->data_reg;
-+ lli->dar = req->sg[j].dma_address + offset;
-+ }
-+ lli_prev = lli;
-+
-+ offset += block_size;
-+ if (offset > req->sg[j].length) {
-+ j++;
-+ offset = 0;
-+ }
-+ }
-+
-+ pr_debug("lli[%d] (0x%p/0x%x): 0x%x 0x%x 0x%x 0x%x 0x%x\n",
-+ i - 1, chan->block[i - 1].lli_vaddr,
-+ chan->block[i - 1].lli_dma_addr, lli_prev->sar,
-+ lli_prev->dar, lli_prev->llp,
-+ lli_prev->ctllo, lli_prev->ctlhi);
-+
-+ /*
-+ * SAR, DAR and CTL are initialized from the LLI. We
-+ * only have to enable the LLI bits in CTL.
-+ */
-+ dmac_chan_writel_lo(dmac, req->req.channel, LLP,
-+ chan->block[0].lli_dma_addr);
-+ dmac_chan_writel_lo(dmac, req->req.channel, CTL, 1 << 28 | 1 << 27);
-+ }
-+
-+ set_channel_bit(dmac, MASK_XFER, req->req.channel);
-+ set_channel_bit(dmac, MASK_ERROR, req->req.channel);
-+ if (req->req.block_complete)
-+ set_channel_bit(dmac, MASK_BLOCK, req->req.channel);
-+ else
-+ clear_channel_bit(dmac, MASK_BLOCK, req->req.channel);
-+
-+ return 0;
-+
-+out_unclaim_channel:
-+ chan->state = CH_STATE_ALLOCATED;
-+ return ret;
-+}
-+
-+static int dmac_prepare_request_cyclic(struct dma_controller *_dmac,
-+ struct dma_request_cyclic *req)
-+{
-+ struct dw_dma_controller *dmac = to_dw_dmac(_dmac);
-+ struct dw_dma_channel *chan;
-+ unsigned long ctlhi, ctllo, cfghi, cfglo;
-+ unsigned long block_size;
-+ int ret, i, direction;
-+ unsigned long flags;
-+
-+ spin_lock_irqsave(&dmac->lock, flags);
-+
-+ block_size = (req->buffer_size/req->periods) >> req->width;
-+
-+ ret = -EINVAL;
-+ if (req->req.channel >= DMAC_NR_CHANNELS
-+ || dmac->channel[req->req.channel].state != CH_STATE_ALLOCATED
-+ || (req->periods == 0)
-+ || block_size > DMAC_MAX_BLOCKSIZE) {
-+ spin_unlock_irqrestore(&dmac->lock, flags);
-+ return -EINVAL;
-+ }
-+
-+ chan = &dmac->channel[req->req.channel];
-+ chan->state = CH_STATE_BUSY;
-+ chan->is_cyclic = 1;
-+ chan->req_cyclic = req;
-+
-+ /*
-+ * We have marked the channel as busy, so no need to keep the
-+ * lock as long as we only touch the channel-specific
-+ * registers
-+ */
-+ spin_unlock_irqrestore(&dmac->lock, flags);
-+
-+ /*
-+ Setup
-+ */
-+ BUG_ON(req->buffer_size % req->periods);
-+ /* printk(KERN_INFO "block_size = %lu, periods = %u\n", block_size, req->periods); */
-+
-+ chan->nr_blocks = req->periods;
-+
-+ ret = -EINVAL;
-+ cfglo = cfghi = 0;
-+ switch (req->direction) {
-+ case DMA_DIR_MEM_TO_PERIPH:
-+ direction = DMA_TO_DEVICE;
-+ cfghi = req->periph_id << (43 - 32);
-+ break;
-+
-+ case DMA_DIR_PERIPH_TO_MEM:
-+ direction = DMA_FROM_DEVICE;
-+ cfghi = req->periph_id << (39 - 32);
-+ break;
-+ default:
-+ goto out_unclaim_channel;
-+ }
-+
-+ chan->direction = direction;
-+
-+ dmac_chan_writel_hi(dmac, req->req.channel, CFG, cfghi);
-+ dmac_chan_writel_lo(dmac, req->req.channel, CFG, cfglo);
-+
-+ ctlhi = block_size;
-+ ctllo = ((req->direction << 20)
-+ | (req->width << 4) | (req->width << 1)
-+ | (1 << 0)); // interrupt enable
-+
-+ {
-+ struct dw_dma_lli *lli = NULL, *lli_prev = NULL;
-+
-+ ret = -ENOMEM;
-+ chan->block = allocate_blocks(dmac, req->periods);
-+ if (!chan->block)
-+ goto out_unclaim_channel;
-+
-+ if (direction == DMA_TO_DEVICE)
-+ ctllo |= 1 << 28 | 1 << 27 | 2 << 7;
-+ else
-+ ctllo |= 1 << 28 | 1 << 27 | 2 << 9;
-+
-+ /*
-+ * Set up a linked list items where each period gets
-+ * an item. The linked list item for the last period
-+ * points back to the star of the buffer making a
-+ * cyclic buffer.
-+ */
-+ for (i = 0; i < req->periods; i++) {
-+ lli = chan->block[i].lli_vaddr;
-+ if (lli_prev) {
-+ lli_prev->llp = chan->block[i].lli_dma_addr;
-+ /* printk(KERN_INFO "lli[%d] (0x%p/0x%x): 0x%x 0x%x 0x%x 0x%x 0x%x\n",
-+ i - 1, chan->block[i - 1].lli_vaddr,
-+ chan->block[i - 1].lli_dma_addr,
-+ lli_prev->sar, lli_prev->dar, lli_prev->llp,
-+ lli_prev->ctllo, lli_prev->ctlhi);*/
-+ }
-+ lli->llp = 0;
-+ lli->ctllo = ctllo;
-+ lli->ctlhi = ctlhi;
-+ if (direction == DMA_TO_DEVICE) {
-+ lli->sar = req->buffer_start + i*(block_size << req->width);
-+ lli->dar = req->data_reg;
-+ } else {
-+ lli->sar = req->data_reg;
-+ lli->dar = req->buffer_start + i*(block_size << req->width);
-+ }
-+ lli_prev = lli;
-+ }
-+ lli->llp = chan->block[0].lli_dma_addr;
-+
-+ /*printk(KERN_INFO "lli[%d] (0x%p/0x%x): 0x%x 0x%x 0x%x 0x%x 0x%x\n",
-+ i - 1, chan->block[i - 1].lli_vaddr,
-+ chan->block[i - 1].lli_dma_addr, lli_prev->sar,
-+ lli_prev->dar, lli_prev->llp,
-+ lli_prev->ctllo, lli_prev->ctlhi); */
-+
-+ /*
-+ * SAR, DAR and CTL are initialized from the LLI. We
-+ * only have to enable the LLI bits in CTL.
-+ */
-+ dmac_chan_writel_lo(dmac, req->req.channel, LLP,
-+ chan->block[0].lli_dma_addr);
-+ dmac_chan_writel_lo(dmac, req->req.channel, CTL, 1 << 28 | 1 << 27);
-+ }
-+
-+ clear_channel_bit(dmac, MASK_XFER, req->req.channel);
-+ set_channel_bit(dmac, MASK_ERROR, req->req.channel);
-+ if (req->req.block_complete)
-+ set_channel_bit(dmac, MASK_BLOCK, req->req.channel);
-+ else
-+ clear_channel_bit(dmac, MASK_BLOCK, req->req.channel);
-+
-+ return 0;
-+
-+out_unclaim_channel:
-+ chan->state = CH_STATE_ALLOCATED;
-+ return ret;
-+}
-+
-+static int dmac_start_request(struct dma_controller *_dmac,
-+ unsigned int channel)
-+{
-+ struct dw_dma_controller *dmac = to_dw_dmac(_dmac);
-+
-+ BUG_ON(channel >= DMAC_NR_CHANNELS);
-+
-+ set_channel_bit(dmac, CH_EN, channel);
-+
-+ return 0;
-+}
-+
-+static dma_addr_t dmac_get_current_pos(struct dma_controller *_dmac,
-+ unsigned int channel)
-+{
-+ struct dw_dma_controller *dmac = to_dw_dmac(_dmac);
-+ struct dw_dma_channel *chan;
-+ dma_addr_t current_pos;
-+
-+ BUG_ON(channel >= DMAC_NR_CHANNELS);
-+
-+ chan = &dmac->channel[channel];
-+
-+ switch (chan->direction) {
-+ case DMA_TO_DEVICE:
-+ current_pos = dmac_chan_readl_lo(dmac, channel, SAR);
-+ break;
-+ case DMA_FROM_DEVICE:
-+ current_pos = dmac_chan_readl_lo(dmac, channel, DAR);
-+ break;
-+ default:
-+ return 0;
-+ }
-+
-+
-+ if (!current_pos) {
-+ if (chan->is_cyclic) {
-+ current_pos = chan->req_cyclic->buffer_start;
-+ } else {
-+ current_pos = chan->req_sg->sg->dma_address;
-+ }
-+ }
-+
-+ return current_pos;
-+}
-+
-+
-+static void cleanup_channel(struct dw_dma_controller *dmac,
-+ struct dw_dma_channel *chan)
-+{
-+ unsigned int i;
-+
-+ if (chan->nr_blocks > 1) {
-+ for (i = 0; i < chan->nr_blocks; i++)
-+ dma_pool_free(dmac->lli_pool, chan->block[i].lli_vaddr,
-+ chan->block[i].lli_dma_addr);
-+ kfree(chan->block);
-+ }
-+
-+ chan->state = CH_STATE_ALLOCATED;
-+}
-+
-+static int dmac_stop_request(struct dma_controller *_dmac,
-+ unsigned int channel)
-+{
-+ struct dw_dma_controller *dmac = to_dw_dmac(_dmac);
-+
-+ BUG_ON(channel >= DMAC_NR_CHANNELS);
-+
-+ BUG_ON(dmac->channel[channel].state != CH_STATE_BUSY);
-+
-+ clear_channel_bit(dmac, CH_EN, channel);
-+
-+ cleanup_channel(dmac, &dmac->channel[channel]);
-+
-+ return 0;
-+}
-+
-+
-+static void dmac_block_complete(struct dw_dma_controller *dmac)
-+{
-+ struct dw_dma_channel *chan;
-+ unsigned long status, chanid;
-+
-+ status = dmac_readl_lo(dmac, STATUS_BLOCK);
-+
-+ while (status) {
-+ struct dma_request *req;
-+ chanid = __ffs(status);
-+ chan = &dmac->channel[chanid];
-+
-+ if (chan->is_cyclic) {
-+ BUG_ON(!chan->req_cyclic
-+ || !chan->req_cyclic->req.block_complete);
-+ req = &chan->req_cyclic->req;
-+ } else {
-+ BUG_ON(!chan->req_sg || !chan->req_sg->req.block_complete);
-+ req = &chan->req_sg->req;
-+ }
-+ dmac_writel_lo(dmac, CLEAR_BLOCK, 1 << chanid);
-+ req->block_complete(req);
-+ status = dmac_readl_lo(dmac, STATUS_BLOCK);
-+ }
-+}
-+
-+static void dmac_xfer_complete(struct dw_dma_controller *dmac)
-+{
-+ struct dw_dma_channel *chan;
-+ struct dma_request *req;
-+ unsigned long status, chanid;
-+
-+ status = dmac_readl_lo(dmac, STATUS_XFER);
-+
-+ while (status) {
-+ chanid = __ffs(status);
-+ chan = &dmac->channel[chanid];
-+
-+ dmac_writel_lo(dmac, CLEAR_XFER, 1 << chanid);
-+
-+ req = &chan->req_sg->req;
-+ BUG_ON(!req);
-+ cleanup_channel(dmac, chan);
-+ if (req->xfer_complete)
-+ req->xfer_complete(req);
-+
-+ status = dmac_readl_lo(dmac, STATUS_XFER);
-+ }
-+}
-+
-+static void dmac_error(struct dw_dma_controller *dmac)
-+{
-+ struct dw_dma_channel *chan;
-+ unsigned long status, chanid;
-+
-+ status = dmac_readl_lo(dmac, STATUS_ERROR);
-+
-+ while (status) {
-+ struct dma_request *req;
-+
-+ chanid = __ffs(status);
-+ chan = &dmac->channel[chanid];
-+
-+ dmac_writel_lo(dmac, CLEAR_ERROR, 1 << chanid);
-+ clear_channel_bit(dmac, CH_EN, chanid);
-+
-+ if (chan->is_cyclic) {
-+ BUG_ON(!chan->req_cyclic);
-+ req = &chan->req_cyclic->req;
-+ } else {
-+ BUG_ON(!chan->req_sg);
-+ req = &chan->req_sg->req;
-+ }
-+
-+ cleanup_channel(dmac, chan);
-+ if (req->error)
-+ req->error(req);
-+
-+ status = dmac_readl_lo(dmac, STATUS_XFER);
-+ }
-+}
-+
-+static irqreturn_t dmac_interrupt(int irq, void *dev_id, struct pt_regs *regs)
-+{
-+ struct dw_dma_controller *dmac = dev_id;
-+ unsigned long status;
-+ int ret = IRQ_NONE;
-+
-+ spin_lock(&dmac->lock);
-+
-+ status = dmac_readl_lo(dmac, STATUS_INT);
-+
-+ while (status) {
-+ ret = IRQ_HANDLED;
-+ if (status & 0x10)
-+ dmac_error(dmac);
-+ if (status & 0x02)
-+ dmac_block_complete(dmac);
-+ if (status & 0x01)
-+ dmac_xfer_complete(dmac);
-+
-+ status = dmac_readl_lo(dmac, STATUS_INT);
-+ }
-+
-+ spin_unlock(&dmac->lock);
-+ return ret;
-+}
-+
-+static int __devinit dmac_probe(struct platform_device *pdev)
-+{
-+ struct dw_dma_controller *dmac;
-+ struct resource *regs;
-+ int ret;
-+
-+ regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-+ if (!regs)
-+ return -ENXIO;
-+
-+ dmac = kmalloc(sizeof(*dmac), GFP_KERNEL);
-+ if (!dmac)
-+ return -ENOMEM;
-+ memset(dmac, 0, sizeof(*dmac));
-+
-+ dmac->hclk = clk_get(&pdev->dev, "hclk");
-+ if (IS_ERR(dmac->hclk)) {
-+ ret = PTR_ERR(dmac->hclk);
-+ goto out_free_dmac;
-+ }
-+ clk_enable(dmac->hclk);
-+
-+ ret = -ENOMEM;
-+ dmac->lli_pool = dma_pool_create("dmac", &pdev->dev,
-+ sizeof(struct dw_dma_lli), 4, 0);
-+ if (!dmac->lli_pool)
-+ goto out_disable_clk;
-+
-+ spin_lock_init(&dmac->lock);
-+ dmac->dma.dev = &pdev->dev;
-+ dmac->dma.alloc_channel = dmac_alloc_channel;
-+ dmac->dma.release_channel = dmac_release_channel;
-+ dmac->dma.prepare_request_sg = dmac_prepare_request_sg;
-+ dmac->dma.prepare_request_cyclic = dmac_prepare_request_cyclic;
-+ dmac->dma.start_request = dmac_start_request;
-+ dmac->dma.stop_request = dmac_stop_request;
-+ dmac->dma.get_current_pos = dmac_get_current_pos;
-+
-+ dmac->regs = ioremap(regs->start, regs->end - regs->start + 1);
-+ if (!dmac->regs)
-+ goto out_free_pool;
-+
-+ ret = request_irq(platform_get_irq(pdev, 0), dmac_interrupt,
-+ SA_SAMPLE_RANDOM, pdev->name, dmac);
-+ if (ret)
-+ goto out_unmap_regs;
-+
-+ /* Enable the DMA controller */
-+ dmac_writel_lo(dmac, CFG, 1);
-+
-+ register_dma_controller(&dmac->dma);
-+
-+ printk(KERN_INFO
-+ "dmac%d: DesignWare DMA controller at 0x%p irq %d\n",
-+ dmac->dma.id, dmac->regs, platform_get_irq(pdev, 0));
-+
-+ return 0;
-+
-+out_unmap_regs:
-+ iounmap(dmac->regs);
-+out_free_pool:
-+ dma_pool_destroy(dmac->lli_pool);
-+out_disable_clk:
-+ clk_disable(dmac->hclk);
-+ clk_put(dmac->hclk);
-+out_free_dmac:
-+ kfree(dmac);
-+ return ret;
-+}
-+
-+static struct platform_driver dmac_driver = {
-+ .probe = dmac_probe,
-+ .driver = {
-+ .name = "dmac",
-+ },
-+};
-+
-+static int __init dmac_init(void)
-+{
-+ return platform_driver_register(&dmac_driver);
-+}
-+subsys_initcall(dmac_init);
-+
-+static void __exit dmac_exit(void)
-+{
-+ platform_driver_unregister(&dmac_driver);
-+}
-+module_exit(dmac_exit);
-+
-+MODULE_DESCRIPTION("Synopsys DesignWare DMA Controller driver");
-+MODULE_AUTHOR("Haavard Skinnemoen <hskinnemoen@atmel.com>");
-+MODULE_LICENSE("GPL");
-Index: linux-2.6.18-avr32/arch/avr32/drivers/dw-dmac.h
-===================================================================
---- /dev/null 1970-01-01 00:00:00.000000000 +0000
-+++ linux-2.6.18-avr32/arch/avr32/drivers/dw-dmac.h 2006-11-02 14:17:29.000000000 +0100
-@@ -0,0 +1,42 @@
-+/*
-+ * Driver for the Synopsys DesignWare DMA Controller
-+ *
-+ * Copyright (C) 2005-2006 Atmel Corporation
-+ *
-+ * 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.
-+ */
-+#ifndef __AVR32_DW_DMAC_H__
-+#define __AVR32_DW_DMAC_H__
-+
-+#define DW_DMAC_CFG 0x398
-+#define DW_DMAC_CH_EN 0x3a0
-+
-+#define DW_DMAC_STATUS_XFER 0x2e8
-+#define DW_DMAC_STATUS_BLOCK 0x2f0
-+#define DW_DMAC_STATUS_ERROR 0x308
-+
-+#define DW_DMAC_MASK_XFER 0x310
-+#define DW_DMAC_MASK_BLOCK 0x318
-+#define DW_DMAC_MASK_ERROR 0x330
-+
-+#define DW_DMAC_CLEAR_XFER 0x338
-+#define DW_DMAC_CLEAR_BLOCK 0x340
-+#define DW_DMAC_CLEAR_ERROR 0x358
-+
-+#define DW_DMAC_STATUS_INT 0x360
-+
-+#define DW_DMAC_CHAN_SAR 0x000
-+#define DW_DMAC_CHAN_DAR 0x008
-+#define DW_DMAC_CHAN_LLP 0x010
-+#define DW_DMAC_CHAN_CTL 0x018
-+#define DW_DMAC_CHAN_SSTAT 0x020
-+#define DW_DMAC_CHAN_DSTAT 0x028
-+#define DW_DMAC_CHAN_SSTATAR 0x030
-+#define DW_DMAC_CHAN_DSTATAR 0x038
-+#define DW_DMAC_CHAN_CFG 0x040
-+#define DW_DMAC_CHAN_SGR 0x048
-+#define DW_DMAC_CHAN_DSR 0x050
-+
-+#endif /* __AVR32_DW_DMAC_H__ */
-Index: linux-2.6.18-avr32/arch/avr32/Kconfig
-===================================================================
---- linux-2.6.18-avr32.orig/arch/avr32/Kconfig 2006-11-02 14:17:29.000000000 +0100
-+++ linux-2.6.18-avr32/arch/avr32/Kconfig 2006-11-02 15:53:13.000000000 +0100
-@@ -157,6 +157,10 @@ config OWNERSHIP_TRACE
- enabling Nexus-compliant debuggers to keep track of the PID of the
- currently executing task.
-
-+config DW_DMAC
-+ tristate "Synopsys DesignWare DMA Controller support"
-+ default y if CPU_AT32AP7000
-+
- # FPU emulation goes here
-
- source "kernel/Kconfig.hz"