diff mbox

[U-Boot,2/3] dma: lpc32xx: add DMA driver

Message ID 1437166134-21204-3-git-send-email-slemieux.tyco@gmail.com
State Superseded
Headers show

Commit Message

Sylvain Lemieux July 17, 2015, 8:48 p.m. UTC
From: Sylvain Lemieux <slemieux@tycoint.com>

Incorporate DMA driver from legacy LPCLinux NXP BSP.
The files taken from the legacy patch are:
- lpc32xx DMA driver
- lpc3250 header file DMA registers definition.

Updated driver to integrate with the latest u-boot:
1) Fixed checkpatch script output in legacy code.
2) Use LPC32xx definition from "cpu.h" and "clk.h".
3) Incorporate NAND specific register definition from "lpc3250.h" header file
   from legacy BSP patch from LPCLinux.
4) Use u-boot API for register access to remove the use of volatile in
   register definition taken from "lpc3250.h" header file.
4) Add DMA interface to "dma.h".
5) Add dma clock control register bits (clk.h).
6) Add functions to initialize the DMA clock.

The legacy BSP patch (u-boot-2009.03_lpc32x0-v1.07.patch.tar.bz2)
was downloaded from the LPCLinux Web site.

Signed-off-by: Sylvain Lemieux <slemieux@tycoint.com>
---
 arch/arm/cpu/arm926ejs/lpc32xx/devices.c      |   6 +
 arch/arm/include/asm/arch-lpc32xx/clk.h       |   3 +
 arch/arm/include/asm/arch-lpc32xx/dma.h       |  33 ++++++
 arch/arm/include/asm/arch-lpc32xx/sys_proto.h |   1 +
 drivers/dma/Makefile                          |   1 +
 drivers/dma/lpc32xx_dma.c                     | 153 ++++++++++++++++++++++++++
 6 files changed, 197 insertions(+)
 create mode 100644 arch/arm/include/asm/arch-lpc32xx/dma.h
 create mode 100644 drivers/dma/lpc32xx_dma.c
diff mbox

Patch

diff --git a/arch/arm/cpu/arm926ejs/lpc32xx/devices.c b/arch/arm/cpu/arm926ejs/lpc32xx/devices.c
index c0c9c6c..0d2ef7a 100644
--- a/arch/arm/cpu/arm926ejs/lpc32xx/devices.c
+++ b/arch/arm/cpu/arm926ejs/lpc32xx/devices.c
@@ -65,6 +65,12 @@  void lpc32xx_slc_nand_init(void)
 	writel(CLK_NAND_SLC | CLK_NAND_SLC_SELECT, &clk->flashclk_ctrl);
 }
 
