[U-Boot] [PATCH 1/5] drivers: dma: Enable DMA-330 driver support
tien.fong.chee at intel.com
tien.fong.chee at intel.com
Thu May 31 08:08:50 UTC 2018
From: Tien Fong Chee <tien.fong.chee at intel.com>
Enable DMAC driver support for DMA-330 controller.
The driver is also compatible to PL330 product.
Signed-off-by: Tien Fong Chee <tien.fong.chee at intel.com>
---
drivers/dma/Kconfig | 9 +-
drivers/dma/Makefile | 1 +
drivers/dma/dma330.c | 1514 ++++++++++++++++++++++++++++++++++++++++++++++++++
include/dma330.h | 136 +++++
4 files changed, 1659 insertions(+), 1 deletion(-)
create mode 100644 drivers/dma/dma330.c
create mode 100644 include/dma330.h
diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig
index 4ee6afa..6e77e07 100644
--- a/drivers/dma/Kconfig
+++ b/drivers/dma/Kconfig
@@ -2,7 +2,7 @@ menu "DMA Support"
config DMA
bool "Enable Driver Model for DMA drivers"
- depends on DM
+ depends on DM || SPL_DM
help
Enable driver model for DMA. DMA engines can do
asynchronous data transfers without involving the host
@@ -34,4 +34,11 @@ config APBH_DMA_BURST8
endif
+config DMA330_DMA
+ bool "PL330/DMA-330 DMA Controller(DMAC) driver"
+ depends on DMA
+ help
+ Enable the DMA controller driver for both PL330 and
+ DMA-330 products.
+
endmenu # menu "DMA Support"
diff --git a/drivers/dma/Makefile b/drivers/dma/Makefile
index 4eaef8a..bfad0dd 100644
--- a/drivers/dma/Makefile
+++ b/drivers/dma/Makefile
@@ -11,3 +11,4 @@ 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
+obj-$(CONFIG_DMA330_DMA) += dma330.o
diff --git a/drivers/dma/dma330.c b/drivers/dma/dma330.c
new file mode 100644
index 0000000..66575d8
--- /dev/null
+++ b/drivers/dma/dma330.c
@@ -0,0 +1,1514 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2018 Intel Corporation <www.intel.com>
+ */
+
+#include <common.h>
+#include <asm/io.h>
+#include <dma330.h>
+#include <dm.h>
+#include <fdtdec.h>
+#include <wait_bit.h>
+#include <linux/unaligned/access_ok.h>
+
+/* Register and Bit field Definitions */
+
+/* DMA Status */
+#define DS 0x0
+#define DS_ST_STOP 0x0
+#define DS_ST_EXEC 0x1
+#define DS_ST_CMISS 0x2
+#define DS_ST_UPDTPC 0x3
+#define DS_ST_WFE 0x4
+#define DS_ST_ATBRR 0x5
+#define DS_ST_QBUSY 0x6
+#define DS_ST_WFP 0x7
+#define DS_ST_KILL 0x8
+#define DS_ST_CMPLT 0x9
+#define DS_ST_FLTCMP 0xe
+#define DS_ST_FAULT 0xf
+
+/* DMA Program Count register */
+#define DPC 0x4
+/* Interrupt Enable register */
+#define INTEN 0x20
+/* event-Interrupt Raw Status register */
+#define ES 0x24
+/* Interrupt Status register */
+#define INTSTATUS 0x28
+/* Interrupt Clear register */
+#define INTCLR 0x2c
+/* Fault Status DMA Manager register */
+#define FSM 0x30
+/* Fault Status DMA Channel register */
+#define FSC 0x34
+/* Fault Type DMA Manager register */
+#define FTM 0x38
+
+/* Fault Type DMA Channel register */
+#define _FTC 0x40
+#define FTC(n) (_FTC + (n) * 0x4)
+
+/* Channel Status register */
+#define _CS 0x100
+#define CS(n) (_CS + (n) * 0x8)
+#define CS_CNS BIT(21)
+
+/* Channel Program Counter register */
+#define _CPC 0x104
+#define CPC(n) (_CPC + (n) * 0x8)
+
+/* Source Address register */
+#define _SA 0x400
+#define SA(n) (_SA + (n) * 0x20)
+
+/* Destination Address register */
+#define _DA 0x404
+#define DA(n) (_DA + (n) * 0x20)
+
+/* Channel Control register */
+#define _CC 0x408
+#define CC(n) (_CC + (n) * 0x20)
+
+/* Channel Control register (CCR) Setting */
+#define CC_SRCINC BIT(0)
+#define CC_DSTINC BIT(14)
+#define CC_SRCPRI BIT(8)
+#define CC_DSTPRI BIT(22)
+#define CC_SRCNS BIT(9)
+#define CC_DSTNS BIT(23)
+#define CC_SRCIA BIT(10)
+#define CC_DSTIA BIT(24)
+#define CC_SRCBRSTLEN_SHFT 4
+#define CC_DSTBRSTLEN_SHFT 18
+#define CC_SRCBRSTSIZE_SHFT 1
+#define CC_DSTBRSTSIZE_SHFT 15
+#define CC_SRCCCTRL_SHFT 11
+#define CC_SRCCCTRL_MASK 0x7
+#define CC_DSTCCTRL_SHFT 25
+#define CC_DRCCCTRL_MASK 0x7
+#define CC_SWAP_SHFT 28
+
+/* Loop Counter 0 register */
+#define _LC0 0x40c
+#define LC0(n) (_LC0 + (n) * 0x20)
+
+/* Loop Counter 1 register */
+#define _LC1 0x410
+#define LC1(n) (_LC1 + (n) * 0x20)
+
+/* Debug Status register */
+#define DBGSTATUS 0xd00
+#define DBG_BUSY BIT(0)
+
+/* Debug Command register */
+#define DBGCMD 0xd04
+/* Debug Instruction 0 register */
+#define DBGINST0 0xd08
+/* Debug Instruction 1 register */
+#define DBGINST1 0xd0c
+
+/* Configuration register */
+#define CR0 0xe00
+#define CR1 0xe04
+#define CR2 0xe08
+#define CR3 0xe0c
+#define CR4 0xe10
+#define CRD 0xe14
+
+/* Peripheral Identification register */
+#define PERIPH_ID 0xfe0
+/* Component Identification register */
+#define PCELL_ID 0xff0
+
+/* Configuration register value */
+#define CR0_PERIPH_REQ_SET BIT(0)
+#define CR0_BOOT_EN_SET BIT(1)
+#define CR0_BOOT_MAN_NS BIT(2)
+#define CR0_NUM_CHANS_SHIFT 4
+#define CR0_NUM_CHANS_MASK 0x7
+#define CR0_NUM_PERIPH_SHIFT 12
+#define CR0_NUM_PERIPH_MASK 0x1f
+#define CR0_NUM_EVENTS_SHIFT 17
+#define CR0_NUM_EVENTS_MASK 0x1f
+
+/* Configuration register value */
+#define CR1_ICACHE_LEN_SHIFT 0
+#define CR1_ICACHE_LEN_MASK 0x7
+#define CR1_NUM_ICACHELINES_SHIFT 4
+#define CR1_NUM_ICACHELINES_MASK 0xf
+
+/* Configuration register value */
+#define CRD_DATA_WIDTH_SHIFT 0
+#define CRD_DATA_WIDTH_MASK 0x7
+#define CRD_WR_CAP_SHIFT 4
+#define CRD_WR_CAP_MASK 0x7
+#define CRD_WR_Q_DEP_SHIFT 8
+#define CRD_WR_Q_DEP_MASK 0xf
+#define CRD_RD_CAP_SHIFT 12
+#define CRD_RD_CAP_MASK 0x7
+#define CRD_RD_Q_DEP_SHIFT 16
+#define CRD_RD_Q_DEP_MASK 0xf
+#define CRD_DATA_BUFF_SHIFT 20
+#define CRD_DATA_BUFF_MASK 0x3ff
+
+/* Microcode opcode value */
+#define CMD_DMAADDH 0x54
+#define CMD_DMAEND 0x00
+#define CMD_DMAFLUSHP 0x35
+#define CMD_DMAGO 0xa0
+#define CMD_DMALD 0x04
+#define CMD_DMALDP 0x25
+#define CMD_DMALP 0x20
+#define CMD_DMALPEND 0x28
+#define CMD_DMAKILL 0x01
+#define CMD_DMAMOV 0xbc
+#define CMD_DMANOP 0x18
+#define CMD_DMARMB 0x12
+#define CMD_DMASEV 0x34
+#define CMD_DMAST 0x08
+#define CMD_DMASTP 0x29
+#define CMD_DMASTZ 0x0c
+#define CMD_DMAWFE 0x36
+#define CMD_DMAWFP 0x30
+#define CMD_DMAWMB 0x13
+
+/* the size of opcode plus opcode required settings */
+#define SZ_DMAADDH 3
+#define SZ_DMAEND 1
+#define SZ_DMAFLUSHP 2
+#define SZ_DMALD 1
+#define SZ_DMALDP 2
+#define SZ_DMALP 2
+#define SZ_DMALPEND 2
+#define SZ_DMAKILL 1
+#define SZ_DMAMOV 6
+#define SZ_DMANOP 1
+#define SZ_DMARMB 1
+#define SZ_DMASEV 2
+#define SZ_DMAST 1
+#define SZ_DMASTP 2
+#define SZ_DMASTZ 1
+#define SZ_DMAWFE 2
+#define SZ_DMAWFP 2
+#define SZ_DMAWMB 1
+#define SZ_DMAGO 6
+
+/* Use this _only_ to wait on transient states */
+#define UNTIL(t, s) do { \
+ WATCHDOG_RESET(); \
+ } while (!(dma330_getstate(t) & (s)))
+
+/* debug message printout */
+#ifdef DEBUG
+#define DMA330_DBGCMD_DUMP(off, x...) do { \
+ printf("%x bytes:", off); \
+ printf(x); \
+ WATCHDOG_RESET(); \
+ } while (0)
+#else
+#define DMA330_DBGCMD_DUMP(off, x...) do {} while (0)
+#endif
+
+/* Enum declaration */
+enum dmamov_dst {
+ SAR = 0,
+ CCR,
+ DAR,
+};
+
+enum dma330_dst {
+ SRC = 0,
+ DST,
+};
+
+enum dma330_cond {
+ SINGLE,
+ BURST,
+ ALWAYS,
+};
+
+/* Structure will be used by _emit_lpend function */
+struct _arg_lpend {
+ enum dma330_cond cond;
+ int forever;
+ u32 loop;
+ u8 bjump;
+};
+
+/* Structure will be used by _emit_go function */
+struct _arg_go {
+ u8 chan;
+ u32 addr;
+ u32 ns;
+};
+
+/*
+ * _emit_end - Add opcode DMAEND into microcode (end).
+ *
+ * @buf: The buffer which stored the microcode program.
+ *
+ * Return: Size of opcode.
+ */
+static inline u32 _emit_end(u8 buf[])
+{
+ buf[0] = CMD_DMAEND;
+ DMA330_DBGCMD_DUMP(SZ_DMAEND, "\tDMAEND\n");
+ return SZ_DMAEND;
+}
+
+/*
+ * _emit_flushp - Add opcode DMAFLUSHP into microcode (flush peripheral).
+ *
+ * @buf -> The buffer which stored the microcode program.
+ * @peri -> Peripheral ID as listed in DMA NPP.
+ *
+ * Return: Size of opcode.
+ */
+static inline u32 _emit_flushp(u8 buf[], u8 peri)
+{
+ u8 *buffer = buf;
+
+ buffer[0] = CMD_DMAFLUSHP;
+ peri &= 0x1f;
+ peri <<= 3;
+ buffer[1] = peri;
+ DMA330_DBGCMD_DUMP(SZ_DMAFLUSHP, "\tDMAFLUSHP %u\n", peri >> 3);
+ return SZ_DMAFLUSHP;
+}
+
+/**
+ * _emit_ld - Add opcode DMALD into microcode (load).
+ *
+ * @buf: The buffer which stored the microcode program.
+ * @cond: Execution criteria such as single, burst or always.
+ *
+ * Return: Size of opcode.
+ */
+static inline u32 _emit_ld(u8 buf[], enum dma330_cond cond)
+{
+ buf[0] = CMD_DMALD;
+ if (cond == SINGLE)
+ buf[0] |= (0 << 1) | (1 << 0);
+ else if (cond == BURST)
+ buf[0] |= (1 << 1) | (1 << 0);
+ DMA330_DBGCMD_DUMP(SZ_DMALD, "\tDMALD%c\n",
+ cond == SINGLE ? 'S' : (cond == BURST ? 'B' : 'A'));
+ return SZ_DMALD;
+}
+
+/**
+ * _emit_lp - Add opcode DMALP into microcode (loop).
+ *
+ * @buf: The buffer which stored the microcode program.
+ * @loop: Selection of using counter 0 or 1 (valid value 0 or 1).
+ * @cnt: number of iteration (valid value 1-256).
+ *
+ * Return: Size of opcode.
+ */
+static inline u32 _emit_lp(u8 buf[], u32 loop, u32 cnt)
+{
+ u8 *buffer = buf;
+
+ buffer[0] = CMD_DMALP;
+ if (loop)
+ buffer[0] |= (1 << 1);
+ /* DMAC increments by 1 internally */
+ cnt--;
+ buffer[1] = cnt;
+ DMA330_DBGCMD_DUMP(SZ_DMALP, "\tDMALP_%c %u\n", loop ? '1' : '0', cnt);
+ return SZ_DMALP;
+}
+
+/**
+ * _emit_lpend - Add opcode DMALPEND into microcode (loop end).
+ *
+ * @buf: The buffer which stored the microcode program.
+ * @arg: Structure _arg_lpend which contain all needed info.
+ * arg->cond -> Execution criteria such as single, burst or always
+ * arg->forever -> Forever loop? used if DMALPFE started the loop
+ * arg->bjump -> Backwards jump (relative location of
+ * 1st instruction in the loop.
+ *
+ * Return: Size of opcode.
+ */
+static inline u32 _emit_lpend(u8 buf[], const struct _arg_lpend *arg)
+{
+ u8 *buffer = buf;
+ enum dma330_cond cond = arg->cond;
+ int forever = arg->forever;
+ u32 loop = arg->loop;
+ u8 bjump = arg->bjump;
+
+ buffer[0] = CMD_DMALPEND;
+ if (loop)
+ buffer[0] |= (1 << 2);
+ if (!forever)
+ buffer[0] |= (1 << 4);
+ if (cond == SINGLE)
+ buffer[0] |= (0 << 1) | (1 << 0);
+ else if (cond == BURST)
+ buffer[0] |= (1 << 1) | (1 << 0);
+
+ buffer[1] = bjump;
+ DMA330_DBGCMD_DUMP(SZ_DMALPEND, "\tDMALP%s%c_%c bjmpto_%x\n",
+ forever ? "FE" : "END",
+ cond == SINGLE ? 'S' : (cond == BURST ? 'B' : 'A'),
+ loop ? '1' : '0',
+ bjump);
+ return SZ_DMALPEND;
+}
+
+/**
+ * _emit_kill - Add opcode DMAKILL into microcode (kill).
+ *
+ * @buf: The buffer which stored the microcode program.
+ *
+ * Return: Size of opcode.
+ */
+static inline u32 _emit_kill(u8 buf[])
+{
+ buf[0] = CMD_DMAKILL;
+ return SZ_DMAKILL;
+}
+
+/**
+ * _emit_mov - Add opcode DMAMOV into microcode (move).
+ *
+ * @buf: The buffer which stored the microcode program.
+ * @dst: Destination register (valid value SAR[0b000], DAR[0b010],
+ * or CCR[0b001]).
+ * @val: 32bit value that to be written into destination register.
+ *
+ * Return: Size of opcode.
+ */
+static inline u32 _emit_mov(u8 buf[], enum dmamov_dst dst, u32 val)
+{
+ u8 *buffer = buf;
+
+ buffer[0] = CMD_DMAMOV;
+ buffer[1] = dst;
+ buffer[2] = val & 0xFF;
+ buffer[3] = (val >> 8) & 0xFF;
+ buffer[4] = (val >> 16) & 0xFF;
+ buffer[5] = (val >> 24) & 0xFF;
+ DMA330_DBGCMD_DUMP(SZ_DMAMOV, "\tDMAMOV %s 0x%x\n",
+ dst == SAR ? "SAR" : (dst == DAR ? "DAR" : "CCR"),
+ val);
+ return SZ_DMAMOV;
+}
+
+/**
+ * _emit_nop - Add opcode DMANOP into microcode (no operation).
+ *
+ * @buf: The buffer which stored the microcode program.
+ *
+ * Return: Size of opcode.
+ */
+static inline u32 _emit_nop(u8 buf[])
+{
+ buf[0] = CMD_DMANOP;
+ DMA330_DBGCMD_DUMP(SZ_DMANOP, "\tDMANOP\n");
+ return SZ_DMANOP;
+}
+
+/**
+ * _emit_rmb - Add opcode DMARMB into microcode (read memory barrier).
+ *
+ * @buf: The buffer which stored the microcode program.
+ *
+ * Return: Size of opcode.
+ */
+static inline u32 _emit_rmb(u8 buf[])
+{
+ buf[0] = CMD_DMARMB;
+ DMA330_DBGCMD_DUMP(SZ_DMARMB, "\tDMARMB\n");
+ return SZ_DMARMB;
+}
+
+/**
+ * _emit_sev - Add opcode DMASEV into microcode (send event).
+ *
+ * @buf: The buffer which stored the microcode program.
+ * @ev: The event number (valid 0 - 31).
+ *
+ * Return: Size of opcode.
+ */
+static inline u32 _emit_sev(u8 buf[], u8 ev)
+{
+ u8 *buffer = buf;
+
+ buffer[0] = CMD_DMASEV;
+ ev &= 0x1f;
+ ev <<= 3;
+ buffer[1] = ev;
+ DMA330_DBGCMD_DUMP(SZ_DMASEV, "\tDMASEV %u\n", ev >> 3);
+ return SZ_DMASEV;
+}
+
+/**
+ * _emit_st - Add opcode DMAST into microcode (store).
+ *
+ * @buf: The buffer which stored the microcode program.
+ * @cond: Execution criteria such as single, burst or always.
+ *
+ * Return: Size of opcode.
+ */
+static inline u32 _emit_st(u8 buf[], enum dma330_cond cond)
+{
+ buf[0] = CMD_DMAST;
+ if (cond == SINGLE)
+ buf[0] |= (0 << 1) | (1 << 0);
+ else if (cond == BURST)
+ buf[0] |= (1 << 1) | (1 << 0);
+
+ DMA330_DBGCMD_DUMP(SZ_DMAST, "\tDMAST%c\n",
+ cond == SINGLE ? 'S' : (cond == BURST ? 'B' : 'A'));
+ return SZ_DMAST;
+}
+
+/**
+ * _emit_stp - Add opcode DMASTP into microcode (store and notify peripheral).
+ *
+ * @buf: The buffer which stored the microcode program.
+ * @cond: Execution criteria such as single, burst or always.
+ * @peri: Peripheral ID as listed in DMA NPP.
+ *
+ * Return: Size of opcode.
+ */
+static inline u32 _emit_stp(u8 buf[], enum dma330_cond cond, u8 peri)
+{
+ u8 *buffer = buf;
+
+ buffer[0] = CMD_DMASTP;
+ if (cond == BURST)
+ buf[0] |= (1 << 1);
+ peri &= 0x1f;
+ peri <<= 3;
+ buffer[1] = peri;
+ DMA330_DBGCMD_DUMP(SZ_DMASTP, "\tDMASTP%c %u\n",
+ cond == SINGLE ? 'S' : 'B', peri >> 3);
+ return SZ_DMASTP;
+}
+
+/**
+ * _emit_stz - Add opcode DMASTZ into microcode (store zeros).
+ *
+ * @buf -> The buffer which stored the microcode program.
+ *
+ * Return: Size of opcode.
+ */
+static inline u32 _emit_stz(u8 buf[])
+{
+ buf[0] = CMD_DMASTZ;
+ DMA330_DBGCMD_DUMP(SZ_DMASTZ, "\tDMASTZ\n");
+ return SZ_DMASTZ;
+}
+
+/**
+ * _emit_wfp - Add opcode DMAWFP into microcode (wait for peripheral).
+ *
+ * @buf: The buffer which stored the microcode program.
+ * @cond: Execution criteria such as single, burst or always.
+ * @peri: Peripheral ID as listed in DMA NPP.
+ *
+ * Return: Size of opcode.
+ */
+static inline u32 _emit_wfp(u8 buf[], enum dma330_cond cond, u8 peri)
+{
+ u8 *buffer = buf;
+
+ buffer[0] = CMD_DMAWFP;
+ if (cond == SINGLE)
+ buffer[0] |= (0 << 1) | (0 << 0);
+ else if (cond == BURST)
+ buffer[0] |= (1 << 1) | (0 << 0);
+ else
+ buffer[0] |= (0 << 1) | (1 << 0);
+
+ peri &= 0x1f;
+ peri <<= 3;
+ buffer[1] = peri;
+ DMA330_DBGCMD_DUMP(SZ_DMAWFP, "\tDMAWFP%c %u\n",
+ cond == SINGLE ? 'S' : (cond == BURST ? 'B' : 'P'), peri >> 3);
+ return SZ_DMAWFP;
+}
+
+/**
+ * _emit_wmb - Add opcode DMAWMB into microcode (write memory barrier).
+ *
+ * @buf: The buffer which stored the microcode program.
+ *
+ * Return: Size of opcode.
+ */
+static inline u32 _emit_wmb(u8 buf[])
+{
+ buf[0] = CMD_DMAWMB;
+ DMA330_DBGCMD_DUMP(SZ_DMAWMB, "\tDMAWMB\n");
+ return SZ_DMAWMB;
+}
+
+/**
+ * _emit_go - Add opcode DMALGO into microcode (go).
+ *
+ * @buf: The buffer which stored the microcode program.
+ * @arg: structure _arg_go which contain all needed info
+ * arg->chan -> channel number
+ * arg->addr -> start address of the microcode program
+ * which will be wrote into CPC register
+ * arg->ns -> 1 for non secure, 0 for secure
+ * (if only DMA Manager is in secure).
+ *
+ * Return: Size of opcode.
+ */
+static inline u32 _emit_go(u8 buf[], const struct _arg_go *arg)
+{
+ u8 *buffer = buf;
+ u8 chan = arg->chan;
+ u32 addr = arg->addr;
+ u32 ns = arg->ns;
+
+ buffer[0] = CMD_DMAGO;
+ buffer[0] |= (ns << 1);
+ buffer[1] = chan & 0x7;
+ buf[2] = addr & 0xFF;
+ buf[3] = (addr >> 8) & 0xFF;
+ buf[4] = (addr >> 16) & 0xFF;
+ buf[5] = (addr >> 24) & 0xFF;
+ return SZ_DMAGO;
+}
+
+/**
+ * _prepare_ccr - Populate the CCR register.
+ * @rqc: Request Configuration.
+ *
+ * Return: Channel Control register (CCR) Setting.
+ */
+static inline u32 _prepare_ccr(const struct dma330_reqcfg *rqc)
+{
+ u32 ccr = 0;
+
+ if (rqc->src_inc)
+ ccr |= CC_SRCINC;
+ if (rqc->dst_inc)
+ ccr |= CC_DSTINC;
+
+ /* We set same protection levels for Src and DST for now */
+ if (rqc->privileged)
+ ccr |= CC_SRCPRI | CC_DSTPRI;
+ if (rqc->nonsecure)
+ ccr |= CC_SRCNS | CC_DSTNS;
+ if (rqc->insnaccess)
+ ccr |= CC_SRCIA | CC_DSTIA;
+
+ ccr |= (((rqc->brst_len - 1) & 0xf) << CC_SRCBRSTLEN_SHFT);
+ ccr |= (((rqc->brst_len - 1) & 0xf) << CC_DSTBRSTLEN_SHFT);
+
+ ccr |= (rqc->brst_size << CC_SRCBRSTSIZE_SHFT);
+ ccr |= (rqc->brst_size << CC_DSTBRSTSIZE_SHFT);
+
+ ccr |= (rqc->scctl << CC_SRCCCTRL_SHFT);
+ ccr |= (rqc->dcctl << CC_DSTCCTRL_SHFT);
+
+ ccr |= (rqc->swap << CC_SWAP_SHFT);
+ return ccr;
+}
+
+/**
+ * dma330_until_dmac_idle - Wait until DMA Manager is idle.
+ *
+ * @plat: Pointer to struct dma_dma330_platdata.
+ * @timeout_ms: Timeout (in miliseconds).
+ *
+ * Return: Negative value for error / timeout ocurred before idle,
+ * 0 for successful.
+ */
+static int dma330_until_dmac_idle(struct dma_dma330_platdata *plat,
+ const u32 timeout_ms)
+{
+ void __iomem *regs = plat->base;
+
+ return wait_for_bit(__func__,
+ (const u32 *)(uintptr_t)(regs + DBGSTATUS),
+ DBG_BUSY, 0, timeout_ms, false);
+}
+
+/**
+ * dma330_execute_dbginsn - Execute debug instruction such as DMAGO and DMAKILL.
+ *
+ * @insn: The buffer which stored the debug instruction.
+ * @plat: Pointer to struct dma_dma330_platdata.
+ * @timeout_ms: Timeout (in miliseconds).
+ *
+ * Return: void.
+ */
+static inline void dma330_execute_dbginsn(u8 insn[],
+ struct dma_dma330_platdata *plat,
+ const u32 timeout_ms)
+{
+ void __iomem *regs = plat->base;
+ u32 val;
+
+ val = (insn[0] << 16) | (insn[1] << 24);
+ if (!plat->dma330.channel0_manager1)
+ val |= (1 << 0);
+ val |= (plat->dma330.channel_num << 8); /* Channel Number */
+ writel(val, regs + DBGINST0);
+ val = insn[2];
+ val = val | (insn[3] << 8);
+ val = val | (insn[4] << 16);
+ val = val | (insn[5] << 24);
+ writel(val, regs + DBGINST1);
+
+ /* If timed out due to halted state-machine */
+ if (dma330_until_dmac_idle(plat, timeout_ms)) {
+ debug("DMAC halted!\n");
+ return;
+ }
+ /* Get going */
+ writel(0, regs + DBGCMD);
+}
+
+/**
+ * dma330_getstate - Get the status of current channel or manager.
+ *
+ * @plat: Pointer to struct dma_dma330_platdata.
+ *
+ * Return: Status state of current channel or current manager.
+ */
+static inline u32 dma330_getstate(struct dma_dma330_platdata *plat)
+{
+ void __iomem *regs = plat->base;
+ u32 val;
+
+ if (plat->dma330.channel0_manager1)
+ val = readl((uintptr_t)(regs + DS)) & 0xf;
+ else
+ val = readl((uintptr_t)(regs + CS(plat->dma330.channel_num))) &
+ 0xf;
+
+ switch (val) {
+ case DS_ST_STOP:
+ return DMA330_STATE_STOPPED;
+ case DS_ST_EXEC:
+ return DMA330_STATE_EXECUTING;
+ case DS_ST_CMISS:
+ return DMA330_STATE_CACHEMISS;
+ case DS_ST_UPDTPC:
+ return DMA330_STATE_UPDTPC;
+ case DS_ST_WFE:
+ return DMA330_STATE_WFE;
+ case DS_ST_FAULT:
+ return DMA330_STATE_FAULTING;
+ case DS_ST_ATBRR:
+ if (plat->dma330.channel0_manager1)
+ return DMA330_STATE_INVALID;
+ else
+ return DMA330_STATE_ATBARRIER;
+ case DS_ST_QBUSY:
+ if (plat->dma330.channel0_manager1)
+ return DMA330_STATE_INVALID;
+ else
+ return DMA330_STATE_QUEUEBUSY;
+ case DS_ST_WFP:
+ if (plat->dma330.channel0_manager1)
+ return DMA330_STATE_INVALID;
+ else
+ return DMA330_STATE_WFP;
+ case DS_ST_KILL:
+ if (plat->dma330.channel0_manager1)
+ return DMA330_STATE_INVALID;
+ else
+ return DMA330_STATE_KILLING;
+ case DS_ST_CMPLT:
+ if (plat->dma330.channel0_manager1)
+ return DMA330_STATE_INVALID;
+ else
+ return DMA330_STATE_COMPLETING;
+ case DS_ST_FLTCMP:
+ if (plat->dma330.channel0_manager1)
+ return DMA330_STATE_INVALID;
+ else
+ return DMA330_STATE_FAULT_COMPLETING;
+ default:
+ return DMA330_STATE_INVALID;
+ }
+}
+
+/**
+ * dma330_trigger - Execute the DMAGO through debug instruction.
+ *
+ * @plat: Pointer to struct dma_dma330_platdata.
+ * @timeout_ms: Timeout (in miliseconds).
+ *
+ * When the DMA manager executes Go for a DMA channel that is in the Stopped
+ * state, it moves a 32-bit immediate into the program counter, then setting
+ * its security state and updating DMA channel to the Executing state.
+ *
+ * Return: Negative value for error as DMA is not ready. 0 for successful.
+ */
+static int dma330_trigger(struct dma_dma330_platdata *plat,
+ const u32 timeout_ms)
+{
+ u8 insn[6] = {0, 0, 0, 0, 0, 0};
+ struct _arg_go go;
+
+ /*
+ * Return if already ACTIVE. It will ensure DMAGO is executed at
+ * STOPPED state too
+ */
+ plat->dma330.channel0_manager1 = 0;
+ if (dma330_getstate(plat) !=
+ DMA330_STATE_STOPPED)
+ return -EPERM;
+
+ go.chan = plat->dma330.channel_num;
+ go.addr = (uintptr_t)plat->dma330.buf;
+
+ if (!plat->dma330.secure)
+ go.ns = 1; /* non-secure condition */
+ else
+ go.ns = 0; /* secure condition */
+
+ _emit_go(insn, &go);
+
+ /* Only manager can execute GO */
+ plat->dma330.channel0_manager1 = 1;
+ dma330_execute_dbginsn(insn, plat, timeout_ms);
+ return 0;
+}
+
+/**
+ * dma330_stop - Stop the manager or channel.
+ *
+ * @plat: Pointer to struct dma_dma330_platdata.
+ * @timeout_ms: Timeout (in miliseconds).
+ *
+ * Stop the manager/channel or killing them and ensure they reach to stop
+ * state.
+ *
+ * Return: void.
+ */
+static void dma330_stop(struct dma_dma330_platdata *plat, const u32 timeout_ms)
+{
+ u8 insn[6] = {0, 0, 0, 0, 0, 0};
+
+ /* If fault completing, wait until reach faulting and killing state */
+ if (dma330_getstate(plat) == DMA330_STATE_FAULT_COMPLETING)
+ UNTIL(plat, DMA330_STATE_FAULTING | DMA330_STATE_KILLING);
+
+ /* Return if nothing needs to be done */
+ if (dma330_getstate(plat) == DMA330_STATE_COMPLETING ||
+ dma330_getstate(plat) == DMA330_STATE_KILLING ||
+ dma330_getstate(plat) == DMA330_STATE_STOPPED)
+ return;
+
+ /* Kill it to ensure it reach to stop state */
+ _emit_kill(insn);
+
+ /* Execute the KILL instruction through debug registers */
+ dma330_execute_dbginsn(insn, plat, timeout_ms);
+}
+
+/**
+ * dma330_start - Execute the command list through DMAGO and debug instruction.
+ *
+ * @plat: Pointer to struct dma_dma330_platdata.
+ * @timeout_ms: Timeout (in miliseconds).
+ *
+ * Return: Negative value for error where DMA is not ready. 0 for successful.
+ */
+static int dma330_start(struct dma_dma330_platdata *plat, const u32 timeout_ms)
+{
+ debug("INFO: DMA BASE Address = 0x%08x\n",
+ (u32)(uintptr_t)plat->base);
+
+ switch (dma330_getstate(plat)) {
+ case DMA330_STATE_FAULT_COMPLETING:
+ UNTIL(plat, DMA330_STATE_FAULTING | DMA330_STATE_KILLING);
+
+ if (dma330_getstate(plat) == DMA330_STATE_KILLING)
+ UNTIL(plat, DMA330_STATE_STOPPED);
+
+ case DMA330_STATE_FAULTING:
+ dma330_stop(plat, timeout_ms);
+
+ case DMA330_STATE_KILLING:
+ case DMA330_STATE_COMPLETING:
+ UNTIL(plat, DMA330_STATE_STOPPED);
+
+ case DMA330_STATE_STOPPED:
+ return dma330_trigger(plat, timeout_ms);
+
+ case DMA330_STATE_WFP:
+ case DMA330_STATE_QUEUEBUSY:
+ case DMA330_STATE_ATBARRIER:
+ case DMA330_STATE_UPDTPC:
+ case DMA330_STATE_CACHEMISS:
+ case DMA330_STATE_EXECUTING:
+ return -ESRCH;
+ /* For RESUME, nothing yet */
+ case DMA330_STATE_WFE:
+ default:
+ return -ESRCH;
+ }
+}
+
+/**
+ * dma330_transfer_start - DMA start to run.
+ *
+ * @plat: Pointer to struct dma_dma330_platdata.
+ *
+ * DMA start to excecute microcode command list.
+ *
+ * Return: Negative value for error or not successful. 0 for successful.
+ */
+static int dma330_transfer_start(struct dma_dma330_platdata *plat)
+{
+ const u32 timeout_ms = 1000;
+
+ /* Execute the command list */
+ return dma330_start(plat, timeout_ms);
+}
+
+/**
+ * dma330_transfer_finish - DMA polling until execution finish or error.
+ *
+ * @plat: Pointer to struct dma_dma330_platdata.
+ *
+ * DMA polling until excution finish and checking the state status.
+ *
+ * Return: Negative value for state error or not successful. 0 for successful.
+ */
+static int dma330_transfer_finish(struct dma_dma330_platdata *plat)
+{
+ void __iomem *regs = plat->base;
+
+ if (!plat->dma330.buf) {
+ debug("ERROR DMA330 : DMA Microcode buffer pointer is NULL\n");
+ return -EINVAL;
+ }
+
+ plat->dma330.channel0_manager1 = 0;
+
+ /* Wait until finish execution to ensure we compared correct result*/
+ UNTIL(plat, DMA330_STATE_STOPPED | DMA330_STATE_FAULTING);
+
+ /* check the state */
+ if (dma330_getstate(plat) == DMA330_STATE_FAULTING) {
+ debug("FAULT Mode: Channel %d Faulting, FTR = 0x%08x,",
+ plat->dma330.channel_num,
+ readl(regs + FTC(plat->dma330.channel_num)));
+ debug("CPC = 0x%08lx\n",
+ (readl(regs + CPC(plat->dma330.channel_num))
+ - (uintptr_t)plat->dma330.buf));
+ return -EPROTO;
+ }
+ return 0;
+}
+
+/**
+ * dma330_transfer_setup - DMA transfer setup (DMA_MEM_TO_MEM, DMA_MEM_TO_DEV
+ * or DMA_DEV_TO_MEM).
+ *
+ * @plat: Pointer to struct dma_dma330_platdata.
+ *
+ * For Peripheral transfer, the FIFO threshold value is expected at
+ * 2 ^ plat->brst_size * plat->brst_len.
+ *
+ * Return: Negative value for error or not successful. 0 for successful.
+ */
+static int dma330_transfer_setup(struct dma_dma330_platdata *plat)
+{
+ /* Variable declaration */
+ /* Buffer offset clear to 0 */
+ int off = 0;
+ int ret = 0;
+ /* For DMALPEND */
+ u32 loopjmp0, loopjmp1;
+ /* Loop count 0 */
+ u32 lcnt0 = 0;
+ /* Loop count 1 */
+ u32 lcnt1 = 0;
+ u32 brst_size = 0;
+ u32 brst_len = 0;
+ u32 data_size_byte = plat->dma330.size_byte;
+ /* Strong order memory is required to store microcode command list */
+ u8 *buf = (u8 *)plat->dma330.buf;
+ /* Channel Control Register */
+ u32 ccr = 0;
+ struct dma330_reqcfg reqcfg;
+
+ if (!buf) {
+ debug("ERROR DMA330 : DMA Microcode buffer pointer is NULL\n");
+ return -EINVAL;
+ }
+
+ if (plat->dma330.transfer_type == DMA_MEM_TO_DEV)
+ debug("INFO: mem2perip");
+ else if (plat->dma330.transfer_type == DMA_DEV_TO_MEM)
+ debug("INFO: perip2mem");
+ else
+ debug("INFO: DMA_MEM_TO_MEM");
+
+ debug(" - 0x%x%x -> 0x%x%x\nsize=%08x brst_size=2^%d ",
+ (u32)(plat->dma330.src_addr >> 32),
+ (u32)plat->dma330.src_addr,
+ (u32)(plat->dma330.dst_addr >> 32),
+ (u32)plat->dma330.dst_addr,
+ data_size_byte,
+ plat->brst_size);
+
+ debug("brst_len=%d singles_brst_size=2^%d\n", plat->brst_len,
+ plat->dma330.single_brst_size);
+
+ /* brst_size = 2 ^ plat->brst_size */
+ brst_size = 1 << plat->brst_size;
+
+ /* Fool proof checking */
+ if (plat->brst_size < 0 || plat->brst_size >
+ plat->max_brst_size) {
+ debug("ERROR DMA330: brst_size must 0-%d (not %d)\n",
+ plat->max_brst_size, plat->brst_size);
+ return -EINVAL;
+ }
+ if (plat->dma330.single_brst_size < 0 ||
+ plat->dma330.single_brst_size > plat->max_brst_size) {
+ debug("ERROR DMA330 : single_brst_size must 0-%d (not %d)\n",
+ plat->max_brst_size, plat->dma330.single_brst_size);
+ return -EINVAL;
+ }
+ if (plat->brst_len < 1 || plat->brst_len > 16) {
+ debug("ERROR DMA330 : brst_len must 1-16 (not %d)\n",
+ plat->brst_len);
+ return -EINVAL;
+ }
+ if (plat->dma330.src_addr & (brst_size - 1)) {
+ debug("ERROR DMA330 : Source address unaligned\n");
+ return -EINVAL;
+ }
+ if (plat->dma330.dst_addr & (brst_size - 1)) {
+ debug("ERROR DMA330 : Destination address unaligned\n");
+ return -EINVAL;
+ }
+
+ /* Setup the command list */
+ /* DMAMOV SAR, x->src_addr */
+ off += _emit_mov(&buf[off], SAR, plat->dma330.src_addr);
+ /* DMAMOV DAR, x->dst_addr */
+ off += _emit_mov(&buf[off], DAR, plat->dma330.dst_addr);
+ /* DMAFLUSHP P(periheral_id) */
+ if (plat->dma330.transfer_type != DMA_MEM_TO_MEM)
+ off += _emit_flushp(&buf[off], plat->dma330.peripheral_id);
+
+ /* Preparing the CCR value */
+ if (plat->dma330.transfer_type == DMA_MEM_TO_DEV) {
+ /* Disable auto increment */
+ reqcfg.dst_inc = 0;
+ /* Enable auto increment */
+ reqcfg.src_inc = 1;
+ } else if (plat->dma330.transfer_type == DMA_DEV_TO_MEM) {
+ reqcfg.dst_inc = 1;
+ reqcfg.src_inc = 0;
+ } else {
+ /* DMA_MEM_TO_MEM */
+ reqcfg.dst_inc = 1;
+ reqcfg.src_inc = 1;
+ }
+
+ if (!plat->dma330.secure)
+ reqcfg.nonsecure = 1; /* Non Secure mode */
+ else
+ reqcfg.nonsecure = 0; /* Secure mode */
+
+ if (plat->dma330.enable_cache1 == 0) {
+ /* Noncacheable but bufferable */
+ reqcfg.dcctl = 0x1;
+ reqcfg.scctl = 0x1;
+ } else {
+ if (plat->dma330.transfer_type == DMA_MEM_TO_DEV) {
+ reqcfg.dcctl = 0x1;
+ /* Cacheable write back */
+ reqcfg.scctl = 0x7;
+ } else if (plat->dma330.transfer_type == DMA_DEV_TO_MEM) {
+ reqcfg.dcctl = 0x7;
+ reqcfg.scctl = 0x1;
+ } else {
+ reqcfg.dcctl = 0x7;
+ reqcfg.scctl = 0x7;
+ }
+ }
+ /* 1 - Privileged */
+ reqcfg.privileged = 1;
+ /* 0 - Data access */
+ reqcfg.insnaccess = 0;
+ /* 0 - No endian swap */
+ reqcfg.swap = 0;
+ /* DMA burst length */
+ reqcfg.brst_len = plat->brst_len;
+ /* DMA burst size */
+ reqcfg.brst_size = plat->brst_size;
+ /* Preparing the CCR value */
+ ccr = _prepare_ccr(&reqcfg);
+ /* DMAMOV CCR, ccr */
+ off += _emit_mov(&buf[off], CCR, ccr);
+
+ /* BURST */
+ /* Can initiate a burst? */
+ while (data_size_byte >= brst_size * plat->brst_len) {
+ lcnt0 = data_size_byte / (brst_size * plat->brst_len);
+ lcnt1 = 0;
+ if (lcnt0 >= 256 * 256) {
+ lcnt0 = 256;
+ lcnt1 = 256;
+ } else if (lcnt0 >= 256) {
+ lcnt1 = lcnt0 / 256;
+ lcnt0 = 256;
+ }
+ data_size_byte = data_size_byte -
+ (brst_size * plat->brst_len * lcnt0 * lcnt1);
+
+ debug("Transferring 0x%08x Remain 0x%08x\n", (brst_size *
+ plat->brst_len * lcnt0 * lcnt1), data_size_byte);
+ debug("Running burst - brst_size=2^%d, brst_len=%d, ",
+ plat->brst_size, plat->brst_len);
+ debug("lcnt0=%d, lcnt1=%d\n", lcnt0, lcnt1);
+
+ if (lcnt1) {
+ /* DMALP1 */
+ off += _emit_lp(&buf[off], 1, lcnt1);
+ loopjmp1 = off;
+ }
+ /* DMALP0 */
+ off += _emit_lp(&buf[off], 0, lcnt0);
+ loopjmp0 = off;
+ /* DMAWFP periheral_id, burst */
+ if (plat->dma330.transfer_type != DMA_MEM_TO_MEM)
+ off += _emit_wfp(&buf[off], BURST,
+ plat->dma330.peripheral_id);
+ /* DMALD */
+ off += _emit_ld(&buf[off], ALWAYS);
+
+ WATCHDOG_RESET();
+
+ /* DMARMB */
+ off += _emit_rmb(&buf[off]);
+ /* DMASTPB peripheral_id */
+ if (plat->dma330.transfer_type != DMA_MEM_TO_MEM)
+ off += _emit_stp(&buf[off], BURST,
+ plat->dma330.peripheral_id);
+ else
+ off += _emit_st(&buf[off], ALWAYS);
+ /* DMAWMB */
+ off += _emit_wmb(&buf[off]);
+ /* DMALP0END */
+ struct _arg_lpend lpend;
+
+ lpend.cond = ALWAYS;
+ lpend.forever = 0;
+ /* Loop cnt 0 */
+ lpend.loop = 0;
+ lpend.bjump = off - loopjmp0;
+ off += _emit_lpend(&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(&buf[off], &lpend);
+ }
+ /* Ensure the microcode don't exceed buffer size */
+ if (off > plat->buf_size) {
+ debug("ERROR DMA330 : Exceed buffer size\n");
+ return -ENOMEM;
+ }
+ }
+
+ /* SINGLE */
+ brst_len = 1;
+ /* brst_size = 2 ^ plat->dma330.single_brst_size; */
+ brst_size = (1 << plat->dma330.single_brst_size);
+ lcnt0 = data_size_byte / (brst_size * brst_len);
+
+ /* Ensure all data will be transferred */
+ data_size_byte = data_size_byte -
+ (brst_size * brst_len * lcnt0);
+ if (data_size_byte) {
+ debug("ERROR DMA330 : Detected the possibility of ");
+ debug("untransfered data. Please ensure correct single ");
+ debug("burst size\n");
+ }
+
+ if (lcnt0) {
+ debug("Transferring 0x%08x Remain 0x%08x\n", (brst_size *
+ brst_len * lcnt0 * lcnt1), data_size_byte);
+ debug("Running single - brst_size=2^%d, brst_len=%d, ",
+ brst_size, brst_len);
+ debug("lcnt0=%d\n", lcnt0);
+
+ /* Preparing the CCR value */
+ /* DMA burst length */
+ reqcfg.brst_len = brst_len;
+ /* DMA burst size */
+ reqcfg.brst_size = brst_size;
+ ccr = _prepare_ccr(&reqcfg);
+ /* DMAMOV CCR, ccr */
+ off += _emit_mov(&buf[off], CCR, ccr);
+
+ /* DMALP0 */
+ off += _emit_lp(&buf[off], 0, lcnt0);
+ loopjmp0 = off;
+ /* DMAWFP peripheral_id, single */
+ if (plat->dma330.transfer_type != DMA_MEM_TO_MEM)
+ off += _emit_wfp(&buf[off], SINGLE,
+ plat->dma330.peripheral_id);
+
+ /* DMALD */
+ off += _emit_ld(&buf[off], ALWAYS);
+ /* DMARMB */
+ off += _emit_rmb(&buf[off]);
+ /* DMASTPS peripheral_id */
+ if (plat->dma330.transfer_type != DMA_MEM_TO_MEM)
+ off += _emit_stp(&buf[off], SINGLE,
+ plat->dma330.peripheral_id);
+ else
+ off += _emit_st(&buf[off], ALWAYS);
+
+ /* DMAWMB */
+ off += _emit_wmb(&buf[off]);
+ /* DMALPEND */
+ struct _arg_lpend lpend1;
+
+ lpend1.cond = ALWAYS;
+ lpend1.forever = 0;
+ /* loop cnt 0 */
+ lpend1.loop = 0;
+ lpend1.bjump = off - loopjmp0;
+ off += _emit_lpend(&buf[off], &lpend1);
+ /* Ensure the microcode don't exceed buffer size */
+ if (off > plat->buf_size) {
+ puts("ERROR DMA330 : Exceed buffer size\n");
+ return -ENOMEM;
+ }
+ }
+
+ /* DMAEND */
+ off += _emit_end(&buf[off]);
+
+ ret = dma330_transfer_start(plat);
+ if (ret)
+ return ret;
+
+ return dma330_transfer_finish(plat);
+}
+
+/**
+ * dma330_transfer_zeroes - DMA transfer zeros.
+ *
+ * @plat: Pointer to struct dma_dma330_platdata.
+ *
+ * Used to write zeros to a memory chunk for memory scrubbing purpose.
+ *
+ * Return: Negative value for error or not successful. 0 for successful.
+ */
+static int dma330_transfer_zeroes_setup(struct dma_dma330_platdata *plat)
+{
+ /* Variable declaration */
+ /* Buffer offset clear to 0 */
+ int off = 0;
+ int ret = 0;
+ /* For DMALPEND */
+ u32 loopjmp0, loopjmp1;
+ /* Loop count 0 */
+ u32 lcnt0 = 0;
+ /* Loop count 1 */
+ u32 lcnt1 = 0;
+ u32 brst_size = 0;
+ u32 brst_len = 0;
+ u32 data_size_byte = plat->dma330.size_byte;
+ /* Strong order memory is required to store microcode command list */
+ u8 *buf = (u8 *)plat->dma330.buf;
+ /* Channel Control Register */
+ u32 ccr = 0;
+ struct dma330_reqcfg reqcfg;
+
+ if (!buf) {
+ debug("ERROR DMA330 : DMA Microcode buffer pointer is NULL\n");
+ return -EINVAL;
+ }
+
+ debug("INFO: Write zeros -> ");
+ debug("0x%x%x size=0x%08x\n",
+ (u32)(plat->dma330.dst_addr >> 32),
+ (u32)plat->dma330.dst_addr,
+ data_size_byte);
+
+ plat->dma330.single_brst_size = 1;
+
+ /* burst_size = 2 ^ plat->brst_size */
+ brst_size = 1 << plat->brst_size;
+
+ /* Setup the command list */
+ /* DMAMOV DAR, x->dst_addr */
+ off += _emit_mov(&buf[off], DAR, plat->dma330.dst_addr);
+
+ /* Preparing the CCR value */
+ /* Enable auto increment */
+ reqcfg.dst_inc = 1;
+ /* Disable auto increment (not applicable) */
+ reqcfg.src_inc = 0;
+ /* Noncacheable but bufferable */
+ reqcfg.dcctl = 0x1;
+ /* Noncacheable and bufferable */
+ reqcfg.scctl = 0x1;
+ /* 1 - Privileged */
+ reqcfg.privileged = 1;
+ /* 0 - Data access */
+ reqcfg.insnaccess = 0;
+ /* 0 - No endian swap */
+ reqcfg.swap = 0;
+
+ if (!plat->dma330.secure)
+ reqcfg.nonsecure = 1; /* Non Secure mode */
+ else
+ reqcfg.nonsecure = 0; /* Secure mode */
+
+ /* DMA burst length */
+ reqcfg.brst_len = plat->brst_len;
+ /* DMA burst size */
+ reqcfg.brst_size = plat->brst_size;
+ /* Preparing the CCR value */
+ ccr = _prepare_ccr(&reqcfg);
+ /* DMAMOV CCR, ccr */
+ off += _emit_mov(&buf[off], CCR, ccr);
+
+ /* BURST */
+ /* Can initiate a burst? */
+ while (data_size_byte >= brst_size * plat->brst_len) {
+ lcnt0 = data_size_byte / (brst_size * plat->brst_len);
+ lcnt1 = 0;
+ if (lcnt0 >= 256 * 256) {
+ lcnt0 = 256;
+ lcnt1 = 256;
+ } else if (lcnt0 >= 256) {
+ lcnt1 = lcnt0 / 256;
+ lcnt0 = 256;
+ }
+
+ data_size_byte = data_size_byte -
+ (brst_size * plat->brst_len * lcnt0 * lcnt1);
+
+ debug("Transferring 0x%08x Remain 0x%08x\n", (brst_size *
+ plat->brst_len * lcnt0 * lcnt1), data_size_byte);
+ debug("Running burst - brst_size=2^%d, brst_len=%d,",
+ plat->brst_size, plat->brst_len);
+ debug("lcnt0=%d, lcnt1=%d\n", lcnt0, lcnt1);
+
+ if (lcnt1) {
+ /* DMALP1 */
+ off += _emit_lp(&buf[off], 1, lcnt1);
+ loopjmp1 = off;
+ }
+ /* DMALP0 */
+ off += _emit_lp(&buf[off], 0, lcnt0);
+ loopjmp0 = off;
+ /* DMALSTZ */
+ off += _emit_stz(&buf[off]);
+
+ WATCHDOG_RESET();
+
+ /* DMALP0END */
+ struct _arg_lpend lpend;
+
+ lpend.cond = ALWAYS;
+ lpend.forever = 0;
+ /* Loop cnt 0 */
+ lpend.loop = 0;
+ lpend.bjump = off - loopjmp0;
+ off += _emit_lpend(&buf[off], &lpend);
+ /* DMALP1END */
+ if (lcnt1) {
+ struct _arg_lpend lpend;
+
+ lpend.cond = ALWAYS;
+ lpend.forever = 0;
+ /* Loop cnt 1*/
+ lpend.loop = 1;
+ lpend.bjump = off - loopjmp1;
+ off += _emit_lpend(&buf[off], &lpend);
+ }
+ /* Ensure the microcode don't exceed buffer size */
+ if (off > plat->buf_size) {
+ printf("off = %d\n", off);
+ debug("ERROR DMA330 : Exceed buffer size off %d\n",
+ off);
+ return -ENOMEM;
+ }
+ }
+
+ /* SINGLE */
+ brst_len = 1;
+ /* brst_size = 2 ^ plat->dma330.single_brst_size */
+ brst_size = (1 << plat->dma330.single_brst_size);
+ lcnt0 = data_size_byte / (brst_size * brst_len);
+
+ /* ensure all data will be transferred */
+ data_size_byte = data_size_byte -
+ (brst_size * brst_len * lcnt0);
+ if (data_size_byte) {
+ debug("ERROR DMA330 : Detected the possibility of ");
+ debug("untransfered data. Please ensure correct single ");
+ debug("burst size\n");
+ }
+
+ if (lcnt0) {
+ debug("Transferring 0x%08x Remain 0x%08x\n", (brst_size *
+ brst_len * lcnt0), data_size_byte);
+ debug("Running single - brst_size=2^%d, brst_len=%d, ",
+ brst_size, brst_len);
+ debug("lcnt0=%d\n", lcnt0);
+
+ /* Preparing the CCR value */
+ /* DMA burst length */
+ reqcfg.brst_len = brst_len;
+ /* DMA burst size */
+ reqcfg.brst_size = brst_size;
+ ccr = _prepare_ccr(&reqcfg);
+ /* DMAMOV CCR, ccr */
+ off += _emit_mov(&buf[off], CCR, ccr);
+
+ /* DMALP0 */
+ off += _emit_lp(&buf[off], 0, lcnt0);
+ loopjmp0 = off;
+ /* DMALSTZ */
+ off += _emit_stz(&buf[off]);
+ /* DMALPEND */
+ struct _arg_lpend lpend1;
+
+ lpend1.cond = ALWAYS;
+ lpend1.forever = 0;
+ /* Loop cnt 0 */
+ lpend1.loop = 0;
+ lpend1.bjump = off - loopjmp0;
+ off += _emit_lpend(&buf[off], &lpend1);
+ /* Ensure the microcode don't exceed buffer size */
+ if (off > plat->buf_size) {
+ debug("ERROR DMA330 : Exceed buffer size\n");
+ return -ENOMEM;
+ }
+ }
+
+ /* DMAEND */
+ off += _emit_end(&buf[off]);
+
+ ret = dma330_transfer_start(plat);
+ if (ret)
+ return ret;
+
+ return dma330_transfer_finish(plat);
+}
+
+static int dma330_transfer_zeroes(struct udevice *dev, void *dst, size_t len)
+{
+ struct dma_dma330_platdata *plat = dev->platdata;
+ int ret = 0;
+
+ /* If DMA access is set to secure, base change to DMA secure base */
+ if (plat->dma330.secure)
+ plat->base += 0x1000;
+
+ plat->dma330.dst_addr = (phys_size_t)(uintptr_t)dst;
+ plat->dma330.size_byte = len;
+
+ ret = dma330_transfer_zeroes_setup(plat);
+
+ /* Revert back to non secure DMA base */
+ if (plat->dma330.secure)
+ plat->base -= 0x1000;
+
+ return ret;
+}
+
+static int dma330_transfer(struct udevice *dev, int direction, void *dst,
+ void *src, size_t len)
+{
+ struct dma_dma330_platdata *plat = dev->platdata;
+ int ret = 0;
+
+ /* If DMA access is set to secure, base change to DMA secure base */
+ if (plat->dma330.secure)
+ plat->base += 0x1000;
+
+ plat->dma330.dst_addr = (phys_size_t)(uintptr_t)dst;
+ plat->dma330.src_addr = (phys_size_t)(uintptr_t)src;
+ plat->dma330.size_byte = len;
+
+ switch (direction) {
+ case DMA_MEM_TO_MEM:
+ plat->dma330.transfer_type = DMA_SUPPORTS_MEM_TO_MEM;
+ break;
+ case DMA_MEM_TO_DEV:
+ plat->dma330.transfer_type = DMA_SUPPORTS_MEM_TO_DEV;
+ break;
+ case DMA_DEV_TO_MEM:
+ plat->dma330.transfer_type = DMA_SUPPORTS_DEV_TO_MEM;
+ break;
+ }
+
+ ret = dma330_transfer_setup(plat);
+
+ /* Revert back to non secure DMA base */
+ if (plat->dma330.secure)
+ plat->base -= 0x1000;
+
+ return ret;
+}
+
+static int dma330_ofdata_to_platdata(struct udevice *dev)
+{
+ struct dma_dma330_platdata *plat = dev->platdata;
+ const void *blob = gd->fdt_blob;
+ int node = dev_of_offset(dev);
+
+ plat->base = (void __iomem *)devfdt_get_addr(dev);
+ plat->max_brst_size = fdtdec_get_uint(blob, node, "dma-max-burst-size",
+ 2);
+ plat->brst_size = fdtdec_get_uint(blob, node, "dma-burst-size", 2);
+ plat->brst_len = fdtdec_get_uint(blob, node, "dma-burst-length", 2);
+
+ return 0;
+}
+
+static int dma330_probe(struct udevice *adev)
+{
+ struct dma_dev_priv *uc_priv = dev_get_uclass_priv(adev);
+
+ uc_priv->supported = (DMA_SUPPORTS_MEM_TO_MEM |
+ DMA_SUPPORTS_MEM_TO_DEV |
+ DMA_SUPPORTS_DEV_TO_MEM);
+ return 0;
+}
+
+static const struct dma_ops dma330_ops = {
+ .transfer = dma330_transfer,
+ .transfer_zeroes = dma330_transfer_zeroes,
+};
+
+static const struct udevice_id dma330_ids[] = {
+ { .compatible = "arm,pl330" },
+ { .compatible = "arm,dma330" },
+ { /* sentinel */ }
+};
+
+U_BOOT_DRIVER(dma_dma330) = {
+ .name = "dma_dma330",
+ .id = UCLASS_DMA,
+ .of_match = dma330_ids,
+ .ops = &dma330_ops,
+ .ofdata_to_platdata = dma330_ofdata_to_platdata,
+ .probe = dma330_probe,
+ .platdata_auto_alloc_size = sizeof(struct dma_dma330_platdata),
+};
diff --git a/include/dma330.h b/include/dma330.h
new file mode 100644
index 0000000..c054bf2
--- /dev/null
+++ b/include/dma330.h
@@ -0,0 +1,136 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2018 Intel Corporation <www.intel.com>
+ */
+
+#include <dma.h>
+#include <linux/sizes.h>
+
+#ifndef __DMA330_CORE_H
+#define __DMA330_CORE_H
+
+#define DMA330_MAX_CHAN 8
+#define DMA330_MAX_IRQS 32
+#define DMA330_MAX_PERI 32
+
+#define DMA330_STATE_STOPPED BIT(0)
+#define DMA330_STATE_EXECUTING BIT(1)
+#define DMA330_STATE_WFE BIT(2)
+#define DMA330_STATE_FAULTING BIT(3)
+#define DMA330_STATE_COMPLETING BIT(4)
+#define DMA330_STATE_WFP BIT(5)
+#define DMA330_STATE_KILLING BIT(6)
+#define DMA330_STATE_FAULT_COMPLETING BIT(7)
+#define DMA330_STATE_CACHEMISS BIT(8)
+#define DMA330_STATE_UPDTPC BIT(9)
+#define DMA330_STATE_ATBARRIER BIT(10)
+#define DMA330_STATE_QUEUEBUSY BIT(11))
+#define DMA330_STATE_INVALID BIT(15)
+
+enum dma330_srccachectrl {
+ SCCTRL0 = 0, /* Noncacheable and nonbufferable */
+ SCCTRL1, /* Bufferable only */
+ SCCTRL2, /* Cacheable, but do not allocate */
+ SCCTRL3, /* Cacheable and bufferable, but do not allocate */
+ SINVALID1,
+ SINVALID2,
+ SCCTRL6, /* Cacheable write-through, allocate on reads only */
+ SCCTRL7, /* Cacheable write-back, allocate on reads only */
+};
+
+enum dma330_dstcachectrl {
+ DCCTRL0 = 0, /* Noncacheable and nonbufferable */
+ DCCTRL1, /* Bufferable only */
+ DCCTRL2, /* Cacheable, but do not allocate */
+ DCCTRL3, /* Cacheable and bufferable, but do not allocate */
+ DINVALID1 = 8,
+ DINVALID2,
+ DCCTRL6, /* Cacheable write-through, allocate on writes only */
+ DCCTRL7, /* Cacheable write-back, allocate on writes only */
+};
+
+enum dma330_byteswap {
+ SWAP_NO = 0,
+ SWAP_2,
+ SWAP_4,
+ SWAP_8,
+ SWAP_16,
+};
+
+/**
+ * dma330_reqcfg - Request Configuration.
+ *
+ * The DMA330 core does not modify this and uses the last
+ * working configuration if the request doesn't provide any.
+ *
+ * The Client may want to provide this info only for the
+ * first request and a request with new settings.
+ */
+struct dma330_reqcfg {
+ /* Address Incrementing */
+ u8 dst_inc;
+ u8 src_inc;
+
+ /*
+ * For now, the SRC & DST protection levels
+ * and burst size/length are assumed same.
+ */
+ u8 nonsecure;
+ u8 privileged;
+ u8 insnaccess;
+ u8 brst_len;
+ u8 brst_size; /* in power of 2 */
+
+ enum dma330_dstcachectrl dcctl;
+ enum dma330_srccachectrl scctl;
+ enum dma330_byteswap swap;
+};
+
+/**
+ * dma330_transfer_struct - Structure to be passed in for dma330_transfer_x.
+ *
+ * @channel0_manager1: Switching configuration on DMA channel or DMA manager.
+ * @channel_num: Channel number assigned, valid from 0 to 7.
+ * @src_addr: Address to transfer from / source.
+ * @dst_addr: Address to transfer to / destination.
+ * @size_byte: Number of bytes to be transferred.
+ * @brst_size: Valid from 0 - 4,
+ * where 0 = 1 (2 ^ 0) bytes and 3 = 16 bytes (2 ^ 4).
+ * @max_brst_size: Max transfer size (from 0 - 4).
+ * @single_brst_size: Single transfer size (from 0 - 4).
+ * @brst_len: Valid from 1 - 16 where each burst can transfer 1 - 16
+ * Data chunk (each chunk size equivalent to brst_size).
+ * @peripheral_id: Assigned peripheral_id, valid from 0 to 31.
+ * @transfer_type: MEM2MEM, MEM2PERIPH or PERIPH2MEM.
+ * @enable_cache1: 1 for cache enabled for memory
+ * (cacheable and bufferable, but do not allocate).
+ * @buf: Buffer handler which will point to the memory
+ * allocated for dma microcode
+ * @secure: Set DMA channel in secure mode.
+ *
+ * Description of the structure.
+ */
+struct dma330_transfer_struct {
+ u32 channel0_manager1;
+ u32 channel_num;
+ phys_size_t src_addr;
+ phys_size_t dst_addr;
+ u32 size_byte;
+ u32 single_brst_size;
+ u32 peripheral_id;
+ u32 transfer_type;
+ u32 enable_cache1;
+ u32 *buf;
+ u8 secure;
+};
+
+struct dma_dma330_platdata {
+ void __iomem *base;
+ u32 buf_size;
+ u32 max_brst_size;
+ u32 brst_size;
+ u32 brst_len;
+ struct dma330_transfer_struct dma330;
+};
+
+#endif /* __DMA330_CORE_H */
--
2.2.0
More information about the U-Boot
mailing list