[U-Boot] [RFC 1/4] drivers: dma: Add the ARM PL330 DMA driver

Chin Liang See clsee at altera.com
Wed Oct 12 18:04:09 CEST 2016


On Mon, 2016-10-10 at 10:52 -0500, Dinh Nguyen wrote:
> From: Dinh Nguyen <dinguyen at opensource.altera.com>
> 
> Adopted from the Linux kernel PL330 DMA driver.
> 
> Signed-off-by: Dinh Nguyen <dinguyen at opensource.altera.com>
> ---
>  arch/arm/include/asm/pl330.h | 105 +++++
>  drivers/dma/pl330.c          | 942
> +++++++++++++++++++++++++++++++++++++++++++
>  2 files changed, 1047 insertions(+)
>  create mode 100644 arch/arm/include/asm/pl330.h
>  create mode 100644 drivers/dma/pl330.c
> 
> diff --git a/arch/arm/include/asm/pl330.h
> b/arch/arm/include/asm/pl330.h
> new file mode 100644
> index 0000000..dd19b4c
> --- /dev/null
> +++ b/arch/arm/include/asm/pl330.h
> @@ -0,0 +1,105 @@
> +/*
> + * Copyright (C) 2010 Samsung Electronics Co. Ltd.
> + *	Jaswinder Singh <jassi.brar at samsung.com>
> + *
> + * SPDX-License-Identifier:	GPL-2.0+
> + *
> + * adapted from linux kernel pl330.h
> + */
> +
> +#ifndef	__PL330_H_
> +#define	__PL330_H_
> +
> +#define PL330_STATE_STOPPED		(1 << 0)
> +#define PL330_STATE_EXECUTING		(1 << 1)
> +#define PL330_STATE_WFE			(1 << 2)
> +#define PL330_STATE_FAULTING		(1 << 3)
> +#define PL330_STATE_COMPLETING		(1 << 4)
> +#define PL330_STATE_WFP			(1 << 5)
> +#define PL330_STATE_KILLING		(1 << 6)
> +#define PL330_STATE_FAULT_COMPLETING	(1 << 7)
> +#define PL330_STATE_CACHEMISS		(1 << 8)
> +#define PL330_STATE_UPDTPC		(1 << 9)
> +#define PL330_STATE_ATBARRIER		(1 << 10)
> +#define PL330_STATE_QUEUEBUSY		(1 << 11)
> +#define PL330_STATE_INVALID		(1 << 15)
> +
> +#define PL330_DMA_MAX_BURST_SIZE	3
> +

Not sure this is true for other platform. If not, this would need goto
include/configs header files.

[..]

> 
diff --git a/drivers/dma/pl330.c b/drivers/dma/pl330.c
> new file mode 100644
> index 0000000..a97cd9f
> --- /dev/null
> +++ b/drivers/dma/pl330.c
> @@ -0,0 +1,942 @@
> +/*
> + * Copyright (c) 2012 Samsung Electronics Co., Ltd.
> + *		http://www.samsung.com
> + *
> + * Copyright (C) 2010 Samsung Electronics Co. Ltd.
> + *	Jaswinder Singh <jassi.brar at samsung.com>
> + *
> + * SPDX-License-Identifier:     GPL-2.0+
> + */
> +
> +#include <asm/io.h>
> +#include <common.h>
> +#include <dma.h>
> +#include <dm/device.h>
> +#include <asm/pl330.h>
> +#include <asm/processor.h>
> +
> 

[..]