+void lpc32xx_dma_init(void)
+{
+	/* Enable DMA interface */
+	setbits_le32(&clk->dmaclk_ctrl, DMA_CLK_ENABLE);
+}
+
 void lpc32xx_i2c_init(unsigned int devnum)
 {
 	/* Enable I2C interface */
diff --git a/arch/arm/include/asm/arch-lpc32xx/clk.h b/arch/arm/include/asm/arch-lpc32xx/clk.h
index 010211a..663f6bc 100644
--- a/arch/arm/include/asm/arch-lpc32xx/clk.h
+++ b/arch/arm/include/asm/arch-lpc32xx/clk.h
@@ -158,6 +158,9 @@  struct clk_pm_regs {
 #define CLK_NAND_SLC_SELECT		(1 << 2)
 #define CLK_NAND_MLC_INT		(1 << 5)
 
+/* DMA Clock Control Register bits */
+#define DMA_CLK_ENABLE			(1 << 0)
+
 /* SSP Clock Control Register bits */
 #define CLK_SSP0_ENABLE_CLOCK		(1 << 0)
 
diff --git a/arch/arm/include/asm/arch-lpc32xx/dma.h b/arch/arm/include/asm/arch-lpc32xx/dma.h
new file mode 100644
index 0000000..2a93510
--- /dev/null
+++ b/arch/arm/include/asm/arch-lpc32xx/dma.h
@@ -0,0 +1,33 @@ 
+/*
+ * LPC32xx DMA Controller Interface
+ *
+ * Copyright (C) 2008-2015 by NXP Semiconductors
+ * All rights reserved.
+ *
+ * @Author: Kevin Wells
+ * @Descr: Definitions for LPC3250 chip
+ * @References: NXP LPC3250 User's Guide
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+#ifndef _LPC32XX_DMA_H
+#define _LPC32XX_DMA_H
+
+#include <common.h>
+
+/* DMA linked list structure used with a channel's LLI register */
+typedef struct
+{
+  volatile uint32_t dma_src;
+  volatile uint32_t dma_dest;
+  volatile uint32_t next_lli;
+  volatile uint32_t next_ctrl;
+} dmac_ll_t;
+
+int lpc32xx_dma_get_channel(void);
+int lpc32xx_dma_start_xfer(int channel, const dmac_ll_t *desc, uint32_t config);
+int lpc32xx_dma_wait_status(int channel);
+void lpc32xx_dma_put_channel(int channel);
+
+#endif /* _LPC32XX_DMA_H */
\ No newline at end of file
diff --git a/arch/arm/include/asm/arch-lpc32xx/sys_proto.h b/arch/arm/include/asm/arch-lpc32xx/sys_proto.h
index 0845f83..7f997d9 100644
--- a/arch/arm/include/asm/arch-lpc32xx/sys_proto.h
+++ b/arch/arm/include/asm/arch-lpc32xx/sys_proto.h
@@ -13,6 +13,7 @@  void lpc32xx_uart_init(unsigned int uart_id);
 void lpc32xx_mac_init(void);
 void lpc32xx_mlc_nand_init(void);
 void lpc32xx_slc_nand_init(void);
+void lpc32xx_dma_init(void);
 void lpc32xx_i2c_init(unsigned int devnum);
 void lpc32xx_ssp_init(void);
 #if defined(CONFIG_SPL_BUILD)
diff --git a/drivers/dma/Makefile b/drivers/dma/Makefile
index 4c8fcc2..f95fe70 100644
--- a/drivers/dma/Makefile
+++ b/drivers/dma/Makefile
@@ -10,3 +10,4 @@  obj-$(CONFIG_APBH_DMA) += apbh_dma.o
 obj-$(CONFIG_FSL_DMA) += fsl_dma.o
 obj-$(CONFIG_TI_KSNAV) += keystone_nav.o keystone_nav_cfg.o
 obj-$(CONFIG_TI_EDMA3) += ti-edma3.o
+obj-$(CONFIG_DMA_LPC32XX) += lpc32xx_dma.o
diff --git a/drivers/dma/lpc32xx_dma.c b/drivers/dma/lpc32xx_dma.c
new file mode 100644
index 0000000..feb16ce
--- /dev/null
+++ b/drivers/dma/lpc32xx_dma.c
@@ -0,0 +1,153 @@ 
+/*
+ * Copyright (C) 2008-2015 by NXP Semiconductors
+ * All rights reserved.
+ *
+ * @Author: Kevin Wells
+ * @Descr: LPC3250 DMA controller interface support functions
+ *
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+#include <common.h>
+#include <asm/arch/dma.h>
+#include <asm/arch/cpu.h>
+#include <asm/arch/clk.h>
+#include <asm/arch/sys_proto.h>
+#include <asm/io.h>
+
+/* Some optimization stuff */
+#ifndef unlikely
+#define likely(x)	__builtin_expect(!!(x), 1)
+#define unlikely(x)	__builtin_expect(!!(x), 0)
+#endif
+
+/* bit position macro */
+#define _BIT(n)		(0x1 << (n))
+
+/* DMA controller channel register structure */
+struct dmac_chan_reg {
+	uint32_t src_addr;
+	uint32_t dest_addr;
+	uint32_t lli;
+	uint32_t control;
+	uint32_t config_ch;
+	uint32_t reserved[3];
+};
+
+/* DMA controller register structures */
+struct dma_reg {
+	uint32_t int_stat;
+	uint32_t int_tc_stat;
+	uint32_t int_tc_clear;
+	uint32_t int_err_stat;
+	uint32_t int_err_clear;
+	uint32_t raw_tc_stat;
+	uint32_t raw_err_stat;
+	uint32_t chan_enable;
+	uint32_t sw_burst_req;
+	uint32_t sw_single_req;
+	uint32_t sw_last_burst_req;
+	uint32_t sw_last_single_req;
+	uint32_t config;
+	uint32_t sync;
+	uint32_t reserved[50];
+	struct dmac_chan_reg dma_chan[8];
+};
+
+/* Macro pointing to DMA registers */
+#define DMA_NO_OF_CHANNELS	8
+
+/* config register definitions */
+#define DMAC_CTRL_ENABLE	(1 << 0) /* For enabling the DMA controller */
+
+static uint32_t alloc_ch;
+
+static struct dma_reg *dma = (struct dma_reg *)DMA_BASE;
+
+int lpc32xx_dma_get_channel(void)
+{
+	int i;
+	uint32_t status = 0;
+
+	if (!alloc_ch) { /* First time caller */
+		/* DMA clock are enable by "lpc32xx_dma_init()" and should
+		 * be call by board "board_early_init_f()" function. */
+
+		/* Make sure DMA controller and all channels are disabled.
+		 * Controller is in little-endian mode. Disable sync signals */
+		writel(0, &dma->config);
+		writel(0, &dma->sync);
+
+		/* Clear interrupt and error statuses */
+		writel(0xFF, &dma->int_tc_clear);
+		writel(0xFF, &dma->raw_tc_stat);
+		writel(0xFF, &dma->int_err_clear);
+		writel(0xFF, &dma->raw_err_stat);
+
+		/* Enable DMA controller */
+		writel(DMAC_CTRL_ENABLE, &dma->config);
+	}
+
+	for (i = 0; i < DMA_NO_OF_CHANNELS && (status & _BIT(i)); i++)
+		;
+
+	/* Check if all the available channles are busy */
+	if (unlikely(i == DMA_NO_OF_CHANNELS))
+		return -1;
+	alloc_ch |= _BIT(i);
+	return i;
+}
+
+int lpc32xx_dma_start_xfer(int channel, const dmac_ll_t *desc, uint32_t config)
+{
+	if (unlikely((_BIT(channel) & alloc_ch) == 0)) {
+		printf("ERR: Request for xfer on unallocated channel %d\r\n",
+		       channel);
+		BUG();
+	}
+	writel(_BIT(channel), &dma->int_tc_clear);
+	writel(_BIT(channel), &dma->int_err_clear);
+	writel(desc->dma_src, &dma->dma_chan[channel].src_addr);
+	writel(desc->dma_dest, &dma->dma_chan[channel].dest_addr);
+	writel(desc->next_lli, &dma->dma_chan[channel].lli);
+	writel(desc->next_ctrl, &dma->dma_chan[channel].control);
+	writel(config, &dma->dma_chan[channel].config_ch);
+
+	return 0;
+}
+
+int lpc32xx_dma_wait_status(int channel)
+{
+	while (((readl(&dma->raw_tc_stat) | readl(dma->raw_err_stat))
+		& _BIT(channel)) == 0)
+		;
+
+	if (unlikely(readl(&dma->raw_err_stat) & _BIT(channel))) {
+		setbits_le32(&dma->int_err_clear, _BIT(channel));
+		setbits_le32(&dma->raw_err_stat, _BIT(channel));
+		return -1;
+	}
+	setbits_le32(&dma->int_tc_clear, _BIT(channel));
+	setbits_le32(&dma->raw_tc_stat, _BIT(channel));
+	return 0;
+}
+
+void lpc32xx_dma_put_channel(int channel)
+{
+	/* Check if given channel no is valid */
+	if (channel >= DMA_NO_OF_CHANNELS || channel < 0)
+		return;
+	alloc_ch &= ~_BIT(channel);
+
+	/* Shut down channel */
+	writel(0, &dma->dma_chan[channel].control);
+	writel(0, &dma->dma_chan[channel].config_ch);
+	clrbits_le32(&dma->sync, _BIT(channel));
+
+	if (!alloc_ch)
+		/* Disable DMA controller */
+		clrbits_le32(&dma->config, DMAC_CTRL_ENABLE);
+}