
From: Tien Fong Chee tien.fong.chee@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@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 */