> +
> +static inline u32 _state(struct pl330_transfer_struct *pl330)
> +{
> +	void __iomem *regs = pl330->reg_base;
> +	u32 val;
> +
> +	val = readl(regs + CS(pl330->channel_num)) & 0xf;
> +
> +	udelay(1);
> +
> +	switch (val) {
> +	case DS_ST_STOP:
> +		return PL330_STATE_STOPPED;
> +	case DS_ST_EXEC:
> +		return PL330_STATE_EXECUTING;
> +	case DS_ST_CMISS:
> +		return PL330_STATE_CACHEMISS;
> +	case DS_ST_UPDTPC:
> +		return PL330_STATE_UPDTPC;
> +	case DS_ST_WFE:
> +		return PL330_STATE_WFE;
> +	case DS_ST_FAULT:
> +		return PL330_STATE_FAULTING;
> +	case DS_ST_ATBRR:
> +		return PL330_STATE_ATBARRIER;

This state and below would yield difference between channel and
manager.

[..]

> 
> +/*
> + * DMA transfer setup (DMA_SUPPORTS_MEM_TO_MEM,
> DMA_SUPPORTS_MEM_TO_DEV or
> +		    DMA_SUPPORTS_DEV_TO_MEM)
> + * For Peripheral transfer, the FIFO threshold value is expected at
> + * 2 ^ pl330->brst_size * pl330->brst_len.
> + * Return:		1 for error or not successful
> + *
> + * channel_num	-	channel number assigned, valid from 0
> to 7
> + * src_addr	-	address to transfer from / source
> + * dst_addr	-	address to transfer to / destination
> + * len	-	number of bytes to be transferred
> + * brst_size	-	valid from 0 - 3
> + *			where 0 = 1 (2 ^ 0) bytes and 3 = 8 bytes
> (2 ^ 3)
> + * single_brst_size -	single transfer size (from 0 - 3)
> + * brst_len	-	valid from 1 - 16 where each burst can
> trasfer 1 - 16
> + *			data chunk (each chunk size equivalent to
> brst_size)
> + * peripheral_id	assigned peripheral_id, valid from 0 to 31
> + * transfer_type	DMA_SUPPORTS_MEM_TO_MEM,
> DMA_SUPPORTS_MEM_TO_DEV or
> + *			DMA_SUPPORTS_DEV_TO_MEM
> + * buf_size	-	sizeof(buf)
> + * buf		-	buffer handler which will point to
> the memory
> + *			allocated for dma microcode
> + */
> +static int pl330_transfer_setup(struct pl330_transfer_struct *pl330)
> +{
> +	/* Variable declaration */
> +	int off = 0;			/* buffer offset clear
> to 0 */
> +	int ret = 0;
> +	unsigned loopjmp0, loopjmp1;	/* for DMALPEND */
> +	unsigned lcnt0 = 0;		/* loop count 0 */
> +	unsigned lcnt1 = 0;		/* loop count 1 */
> +	unsigned burst_size = 0;
> +	unsigned len = pl330->len;
> +	u32 ccr = 0;			/* Channel Control
> Register */
> +	struct pl330_reqcfg reqcfg;
> +
> +	/* for burst, always use the maximum burst size and length
> */
> +	pl330->brst_size = PL330_DMA_MAX_BURST_SIZE;
> +	pl330->brst_len = 16;
> +	pl330->single_brst_size = 1;
> +
> +	/* burst_size = 2 ^ brst_size */
> +	burst_size = 1 << pl330->brst_size;
> +
> +	pl330->src_addr	= (u32)&pl330->buf;
> +	if (pl330->dst_addr & (burst_size - 1)) {
> +		puts("ERROR PL330 : destination address
> unaligned\n");
> +		return 1;
> +	}
> +

Good to check the src_addr too. If unaligned, the microcode would not
be applicable.


> +	/* DMAMOV DAR, x->dst_addr */
> +	off += _emit_MOV(&pl330->buf[off], DAR, pl330->dst_addr);
> +	/* DMAFLUSHP P(periheral_id) */
> +	if (pl330->transfer_type != DMA_SUPPORTS_MEM_TO_MEM)
> +		off += _emit_FLUSHP(&pl330->buf[off], pl330
> ->peripheral_id);
> +
> +	/* Preparing the CCR value */
> +	if (pl330->transfer_type == DMA_SUPPORTS_MEM_TO_DEV) {
> +		reqcfg.dst_inc = 0;	/* disable auto increment
> */
> +		reqcfg.src_inc = 1;	/* enable auto increment
> */
> +	} else if (pl330->transfer_type == DMA_SUPPORTS_DEV_TO_MEM)
> {
> +		reqcfg.dst_inc = 1;
> +		reqcfg.src_inc = 0;
> +	} else {
> +		/* DMA_SUPPORTS_MEM_TO_MEM */
> +		reqcfg.dst_inc = 1;
> +		reqcfg.src_inc = 1;
> +	}

We won't need setup based on transfer type as the microcode is setup to
write zero to mem only.

Thanks
Chin Liang

> +
> +	reqcfg.nonsecure = 0;	/* Secure mode */
> +	reqcfg.dcctl = 0x1;	/* noncacheable but bufferable */
> +	reqcfg.scctl = 0x1;
> +	reqcfg.privileged = 1;		/* 1 - Priviledge  */
> +	reqcfg.insnaccess = 0;		/* 0 - data access */
> +	reqcfg.swap = 0;		/* 0 - no endian swap */
> +	reqcfg.brst_len = pl330->brst_len;	/* DMA burst
> length */
> +	reqcfg.brst_size = pl330->brst_size;	/* DMA burst
> size */
> +	/* Preparing the CCR value */
> +	ccr = _prepare_ccr(&reqcfg);
> +	/* DMAMOV CCR, ccr */
> +	off += _emit_MOV(&pl330->buf[off], CCR, ccr);
> +
> +	/* BURST */
> +	/* Can initiate a burst? */
> +	while (len >= burst_size * pl330->brst_len) {
> +		lcnt0 = len / (burst_size * pl330->brst_len);
> +		lcnt1 = 0;
> +		if (lcnt0 >= 256 * 256)
> +			lcnt0 = lcnt1 = 256;
> +		else if (lcnt0 >= 256) {
> +			lcnt1 = lcnt0 / 256;
> +			lcnt0 = 256;
> +		}
> +		len = len -
> +			(burst_size * pl330->brst_len * lcnt0 *
> lcnt1);
> +
> +		if (lcnt1) {
> +			/* DMALP1 */
> +			off += _emit_LP(&pl330->buf[off], 1, lcnt1);
> +			loopjmp1 = off;
> +		}
> +		/* DMALP0 */
> +		off += _emit_LP(&pl330->buf[off], 0, lcnt0);
> +		loopjmp0 = off;
> +
> +		off += _emit_STZ(&pl330->buf[off]);
> +		/* DMALP0END */
> +		struct _arg_LPEND lpend;
> +		lpend.cond = ALWAYS;
> +		lpend.forever = 0;
> +		lpend.loop = 0;		/* loop cnt 0 */
> +		lpend.bjump = off - loopjmp0;
> +		off += _emit_LPEND(&pl330->buf[off], &lpend);
> +		/* DMALP1END */
> +		if (lcnt1) {
> +			struct _arg_LPEND lpend;
> +			lpend.cond = ALWAYS;
> +			lpend.forever = 0;
> +			lpend.loop = 1;		/* loop cnt
> 1*/
> +			lpend.bjump = off - loopjmp1;
> +			off += _emit_LPEND(&pl330->buf[off],
> &lpend);
> +		}
> +		/* ensure the microcode don't exceed buffer size */
> +		if (off > pl330->buf_size) {
> +			puts("ERROR PL330 : Exceed buffer size\n");
> +			return 1;
> +		}
> +	}
> +
> +	/* SINGLE */
> +	pl330->brst_size = pl330->single_brst_size;
> +	pl330->brst_len = 1;
> +	/* burst_size = 2 ^ brst_size */
> +	burst_size = (1 << pl330->brst_size);
> +	lcnt0 = len / (burst_size * pl330->brst_len);
> +
> +	/* ensure all data will be transfered */
> +	len = len -
> +		(burst_size * pl330->brst_len * lcnt0);
> +	if (len)
> +		puts("ERROR PL330 : Detected the possibility of
> untransfered"
> +			"data. Please ensure correct single burst
> size\n");
> +
> +	if (lcnt0) {
> +		/* Preparing the CCR value */
> +		reqcfg.brst_len = pl330->brst_len;	/* DMA
> burst length */
> +		reqcfg.brst_size = pl330->brst_size;	/* DMA
> burst size */
> +		ccr = _prepare_ccr(&reqcfg);
> +		/* DMAMOV CCR, ccr */
> +		off += _emit_MOV(&pl330->buf[off], CCR, ccr);
> +
> +		/* DMALP0 */
> +		off += _emit_LP(&pl330->buf[off], 0, lcnt0);
> +		loopjmp0 = off;
> +
> +		off += _emit_STZ(&pl330->buf[off]);
> +		struct _arg_LPEND lpend1;
> +		lpend1.cond = ALWAYS;
> +		lpend1.forever = 0;
> +		lpend1.loop = 0;	/* loop cnt 0 */
> +		lpend1.bjump = off - loopjmp0;
> +		off += _emit_LPEND(&pl330->buf[off], &lpend1);
> +		/* ensure the microcode don't exceed buffer size */
> +		if (off > pl330->buf_size) {
> +			puts("ERROR PL330 : Exceed buffer size\n");
> +			return 1;
> +		}
> +	}
> +
> +	/* DMAEND */
> +	off += _emit_END(&pl330->buf[off]);
> +
> +	ret = pl330_transfer_start(pl330);
> +	if (ret)
> +		return ret;
> +
> +	ret = pl330_transfer_finish(pl330);
> +	if (ret)
> +		return ret;
> +
> +	return 0;
> +}
> +
> 

[..]


More information about the U-Boot mailing list