GK SDK 源码库: XMIPCLinuxV100R005C00SPC030 (kernel/tools/open_source excluded)

This commit is contained in:
lai
2026-09-06 03:52:57 +08:00
commit b1928b41c0
21813 changed files with 4413081 additions and 0 deletions
@@ -0,0 +1,62 @@
menu "DMA Support"
config DMA
bool "Enable Driver Model for DMA drivers"
depends on DM
help
Enable driver model for DMA. DMA engines can do
asynchronous data transfers without involving the host
CPU. Currently, this framework can be used to offload
memory copies to and from devices like qspi, ethernet
etc Drivers provide methods to access the DMA devices
buses that is used to transfer data to and from memory.
The uclass interface is defined in include/dma.h.
config DMA_CHANNELS
bool "Enable DMA channels support"
depends on DMA
help
Enable channels support for DMA. Some DMA controllers have multiple
channels which can either transfer data to/from different devices.
config SANDBOX_DMA
bool "Enable the sandbox DMA test driver"
depends on DMA && DMA_CHANNELS && SANDBOX
help
Enable support for a test DMA uclass implementation. It stimulates
DMA transfer by simple copying data between channels.
config BCM6348_IUDMA
bool "BCM6348 IUDMA driver"
depends on ARCH_BMIPS
select DMA_CHANNELS
help
Enable the BCM6348 IUDMA driver.
This driver support data transfer from devices to
memory and from memory to devices.
config TI_EDMA3
bool "TI EDMA3 driver"
help
Enable the TI EDMA3 driver for DRA7xx and AM43xx evms.
This driver support data transfer between memory
regions.
config APBH_DMA
bool "Support APBH DMA"
depends on MX23 || MX28 || MX6 || MX7
help
Enable APBH DMA driver.
if APBH_DMA
config APBH_DMA_BURST
bool "Enable DMA BURST"
config APBH_DMA_BURST8
bool "Enable DMA BURST8"
endif
source "drivers/dma/ti/Kconfig"
endmenu # menu "DMA Support"
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,225 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (C) 2004-2007 Freescale Semiconductor, Inc.
*/
#include <common.h>
/* Functions for initializing variable tables of different types of tasks. */
/*
* Do not edit!
*/
#include <MCD_dma.h>
extern dmaRegs *MCD_dmaBar;
/* Task 0 */
void MCD_startDmaChainNoEu(int *currBD, short srcIncr, short destIncr,
int xferSize, short xferSizeIncr, int *cSave,
volatile TaskTableEntry * taskTable, int channel)
{
volatile TaskTableEntry *taskChan = taskTable + channel;
MCD_SET_VAR(taskChan, 2, (u32) currBD); /* var[2] */
MCD_SET_VAR(taskChan, 25, (u32) (0xe000 << 16) | (0xffff & srcIncr)); /* inc[1] */
MCD_SET_VAR(taskChan, 24, (u32) (0xe000 << 16) | (0xffff & destIncr)); /* inc[0] */
MCD_SET_VAR(taskChan, 11, (u32) xferSize); /* var[11] */
MCD_SET_VAR(taskChan, 26, (u32) (0x2000 << 16) | (0xffff & xferSizeIncr)); /* inc[2] */
MCD_SET_VAR(taskChan, 0, (u32) cSave); /* var[0] */
MCD_SET_VAR(taskChan, 1, (u32) 0x00000000); /* var[1] */
MCD_SET_VAR(taskChan, 3, (u32) 0x00000000); /* var[3] */
MCD_SET_VAR(taskChan, 4, (u32) 0x00000000); /* var[4] */
MCD_SET_VAR(taskChan, 5, (u32) 0x00000000); /* var[5] */
MCD_SET_VAR(taskChan, 6, (u32) 0x00000000); /* var[6] */
MCD_SET_VAR(taskChan, 7, (u32) 0x00000000); /* var[7] */
MCD_SET_VAR(taskChan, 8, (u32) 0x00000000); /* var[8] */
MCD_SET_VAR(taskChan, 9, (u32) 0x00000000); /* var[9] */
MCD_SET_VAR(taskChan, 10, (u32) 0x00000000); /* var[10] */
MCD_SET_VAR(taskChan, 12, (u32) 0x00000000); /* var[12] */
MCD_SET_VAR(taskChan, 13, (u32) 0x80000000); /* var[13] */
MCD_SET_VAR(taskChan, 14, (u32) 0x00000010); /* var[14] */
MCD_SET_VAR(taskChan, 15, (u32) 0x00000004); /* var[15] */
MCD_SET_VAR(taskChan, 16, (u32) 0x08000000); /* var[16] */
MCD_SET_VAR(taskChan, 27, (u32) 0x00000000); /* inc[3] */
MCD_SET_VAR(taskChan, 28, (u32) 0x80000000); /* inc[4] */
MCD_SET_VAR(taskChan, 29, (u32) 0x80000001); /* inc[5] */
MCD_SET_VAR(taskChan, 30, (u32) 0x40000000); /* inc[6] */
/* Set the task's Enable bit in its Task Control Register */
MCD_dmaBar->taskControl[channel] |= (u16) 0x8000;
}
/* Task 1 */
void MCD_startDmaSingleNoEu(char *srcAddr, short srcIncr, char *destAddr,
short destIncr, int dmaSize, short xferSizeIncr,
int flags, int *currBD, int *cSave,
volatile TaskTableEntry * taskTable, int channel)
{
volatile TaskTableEntry *taskChan = taskTable + channel;
MCD_SET_VAR(taskChan, 7, (u32) srcAddr); /* var[7] */
MCD_SET_VAR(taskChan, 25, (u32) (0xe000 << 16) | (0xffff & srcIncr)); /* inc[1] */
MCD_SET_VAR(taskChan, 2, (u32) destAddr); /* var[2] */
MCD_SET_VAR(taskChan, 24, (u32) (0xe000 << 16) | (0xffff & destIncr)); /* inc[0] */
MCD_SET_VAR(taskChan, 3, (u32) dmaSize); /* var[3] */
MCD_SET_VAR(taskChan, 26, (u32) (0x2000 << 16) | (0xffff & xferSizeIncr)); /* inc[2] */
MCD_SET_VAR(taskChan, 5, (u32) flags); /* var[5] */
MCD_SET_VAR(taskChan, 1, (u32) currBD); /* var[1] */
MCD_SET_VAR(taskChan, 0, (u32) cSave); /* var[0] */
MCD_SET_VAR(taskChan, 4, (u32) 0x00000000); /* var[4] */
MCD_SET_VAR(taskChan, 6, (u32) 0x00000000); /* var[6] */
MCD_SET_VAR(taskChan, 8, (u32) 0x00000000); /* var[8] */
MCD_SET_VAR(taskChan, 9, (u32) 0x00000004); /* var[9] */
MCD_SET_VAR(taskChan, 10, (u32) 0x08000000); /* var[10] */
MCD_SET_VAR(taskChan, 27, (u32) 0x00000000); /* inc[3] */
MCD_SET_VAR(taskChan, 28, (u32) 0x80000001); /* inc[4] */
MCD_SET_VAR(taskChan, 29, (u32) 0x40000000); /* inc[5] */
/* Set the task's Enable bit in its Task Control Register */
MCD_dmaBar->taskControl[channel] |= (u16) 0x8000;
}
/* Task 2 */
void MCD_startDmaChainEu(int *currBD, short srcIncr, short destIncr,
int xferSize, short xferSizeIncr, int *cSave,
volatile TaskTableEntry * taskTable, int channel)
{
volatile TaskTableEntry *taskChan = taskTable + channel;
MCD_SET_VAR(taskChan, 3, (u32) currBD); /* var[3] */
MCD_SET_VAR(taskChan, 25, (u32) (0xe000 << 16) | (0xffff & srcIncr)); /* inc[1] */
MCD_SET_VAR(taskChan, 24, (u32) (0xe000 << 16) | (0xffff & destIncr)); /* inc[0] */
MCD_SET_VAR(taskChan, 12, (u32) xferSize); /* var[12] */
MCD_SET_VAR(taskChan, 26, (u32) (0x2000 << 16) | (0xffff & xferSizeIncr)); /* inc[2] */
MCD_SET_VAR(taskChan, 0, (u32) cSave); /* var[0] */
MCD_SET_VAR(taskChan, 1, (u32) 0x00000000); /* var[1] */
MCD_SET_VAR(taskChan, 2, (u32) 0x00000000); /* var[2] */
MCD_SET_VAR(taskChan, 4, (u32) 0x00000000); /* var[4] */
MCD_SET_VAR(taskChan, 5, (u32) 0x00000000); /* var[5] */
MCD_SET_VAR(taskChan, 6, (u32) 0x00000000); /* var[6] */
MCD_SET_VAR(taskChan, 7, (u32) 0x00000000); /* var[7] */
MCD_SET_VAR(taskChan, 8, (u32) 0x00000000); /* var[8] */
MCD_SET_VAR(taskChan, 9, (u32) 0x00000000); /* var[9] */
MCD_SET_VAR(taskChan, 10, (u32) 0x00000000); /* var[10] */
MCD_SET_VAR(taskChan, 11, (u32) 0x00000000); /* var[11] */
MCD_SET_VAR(taskChan, 13, (u32) 0x00000000); /* var[13] */
MCD_SET_VAR(taskChan, 14, (u32) 0x80000000); /* var[14] */
MCD_SET_VAR(taskChan, 15, (u32) 0x00000010); /* var[15] */
MCD_SET_VAR(taskChan, 16, (u32) 0x00000001); /* var[16] */
MCD_SET_VAR(taskChan, 17, (u32) 0x00000004); /* var[17] */
MCD_SET_VAR(taskChan, 18, (u32) 0x08000000); /* var[18] */
MCD_SET_VAR(taskChan, 27, (u32) 0x00000000); /* inc[3] */
MCD_SET_VAR(taskChan, 28, (u32) 0x80000000); /* inc[4] */
MCD_SET_VAR(taskChan, 29, (u32) 0xc0000000); /* inc[5] */
MCD_SET_VAR(taskChan, 30, (u32) 0x80000001); /* inc[6] */
MCD_SET_VAR(taskChan, 31, (u32) 0x40000000); /* inc[7] */
/* Set the task's Enable bit in its Task Control Register */
MCD_dmaBar->taskControl[channel] |= (u16) 0x8000;
}
/* Task 3 */
void MCD_startDmaSingleEu(char *srcAddr, short srcIncr, char *destAddr,
short destIncr, int dmaSize, short xferSizeIncr,
int flags, int *currBD, int *cSave,
volatile TaskTableEntry * taskTable, int channel)
{
volatile TaskTableEntry *taskChan = taskTable + channel;
MCD_SET_VAR(taskChan, 8, (u32) srcAddr); /* var[8] */
MCD_SET_VAR(taskChan, 25, (u32) (0xe000 << 16) | (0xffff & srcIncr)); /* inc[1] */
MCD_SET_VAR(taskChan, 3, (u32) destAddr); /* var[3] */
MCD_SET_VAR(taskChan, 24, (u32) (0xe000 << 16) | (0xffff & destIncr)); /* inc[0] */
MCD_SET_VAR(taskChan, 4, (u32) dmaSize); /* var[4] */
MCD_SET_VAR(taskChan, 26, (u32) (0x2000 << 16) | (0xffff & xferSizeIncr)); /* inc[2] */
MCD_SET_VAR(taskChan, 6, (u32) flags); /* var[6] */
MCD_SET_VAR(taskChan, 2, (u32) currBD); /* var[2] */
MCD_SET_VAR(taskChan, 0, (u32) cSave); /* var[0] */
MCD_SET_VAR(taskChan, 1, (u32) 0x00000000); /* var[1] */
MCD_SET_VAR(taskChan, 5, (u32) 0x00000000); /* var[5] */
MCD_SET_VAR(taskChan, 7, (u32) 0x00000000); /* var[7] */
MCD_SET_VAR(taskChan, 9, (u32) 0x00000000); /* var[9] */
MCD_SET_VAR(taskChan, 10, (u32) 0x00000001); /* var[10] */
MCD_SET_VAR(taskChan, 11, (u32) 0x00000004); /* var[11] */
MCD_SET_VAR(taskChan, 12, (u32) 0x08000000); /* var[12] */
MCD_SET_VAR(taskChan, 27, (u32) 0x00000000); /* inc[3] */
MCD_SET_VAR(taskChan, 28, (u32) 0xc0000000); /* inc[4] */
MCD_SET_VAR(taskChan, 29, (u32) 0x80000000); /* inc[5] */
MCD_SET_VAR(taskChan, 30, (u32) 0x80000001); /* inc[6] */
MCD_SET_VAR(taskChan, 31, (u32) 0x40000000); /* inc[7] */
/* Set the task's Enable bit in its Task Control Register */
MCD_dmaBar->taskControl[channel] |= (u16) 0x8000;
}
/* Task 4 */
void MCD_startDmaENetRcv(char *bDBase, char *currBD, char *rcvFifoPtr,
volatile TaskTableEntry * taskTable, int channel)
{
volatile TaskTableEntry *taskChan = taskTable + channel;
MCD_SET_VAR(taskChan, 0, (u32) bDBase); /* var[0] */
MCD_SET_VAR(taskChan, 3, (u32) currBD); /* var[3] */
MCD_SET_VAR(taskChan, 6, (u32) rcvFifoPtr); /* var[6] */
MCD_SET_VAR(taskChan, 1, (u32) 0x00000000); /* var[1] */
MCD_SET_VAR(taskChan, 2, (u32) 0x00000000); /* var[2] */
MCD_SET_VAR(taskChan, 4, (u32) 0x00000000); /* var[4] */
MCD_SET_VAR(taskChan, 5, (u32) 0x00000000); /* var[5] */
MCD_SET_VAR(taskChan, 7, (u32) 0x00000000); /* var[7] */
MCD_SET_VAR(taskChan, 8, (u32) 0x00000000); /* var[8] */
MCD_SET_VAR(taskChan, 9, (u32) 0x0000ffff); /* var[9] */
MCD_SET_VAR(taskChan, 10, (u32) 0x30000000); /* var[10] */
MCD_SET_VAR(taskChan, 11, (u32) 0x0fffffff); /* var[11] */
MCD_SET_VAR(taskChan, 12, (u32) 0x00000008); /* var[12] */
MCD_SET_VAR(taskChan, 24, (u32) 0x00000000); /* inc[0] */
MCD_SET_VAR(taskChan, 25, (u32) 0x60000000); /* inc[1] */
MCD_SET_VAR(taskChan, 26, (u32) 0x20000004); /* inc[2] */
MCD_SET_VAR(taskChan, 27, (u32) 0x40000000); /* inc[3] */
/* Set the task's Enable bit in its Task Control Register */
MCD_dmaBar->taskControl[channel] |= (u16) 0x8000;
}
/* Task 5 */
void MCD_startDmaENetXmit(char *bDBase, char *currBD, char *xmitFifoPtr,
volatile TaskTableEntry * taskTable, int channel)
{
volatile TaskTableEntry *taskChan = taskTable + channel;
MCD_SET_VAR(taskChan, 0, (u32) bDBase); /* var[0] */
MCD_SET_VAR(taskChan, 3, (u32) currBD); /* var[3] */
MCD_SET_VAR(taskChan, 11, (u32) xmitFifoPtr); /* var[11] */
MCD_SET_VAR(taskChan, 1, (u32) 0x00000000); /* var[1] */
MCD_SET_VAR(taskChan, 2, (u32) 0x00000000); /* var[2] */
MCD_SET_VAR(taskChan, 4, (u32) 0x00000000); /* var[4] */
MCD_SET_VAR(taskChan, 5, (u32) 0x00000000); /* var[5] */
MCD_SET_VAR(taskChan, 6, (u32) 0x00000000); /* var[6] */
MCD_SET_VAR(taskChan, 7, (u32) 0x00000000); /* var[7] */
MCD_SET_VAR(taskChan, 8, (u32) 0x00000000); /* var[8] */
MCD_SET_VAR(taskChan, 9, (u32) 0x00000000); /* var[9] */
MCD_SET_VAR(taskChan, 10, (u32) 0x00000000); /* var[10] */
MCD_SET_VAR(taskChan, 12, (u32) 0x00000000); /* var[12] */
MCD_SET_VAR(taskChan, 13, (u32) 0x0000ffff); /* var[13] */
MCD_SET_VAR(taskChan, 14, (u32) 0xffffffff); /* var[14] */
MCD_SET_VAR(taskChan, 15, (u32) 0x00000004); /* var[15] */
MCD_SET_VAR(taskChan, 16, (u32) 0x00000008); /* var[16] */
MCD_SET_VAR(taskChan, 24, (u32) 0x00000000); /* inc[0] */
MCD_SET_VAR(taskChan, 25, (u32) 0x60000000); /* inc[1] */
MCD_SET_VAR(taskChan, 26, (u32) 0x40000000); /* inc[2] */
MCD_SET_VAR(taskChan, 27, (u32) 0xc000fffc); /* inc[3] */
MCD_SET_VAR(taskChan, 28, (u32) 0xe0000004); /* inc[4] */
MCD_SET_VAR(taskChan, 29, (u32) 0x80000000); /* inc[5] */
MCD_SET_VAR(taskChan, 30, (u32) 0x4000ffff); /* inc[6] */
MCD_SET_VAR(taskChan, 31, (u32) 0xe0000001); /* inc[7] */
/* Set the task's Enable bit in its Task Control Register */
MCD_dmaBar->taskControl[channel] |= (u16) 0x8000;
}
@@ -0,0 +1,17 @@
# SPDX-License-Identifier: GPL-2.0+
#
# (C) Copyright 2006
# Wolfgang Denk, DENX Software Engineering, wd@denx.de.
obj-$(CONFIG_DMA) += dma-uclass.o
obj-$(CONFIG_FSLDMAFEC) += MCD_tasksInit.o MCD_dmaApi.o MCD_tasks.o
obj-$(CONFIG_APBH_DMA) += apbh_dma.o
obj-$(CONFIG_BCM6348_IUDMA) += bcm6348-iudma.o
obj-$(CONFIG_FSL_DMA) += fsl_dma.o
obj-$(CONFIG_SANDBOX_DMA) += sandbox-dma-test.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-y += ti/
@@ -0,0 +1,616 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Freescale i.MX28 APBH DMA driver
*
* Copyright (C) 2011 Marek Vasut <marek.vasut@gmail.com>
* on behalf of DENX Software Engineering GmbH
*
* Based on code from LTIB:
* Copyright (C) 2010 Freescale Semiconductor, Inc. All Rights Reserved.
*/
#include <cpu_func.h>
#include <linux/list.h>
#include <common.h>
#include <malloc.h>
#include <linux/errno.h>
#include <asm/io.h>
#include <asm/arch/clock.h>
#include <asm/arch/imx-regs.h>
#include <asm/arch/sys_proto.h>
#include <asm/mach-imx/dma.h>
#include <asm/mach-imx/regs-apbh.h>
static struct mxs_dma_chan mxs_dma_channels[MXS_MAX_DMA_CHANNELS];
/*
* Test is the DMA channel is valid channel
*/
int mxs_dma_validate_chan(int channel)
{
struct mxs_dma_chan *pchan;
if ((channel < 0) || (channel >= MXS_MAX_DMA_CHANNELS))
return -EINVAL;
pchan = mxs_dma_channels + channel;
if (!(pchan->flags & MXS_DMA_FLAGS_ALLOCATED))
return -EINVAL;
return 0;
}
/*
* Return the address of the command within a descriptor.
*/
static unsigned int mxs_dma_cmd_address(struct mxs_dma_desc *desc)
{
return desc->address + offsetof(struct mxs_dma_desc, cmd);
}
/*
* Read a DMA channel's hardware semaphore.
*
* As used by the MXS platform's DMA software, the DMA channel's hardware
* semaphore reflects the number of DMA commands the hardware will process, but
* has not yet finished. This is a volatile value read directly from hardware,
* so it must be be viewed as immediately stale.
*
* If the channel is not marked busy, or has finished processing all its
* commands, this value should be zero.
*
* See mxs_dma_append() for details on how DMA command blocks must be configured
* to maintain the expected behavior of the semaphore's value.
*/
static int mxs_dma_read_semaphore(int channel)
{
struct mxs_apbh_regs *apbh_regs =
(struct mxs_apbh_regs *)MXS_APBH_BASE;
uint32_t tmp;
int ret;
ret = mxs_dma_validate_chan(channel);
if (ret)
return ret;
tmp = readl(&apbh_regs->ch[channel].hw_apbh_ch_sema);
tmp &= APBH_CHn_SEMA_PHORE_MASK;
tmp >>= APBH_CHn_SEMA_PHORE_OFFSET;
return tmp;
}
#if !CONFIG_IS_ENABLED(SYS_DCACHE_OFF)
void mxs_dma_flush_desc(struct mxs_dma_desc *desc)
{
uint32_t addr;
uint32_t size;
addr = (uint32_t)desc;
size = roundup(sizeof(struct mxs_dma_desc), MXS_DMA_ALIGNMENT);
flush_dcache_range(addr, addr + size);
}
#else
inline void mxs_dma_flush_desc(struct mxs_dma_desc *desc) {}
#endif
/*
* Enable a DMA channel.
*
* If the given channel has any DMA descriptors on its active list, this
* function causes the DMA hardware to begin processing them.
*
* This function marks the DMA channel as "busy," whether or not there are any
* descriptors to process.
*/
static int mxs_dma_enable(int channel)
{
struct mxs_apbh_regs *apbh_regs =
(struct mxs_apbh_regs *)MXS_APBH_BASE;
unsigned int sem;
struct mxs_dma_chan *pchan;
struct mxs_dma_desc *pdesc;
int ret;
ret = mxs_dma_validate_chan(channel);
if (ret)
return ret;
pchan = mxs_dma_channels + channel;
if (pchan->pending_num == 0) {
pchan->flags |= MXS_DMA_FLAGS_BUSY;
return 0;
}
pdesc = list_first_entry(&pchan->active, struct mxs_dma_desc, node);
if (pdesc == NULL)
return -EFAULT;
if (pchan->flags & MXS_DMA_FLAGS_BUSY) {
if (!(pdesc->cmd.data & MXS_DMA_DESC_CHAIN))
return 0;
sem = mxs_dma_read_semaphore(channel);
if (sem == 0)
return 0;
if (sem == 1) {
pdesc = list_entry(pdesc->node.next,
struct mxs_dma_desc, node);
writel(mxs_dma_cmd_address(pdesc),
&apbh_regs->ch[channel].hw_apbh_ch_nxtcmdar);
}
writel(pchan->pending_num,
&apbh_regs->ch[channel].hw_apbh_ch_sema);
pchan->active_num += pchan->pending_num;
pchan->pending_num = 0;
} else {
pchan->active_num += pchan->pending_num;
pchan->pending_num = 0;
writel(mxs_dma_cmd_address(pdesc),
&apbh_regs->ch[channel].hw_apbh_ch_nxtcmdar);
writel(pchan->active_num,
&apbh_regs->ch[channel].hw_apbh_ch_sema);
writel(1 << (channel + APBH_CTRL0_CLKGATE_CHANNEL_OFFSET),
&apbh_regs->hw_apbh_ctrl0_clr);
}
pchan->flags |= MXS_DMA_FLAGS_BUSY;
return 0;
}
/*
* Disable a DMA channel.
*
* This function shuts down a DMA channel and marks it as "not busy." Any
* descriptors on the active list are immediately moved to the head of the
* "done" list, whether or not they have actually been processed by the
* hardware. The "ready" flags of these descriptors are NOT cleared, so they
* still appear to be active.
*
* This function immediately shuts down a DMA channel's hardware, aborting any
* I/O that may be in progress, potentially leaving I/O hardware in an undefined
* state. It is unwise to call this function if there is ANY chance the hardware
* is still processing a command.
*/
static int mxs_dma_disable(int channel)
{
struct mxs_dma_chan *pchan;
struct mxs_apbh_regs *apbh_regs =
(struct mxs_apbh_regs *)MXS_APBH_BASE;
int ret;
ret = mxs_dma_validate_chan(channel);
if (ret)
return ret;
pchan = mxs_dma_channels + channel;
if (!(pchan->flags & MXS_DMA_FLAGS_BUSY))
return -EINVAL;
writel(1 << (channel + APBH_CTRL0_CLKGATE_CHANNEL_OFFSET),
&apbh_regs->hw_apbh_ctrl0_set);
pchan->flags &= ~MXS_DMA_FLAGS_BUSY;
pchan->active_num = 0;
pchan->pending_num = 0;
list_splice_init(&pchan->active, &pchan->done);
return 0;
}
/*
* Resets the DMA channel hardware.
*/
static int mxs_dma_reset(int channel)
{
struct mxs_apbh_regs *apbh_regs =
(struct mxs_apbh_regs *)MXS_APBH_BASE;
int ret;
#if defined(CONFIG_MX23)
uint32_t setreg = (uint32_t)(&apbh_regs->hw_apbh_ctrl0_set);
uint32_t offset = APBH_CTRL0_RESET_CHANNEL_OFFSET;
#elif (defined(CONFIG_MX28) || defined(CONFIG_MX6) || defined(CONFIG_MX7))
uint32_t setreg = (uint32_t)(&apbh_regs->hw_apbh_channel_ctrl_set);
uint32_t offset = APBH_CHANNEL_CTRL_RESET_CHANNEL_OFFSET;
#endif
ret = mxs_dma_validate_chan(channel);
if (ret)
return ret;
writel(1 << (channel + offset), setreg);
return 0;
}
/*
* Enable or disable DMA interrupt.
*
* This function enables the given DMA channel to interrupt the CPU.
*/
static int mxs_dma_enable_irq(int channel, int enable)
{
struct mxs_apbh_regs *apbh_regs =
(struct mxs_apbh_regs *)MXS_APBH_BASE;
int ret;
ret = mxs_dma_validate_chan(channel);
if (ret)
return ret;
if (enable)
writel(1 << (channel + APBH_CTRL1_CH_CMDCMPLT_IRQ_EN_OFFSET),
&apbh_regs->hw_apbh_ctrl1_set);
else
writel(1 << (channel + APBH_CTRL1_CH_CMDCMPLT_IRQ_EN_OFFSET),
&apbh_regs->hw_apbh_ctrl1_clr);
return 0;
}
/*
* Clear DMA interrupt.
*
* The software that is using the DMA channel must register to receive its
* interrupts and, when they arrive, must call this function to clear them.
*/
static int mxs_dma_ack_irq(int channel)
{
struct mxs_apbh_regs *apbh_regs =
(struct mxs_apbh_regs *)MXS_APBH_BASE;
int ret;
ret = mxs_dma_validate_chan(channel);
if (ret)
return ret;
writel(1 << channel, &apbh_regs->hw_apbh_ctrl1_clr);
writel(1 << channel, &apbh_regs->hw_apbh_ctrl2_clr);
return 0;
}
/*
* Request to reserve a DMA channel
*/
static int mxs_dma_request(int channel)
{
struct mxs_dma_chan *pchan;
if ((channel < 0) || (channel >= MXS_MAX_DMA_CHANNELS))
return -EINVAL;
pchan = mxs_dma_channels + channel;
if ((pchan->flags & MXS_DMA_FLAGS_VALID) != MXS_DMA_FLAGS_VALID)
return -ENODEV;
if (pchan->flags & MXS_DMA_FLAGS_ALLOCATED)
return -EBUSY;
pchan->flags |= MXS_DMA_FLAGS_ALLOCATED;
pchan->active_num = 0;
pchan->pending_num = 0;
INIT_LIST_HEAD(&pchan->active);
INIT_LIST_HEAD(&pchan->done);
return 0;
}
/*
* Release a DMA channel.
*
* This function releases a DMA channel from its current owner.
*
* The channel will NOT be released if it's marked "busy" (see
* mxs_dma_enable()).
*/
int mxs_dma_release(int channel)
{
struct mxs_dma_chan *pchan;
int ret;
ret = mxs_dma_validate_chan(channel);
if (ret)
return ret;
pchan = mxs_dma_channels + channel;
if (pchan->flags & MXS_DMA_FLAGS_BUSY)
return -EBUSY;
pchan->dev = 0;
pchan->active_num = 0;
pchan->pending_num = 0;
pchan->flags &= ~MXS_DMA_FLAGS_ALLOCATED;
return 0;
}
/*
* Allocate DMA descriptor
*/
struct mxs_dma_desc *mxs_dma_desc_alloc(void)
{
struct mxs_dma_desc *pdesc;
uint32_t size;
size = roundup(sizeof(struct mxs_dma_desc), MXS_DMA_ALIGNMENT);
pdesc = memalign(MXS_DMA_ALIGNMENT, size);
if (pdesc == NULL)
return NULL;
memset(pdesc, 0, sizeof(*pdesc));
pdesc->address = (dma_addr_t)pdesc;
return pdesc;
};
/*
* Free DMA descriptor
*/
void mxs_dma_desc_free(struct mxs_dma_desc *pdesc)
{
if (pdesc == NULL)
return;
free(pdesc);
}
/*
* Add a DMA descriptor to a channel.
*
* If the descriptor list for this channel is not empty, this function sets the
* CHAIN bit and the NEXTCMD_ADDR fields in the last descriptor's DMA command so
* it will chain to the new descriptor's command.
*
* Then, this function marks the new descriptor as "ready," adds it to the end
* of the active descriptor list, and increments the count of pending
* descriptors.
*
* The MXS platform DMA software imposes some rules on DMA commands to maintain
* important invariants. These rules are NOT checked, but they must be carefully
* applied by software that uses MXS DMA channels.
*
* Invariant:
* The DMA channel's hardware semaphore must reflect the number of DMA
* commands the hardware will process, but has not yet finished.
*
* Explanation:
* A DMA channel begins processing commands when its hardware semaphore is
* written with a value greater than zero, and it stops processing commands
* when the semaphore returns to zero.
*
* When a channel finishes a DMA command, it will decrement its semaphore if
* the DECREMENT_SEMAPHORE bit is set in that command's flags bits.
*
* In principle, it's not necessary for the DECREMENT_SEMAPHORE to be set,
* unless it suits the purposes of the software. For example, one could
* construct a series of five DMA commands, with the DECREMENT_SEMAPHORE
* bit set only in the last one. Then, setting the DMA channel's hardware
* semaphore to one would cause the entire series of five commands to be
* processed. However, this example would violate the invariant given above.
*
* Rule:
* ALL DMA commands MUST have the DECREMENT_SEMAPHORE bit set so that the DMA
* channel's hardware semaphore will be decremented EVERY time a command is
* processed.
*/
int mxs_dma_desc_append(int channel, struct mxs_dma_desc *pdesc)
{
struct mxs_dma_chan *pchan;
struct mxs_dma_desc *last;
int ret;
ret = mxs_dma_validate_chan(channel);
if (ret)
return ret;
pchan = mxs_dma_channels + channel;
pdesc->cmd.next = mxs_dma_cmd_address(pdesc);
pdesc->flags |= MXS_DMA_DESC_FIRST | MXS_DMA_DESC_LAST;
if (!list_empty(&pchan->active)) {
last = list_entry(pchan->active.prev, struct mxs_dma_desc,
node);
pdesc->flags &= ~MXS_DMA_DESC_FIRST;
last->flags &= ~MXS_DMA_DESC_LAST;
last->cmd.next = mxs_dma_cmd_address(pdesc);
last->cmd.data |= MXS_DMA_DESC_CHAIN;
mxs_dma_flush_desc(last);
}
pdesc->flags |= MXS_DMA_DESC_READY;
if (pdesc->flags & MXS_DMA_DESC_FIRST)
pchan->pending_num++;
list_add_tail(&pdesc->node, &pchan->active);
mxs_dma_flush_desc(pdesc);
return ret;
}
/*
* Clean up processed DMA descriptors.
*
* This function removes processed DMA descriptors from the "active" list. Pass
* in a non-NULL list head to get the descriptors moved to your list. Pass NULL
* to get the descriptors moved to the channel's "done" list. Descriptors on
* the "done" list can be retrieved with mxs_dma_get_finished().
*
* This function marks the DMA channel as "not busy" if no unprocessed
* descriptors remain on the "active" list.
*/
static int mxs_dma_finish(int channel, struct list_head *head)
{
int sem;
struct mxs_dma_chan *pchan;
struct list_head *p, *q;
struct mxs_dma_desc *pdesc;
int ret;
ret = mxs_dma_validate_chan(channel);
if (ret)
return ret;
pchan = mxs_dma_channels + channel;
sem = mxs_dma_read_semaphore(channel);
if (sem < 0)
return sem;
if (sem == pchan->active_num)
return 0;
list_for_each_safe(p, q, &pchan->active) {
if ((pchan->active_num) <= sem)
break;
pdesc = list_entry(p, struct mxs_dma_desc, node);
pdesc->flags &= ~MXS_DMA_DESC_READY;
if (head)
list_move_tail(p, head);
else
list_move_tail(p, &pchan->done);
if (pdesc->flags & MXS_DMA_DESC_LAST)
pchan->active_num--;
}
if (sem == 0)
pchan->flags &= ~MXS_DMA_FLAGS_BUSY;
return 0;
}
/*
* Wait for DMA channel to complete
*/
static int mxs_dma_wait_complete(uint32_t timeout, unsigned int chan)
{
struct mxs_apbh_regs *apbh_regs =
(struct mxs_apbh_regs *)MXS_APBH_BASE;
int ret;
ret = mxs_dma_validate_chan(chan);
if (ret)
return ret;
if (mxs_wait_mask_set(&apbh_regs->hw_apbh_ctrl1_reg,
1 << chan, timeout)) {
ret = -ETIMEDOUT;
mxs_dma_reset(chan);
}
return ret;
}
/*
* Execute the DMA channel
*/
int mxs_dma_go(int chan)
{
uint32_t timeout = 10000000;
int ret;
LIST_HEAD(tmp_desc_list);
mxs_dma_enable_irq(chan, 1);
mxs_dma_enable(chan);
/* Wait for DMA to finish. */
ret = mxs_dma_wait_complete(timeout, chan);
/* Clear out the descriptors we just ran. */
mxs_dma_finish(chan, &tmp_desc_list);
/* Shut the DMA channel down. */
mxs_dma_ack_irq(chan);
mxs_dma_reset(chan);
mxs_dma_enable_irq(chan, 0);
mxs_dma_disable(chan);
return ret;
}
/*
* Execute a continuously running circular DMA descriptor.
* NOTE: This is not intended for general use, but rather
* for the LCD driver in Smart-LCD mode. It allows
* continuous triggering of the RUN bit there.
*/
void mxs_dma_circ_start(int chan, struct mxs_dma_desc *pdesc)
{
struct mxs_apbh_regs *apbh_regs =
(struct mxs_apbh_regs *)MXS_APBH_BASE;
mxs_dma_flush_desc(pdesc);
mxs_dma_enable_irq(chan, 1);
writel(mxs_dma_cmd_address(pdesc),
&apbh_regs->ch[chan].hw_apbh_ch_nxtcmdar);
writel(1, &apbh_regs->ch[chan].hw_apbh_ch_sema);
writel(1 << (chan + APBH_CTRL0_CLKGATE_CHANNEL_OFFSET),
&apbh_regs->hw_apbh_ctrl0_clr);
}
/*
* Initialize the DMA hardware
*/
void mxs_dma_init(void)
{
struct mxs_apbh_regs *apbh_regs =
(struct mxs_apbh_regs *)MXS_APBH_BASE;
mxs_reset_block(&apbh_regs->hw_apbh_ctrl0_reg);
#ifdef CONFIG_APBH_DMA_BURST8
writel(APBH_CTRL0_AHB_BURST8_EN,
&apbh_regs->hw_apbh_ctrl0_set);
#else
writel(APBH_CTRL0_AHB_BURST8_EN,
&apbh_regs->hw_apbh_ctrl0_clr);
#endif
#ifdef CONFIG_APBH_DMA_BURST
writel(APBH_CTRL0_APB_BURST_EN,
&apbh_regs->hw_apbh_ctrl0_set);
#else
writel(APBH_CTRL0_APB_BURST_EN,
&apbh_regs->hw_apbh_ctrl0_clr);
#endif
}
int mxs_dma_init_channel(int channel)
{
struct mxs_dma_chan *pchan;
int ret;
pchan = mxs_dma_channels + channel;
pchan->flags = MXS_DMA_FLAGS_VALID;
ret = mxs_dma_request(channel);
if (ret) {
printf("MXS DMA: Can't acquire DMA channel %i\n",
channel);
return ret;
}
mxs_dma_reset(channel);
mxs_dma_ack_irq(channel);
return 0;
}
@@ -0,0 +1,649 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (C) 2018 Álvaro Fernández Rojas <noltari@gmail.com>
*
* Derived from linux/drivers/dma/bcm63xx-iudma.c:
* Copyright (C) 2015 Simon Arlott <simon@fire.lp0.eu>
*
* Derived from linux/drivers/net/ethernet/broadcom/bcm63xx_enet.c:
* Copyright (C) 2008 Maxime Bizon <mbizon@freebox.fr>
*
* Derived from bcm963xx_4.12L.06B_consumer/shared/opensource/include/bcm963xx/63268_map_part.h:
* Copyright (C) 2000-2010 Broadcom Corporation
*
* Derived from bcm963xx_4.12L.06B_consumer/bcmdrivers/opensource/net/enet/impl4/bcmenet.c:
* Copyright (C) 2010 Broadcom Corporation
*/
#include <common.h>
#include <clk.h>
#include <cpu_func.h>
#include <dm.h>
#include <dma-uclass.h>
#include <memalign.h>
#include <reset.h>
#include <asm/io.h>
#define DMA_RX_DESC 6
#define DMA_TX_DESC 1
/* DMA Channels */
#define DMA_CHAN_FLOWC(x) ((x) >> 1)
#define DMA_CHAN_MAX 16
#define DMA_CHAN_SIZE 0x10
#define DMA_CHAN_TOUT 500
/* DMA Global Configuration register */
#define DMA_CFG_REG 0x00
#define DMA_CFG_ENABLE_SHIFT 0
#define DMA_CFG_ENABLE_MASK (1 << DMA_CFG_ENABLE_SHIFT)
#define DMA_CFG_FLOWC_ENABLE(x) BIT(DMA_CHAN_FLOWC(x) + 1)
#define DMA_CFG_NCHANS_SHIFT 24
#define DMA_CFG_NCHANS_MASK (0xf << DMA_CFG_NCHANS_SHIFT)
/* DMA Global Flow Control registers */
#define DMA_FLOWC_THR_LO_REG(x) (0x04 + DMA_CHAN_FLOWC(x) * 0x0c)
#define DMA_FLOWC_THR_HI_REG(x) (0x08 + DMA_CHAN_FLOWC(x) * 0x0c)
#define DMA_FLOWC_ALLOC_REG(x) (0x0c + DMA_CHAN_FLOWC(x) * 0x0c)
#define DMA_FLOWC_ALLOC_FORCE_SHIFT 31
#define DMA_FLOWC_ALLOC_FORCE_MASK (1 << DMA_FLOWC_ALLOC_FORCE_SHIFT)
/* DMA Global Reset register */
#define DMA_RST_REG 0x34
#define DMA_RST_CHAN_SHIFT 0
#define DMA_RST_CHAN_MASK(x) (1 << x)
/* DMA Channel Configuration register */
#define DMAC_CFG_REG(x) (DMA_CHAN_SIZE * (x) + 0x00)
#define DMAC_CFG_ENABLE_SHIFT 0
#define DMAC_CFG_ENABLE_MASK (1 << DMAC_CFG_ENABLE_SHIFT)
#define DMAC_CFG_PKT_HALT_SHIFT 1
#define DMAC_CFG_PKT_HALT_MASK (1 << DMAC_CFG_PKT_HALT_SHIFT)
#define DMAC_CFG_BRST_HALT_SHIFT 2
#define DMAC_CFG_BRST_HALT_MASK (1 << DMAC_CFG_BRST_HALT_SHIFT)
/* DMA Channel Max Burst Length register */
#define DMAC_BURST_REG(x) (DMA_CHAN_SIZE * (x) + 0x0c)
/* DMA SRAM Descriptor Ring Start register */
#define DMAS_RSTART_REG(x) (DMA_CHAN_SIZE * (x) + 0x00)
/* DMA SRAM State/Bytes done/ring offset register */
#define DMAS_STATE_DATA_REG(x) (DMA_CHAN_SIZE * (x) + 0x04)
/* DMA SRAM Buffer Descriptor status and length register */
#define DMAS_DESC_LEN_STATUS_REG(x) (DMA_CHAN_SIZE * (x) + 0x08)
/* DMA SRAM Buffer Descriptor status and length register */
#define DMAS_DESC_BASE_BUFPTR_REG(x) (DMA_CHAN_SIZE * (x) + 0x0c)
/* DMA Descriptor Status */
#define DMAD_ST_CRC_SHIFT 8
#define DMAD_ST_CRC_MASK (1 << DMAD_ST_CRC_SHIFT)
#define DMAD_ST_WRAP_SHIFT 12
#define DMAD_ST_WRAP_MASK (1 << DMAD_ST_WRAP_SHIFT)
#define DMAD_ST_SOP_SHIFT 13
#define DMAD_ST_SOP_MASK (1 << DMAD_ST_SOP_SHIFT)
#define DMAD_ST_EOP_SHIFT 14
#define DMAD_ST_EOP_MASK (1 << DMAD_ST_EOP_SHIFT)
#define DMAD_ST_OWN_SHIFT 15
#define DMAD_ST_OWN_MASK (1 << DMAD_ST_OWN_SHIFT)
#define DMAD6348_ST_OV_ERR_SHIFT 0
#define DMAD6348_ST_OV_ERR_MASK (1 << DMAD6348_ST_OV_ERR_SHIFT)
#define DMAD6348_ST_CRC_ERR_SHIFT 1
#define DMAD6348_ST_CRC_ERR_MASK (1 << DMAD6348_ST_CRC_ERR_SHIFT)
#define DMAD6348_ST_RX_ERR_SHIFT 2
#define DMAD6348_ST_RX_ERR_MASK (1 << DMAD6348_ST_RX_ERR_SHIFT)
#define DMAD6348_ST_OS_ERR_SHIFT 4
#define DMAD6348_ST_OS_ERR_MASK (1 << DMAD6348_ST_OS_ERR_SHIFT)
#define DMAD6348_ST_UN_ERR_SHIFT 9
#define DMAD6348_ST_UN_ERR_MASK (1 << DMAD6348_ST_UN_ERR_SHIFT)
struct bcm6348_dma_desc {
uint16_t length;
uint16_t status;
uint32_t address;
};
struct bcm6348_chan_priv {
void __iomem *dma_ring;
uint8_t dma_ring_size;
uint8_t desc_id;
uint8_t desc_cnt;
bool *busy_desc;
bool running;
};
struct bcm6348_iudma_hw {
uint16_t err_mask;
};
struct bcm6348_iudma_priv {
const struct bcm6348_iudma_hw *hw;
void __iomem *base;
void __iomem *chan;
void __iomem *sram;
struct bcm6348_chan_priv **ch_priv;
uint8_t n_channels;
};
static inline bool bcm6348_iudma_chan_is_rx(uint8_t ch)
{
return !(ch & 1);
}
static inline void bcm6348_iudma_fdc(void *ptr, ulong size)
{
ulong start = (ulong) ptr;
flush_dcache_range(start, start + size);
}
static inline void bcm6348_iudma_idc(void *ptr, ulong size)
{
ulong start = (ulong) ptr;
invalidate_dcache_range(start, start + size);
}
static void bcm6348_iudma_chan_stop(struct bcm6348_iudma_priv *priv,
uint8_t ch)
{
unsigned int timeout = DMA_CHAN_TOUT;
do {
uint32_t cfg, halt;
if (timeout > DMA_CHAN_TOUT / 2)
halt = DMAC_CFG_PKT_HALT_MASK;
else
halt = DMAC_CFG_BRST_HALT_MASK;
/* try to stop dma channel */
writel_be(halt, priv->chan + DMAC_CFG_REG(ch));
mb();
/* check if channel was stopped */
cfg = readl_be(priv->chan + DMAC_CFG_REG(ch));
if (!(cfg & DMAC_CFG_ENABLE_MASK))
break;
udelay(1);
} while (--timeout);
if (!timeout)
pr_err("unable to stop channel %u\n", ch);
/* reset dma channel */
setbits_be32(priv->base + DMA_RST_REG, DMA_RST_CHAN_MASK(ch));
mb();
clrbits_be32(priv->base + DMA_RST_REG, DMA_RST_CHAN_MASK(ch));
}
static int bcm6348_iudma_disable(struct dma *dma)
{
struct bcm6348_iudma_priv *priv = dev_get_priv(dma->dev);
struct bcm6348_chan_priv *ch_priv = priv->ch_priv[dma->id];
/* stop dma channel */
bcm6348_iudma_chan_stop(priv, dma->id);
/* dma flow control */
if (bcm6348_iudma_chan_is_rx(dma->id))
writel_be(DMA_FLOWC_ALLOC_FORCE_MASK,
DMA_FLOWC_ALLOC_REG(dma->id));
/* init channel config */
ch_priv->running = false;
ch_priv->desc_id = 0;
if (bcm6348_iudma_chan_is_rx(dma->id))
ch_priv->desc_cnt = 0;
else
ch_priv->desc_cnt = ch_priv->dma_ring_size;
return 0;
}
static int bcm6348_iudma_enable(struct dma *dma)
{
const struct bcm6348_iudma_priv *priv = dev_get_priv(dma->dev);
struct bcm6348_chan_priv *ch_priv = priv->ch_priv[dma->id];
struct bcm6348_dma_desc *dma_desc = ch_priv->dma_ring;
uint8_t i;
/* dma ring init */
for (i = 0; i < ch_priv->desc_cnt; i++) {
if (bcm6348_iudma_chan_is_rx(dma->id)) {
ch_priv->busy_desc[i] = false;
dma_desc->status |= DMAD_ST_OWN_MASK;
} else {
dma_desc->status = 0;
dma_desc->length = 0;
dma_desc->address = 0;
}
if (i == ch_priv->desc_cnt - 1)
dma_desc->status |= DMAD_ST_WRAP_MASK;
dma_desc++;
}
/* init to first descriptor */
ch_priv->desc_id = 0;
/* force cache writeback */
bcm6348_iudma_fdc(ch_priv->dma_ring,
sizeof(*dma_desc) * ch_priv->desc_cnt);
/* clear sram */
writel_be(0, priv->sram + DMAS_STATE_DATA_REG(dma->id));
writel_be(0, priv->sram + DMAS_DESC_LEN_STATUS_REG(dma->id));
writel_be(0, priv->sram + DMAS_DESC_BASE_BUFPTR_REG(dma->id));
/* set dma ring start */
writel_be(virt_to_phys(ch_priv->dma_ring),
priv->sram + DMAS_RSTART_REG(dma->id));
/* set flow control */
if (bcm6348_iudma_chan_is_rx(dma->id)) {
u32 val;
setbits_be32(priv->base + DMA_CFG_REG,
DMA_CFG_FLOWC_ENABLE(dma->id));
val = ch_priv->desc_cnt / 3;
writel_be(val, priv->base + DMA_FLOWC_THR_LO_REG(dma->id));
val = (ch_priv->desc_cnt * 2) / 3;
writel_be(val, priv->base + DMA_FLOWC_THR_HI_REG(dma->id));
writel_be(0, priv->base + DMA_FLOWC_ALLOC_REG(dma->id));
}
/* set dma max burst */
writel_be(ch_priv->desc_cnt,
priv->chan + DMAC_BURST_REG(dma->id));
/* kick rx dma channel */
if (bcm6348_iudma_chan_is_rx(dma->id))
setbits_be32(priv->chan + DMAC_CFG_REG(dma->id),
DMAC_CFG_ENABLE_MASK);
/* channel is now enabled */
ch_priv->running = true;
return 0;
}
static int bcm6348_iudma_request(struct dma *dma)
{
const struct bcm6348_iudma_priv *priv = dev_get_priv(dma->dev);
struct bcm6348_chan_priv *ch_priv;
/* check if channel is valid */
if (dma->id >= priv->n_channels)
return -ENODEV;
/* alloc channel private data */
priv->ch_priv[dma->id] = calloc(1, sizeof(struct bcm6348_chan_priv));
if (!priv->ch_priv[dma->id])
return -ENOMEM;
ch_priv = priv->ch_priv[dma->id];
/* alloc dma ring */
if (bcm6348_iudma_chan_is_rx(dma->id))
ch_priv->dma_ring_size = DMA_RX_DESC;
else
ch_priv->dma_ring_size = DMA_TX_DESC;
ch_priv->dma_ring =
malloc_cache_aligned(sizeof(struct bcm6348_dma_desc) *
ch_priv->dma_ring_size);
if (!ch_priv->dma_ring)
return -ENOMEM;
/* init channel config */
ch_priv->running = false;
ch_priv->desc_id = 0;
if (bcm6348_iudma_chan_is_rx(dma->id)) {
ch_priv->desc_cnt = 0;
ch_priv->busy_desc = calloc(ch_priv->desc_cnt, sizeof(bool));
} else {
ch_priv->desc_cnt = ch_priv->dma_ring_size;
ch_priv->busy_desc = NULL;
}
return 0;
}
static int bcm6348_iudma_receive(struct dma *dma, void **dst, void *metadata)
{
const struct bcm6348_iudma_priv *priv = dev_get_priv(dma->dev);
const struct bcm6348_iudma_hw *hw = priv->hw;
struct bcm6348_chan_priv *ch_priv = priv->ch_priv[dma->id];
struct bcm6348_dma_desc *dma_desc = dma_desc = ch_priv->dma_ring;
int ret;
if (!ch_priv->running)
return -EINVAL;
/* get dma ring descriptor address */
dma_desc += ch_priv->desc_id;
/* invalidate cache data */
bcm6348_iudma_idc(dma_desc, sizeof(*dma_desc));
/* check dma own */
if (dma_desc->status & DMAD_ST_OWN_MASK)
return -EAGAIN;
/* check pkt */
if (!(dma_desc->status & DMAD_ST_EOP_MASK) ||
!(dma_desc->status & DMAD_ST_SOP_MASK) ||
(dma_desc->status & hw->err_mask)) {
pr_err("invalid pkt received (ch=%ld desc=%u) (st=%04x)\n",
dma->id, ch_priv->desc_id, dma_desc->status);
ret = -EAGAIN;
} else {
/* set dma buffer address */
*dst = phys_to_virt(dma_desc->address);
/* invalidate cache data */
bcm6348_iudma_idc(*dst, dma_desc->length);
/* return packet length */
ret = dma_desc->length;
}
/* busy dma descriptor */
ch_priv->busy_desc[ch_priv->desc_id] = true;
/* increment dma descriptor */
ch_priv->desc_id = (ch_priv->desc_id + 1) % ch_priv->desc_cnt;
return ret;
}
static int bcm6348_iudma_send(struct dma *dma, void *src, size_t len,
void *metadata)
{
const struct bcm6348_iudma_priv *priv = dev_get_priv(dma->dev);
struct bcm6348_chan_priv *ch_priv = priv->ch_priv[dma->id];
struct bcm6348_dma_desc *dma_desc;
uint16_t status;
if (!ch_priv->running)
return -EINVAL;
/* flush cache */
bcm6348_iudma_fdc(src, len);
/* get dma ring descriptor address */
dma_desc = ch_priv->dma_ring;
dma_desc += ch_priv->desc_id;
/* config dma descriptor */
status = (DMAD_ST_OWN_MASK |
DMAD_ST_EOP_MASK |
DMAD_ST_CRC_MASK |
DMAD_ST_SOP_MASK);
if (ch_priv->desc_id == ch_priv->desc_cnt - 1)
status |= DMAD_ST_WRAP_MASK;
/* set dma descriptor */
dma_desc->address = virt_to_phys(src);
dma_desc->length = len;
dma_desc->status = status;
/* flush cache */
bcm6348_iudma_fdc(dma_desc, sizeof(*dma_desc));
/* kick tx dma channel */
setbits_be32(priv->chan + DMAC_CFG_REG(dma->id), DMAC_CFG_ENABLE_MASK);
/* poll dma status */
do {
/* invalidate cache */
bcm6348_iudma_idc(dma_desc, sizeof(*dma_desc));
if (!(dma_desc->status & DMAD_ST_OWN_MASK))
break;
} while(1);
/* increment dma descriptor */
ch_priv->desc_id = (ch_priv->desc_id + 1) % ch_priv->desc_cnt;
return 0;
}
static int bcm6348_iudma_free_rcv_buf(struct dma *dma, void *dst, size_t size)
{
const struct bcm6348_iudma_priv *priv = dev_get_priv(dma->dev);
struct bcm6348_chan_priv *ch_priv = priv->ch_priv[dma->id];
struct bcm6348_dma_desc *dma_desc = ch_priv->dma_ring;
uint16_t status;
uint8_t i;
u32 cfg;
/* get dirty dma descriptor */
for (i = 0; i < ch_priv->desc_cnt; i++) {
if (phys_to_virt(dma_desc->address) == dst)
break;
dma_desc++;
}
/* dma descriptor not found */
if (i == ch_priv->desc_cnt) {
pr_err("dirty dma descriptor not found\n");
return -ENOENT;
}
/* invalidate cache */
bcm6348_iudma_idc(ch_priv->dma_ring,
sizeof(*dma_desc) * ch_priv->desc_cnt);
/* free dma descriptor */
ch_priv->busy_desc[i] = false;
status = DMAD_ST_OWN_MASK;
if (i == ch_priv->desc_cnt - 1)
status |= DMAD_ST_WRAP_MASK;
dma_desc->status |= status;
dma_desc->length = PKTSIZE_ALIGN;
/* tell dma we allocated one buffer */
writel_be(1, DMA_FLOWC_ALLOC_REG(dma->id));
/* flush cache */
bcm6348_iudma_fdc(ch_priv->dma_ring,
sizeof(*dma_desc) * ch_priv->desc_cnt);
/* kick rx dma channel if disabled */
cfg = readl_be(priv->chan + DMAC_CFG_REG(dma->id));
if (!(cfg & DMAC_CFG_ENABLE_MASK))
setbits_be32(priv->chan + DMAC_CFG_REG(dma->id),
DMAC_CFG_ENABLE_MASK);
return 0;
}
static int bcm6348_iudma_add_rcv_buf(struct dma *dma, void *dst, size_t size)
{
const struct bcm6348_iudma_priv *priv = dev_get_priv(dma->dev);
struct bcm6348_chan_priv *ch_priv = priv->ch_priv[dma->id];
struct bcm6348_dma_desc *dma_desc = ch_priv->dma_ring;
/* no more dma descriptors available */
if (ch_priv->desc_cnt == ch_priv->dma_ring_size) {
pr_err("max number of buffers reached\n");
return -EINVAL;
}
/* get next dma descriptor */
dma_desc += ch_priv->desc_cnt;
/* init dma descriptor */
dma_desc->address = virt_to_phys(dst);
dma_desc->length = size;
dma_desc->status = 0;
/* flush cache */
bcm6348_iudma_fdc(dma_desc, sizeof(*dma_desc));
/* increment dma descriptors */
ch_priv->desc_cnt++;
return 0;
}
static int bcm6348_iudma_prepare_rcv_buf(struct dma *dma, void *dst,
size_t size)
{
const struct bcm6348_iudma_priv *priv = dev_get_priv(dma->dev);
struct bcm6348_chan_priv *ch_priv = priv->ch_priv[dma->id];
/* only add new rx buffers if channel isn't running */
if (ch_priv->running)
return bcm6348_iudma_free_rcv_buf(dma, dst, size);
else
return bcm6348_iudma_add_rcv_buf(dma, dst, size);
}
static const struct dma_ops bcm6348_iudma_ops = {
.disable = bcm6348_iudma_disable,
.enable = bcm6348_iudma_enable,
.prepare_rcv_buf = bcm6348_iudma_prepare_rcv_buf,
.request = bcm6348_iudma_request,
.receive = bcm6348_iudma_receive,
.send = bcm6348_iudma_send,
};
static const struct bcm6348_iudma_hw bcm6348_hw = {
.err_mask = (DMAD6348_ST_OV_ERR_MASK |
DMAD6348_ST_CRC_ERR_MASK |
DMAD6348_ST_RX_ERR_MASK |
DMAD6348_ST_OS_ERR_MASK |
DMAD6348_ST_UN_ERR_MASK),
};
static const struct bcm6348_iudma_hw bcm6368_hw = {
.err_mask = 0,
};
static const struct udevice_id bcm6348_iudma_ids[] = {
{
.compatible = "brcm,bcm6348-iudma",
.data = (ulong)&bcm6348_hw,
}, {
.compatible = "brcm,bcm6368-iudma",
.data = (ulong)&bcm6368_hw,
}, { /* sentinel */ }
};
static int bcm6348_iudma_probe(struct udevice *dev)
{
struct dma_dev_priv *uc_priv = dev_get_uclass_priv(dev);
struct bcm6348_iudma_priv *priv = dev_get_priv(dev);
const struct bcm6348_iudma_hw *hw =
(const struct bcm6348_iudma_hw *)dev_get_driver_data(dev);
uint8_t ch;
int i;
uc_priv->supported = (DMA_SUPPORTS_DEV_TO_MEM |
DMA_SUPPORTS_MEM_TO_DEV);
priv->hw = hw;
/* dma global base address */
priv->base = dev_remap_addr_name(dev, "dma");
if (!priv->base)
return -EINVAL;
/* dma channels base address */
priv->chan = dev_remap_addr_name(dev, "dma-channels");
if (!priv->chan)
return -EINVAL;
/* dma sram base address */
priv->sram = dev_remap_addr_name(dev, "dma-sram");
if (!priv->sram)
return -EINVAL;
/* get number of channels */
priv->n_channels = dev_read_u32_default(dev, "dma-channels", 8);
if (priv->n_channels > DMA_CHAN_MAX)
return -EINVAL;
/* try to enable clocks */
for (i = 0; ; i++) {
struct clk clk;
int ret;
ret = clk_get_by_index(dev, i, &clk);
if (ret < 0)
break;
ret = clk_enable(&clk);
if (ret < 0) {
pr_err("error enabling clock %d\n", i);
return ret;
}
ret = clk_free(&clk);
if (ret < 0) {
pr_err("error freeing clock %d\n", i);
return ret;
}
}
/* try to perform resets */
for (i = 0; ; i++) {
struct reset_ctl reset;
int ret;
ret = reset_get_by_index(dev, i, &reset);
if (ret < 0)
break;
ret = reset_deassert(&reset);
if (ret < 0) {
pr_err("error deasserting reset %d\n", i);
return ret;
}
ret = reset_free(&reset);
if (ret < 0) {
pr_err("error freeing reset %d\n", i);
return ret;
}
}
/* disable dma controller */
clrbits_be32(priv->base + DMA_CFG_REG, DMA_CFG_ENABLE_MASK);
/* alloc channel private data pointers */
priv->ch_priv = calloc(priv->n_channels,
sizeof(struct bcm6348_chan_priv*));
if (!priv->ch_priv)
return -ENOMEM;
/* stop dma channels */
for (ch = 0; ch < priv->n_channels; ch++)
bcm6348_iudma_chan_stop(priv, ch);
/* enable dma controller */
setbits_be32(priv->base + DMA_CFG_REG, DMA_CFG_ENABLE_MASK);
return 0;
}
U_BOOT_DRIVER(bcm6348_iudma) = {
.name = "bcm6348_iudma",
.id = UCLASS_DMA,
.of_match = bcm6348_iudma_ids,
.ops = &bcm6348_iudma_ops,
.priv_auto_alloc_size = sizeof(struct bcm6348_iudma_priv),
.probe = bcm6348_iudma_probe,
};
@@ -0,0 +1,255 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Direct Memory Access U-Class driver
*
* Copyright (C) 2018 Álvaro Fernández Rojas <noltari@gmail.com>
* Copyright (C) 2015 - 2018 Texas Instruments Incorporated <www.ti.com>
* Written by Mugunthan V N <mugunthanvnm@ti.com>
*
* Author: Mugunthan V N <mugunthanvnm@ti.com>
*/
#include <common.h>
#include <cpu_func.h>
#include <dm.h>
#include <dm/read.h>
#include <dma-uclass.h>
#include <dt-structs.h>
#include <errno.h>
#ifdef CONFIG_DMA_CHANNELS
static inline struct dma_ops *dma_dev_ops(struct udevice *dev)
{
return (struct dma_ops *)dev->driver->ops;
}
# if CONFIG_IS_ENABLED(OF_CONTROL)
static int dma_of_xlate_default(struct dma *dma,
struct ofnode_phandle_args *args)
{
debug("%s(dma=%p)\n", __func__, dma);
if (args->args_count > 1) {
pr_err("Invaild args_count: %d\n", args->args_count);
return -EINVAL;
}
if (args->args_count)
dma->id = args->args[0];
else
dma->id = 0;
return 0;
}
int dma_get_by_index(struct udevice *dev, int index, struct dma *dma)
{
int ret;
struct ofnode_phandle_args args;
struct udevice *dev_dma;
const struct dma_ops *ops;
debug("%s(dev=%p, index=%d, dma=%p)\n", __func__, dev, index, dma);
assert(dma);
dma->dev = NULL;
ret = dev_read_phandle_with_args(dev, "dmas", "#dma-cells", 0, index,
&args);
if (ret) {
pr_err("%s: dev_read_phandle_with_args failed: err=%d\n",
__func__, ret);
return ret;
}
ret = uclass_get_device_by_ofnode(UCLASS_DMA, args.node, &dev_dma);
if (ret) {
pr_err("%s: uclass_get_device_by_ofnode failed: err=%d\n",
__func__, ret);
return ret;
}
dma->dev = dev_dma;
ops = dma_dev_ops(dev_dma);
if (ops->of_xlate)
ret = ops->of_xlate(dma, &args);
else
ret = dma_of_xlate_default(dma, &args);
if (ret) {
pr_err("of_xlate() failed: %d\n", ret);
return ret;
}
return dma_request(dev_dma, dma);
}
int dma_get_by_name(struct udevice *dev, const char *name, struct dma *dma)
{
int index;
debug("%s(dev=%p, name=%s, dma=%p)\n", __func__, dev, name, dma);
dma->dev = NULL;
index = dev_read_stringlist_search(dev, "dma-names", name);
if (index < 0) {
pr_err("dev_read_stringlist_search() failed: %d\n", index);
return index;
}
return dma_get_by_index(dev, index, dma);
}
# endif /* OF_CONTROL */
int dma_request(struct udevice *dev, struct dma *dma)
{
struct dma_ops *ops = dma_dev_ops(dev);
debug("%s(dev=%p, dma=%p)\n", __func__, dev, dma);
dma->dev = dev;
if (!ops->request)
return 0;
return ops->request(dma);
}
int dma_free(struct dma *dma)
{
struct dma_ops *ops = dma_dev_ops(dma->dev);
debug("%s(dma=%p)\n", __func__, dma);
if (!ops->free)
return 0;
return ops->free(dma);
}
int dma_enable(struct dma *dma)
{
struct dma_ops *ops = dma_dev_ops(dma->dev);
debug("%s(dma=%p)\n", __func__, dma);
if (!ops->enable)
return -ENOSYS;
return ops->enable(dma);
}
int dma_disable(struct dma *dma)
{
struct dma_ops *ops = dma_dev_ops(dma->dev);
debug("%s(dma=%p)\n", __func__, dma);
if (!ops->disable)
return -ENOSYS;
return ops->disable(dma);
}
int dma_prepare_rcv_buf(struct dma *dma, void *dst, size_t size)
{
struct dma_ops *ops = dma_dev_ops(dma->dev);
debug("%s(dma=%p)\n", __func__, dma);
if (!ops->prepare_rcv_buf)
return -1;
return ops->prepare_rcv_buf(dma, dst, size);
}
int dma_receive(struct dma *dma, void **dst, void *metadata)
{
struct dma_ops *ops = dma_dev_ops(dma->dev);
debug("%s(dma=%p)\n", __func__, dma);
if (!ops->receive)
return -ENOSYS;
return ops->receive(dma, dst, metadata);
}
int dma_send(struct dma *dma, void *src, size_t len, void *metadata)
{
struct dma_ops *ops = dma_dev_ops(dma->dev);
debug("%s(dma=%p)\n", __func__, dma);
if (!ops->send)
return -ENOSYS;
return ops->send(dma, src, len, metadata);
}
int dma_get_cfg(struct dma *dma, u32 cfg_id, void **cfg_data)
{
struct dma_ops *ops = dma_dev_ops(dma->dev);
debug("%s(dma=%p)\n", __func__, dma);
if (!ops->get_cfg)
return -ENOSYS;
return ops->get_cfg(dma, cfg_id, cfg_data);
}
#endif /* CONFIG_DMA_CHANNELS */
int dma_get_device(u32 transfer_type, struct udevice **devp)
{
struct udevice *dev;
int ret;
for (ret = uclass_first_device(UCLASS_DMA, &dev); dev && !ret;
ret = uclass_next_device(&dev)) {
struct dma_dev_priv *uc_priv;
uc_priv = dev_get_uclass_priv(dev);
if (uc_priv->supported & transfer_type)
break;
}
if (!dev) {
pr_err("No DMA device found that supports %x type\n",
transfer_type);
return -EPROTONOSUPPORT;
}
*devp = dev;
return ret;
}
int dma_memcpy(void *dst, void *src, size_t len)
{
struct udevice *dev;
const struct dma_ops *ops;
int ret;
ret = dma_get_device(DMA_SUPPORTS_MEM_TO_MEM, &dev);
if (ret < 0)
return ret;
ops = device_get_ops(dev);
if (!ops->transfer)
return -ENOSYS;
/* Invalidate the area, so no writeback into the RAM races with DMA */
invalidate_dcache_range((unsigned long)dst, (unsigned long)dst +
roundup(len, ARCH_DMA_MINALIGN));
return ops->transfer(dev, DMA_MEM_TO_MEM, dst, src, len);
}
UCLASS_DRIVER(dma) = {
.id = UCLASS_DMA,
.name = "dma",
.flags = DM_UC_FLAG_SEQ_ALIAS,
.per_device_auto_alloc_size = sizeof(struct dma_dev_priv),
};
@@ -0,0 +1,167 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright 2004,2007,2008 Freescale Semiconductor, Inc.
* (C) Copyright 2002, 2003 Motorola Inc.
* Xianghua Xiao (X.Xiao@motorola.com)
*
* (C) Copyright 2000
* Wolfgang Denk, DENX Software Engineering, wd@denx.de.
*/
#include <config.h>
#include <common.h>
#include <asm/io.h>
#include <asm/fsl_dma.h>
/* Controller can only transfer 2^26 - 1 bytes at a time */
#define FSL_DMA_MAX_SIZE (0x3ffffff)
#if defined(CONFIG_MPC83xx)
#define FSL_DMA_MR_DEFAULT (FSL_DMA_MR_CTM_DIRECT | FSL_DMA_MR_DMSEN)
#else
#define FSL_DMA_MR_DEFAULT (FSL_DMA_MR_BWC_DIS | FSL_DMA_MR_CTM_DIRECT)
#endif
#if defined(CONFIG_MPC83xx)
dma83xx_t *dma_base = (void *)(CONFIG_SYS_MPC83xx_DMA_ADDR);
#elif defined(CONFIG_MPC85xx)
ccsr_dma_t *dma_base = (void *)(CONFIG_SYS_MPC85xx_DMA_ADDR);
#elif defined(CONFIG_MPC86xx)
ccsr_dma_t *dma_base = (void *)(CONFIG_SYS_MPC86xx_DMA_ADDR);
#else
#error "Freescale DMA engine not supported on your processor"
#endif
static void dma_sync(void)
{
#if defined(CONFIG_MPC85xx)
asm("sync; isync; msync");
#elif defined(CONFIG_MPC86xx)
asm("sync; isync");
#endif
}
static void out_dma32(volatile unsigned *addr, int val)
{
#if defined(CONFIG_MPC83xx)
out_le32(addr, val);
#else
out_be32(addr, val);
#endif
}
static uint in_dma32(volatile unsigned *addr)
{
#if defined(CONFIG_MPC83xx)
return in_le32(addr);
#else
return in_be32(addr);
#endif
}
static uint dma_check(void) {
volatile fsl_dma_t *dma = &dma_base->dma[0];
uint status;
/* While the channel is busy, spin */
do {
status = in_dma32(&dma->sr);
} while (status & FSL_DMA_SR_CB);
/* clear MR[CS] channel start bit */
out_dma32(&dma->mr, in_dma32(&dma->mr) & ~FSL_DMA_MR_CS);
dma_sync();
if (status != 0)
printf ("DMA Error: status = %x\n", status);
return status;
}
#if !defined(CONFIG_MPC83xx)
void dma_init(void) {
volatile fsl_dma_t *dma = &dma_base->dma[0];
out_dma32(&dma->satr, FSL_DMA_SATR_SREAD_SNOOP);
out_dma32(&dma->datr, FSL_DMA_DATR_DWRITE_SNOOP);
out_dma32(&dma->sr, 0xffffffff); /* clear any errors */
dma_sync();
}
#endif
int dmacpy(phys_addr_t dest, phys_addr_t src, phys_size_t count) {
volatile fsl_dma_t *dma = &dma_base->dma[0];
uint xfer_size;
while (count) {
xfer_size = min(FSL_DMA_MAX_SIZE, count);
out_dma32(&dma->dar, (u32) (dest & 0xFFFFFFFF));
out_dma32(&dma->sar, (u32) (src & 0xFFFFFFFF));
#if !defined(CONFIG_MPC83xx)
out_dma32(&dma->satr,
in_dma32(&dma->satr) | (u32)((u64)src >> 32));
out_dma32(&dma->datr,
in_dma32(&dma->datr) | (u32)((u64)dest >> 32));
#endif
out_dma32(&dma->bcr, xfer_size);
dma_sync();
/* Prepare mode register */
out_dma32(&dma->mr, FSL_DMA_MR_DEFAULT);
dma_sync();
/* Start the transfer */
out_dma32(&dma->mr, FSL_DMA_MR_DEFAULT | FSL_DMA_MR_CS);
count -= xfer_size;
src += xfer_size;
dest += xfer_size;
dma_sync();
if (dma_check())
return -1;
}
return 0;
}
/*
* 85xx/86xx use dma to initialize SDRAM when !CONFIG_ECC_INIT_VIA_DDRCONTROLLER
* while 83xx uses dma to initialize SDRAM when CONFIG_DDR_ECC_INIT_VIA_DMA
*/
#if ((!defined CONFIG_MPC83xx && defined(CONFIG_DDR_ECC) && \
!defined(CONFIG_ECC_INIT_VIA_DDRCONTROLLER)) || \
(defined(CONFIG_MPC83xx) && defined(CONFIG_DDR_ECC_INIT_VIA_DMA)))
void dma_meminit(uint val, uint size)
{
uint *p = 0;
uint i = 0;
for (*p = 0; p < (uint *)(8 * 1024); p++) {
if (((uint)p & 0x1f) == 0)
ppcDcbz((ulong)p);
*p = (uint)CONFIG_MEM_INIT_VALUE;
if (((uint)p & 0x1c) == 0x1c)
ppcDcbf((ulong)p);
}
dmacpy(0x002000, 0, 0x002000); /* 8K */
dmacpy(0x004000, 0, 0x004000); /* 16K */
dmacpy(0x008000, 0, 0x008000); /* 32K */
dmacpy(0x010000, 0, 0x010000); /* 64K */
dmacpy(0x020000, 0, 0x020000); /* 128K */
dmacpy(0x040000, 0, 0x040000); /* 256K */
dmacpy(0x080000, 0, 0x080000); /* 512K */
dmacpy(0x100000, 0, 0x100000); /* 1M */
dmacpy(0x200000, 0, 0x200000); /* 2M */
dmacpy(0x400000, 0, 0x400000); /* 4M */
for (i = 1; i < size / 0x800000; i++)
dmacpy((0x800000 * i), 0, 0x800000);
}
#endif
@@ -0,0 +1,319 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Multicore Navigator driver for TI Keystone 2 devices.
*
* (C) Copyright 2012-2014
* Texas Instruments Incorporated, <www.ti.com>
*/
#include <common.h>
#include <asm/io.h>
#include <asm/ti-common/keystone_nav.h>
struct qm_config qm_memmap = {
.stat_cfg = CONFIG_KSNAV_QM_QUEUE_STATUS_BASE,
.queue = (void *)CONFIG_KSNAV_QM_MANAGER_QUEUES_BASE,
.mngr_vbusm = CONFIG_KSNAV_QM_BASE_ADDRESS,
.i_lram = CONFIG_KSNAV_QM_LINK_RAM_BASE,
.proxy = (void *)CONFIG_KSNAV_QM_MANAGER_Q_PROXY_BASE,
.status_ram = CONFIG_KSNAV_QM_STATUS_RAM_BASE,
.mngr_cfg = (void *)CONFIG_KSNAV_QM_CONF_BASE,
.intd_cfg = CONFIG_KSNAV_QM_INTD_CONF_BASE,
.desc_mem = (void *)CONFIG_KSNAV_QM_DESC_SETUP_BASE,
.region_num = CONFIG_KSNAV_QM_REGION_NUM,
.pdsp_cmd = CONFIG_KSNAV_QM_PDSP1_CMD_BASE,
.pdsp_ctl = CONFIG_KSNAV_QM_PDSP1_CTRL_BASE,
.pdsp_iram = CONFIG_KSNAV_QM_PDSP1_IRAM_BASE,
.qpool_num = CONFIG_KSNAV_QM_QPOOL_NUM,
};
/*
* We are going to use only one type of descriptors - host packet
* descriptors. We staticaly allocate memory for them here
*/
struct qm_host_desc desc_pool[HDESC_NUM] __aligned(sizeof(struct qm_host_desc));
static struct qm_config *qm_cfg;
inline int num_of_desc_to_reg(int num_descr)
{
int j, num;
for (j = 0, num = 32; j < 15; j++, num *= 2) {
if (num_descr <= num)
return j;
}
return 15;
}
int _qm_init(struct qm_config *cfg)
{
u32 j;
qm_cfg = cfg;
qm_cfg->mngr_cfg->link_ram_base0 = qm_cfg->i_lram;
qm_cfg->mngr_cfg->link_ram_size0 = HDESC_NUM * 8 - 1;
qm_cfg->mngr_cfg->link_ram_base1 = 0;
qm_cfg->mngr_cfg->link_ram_size1 = 0;
qm_cfg->mngr_cfg->link_ram_base2 = 0;
qm_cfg->desc_mem[0].base_addr = (u32)desc_pool;
qm_cfg->desc_mem[0].start_idx = 0;
qm_cfg->desc_mem[0].desc_reg_size =
(((sizeof(struct qm_host_desc) >> 4) - 1) << 16) |
num_of_desc_to_reg(HDESC_NUM);
memset(desc_pool, 0, sizeof(desc_pool));
for (j = 0; j < HDESC_NUM; j++)
qm_push(&desc_pool[j], qm_cfg->qpool_num);
return QM_OK;
}
int qm_init(void)
{
return _qm_init(&qm_memmap);
}
void qm_close(void)
{
u32 j;
queue_close(qm_cfg->qpool_num);
qm_cfg->mngr_cfg->link_ram_base0 = 0;
qm_cfg->mngr_cfg->link_ram_size0 = 0;
qm_cfg->mngr_cfg->link_ram_base1 = 0;
qm_cfg->mngr_cfg->link_ram_size1 = 0;
qm_cfg->mngr_cfg->link_ram_base2 = 0;
for (j = 0; j < qm_cfg->region_num; j++) {
qm_cfg->desc_mem[j].base_addr = 0;
qm_cfg->desc_mem[j].start_idx = 0;
qm_cfg->desc_mem[j].desc_reg_size = 0;
}
qm_cfg = NULL;
}
void qm_push(struct qm_host_desc *hd, u32 qnum)
{
u32 regd;
cpu_to_bus((u32 *)hd, sizeof(struct qm_host_desc)/4);
regd = (u32)hd | ((sizeof(struct qm_host_desc) >> 4) - 1);
writel(regd, &qm_cfg->queue[qnum].ptr_size_thresh);
}
void qm_buff_push(struct qm_host_desc *hd, u32 qnum,
void *buff_ptr, u32 buff_len)
{
hd->orig_buff_len = buff_len;
hd->buff_len = buff_len;
hd->orig_buff_ptr = (u32)buff_ptr;
hd->buff_ptr = (u32)buff_ptr;
qm_push(hd, qnum);
}
struct qm_host_desc *qm_pop(u32 qnum)
{
u32 uhd;
uhd = readl(&qm_cfg->queue[qnum].ptr_size_thresh) & ~0xf;
if (uhd)
cpu_to_bus((u32 *)uhd, sizeof(struct qm_host_desc)/4);
return (struct qm_host_desc *)uhd;
}
struct qm_host_desc *qm_pop_from_free_pool(void)
{
return qm_pop(qm_cfg->qpool_num);
}
void queue_close(u32 qnum)
{
struct qm_host_desc *hd;
while ((hd = qm_pop(qnum)))
;
}
/**
* DMA API
*/
static int ksnav_rx_disable(struct pktdma_cfg *pktdma)
{
u32 j, v, k;
for (j = 0; j < pktdma->rx_ch_num; j++) {
v = readl(&pktdma->rx_ch[j].cfg_a);
if (!(v & CPDMA_CHAN_A_ENABLE))
continue;
writel(v | CPDMA_CHAN_A_TDOWN, &pktdma->rx_ch[j].cfg_a);
for (k = 0; k < TDOWN_TIMEOUT_COUNT; k++) {
udelay(100);
v = readl(&pktdma->rx_ch[j].cfg_a);
if (!(v & CPDMA_CHAN_A_ENABLE))
continue;
}
/* TODO: teardown error on if TDOWN_TIMEOUT_COUNT is reached */
}
/* Clear all of the flow registers */
for (j = 0; j < pktdma->rx_flow_num; j++) {
writel(0, &pktdma->rx_flows[j].control);
writel(0, &pktdma->rx_flows[j].tags);
writel(0, &pktdma->rx_flows[j].tag_sel);
writel(0, &pktdma->rx_flows[j].fdq_sel[0]);
writel(0, &pktdma->rx_flows[j].fdq_sel[1]);
writel(0, &pktdma->rx_flows[j].thresh[0]);
writel(0, &pktdma->rx_flows[j].thresh[1]);
writel(0, &pktdma->rx_flows[j].thresh[2]);
}
return QM_OK;
}
static int ksnav_tx_disable(struct pktdma_cfg *pktdma)
{
u32 j, v, k;
for (j = 0; j < pktdma->tx_ch_num; j++) {
v = readl(&pktdma->tx_ch[j].cfg_a);
if (!(v & CPDMA_CHAN_A_ENABLE))
continue;
writel(v | CPDMA_CHAN_A_TDOWN, &pktdma->tx_ch[j].cfg_a);
for (k = 0; k < TDOWN_TIMEOUT_COUNT; k++) {
udelay(100);
v = readl(&pktdma->tx_ch[j].cfg_a);
if (!(v & CPDMA_CHAN_A_ENABLE))
continue;
}
/* TODO: teardown error on if TDOWN_TIMEOUT_COUNT is reached */
}
return QM_OK;
}
int ksnav_init(struct pktdma_cfg *pktdma, struct rx_buff_desc *rx_buffers)
{
u32 j, v;
struct qm_host_desc *hd;
u8 *rx_ptr;
if (pktdma == NULL || rx_buffers == NULL ||
rx_buffers->buff_ptr == NULL || qm_cfg == NULL)
return QM_ERR;
pktdma->rx_flow = rx_buffers->rx_flow;
/* init rx queue */
rx_ptr = rx_buffers->buff_ptr;
for (j = 0; j < rx_buffers->num_buffs; j++) {
hd = qm_pop(qm_cfg->qpool_num);
if (hd == NULL)
return QM_ERR;
qm_buff_push(hd, pktdma->rx_free_q,
rx_ptr, rx_buffers->buff_len);
rx_ptr += rx_buffers->buff_len;
}
ksnav_rx_disable(pktdma);
/* configure rx channels */
v = CPDMA_REG_VAL_MAKE_RX_FLOW_A(1, 1, 0, 0, 0, 0, 0, pktdma->rx_rcv_q);
writel(v, &pktdma->rx_flows[pktdma->rx_flow].control);
writel(0, &pktdma->rx_flows[pktdma->rx_flow].tags);
writel(0, &pktdma->rx_flows[pktdma->rx_flow].tag_sel);
v = CPDMA_REG_VAL_MAKE_RX_FLOW_D(0, pktdma->rx_free_q, 0,
pktdma->rx_free_q);
writel(v, &pktdma->rx_flows[pktdma->rx_flow].fdq_sel[0]);
writel(v, &pktdma->rx_flows[pktdma->rx_flow].fdq_sel[1]);
writel(0, &pktdma->rx_flows[pktdma->rx_flow].thresh[0]);
writel(0, &pktdma->rx_flows[pktdma->rx_flow].thresh[1]);
writel(0, &pktdma->rx_flows[pktdma->rx_flow].thresh[2]);
for (j = 0; j < pktdma->rx_ch_num; j++)
writel(CPDMA_CHAN_A_ENABLE, &pktdma->rx_ch[j].cfg_a);
/* configure tx channels */
/* Disable loopback in the tx direction */
writel(0, &pktdma->global->emulation_control);
/* Set QM base address, only for K2x devices */
writel(CONFIG_KSNAV_QM_BASE_ADDRESS, &pktdma->global->qm_base_addr[0]);
/* Enable all channels. The current state isn't important */
for (j = 0; j < pktdma->tx_ch_num; j++) {
writel(0, &pktdma->tx_ch[j].cfg_b);
writel(CPDMA_CHAN_A_ENABLE, &pktdma->tx_ch[j].cfg_a);
}
return QM_OK;
}
int ksnav_close(struct pktdma_cfg *pktdma)
{
if (!pktdma)
return QM_ERR;
ksnav_tx_disable(pktdma);
ksnav_rx_disable(pktdma);
queue_close(pktdma->rx_free_q);
queue_close(pktdma->rx_rcv_q);
queue_close(pktdma->tx_snd_q);
return QM_OK;
}
int ksnav_send(struct pktdma_cfg *pktdma, u32 *pkt, int num_bytes, u32 swinfo2)
{
struct qm_host_desc *hd;
hd = qm_pop(qm_cfg->qpool_num);
if (hd == NULL)
return QM_ERR;
hd->desc_info = num_bytes;
hd->swinfo[2] = swinfo2;
hd->packet_info = qm_cfg->qpool_num;
qm_buff_push(hd, pktdma->tx_snd_q, pkt, num_bytes);
return QM_OK;
}
void *ksnav_recv(struct pktdma_cfg *pktdma, u32 **pkt, int *num_bytes)
{
struct qm_host_desc *hd;
hd = qm_pop(pktdma->rx_rcv_q);
if (!hd)
return NULL;
*pkt = (u32 *)hd->buff_ptr;
*num_bytes = hd->desc_info & 0x3fffff;
return hd;
}
void ksnav_release_rxhd(struct pktdma_cfg *pktdma, void *hd)
{
struct qm_host_desc *_hd = (struct qm_host_desc *)hd;
_hd->buff_len = _hd->orig_buff_len;
_hd->buff_ptr = _hd->orig_buff_ptr;
qm_push(_hd, pktdma->rx_free_q);
}
@@ -0,0 +1,26 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Multicore Navigator driver for TI Keystone 2 devices.
*
* (C) Copyright 2012-2014
* Texas Instruments Incorporated, <www.ti.com>
*/
#include <asm/ti-common/keystone_nav.h>
#ifdef CONFIG_KSNAV_PKTDMA_NETCP
/* NETCP Pktdma */
struct pktdma_cfg netcp_pktdma = {
.global = (void *)CONFIG_KSNAV_NETCP_PDMA_CTRL_BASE,
.tx_ch = (void *)CONFIG_KSNAV_NETCP_PDMA_TX_BASE,
.tx_ch_num = CONFIG_KSNAV_NETCP_PDMA_TX_CH_NUM,
.rx_ch = (void *)CONFIG_KSNAV_NETCP_PDMA_RX_BASE,
.rx_ch_num = CONFIG_KSNAV_NETCP_PDMA_RX_CH_NUM,
.tx_sched = (u32 *)CONFIG_KSNAV_NETCP_PDMA_SCHED_BASE,
.rx_flows = (void *)CONFIG_KSNAV_NETCP_PDMA_RX_FLOW_BASE,
.rx_flow_num = CONFIG_KSNAV_NETCP_PDMA_RX_FLOW_NUM,
.rx_free_q = CONFIG_KSNAV_NETCP_PDMA_RX_FREE_QUEUE,
.rx_rcv_q = CONFIG_KSNAV_NETCP_PDMA_RX_RCV_QUEUE,
.tx_snd_q = CONFIG_KSNAV_NETCP_PDMA_TX_SND_QUEUE,
};
#endif
@@ -0,0 +1,146 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (C) 2008 by NXP Semiconductors
* @Author: Kevin Wells
* @Descr: LPC3250 DMA controller interface support functions
*
* Copyright (c) 2015 Tyco Fire Protection Products.
*/
#include <common.h>
#include <errno.h>
#include <asm/arch/dma.h>
#include <asm/arch/cpu.h>
#include <asm/arch/clk.h>
#include <asm/arch/sys_proto.h>
#include <asm/io.h>
/* DMA controller channel register structure */
struct dmac_chan_reg {
u32 src_addr;
u32 dest_addr;
u32 lli;
u32 control;
u32 config_ch;
u32 reserved[3];
};
/* DMA controller register structures */
struct dma_reg {
u32 int_stat;
u32 int_tc_stat;
u32 int_tc_clear;
u32 int_err_stat;
u32 int_err_clear;
u32 raw_tc_stat;
u32 raw_err_stat;
u32 chan_enable;
u32 sw_burst_req;
u32 sw_single_req;
u32 sw_last_burst_req;
u32 sw_last_single_req;
u32 config;
u32 sync;
u32 reserved[50];
struct dmac_chan_reg dma_chan[8];
};
#define DMA_NO_OF_CHANNELS 8
/* config register definitions */
#define DMAC_CTRL_ENABLE (1 << 0) /* For enabling the DMA controller */
static u32 alloc_ch;
static struct dma_reg *dma = (struct dma_reg *)DMA_BASE;
int lpc32xx_dma_get_channel(void)
{
int i;
if (!alloc_ch) { /* First time caller */
/*
* DMA clock are enable by "lpc32xx_dma_init()" and should
* be call by board "board_early_init_f()" function.
*/
/*
* Make sure DMA controller and all channels are disabled.
* Controller is in little-endian mode. Disable sync signals.
*/
writel(0, &dma->config);
writel(0, &dma->sync);
/* Clear interrupt and error statuses */
writel(0xFF, &dma->int_tc_clear);
writel(0xFF, &dma->raw_tc_stat);
writel(0xFF, &dma->int_err_clear);
writel(0xFF, &dma->raw_err_stat);
/* Enable DMA controller */
writel(DMAC_CTRL_ENABLE, &dma->config);
}
i = ffz(alloc_ch);
/* Check if all the available channels are busy */
if (unlikely(i == DMA_NO_OF_CHANNELS))
return -1;
alloc_ch |= BIT_MASK(i);
return i;
}
int lpc32xx_dma_start_xfer(unsigned int channel,
const struct lpc32xx_dmac_ll *desc, u32 config)
{
if (unlikely(((BIT_MASK(channel) & alloc_ch) == 0) ||
(channel >= DMA_NO_OF_CHANNELS))) {
pr_err("Request for xfer on unallocated channel %d", channel);
return -1;
}
writel(BIT_MASK(channel), &dma->int_tc_clear);
writel(BIT_MASK(channel), &dma->int_err_clear);
writel(desc->dma_src, &dma->dma_chan[channel].src_addr);
writel(desc->dma_dest, &dma->dma_chan[channel].dest_addr);
writel(desc->next_lli, &dma->dma_chan[channel].lli);
writel(desc->next_ctrl, &dma->dma_chan[channel].control);
writel(config, &dma->dma_chan[channel].config_ch);
return 0;
}
int lpc32xx_dma_wait_status(unsigned int channel)
{
unsigned long start;
u32 reg;
/* Check if given channel is valid */
if (unlikely(channel >= DMA_NO_OF_CHANNELS)) {
pr_err("Request for status on unallocated channel %d", channel);
return -1;
}
start = get_timer(0);
while (1) {
reg = readl(&dma->raw_tc_stat);
reg |= readl(dma->raw_err_stat);
if (reg & BIT_MASK(channel))
break;
if (get_timer(start) > CONFIG_SYS_HZ) {
pr_err("DMA status timeout channel %d\n", channel);
return -ETIMEDOUT;
}
udelay(1);
}
if (unlikely(readl(&dma->raw_err_stat) & BIT_MASK(channel))) {
setbits_le32(&dma->int_err_clear, BIT_MASK(channel));
setbits_le32(&dma->raw_err_stat, BIT_MASK(channel));
pr_err("DMA error on channel %d\n", channel);
return -1;
}
setbits_le32(&dma->int_tc_clear, BIT_MASK(channel));
setbits_le32(&dma->raw_tc_stat, BIT_MASK(channel));
return 0;
}
@@ -0,0 +1,282 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Direct Memory Access U-Class Simulation driver
*
* Copyright (C) 2018 Texas Instruments Incorporated <www.ti.com>
*
* Author: Grygorii Strashko <grygorii.strashko@ti.com>
*/
#include <common.h>
#include <dm.h>
#include <dm/read.h>
#include <dma-uclass.h>
#include <dt-structs.h>
#include <errno.h>
#define SANDBOX_DMA_CH_CNT 3
#define SANDBOX_DMA_BUF_SIZE 1024
struct sandbox_dma_chan {
struct sandbox_dma_dev *ud;
char name[20];
u32 id;
enum dma_direction dir;
bool in_use;
bool enabled;
};
struct sandbox_dma_dev {
struct device *dev;
u32 ch_count;
struct sandbox_dma_chan channels[SANDBOX_DMA_CH_CNT];
uchar buf[SANDBOX_DMA_BUF_SIZE];
uchar *buf_rx;
size_t data_len;
u32 meta;
};
static int sandbox_dma_transfer(struct udevice *dev, int direction,
void *dst, void *src, size_t len)
{
memcpy(dst, src, len);
return 0;
}
static int sandbox_dma_of_xlate(struct dma *dma,
struct ofnode_phandle_args *args)
{
struct sandbox_dma_dev *ud = dev_get_priv(dma->dev);
struct sandbox_dma_chan *uc;
debug("%s(dma id=%u)\n", __func__, args->args[0]);
if (args->args[0] >= SANDBOX_DMA_CH_CNT)
return -EINVAL;
dma->id = args->args[0];
uc = &ud->channels[dma->id];
if (dma->id == 1)
uc->dir = DMA_MEM_TO_DEV;
else if (dma->id == 2)
uc->dir = DMA_DEV_TO_MEM;
else
uc->dir = DMA_MEM_TO_MEM;
debug("%s(dma id=%lu dir=%d)\n", __func__, dma->id, uc->dir);
return 0;
}
static int sandbox_dma_request(struct dma *dma)
{
struct sandbox_dma_dev *ud = dev_get_priv(dma->dev);
struct sandbox_dma_chan *uc;
if (dma->id >= SANDBOX_DMA_CH_CNT)
return -EINVAL;
uc = &ud->channels[dma->id];
if (uc->in_use)
return -EBUSY;
uc->in_use = true;
debug("%s(dma id=%lu in_use=%d)\n", __func__, dma->id, uc->in_use);
return 0;
}
static int sandbox_dma_free(struct dma *dma)
{
struct sandbox_dma_dev *ud = dev_get_priv(dma->dev);
struct sandbox_dma_chan *uc;
if (dma->id >= SANDBOX_DMA_CH_CNT)
return -EINVAL;
uc = &ud->channels[dma->id];
if (!uc->in_use)
return -EINVAL;
uc->in_use = false;
ud->buf_rx = NULL;
ud->data_len = 0;
debug("%s(dma id=%lu in_use=%d)\n", __func__, dma->id, uc->in_use);
return 0;
}
static int sandbox_dma_enable(struct dma *dma)
{
struct sandbox_dma_dev *ud = dev_get_priv(dma->dev);
struct sandbox_dma_chan *uc;
if (dma->id >= SANDBOX_DMA_CH_CNT)
return -EINVAL;
uc = &ud->channels[dma->id];
if (!uc->in_use)
return -EINVAL;
if (uc->enabled)
return -EINVAL;
uc->enabled = true;
debug("%s(dma id=%lu enabled=%d)\n", __func__, dma->id, uc->enabled);
return 0;
}
static int sandbox_dma_disable(struct dma *dma)
{
struct sandbox_dma_dev *ud = dev_get_priv(dma->dev);
struct sandbox_dma_chan *uc;
if (dma->id >= SANDBOX_DMA_CH_CNT)
return -EINVAL;
uc = &ud->channels[dma->id];
if (!uc->in_use)
return -EINVAL;
if (!uc->enabled)
return -EINVAL;
uc->enabled = false;
debug("%s(dma id=%lu enabled=%d)\n", __func__, dma->id, uc->enabled);
return 0;
}
static int sandbox_dma_send(struct dma *dma,
void *src, size_t len, void *metadata)
{
struct sandbox_dma_dev *ud = dev_get_priv(dma->dev);
struct sandbox_dma_chan *uc;
if (dma->id >= SANDBOX_DMA_CH_CNT)
return -EINVAL;
if (!src || !metadata)
return -EINVAL;
debug("%s(dma id=%lu)\n", __func__, dma->id);
uc = &ud->channels[dma->id];
if (uc->dir != DMA_MEM_TO_DEV)
return -EINVAL;
if (!uc->in_use)
return -EINVAL;
if (!uc->enabled)
return -EINVAL;
if (len >= SANDBOX_DMA_BUF_SIZE)
return -EINVAL;
memcpy(ud->buf, src, len);
ud->data_len = len;
ud->meta = *((u32 *)metadata);
debug("%s(dma id=%lu len=%zu meta=%08x)\n",
__func__, dma->id, len, ud->meta);
return 0;
}
static int sandbox_dma_receive(struct dma *dma, void **dst, void *metadata)
{
struct sandbox_dma_dev *ud = dev_get_priv(dma->dev);
struct sandbox_dma_chan *uc;
if (dma->id >= SANDBOX_DMA_CH_CNT)
return -EINVAL;
if (!dst || !metadata)
return -EINVAL;
uc = &ud->channels[dma->id];
if (uc->dir != DMA_DEV_TO_MEM)
return -EINVAL;
if (!uc->in_use)
return -EINVAL;
if (!uc->enabled)
return -EINVAL;
if (!ud->data_len)
return 0;
if (ud->buf_rx) {
memcpy(ud->buf_rx, ud->buf, ud->data_len);
*dst = ud->buf_rx;
} else {
memcpy(*dst, ud->buf, ud->data_len);
}
*((u32 *)metadata) = ud->meta;
debug("%s(dma id=%lu len=%zu meta=%08x %p)\n",
__func__, dma->id, ud->data_len, ud->meta, *dst);
return ud->data_len;
}
static int sandbox_dma_prepare_rcv_buf(struct dma *dma, void *dst, size_t size)
{
struct sandbox_dma_dev *ud = dev_get_priv(dma->dev);
ud->buf_rx = dst;
return 0;
}
static const struct dma_ops sandbox_dma_ops = {
.transfer = sandbox_dma_transfer,
.of_xlate = sandbox_dma_of_xlate,
.request = sandbox_dma_request,
.free = sandbox_dma_free,
.enable = sandbox_dma_enable,
.disable = sandbox_dma_disable,
.send = sandbox_dma_send,
.receive = sandbox_dma_receive,
.prepare_rcv_buf = sandbox_dma_prepare_rcv_buf,
};
static int sandbox_dma_probe(struct udevice *dev)
{
struct dma_dev_priv *uc_priv = dev_get_uclass_priv(dev);
struct sandbox_dma_dev *ud = dev_get_priv(dev);
int i, ret = 0;
uc_priv->supported = DMA_SUPPORTS_MEM_TO_MEM |
DMA_SUPPORTS_MEM_TO_DEV |
DMA_SUPPORTS_DEV_TO_MEM;
ud->ch_count = SANDBOX_DMA_CH_CNT;
ud->buf_rx = NULL;
ud->meta = 0;
ud->data_len = 0;
pr_err("Number of channels: %u\n", ud->ch_count);
for (i = 0; i < ud->ch_count; i++) {
struct sandbox_dma_chan *uc = &ud->channels[i];
uc->ud = ud;
uc->id = i;
sprintf(uc->name, "DMA chan%d\n", i);
uc->in_use = false;
uc->enabled = false;
}
return ret;
}
static const struct udevice_id sandbox_dma_ids[] = {
{ .compatible = "sandbox,dma" },
{ }
};
U_BOOT_DRIVER(sandbox_dma) = {
.name = "sandbox-dma",
.id = UCLASS_DMA,
.of_match = sandbox_dma_ids,
.ops = &sandbox_dma_ops,
.probe = sandbox_dma_probe,
.priv_auto_alloc_size = sizeof(struct sandbox_dma_dev),
};
@@ -0,0 +1,580 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Enhanced Direct Memory Access (EDMA3) Controller
*
* (C) Copyright 2014
* Texas Instruments Incorporated, <www.ti.com>
*
* Author: Ivan Khoronzhuk <ivan.khoronzhuk@ti.com>
*/
#include <asm/io.h>
#include <common.h>
#include <dm.h>
#include <dma-uclass.h>
#include <asm/omap_common.h>
#include <asm/ti-common/ti-edma3.h>
#define EDMA3_SL_BASE(slot) (0x4000 + ((slot) << 5))
#define EDMA3_SL_MAX_NUM 512
#define EDMA3_SLOPT_FIFO_WIDTH_MASK (0x7 << 8)
#define EDMA3_QCHMAP(ch) 0x0200 + ((ch) << 2)
#define EDMA3_CHMAP_PARSET_MASK 0x1ff
#define EDMA3_CHMAP_PARSET_SHIFT 0x5
#define EDMA3_CHMAP_TRIGWORD_SHIFT 0x2
#define EDMA3_QEMCR 0x314
#define EDMA3_IPR 0x1068
#define EDMA3_IPRH 0x106c
#define EDMA3_ICR 0x1070
#define EDMA3_ICRH 0x1074
#define EDMA3_QEECR 0x1088
#define EDMA3_QEESR 0x108c
#define EDMA3_QSECR 0x1094
#define EDMA_FILL_BUFFER_SIZE 512
struct ti_edma3_priv {
u32 base;
};
static u8 edma_fill_buffer[EDMA_FILL_BUFFER_SIZE] __aligned(ARCH_DMA_MINALIGN);
/**
* qedma3_start - start qdma on a channel
* @base: base address of edma
* @cfg: pinter to struct edma3_channel_config where you can set
* the slot number to associate with, the chnum, which corresponds
* your quick channel number 0-7, complete code - transfer complete code
* and trigger slot word - which has to correspond to the word number in
* edma3_slot_layout struct for generating event.
*
*/
void qedma3_start(u32 base, struct edma3_channel_config *cfg)
{
u32 qchmap;
/* Clear the pending int bit */
if (cfg->complete_code < 32)
__raw_writel(1 << cfg->complete_code, base + EDMA3_ICR);
else
__raw_writel(1 << cfg->complete_code, base + EDMA3_ICRH);
/* Map parameter set and trigger word 7 to quick channel */
qchmap = ((EDMA3_CHMAP_PARSET_MASK & cfg->slot)
<< EDMA3_CHMAP_PARSET_SHIFT) |
(cfg->trigger_slot_word << EDMA3_CHMAP_TRIGWORD_SHIFT);
__raw_writel(qchmap, base + EDMA3_QCHMAP(cfg->chnum));
/* Clear missed event if set*/
__raw_writel(1 << cfg->chnum, base + EDMA3_QSECR);
__raw_writel(1 << cfg->chnum, base + EDMA3_QEMCR);
/* Enable qdma channel event */
__raw_writel(1 << cfg->chnum, base + EDMA3_QEESR);
}
/**
* edma3_set_dest - set initial DMA destination address in parameter RAM slot
* @base: base address of edma
* @slot: parameter RAM slot being configured
* @dst: physical address of destination (memory, controller FIFO, etc)
* @addressMode: INCR, except in very rare cases
* @width: ignored unless @addressMode is FIFO, else specifies the
* width to use when addressing the fifo (e.g. W8BIT, W32BIT)
*
* Note that the destination address is modified during the DMA transfer
* according to edma3_set_dest_index().
*/
void edma3_set_dest(u32 base, int slot, u32 dst, enum edma3_address_mode mode,
enum edma3_fifo_width width)
{
u32 opt;
struct edma3_slot_layout *rg;
rg = (struct edma3_slot_layout *)(base + EDMA3_SL_BASE(slot));
opt = __raw_readl(&rg->opt);
if (mode == FIFO)
opt = (opt & EDMA3_SLOPT_FIFO_WIDTH_MASK) |
(EDMA3_SLOPT_DST_ADDR_CONST_MODE |
EDMA3_SLOPT_FIFO_WIDTH_SET(width));
else
opt &= ~EDMA3_SLOPT_DST_ADDR_CONST_MODE;
__raw_writel(opt, &rg->opt);
__raw_writel(dst, &rg->dst);
}
/**
* edma3_set_dest_index - configure DMA destination address indexing
* @base: base address of edma
* @slot: parameter RAM slot being configured
* @bidx: byte offset between destination arrays in a frame
* @cidx: byte offset between destination frames in a block
*
* Offsets are specified to support either contiguous or discontiguous
* memory transfers, or repeated access to a hardware register, as needed.
* When accessing hardware registers, both offsets are normally zero.
*/
void edma3_set_dest_index(u32 base, unsigned slot, int bidx, int cidx)
{
u32 src_dst_bidx;
u32 src_dst_cidx;
struct edma3_slot_layout *rg;
rg = (struct edma3_slot_layout *)(base + EDMA3_SL_BASE(slot));
src_dst_bidx = __raw_readl(&rg->src_dst_bidx);
src_dst_cidx = __raw_readl(&rg->src_dst_cidx);
__raw_writel((src_dst_bidx & 0x0000ffff) | (bidx << 16),
&rg->src_dst_bidx);
__raw_writel((src_dst_cidx & 0x0000ffff) | (cidx << 16),
&rg->src_dst_cidx);
}
/**
* edma3_set_dest_addr - set destination address for slot only
*/
void edma3_set_dest_addr(u32 base, int slot, u32 dst)
{
struct edma3_slot_layout *rg;
rg = (struct edma3_slot_layout *)(base + EDMA3_SL_BASE(slot));
__raw_writel(dst, &rg->dst);
}
/**
* edma3_set_src - set initial DMA source address in parameter RAM slot
* @base: base address of edma
* @slot: parameter RAM slot being configured
* @src_port: physical address of source (memory, controller FIFO, etc)
* @mode: INCR, except in very rare cases
* @width: ignored unless @addressMode is FIFO, else specifies the
* width to use when addressing the fifo (e.g. W8BIT, W32BIT)
*
* Note that the source address is modified during the DMA transfer
* according to edma3_set_src_index().
*/
void edma3_set_src(u32 base, int slot, u32 src, enum edma3_address_mode mode,
enum edma3_fifo_width width)
{
u32 opt;
struct edma3_slot_layout *rg;
rg = (struct edma3_slot_layout *)(base + EDMA3_SL_BASE(slot));
opt = __raw_readl(&rg->opt);
if (mode == FIFO)
opt = (opt & EDMA3_SLOPT_FIFO_WIDTH_MASK) |
(EDMA3_SLOPT_DST_ADDR_CONST_MODE |
EDMA3_SLOPT_FIFO_WIDTH_SET(width));
else
opt &= ~EDMA3_SLOPT_DST_ADDR_CONST_MODE;
__raw_writel(opt, &rg->opt);
__raw_writel(src, &rg->src);
}
/**
* edma3_set_src_index - configure DMA source address indexing
* @base: base address of edma
* @slot: parameter RAM slot being configured
* @bidx: byte offset between source arrays in a frame
* @cidx: byte offset between source frames in a block
*
* Offsets are specified to support either contiguous or discontiguous
* memory transfers, or repeated access to a hardware register, as needed.
* When accessing hardware registers, both offsets are normally zero.
*/
void edma3_set_src_index(u32 base, unsigned slot, int bidx, int cidx)
{
u32 src_dst_bidx;
u32 src_dst_cidx;
struct edma3_slot_layout *rg;
rg = (struct edma3_slot_layout *)(base + EDMA3_SL_BASE(slot));
src_dst_bidx = __raw_readl(&rg->src_dst_bidx);
src_dst_cidx = __raw_readl(&rg->src_dst_cidx);
__raw_writel((src_dst_bidx & 0xffff0000) | bidx,
&rg->src_dst_bidx);
__raw_writel((src_dst_cidx & 0xffff0000) | cidx,
&rg->src_dst_cidx);
}
/**
* edma3_set_src_addr - set source address for slot only
*/
void edma3_set_src_addr(u32 base, int slot, u32 src)
{
struct edma3_slot_layout *rg;
rg = (struct edma3_slot_layout *)(base + EDMA3_SL_BASE(slot));
__raw_writel(src, &rg->src);
}
/**
* edma3_set_transfer_params - configure DMA transfer parameters
* @base: base address of edma
* @slot: parameter RAM slot being configured
* @acnt: how many bytes per array (at least one)
* @bcnt: how many arrays per frame (at least one)
* @ccnt: how many frames per block (at least one)
* @bcnt_rld: used only for A-Synchronized transfers; this specifies
* the value to reload into bcnt when it decrements to zero
* @sync_mode: ASYNC or ABSYNC
*
* See the EDMA3 documentation to understand how to configure and link
* transfers using the fields in PaRAM slots. If you are not doing it
* all at once with edma3_write_slot(), you will use this routine
* plus two calls each for source and destination, setting the initial
* address and saying how to index that address.
*
* An example of an A-Synchronized transfer is a serial link using a
* single word shift register. In that case, @acnt would be equal to
* that word size; the serial controller issues a DMA synchronization
* event to transfer each word, and memory access by the DMA transfer
* controller will be word-at-a-time.
*
* An example of an AB-Synchronized transfer is a device using a FIFO.
* In that case, @acnt equals the FIFO width and @bcnt equals its depth.
* The controller with the FIFO issues DMA synchronization events when
* the FIFO threshold is reached, and the DMA transfer controller will
* transfer one frame to (or from) the FIFO. It will probably use
* efficient burst modes to access memory.
*/
void edma3_set_transfer_params(u32 base, int slot, int acnt,
int bcnt, int ccnt, u16 bcnt_rld,
enum edma3_sync_dimension sync_mode)
{
u32 opt;
u32 link_bcntrld;
struct edma3_slot_layout *rg;
rg = (struct edma3_slot_layout *)(base + EDMA3_SL_BASE(slot));
link_bcntrld = __raw_readl(&rg->link_bcntrld);
__raw_writel((bcnt_rld << 16) | (0x0000ffff & link_bcntrld),
&rg->link_bcntrld);
opt = __raw_readl(&rg->opt);
if (sync_mode == ASYNC)
__raw_writel(opt & ~EDMA3_SLOPT_AB_SYNC, &rg->opt);
else
__raw_writel(opt | EDMA3_SLOPT_AB_SYNC, &rg->opt);
/* Set the acount, bcount, ccount registers */
__raw_writel((bcnt << 16) | (acnt & 0xffff), &rg->a_b_cnt);
__raw_writel(0xffff & ccnt, &rg->ccnt);
}
/**
* edma3_write_slot - write parameter RAM data for slot
* @base: base address of edma
* @slot: number of parameter RAM slot being modified
* @param: data to be written into parameter RAM slot
*
* Use this to assign all parameters of a transfer at once. This
* allows more efficient setup of transfers than issuing multiple
* calls to set up those parameters in small pieces, and provides
* complete control over all transfer options.
*/
void edma3_write_slot(u32 base, int slot, struct edma3_slot_layout *param)
{
int i;
u32 *p = (u32 *)param;
u32 *addr = (u32 *)(base + EDMA3_SL_BASE(slot));
for (i = 0; i < sizeof(struct edma3_slot_layout)/4; i += 4)
__raw_writel(*p++, addr++);
}
/**
* edma3_read_slot - read parameter RAM data from slot
* @base: base address of edma
* @slot: number of parameter RAM slot being copied
* @param: where to store copy of parameter RAM data
*
* Use this to read data from a parameter RAM slot, perhaps to
* save them as a template for later reuse.
*/
void edma3_read_slot(u32 base, int slot, struct edma3_slot_layout *param)
{
int i;
u32 *p = (u32 *)param;
u32 *addr = (u32 *)(base + EDMA3_SL_BASE(slot));
for (i = 0; i < sizeof(struct edma3_slot_layout)/4; i += 4)
*p++ = __raw_readl(addr++);
}
void edma3_slot_configure(u32 base, int slot, struct edma3_slot_config *cfg)
{
struct edma3_slot_layout *rg;
rg = (struct edma3_slot_layout *)(base + EDMA3_SL_BASE(slot));
__raw_writel(cfg->opt, &rg->opt);
__raw_writel(cfg->src, &rg->src);
__raw_writel((cfg->bcnt << 16) | (cfg->acnt & 0xffff), &rg->a_b_cnt);
__raw_writel(cfg->dst, &rg->dst);
__raw_writel((cfg->dst_bidx << 16) |
(cfg->src_bidx & 0xffff), &rg->src_dst_bidx);
__raw_writel((cfg->bcntrld << 16) |
(cfg->link & 0xffff), &rg->link_bcntrld);
__raw_writel((cfg->dst_cidx << 16) |
(cfg->src_cidx & 0xffff), &rg->src_dst_cidx);
__raw_writel(0xffff & cfg->ccnt, &rg->ccnt);
}
/**
* edma3_check_for_transfer - check if transfer coplete by checking
* interrupt pending bit. Clear interrupt pending bit if complete.
* @base: base address of edma
* @cfg: pinter to struct edma3_channel_config which was passed
* to qedma3_start when you started qdma channel
*
* Return 0 if complete, 1 if not.
*/
int edma3_check_for_transfer(u32 base, struct edma3_channel_config *cfg)
{
u32 inum;
u32 ipr_base;
u32 icr_base;
if (cfg->complete_code < 32) {
ipr_base = base + EDMA3_IPR;
icr_base = base + EDMA3_ICR;
inum = 1 << cfg->complete_code;
} else {
ipr_base = base + EDMA3_IPRH;
icr_base = base + EDMA3_ICRH;
inum = 1 << (cfg->complete_code - 32);
}
/* check complete interrupt */
if (!(__raw_readl(ipr_base) & inum))
return 1;
/* clean up the pending int bit */
__raw_writel(inum, icr_base);
return 0;
}
/**
* qedma3_stop - stops dma on the channel passed
* @base: base address of edma
* @cfg: pinter to struct edma3_channel_config which was passed
* to qedma3_start when you started qdma channel
*/
void qedma3_stop(u32 base, struct edma3_channel_config *cfg)
{
/* Disable qdma channel event */
__raw_writel(1 << cfg->chnum, base + EDMA3_QEECR);
/* clean up the interrupt indication */
if (cfg->complete_code < 32)
__raw_writel(1 << cfg->complete_code, base + EDMA3_ICR);
else
__raw_writel(1 << cfg->complete_code, base + EDMA3_ICRH);
/* Clear missed event if set*/
__raw_writel(1 << cfg->chnum, base + EDMA3_QSECR);
__raw_writel(1 << cfg->chnum, base + EDMA3_QEMCR);
/* Clear the channel map */
__raw_writel(0, base + EDMA3_QCHMAP(cfg->chnum));
}
void __edma3_transfer(unsigned long edma3_base_addr, unsigned int edma_slot_num,
void *dst, void *src, size_t len, size_t s_len)
{
struct edma3_slot_config slot;
struct edma3_channel_config edma_channel;
int b_cnt_value = 1;
int rem_bytes = 0;
int a_cnt_value = len;
unsigned int addr = (unsigned int) (dst);
unsigned int max_acnt = 0x7FFFU;
if (len > s_len) {
b_cnt_value = (len / s_len);
rem_bytes = (len % s_len);
a_cnt_value = s_len;
} else if (len > max_acnt) {
b_cnt_value = (len / max_acnt);
rem_bytes = (len % max_acnt);
a_cnt_value = max_acnt;
}
slot.opt = 0;
slot.src = ((unsigned int) src);
slot.acnt = a_cnt_value;
slot.bcnt = b_cnt_value;
slot.ccnt = 1;
if (len == s_len)
slot.src_bidx = a_cnt_value;
else
slot.src_bidx = 0;
slot.dst_bidx = a_cnt_value;
slot.src_cidx = 0;
slot.dst_cidx = 0;
slot.link = EDMA3_PARSET_NULL_LINK;
slot.bcntrld = 0;
slot.opt = EDMA3_SLOPT_TRANS_COMP_INT_ENB |
EDMA3_SLOPT_COMP_CODE(0) |
EDMA3_SLOPT_STATIC | EDMA3_SLOPT_AB_SYNC;
edma3_slot_configure(edma3_base_addr, edma_slot_num, &slot);
edma_channel.slot = edma_slot_num;
edma_channel.chnum = 0;
edma_channel.complete_code = 0;
/* set event trigger to dst update */
edma_channel.trigger_slot_word = EDMA3_TWORD(dst);
qedma3_start(edma3_base_addr, &edma_channel);
edma3_set_dest_addr(edma3_base_addr, edma_channel.slot, addr);
while (edma3_check_for_transfer(edma3_base_addr, &edma_channel))
;
qedma3_stop(edma3_base_addr, &edma_channel);
if (rem_bytes != 0) {
slot.opt = 0;
if (len == s_len)
slot.src =
(b_cnt_value * max_acnt) + ((unsigned int) src);
else
slot.src = (unsigned int) src;
slot.acnt = rem_bytes;
slot.bcnt = 1;
slot.ccnt = 1;
slot.src_bidx = rem_bytes;
slot.dst_bidx = rem_bytes;
slot.src_cidx = 0;
slot.dst_cidx = 0;
slot.link = EDMA3_PARSET_NULL_LINK;
slot.bcntrld = 0;
slot.opt = EDMA3_SLOPT_TRANS_COMP_INT_ENB |
EDMA3_SLOPT_COMP_CODE(0) |
EDMA3_SLOPT_STATIC | EDMA3_SLOPT_AB_SYNC;
edma3_slot_configure(edma3_base_addr, edma_slot_num, &slot);
edma_channel.slot = edma_slot_num;
edma_channel.chnum = 0;
edma_channel.complete_code = 0;
/* set event trigger to dst update */
edma_channel.trigger_slot_word = EDMA3_TWORD(dst);
qedma3_start(edma3_base_addr, &edma_channel);
edma3_set_dest_addr(edma3_base_addr, edma_channel.slot, addr +
(max_acnt * b_cnt_value));
while (edma3_check_for_transfer(edma3_base_addr, &edma_channel))
;
qedma3_stop(edma3_base_addr, &edma_channel);
}
}
void __edma3_fill(unsigned long edma3_base_addr, unsigned int edma_slot_num,
void *dst, u8 val, size_t len)
{
int xfer_len;
int max_xfer = EDMA_FILL_BUFFER_SIZE * 65535;
memset((void *)edma_fill_buffer, val, sizeof(edma_fill_buffer));
while (len) {
xfer_len = len;
if (xfer_len > max_xfer)
xfer_len = max_xfer;
__edma3_transfer(edma3_base_addr, edma_slot_num, dst,
edma_fill_buffer, xfer_len,
EDMA_FILL_BUFFER_SIZE);
len -= xfer_len;
dst += xfer_len;
}
}
#ifndef CONFIG_DMA
void edma3_transfer(unsigned long edma3_base_addr, unsigned int edma_slot_num,
void *dst, void *src, size_t len)
{
__edma3_transfer(edma3_base_addr, edma_slot_num, dst, src, len, len);
}
void edma3_fill(unsigned long edma3_base_addr, unsigned int edma_slot_num,
void *dst, u8 val, size_t len)
{
__edma3_fill(edma3_base_addr, edma_slot_num, dst, val, len);
}
#else
static int ti_edma3_transfer(struct udevice *dev, int direction, void *dst,
void *src, size_t len)
{
struct ti_edma3_priv *priv = dev_get_priv(dev);
/* enable edma3 clocks */
enable_edma3_clocks();
switch (direction) {
case DMA_MEM_TO_MEM:
__edma3_transfer(priv->base, 1, dst, src, len, len);
break;
default:
pr_err("Transfer type not implemented in DMA driver\n");
break;
}
/* disable edma3 clocks */
disable_edma3_clocks();
return 0;
}
static int ti_edma3_ofdata_to_platdata(struct udevice *dev)
{
struct ti_edma3_priv *priv = dev_get_priv(dev);
priv->base = devfdt_get_addr(dev);
return 0;
}
static int ti_edma3_probe(struct udevice *dev)
{
struct dma_dev_priv *uc_priv = dev_get_uclass_priv(dev);
uc_priv->supported = DMA_SUPPORTS_MEM_TO_MEM;
return 0;
}
static const struct dma_ops ti_edma3_ops = {
.transfer = ti_edma3_transfer,
};
static const struct udevice_id ti_edma3_ids[] = {
{ .compatible = "ti,edma3" },
{ }
};
U_BOOT_DRIVER(ti_edma3) = {
.name = "ti_edma3",
.id = UCLASS_DMA,
.of_match = ti_edma3_ids,
.ops = &ti_edma3_ops,
.ofdata_to_platdata = ti_edma3_ofdata_to_platdata,
.probe = ti_edma3_probe,
.priv_auto_alloc_size = sizeof(struct ti_edma3_priv),
};
#endif /* CONFIG_DMA */
@@ -0,0 +1,14 @@
# SPDX-License-Identifier: GPL-2.0+
if ARCH_K3
config TI_K3_NAVSS_UDMA
bool "Texas Instruments UDMA"
depends on ARCH_K3
select DMA
select TI_K3_NAVSS_RINGACC
select TI_K3_NAVSS_PSILCFG
default n
help
Support for UDMA used in K3 devices.
endif
@@ -0,0 +1,3 @@
# SPDX-License-Identifier: GPL-2.0+
obj-$(CONFIG_TI_K3_NAVSS_UDMA) += k3-udma.o
@@ -0,0 +1,184 @@
/* SPDX-License-Identifier: GPL-2.0+ */
/*
* Copyright (C) 2018 Texas Instruments Incorporated - http://www.ti.com
*
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
*/
#ifndef K3_NAVSS_UDMA_HWDEF_H_
#define K3_NAVSS_UDMA_HWDEF_H_
#define UDMA_PSIL_DST_THREAD_ID_OFFSET 0x8000
/* Global registers */
#define UDMA_REV_REG 0x0
#define UDMA_PERF_CTL_REG 0x4
#define UDMA_EMU_CTL_REG 0x8
#define UDMA_PSIL_TO_REG 0x10
#define UDMA_UTC_CTL_REG 0x1c
#define UDMA_CAP_REG(i) (0x20 + (i * 4))
#define UDMA_RX_FLOW_ID_FW_OES_REG 0x80
#define UDMA_RX_FLOW_ID_FW_STATUS_REG 0x88
/* RX Flow regs */
#define UDMA_RFLOW_RFA_REG 0x0
#define UDMA_RFLOW_RFB_REG 0x4
#define UDMA_RFLOW_RFC_REG 0x8
#define UDMA_RFLOW_RFD_REG 0xc
#define UDMA_RFLOW_RFE_REG 0x10
#define UDMA_RFLOW_RFF_REG 0x14
#define UDMA_RFLOW_RFG_REG 0x18
#define UDMA_RFLOW_RFH_REG 0x1c
#define UDMA_RFLOW_REG(x) (UDMA_RFLOW_RF##x##_REG)
/* TX chan regs */
#define UDMA_TCHAN_TCFG_REG 0x0
#define UDMA_TCHAN_TCREDIT_REG 0x4
#define UDMA_TCHAN_TCQ_REG 0x14
#define UDMA_TCHAN_TOES_REG(i) (0x20 + (i) * 4)
#define UDMA_TCHAN_TEOES_REG 0x60
#define UDMA_TCHAN_TPRI_CTRL_REG 0x64
#define UDMA_TCHAN_THREAD_ID_REG 0x68
#define UDMA_TCHAN_TFIFO_DEPTH_REG 0x70
#define UDMA_TCHAN_TST_SCHED_REG 0x80
/* RX chan regs */
#define UDMA_RCHAN_RCFG_REG 0x0
#define UDMA_RCHAN_RCQ_REG 0x14
#define UDMA_RCHAN_ROES_REG(i) (0x20 + (i) * 4)
#define UDMA_RCHAN_REOES_REG 0x60
#define UDMA_RCHAN_RPRI_CTRL_REG 0x64
#define UDMA_RCHAN_THREAD_ID_REG 0x68
#define UDMA_RCHAN_RST_SCHED_REG 0x80
#define UDMA_RCHAN_RFLOW_RNG_REG 0xf0
/* TX chan RT regs */
#define UDMA_TCHAN_RT_CTL_REG 0x0
#define UDMA_TCHAN_RT_SWTRIG_REG 0x8
#define UDMA_TCHAN_RT_STDATA_REG 0x80
#define UDMA_TCHAN_RT_PEERn_REG(i) (0x200 + (i * 0x4))
#define UDMA_TCHAN_RT_PEER_STATIC_TR_XY_REG \
UDMA_TCHAN_RT_PEERn_REG(0) /* PSI-L: 0x400 */
#define UDMA_TCHAN_RT_PEER_STATIC_TR_Z_REG \
UDMA_TCHAN_RT_PEERn_REG(1) /* PSI-L: 0x401 */
#define UDMA_TCHAN_RT_PEER_BCNT_REG \
UDMA_TCHAN_RT_PEERn_REG(4) /* PSI-L: 0x404 */
#define UDMA_TCHAN_RT_PEER_RT_EN_REG \
UDMA_TCHAN_RT_PEERn_REG(8) /* PSI-L: 0x408 */
#define UDMA_TCHAN_RT_PCNT_REG 0x400
#define UDMA_TCHAN_RT_BCNT_REG 0x408
#define UDMA_TCHAN_RT_SBCNT_REG 0x410
/* RX chan RT regs */
#define UDMA_RCHAN_RT_CTL_REG 0x0
#define UDMA_RCHAN_RT_SWTRIG_REG 0x8
#define UDMA_RCHAN_RT_STDATA_REG 0x80
#define UDMA_RCHAN_RT_PEERn_REG(i) (0x200 + (i * 0x4))
#define UDMA_RCHAN_RT_PEER_STATIC_TR_XY_REG \
UDMA_RCHAN_RT_PEERn_REG(0) /* PSI-L: 0x400 */
#define UDMA_RCHAN_RT_PEER_STATIC_TR_Z_REG \
UDMA_RCHAN_RT_PEERn_REG(1) /* PSI-L: 0x401 */
#define UDMA_RCHAN_RT_PEER_BCNT_REG \
UDMA_RCHAN_RT_PEERn_REG(4) /* PSI-L: 0x404 */
#define UDMA_RCHAN_RT_PEER_RT_EN_REG \
UDMA_RCHAN_RT_PEERn_REG(8) /* PSI-L: 0x408 */
#define UDMA_RCHAN_RT_PCNT_REG 0x400
#define UDMA_RCHAN_RT_BCNT_REG 0x408
#define UDMA_RCHAN_RT_SBCNT_REG 0x410
/* UDMA_TCHAN_TCFG_REG/UDMA_RCHAN_RCFG_REG */
#define UDMA_CHAN_CFG_PAUSE_ON_ERR BIT(31)
#define UDMA_TCHAN_CFG_FILT_EINFO BIT(30)
#define UDMA_TCHAN_CFG_FILT_PSWORDS BIT(29)
#define UDMA_CHAN_CFG_ATYPE_MASK GENMASK(25, 24)
#define UDMA_CHAN_CFG_ATYPE_SHIFT 24
#define UDMA_CHAN_CFG_CHAN_TYPE_MASK GENMASK(19, 16)
#define UDMA_CHAN_CFG_CHAN_TYPE_SHIFT 16
/*
* PBVR - using pass by value rings
* PBRR - using pass by reference rings
* 3RDP - Third Party DMA
* BC - Block Copy
* SB - single buffer packet mode enabled
*/
#define UDMA_CHAN_CFG_CHAN_TYPE_PACKET_PBRR \
(2 << UDMA_CHAN_CFG_CHAN_TYPE_SHIFT)
#define UDMA_CHAN_CFG_CHAN_TYPE_PACKET_SB_PBRR \
(3 << UDMA_CHAN_CFG_CHAN_TYPE_SHIFT)
#define UDMA_CHAN_CFG_CHAN_TYPE_3RDP_PBRR \
(10 << UDMA_CHAN_CFG_CHAN_TYPE_SHIFT)
#define UDMA_CHAN_CFG_CHAN_TYPE_3RDP_PBVR \
(11 << UDMA_CHAN_CFG_CHAN_TYPE_SHIFT)
#define UDMA_CHAN_CFG_CHAN_TYPE_3RDP_BC_PBRR \
(12 << UDMA_CHAN_CFG_CHAN_TYPE_SHIFT)
#define UDMA_RCHAN_CFG_IGNORE_SHORT BIT(15)
#define UDMA_RCHAN_CFG_IGNORE_LONG BIT(14)
#define UDMA_TCHAN_CFG_SUPR_TDPKT BIT(8)
#define UDMA_CHAN_CFG_FETCH_SIZE_MASK GENMASK(6, 0)
#define UDMA_CHAN_CFG_FETCH_SIZE_SHIFT 0
/* UDMA_TCHAN_RT_CTL_REG/UDMA_RCHAN_RT_CTL_REG */
#define UDMA_CHAN_RT_CTL_EN BIT(31)
#define UDMA_CHAN_RT_CTL_TDOWN BIT(30)
#define UDMA_CHAN_RT_CTL_PAUSE BIT(29)
#define UDMA_CHAN_RT_CTL_FTDOWN BIT(28)
#define UDMA_CHAN_RT_CTL_ERROR BIT(0)
/* UDMA_TCHAN_RT_PEER_RT_EN_REG/UDMA_RCHAN_RT_PEER_RT_EN_REG (PSI-L: 0x408) */
#define UDMA_PEER_RT_EN_ENABLE BIT(31)
#define UDMA_PEER_RT_EN_TEARDOWN BIT(30)
#define UDMA_PEER_RT_EN_PAUSE BIT(29)
#define UDMA_PEER_RT_EN_FLUSH BIT(28)
#define UDMA_PEER_RT_EN_IDLE BIT(1)
/* RX Flow reg RFA */
#define UDMA_RFLOW_RFA_EINFO BIT(30)
#define UDMA_RFLOW_RFA_PSINFO BIT(29)
#define UDMA_RFLOW_RFA_ERR_HANDLING BIT(28)
#define UDMA_RFLOW_RFA_DESC_TYPE_MASK GENMASK(27, 26)
#define UDMA_RFLOW_RFA_DESC_TYPE_SHIFT 26
#define UDMA_RFLOW_RFA_PS_LOC BIT(25)
#define UDMA_RFLOW_RFA_SOP_OFF_MASK GENMASK(24, 16)
#define UDMA_RFLOW_RFA_SOP_OFF_SHIFT 16
#define UDMA_RFLOW_RFA_DEST_QNUM_MASK GENMASK(15, 0)
#define UDMA_RFLOW_RFA_DEST_QNUM_SHIFT 0
/* RX Flow reg RFC */
#define UDMA_RFLOW_RFC_SRC_TAG_HI_SEL_SHIFT 28
#define UDMA_RFLOW_RFC_SRC_TAG_LO_SEL_SHIFT 24
#define UDMA_RFLOW_RFC_DST_TAG_HI_SEL_SHIFT 20
#define UDMA_RFLOW_RFC_DST_TAG_LO_SE_SHIFT 16
/*
* UDMA_TCHAN_RT_PEER_STATIC_TR_XY_REG /
* UDMA_RCHAN_RT_PEER_STATIC_TR_XY_REG
*/
#define PDMA_STATIC_TR_X_MASK GENMASK(26, 24)
#define PDMA_STATIC_TR_X_SHIFT (24)
#define PDMA_STATIC_TR_Y_MASK GENMASK(11, 0)
#define PDMA_STATIC_TR_Y_SHIFT (0)
#define PDMA_STATIC_TR_Y(x) \
(((x) << PDMA_STATIC_TR_Y_SHIFT) & PDMA_STATIC_TR_Y_MASK)
#define PDMA_STATIC_TR_X(x) \
(((x) << PDMA_STATIC_TR_X_SHIFT) & PDMA_STATIC_TR_X_MASK)
/*
* UDMA_TCHAN_RT_PEER_STATIC_TR_Z_REG /
* UDMA_RCHAN_RT_PEER_STATIC_TR_Z_REG
*/
#define PDMA_STATIC_TR_Z_MASK GENMASK(11, 0)
#define PDMA_STATIC_TR_Z_SHIFT (0)
#define PDMA_STATIC_TR_Z(x) \
(((x) << PDMA_STATIC_TR_Z_SHIFT) & PDMA_STATIC_TR_Z_MASK)
#endif /* K3_NAVSS_UDMA_HWDEF_H_ */
File diff suppressed because it is too large Load Diff