|
@@ -124,3 +124,130 @@ int s3c2410_dma_config(enum dma_ch channel, int xferunit)
|
|
|
case 1:
|
|
|
chan->hw_width = 0;
|
|
|
break;
|
|
|
+ case 2:
|
|
|
+ chan->hw_width = 1;
|
|
|
+ break;
|
|
|
+ case 4:
|
|
|
+ chan->hw_width = 2;
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ printk(KERN_ERR "%s: illegal width %d\n", __func__, xferunit);
|
|
|
+ return -EINVAL;
|
|
|
+ }
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+EXPORT_SYMBOL(s3c2410_dma_config);
|
|
|
+
|
|
|
+static void s3c64xx_dma_fill_lli(struct s3c2410_dma_chan *chan,
|
|
|
+ struct pl080s_lli *lli,
|
|
|
+ dma_addr_t data, int size)
|
|
|
+{
|
|
|
+ dma_addr_t src, dst;
|
|
|
+ u32 control0, control1;
|
|
|
+
|
|
|
+ switch (chan->source) {
|
|
|
+ case DMA_FROM_DEVICE:
|
|
|
+ src = chan->dev_addr;
|
|
|
+ dst = data;
|
|
|
+ control0 = PL080_CONTROL_SRC_AHB2;
|
|
|
+ control0 |= PL080_CONTROL_DST_INCR;
|
|
|
+ break;
|
|
|
+
|
|
|
+ case DMA_TO_DEVICE:
|
|
|
+ src = data;
|
|
|
+ dst = chan->dev_addr;
|
|
|
+ control0 = PL080_CONTROL_DST_AHB2;
|
|
|
+ control0 |= PL080_CONTROL_SRC_INCR;
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ BUG();
|
|
|
+ }
|
|
|
+
|
|
|
+ /* note, we do not currently setup any of the burst controls */
|
|
|
+
|
|
|
+ control1 = size >> chan->hw_width; /* size in no of xfers */
|
|
|
+ control0 |= PL080_CONTROL_PROT_SYS; /* always in priv. mode */
|
|
|
+ control0 |= PL080_CONTROL_TC_IRQ_EN; /* always fire IRQ */
|
|
|
+ control0 |= (u32)chan->hw_width << PL080_CONTROL_DWIDTH_SHIFT;
|
|
|
+ control0 |= (u32)chan->hw_width << PL080_CONTROL_SWIDTH_SHIFT;
|
|
|
+
|
|
|
+ lli->src_addr = src;
|
|
|
+ lli->dst_addr = dst;
|
|
|
+ lli->next_lli = 0;
|
|
|
+ lli->control0 = control0;
|
|
|
+ lli->control1 = control1;
|
|
|
+}
|
|
|
+
|
|
|
+static void s3c64xx_lli_to_regs(struct s3c2410_dma_chan *chan,
|
|
|
+ struct pl080s_lli *lli)
|
|
|
+{
|
|
|
+ void __iomem *regs = chan->regs;
|
|
|
+
|
|
|
+ pr_debug("%s: LLI %p => regs\n", __func__, lli);
|
|
|
+ show_lli(lli);
|
|
|
+
|
|
|
+ writel(lli->src_addr, regs + PL080_CH_SRC_ADDR);
|
|
|
+ writel(lli->dst_addr, regs + PL080_CH_DST_ADDR);
|
|
|
+ writel(lli->next_lli, regs + PL080_CH_LLI);
|
|
|
+ writel(lli->control0, regs + PL080_CH_CONTROL);
|
|
|
+ writel(lli->control1, regs + PL080S_CH_CONTROL2);
|
|
|
+}
|
|
|
+
|
|
|
+static int s3c64xx_dma_start(struct s3c2410_dma_chan *chan)
|
|
|
+{
|
|
|
+ struct s3c64xx_dmac *dmac = chan->dmac;
|
|
|
+ u32 config;
|
|
|
+ u32 bit = chan->bit;
|
|
|
+
|
|
|
+ dbg_showchan(chan);
|
|
|
+
|
|
|
+ pr_debug("%s: clearing interrupts\n", __func__);
|
|
|
+
|
|
|
+ /* clear interrupts */
|
|
|
+ writel(bit, dmac->regs + PL080_TC_CLEAR);
|
|
|
+ writel(bit, dmac->regs + PL080_ERR_CLEAR);
|
|
|
+
|
|
|
+ pr_debug("%s: starting channel\n", __func__);
|
|
|
+
|
|
|
+ config = readl(chan->regs + PL080S_CH_CONFIG);
|
|
|
+ config |= PL080_CONFIG_ENABLE;
|
|
|
+ config &= ~PL080_CONFIG_HALT;
|
|
|
+
|
|
|
+ pr_debug("%s: writing config %08x\n", __func__, config);
|
|
|
+ writel(config, chan->regs + PL080S_CH_CONFIG);
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static int s3c64xx_dma_stop(struct s3c2410_dma_chan *chan)
|
|
|
+{
|
|
|
+ u32 config;
|
|
|
+ int timeout;
|
|
|
+
|
|
|
+ pr_debug("%s: stopping channel\n", __func__);
|
|
|
+
|
|
|
+ dbg_showchan(chan);
|
|
|
+
|
|
|
+ config = readl(chan->regs + PL080S_CH_CONFIG);
|
|
|
+ config |= PL080_CONFIG_HALT;
|
|
|
+ writel(config, chan->regs + PL080S_CH_CONFIG);
|
|
|
+
|
|
|
+ timeout = 1000;
|
|
|
+ do {
|
|
|
+ config = readl(chan->regs + PL080S_CH_CONFIG);
|
|
|
+ pr_debug("%s: %d - config %08x\n", __func__, timeout, config);
|
|
|
+ if (config & PL080_CONFIG_ACTIVE)
|
|
|
+ udelay(10);
|
|
|
+ else
|
|
|
+ break;
|
|
|
+ } while (--timeout > 0);
|
|
|
+
|
|
|
+ if (config & PL080_CONFIG_ACTIVE) {
|
|
|
+ printk(KERN_ERR "%s: channel still active\n", __func__);
|
|
|
+ return -EFAULT;
|
|
|
+ }
|
|
|
+
|
|
|
+ config = readl(chan->regs + PL080S_CH_CONFIG);
|
|
|
+ config &= ~PL080_CONFIG_ENABLE;
|
|
|
+ writel(config, chan->regs + PL080S_CH_CONFIG);
|