DMAENGINE: Support for ST-Ericssons DMA40 block v3
This is a straightforward driver for the ST-Ericsson DMA40 DMA controller found in U8500, implemented akin to the existing COH 901 318 driver. Signed-off-by: Linus Walleij <linus.walleij@stericsson.com> Acked-by: Srinidh Kasagar <srinidhi.kasagar@stericsson.com> Cc: STEricsson_nomadik_linux@list.st.com Cc: Alessandro Rubini <rubini@unipv.it> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Dan Williams <dan.j.williams@intel.com>
This commit is contained in:
parent
6a3cd3ea48
commit
8d318a50b3
6 changed files with 3651 additions and 0 deletions
239
arch/arm/plat-nomadik/include/plat/ste_dma40.h
Normal file
239
arch/arm/plat-nomadik/include/plat/ste_dma40.h
Normal file
|
@ -0,0 +1,239 @@
|
|||
/*
|
||||
* arch/arm/plat-nomadik/include/plat/ste_dma40.h
|
||||
*
|
||||
* Copyright (C) ST-Ericsson 2007-2010
|
||||
* License terms: GNU General Public License (GPL) version 2
|
||||
* Author: Per Friden <per.friden@stericsson.com>
|
||||
* Author: Jonas Aaberg <jonas.aberg@stericsson.com>
|
||||
*/
|
||||
|
||||
|
||||
#ifndef STE_DMA40_H
|
||||
#define STE_DMA40_H
|
||||
|
||||
#include <linux/dmaengine.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/dmaengine.h>
|
||||
|
||||
/* dev types for memcpy */
|
||||
#define STEDMA40_DEV_DST_MEMORY (-1)
|
||||
#define STEDMA40_DEV_SRC_MEMORY (-1)
|
||||
|
||||
/*
|
||||
* Description of bitfields of channel_type variable is available in
|
||||
* the info structure.
|
||||
*/
|
||||
|
||||
/* Priority */
|
||||
#define STEDMA40_INFO_PRIO_TYPE_POS 2
|
||||
#define STEDMA40_HIGH_PRIORITY_CHANNEL (0x1 << STEDMA40_INFO_PRIO_TYPE_POS)
|
||||
#define STEDMA40_LOW_PRIORITY_CHANNEL (0x2 << STEDMA40_INFO_PRIO_TYPE_POS)
|
||||
|
||||
/* Mode */
|
||||
#define STEDMA40_INFO_CH_MODE_TYPE_POS 6
|
||||
#define STEDMA40_CHANNEL_IN_PHY_MODE (0x1 << STEDMA40_INFO_CH_MODE_TYPE_POS)
|
||||
#define STEDMA40_CHANNEL_IN_LOG_MODE (0x2 << STEDMA40_INFO_CH_MODE_TYPE_POS)
|
||||
#define STEDMA40_CHANNEL_IN_OPER_MODE (0x3 << STEDMA40_INFO_CH_MODE_TYPE_POS)
|
||||
|
||||
/* Mode options */
|
||||
#define STEDMA40_INFO_CH_MODE_OPT_POS 8
|
||||
#define STEDMA40_PCHAN_BASIC_MODE (0x1 << STEDMA40_INFO_CH_MODE_OPT_POS)
|
||||
#define STEDMA40_PCHAN_MODULO_MODE (0x2 << STEDMA40_INFO_CH_MODE_OPT_POS)
|
||||
#define STEDMA40_PCHAN_DOUBLE_DST_MODE (0x3 << STEDMA40_INFO_CH_MODE_OPT_POS)
|
||||
#define STEDMA40_LCHAN_SRC_PHY_DST_LOG (0x1 << STEDMA40_INFO_CH_MODE_OPT_POS)
|
||||
#define STEDMA40_LCHAN_SRC_LOG_DST_PHS (0x2 << STEDMA40_INFO_CH_MODE_OPT_POS)
|
||||
#define STEDMA40_LCHAN_SRC_LOG_DST_LOG (0x3 << STEDMA40_INFO_CH_MODE_OPT_POS)
|
||||
|
||||
/* Interrupt */
|
||||
#define STEDMA40_INFO_TIM_POS 10
|
||||
#define STEDMA40_NO_TIM_FOR_LINK (0x0 << STEDMA40_INFO_TIM_POS)
|
||||
#define STEDMA40_TIM_FOR_LINK (0x1 << STEDMA40_INFO_TIM_POS)
|
||||
|
||||
/* End of channel_type configuration */
|
||||
|
||||
#define STEDMA40_ESIZE_8_BIT 0x0
|
||||
#define STEDMA40_ESIZE_16_BIT 0x1
|
||||
#define STEDMA40_ESIZE_32_BIT 0x2
|
||||
#define STEDMA40_ESIZE_64_BIT 0x3
|
||||
|
||||
/* The value 4 indicates that PEN-reg shall be set to 0 */
|
||||
#define STEDMA40_PSIZE_PHY_1 0x4
|
||||
#define STEDMA40_PSIZE_PHY_2 0x0
|
||||
#define STEDMA40_PSIZE_PHY_4 0x1
|
||||
#define STEDMA40_PSIZE_PHY_8 0x2
|
||||
#define STEDMA40_PSIZE_PHY_16 0x3
|
||||
|
||||
/*
|
||||
* The number of elements differ in logical and
|
||||
* physical mode
|
||||
*/
|
||||
#define STEDMA40_PSIZE_LOG_1 STEDMA40_PSIZE_PHY_2
|
||||
#define STEDMA40_PSIZE_LOG_4 STEDMA40_PSIZE_PHY_4
|
||||
#define STEDMA40_PSIZE_LOG_8 STEDMA40_PSIZE_PHY_8
|
||||
#define STEDMA40_PSIZE_LOG_16 STEDMA40_PSIZE_PHY_16
|
||||
|
||||
enum stedma40_flow_ctrl {
|
||||
STEDMA40_NO_FLOW_CTRL,
|
||||
STEDMA40_FLOW_CTRL,
|
||||
};
|
||||
|
||||
enum stedma40_endianess {
|
||||
STEDMA40_LITTLE_ENDIAN,
|
||||
STEDMA40_BIG_ENDIAN
|
||||
};
|
||||
|
||||
enum stedma40_periph_data_width {
|
||||
STEDMA40_BYTE_WIDTH = STEDMA40_ESIZE_8_BIT,
|
||||
STEDMA40_HALFWORD_WIDTH = STEDMA40_ESIZE_16_BIT,
|
||||
STEDMA40_WORD_WIDTH = STEDMA40_ESIZE_32_BIT,
|
||||
STEDMA40_DOUBLEWORD_WIDTH = STEDMA40_ESIZE_64_BIT
|
||||
};
|
||||
|
||||
struct stedma40_half_channel_info {
|
||||
enum stedma40_endianess endianess;
|
||||
enum stedma40_periph_data_width data_width;
|
||||
int psize;
|
||||
enum stedma40_flow_ctrl flow_ctrl;
|
||||
};
|
||||
|
||||
enum stedma40_xfer_dir {
|
||||
STEDMA40_MEM_TO_MEM,
|
||||
STEDMA40_MEM_TO_PERIPH,
|
||||
STEDMA40_PERIPH_TO_MEM,
|
||||
STEDMA40_PERIPH_TO_PERIPH
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* struct stedma40_chan_cfg - Structure to be filled by client drivers.
|
||||
*
|
||||
* @dir: MEM 2 MEM, PERIPH 2 MEM , MEM 2 PERIPH, PERIPH 2 PERIPH
|
||||
* @channel_type: priority, mode, mode options and interrupt configuration.
|
||||
* @src_dev_type: Src device type
|
||||
* @dst_dev_type: Dst device type
|
||||
* @src_info: Parameters for dst half channel
|
||||
* @dst_info: Parameters for dst half channel
|
||||
* @pre_transfer_data: Data to be passed on to the pre_transfer() function.
|
||||
* @pre_transfer: Callback used if needed before preparation of transfer.
|
||||
* Only called if device is set. size of bytes to transfer
|
||||
* (in case of multiple element transfer size is size of the first element).
|
||||
*
|
||||
*
|
||||
* This structure has to be filled by the client drivers.
|
||||
* It is recommended to do all dma configurations for clients in the machine.
|
||||
*
|
||||
*/
|
||||
struct stedma40_chan_cfg {
|
||||
enum stedma40_xfer_dir dir;
|
||||
unsigned int channel_type;
|
||||
int src_dev_type;
|
||||
int dst_dev_type;
|
||||
struct stedma40_half_channel_info src_info;
|
||||
struct stedma40_half_channel_info dst_info;
|
||||
void *pre_transfer_data;
|
||||
int (*pre_transfer) (struct dma_chan *chan,
|
||||
void *data,
|
||||
int size);
|
||||
};
|
||||
|
||||
/**
|
||||
* struct stedma40_platform_data - Configuration struct for the dma device.
|
||||
*
|
||||
* @dev_len: length of dev_tx and dev_rx
|
||||
* @dev_tx: mapping between destination event line and io address
|
||||
* @dev_rx: mapping between source event line and io address
|
||||
* @memcpy: list of memcpy event lines
|
||||
* @memcpy_len: length of memcpy
|
||||
* @memcpy_conf_phy: default configuration of physical channel memcpy
|
||||
* @memcpy_conf_log: default configuration of logical channel memcpy
|
||||
* @llis_per_log: number of max linked list items per logical channel
|
||||
*
|
||||
*/
|
||||
struct stedma40_platform_data {
|
||||
u32 dev_len;
|
||||
const dma_addr_t *dev_tx;
|
||||
const dma_addr_t *dev_rx;
|
||||
int *memcpy;
|
||||
u32 memcpy_len;
|
||||
struct stedma40_chan_cfg *memcpy_conf_phy;
|
||||
struct stedma40_chan_cfg *memcpy_conf_log;
|
||||
unsigned int llis_per_log;
|
||||
};
|
||||
|
||||
/**
|
||||
* setdma40_set_psize() - Used for changing the package size of an
|
||||
* already configured dma channel.
|
||||
*
|
||||
* @chan: dmaengine handle
|
||||
* @src_psize: new package side for src. (STEDMA40_PSIZE*)
|
||||
* @src_psize: new package side for dst. (STEDMA40_PSIZE*)
|
||||
*
|
||||
* returns 0 on ok, otherwise negative error number.
|
||||
*/
|
||||
int stedma40_set_psize(struct dma_chan *chan,
|
||||
int src_psize,
|
||||
int dst_psize);
|
||||
|
||||
/**
|
||||
* stedma40_filter() - Provides stedma40_chan_cfg to the
|
||||
* ste_dma40 dma driver via the dmaengine framework.
|
||||
* does some checking of what's provided.
|
||||
*
|
||||
* Never directly called by client. It used by dmaengine.
|
||||
* @chan: dmaengine handle.
|
||||
* @data: Must be of type: struct stedma40_chan_cfg and is
|
||||
* the configuration of the framework.
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
bool stedma40_filter(struct dma_chan *chan, void *data);
|
||||
|
||||
/**
|
||||
* stedma40_memcpy_sg() - extension of the dma framework, memcpy to/from
|
||||
* scattergatter lists.
|
||||
*
|
||||
* @chan: dmaengine handle
|
||||
* @sgl_dst: Destination scatter list
|
||||
* @sgl_src: Source scatter list
|
||||
* @sgl_len: The length of each scatterlist. Both lists must be of equal length
|
||||
* and each element must match the corresponding element in the other scatter
|
||||
* list.
|
||||
* @flags: is actually enum dma_ctrl_flags. See dmaengine.h
|
||||
*/
|
||||
|
||||
struct dma_async_tx_descriptor *stedma40_memcpy_sg(struct dma_chan *chan,
|
||||
struct scatterlist *sgl_dst,
|
||||
struct scatterlist *sgl_src,
|
||||
unsigned int sgl_len,
|
||||
unsigned long flags);
|
||||
|
||||
/**
|
||||
* stedma40_slave_mem() - Transfers a raw data buffer to or from a slave
|
||||
* (=device)
|
||||
*
|
||||
* @chan: dmaengine handle
|
||||
* @addr: source or destination physicall address.
|
||||
* @size: bytes to transfer
|
||||
* @direction: direction of transfer
|
||||
* @flags: is actually enum dma_ctrl_flags. See dmaengine.h
|
||||
*/
|
||||
|
||||
static inline struct
|
||||
dma_async_tx_descriptor *stedma40_slave_mem(struct dma_chan *chan,
|
||||
dma_addr_t addr,
|
||||
unsigned int size,
|
||||
enum dma_data_direction direction,
|
||||
unsigned long flags)
|
||||
{
|
||||
struct scatterlist sg;
|
||||
sg_init_table(&sg, 1);
|
||||
sg.dma_address = addr;
|
||||
sg.length = size;
|
||||
|
||||
return chan->device->device_prep_slave_sg(chan, &sg, 1,
|
||||
direction, flags);
|
||||
}
|
||||
|
||||
#endif
|
|
@ -141,6 +141,13 @@ config COH901318
|
|||
help
|
||||
Enable support for ST-Ericsson COH 901 318 DMA.
|
||||
|
||||
config STE_DMA40
|
||||
bool "ST-Ericsson DMA40 support"
|
||||
depends on ARCH_U8500
|
||||
select DMA_ENGINE
|
||||
help
|
||||
Support for ST-Ericsson DMA40 controller
|
||||
|
||||
config AMCC_PPC440SPE_ADMA
|
||||
tristate "AMCC PPC440SPe ADMA support"
|
||||
depends on 440SPe || 440SP
|
||||
|
|
|
@ -21,3 +21,4 @@ obj-$(CONFIG_SH_DMAE) += shdma.o
|
|||
obj-$(CONFIG_COH901318) += coh901318.o coh901318_lli.o
|
||||
obj-$(CONFIG_AMCC_PPC440SPE_ADMA) += ppc4xx/
|
||||
obj-$(CONFIG_TIMB_DMA) += timb_dma.o
|
||||
obj-$(CONFIG_STE_DMA40) += ste_dma40.o ste_dma40_ll.o
|
||||
|
|
2596
drivers/dma/ste_dma40.c
Normal file
2596
drivers/dma/ste_dma40.c
Normal file
File diff suppressed because it is too large
Load diff
454
drivers/dma/ste_dma40_ll.c
Normal file
454
drivers/dma/ste_dma40_ll.c
Normal file
|
@ -0,0 +1,454 @@
|
|||
/*
|
||||
* driver/dma/ste_dma40_ll.c
|
||||
*
|
||||
* Copyright (C) ST-Ericsson 2007-2010
|
||||
* License terms: GNU General Public License (GPL) version 2
|
||||
* Author: Per Friden <per.friden@stericsson.com>
|
||||
* Author: Jonas Aaberg <jonas.aberg@stericsson.com>
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <plat/ste_dma40.h>
|
||||
|
||||
#include "ste_dma40_ll.h"
|
||||
|
||||
/* Sets up proper LCSP1 and LCSP3 register for a logical channel */
|
||||
void d40_log_cfg(struct stedma40_chan_cfg *cfg,
|
||||
u32 *lcsp1, u32 *lcsp3)
|
||||
{
|
||||
u32 l3 = 0; /* dst */
|
||||
u32 l1 = 0; /* src */
|
||||
|
||||
/* src is mem? -> increase address pos */
|
||||
if (cfg->dir == STEDMA40_MEM_TO_PERIPH ||
|
||||
cfg->dir == STEDMA40_MEM_TO_MEM)
|
||||
l1 |= 1 << D40_MEM_LCSP1_SCFG_INCR_POS;
|
||||
|
||||
/* dst is mem? -> increase address pos */
|
||||
if (cfg->dir == STEDMA40_PERIPH_TO_MEM ||
|
||||
cfg->dir == STEDMA40_MEM_TO_MEM)
|
||||
l3 |= 1 << D40_MEM_LCSP3_DCFG_INCR_POS;
|
||||
|
||||
/* src is hw? -> master port 1 */
|
||||
if (cfg->dir == STEDMA40_PERIPH_TO_MEM ||
|
||||
cfg->dir == STEDMA40_PERIPH_TO_PERIPH)
|
||||
l1 |= 1 << D40_MEM_LCSP1_SCFG_MST_POS;
|
||||
|
||||
/* dst is hw? -> master port 1 */
|
||||
if (cfg->dir == STEDMA40_MEM_TO_PERIPH ||
|
||||
cfg->dir == STEDMA40_PERIPH_TO_PERIPH)
|
||||
l3 |= 1 << D40_MEM_LCSP3_DCFG_MST_POS;
|
||||
|
||||
l3 |= 1 << D40_MEM_LCSP3_DCFG_TIM_POS;
|
||||
l3 |= 1 << D40_MEM_LCSP3_DCFG_EIM_POS;
|
||||
l3 |= cfg->dst_info.psize << D40_MEM_LCSP3_DCFG_PSIZE_POS;
|
||||
l3 |= cfg->dst_info.data_width << D40_MEM_LCSP3_DCFG_ESIZE_POS;
|
||||
l3 |= 1 << D40_MEM_LCSP3_DTCP_POS;
|
||||
|
||||
l1 |= 1 << D40_MEM_LCSP1_SCFG_EIM_POS;
|
||||
l1 |= cfg->src_info.psize << D40_MEM_LCSP1_SCFG_PSIZE_POS;
|
||||
l1 |= cfg->src_info.data_width << D40_MEM_LCSP1_SCFG_ESIZE_POS;
|
||||
l1 |= 1 << D40_MEM_LCSP1_STCP_POS;
|
||||
|
||||
*lcsp1 = l1;
|
||||
*lcsp3 = l3;
|
||||
|
||||
}
|
||||
|
||||
/* Sets up SRC and DST CFG register for both logical and physical channels */
|
||||
void d40_phy_cfg(struct stedma40_chan_cfg *cfg,
|
||||
u32 *src_cfg, u32 *dst_cfg, bool is_log)
|
||||
{
|
||||
u32 src = 0;
|
||||
u32 dst = 0;
|
||||
|
||||
if (!is_log) {
|
||||
/* Physical channel */
|
||||
if ((cfg->dir == STEDMA40_PERIPH_TO_MEM) ||
|
||||
(cfg->dir == STEDMA40_PERIPH_TO_PERIPH)) {
|
||||
/* Set master port to 1 */
|
||||
src |= 1 << D40_SREG_CFG_MST_POS;
|
||||
src |= D40_TYPE_TO_EVENT(cfg->src_dev_type);
|
||||
|
||||
if (cfg->src_info.flow_ctrl == STEDMA40_NO_FLOW_CTRL)
|
||||
src |= 1 << D40_SREG_CFG_PHY_TM_POS;
|
||||
else
|
||||
src |= 3 << D40_SREG_CFG_PHY_TM_POS;
|
||||
}
|
||||
if ((cfg->dir == STEDMA40_MEM_TO_PERIPH) ||
|
||||
(cfg->dir == STEDMA40_PERIPH_TO_PERIPH)) {
|
||||
/* Set master port to 1 */
|
||||
dst |= 1 << D40_SREG_CFG_MST_POS;
|
||||
dst |= D40_TYPE_TO_EVENT(cfg->dst_dev_type);
|
||||
|
||||
if (cfg->dst_info.flow_ctrl == STEDMA40_NO_FLOW_CTRL)
|
||||
dst |= 1 << D40_SREG_CFG_PHY_TM_POS;
|
||||
else
|
||||
dst |= 3 << D40_SREG_CFG_PHY_TM_POS;
|
||||
}
|
||||
/* Interrupt on end of transfer for destination */
|
||||
dst |= 1 << D40_SREG_CFG_TIM_POS;
|
||||
|
||||
/* Generate interrupt on error */
|
||||
src |= 1 << D40_SREG_CFG_EIM_POS;
|
||||
dst |= 1 << D40_SREG_CFG_EIM_POS;
|
||||
|
||||
/* PSIZE */
|
||||
if (cfg->src_info.psize != STEDMA40_PSIZE_PHY_1) {
|
||||
src |= 1 << D40_SREG_CFG_PHY_PEN_POS;
|
||||
src |= cfg->src_info.psize << D40_SREG_CFG_PSIZE_POS;
|
||||
}
|
||||
if (cfg->dst_info.psize != STEDMA40_PSIZE_PHY_1) {
|
||||
dst |= 1 << D40_SREG_CFG_PHY_PEN_POS;
|
||||
dst |= cfg->dst_info.psize << D40_SREG_CFG_PSIZE_POS;
|
||||
}
|
||||
|
||||
/* Element size */
|
||||
src |= cfg->src_info.data_width << D40_SREG_CFG_ESIZE_POS;
|
||||
dst |= cfg->dst_info.data_width << D40_SREG_CFG_ESIZE_POS;
|
||||
|
||||
} else {
|
||||
/* Logical channel */
|
||||
dst |= 1 << D40_SREG_CFG_LOG_GIM_POS;
|
||||
src |= 1 << D40_SREG_CFG_LOG_GIM_POS;
|
||||
}
|
||||
|
||||
if (cfg->channel_type & STEDMA40_HIGH_PRIORITY_CHANNEL) {
|
||||
src |= 1 << D40_SREG_CFG_PRI_POS;
|
||||
dst |= 1 << D40_SREG_CFG_PRI_POS;
|
||||
}
|
||||
|
||||
src |= cfg->src_info.endianess << D40_SREG_CFG_LBE_POS;
|
||||
dst |= cfg->dst_info.endianess << D40_SREG_CFG_LBE_POS;
|
||||
|
||||
*src_cfg = src;
|
||||
*dst_cfg = dst;
|
||||
}
|
||||
|
||||
int d40_phy_fill_lli(struct d40_phy_lli *lli,
|
||||
dma_addr_t data,
|
||||
u32 data_size,
|
||||
int psize,
|
||||
dma_addr_t next_lli,
|
||||
u32 reg_cfg,
|
||||
bool term_int,
|
||||
u32 data_width,
|
||||
bool is_device)
|
||||
{
|
||||
int num_elems;
|
||||
|
||||
if (psize == STEDMA40_PSIZE_PHY_1)
|
||||
num_elems = 1;
|
||||
else
|
||||
num_elems = 2 << psize;
|
||||
|
||||
/*
|
||||
* Size is 16bit. data_width is 8, 16, 32 or 64 bit
|
||||
* Block large than 64 KiB must be split.
|
||||
*/
|
||||
if (data_size > (0xffff << data_width))
|
||||
return -EINVAL;
|
||||
|
||||
/* Must be aligned */
|
||||
if (!IS_ALIGNED(data, 0x1 << data_width))
|
||||
return -EINVAL;
|
||||
|
||||
/* Transfer size can't be smaller than (num_elms * elem_size) */
|
||||
if (data_size < num_elems * (0x1 << data_width))
|
||||
return -EINVAL;
|
||||
|
||||
/* The number of elements. IE now many chunks */
|
||||
lli->reg_elt = (data_size >> data_width) << D40_SREG_ELEM_PHY_ECNT_POS;
|
||||
|
||||
/*
|
||||
* Distance to next element sized entry.
|
||||
* Usually the size of the element unless you want gaps.
|
||||
*/
|
||||
if (!is_device)
|
||||
lli->reg_elt |= (0x1 << data_width) <<
|
||||
D40_SREG_ELEM_PHY_EIDX_POS;
|
||||
|
||||
/* Where the data is */
|
||||
lli->reg_ptr = data;
|
||||
lli->reg_cfg = reg_cfg;
|
||||
|
||||
/* If this scatter list entry is the last one, no next link */
|
||||
if (next_lli == 0)
|
||||
lli->reg_lnk = 0x1 << D40_SREG_LNK_PHY_TCP_POS;
|
||||
else
|
||||
lli->reg_lnk = next_lli;
|
||||
|
||||
/* Set/clear interrupt generation on this link item.*/
|
||||
if (term_int)
|
||||
lli->reg_cfg |= 0x1 << D40_SREG_CFG_TIM_POS;
|
||||
else
|
||||
lli->reg_cfg &= ~(0x1 << D40_SREG_CFG_TIM_POS);
|
||||
|
||||
/* Post link */
|
||||
lli->reg_lnk |= 0 << D40_SREG_LNK_PHY_PRE_POS;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int d40_phy_sg_to_lli(struct scatterlist *sg,
|
||||
int sg_len,
|
||||
dma_addr_t target,
|
||||
struct d40_phy_lli *lli,
|
||||
dma_addr_t lli_phys,
|
||||
u32 reg_cfg,
|
||||
u32 data_width,
|
||||
int psize,
|
||||
bool term_int)
|
||||
{
|
||||
int total_size = 0;
|
||||
int i;
|
||||
struct scatterlist *current_sg = sg;
|
||||
dma_addr_t next_lli_phys;
|
||||
dma_addr_t dst;
|
||||
int err = 0;
|
||||
|
||||
for_each_sg(sg, current_sg, sg_len, i) {
|
||||
|
||||
total_size += sg_dma_len(current_sg);
|
||||
|
||||
/* If this scatter list entry is the last one, no next link */
|
||||
if (sg_len - 1 == i)
|
||||
next_lli_phys = 0;
|
||||
else
|
||||
next_lli_phys = ALIGN(lli_phys + (i + 1) *
|
||||
sizeof(struct d40_phy_lli),
|
||||
D40_LLI_ALIGN);
|
||||
|
||||
if (target)
|
||||
dst = target;
|
||||
else
|
||||
dst = sg_phys(current_sg);
|
||||
|
||||
err = d40_phy_fill_lli(&lli[i],
|
||||
dst,
|
||||
sg_dma_len(current_sg),
|
||||
psize,
|
||||
next_lli_phys,
|
||||
reg_cfg,
|
||||
!next_lli_phys,
|
||||
data_width,
|
||||
target == dst);
|
||||
if (err)
|
||||
goto err;
|
||||
}
|
||||
|
||||
return total_size;
|
||||
err:
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
void d40_phy_lli_write(void __iomem *virtbase,
|
||||
u32 phy_chan_num,
|
||||
struct d40_phy_lli *lli_dst,
|
||||
struct d40_phy_lli *lli_src)
|
||||
{
|
||||
|
||||
writel(lli_src->reg_cfg, virtbase + D40_DREG_PCBASE +
|
||||
phy_chan_num * D40_DREG_PCDELTA + D40_CHAN_REG_SSCFG);
|
||||
writel(lli_src->reg_elt, virtbase + D40_DREG_PCBASE +
|
||||
phy_chan_num * D40_DREG_PCDELTA + D40_CHAN_REG_SSELT);
|
||||
writel(lli_src->reg_ptr, virtbase + D40_DREG_PCBASE +
|
||||
phy_chan_num * D40_DREG_PCDELTA + D40_CHAN_REG_SSPTR);
|
||||
writel(lli_src->reg_lnk, virtbase + D40_DREG_PCBASE +
|
||||
phy_chan_num * D40_DREG_PCDELTA + D40_CHAN_REG_SSLNK);
|
||||
|
||||
writel(lli_dst->reg_cfg, virtbase + D40_DREG_PCBASE +
|
||||
phy_chan_num * D40_DREG_PCDELTA + D40_CHAN_REG_SDCFG);
|
||||
writel(lli_dst->reg_elt, virtbase + D40_DREG_PCBASE +
|
||||
phy_chan_num * D40_DREG_PCDELTA + D40_CHAN_REG_SDELT);
|
||||
writel(lli_dst->reg_ptr, virtbase + D40_DREG_PCBASE +
|
||||
phy_chan_num * D40_DREG_PCDELTA + D40_CHAN_REG_SDPTR);
|
||||
writel(lli_dst->reg_lnk, virtbase + D40_DREG_PCBASE +
|
||||
phy_chan_num * D40_DREG_PCDELTA + D40_CHAN_REG_SDLNK);
|
||||
|
||||
}
|
||||
|
||||
/* DMA logical lli operations */
|
||||
|
||||
void d40_log_fill_lli(struct d40_log_lli *lli,
|
||||
dma_addr_t data, u32 data_size,
|
||||
u32 lli_next_off, u32 reg_cfg,
|
||||
u32 data_width,
|
||||
bool term_int, bool addr_inc)
|
||||
{
|
||||
lli->lcsp13 = reg_cfg;
|
||||
|
||||
/* The number of elements to transfer */
|
||||
lli->lcsp02 = ((data_size >> data_width) <<
|
||||
D40_MEM_LCSP0_ECNT_POS) & D40_MEM_LCSP0_ECNT_MASK;
|
||||
/* 16 LSBs address of the current element */
|
||||
lli->lcsp02 |= data & D40_MEM_LCSP0_SPTR_MASK;
|
||||
/* 16 MSBs address of the current element */
|
||||
lli->lcsp13 |= data & D40_MEM_LCSP1_SPTR_MASK;
|
||||
|
||||
if (addr_inc)
|
||||
lli->lcsp13 |= D40_MEM_LCSP1_SCFG_INCR_MASK;
|
||||
|
||||
lli->lcsp13 |= D40_MEM_LCSP3_DTCP_MASK;
|
||||
/* If this scatter list entry is the last one, no next link */
|
||||
lli->lcsp13 |= (lli_next_off << D40_MEM_LCSP1_SLOS_POS) &
|
||||
D40_MEM_LCSP1_SLOS_MASK;
|
||||
|
||||
if (term_int)
|
||||
lli->lcsp13 |= D40_MEM_LCSP1_SCFG_TIM_MASK;
|
||||
else
|
||||
lli->lcsp13 &= ~D40_MEM_LCSP1_SCFG_TIM_MASK;
|
||||
}
|
||||
|
||||
int d40_log_sg_to_dev(struct d40_lcla_elem *lcla,
|
||||
struct scatterlist *sg,
|
||||
int sg_len,
|
||||
struct d40_log_lli_bidir *lli,
|
||||
struct d40_def_lcsp *lcsp,
|
||||
u32 src_data_width,
|
||||
u32 dst_data_width,
|
||||
enum dma_data_direction direction,
|
||||
bool term_int, dma_addr_t dev_addr, int max_len,
|
||||
int llis_per_log)
|
||||
{
|
||||
int total_size = 0;
|
||||
struct scatterlist *current_sg = sg;
|
||||
int i;
|
||||
u32 next_lli_off_dst;
|
||||
u32 next_lli_off_src;
|
||||
|
||||
next_lli_off_src = 0;
|
||||
next_lli_off_dst = 0;
|
||||
|
||||
for_each_sg(sg, current_sg, sg_len, i) {
|
||||
total_size += sg_dma_len(current_sg);
|
||||
|
||||
/*
|
||||
* If this scatter list entry is the last one or
|
||||
* max length, terminate link.
|
||||
*/
|
||||
if (sg_len - 1 == i || ((i+1) % max_len == 0)) {
|
||||
next_lli_off_src = 0;
|
||||
next_lli_off_dst = 0;
|
||||
} else {
|
||||
if (next_lli_off_dst == 0 &&
|
||||
next_lli_off_src == 0) {
|
||||
/* The first lli will be at next_lli_off */
|
||||
next_lli_off_dst = (lcla->dst_id *
|
||||
llis_per_log + 1);
|
||||
next_lli_off_src = (lcla->src_id *
|
||||
llis_per_log + 1);
|
||||
} else {
|
||||
next_lli_off_dst++;
|
||||
next_lli_off_src++;
|
||||
}
|
||||
}
|
||||
|
||||
if (direction == DMA_TO_DEVICE) {
|
||||
d40_log_fill_lli(&lli->src[i],
|
||||
sg_phys(current_sg),
|
||||
sg_dma_len(current_sg),
|
||||
next_lli_off_src,
|
||||
lcsp->lcsp1, src_data_width,
|
||||
term_int && !next_lli_off_src,
|
||||
true);
|
||||
d40_log_fill_lli(&lli->dst[i],
|
||||
dev_addr,
|
||||
sg_dma_len(current_sg),
|
||||
next_lli_off_dst,
|
||||
lcsp->lcsp3, dst_data_width,
|
||||
/* No next == terminal interrupt */
|
||||
term_int && !next_lli_off_dst,
|
||||
false);
|
||||
} else {
|
||||
d40_log_fill_lli(&lli->dst[i],
|
||||
sg_phys(current_sg),
|
||||
sg_dma_len(current_sg),
|
||||
next_lli_off_dst,
|
||||
lcsp->lcsp3, dst_data_width,
|
||||
/* No next == terminal interrupt */
|
||||
term_int && !next_lli_off_dst,
|
||||
true);
|
||||
d40_log_fill_lli(&lli->src[i],
|
||||
dev_addr,
|
||||
sg_dma_len(current_sg),
|
||||
next_lli_off_src,
|
||||
lcsp->lcsp1, src_data_width,
|
||||
term_int && !next_lli_off_src,
|
||||
false);
|
||||
}
|
||||
}
|
||||
return total_size;
|
||||
}
|
||||
|
||||
int d40_log_sg_to_lli(int lcla_id,
|
||||
struct scatterlist *sg,
|
||||
int sg_len,
|
||||
struct d40_log_lli *lli_sg,
|
||||
u32 lcsp13, /* src or dst*/
|
||||
u32 data_width,
|
||||
bool term_int, int max_len, int llis_per_log)
|
||||
{
|
||||
int total_size = 0;
|
||||
struct scatterlist *current_sg = sg;
|
||||
int i;
|
||||
u32 next_lli_off = 0;
|
||||
|
||||
for_each_sg(sg, current_sg, sg_len, i) {
|
||||
total_size += sg_dma_len(current_sg);
|
||||
|
||||
/*
|
||||
* If this scatter list entry is the last one or
|
||||
* max length, terminate link.
|
||||
*/
|
||||
if (sg_len - 1 == i || ((i+1) % max_len == 0))
|
||||
next_lli_off = 0;
|
||||
else {
|
||||
if (next_lli_off == 0)
|
||||
/* The first lli will be at next_lli_off */
|
||||
next_lli_off = lcla_id * llis_per_log + 1;
|
||||
else
|
||||
next_lli_off++;
|
||||
}
|
||||
|
||||
d40_log_fill_lli(&lli_sg[i],
|
||||
sg_phys(current_sg),
|
||||
sg_dma_len(current_sg),
|
||||
next_lli_off,
|
||||
lcsp13, data_width,
|
||||
term_int && !next_lli_off,
|
||||
true);
|
||||
}
|
||||
return total_size;
|
||||
}
|
||||
|
||||
void d40_log_lli_write(struct d40_log_lli_full *lcpa,
|
||||
struct d40_log_lli *lcla_src,
|
||||
struct d40_log_lli *lcla_dst,
|
||||
struct d40_log_lli *lli_dst,
|
||||
struct d40_log_lli *lli_src,
|
||||
int llis_per_log)
|
||||
{
|
||||
u32 slos = 0;
|
||||
u32 dlos = 0;
|
||||
int i;
|
||||
|
||||
lcpa->lcsp0 = lli_src->lcsp02;
|
||||
lcpa->lcsp1 = lli_src->lcsp13;
|
||||
lcpa->lcsp2 = lli_dst->lcsp02;
|
||||
lcpa->lcsp3 = lli_dst->lcsp13;
|
||||
|
||||
slos = lli_src->lcsp13 & D40_MEM_LCSP1_SLOS_MASK;
|
||||
dlos = lli_dst->lcsp13 & D40_MEM_LCSP3_DLOS_MASK;
|
||||
|
||||
for (i = 0; (i < llis_per_log) && slos && dlos; i++) {
|
||||
writel(lli_src[i+1].lcsp02, &lcla_src[i].lcsp02);
|
||||
writel(lli_src[i+1].lcsp13, &lcla_src[i].lcsp13);
|
||||
writel(lli_dst[i+1].lcsp02, &lcla_dst[i].lcsp02);
|
||||
writel(lli_dst[i+1].lcsp13, &lcla_dst[i].lcsp13);
|
||||
|
||||
slos = lli_src[i+1].lcsp13 & D40_MEM_LCSP1_SLOS_MASK;
|
||||
dlos = lli_dst[i+1].lcsp13 & D40_MEM_LCSP3_DLOS_MASK;
|
||||
}
|
||||
}
|
354
drivers/dma/ste_dma40_ll.h
Normal file
354
drivers/dma/ste_dma40_ll.h
Normal file
|
@ -0,0 +1,354 @@
|
|||
/*
|
||||
* driver/dma/ste_dma40_ll.h
|
||||
*
|
||||
* Copyright (C) ST-Ericsson 2007-2010
|
||||
* License terms: GNU General Public License (GPL) version 2
|
||||
* Author: Per Friden <per.friden@stericsson.com>
|
||||
* Author: Jonas Aaberg <jonas.aberg@stericsson.com>
|
||||
*/
|
||||
#ifndef STE_DMA40_LL_H
|
||||
#define STE_DMA40_LL_H
|
||||
|
||||
#define D40_DREG_PCBASE 0x400
|
||||
#define D40_DREG_PCDELTA (8 * 4)
|
||||
#define D40_LLI_ALIGN 16 /* LLI alignment must be 16 bytes. */
|
||||
|
||||
#define D40_TYPE_TO_GROUP(type) (type / 16)
|
||||
#define D40_TYPE_TO_EVENT(type) (type % 16)
|
||||
|
||||
/* Most bits of the CFG register are the same in log as in phy mode */
|
||||
#define D40_SREG_CFG_MST_POS 15
|
||||
#define D40_SREG_CFG_TIM_POS 14
|
||||
#define D40_SREG_CFG_EIM_POS 13
|
||||
#define D40_SREG_CFG_LOG_INCR_POS 12
|
||||
#define D40_SREG_CFG_PHY_PEN_POS 12
|
||||
#define D40_SREG_CFG_PSIZE_POS 10
|
||||
#define D40_SREG_CFG_ESIZE_POS 8
|
||||
#define D40_SREG_CFG_PRI_POS 7
|
||||
#define D40_SREG_CFG_LBE_POS 6
|
||||
#define D40_SREG_CFG_LOG_GIM_POS 5
|
||||
#define D40_SREG_CFG_LOG_MFU_POS 4
|
||||
#define D40_SREG_CFG_PHY_TM_POS 4
|
||||
#define D40_SREG_CFG_PHY_EVTL_POS 0
|
||||
|
||||
|
||||
/* Standard channel parameters - basic mode (element register) */
|
||||
#define D40_SREG_ELEM_PHY_ECNT_POS 16
|
||||
#define D40_SREG_ELEM_PHY_EIDX_POS 0
|
||||
|
||||
#define D40_SREG_ELEM_PHY_ECNT_MASK (0xFFFF << D40_SREG_ELEM_PHY_ECNT_POS)
|
||||
|
||||
/* Standard channel parameters - basic mode (Link register) */
|
||||
#define D40_SREG_LNK_PHY_TCP_POS 0
|
||||
#define D40_SREG_LNK_PHY_LMP_POS 1
|
||||
#define D40_SREG_LNK_PHY_PRE_POS 2
|
||||
/*
|
||||
* Source destination link address. Contains the
|
||||
* 29-bit byte word aligned address of the reload area.
|
||||
*/
|
||||
#define D40_SREG_LNK_PHYS_LNK_MASK 0xFFFFFFF8UL
|
||||
|
||||
/* Standard basic channel logical mode */
|
||||
|
||||
/* Element register */
|
||||
#define D40_SREG_ELEM_LOG_ECNT_POS 16
|
||||
#define D40_SREG_ELEM_LOG_LIDX_POS 8
|
||||
#define D40_SREG_ELEM_LOG_LOS_POS 1
|
||||
#define D40_SREG_ELEM_LOG_TCP_POS 0
|
||||
|
||||
#define D40_SREG_ELEM_LOG_LIDX_MASK (0xFF << D40_SREG_ELEM_LOG_LIDX_POS)
|
||||
|
||||
/* Link register */
|
||||
#define D40_DEACTIVATE_EVENTLINE 0x0
|
||||
#define D40_ACTIVATE_EVENTLINE 0x1
|
||||
#define D40_EVENTLINE_POS(i) (2 * i)
|
||||
#define D40_EVENTLINE_MASK(i) (0x3 << D40_EVENTLINE_POS(i))
|
||||
|
||||
/* Standard basic channel logical params in memory */
|
||||
|
||||
/* LCSP0 */
|
||||
#define D40_MEM_LCSP0_ECNT_POS 16
|
||||
#define D40_MEM_LCSP0_SPTR_POS 0
|
||||
|
||||
#define D40_MEM_LCSP0_ECNT_MASK (0xFFFF << D40_MEM_LCSP0_ECNT_POS)
|
||||
#define D40_MEM_LCSP0_SPTR_MASK (0xFFFF << D40_MEM_LCSP0_SPTR_POS)
|
||||
|
||||
/* LCSP1 */
|
||||
#define D40_MEM_LCSP1_SPTR_POS 16
|
||||
#define D40_MEM_LCSP1_SCFG_MST_POS 15
|
||||
#define D40_MEM_LCSP1_SCFG_TIM_POS 14
|
||||
#define D40_MEM_LCSP1_SCFG_EIM_POS 13
|
||||
#define D40_MEM_LCSP1_SCFG_INCR_POS 12
|
||||
#define D40_MEM_LCSP1_SCFG_PSIZE_POS 10
|
||||
#define D40_MEM_LCSP1_SCFG_ESIZE_POS 8
|
||||
#define D40_MEM_LCSP1_SLOS_POS 1
|
||||
#define D40_MEM_LCSP1_STCP_POS 0
|
||||
|
||||
#define D40_MEM_LCSP1_SPTR_MASK (0xFFFF << D40_MEM_LCSP1_SPTR_POS)
|
||||
#define D40_MEM_LCSP1_SCFG_TIM_MASK (0x1 << D40_MEM_LCSP1_SCFG_TIM_POS)
|
||||
#define D40_MEM_LCSP1_SCFG_INCR_MASK (0x1 << D40_MEM_LCSP1_SCFG_INCR_POS)
|
||||
#define D40_MEM_LCSP1_SCFG_PSIZE_MASK (0x3 << D40_MEM_LCSP1_SCFG_PSIZE_POS)
|
||||
#define D40_MEM_LCSP1_SLOS_MASK (0x7F << D40_MEM_LCSP1_SLOS_POS)
|
||||
#define D40_MEM_LCSP1_STCP_MASK (0x1 << D40_MEM_LCSP1_STCP_POS)
|
||||
|
||||
/* LCSP2 */
|
||||
#define D40_MEM_LCSP2_ECNT_POS 16
|
||||
|
||||
#define D40_MEM_LCSP2_ECNT_MASK (0xFFFF << D40_MEM_LCSP2_ECNT_POS)
|
||||
|
||||
/* LCSP3 */
|
||||
#define D40_MEM_LCSP3_DCFG_MST_POS 15
|
||||
#define D40_MEM_LCSP3_DCFG_TIM_POS 14
|
||||
#define D40_MEM_LCSP3_DCFG_EIM_POS 13
|
||||
#define D40_MEM_LCSP3_DCFG_INCR_POS 12
|
||||
#define D40_MEM_LCSP3_DCFG_PSIZE_POS 10
|
||||
#define D40_MEM_LCSP3_DCFG_ESIZE_POS 8
|
||||
#define D40_MEM_LCSP3_DLOS_POS 1
|
||||
#define D40_MEM_LCSP3_DTCP_POS 0
|
||||
|
||||
#define D40_MEM_LCSP3_DLOS_MASK (0x7F << D40_MEM_LCSP3_DLOS_POS)
|
||||
#define D40_MEM_LCSP3_DTCP_MASK (0x1 << D40_MEM_LCSP3_DTCP_POS)
|
||||
|
||||
|
||||
/* Standard channel parameter register offsets */
|
||||
#define D40_CHAN_REG_SSCFG 0x00
|
||||
#define D40_CHAN_REG_SSELT 0x04
|
||||
#define D40_CHAN_REG_SSPTR 0x08
|
||||
#define D40_CHAN_REG_SSLNK 0x0C
|
||||
#define D40_CHAN_REG_SDCFG 0x10
|
||||
#define D40_CHAN_REG_SDELT 0x14
|
||||
#define D40_CHAN_REG_SDPTR 0x18
|
||||
#define D40_CHAN_REG_SDLNK 0x1C
|
||||
|
||||
/* DMA Register Offsets */
|
||||
#define D40_DREG_GCC 0x000
|
||||
#define D40_DREG_PRTYP 0x004
|
||||
#define D40_DREG_PRSME 0x008
|
||||
#define D40_DREG_PRSMO 0x00C
|
||||
#define D40_DREG_PRMSE 0x010
|
||||
#define D40_DREG_PRMSO 0x014
|
||||
#define D40_DREG_PRMOE 0x018
|
||||
#define D40_DREG_PRMOO 0x01C
|
||||
#define D40_DREG_LCPA 0x020
|
||||
#define D40_DREG_LCLA 0x024
|
||||
#define D40_DREG_ACTIVE 0x050
|
||||
#define D40_DREG_ACTIVO 0x054
|
||||
#define D40_DREG_FSEB1 0x058
|
||||
#define D40_DREG_FSEB2 0x05C
|
||||
#define D40_DREG_PCMIS 0x060
|
||||
#define D40_DREG_PCICR 0x064
|
||||
#define D40_DREG_PCTIS 0x068
|
||||
#define D40_DREG_PCEIS 0x06C
|
||||
#define D40_DREG_LCMIS0 0x080
|
||||
#define D40_DREG_LCMIS1 0x084
|
||||
#define D40_DREG_LCMIS2 0x088
|
||||
#define D40_DREG_LCMIS3 0x08C
|
||||
#define D40_DREG_LCICR0 0x090
|
||||
#define D40_DREG_LCICR1 0x094
|
||||
#define D40_DREG_LCICR2 0x098
|
||||
#define D40_DREG_LCICR3 0x09C
|
||||
#define D40_DREG_LCTIS0 0x0A0
|
||||
#define D40_DREG_LCTIS1 0x0A4
|
||||
#define D40_DREG_LCTIS2 0x0A8
|
||||
#define D40_DREG_LCTIS3 0x0AC
|
||||
#define D40_DREG_LCEIS0 0x0B0
|
||||
#define D40_DREG_LCEIS1 0x0B4
|
||||
#define D40_DREG_LCEIS2 0x0B8
|
||||
#define D40_DREG_LCEIS3 0x0BC
|
||||
#define D40_DREG_STFU 0xFC8
|
||||
#define D40_DREG_ICFG 0xFCC
|
||||
#define D40_DREG_PERIPHID0 0xFE0
|
||||
#define D40_DREG_PERIPHID1 0xFE4
|
||||
#define D40_DREG_PERIPHID2 0xFE8
|
||||
#define D40_DREG_PERIPHID3 0xFEC
|
||||
#define D40_DREG_CELLID0 0xFF0
|
||||
#define D40_DREG_CELLID1 0xFF4
|
||||
#define D40_DREG_CELLID2 0xFF8
|
||||
#define D40_DREG_CELLID3 0xFFC
|
||||
|
||||
/* LLI related structures */
|
||||
|
||||
/**
|
||||
* struct d40_phy_lli - The basic configration register for each physical
|
||||
* channel.
|
||||
*
|
||||
* @reg_cfg: The configuration register.
|
||||
* @reg_elt: The element register.
|
||||
* @reg_ptr: The pointer register.
|
||||
* @reg_lnk: The link register.
|
||||
*
|
||||
* These registers are set up for both physical and logical transfers
|
||||
* Note that the bit in each register means differently in logical and
|
||||
* physical(standard) mode.
|
||||
*
|
||||
* This struct must be 16 bytes aligned, and only contain physical registers
|
||||
* since it will be directly accessed by the DMA.
|
||||
*/
|
||||
struct d40_phy_lli {
|
||||
u32 reg_cfg;
|
||||
u32 reg_elt;
|
||||
u32 reg_ptr;
|
||||
u32 reg_lnk;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct d40_phy_lli_bidir - struct for a transfer.
|
||||
*
|
||||
* @src: Register settings for src channel.
|
||||
* @dst: Register settings for dst channel.
|
||||
* @dst_addr: Physical destination address.
|
||||
* @src_addr: Physical source address.
|
||||
*
|
||||
* All DMA transfers have a source and a destination.
|
||||
*/
|
||||
|
||||
struct d40_phy_lli_bidir {
|
||||
struct d40_phy_lli *src;
|
||||
struct d40_phy_lli *dst;
|
||||
dma_addr_t dst_addr;
|
||||
dma_addr_t src_addr;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* struct d40_log_lli - logical lli configuration
|
||||
*
|
||||
* @lcsp02: Either maps to register lcsp0 if src or lcsp2 if dst.
|
||||
* @lcsp13: Either maps to register lcsp1 if src or lcsp3 if dst.
|
||||
*
|
||||
* This struct must be 8 bytes aligned since it will be accessed directy by
|
||||
* the DMA. Never add any none hw mapped registers to this struct.
|
||||
*/
|
||||
|
||||
struct d40_log_lli {
|
||||
u32 lcsp02;
|
||||
u32 lcsp13;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct d40_log_lli_bidir - For both src and dst
|
||||
*
|
||||
* @src: pointer to src lli configuration.
|
||||
* @dst: pointer to dst lli configuration.
|
||||
*
|
||||
* You always have a src and a dst when doing DMA transfers.
|
||||
*/
|
||||
|
||||
struct d40_log_lli_bidir {
|
||||
struct d40_log_lli *src;
|
||||
struct d40_log_lli *dst;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct d40_log_lli_full - LCPA layout
|
||||
*
|
||||
* @lcsp0: Logical Channel Standard Param 0 - Src.
|
||||
* @lcsp1: Logical Channel Standard Param 1 - Src.
|
||||
* @lcsp2: Logical Channel Standard Param 2 - Dst.
|
||||
* @lcsp3: Logical Channel Standard Param 3 - Dst.
|
||||
*
|
||||
* This struct maps to LCPA physical memory layout. Must map to
|
||||
* the hw.
|
||||
*/
|
||||
struct d40_log_lli_full {
|
||||
u32 lcsp0;
|
||||
u32 lcsp1;
|
||||
u32 lcsp2;
|
||||
u32 lcsp3;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct d40_def_lcsp - Default LCSP1 and LCSP3 settings
|
||||
*
|
||||
* @lcsp3: The default configuration for dst.
|
||||
* @lcsp1: The default configuration for src.
|
||||
*/
|
||||
struct d40_def_lcsp {
|
||||
u32 lcsp3;
|
||||
u32 lcsp1;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct d40_lcla_elem - Info for one LCA element.
|
||||
*
|
||||
* @src_id: logical channel src id
|
||||
* @dst_id: logical channel dst id
|
||||
* @src: LCPA formated src parameters
|
||||
* @dst: LCPA formated dst parameters
|
||||
*
|
||||
*/
|
||||
struct d40_lcla_elem {
|
||||
int src_id;
|
||||
int dst_id;
|
||||
struct d40_log_lli *src;
|
||||
struct d40_log_lli *dst;
|
||||
};
|
||||
|
||||
/* Physical channels */
|
||||
|
||||
void d40_phy_cfg(struct stedma40_chan_cfg *cfg,
|
||||
u32 *src_cfg, u32 *dst_cfg, bool is_log);
|
||||
|
||||
void d40_log_cfg(struct stedma40_chan_cfg *cfg,
|
||||
u32 *lcsp1, u32 *lcsp2);
|
||||
|
||||
int d40_phy_sg_to_lli(struct scatterlist *sg,
|
||||
int sg_len,
|
||||
dma_addr_t target,
|
||||
struct d40_phy_lli *lli,
|
||||
dma_addr_t lli_phys,
|
||||
u32 reg_cfg,
|
||||
u32 data_width,
|
||||
int psize,
|
||||
bool term_int);
|
||||
|
||||
int d40_phy_fill_lli(struct d40_phy_lli *lli,
|
||||
dma_addr_t data,
|
||||
u32 data_size,
|
||||
int psize,
|
||||
dma_addr_t next_lli,
|
||||
u32 reg_cfg,
|
||||
bool term_int,
|
||||
u32 data_width,
|
||||
bool is_device);
|
||||
|
||||
void d40_phy_lli_write(void __iomem *virtbase,
|
||||
u32 phy_chan_num,
|
||||
struct d40_phy_lli *lli_dst,
|
||||
struct d40_phy_lli *lli_src);
|
||||
|
||||
/* Logical channels */
|
||||
|
||||
void d40_log_fill_lli(struct d40_log_lli *lli,
|
||||
dma_addr_t data, u32 data_size,
|
||||
u32 lli_next_off, u32 reg_cfg,
|
||||
u32 data_width,
|
||||
bool term_int, bool addr_inc);
|
||||
|
||||
int d40_log_sg_to_dev(struct d40_lcla_elem *lcla,
|
||||
struct scatterlist *sg,
|
||||
int sg_len,
|
||||
struct d40_log_lli_bidir *lli,
|
||||
struct d40_def_lcsp *lcsp,
|
||||
u32 src_data_width,
|
||||
u32 dst_data_width,
|
||||
enum dma_data_direction direction,
|
||||
bool term_int, dma_addr_t dev_addr, int max_len,
|
||||
int llis_per_log);
|
||||
|
||||
void d40_log_lli_write(struct d40_log_lli_full *lcpa,
|
||||
struct d40_log_lli *lcla_src,
|
||||
struct d40_log_lli *lcla_dst,
|
||||
struct d40_log_lli *lli_dst,
|
||||
struct d40_log_lli *lli_src,
|
||||
int llis_per_log);
|
||||
|
||||
int d40_log_sg_to_lli(int lcla_id,
|
||||
struct scatterlist *sg,
|
||||
int sg_len,
|
||||
struct d40_log_lli *lli_sg,
|
||||
u32 lcsp13, /* src or dst*/
|
||||
u32 data_width,
|
||||
bool term_int, int max_len, int llis_per_log);
|
||||
|
||||
#endif /* STE_DMA40_LLI_H */
|
Loading…
Reference in a new issue