|
@@ -0,0 +1,126 @@
|
|
|
+/* linux/arch/arm/plat-s3c64xx/dma.c
|
|
|
+ *
|
|
|
+ * Copyright 2009 Openmoko, Inc.
|
|
|
+ * Copyright 2009 Simtec Electronics
|
|
|
+ * Ben Dooks <ben@simtec.co.uk>
|
|
|
+ * http://armlinux.simtec.co.uk/
|
|
|
+ *
|
|
|
+ * S3C64XX DMA core
|
|
|
+ *
|
|
|
+ * 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/kernel.h>
|
|
|
+#include <linux/module.h>
|
|
|
+#include <linux/interrupt.h>
|
|
|
+#include <linux/dmapool.h>
|
|
|
+#include <linux/device.h>
|
|
|
+#include <linux/errno.h>
|
|
|
+#include <linux/slab.h>
|
|
|
+#include <linux/delay.h>
|
|
|
+#include <linux/clk.h>
|
|
|
+#include <linux/err.h>
|
|
|
+#include <linux/io.h>
|
|
|
+
|
|
|
+#include <mach/dma.h>
|
|
|
+#include <mach/map.h>
|
|
|
+#include <mach/irqs.h>
|
|
|
+
|
|
|
+#include <mach/regs-sys.h>
|
|
|
+
|
|
|
+#include <asm/hardware/pl080.h>
|
|
|
+
|
|
|
+/* dma channel state information */
|
|
|
+
|
|
|
+struct s3c64xx_dmac {
|
|
|
+ struct device dev;
|
|
|
+ struct clk *clk;
|
|
|
+ void __iomem *regs;
|
|
|
+ struct s3c2410_dma_chan *channels;
|
|
|
+ enum dma_ch chanbase;
|
|
|
+};
|
|
|
+
|
|
|
+/* pool to provide LLI buffers */
|
|
|
+static struct dma_pool *dma_pool;
|
|
|
+
|
|
|
+/* Debug configuration and code */
|
|
|
+
|
|
|
+static unsigned char debug_show_buffs = 0;
|
|
|
+
|
|
|
+static void dbg_showchan(struct s3c2410_dma_chan *chan)
|
|
|
+{
|
|
|
+ pr_debug("DMA%d: %08x->%08x L %08x C %08x,%08x S %08x\n",
|
|
|
+ chan->number,
|
|
|
+ readl(chan->regs + PL080_CH_SRC_ADDR),
|
|
|
+ readl(chan->regs + PL080_CH_DST_ADDR),
|
|
|
+ readl(chan->regs + PL080_CH_LLI),
|
|
|
+ readl(chan->regs + PL080_CH_CONTROL),
|
|
|
+ readl(chan->regs + PL080S_CH_CONTROL2),
|
|
|
+ readl(chan->regs + PL080S_CH_CONFIG));
|
|
|
+}
|
|
|
+
|
|
|
+static void show_lli(struct pl080s_lli *lli)
|
|
|
+{
|
|
|
+ pr_debug("LLI[%p] %08x->%08x, NL %08x C %08x,%08x\n",
|
|
|
+ lli, lli->src_addr, lli->dst_addr, lli->next_lli,
|
|
|
+ lli->control0, lli->control1);
|
|
|
+}
|
|
|
+
|
|
|
+static void dbg_showbuffs(struct s3c2410_dma_chan *chan)
|
|
|
+{
|
|
|
+ struct s3c64xx_dma_buff *ptr;
|
|
|
+ struct s3c64xx_dma_buff *end;
|
|
|
+
|
|
|
+ pr_debug("DMA%d: buffs next %p, curr %p, end %p\n",
|
|
|
+ chan->number, chan->next, chan->curr, chan->end);
|
|
|
+
|
|
|
+ ptr = chan->next;
|
|
|
+ end = chan->end;
|
|
|
+
|
|
|
+ if (debug_show_buffs) {
|
|
|
+ for (; ptr != NULL; ptr = ptr->next) {
|
|
|
+ pr_debug("DMA%d: %08x ",
|
|
|
+ chan->number, ptr->lli_dma);
|
|
|
+ show_lli(ptr->lli);
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+/* End of Debug */
|
|
|
+
|
|
|
+static struct s3c2410_dma_chan *s3c64xx_dma_map_channel(unsigned int channel)
|
|
|
+{
|
|
|
+ struct s3c2410_dma_chan *chan;
|
|
|
+ unsigned int start, offs;
|
|
|
+
|
|
|
+ start = 0;
|
|
|
+
|
|
|
+ if (channel >= DMACH_PCM1_TX)
|
|
|
+ start = 8;
|
|
|
+
|
|
|
+ for (offs = 0; offs < 8; offs++) {
|
|
|
+ chan = &s3c2410_chans[start + offs];
|
|
|
+ if (!chan->in_use)
|
|
|
+ goto found;
|
|
|
+ }
|
|
|
+
|
|
|
+ return NULL;
|
|
|
+
|
|
|
+found:
|
|
|
+ s3c_dma_chan_map[channel] = chan;
|
|
|
+ return chan;
|
|
|
+}
|
|
|
+
|
|
|
+int s3c2410_dma_config(enum dma_ch channel, int xferunit)
|
|
|
+{
|
|
|
+ struct s3c2410_dma_chan *chan = s3c_dma_lookup_channel(channel);
|
|
|
+
|
|
|
+ if (chan == NULL)
|
|
|
+ return -EINVAL;
|
|
|
+
|
|
|
+ switch (xferunit) {
|
|
|
+ case 1:
|
|
|
+ chan->hw_width = 0;
|
|
|
+ break;
|