297 lines
8.2 KiB
C
297 lines
8.2 KiB
C
/*
|
|
* Copyright (c) 2022 Intel Corporation.
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
#ifndef ZEPHYR_DRIVERS_DMA_DMA_DW_COMMON_H_
|
|
#define ZEPHYR_DRIVERS_DMA_DMA_DW_COMMON_H_
|
|
|
|
#include <zephyr/sys/atomic.h>
|
|
#include <zephyr/drivers/dma.h>
|
|
|
|
#ifdef __cplusplus
|
|
extern "C" {
|
|
#endif
|
|
|
|
#define MASK(b_hi, b_lo) \
|
|
(((1ULL << ((b_hi) - (b_lo) + 1ULL)) - 1ULL) << (b_lo))
|
|
#define SET_BIT(b, x) (((x) & 1) << (b))
|
|
#define SET_BITS(b_hi, b_lo, x) \
|
|
(((x) & ((1ULL << ((b_hi) - (b_lo) + 1ULL)) - 1ULL)) << (b_lo))
|
|
|
|
#define DW_MAX_CHAN 8
|
|
#define DW_CHAN_COUNT CONFIG_DMA_DW_CHANNEL_COUNT
|
|
#define DW_CH_SIZE 0x58
|
|
#define DW_CHAN_OFFSET(chan) (DW_CH_SIZE * chan)
|
|
#define DW_ADDR_MASK_32 BIT_MASK(32)
|
|
#define DW_ADDR_RIGHT_SHIFT 32
|
|
|
|
#define DW_SAR(chan) \
|
|
(0x0000 + DW_CHAN_OFFSET(chan))
|
|
#define DW_DAR(chan) \
|
|
(0x0008 + DW_CHAN_OFFSET(chan))
|
|
#define DW_LLP(chan) \
|
|
(0x0010 + DW_CHAN_OFFSET(chan))
|
|
#define DW_CTRL_LOW(chan) \
|
|
(0x0018 + DW_CHAN_OFFSET(chan))
|
|
#define DW_CTRL_HIGH(chan) \
|
|
(0x001C + DW_CHAN_OFFSET(chan))
|
|
#define DW_CFG_LOW(chan) \
|
|
(0x0040 + DW_CHAN_OFFSET(chan))
|
|
#define DW_CFG_HIGH(chan) \
|
|
(0x0044 + DW_CHAN_OFFSET(chan))
|
|
#define DW_DSR(chan) \
|
|
(0x0050 + DW_CHAN_OFFSET(chan))
|
|
|
|
#ifdef CONFIG_DMA_64BIT
|
|
#define DW_SAR_HI(chan) \
|
|
(0x0004 + DW_CHAN_OFFSET(chan))
|
|
#define DW_DAR_HI(chan) \
|
|
(0x000C + DW_CHAN_OFFSET(chan))
|
|
#endif
|
|
|
|
/* registers */
|
|
#define DW_RAW_TFR 0x02C0
|
|
#define DW_RAW_BLOCK 0x02C8
|
|
#define DW_RAW_SRC_TRAN 0x02D0
|
|
#define DW_RAW_DST_TRAN 0x02D8
|
|
#define DW_RAW_ERR 0x02E0
|
|
#define DW_STATUS_TFR 0x02E8
|
|
#define DW_STATUS_BLOCK 0x02F0
|
|
#define DW_STATUS_SRC_TRAN 0x02F8
|
|
#define DW_STATUS_DST_TRAN 0x0300
|
|
#define DW_STATUS_ERR 0x0308
|
|
#define DW_MASK_TFR 0x0310
|
|
#define DW_MASK_BLOCK 0x0318
|
|
#define DW_MASK_SRC_TRAN 0x0320
|
|
#define DW_MASK_DST_TRAN 0x0328
|
|
#define DW_MASK_ERR 0x0330
|
|
#define DW_CLEAR_TFR 0x0338
|
|
#define DW_CLEAR_BLOCK 0x0340
|
|
#define DW_CLEAR_SRC_TRAN 0x0348
|
|
#define DW_CLEAR_DST_TRAN 0x0350
|
|
#define DW_CLEAR_ERR 0x0358
|
|
#define DW_INTR_STATUS 0x0360
|
|
#define DW_DMA_CFG 0x0398
|
|
#define DW_DMA_CHAN_EN 0x03A0
|
|
#define DW_FIFO_PART0_LO 0x400
|
|
#define DW_FIFO_PART0_HI 0x404
|
|
#define DW_FIFO_PART1_LO 0x408
|
|
#define DW_FIFO_PART1_HI 0x40C
|
|
|
|
/* channel bits */
|
|
#define DW_CHAN_WRITE_EN_ALL MASK(2 * DW_MAX_CHAN - 1, DW_MAX_CHAN)
|
|
#define DW_CHAN_WRITE_EN(chan) BIT((chan) + DW_MAX_CHAN)
|
|
#define DW_CHAN_ALL MASK(DW_MAX_CHAN - 1, 0)
|
|
#define DW_CHAN(chan) BIT(chan)
|
|
#define DW_CHAN_MASK_ALL DW_CHAN_WRITE_EN_ALL
|
|
#define DW_CHAN_MASK(chan) DW_CHAN_WRITE_EN(chan)
|
|
#define DW_CHAN_UNMASK_ALL (DW_CHAN_WRITE_EN_ALL | DW_CHAN_ALL)
|
|
#define DW_CHAN_UNMASK(chan) (DW_CHAN_WRITE_EN(chan) | DW_CHAN(chan))
|
|
|
|
/* CFG_LO */
|
|
#define DW_CFGL_RELOAD_DST BIT(31)
|
|
#define DW_CFGL_RELOAD_SRC BIT(30)
|
|
#define DW_CFGL_DRAIN BIT(10) /* For Intel GPDMA variant only */
|
|
#define DW_CFGL_SRC_SW_HS BIT(10) /* For Synopsys variant only */
|
|
#define DW_CFGL_DST_SW_HS BIT(11) /* For Synopsys variant only */
|
|
#define DW_CFGL_FIFO_EMPTY BIT(9)
|
|
#define DW_CFGL_SUSPEND BIT(8)
|
|
#define DW_CFGL_CTL_HI_UPD_EN BIT(5)
|
|
|
|
/* CFG_HI */
|
|
#define DW_CFGH_DST_PER_EXT(x) SET_BITS(31, 30, x)
|
|
#define DW_CFGH_SRC_PER_EXT(x) SET_BITS(29, 28, x)
|
|
#define DW_CFGH_DST_PER(x) SET_BITS(7, 4, x)
|
|
#define DW_CFGH_SRC_PER(x) SET_BITS(3, 0, x)
|
|
#define DW_CFGH_DST(x) \
|
|
(DW_CFGH_DST_PER_EXT((x) >> 4) | DW_CFGH_DST_PER(x))
|
|
#define DW_CFGH_SRC(x) \
|
|
(DW_CFGH_SRC_PER_EXT((x) >> 4) | DW_CFGH_SRC_PER(x))
|
|
|
|
/* CTL_LO */
|
|
#define DW_CTLL_RELOAD_DST BIT(31)
|
|
#define DW_CTLL_RELOAD_SRC BIT(30)
|
|
#define DW_CTLL_LLP_S_EN BIT(28)
|
|
#define DW_CTLL_LLP_D_EN BIT(27)
|
|
#define DW_CTLL_SMS(x) SET_BIT(25, x)
|
|
#define DW_CTLL_DMS(x) SET_BIT(23, x)
|
|
#define DW_CTLL_FC_P2P SET_BITS(21, 20, 3)
|
|
#define DW_CTLL_FC_P2M SET_BITS(21, 20, 2)
|
|
#define DW_CTLL_FC_M2P SET_BITS(21, 20, 1)
|
|
#define DW_CTLL_FC_M2M SET_BITS(21, 20, 0)
|
|
#define DW_CTLL_D_SCAT_EN BIT(18)
|
|
#define DW_CTLL_S_GATH_EN BIT(17)
|
|
#define DW_CTLL_SRC_MSIZE(x) SET_BITS(16, 14, x)
|
|
#define DW_CTLL_DST_MSIZE(x) SET_BITS(13, 11, x)
|
|
#define DW_CTLL_SRC_FIX SET_BITS(10, 9, 2)
|
|
#define DW_CTLL_SRC_DEC SET_BITS(10, 9, 1)
|
|
#define DW_CTLL_SRC_INC SET_BITS(10, 9, 0)
|
|
#define DW_CTLL_DST_FIX SET_BITS(8, 7, 2)
|
|
#define DW_CTLL_DST_DEC SET_BITS(8, 7, 1)
|
|
#define DW_CTLL_DST_INC SET_BITS(8, 7, 0)
|
|
#define DW_CTLL_SRC_WIDTH(x) SET_BITS(6, 4, x)
|
|
#define DW_CTLL_DST_WIDTH(x) SET_BITS(3, 1, x)
|
|
#define DW_CTLL_INT_EN BIT(0)
|
|
#define DW_CTLL_SRC_WIDTH_MASK MASK(6, 4)
|
|
#define DW_CTLL_SRC_WIDTH_SHIFT 4
|
|
#define DW_CTLL_DST_WIDTH_MASK MASK(3, 1)
|
|
#define DW_CTLL_DST_WIDTH_SHIFT 1
|
|
|
|
/* CTL_HI */
|
|
#define DW_CTLH_CLASS(x) SET_BITS(31, 29, x)
|
|
#define DW_CTLH_WEIGHT(x) SET_BITS(28, 18, x)
|
|
#define DW_CTLH_DONE(x) SET_BIT(17, x)
|
|
#define DW_CTLH_BLOCK_TS_MASK MASK(16, 0)
|
|
|
|
/* DSR */
|
|
#define DW_DSR_DSC(x) SET_BITS(31, 20, x)
|
|
#define DW_DSR_DSI(x) SET_BITS(19, 0, x)
|
|
|
|
/* FIFO_PART */
|
|
#define DW_FIFO_SIZE 0x80
|
|
#define DW_FIFO_UPD BIT(26)
|
|
#define DW_FIFO_CHx(x) SET_BITS(25, 13, x)
|
|
#define DW_FIFO_CHy(x) SET_BITS(12, 0, x)
|
|
|
|
/* number of tries to wait for reset */
|
|
#define DW_DMA_CFG_TRIES 10000
|
|
|
|
/* channel drain timeout in microseconds */
|
|
#define DW_DMA_TIMEOUT 1333
|
|
|
|
/* min number of elems for config with irq disabled */
|
|
#define DW_DMA_CFG_NO_IRQ_MIN_ELEMS 3
|
|
|
|
#define DW_DMA_CHANNEL_REGISTER_OFFSET_END 0x50
|
|
#define DW_DMA_IP_REGISTER_OFFSET_END 0x418
|
|
#define DW_DMA_IP_REGISTER_OFFSET_START 0x2C0
|
|
|
|
/* linked list item address */
|
|
#define DW_DMA_LLI_ADDRESS(lli, dir) \
|
|
(((dir) == MEMORY_TO_PERIPHERAL) ? ((lli)->sar) : ((lli)->dar))
|
|
|
|
/* TODO: add FIFO sizes */
|
|
struct dw_chan_arbit_data {
|
|
uint16_t class;
|
|
uint16_t weight;
|
|
};
|
|
|
|
struct dw_drv_plat_data {
|
|
struct dw_chan_arbit_data chan[DW_CHAN_COUNT];
|
|
};
|
|
|
|
/* DMA descriptor used by HW */
|
|
struct dw_lli {
|
|
#ifdef CONFIG_DMA_64BIT
|
|
uint64_t sar;
|
|
uint64_t dar;
|
|
#else
|
|
uint32_t sar;
|
|
uint32_t dar;
|
|
#endif
|
|
uint32_t llp;
|
|
uint32_t ctrl_lo;
|
|
uint32_t ctrl_hi;
|
|
uint32_t sstat;
|
|
uint32_t dstat;
|
|
|
|
/* align to 32 bytes to not cross cache line
|
|
* in case of more than two items
|
|
*/
|
|
uint32_t reserved;
|
|
} __packed;
|
|
|
|
/* pointer data for DW DMA buffer */
|
|
struct dw_dma_ptr_data {
|
|
uint32_t current_ptr;
|
|
uint32_t start_ptr;
|
|
uint32_t end_ptr;
|
|
uint32_t hw_ptr;
|
|
uint32_t buffer_bytes;
|
|
};
|
|
|
|
/* State tracking for each channel */
|
|
enum dw_dma_state {
|
|
DW_DMA_IDLE,
|
|
DW_DMA_PREPARED,
|
|
DW_DMA_SUSPENDED,
|
|
DW_DMA_ACTIVE,
|
|
};
|
|
|
|
/* data for each DMA channel */
|
|
struct dw_dma_chan_data {
|
|
uint32_t direction;
|
|
enum dw_dma_state state;
|
|
struct dw_lli *lli; /* allocated array of LLI's */
|
|
uint32_t lli_count; /* number of lli's in the allocation */
|
|
struct dw_lli *lli_current; /* current LLI being used */
|
|
uint32_t cfg_lo;
|
|
uint32_t cfg_hi;
|
|
struct dw_dma_ptr_data ptr_data; /* pointer data */
|
|
dma_callback_t dma_blkcallback;
|
|
void *blkuser_data;
|
|
dma_callback_t dma_tfrcallback;
|
|
void *tfruser_data;
|
|
};
|
|
|
|
/* use array to get burst_elems for specific slot number setting.
|
|
* the relation between msize and burst_elems should be
|
|
* 2 ^ msize = burst_elems
|
|
*/
|
|
static const uint32_t burst_elems[] = {1, 2, 4, 8};
|
|
|
|
/* Device run time data */
|
|
struct dw_dma_dev_data {
|
|
struct dma_context dma_ctx;
|
|
struct dw_drv_plat_data *channel_data;
|
|
struct dw_dma_chan_data chan[DW_CHAN_COUNT];
|
|
struct dw_lli lli_pool[DW_CHAN_COUNT][CONFIG_DMA_DW_LLI_POOL_SIZE] __aligned(64);
|
|
|
|
ATOMIC_DEFINE(channels_atomic, DW_CHAN_COUNT);
|
|
};
|
|
|
|
/* Device constant configuration parameters */
|
|
struct dw_dma_dev_cfg {
|
|
uintptr_t base;
|
|
void (*irq_config)(void);
|
|
};
|
|
|
|
static ALWAYS_INLINE void dw_write(uintptr_t dma_base, uint32_t reg, uint32_t value)
|
|
{
|
|
*((volatile uint32_t *)(dma_base + reg)) = value;
|
|
}
|
|
|
|
static ALWAYS_INLINE uint32_t dw_read(uintptr_t dma_base, uint32_t reg)
|
|
{
|
|
return *((volatile uint32_t *)(dma_base + reg));
|
|
}
|
|
|
|
int dw_dma_setup(const struct device *dev);
|
|
|
|
int dw_dma_config(const struct device *dev, uint32_t channel,
|
|
struct dma_config *cfg);
|
|
|
|
int dw_dma_reload(const struct device *dev, uint32_t channel,
|
|
uint32_t src, uint32_t dst, size_t size);
|
|
|
|
int dw_dma_start(const struct device *dev, uint32_t channel);
|
|
|
|
int dw_dma_stop(const struct device *dev, uint32_t channel);
|
|
|
|
int dw_dma_suspend(const struct device *dev, uint32_t channel);
|
|
|
|
int dw_dma_resume(const struct device *dev, uint32_t channel);
|
|
|
|
void dw_dma_isr(const struct device *dev);
|
|
|
|
int dw_dma_get_status(const struct device *dev, uint32_t channel,
|
|
struct dma_status *stat);
|
|
|
|
#ifdef __cplusplus
|
|
}
|
|
#endif
|
|
|
|
#endif /* ZEPHYR_DRIVERS_DMA_DMA_DW_COMMON_H_ */
|