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

Reply via email to