
On Mon, 2016-10-10 at 10:52 -0500, Dinh Nguyen wrote:
From: Dinh Nguyen dinguyen@opensource.altera.com
Adopted from the Linux kernel PL330 DMA driver.
Signed-off-by: Dinh Nguyen dinguyen@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@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@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;
+}
[..]