On Mon, 2016-10-10 at 10:52 -0500, Dinh Nguyen wrote: > From: Dinh Nguyen <dingu...@opensource.altera.com> > > Adopted from the Linux kernel PL330 DMA driver. > > Signed-off-by: Dinh Nguyen <dingu...@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.b...@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.b...@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; > +} > + > [..] _______________________________________________ U-Boot mailing list U-Boot@lists.denx.de http://lists.denx.de/mailman/listinfo/u-boot