Message ID | 1438722282-3736-2-git-send-email-slemieux.tyco@gmail.com |
---|---|
State | Superseded |
Headers | show |
On Tuesday, August 04, 2015 at 11:04:38 PM, slemieux.tyco@gmail.com wrote: > 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. > > The legacy driver was updated and clean-up as part of the integration with > the latest u-boot. > > Signed-off-by: Sylvain Lemieux <slemieux@tycoint.com> [...] > + > +/* > + * The "dmac_ll" structure is mapping the DMA channel register, > + * as per UM10326, "LPC32x0 and LPC32x0/01 User manual" - Rev. 3 > + * tables 84, 85, 86 & 87. > + * > + * The DMA client (ex. NAND SLC) can use this macro to write the > + * next DMA linked list item address. > + */ > +#define lpc32xx_dmac_next_lli(x) (uint32_t)x Ew, and it's not even used :-) > +/* DMA linked list structure used with a channel's LLI register */ > +struct lpc32xx_dmac_ll { > + u32 dma_src; > + u32 dma_dest; > + u32 next_lli; > + u32 next_ctrl; > +}; [...] > +int lpc32xx_dma_wait_status(int channel) > +{ > + unsigned long start; > + > + start = get_timer(0); > + while (1) { Here , you can do reg = readl(); reg |= readl(); if (reg & BIT_MASK()) break; > + if (((readl(&dma->raw_tc_stat) | readl(dma->raw_err_stat)) > + & BIT_MASK(channel)) != 0) > + break; > + > + if (get_timer(start) > 10000) > + return -1; > + > + udelay(1); > + } > + > + if (unlikely(readl(&dma->raw_err_stat) & BIT_MASK(channel))) { > + setbits_le32(&dma->int_err_clear, BIT_MASK(channel)); > + setbits_le32(&dma->raw_err_stat, BIT_MASK(channel)); > + return -1; > + } > + setbits_le32(&dma->int_tc_clear, BIT_MASK(channel)); > + setbits_le32(&dma->raw_tc_stat, BIT_MASK(channel)); > + return 0; > +} Other than that, Acked-by: Marek Vasut <marex@denx.de>
Hi Marek, See comment and question below. > -----Original Message----- > From: Marek Vasut [mailto:marex@denx.de] > Sent: 4-Aug-15 5:32 PM > > On Tuesday, August 04, 2015 at 11:04:38 PM, slemieux.tyco@gmail.com wrote: > > 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. > > > > The legacy driver was updated and clean-up as part of the integration with > > the latest u-boot. > > > > Signed-off-by: Sylvain Lemieux <slemieux@tycoint.com> > > [...] > > > + > > +/* > > + * The "dmac_ll" structure is mapping the DMA channel register, > > + * as per UM10326, "LPC32x0 and LPC32x0/01 User manual" - Rev. 3 > > + * tables 84, 85, 86 & 87. > > + * > > + * The DMA client (ex. NAND SLC) can use this macro to write the > > + * next DMA linked list item address. > > + */ > > +#define lpc32xx_dmac_next_lli(x) (uint32_t)x > > Ew, and it's not even used :-) This is used inside "drivers/mtd/nand/lpc32xx_nand_slc.c"; Refer to this patch for details: https://patchwork.ozlabs.org/patch/503815/ Are we OK to keep this define there? > > > +/* DMA linked list structure used with a channel's LLI register */ > > +struct lpc32xx_dmac_ll { > > + u32 dma_src; > > + u32 dma_dest; > > + u32 next_lli; > > + u32 next_ctrl; > > +}; > > [...] > > > +int lpc32xx_dma_wait_status(int channel) > > +{ > > + unsigned long start; > > + > > + start = get_timer(0); > > + while (1) { > > Here , you can do > > reg = readl(); > reg |= readl(); > if (reg & BIT_MASK()) > break; > Will update the patch. > > + if (((readl(&dma->raw_tc_stat) | readl(dma->raw_err_stat)) > > + & BIT_MASK(channel)) != 0) > > + break; > > + > > + if (get_timer(start) > 10000) > > + return -1; > > + > > + udelay(1); > > + } > > + > > + if (unlikely(readl(&dma->raw_err_stat) & BIT_MASK(channel))) { > > + setbits_le32(&dma->int_err_clear, BIT_MASK(channel)); > > + setbits_le32(&dma->raw_err_stat, BIT_MASK(channel)); > > + return -1; > > + } > > + setbits_le32(&dma->int_tc_clear, BIT_MASK(channel)); > > + setbits_le32(&dma->raw_tc_stat, BIT_MASK(channel)); > > + return 0; > > +} > > Other than that, > > Acked-by: Marek Vasut <marex@denx.de>
On Wednesday, August 05, 2015 at 01:00:03 PM, LEMIEUX, SYLVAIN wrote: > Hi Marek, Hi! > See comment and question below. > > > -----Original Message----- > > From: Marek Vasut [mailto:marex@denx.de] > > Sent: 4-Aug-15 5:32 PM > > > > On Tuesday, August 04, 2015 at 11:04:38 PM, slemieux.tyco@gmail.com wrote: > > > 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. > > > > > > The legacy driver was updated and clean-up as part of the integration > > > with the latest u-boot. > > > > > > Signed-off-by: Sylvain Lemieux <slemieux@tycoint.com> > > > > [...] > > > > > + > > > +/* > > > + * The "dmac_ll" structure is mapping the DMA channel register, > > > + * as per UM10326, "LPC32x0 and LPC32x0/01 User manual" - Rev. 3 > > > + * tables 84, 85, 86 & 87. > > > + * > > > + * The DMA client (ex. NAND SLC) can use this macro to write the > > > + * next DMA linked list item address. > > > + */ > > > +#define lpc32xx_dmac_next_lli(x) (uint32_t)x > > > > Ew, and it's not even used :-) > > This is used inside "drivers/mtd/nand/lpc32xx_nand_slc.c"; > Refer to this patch for details: https://patchwork.ozlabs.org/patch/503815/ > > Are we OK to keep this define there? Sorry, I didn't realize that. But in that case, just use (x) in lpc32xx_nand_slc.c ? The macro is only obfuscating the code. Also, the macro is wrong, not only does it do explicit cast (not a very nice thing to do), but it also doens't put parenthesis around the x, which can have ugly impacts. Best regards, Marek Vasut
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..4f0cf7e --- /dev/null +++ b/arch/arm/include/asm/arch-lpc32xx/dma.h @@ -0,0 +1,44 @@ +/* + * 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> + + +/* + * The "dmac_ll" structure is mapping the DMA channel register, + * as per UM10326, "LPC32x0 and LPC32x0/01 User manual" - Rev. 3 + * tables 84, 85, 86 & 87. + * + * The DMA client (ex. NAND SLC) can use this macro to write the + * next DMA linked list item address. + */ +#define lpc32xx_dmac_next_lli(x) (uint32_t)x + +/* DMA linked list structure used with a channel's LLI register */ +struct lpc32xx_dmac_ll { + u32 dma_src; + u32 dma_dest; + u32 next_lli; + u32 next_ctrl; +}; + +int lpc32xx_dma_get_channel(void); +int lpc32xx_dma_start_xfer(int channel, const struct lpc32xx_dmac_ll *desc, + u32 config); +int lpc32xx_dma_wait_status(int channel); +void lpc32xx_dma_put_channel(int channel); + +#endif /* _LPC32XX_DMA_H */ 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..3cf32fb --- /dev/null +++ b/drivers/dma/lpc32xx_dma.c @@ -0,0 +1,157 @@ +/* + * Copyright (C) 2008-2015 by NXP Semiconductors + * All rights reserved. + * + * @Author: Kevin Wells + * @Descr: LPC3250 DMA controller interface support functions + * + * 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> + +/* + * Notes: + * + * Only bitfield masks/values which are actually used by the driver + * are defined. + */ + +/* DMA controller channel register structure */ +struct dmac_chan_reg { + u32 src_addr; + u32 dest_addr; + u32 lli; + u32 control; + u32 config_ch; + u32 reserved[3]; +}; + +/* DMA controller register structures */ +struct dma_reg { + u32 int_stat; + u32 int_tc_stat; + u32 int_tc_clear; + u32 int_err_stat; + u32 int_err_clear; + u32 raw_tc_stat; + u32 raw_err_stat; + u32 chan_enable; + u32 sw_burst_req; + u32 sw_single_req; + u32 sw_last_burst_req; + u32 sw_last_single_req; + u32 config; + u32 sync; + u32 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 u32 alloc_ch; + +static struct dma_reg *dma = (struct dma_reg *)DMA_BASE; + +int lpc32xx_dma_get_channel(void) +{ + int i; + + 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); + } + + i = ffz(alloc_ch); + + /* Check if all the available channels are busy */ + if (unlikely(i == DMA_NO_OF_CHANNELS)) + return -1; + alloc_ch |= BIT_MASK(i); + return i; +} + +int lpc32xx_dma_start_xfer(int channel, const struct lpc32xx_dmac_ll *desc, + u32 config) +{ + if (unlikely((BIT_MASK(channel) & alloc_ch) == 0)) { + printf("ERR: Request for xfer on unallocated channel %d\r\n", + channel); + BUG(); + } + writel(BIT_MASK(channel), &dma->int_tc_clear); + writel(BIT_MASK(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) +{ + unsigned long start; + + start = get_timer(0); + while (1) { + if (((readl(&dma->raw_tc_stat) | readl(dma->raw_err_stat)) + & BIT_MASK(channel)) != 0) + break; + + if (get_timer(start) > 10000) + return -1; + + udelay(1); + } + + if (unlikely(readl(&dma->raw_err_stat) & BIT_MASK(channel))) { + setbits_le32(&dma->int_err_clear, BIT_MASK(channel)); + setbits_le32(&dma->raw_err_stat, BIT_MASK(channel)); + return -1; + } + setbits_le32(&dma->int_tc_clear, BIT_MASK(channel)); + setbits_le32(&dma->raw_tc_stat, BIT_MASK(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_MASK(channel); + + /* Shut down channel */ + writel(0, &dma->dma_chan[channel].control); + writel(0, &dma->dma_chan[channel].config_ch); + clrbits_le32(&dma->sync, BIT_MASK(channel)); + + if (!alloc_ch) + /* Disable DMA controller */ + clrbits_le32(&dma->config, DMAC_CTRL_ENABLE); +}