GK SDK 源码库: XMIPCLinuxV100R005C00SPC030 (kernel/tools/open_source excluded)
This commit is contained in:
@@ -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
Reference in New Issue
Block a user