diff options
Diffstat (limited to 'recipes/linux/linux-2.6.34/ts72xx/0005-ep93xx-m2m-DMA-support.patch')
-rw-r--r-- | recipes/linux/linux-2.6.34/ts72xx/0005-ep93xx-m2m-DMA-support.patch | 882 |
1 files changed, 882 insertions, 0 deletions
diff --git a/recipes/linux/linux-2.6.34/ts72xx/0005-ep93xx-m2m-DMA-support.patch b/recipes/linux/linux-2.6.34/ts72xx/0005-ep93xx-m2m-DMA-support.patch new file mode 100644 index 0000000000..5adc450a85 --- /dev/null +++ b/recipes/linux/linux-2.6.34/ts72xx/0005-ep93xx-m2m-DMA-support.patch @@ -0,0 +1,882 @@ +From 62a8847f18ac66120bf1dc07a497a4d74e099036 Mon Sep 17 00:00:00 2001 +From: Matthieu Crapet <mcrapet@gmail.com> +Date: Thu, 10 Jun 2010 16:40:16 +0200 +Subject: [PATCH 05/18] ep93xx: m2m DMA support + +--- + arch/arm/mach-ep93xx/Makefile | 2 +- + arch/arm/mach-ep93xx/dma-m2m.c | 753 +++++++++++++++++++++++++++++++ + arch/arm/mach-ep93xx/include/mach/dma.h | 65 +++ + 3 files changed, 819 insertions(+), 1 deletions(-) + create mode 100644 arch/arm/mach-ep93xx/dma-m2m.c + +diff --git a/arch/arm/mach-ep93xx/Makefile b/arch/arm/mach-ep93xx/Makefile +index 33ee2c8..ea652c2 100644 +--- a/arch/arm/mach-ep93xx/Makefile ++++ b/arch/arm/mach-ep93xx/Makefile +@@ -1,7 +1,7 @@ + # + # Makefile for the linux kernel. + # +-obj-y := core.o clock.o dma-m2p.o gpio.o ++obj-y := core.o clock.o dma-m2p.o dma-m2m.o gpio.o + obj-m := + obj-n := + obj- := +diff --git a/arch/arm/mach-ep93xx/dma-m2m.c b/arch/arm/mach-ep93xx/dma-m2m.c +new file mode 100644 +index 0000000..8b0d720 +--- /dev/null ++++ b/arch/arm/mach-ep93xx/dma-m2m.c +@@ -0,0 +1,753 @@ ++/* ++ * arch/arm/mach-ep93xx/dma-m2m.c ++ * M2M DMA handling for Cirrus EP93xx chips. ++ * Copyright (C) 2007 Metasoft <prylowski@xxxxxxxxxxx> ++ * ++ * Based on dma-m2p.c by: ++ * Copyright (C) 2006 Lennert Buytenhek <buytenh@xxxxxxxxxxxxxx> ++ * Copyright (C) 2006 Applied Data Systems ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or (at ++ * your option) any later version. ++ */ ++ ++#define pr_fmt(fmt) "ep93xx " KBUILD_MODNAME ": " fmt ++ ++#include <linux/kernel.h> ++#include <linux/clk.h> ++#include <linux/err.h> ++#include <linux/interrupt.h> ++#include <linux/module.h> ++#include <linux/string.h> ++#include <linux/io.h> ++ ++#include <mach/dma.h> ++#include <mach/hardware.h> ++ ++/* TEMP */ ++#define DPRINTK(fmt, args...) ++ ++#define M2M_CONTROL 0x00 ++#define M2M_INTERRUPT 0x04 ++#define M2M_STATUS 0x0c ++#define M2M_BCR0 0x10 ++#define M2M_BCR1 0x14 ++#define M2M_SAR_BASE0 0x18 ++#define M2M_SAR_BASE1 0x1c ++#define M2M_SAR_CURR0 0x24 ++#define M2M_SAR_CURR1 0x28 ++#define M2M_DAR_BASE0 0x2c ++#define M2M_DAR_BASE1 0x30 ++#define M2M_DAR_CURR0 0x34 ++#define M2M_DAR_CURR1 0x3c ++ ++ ++/* control register bits */ ++#define CTRL_STALL_INT_EN 0x00000001 /* stall interrupt enable */ ++#define CTRL_SCT 0x00000002 /* source copy transfer ++ (1 elem. from source fills ++ destination block */ ++#define CTRL_DONE_INT_EN 0x00000004 /* done interrupt enable */ ++#define CTRL_ENABLE 0x00000008 /* channel enable / disable, ++ should be set after ++ write to SAR/DAR/BCR ++ registers */ ++#define CTRL_NFB_INT_EN 0x00200000 /* nfb (next frame buffer) ++ interrupt enable */ ++ ++ ++#define CTRL_START 0x00000010 /* software triggered ++ dma start, not used ++ for M2P/P2M/IDE/SSP */ ++#define CTRL_BWC_MASK 0x000001e0 /* bandwidth control (number ++ of bytes in a block ++ transfer, only M2M */ ++#define CTRL_BWC_SHIFT 5 ++ ++#define BWC_FULL 0x0 /* full bandwidth utilized */ ++#define BWC_16 0x1 /* 16 bytes per block */ ++#define BWC_32 0x5 ++#define BWC_64 0x6 ++#define BWC_128 0x7 ++#define BWC_256 0x8 ++#define BWC_512 0x9 ++#define BWC_1024 0xa ++#define BWC_2048 0xb ++#define BWC_4096 0xc ++#define BWC_8192 0xd ++#define BWC_16384 0xe ++#define BWC_32768 0xf ++ ++#define CTRL_PW_MASK 0x00000600 /* peripheral width, ++ only M2P/P2M */ ++#define CTRL_PW_SHIFT 9 ++ ++#define PW_BYTE 0x0 /* one byte width */ ++#define PW_HALFWORD 0x1 /* 16 bits */ ++#define PW_WORD 0x2 /* 32 bits */ ++#define PW_NOT_USED 0x3 ++ ++#define CTRL_DAH 0x00000800 /* destination address ++ hold, for M2P */ ++#define CTRL_SAH 0x00001000 /* source address ++ hold, for P2M */ ++#define CTRL_TM_MASK 0x00006000 /* transfer mode */ ++#define CTRL_TM_SHIFT 13 ++ ++#define TM_M2M 0x0 /* software initiated M2M transfer */ ++#define TM_M2P 0x1 /* memory to ext. peripheral ++ or IDE/SSP */ ++#define TM_P2M 0x2 /* ext. peripheral or IDE/SSP ++ to memory */ ++#define TM_NOT_USED 0x3 ++ ++#define CTRL_ETDP_MASK 0x00018000 /* end of transfer/terminal ++ count pin direction ++ & polarity */ ++#define CTRL_ETDP_SHIFT 15 ++ ++#define ETDP_ACT_LOW_EOT 0x0 /* pin programmed as active ++ * low end-of-transfer input */ ++#define ETDP_ACT_HIGH_EOT 0x1 /* active high eot input */ ++#define ETDP_ACT_LOW_TC 0x2 /* active low terminal count output */ ++#define ETDP_ACT_HIGH_TC 0x3 /* active high tc output */ ++ ++#define CTRL_DACKP 0x00020000 /* dma acknowledge pin ++ polarity */ ++#define CTRL_DREQP_MASK 0x00180000 /* dma request pin polarity */ ++#define CTRL_DREQP_SHIFT 19 ++ ++#define DREQP_ACT_LOW_LEVEL 0x0 /* DREQ is active low, level ++ sensitive */ ++#define DREQP_ACT_HIGH_LEVEL 0x1 /* active high, level sensitive */ ++#define DREQP_ACT_LOW_EDGE 0x2 /* active low, edge sensitive */ ++#define DREQP_ACT_HIGH_EDGE 0x3 /* active high, edge sensitive */ ++ ++ ++#define CTRL_RSS_MASK 0x00c00000 /* request source selection */ ++#define CTRL_RSS_SHIFT 22 ++ ++#define RSS_EXT 0x0 /* external dma request */ ++#define RSS_SSP_RX 0x1 /* internal SSPRx */ ++#define RSS_SSP_TX 0x2 /* internal SSPTx */ ++#define RSS_IDE 0x3 /* internal IDE */ ++ ++#define CTRL_NO_HDSK 0x01000000 /* no handshake, required for ++ SSP/IDE, optional for ++ ext. M2P/P2M */ ++ ++/* interrupt register bits */ ++#define INTR_STALL 0x1 ++#define INTR_DONE 0x2 ++#define INTR_NFB 0x4 ++#define INTR_ALL 0x7 ++ ++/* status register bits */ ++#define STAT_STALL 0x0001 /* waiting for software start ++ or device request */ ++#define STAT_CTL_STATE_MASK 0x000e /* control fsm state */ ++#define STAT_CTL_STATE_SHIFT 1 ++ ++#define CTL_STATE_IDLE 0x0 ++#define CTL_STATE_STALL 0x1 ++#define CTL_STATE_MEM_RD 0x2 ++#define CTL_STATE_MEM_WR 0x3 ++#define CTL_STATE_BWC_WAIT 0x4 ++ ++#define STAT_BUF_STATE_MASK 0x0030 /* buffer fsm state */ ++#define STAT_BUF_STATE_SHIFT 4 ++ ++#define BUF_STATE_NO_BUF 0x0 ++#define BUF_STATE_BUF_ON 0x1 ++#define BUF_STATE_BUF_NEXT 0x2 ++ ++#define STAT_DONE 0x0040 /* transfer completed successfully ++ (by device or BCR is 0) */ ++ ++#define STAT_TCS_MASK 0x0018 /* terminal count status */ ++#define STAT_TCS_SHIFT 7 ++ ++#define TCS_NONE 0x0 /* terminal count not reached ++ for buffer0 and buffer1 */ ++#define TCS_BUF0 0x1 /* terminal count reached ++ for buffer0 */ ++#define TCS_BUF1 0x2 ++#define TCS_BOTH 0x3 /* terminal count reached ++ for both buffers */ ++ ++#define STAT_EOTS_MASK 0x0060 /* end of transfer status */ ++#define STAT_EOTS_SHIFT 9 ++ ++#define EOTS_NONE 0x0 /* end of transfer has not been ++ requested by ext. periph. for +++ any buffer */ ++#define EOTS_BUF0 0x1 /* eot requested for buffer0 */ ++#define EOTS_BUF1 0x2 ++#define EOTS_BOTH 0x3 /* eot requested for both buffers */ ++ ++#define STAT_NFB 0x0800 /* next frame buffer interrupt */ ++#define STAT_NB 0x1000 /* next buffer status, inform which ++ buffer is free for update */ ++#define STAT_DREQS 0x2000 /* status of dma request signal from ++ ext. periph or IDE/SSP request */ ++ ++/* IDE/SSP support */ ++#define IDE_UDMA_DATAOUT 0x20 ++#define IDE_UDMA_DATAIN 0x24 ++ ++#ifndef SSPDR ++#define SSPDR 0x08 ++#endif ++ ++struct m2m_channel { ++ char *name; ++ void __iomem *base; ++ int irq; ++ ++ struct clk *clk; ++ spinlock_t lock; ++ ++ void *client; ++ unsigned next_slot:1; ++ struct ep93xx_dma_buffer *buffer_xfer; ++ struct ep93xx_dma_buffer *buffer_next; ++ struct list_head buffers_pending; ++}; ++ ++static struct m2m_channel m2m_rxtx[] = { ++ {"m2m0", EP93XX_DMA_BASE + 0x0100, IRQ_EP93XX_DMAM2M0}, ++ {"m2m1", EP93XX_DMA_BASE + 0x0140, IRQ_EP93XX_DMAM2M1}, ++ {NULL}, ++}; ++ ++ ++static void feed_buf(struct m2m_channel *ch, struct ep93xx_dma_buffer *buf) ++{ ++ struct ep93xx_dma_m2m_client *cl = ch->client; ++ u32 src_addr, dst_addr; ++ ++ if ((cl->flags & EP93XX_DMA_M2M_DIR_MASK) == EP93XX_DMA_M2M_TX) { ++ src_addr = buf->bus_addr; ++ switch (cl->flags & EP93XX_DMA_M2M_DEV_MASK) { ++ case EP93XX_DMA_M2M_DEV_IDE: ++ dst_addr = EP93XX_IDE_PHYS_BASE + IDE_UDMA_DATAOUT; ++ break; ++ case EP93XX_DMA_M2M_DEV_SSP: ++ dst_addr = EP93XX_SPI_PHYS_BASE + SSPDR; ++ break; ++ default: ++ dst_addr = buf->bus_addr2; ++ break; ++ } ++ } else { ++ switch (cl->flags & EP93XX_DMA_M2M_DEV_MASK) { ++ case EP93XX_DMA_M2M_DEV_IDE: ++ src_addr = EP93XX_IDE_PHYS_BASE + IDE_UDMA_DATAIN; ++ break; ++ case EP93XX_DMA_M2M_DEV_SSP: ++ src_addr = EP93XX_SPI_PHYS_BASE + SSPDR; ++ break; ++ default: ++ src_addr = buf->bus_addr2; ++ break; ++ } ++ dst_addr = buf->bus_addr; ++ } ++ ++ if (ch->next_slot == 0) { ++ DPRINTK("Writing src_addr: %08x\n", src_addr); ++ DPRINTK("Writing dest_addr: %08x\n", dst_addr); ++ DPRINTK("Writing size: %08x\n", buf->size); ++ writel(src_addr, ch->base + M2M_SAR_BASE0); ++ writel(dst_addr, ch->base + M2M_DAR_BASE0); ++ writel(buf->size, ch->base + M2M_BCR0); ++ } else { ++ writel(src_addr, ch->base + M2M_SAR_BASE1); ++ writel(dst_addr, ch->base + M2M_DAR_BASE1); ++ writel(buf->size, ch->base + M2M_BCR1); ++ } ++ ch->next_slot ^= 1; ++ DPRINTK("data size = %d, slot %d\n", buf->size, ch->next_slot ^ 1); ++} ++ ++static void choose_buffer_xfer(struct m2m_channel *ch) ++{ ++ struct ep93xx_dma_buffer *buf; ++ ++ ch->buffer_xfer = NULL; ++ if (!list_empty(&ch->buffers_pending)) { ++ buf = list_entry(ch->buffers_pending.next, ++ struct ep93xx_dma_buffer, list); ++ list_del(&buf->list); ++ feed_buf(ch, buf); ++ ch->buffer_xfer = buf; ++ } ++} ++ ++static void choose_buffer_next(struct m2m_channel *ch) ++{ ++ struct ep93xx_dma_buffer *buf; ++ ++ ch->buffer_next = NULL; ++ if (!list_empty(&ch->buffers_pending)) { ++ buf = list_entry(ch->buffers_pending.next, ++ struct ep93xx_dma_buffer, list); ++ list_del(&buf->list); ++ feed_buf(ch, buf); ++ ch->buffer_next = buf; ++ } ++} ++ ++static irqreturn_t m2m_irq(int irq, void *dev_id) ++{ ++ struct m2m_channel *ch = dev_id; ++ struct ep93xx_dma_m2m_client *cl; ++ u32 irq_status, dma_state, buf_state, ctl_state; ++ ++ spin_lock(&ch->lock); ++ irq_status = readl(ch->base + M2M_INTERRUPT); ++ /*if ((irq_status & INTR_ALL) == 0) { ++ spin_unlock(&ch->lock); ++ return IRQ_NONE; ++ }*/ ++ dma_state = readl(ch->base + M2M_STATUS); ++ cl = ch->client; ++ ++ //printk("intr status: %08x, dma state: %08x\n", irq_status, dma_state); ++ ++ DPRINTK("intr status %d, dma state %x\n", ++ irq_status, dma_state); ++ ++ buf_state = (dma_state & STAT_BUF_STATE_MASK) >> STAT_BUF_STATE_SHIFT; ++ ctl_state = (dma_state & STAT_CTL_STATE_MASK) >> STAT_CTL_STATE_SHIFT; ++ /*printk("STAT_CTL_STATE: %d, STAT_BUF_STATE: %d\n", ++ * ctl_state, buf_state);*/ ++ if (ctl_state == CTL_STATE_STALL && ++ buf_state == BUF_STATE_NO_BUF && ++ dma_state & STAT_DONE) { ++ /* transfer completed successfully (done) */ ++ ++ ++ /* send client the done command */ ++ if (cl->buffer_finished) { ++ cl->buffer_finished(cl->cookie, ch->buffer_xfer, ch->buffer_xfer->size, 0); ++ } ++ ++ writel(0, ch->base + M2M_INTERRUPT); ++ choose_buffer_xfer(ch); ++ choose_buffer_next(ch); ++ if (ch->buffer_xfer != NULL) { ++ /* retrigger if more buffers exist */ ++ if ((cl->flags & EP93XX_DMA_M2M_DEV_MASK) == ++ EP93XX_DMA_M2M_DEV_MEM) { ++ DPRINTK("Writing start1 to M2M control\n"); ++ writel(readl(ch->base + M2M_CONTROL) | ++ CTRL_START, ch->base + M2M_CONTROL); ++ readl(ch->base + M2M_CONTROL); ++ } ++ } else { ++ DPRINTK("DISABLING DMA: dreqs state: %d\n", dma_state & STAT_DREQS); ++ ++ writel(readl(ch->base + M2M_CONTROL) ++ & ~CTRL_ENABLE, ch->base + M2M_CONTROL); ++ readl(ch->base + M2M_CONTROL); ++ } ++ } else if (ctl_state == CTL_STATE_MEM_RD && ++ buf_state == BUF_STATE_BUF_ON && ++ dma_state & STAT_NFB) { ++ /* next frame buffer */ ++ if (cl->buffer_finished) { ++ cl->buffer_finished(cl->cookie, ch->buffer_xfer, 0, 0); ++ } ++ ch->buffer_xfer = ch->buffer_next; ++ choose_buffer_next(ch); ++ } ++ ++ if (cl->buffer_started && ch->buffer_xfer != NULL) { ++ cl->buffer_started(cl->cookie, ch->buffer_xfer); ++ } ++ ++ spin_unlock(&ch->lock); ++ return IRQ_HANDLED; ++} ++ ++static struct m2m_channel *find_free_channel(struct ep93xx_dma_m2m_client *cl, int channel_spec) ++{ ++ struct m2m_channel *ch = m2m_rxtx; ++ int i; ++ ++#if 0 ++ /* BMS: This code isn't particularly clear; look like it asserts ++ * that a requested channel must not share the same data direction ++ * as a previously requested channel - which makes sense for the SSP, ++ * but not at all for direct hardware transferrs ++ */ ++ for (i = 0; ch[i].base; i++) { ++ struct ep93xx_dma_m2m_client *cl2; ++ ++ cl2 = ch[i].client; ++ if (cl2 != NULL) { ++ int port; ++ ++ /* two the same devices in the same direction ++ are not allowed ++ (two "memory devices" should be allowed) */ ++ port = cl2->flags & (EP93XX_DMA_M2M_DEV_MASK | ++ EP93XX_DMA_M2M_DIR_MASK); ++ if (port == (cl->flags & (EP93XX_DMA_M2M_DEV_MASK | ++ EP93XX_DMA_M2M_DIR_MASK))) ++ return NULL; ++ } ++ } ++#endif ++ ++ if (channel_spec == EP93XX_DMA_M2M_REQUIRES_CH_ANY) { ++ for (i = 0; ch[i].base; i++) { ++ if (ch[i].client == NULL) ++ return ch + i; ++ } ++ } else if (channel_spec == EP93XX_DMA_M2M_REQUIRES_CH_0) { ++ if (ch[0].client == NULL) { ++ return &(ch[0]); ++ } ++ } else if (channel_spec == EP93XX_DMA_M2M_REQUIRES_CH_1) { ++ if (ch[1].client == NULL) { ++ return &(ch[1]); ++ } ++ } else { ++ printk(KERN_ERR "ep93xx-m2m dma channel request: unknown channel spec\n"); ++ } ++ return NULL; ++} ++ ++static u32 set_direction_reg(u32 outv, u32 flags) ++{ ++ switch (flags & EP93XX_DMA_M2M_DEV_MASK) { ++ case EP93XX_DMA_M2M_DEV_EXT: ++ outv &= ~(CTRL_SAH | CTRL_DAH | CTRL_TM_MASK); ++ ++ if (flags & EP93XX_DMA_M2M_EXT_FIFO) ++ outv |= (flags & EP93XX_DMA_M2M_DIR_MASK) == ++ EP93XX_DMA_M2M_TX ? CTRL_DAH : CTRL_SAH; ++ ++ outv |= (((flags & EP93XX_DMA_M2M_DIR_MASK) == ++ EP93XX_DMA_M2M_TX) ? TM_M2P : TM_P2M) << ++ CTRL_TM_SHIFT; ++ ++ break; ++ case EP93XX_DMA_M2M_DEV_IDE: ++ outv &= ~(CTRL_SAH | CTRL_DAH | CTRL_TM_MASK | CTRL_PWSC_MASK); ++ if ((flags & EP93XX_DMA_M2M_DIR_MASK) == EP93XX_DMA_M2M_TX) { ++ outv |= (2 << CTRL_PWSC_SHIFT) & CTRL_PWSC_MASK; ++ outv |= CTRL_DAH; ++ outv |= TM_M2P << CTRL_TM_SHIFT; ++ } else { ++ outv |= (1 << CTRL_PWSC_SHIFT) & CTRL_PWSC_MASK; ++ outv |= CTRL_SAH; ++ outv |= TM_P2M << CTRL_TM_SHIFT; ++ } ++ break; ++ case EP93XX_DMA_M2M_DEV_SSP: ++ outv &= ~(CTRL_SAH | CTRL_DAH | CTRL_TM_MASK | CTRL_RSS_MASK); ++ if ((flags & EP93XX_DMA_M2M_DIR_MASK) == EP93XX_DMA_M2M_TX) { ++ outv |= TM_M2P << CTRL_TM_SHIFT; ++ outv |= CTRL_DAH; ++ outv |= RSS_SSP_TX << CTRL_RSS_SHIFT; ++ } else { ++ outv |= TM_P2M << CTRL_TM_SHIFT; ++ outv |= CTRL_SAH; ++ outv |= RSS_SSP_RX << CTRL_RSS_SHIFT; ++ } ++ break; ++ case EP93XX_DMA_M2M_DEV_MEM: ++ break; ++ } ++ return outv; ++} ++ ++static void channel_enable(struct m2m_channel *ch) ++{ ++ struct ep93xx_dma_m2m_client *cl = ch->client; ++ u32 outv = 0; ++ ++ clk_enable(ch->clk); ++ ++ /* set peripheral wait state mask - IFF specified in control word */ ++ outv |= (cl->flags & CTRL_PWSC_MASK); ++ outv |= (cl->flags & EP93XX_DREQ_MASK); ++ ++ DPRINTK("Set outv to: %08x\n",outv); ++ ++ switch (cl->flags & EP93XX_DMA_M2M_DEV_MASK) { ++ case EP93XX_DMA_M2M_DEV_EXT: ++ switch (cl->flags & EP93XX_DMA_M2M_EXT_WIDTH_MASK) { ++ case EP93XX_DMA_M2M_EXT_WIDTH_BYTE: ++ outv |= PW_BYTE << CTRL_PW_SHIFT; ++ break; ++ case EP93XX_DMA_M2M_EXT_WIDTH_2BYTES: ++ outv |= PW_HALFWORD << CTRL_PW_SHIFT; ++ break; ++ case EP93XX_DMA_M2M_EXT_WIDTH_4BYTES: ++ outv |= PW_WORD << CTRL_PW_SHIFT; ++ break; ++ } ++ /* if NO_HDSK then PWSC, if not, then DREQ, DACK, TC/DEOT */ ++ if (cl->flags & EP93XX_DMA_M2M_EXT_NO_HDSK) { ++ outv |= CTRL_NO_HDSK; ++ /* TODO: wait states */ ++ } else { ++ /* TODO: regular handshaking */ ++ } ++ outv |= RSS_EXT << CTRL_RSS_SHIFT; ++ break; ++ case EP93XX_DMA_M2M_DEV_IDE: ++ /* NO_HDSK, PWSC, PW, SAH, DAH */ ++ outv |= CTRL_NO_HDSK; ++ outv |= PW_WORD << CTRL_PW_SHIFT; ++ /* PWSC = 1 for read, PWSC = 2 for write in UDMA */ ++ outv |= RSS_IDE << CTRL_RSS_SHIFT; ++ break; ++ case EP93XX_DMA_M2M_DEV_SSP: ++ outv |= CTRL_NO_HDSK; ++ outv |= PW_HALFWORD << CTRL_PW_SHIFT; ++ outv |= (8 << CTRL_PWSC_SHIFT) & CTRL_PWSC_MASK; ++ break; ++ case EP93XX_DMA_M2M_DEV_MEM: ++ switch (cl->flags & EP93XX_DMA_M2M_MEM_SPEED_MASK) { ++ case EP93XX_DMA_M2M_MEM_SPEED_FULL: ++ outv |= BWC_FULL << CTRL_BWC_SHIFT; ++ break; ++ case EP93XX_DMA_M2M_MEM_SPEED_HALF: ++ outv |= BWC_32768 << CTRL_BWC_SHIFT; ++ break; ++ case EP93XX_DMA_M2M_MEM_SPEED_QUART: ++ outv |= BWC_16384 << CTRL_BWC_SHIFT; ++ break; ++ case EP93XX_DMA_M2M_MEM_SPEED_SLOW: ++ outv |= BWC_16 << CTRL_BWC_SHIFT; ++ break; ++ } ++ outv |= (cl->flags & EP93XX_DMA_M2M_MEM_FILL) ? CTRL_SCT : 0; ++ outv |= TM_M2M << CTRL_TM_SHIFT; ++ break; ++ } ++ ++ // debug code ++ DPRINTK("PRE-Enable, status is: %08x\n", readl(ch->base+M2M_STATUS)); ++ ++ outv = set_direction_reg(outv, cl->flags); ++ /* STALL interrupt must be enabled */ ++ outv |= CTRL_NFB_INT_EN | CTRL_DONE_INT_EN | CTRL_STALL_INT_EN; ++ ++ writel(outv, ch->base + M2M_CONTROL); ++ outv = readl(ch->base + M2M_CONTROL); ++ DPRINTK("channel enable, writing control reg = %08x\n", outv); ++} ++ ++static void channel_disable(struct m2m_channel *ch) ++{ ++ u32 v; ++ ++ DPRINTK("Disabling channel\n"); ++ v = readl(ch->base + M2M_CONTROL); ++ ++ writel(v & ~(CTRL_NFB_INT_EN | CTRL_DONE_INT_EN | CTRL_STALL_INT_EN), ++ ch->base + M2M_CONTROL); ++ ++ v = readl(ch->base + M2M_CONTROL); ++ ++ while (readl(ch->base + M2M_STATUS) & STAT_NFB) { ++ cpu_relax(); ++ } ++ ++ writel(0, ch->base + M2M_CONTROL); ++ ++ v = readl(ch->base + M2M_CONTROL); ++ ++ while (readl(ch->base + M2M_STATUS) & STAT_STALL) { ++ cpu_relax(); ++ } ++ ++ clk_disable(ch->clk); ++} ++ ++void ep93xx_dma_m2m_set_direction(struct ep93xx_dma_m2m_client *cl, ++ int direction) ++{ ++ struct m2m_channel *ch = cl->channel; ++ u32 outv; ++ unsigned long flags; ++ ++ direction &= EP93XX_DMA_M2M_DIR_MASK; ++ ++ spin_lock_irqsave(&ch->lock, flags); ++ ++ cl->flags &= ~EP93XX_DMA_M2M_DIR_MASK; ++ cl->flags |= direction; ++ ++ outv = readl(ch->base + M2M_CONTROL); ++ outv = set_direction_reg(outv, cl->flags); ++ writel(outv, ch->base + M2M_CONTROL); ++ outv = readl(ch->base + M2M_CONTROL); ++ DPRINTK("set_direction: configured control reg = %08x\n", outv); ++ ++ spin_unlock_irqrestore(&ch->lock, flags); ++} ++EXPORT_SYMBOL_GPL(ep93xx_dma_m2m_set_direction); ++ ++int ep93xx_dma_m2m_client_register(struct ep93xx_dma_m2m_client *cl, int channel_spec) ++{ ++ struct m2m_channel *ch; ++ int err; ++ ++ ch = find_free_channel(cl, channel_spec); ++ if (ch == NULL) ++ return -1; ++ ++ err = request_irq(ch->irq, m2m_irq, IRQF_DISABLED, cl->name ? : "dma-m2m", ch); ++ if (err) ++ return err; ++ ++ ch->client = cl; ++ ch->next_slot = 0; ++ ch->buffer_xfer = NULL; ++ ch->buffer_next = NULL; ++ INIT_LIST_HEAD(&ch->buffers_pending); ++ ++ cl->channel = ch; ++ ++ channel_enable(ch); ++ ++ return 0; ++} ++EXPORT_SYMBOL_GPL(ep93xx_dma_m2m_client_register); ++ ++void ep93xx_dma_m2m_client_unregister(struct ep93xx_dma_m2m_client *cl) ++{ ++ struct m2m_channel *ch = cl->channel; ++ ++ channel_disable(ch); ++ free_irq(ch->irq, ch); ++ ch->client = NULL; ++} ++EXPORT_SYMBOL_GPL(ep93xx_dma_m2m_client_unregister); ++ ++void ep93xx_dma_m2m_submit(struct ep93xx_dma_m2m_client *cl, ++ struct ep93xx_dma_buffer *buf) ++{ ++ struct m2m_channel *ch = cl->channel; ++ unsigned long flags; ++ ++ spin_lock_irqsave(&ch->lock, flags); ++ ++ if (ch->buffer_xfer == NULL) { ++ ch->buffer_xfer = buf; ++ feed_buf(ch, buf); ++ if (readl(ch->base + M2M_CONTROL) & CTRL_ENABLE) { ++ DPRINTK("CTRL_ENABLE\n"); ++ if ((cl->flags & EP93XX_DMA_M2M_DEV_MASK) == ++ EP93XX_DMA_M2M_DEV_MEM) { ++ DPRINTK("WRITING START2 TO M2M control\n"); ++ writel(readl(ch->base + M2M_CONTROL) | ++ CTRL_START, ch->base + M2M_CONTROL); ++ readl(ch->base + M2M_CONTROL); ++ } ++ } ++ } else if (ch->buffer_next == NULL) { ++ ch->buffer_next = buf; ++ feed_buf(ch, buf); ++ } else ++ list_add_tail(&buf->list, &ch->buffers_pending); ++ spin_unlock_irqrestore(&ch->lock, flags); ++} ++EXPORT_SYMBOL_GPL(ep93xx_dma_m2m_submit); ++ ++void ep93xx_dma_m2m_start(struct ep93xx_dma_m2m_client *cl) ++{ ++ struct m2m_channel *ch = cl->channel; ++ u32 v; ++ ++ unsigned long flags; ++ ++ spin_lock_irqsave(&ch->lock, flags); ++ ++ writel(readl(ch->base + M2M_STATUS), ch->base+M2M_STATUS); ++ //printk("At start, status is: %08x\n", readl(ch->base + M2M_STATUS)); ++ ++ v = readl(ch->base + M2M_CONTROL) | CTRL_ENABLE; ++ writel(v, ch->base + M2M_CONTROL); ++ v = readl(ch->base + M2M_CONTROL); ++ if (ch->buffer_xfer != NULL) { ++ if (((cl->flags & EP93XX_DMA_M2M_DEV_MASK) == ++ EP93XX_DMA_M2M_DEV_MEM)) { ++ DPRINTK("WRITING START3 to M2M controller\n"); ++ v |= CTRL_START; ++ writel(v, ch->base + M2M_CONTROL); ++ v = readl(ch->base + M2M_CONTROL); ++ } ++ } ++ ++ spin_unlock_irqrestore(&ch->lock, flags); ++} ++EXPORT_SYMBOL_GPL(ep93xx_dma_m2m_start); ++ ++void ep93xx_dma_m2m_stop(struct ep93xx_dma_m2m_client *cl) ++{ ++ struct m2m_channel *ch = cl->channel; ++ u32 v; ++ unsigned long flags; ++ ++ spin_lock_irqsave(&ch->lock, flags); ++ ++ DPRINTK("Stopping DMA by disabling CTRL_ENABLE\n"); ++ v = readl(ch->base + M2M_CONTROL) & ~CTRL_ENABLE; ++ writel(v, ch->base + M2M_CONTROL); ++ readl(ch->base + M2M_CONTROL); ++ DPRINTK("configured control reg = %08x\n", v); ++ ++ spin_unlock_irqrestore(&ch->lock, flags); ++} ++EXPORT_SYMBOL_GPL(ep93xx_dma_m2m_stop); ++ ++void ep93xx_dma_m2m_flush(struct ep93xx_dma_m2m_client *cl) ++{ ++ struct m2m_channel *ch = cl->channel; ++ ++ channel_disable(ch); ++ ch->next_slot = 0; ++ ch->buffer_xfer = NULL; ++ ch->buffer_next = NULL; ++ INIT_LIST_HEAD(&ch->buffers_pending); ++ channel_enable(ch); ++} ++EXPORT_SYMBOL_GPL(ep93xx_dma_m2m_flush); ++ ++static int init_channel(struct m2m_channel *ch) ++{ ++ ch->clk = clk_get(NULL, ch->name); ++ if (IS_ERR(ch->clk)) ++ return PTR_ERR(ch->clk); ++ ++ spin_lock_init(&ch->lock); ++ ch->client = NULL; ++ ++ return 0; ++} ++ ++static int __init ep93xx_dma_m2m_init(void) ++{ ++ int i; ++ int ret; ++ ++ for (i = 0; m2m_rxtx[i].base; i++) { ++ ret = init_channel(m2m_rxtx + i); ++ if (ret) ++ return ret; ++ } ++ ++ pr_info("M2M DMA subsystem initialized\n"); ++ return 0; ++} ++arch_initcall(ep93xx_dma_m2m_init); +diff --git a/arch/arm/mach-ep93xx/include/mach/dma.h b/arch/arm/mach-ep93xx/include/mach/dma.h +index 3a5961d..6a3552b 100644 +--- a/arch/arm/mach-ep93xx/include/mach/dma.h ++++ b/arch/arm/mach-ep93xx/include/mach/dma.h +@@ -11,6 +11,7 @@ + struct ep93xx_dma_buffer { + struct list_head list; + u32 bus_addr; ++ u32 bus_addr2; /* only used by M2M */ + u16 size; + }; + +@@ -28,6 +29,7 @@ struct ep93xx_dma_m2p_client { + void *channel; + }; + ++/* flags (m2p client) */ + #define EP93XX_DMA_M2P_PORT_I2S1 0x00 + #define EP93XX_DMA_M2P_PORT_I2S2 0x01 + #define EP93XX_DMA_M2P_PORT_AAC1 0x02 +@@ -45,6 +47,58 @@ struct ep93xx_dma_m2p_client { + #define EP93XX_DMA_M2P_IGNORE_ERROR 0x40 + #define EP93XX_DMA_M2P_ERROR_MASK 0x60 + ++ ++struct ep93xx_dma_m2m_client { ++ char *name; ++ u32 flags; ++ void *cookie; ++ void (*buffer_started)(void *cookie, ++ struct ep93xx_dma_buffer *buf); ++ void (*buffer_finished)(void *cookie, ++ struct ep93xx_dma_buffer *buf, ++ int bytes, int error); ++ ++ /* Internal to the DMA code. */ ++ void *channel; ++}; ++ ++/* flags (m2m client) */ ++#define EP93XX_DMA_M2M_RX 0x000 /* read from periph./memory */ ++#define EP93XX_DMA_M2M_TX 0x004 /* write to periph./memory */ ++#define EP93XX_DMA_M2M_DIR_MASK 0x004 /* direction mask */ ++#define EP93XX_DMA_M2M_DEV_EXT 0x000 /* external peripheral */ ++#define EP93XX_DMA_M2M_DEV_SSP 0x001 /* internal SSP */ ++#define EP93XX_DMA_M2M_DEV_IDE 0x002 /* internal IDE */ ++#define EP93XX_DMA_M2M_DEV_MEM 0x003 /* memory to memory transfer */ ++#define EP93XX_DMA_M2M_DEV_MASK 0x003 /* device mask */ ++#define EP93XX_DMA_M2M_EXT_FIFO 0x008 /* external peripheral is one location fifo */ ++#define EP93XX_DMA_M2M_EXT_NO_HDSK 0x010 /* external peripheral doesn't require regular handshaking protocol */ ++#define EP93XX_DMA_M2M_EXT_WIDTH_MASK 0x300 ++#define EP93XX_DMA_M2M_EXT_WIDTH_BYTE 0x000 /* external peripheral transfer is one byte width */ ++#define EP93XX_DMA_M2M_EXT_WIDTH_2BYTES 0x100 ++#define EP93XX_DMA_M2M_EXT_WIDTH_4BYTES 0x200 ++#define EP93XX_DMA_M2M_MEM_SPEED_FULL 0x000 /* M2M bandwidth control */ ++#define EP93XX_DMA_M2M_MEM_SPEED_HALF 0x040 /* half bus bandwidth */ ++#define EP93XX_DMA_M2M_MEM_SPEED_QUART 0x080 /* quarter bus bandwidth */ ++#define EP93XX_DMA_M2M_MEM_SPEED_SLOW 0x0c0 /* slowest speed */ ++#define EP93XX_DMA_M2M_MEM_SPEED_MASK 0x0c0 /* memory speed mask */ ++#define EP93XX_DMA_M2M_MEM_FILL 0x020 /* M2M is one location to block fill */ ++ ++/* FIXME */ ++#define CTRL_PWSC_MASK 0xfe000000 /* peripheral wait states count */ ++#define CTRL_PWSC_SHIFT 25 ++#define EP93XX_DREQ_SHIFT 19 ++#define EP93XX_DREQ_MASK 0x00180000 ++#define EP93XX_DMA_M2M_DREQ_LS_L (00 << EP93XX_DREQ_SHIFT) ++#define EP93XX_DMA_M2M_DREQ_LS_H (01 << EP93XX_DREQ_SHIFT) ++#define EP93XX_DMA_M2M_DREQ_ES_L (10 << EP93XX_DREQ_SHIFT) ++#define EP93XX_DMA_M2M_DREQ_ES_H (11 << EP93XX_DREQ_SHIFT) ++ ++/* See ep93xx_dma_m2m_client_register (channel_spec) */ ++#define EP93XX_DMA_M2M_REQUIRES_CH_ANY 0 ++#define EP93XX_DMA_M2M_REQUIRES_CH_0 1 ++#define EP93XX_DMA_M2M_REQUIRES_CH_1 2 ++ + int ep93xx_dma_m2p_client_register(struct ep93xx_dma_m2p_client *m2p); + void ep93xx_dma_m2p_client_unregister(struct ep93xx_dma_m2p_client *m2p); + void ep93xx_dma_m2p_submit(struct ep93xx_dma_m2p_client *m2p, +@@ -53,4 +107,15 @@ void ep93xx_dma_m2p_submit_recursive(struct ep93xx_dma_m2p_client *m2p, + struct ep93xx_dma_buffer *buf); + void ep93xx_dma_m2p_flush(struct ep93xx_dma_m2p_client *m2p); + ++int ep93xx_dma_m2m_client_register(struct ep93xx_dma_m2m_client *m2m, ++ int channel_spec); ++void ep93xx_dma_m2m_client_unregister(struct ep93xx_dma_m2m_client *m2m); ++void ep93xx_dma_m2m_submit(struct ep93xx_dma_m2m_client *m2m, ++ struct ep93xx_dma_buffer *buf); ++void ep93xx_dma_m2m_flush(struct ep93xx_dma_m2m_client *m2m); ++void ep93xx_dma_m2m_start(struct ep93xx_dma_m2m_client *m2m); ++void ep93xx_dma_m2m_stop(struct ep93xx_dma_m2m_client *m2m); ++void ep93xx_dma_m2m_set_direction(struct ep93xx_dma_m2m_client *m2m, ++ int direction); ++ + #endif /* __ASM_ARCH_DMA_H */ +-- +1.7.1 + |