[PATCH] ARM: LPX32xx: DMA support
Russell King - ARM Linux
linux at arm.linux.org.uk
Wed Mar 7 16:52:49 EST 2012
On Wed, Mar 07, 2012 at 09:25:29PM +0100, Roland Stigge wrote:
> This patch adds DMA support to the LPC32xx platform
>
> Signed-off-by: Roland Stigge <stigge at antcom.de>
>
> ---
>
> Applies to v3.3-rc6. This patch is a precondition for the NAND and audio
> drivers (to be sent in one of my next mails).
Is there a reason this can't be converted to a dmaengine driver?
> +#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 <mach/platform.h>
> +#include <asm/dma-mapping.h>
Not required.
> +#include <asm/io.h>
linux/io.h?
> +static int __init lpc32xx_dma_init(void)
> +{
> + int ret;
> +
> + ret = request_irq(IRQ_LPC32XX_DMA, dma_irq_handler, 0, "DMA", NULL);
> + if (ret) {
> + printk(KERN_CRIT "Wow! Can't register IRQ for DMA\n");
Why KERN_CRIT?
Also consider printing the reason why it failed (errno stored in ret.)
> + goto out;
> + }
> +
> + /* Get DMA clock */
> + dma_ctrl.clk = clk_get(NULL, "clk_dmac");
> + if (IS_ERR(dma_ctrl.clk)) {
> + ret = -ENODEV;
ret = PTR_ERR(dma_ctrl.clk);
> +struct dma_linked_list {
> + __le32 src;
> + __le32 dest;
> + __le32 next_lli;
> + __le32 ctrl;
> +};
This looks familiar.
> +struct dma_config {
> + int ch; /* Channel # to use */
> + int tc_inten; /* !0 = Enable TC interrupts for this channel */
> + int err_inten; /* !0 = Enable error interrupts for this channel */
> + int src_size; /* Source xfer size - must be 1, 2, or 4 */
> + int src_inc; /* !0 = Enable source address increment */
> + int src_ahb1; /* !0 = Use AHB1 for source transfer */
Oh, two AHBs.
> +/* DMA controller register structures */
> +#define DMA_INT_STAT(x) (x + 0x00)
> +#define DMA_INT_TC_STAT(x) (x + 0x04)
> +#define DMA_INT_TC_CLEAR(x) (x + 0x08)
> +#define DMA_INT_ERR_STAT(x) (x + 0x0C)
> +#define DMA_INT_ERR_CLEAR(x) (x + 0x10)
> +#define DMA_RAW_TC_STAT(x) (x + 0x14)
> +#define DMA_RAW_ERR_STAT(x) (x + 0x18)
> +#define DMA_CH_ENABLE(x) (x + 0x1C)
> +#define DMA_SW_BURST_REQ(x) (x + 0x20)
> +#define DMA_SW_SINGLE_REQ(x) (x + 0x24)
> +#define DMA_SW_LAST_BURST_REQ(x) (x + 0x28)
> +#define DMA_SW_LAST_SINGLE_REQ(x) (x + 0x2C)
> +#define DMA_CONFIG(x) (x + 0x30)
> +#define DMA_SYNC(x) (x + 0x34)
> +
> +/* DMA controller channel register structure */
> +#define DMA_CH_OFFS(c) ((c * 0x20) + 0x100)
> +#define DMACH_SRC_ADDR(x, c) (x + DMA_CH_OFFS(c) + 0x00)
> +#define DMACH_DEST_ADDR(x, c) (x + DMA_CH_OFFS(c) + 0x04)
> +#define DMACH_LLI(x, c) (x + DMA_CH_OFFS(c) + 0x08)
> +#define DMACH_CONTROL(x, c) (x + DMA_CH_OFFS(c) + 0x0C)
> +#define DMACH_CONFIG_CH(x, c) (x + DMA_CH_OFFS(c) + 0x10)
Oh, the register layout is identical to the AMBA PL08x primecell.
> +
> +/* DMA linked list structure */
> +#define DMA_LL_SRC(x) (x + 0x0)
> +#define DMA_LL_DEST(x) (x + 0x4)
> +#define DMA_LL_NEXT_LLI(x) (x + 0x8)
> +#define DMA_LL_NEXT_CTRL(x) (x + 0xC)
> +
> +#define DMA_LL_SIZE 16
> +
> +/**********************************************************************
> +* int_stat, int_tc_stat, int_tc_clear, int_err_stat, raw_tc_stat,
> +* raw_err_stat, and chan_enable register definitions
> +**********************************************************************/
> +/* Macro for determining a bit position for a channel */
> +#define DMAC_GET_CHAN_POS(chan) (0x1 << ((chan) & 0x7))
> +
> +/**********************************************************************
> +* sw_burst_req, sw_single_req, sw_last_burst_req, sw_last_single_req,
> +* and sync register definitions
> +**********************************************************************/
> +/* Peripheral DMA bit position for I2S0 DMA0 */
> +#define DMA_PER_I2S0_DMA0 _BIT(0)
> +
> +/* Peripheral DMA bit position for NAND FLASH (same as 12) */
> +#define DMA_PER_NAND1 _BIT(1)
> +
> +/* Peripheral DMA bit position for I2S1 DMA0 */
> +#define DMA_PER_I2S1_DMA0 _BIT(2)
> +
> +/* Peripheral DMA bit position for SPI2 (RX and TX) */
> +#define DMA_PER_SPI2_TXRX _BIT(3)
> +
> +/* Peripheral DMA bit position for SSP1 (RX) */
> +#define DMA_PER_SSP1_RX _BIT(3)
> +
> +/* Peripheral DMA bit position for SD card */
> +#define DMA_PER_SDCARD _BIT(4)
> +
> +/* Peripheral DMA bit position for HSUART1 TX */
> +#define DMA_PER_HSUART1_TX _BIT(5)
> +
> +/* Peripheral DMA bit position for HSUART1 RX */
> +#define DMA_PER_HSUART1_RX _BIT(6)
> +
> +/* Peripheral DMA bit position for HSUART2 TX */
> +#define DMA_PER_HSUART2_TX _BIT(7)
> +
> +/* Peripheral DMA bit position for HSUART2 RX */
> +#define DMA_PER_HSUART2_RX _BIT(8)
> +
> +/* Peripheral DMA bit position for HSUART7 TX */
> +#define DMA_PER_HSUART7_TX _BIT(9)
> +
> +/* Peripheral DMA bit position for HSUART7 RX */
> +#define DMA_PER_HSUART7_RX _BIT(10)
> +
> +/* Peripheral DMA bit position for I2S1 DMA1 */
> +#define DMA_PER_I2S1_DMA1 _BIT(10)
> +
> +/* Peripheral DMA bit position for SPI1 (RX and TX) */
> +#define DMA_PER_SPI1_TXRX _BIT(11)
> +
> +/* Peripheral DMA bit position for SSP1 (TX) */
> +#define DMA_PER_SSP1_TX _BIT(11)
> +
> +/* Peripheral DMA bit position for NAND FLASH (same as 1) */
> +#define DMA_PER_NAND2 _BIT(12)
> +
> +/* Peripheral DMA bit position for I2S0 DMA1 */
> +#define DMA_PER_I2S0_DMA1 _BIT(13)
> +
> +/* Peripheral DMA bit position for SSP0 (RX) */
> +#define DMA_PER_SSP0_RX _BIT(14)
> +
> +/* Peripheral DMA bit position for SSP0 (TX) */
> +#define DMA_PER_SSP0_TX _BIT(15)
> +
> +/**********************************************************************
> +* config register definitions
> +**********************************************************************/
> +/* Bit for enabling big endian mode on AHB 1 */
> +#define DMAC_BIG_ENDIAN_AHB1 _BIT(2)
> +
> +/* Bit for enabling big endian mode on AHB 0 */
> +#define DMAC_BIG_ENDIAN_AHB0 _BIT(1)
> +
> +/* Bit for enabling the DMA controller */
> +#define DMAC_CTRL_ENABLE _BIT(0)
> +
> +/**********************************************************************
> +* lli register definitions
> +**********************************************************************/
> +/* Bit for selecting AHB0 (0) or AHB1 (1) */
> +#define DMAC_CHAN_LLI_SEL_AHB1 _BIT(0)
> +
> +/**********************************************************************
> +* control register definitions
> +**********************************************************************/
> +/* Bit for enabling a channel terminal count interrupt */
> +#define DMAC_CHAN_INT_TC_EN _BIT(31)
> +
> +/* Bit for indicating address is cacheable */
> +#define DMAC_CHAN_PROT3 _BIT(30)
> +
> +/* Bit for indicating address is bufferable */
> +#define DMAC_CHAN_PROT2 _BIT(29)
> +
> +/* Bit for indicating address is privelaged mode (1) or user
> + mode (0) */
> +#define DMAC_CHAN_PROT1 _BIT(28)
> +
> +/* Bit for enabling automatic destination increment */
> +#define DMAC_CHAN_DEST_AUTOINC _BIT(27)
> +
> +/* Bit for enabling automatic source increment */
> +#define DMAC_CHAN_SRC_AUTOINC _BIT(26)
> +
> +/* Bit for using AHB1 master for destination transfer */
> +#define DMAC_CHAN_DEST_AHB1 _BIT(25)
> +
> +/* Bit for using AHB1 master for source transfer */
> +#define DMAC_CHAN_SRC_AHB1 _BIT(24)
> +
> +/* Destination data width selection defines */
> +#define DMAC_CHAN_DEST_WIDTH_8 0x0
> +#define DMAC_CHAN_DEST_WIDTH_16 _BIT(21)
> +#define DMAC_CHAN_DEST_WIDTH_32 _BIT(22)
> +
> +/* Source data width selection defines */
> +#define DMAC_CHAN_SRC_WIDTH_8 0x0
> +#define DMAC_CHAN_SRC_WIDTH_16 _BIT(18)
> +#define DMAC_CHAN_SRC_WIDTH_32 _BIT(19)
> +
> +/* Destination data burst size defines (in transfer width) */
> +#define DMAC_CHAN_DEST_BURST_1 0
> +#define DMAC_CHAN_DEST_BURST_4 _BIT(15)
> +#define DMAC_CHAN_DEST_BURST_8 _BIT(16)
> +#define DMAC_CHAN_DEST_BURST_16 (_BIT(16) | _BIT(15))
> +#define DMAC_CHAN_DEST_BURST_32 _BIT(17)
> +#define DMAC_CHAN_DEST_BURST_64 (_BIT(17) | _BIT(15))
> +#define DMAC_CHAN_DEST_BURST_128 (_BIT(17) | _BIT(16))
> +#define DMAC_CHAN_DEST_BURST_256 (_BIT(17) | _BIT(16) | _BIT(15))
> +
> +/* Macro for direct loading of destination burst size field */
> +#define DMAC_CHAN_DEST_BURST_LOAD(n) (((n) & 0x7) << 15)
> +
> +/* Source data burst size defines (in transfer width) */
> +#define DMAC_CHAN_SRC_BURST_1 0
> +#define DMAC_CHAN_SRC_BURST_4 _BIT(12)
> +#define DMAC_CHAN_SRC_BURST_8 _BIT(13)
> +#define DMAC_CHAN_SRC_BURST_16 (_BIT(13) | _BIT(12))
> +#define DMAC_CHAN_SRC_BURST_32 _BIT(14)
> +#define DMAC_CHAN_SRC_BURST_64 (_BIT(14) | _BIT(12))
> +#define DMAC_CHAN_SRC_BURST_128 (_BIT(14) | _BIT(13))
> +#define DMAC_CHAN_SRC_BURST_256 (_BIT(14) | _BIT(13) | _BIT(12))
> +
> +/* Macro for direct loading of source burst size field */
> +#define DMAC_CHAN_SRC_BURST_LOAD(n) (((n) & 0x7) << 12)
> +
> +/* Macro for loading transfer size */
> +#define DMAC_CHAN_TRANSFER_SIZE(n) ((n) & 0xFFF)
Same bit definitions for the control register....
> +
> +/**********************************************************************
> +* config_ch register definitions
> +**********************************************************************/
> +/* Bit for halting a DMA transfer */
> +#define DMAC_CHAN_HALT _BIT(18)
> +
> +/* Bit for checking active status of the DMA channel */
> +#define DMAC_CHAN_ACTIVE _BIT(17)
> +
> +/* Bit for enabling locked transfers */
> +#define DMAC_CHAN_LOCK _BIT(16)
> +
> +/* Terminal count interrupt mask bit */
> +#define DMAC_CHAN_ITC _BIT(15)
> +
> +/* Interrupt error mask bit */
> +#define DMAC_CHAN_IE _BIT(14)
> +
> +/* Defines for flow control with DMA as the controller */
> +#define DMAC_CHAN_FLOW_D_M2M (0x0 << 11)
> +#define DMAC_CHAN_FLOW_D_M2P (0x1 << 11)
> +#define DMAC_CHAN_FLOW_D_P2M (0x2 << 11)
> +#define DMAC_CHAN_FLOW_D_SP2DP (0x3 << 11)
> +
> +/* Defines for flow control with destination peripheral as the
> + controller */
> +#define DMAC_CHAN_FLOW_DP_SP2DP (0x4 << 11)
> +
> +/* Defines for flow control with peripheral as the controller */
> +#define DMAC_CHAN_FLOW_P_M2P (0x5 << 11)
> +#define DMAC_CHAN_FLOW_P_P2M (0x6 << 11)
> +
> +/* Defines for flow control with source peripheral as the
> + controller */
> +#define DMAC_CHAN_FLOW_SP_SP2DP (0x7 << 11)
> +
> +/* Macro for loading destination peripheral */
> +#define DMAC_DEST_PERIP(n) (((n) & 0x1F) << 6)
> +
> +/* Macro for loading source peripheral */
> +#define DMAC_SRC_PERIP(n) (((n) & 0x1F) << 1)
> +
> +/* Channel enable bit */
> +#define DMAC_CHAN_ENABLE _BIT(0)
And the channel configuration register...
So, no need to convert this to a DMA engine driver, because there's
already one provided in the mainline kernel source. Please make use
of it rather than reinventing the square wheel.
Thanks.
More information about the linux-arm-kernel
mailing list