zephyr/drivers/dma/dma_dw_common.h

282 lines
7.6 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_CH_SIZE 0x58
#define DW_CHAN_OFFSET(chan) (DW_CH_SIZE * chan)
#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))
/* 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)
#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
/* 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_MAX_CHAN];
};
/* DMA descriptor used by HW */
struct dw_lli {
uint32_t sar;
uint32_t dar;
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};
#if CONFIG_DMA_HW_LLI
#define DW_DMA_BUFFER_PERIOD_COUNT 4
#else
#define DW_DMA_BUFFER_PERIOD_COUNT 2
#endif
/* 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_MAX_CHAN];
struct dw_lli lli_pool[DW_MAX_CHAN][CONFIG_DMA_DW_LLI_POOL_SIZE];
ATOMIC_DEFINE(channels_atomic, DW_MAX_CHAN);
};
/* Device constant configuration parameters */
struct dw_dma_dev_cfg {
uint32_t base;
void (*irq_config)(void);
};
static ALWAYS_INLINE void dw_write(uint32_t dma_base, uint32_t reg, uint32_t value)
{
*((volatile uint32_t *)(dma_base + reg)) = value;
}
static ALWAYS_INLINE uint32_t dw_read(uint32_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_ */