Hi Tien, On 31 May 2018 at 02:08, <tien.fong.c...@intel.com> wrote: > From: Tien Fong Chee <tien.fong.c...@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.c...@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
It is possible to use enum for some of these? enum { DS = 0, DS_ST_STOP, ... } > + > +/* 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 Can you make the mask a shifted mask? Then it is easier to use in clrsetbits_le32(), for example. e.g. #define CC_SRCCCTRL_MASK (7 << CC_SRCCCTRL_SHFT) > +#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 Can DMA330_DBGCMD_DUMP be a static function, and lower case? It's only used in one file, right? > + > +/* Enum declaration */ We know that :-) Can you please add comments explaining what these enums are for, and what the values mean? > +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 { Please drop the _ on these struct names, and document the members here. > + 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[]) Why is everything inline? The compiler will do it automatically when there is only one caller. Other than that, it just increases code size I think. > +{ > + 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; These should be enums too so we don't have open-coded values in the code, Please check throughout. > + 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); More open-coded things > + 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. The members of the struct should be documented in the struct above, not here. > + * > + * 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; Why have these? Can you not use arg-bjump, etc., directly? > + > + buffer[0] = CMD_DMALPEND; > + if (loop) > + buffer[0] |= (1 << 2); More open-coded things > + 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; Can you use put_unaligned_le32() ? Also check the same thing below. > + 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"); please add a blank line before 'return' > + 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); Perhaps these three values are the same for each command? If so, the three expressions (0, 1, 2) could go in an enum. > + > + 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; These local vars seem pointless as the are only used once. Maybe it's worth it for 'addr', but even there you should use the put_unaligned thing. > + > + 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); You don't need the outer brackets on these. > + > + 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) This function is extremely long, Can it be split into separete sub-functions which perform the work, if the work can be split sensibly into different parts? > +{ > + /* 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 */ I am not keen on these comments. The struct members should be documented when the struct is declared, not here. > + 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? */ These two comments should be joined > + 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 */ What does this comment mean? > + 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) Can this function be combined with the above somehow? It seems like a lot of duplicate code. > +{ > + /* 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; What is this? I suggest you set up the secure base separately in 'plat', e.g. with a base_secure member. You should not change platform data while running. You can pass the base address to dma330_transfer_zeroes_setup, perhaps. > + > + 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; Same as above > + > + 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); dev_read_addr() > + plat->max_brst_size = fdtdec_get_uint(blob, node, > "dma-max-burst-size", > + 2); Use dev_read API please (live tree) > + 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. This is a good way to document structs. Please do it for the others. > + * > + * Description of the structure. Yes? > + */ > +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 > Regards, Simon _______________________________________________ U-Boot mailing list U-Boot@lists.denx.de https://lists.denx.de/listinfo/u-boot