Browse Source
Adding dma driver source for designware axi dma controller Signed-off-by: Balsundar Ponnusamy <balsundar.ponnusamy@intel.com>pull/71174/head
5 changed files with 1004 additions and 0 deletions
@ -0,0 +1,53 @@
@@ -0,0 +1,53 @@
|
||||
# DesignWare DMA configuration options |
||||
|
||||
# Copyright (c) 2023 Intel Corporation |
||||
# SPDX-License-Identifier: Apache-2.0 |
||||
|
||||
config DMA_DW_AXI |
||||
bool "DesignWare AXI DMA driver" |
||||
default y |
||||
depends on DT_HAS_SNPS_DESIGNWARE_DMA_AXI_ENABLED |
||||
imply DMA_64BIT |
||||
help |
||||
DesignWare AXI DMA driver. |
||||
|
||||
if DMA_DW_AXI |
||||
|
||||
config DMA_DW_AXI_MAX_DESC |
||||
int "allocate number of lli descriptor" |
||||
default 10 |
||||
help |
||||
creates number of descriptor per channel in a statically allocated pool. |
||||
Each channel has its own dedicated pool. |
||||
|
||||
config DMA_DW_AXI_LLI_SUPPORT |
||||
bool "hardware supports linked list multi block transfer" |
||||
default y |
||||
help |
||||
This flag can be enabled if hardware support Linked List multi-block transfer |
||||
|
||||
config DMA_CHANNEL_STATUS_TIMEOUT |
||||
int "Channel status timeout" |
||||
default 1000 |
||||
help |
||||
Max timeout to abort or disable the channel |
||||
|
||||
config DMA_DW_AXI_MAX_BURST_TXN_LEN |
||||
int "max burst transaction length" |
||||
default 8 |
||||
help |
||||
set max number of source and destination data units supported |
||||
|
||||
config DMA_DW_AXI_DATA_WIDTH |
||||
int "data bus width" |
||||
default 64 |
||||
help |
||||
update this flag to change the axi master interface data width |
||||
|
||||
config DMA_DW_AXI_MAX_BLOCK_TS |
||||
int "max block size" |
||||
default 32767 |
||||
help |
||||
update this config to set maximum value of block size |
||||
|
||||
endif # DMA_DW_AXI |
@ -0,0 +1,912 @@
@@ -0,0 +1,912 @@
|
||||
/*
|
||||
* Copyright (c) 2023 Intel Corporation. |
||||
* |
||||
* SPDX-License-Identifier: Apache-2.0 |
||||
*/ |
||||
|
||||
#define DT_DRV_COMPAT snps_designware_dma_axi |
||||
|
||||
#include <zephyr/device.h> |
||||
#include <zephyr/drivers/dma.h> |
||||
#include <zephyr/logging/log.h> |
||||
#include <zephyr/drivers/reset.h> |
||||
#include <zephyr/cache.h> |
||||
|
||||
LOG_MODULE_REGISTER(dma_designware_axi, CONFIG_DMA_LOG_LEVEL); |
||||
|
||||
#define DEV_CFG(_dev) ((const struct dma_dw_axi_dev_cfg *)(_dev)->config) |
||||
#define DEV_DATA(_dev) ((struct dma_dw_axi_dev_data *const)(_dev)->data) |
||||
|
||||
/* mask for block transfer size */ |
||||
#define BLOCK_TS_MASK GENMASK(21, 0) |
||||
|
||||
/* blen : number of data units
|
||||
* blen will always be in power of two |
||||
* |
||||
* when blen is 1 then set msize to zero otherwise find most significant bit set |
||||
* and subtract two (as IP doesn't support number of data items 2) |
||||
*/ |
||||
#define DMA_DW_AXI_GET_MSIZE(blen) ((blen == 1) ? (0U) : (find_msb_set(blen) - 2U)) |
||||
|
||||
/* Common_Registers_Address_Block */ |
||||
#define DMA_DW_AXI_IDREG 0x0 |
||||
#define DMA_DW_AXI_COMPVERREG 0x08 |
||||
#define DMA_DW_AXI_CFGREG 0x10 |
||||
#define DMA_DW_AXI_CHENREG 0x18 |
||||
#define DMA_DW_AXI_INTSTATUSREG 0x30 |
||||
#define DMA_DW_AXI_COMMONREG_INTCLEARREG 0x38 |
||||
#define DMA_DW_AXI_COMMONREG_INTSTATUS_ENABLEREG 0x40 |
||||
#define DMA_DW_AXI_COMMONREG_INTSIGNAL_ENABLEREG 0x48 |
||||
#define DMA_DW_AXI_COMMONREG_INTSTATUSREG 0x50 |
||||
#define DMA_DW_AXI_RESETREG 0x58 |
||||
#define DMA_DW_AXI_LOWPOWER_CFGREG 0x60 |
||||
|
||||
/* Channel enable by setting ch_en and ch_en_we */ |
||||
#define CH_EN(chan) (BIT64(8 + chan) | BIT64(chan)) |
||||
/* Channel enable by setting ch_susp and ch_susp_we */ |
||||
#define CH_SUSP(chan) (BIT64(24 + chan) | BIT64(16 + chan)) |
||||
/* Channel enable by setting ch_abort and ch_abort_we */ |
||||
#define CH_ABORT(chan) (BIT64(40 + chan) | BIT64(32 + chan)) |
||||
|
||||
/* channel susp/resume write enable pos */ |
||||
#define CH_RESUME_WE(chan) (BIT64(24 + chan)) |
||||
/* channel resume bit pos */ |
||||
#define CH_RESUME(chan) (BIT64(16 + chan)) |
||||
|
||||
#define DMA_DW_AXI_CHAN_OFFSET(chan) (0x100 * chan) |
||||
|
||||
/* source address register for a channel */ |
||||
#define DMA_DW_AXI_CH_SAR(chan) (0x100 + DMA_DW_AXI_CHAN_OFFSET(chan)) |
||||
/* destination address register for a channel */ |
||||
#define DMA_DW_AXI_CH_DAR(chan) (0x108 + DMA_DW_AXI_CHAN_OFFSET(chan)) |
||||
/* block transfer size register for a channel */ |
||||
#define DMA_DW_AXI_CH_BLOCK_TS(chan) (0x110 + DMA_DW_AXI_CHAN_OFFSET(chan)) |
||||
/* channel control register */ |
||||
#define DMA_DW_AXI_CH_CTL(chan) (0x118 + DMA_DW_AXI_CHAN_OFFSET(chan)) |
||||
/* channel configuration register */ |
||||
#define DMA_DW_AXI_CH_CFG(chan) (0x120 + DMA_DW_AXI_CHAN_OFFSET(chan)) |
||||
/* linked list pointer register */ |
||||
#define DMA_DW_AXI_CH_LLP(chan) (0x128 + DMA_DW_AXI_CHAN_OFFSET(chan)) |
||||
/* channel status register */ |
||||
#define DMA_DW_AXI_CH_STATUSREG(chan) (0x130 + DMA_DW_AXI_CHAN_OFFSET(chan)) |
||||
/* channel software handshake source register */ |
||||
#define DMA_DW_AXI_CH_SWHSSRCREG(chan) (0x138 + DMA_DW_AXI_CHAN_OFFSET(chan)) |
||||
/* channel software handshake destination register */ |
||||
#define DMA_DW_AXI_CH_SWHSDSTREG(chan) (0x140 + DMA_DW_AXI_CHAN_OFFSET(chan)) |
||||
/* channel block transfer resume request register */ |
||||
#define DMA_DW_AXI_CH_BLK_TFR_RESUMEREQREG(chan) (0x148 + DMA_DW_AXI_CHAN_OFFSET(chan)) |
||||
/* channel AXI ID rester */ |
||||
#define DMA_DW_AXI_CH_AXI_IDREG(chan) (0x150 + DMA_DW_AXI_CHAN_OFFSET(chan)) |
||||
/* channel AXI QOS register */ |
||||
#define DMA_DW_AXI_CH_AXI_QOSREG(chan) (0x158 + DMA_DW_AXI_CHAN_OFFSET(chan)) |
||||
/* channel interrupt status enable register */ |
||||
#define DMA_DW_AXI_CH_INTSTATUS_ENABLEREG(chan) (0x180 + DMA_DW_AXI_CHAN_OFFSET(chan)) |
||||
/* channel interrupt status register */ |
||||
#define DMA_DW_AXI_CH_INTSTATUS(chan) (0x188 + DMA_DW_AXI_CHAN_OFFSET(chan)) |
||||
/* channel interrupt signal enable register */ |
||||
#define DMA_DW_AXI_CH_INTSIGNAL_ENABLEREG(chan) (0x190 + DMA_DW_AXI_CHAN_OFFSET(chan)) |
||||
/* channel interrupt clear register */ |
||||
#define DMA_DW_AXI_CH_INTCLEARREG(chan) (0x198 + DMA_DW_AXI_CHAN_OFFSET(chan)) |
||||
|
||||
/* bitfield configuration for multi-block transfer */ |
||||
#define DMA_DW_AXI_CFG_SRC_MULTBLK_TYPE(x) FIELD_PREP(GENMASK64(1, 0), x) |
||||
#define DMA_DW_AXI_CFG_DST_MULTBLK_TYPE(x) FIELD_PREP(GENMASK64(3, 2), x) |
||||
|
||||
/* bitfield configuration to assign handshaking interface to source and destination */ |
||||
#define DMA_DW_AXI_CFG_SRC_PER(x) FIELD_PREP(GENMASK64(9, 4), x) |
||||
#define DMA_DW_AXI_CFG_DST_PER(x) FIELD_PREP(GENMASK64(16, 11), x) |
||||
|
||||
/* bitfield configuration for transfer type and flow controller */ |
||||
#define DMA_DW_AXI_CFG_TT_FC(x) FIELD_PREP(GENMASK64(34, 32), x) |
||||
|
||||
#define DMA_DW_AXI_CFG_HW_HS_SRC_BIT_POS 35 |
||||
#define DMA_DW_AXI_CFG_HW_HS_DST_BIT_POS 36 |
||||
|
||||
#define DMA_DW_AXI_CFG_PRIORITY(x) FIELD_PREP(GENMASK64(51, 47), x) |
||||
|
||||
/* descriptor valid or not */ |
||||
#define DMA_DW_AXI_CTL_LLI_VALID BIT64(63) |
||||
/* descriptor is last or not in a link */ |
||||
#define DMA_DW_AXI_CTL_LLI_LAST BIT64(62) |
||||
/* interrupt on completion of block transfer */ |
||||
#define DMA_DW_AXI_CTL_IOC_BLK_TFR BIT64(58) |
||||
/* source status enable bit */ |
||||
#define DMA_DW_AXI_CTL_SRC_STAT_EN BIT64(56) |
||||
/* destination status enable bit */ |
||||
#define DMA_DW_AXI_CTL_DST_STAT_EN BIT64(57) |
||||
/* source burst length enable */ |
||||
#define DMA_DW_AXI_CTL_ARLEN_EN BIT64(38) |
||||
/* source burst length(considered when corresponding enable bit is set) */ |
||||
#define DMA_DW_AXI_CTL_ARLEN(x) FIELD_PREP(GENMASK64(46, 39), x) |
||||
/* destination burst length enable */ |
||||
#define DMA_DW_AXI_CTL_AWLEN_EN BIT64(47) |
||||
/* destination burst length(considered when corresponding enable bit is set) */ |
||||
#define DMA_DW_AXI_CTL_AWLEN(x) FIELD_PREP(GENMASK64(55, 48), x) |
||||
|
||||
/* source burst transaction length */ |
||||
#define DMA_DW_AXI_CTL_SRC_MSIZE(x) FIELD_PREP(GENMASK64(17, 14), x) |
||||
/* destination burst transaction length */ |
||||
#define DMA_DW_AXI_CTL_DST_MSIZE(x) FIELD_PREP(GENMASK64(21, 18), x) |
||||
/* source transfer width */ |
||||
#define DMA_DW_AXI_CTL_SRC_WIDTH(x) FIELD_PREP(GENMASK64(10, 8), x) |
||||
/* destination transfer width */ |
||||
#define DMA_DW_AXI_CTL_DST_WIDTH(x) FIELD_PREP(GENMASK64(13, 11), x) |
||||
|
||||
/* mask all the interrupts */ |
||||
#define DMA_DW_AXI_IRQ_NONE 0 |
||||
/* enable block completion transfer interrupt */ |
||||
#define DMA_DW_AXI_IRQ_BLOCK_TFR BIT64(0) |
||||
/* enable transfer completion interrupt */ |
||||
#define DMA_DW_AXI_IRQ_DMA_TFR BIT64(1) |
||||
/* enable interrupts on any dma transfer error */ |
||||
#define DMA_DW_AXI_IRQ_ALL_ERR (GENMASK64(14, 5) | GENMASK64(21, 16)) |
||||
|
||||
/* global enable bit for dma controller */ |
||||
#define DMA_DW_AXI_CFG_EN BIT64(0) |
||||
/* global enable bit for interrupt */ |
||||
#define DMA_DW_AXI_CFG_INT_EN BIT64(1) |
||||
|
||||
/* descriptor used by dw axi dma controller*/ |
||||
struct dma_lli { |
||||
uint64_t sar; |
||||
uint64_t dar; |
||||
uint32_t block_ts_lo; |
||||
uint32_t reserved; |
||||
uint64_t llp; |
||||
uint64_t ctl; |
||||
uint32_t sstat; |
||||
uint32_t dstat; |
||||
uint64_t llp_status; |
||||
uint64_t reserved1; |
||||
} __aligned(64); |
||||
|
||||
/* status of the channel */ |
||||
enum dma_dw_axi_ch_state { |
||||
DMA_DW_AXI_CH_IDLE, |
||||
DMA_DW_AXI_CH_SUSPENDED, |
||||
DMA_DW_AXI_CH_ACTIVE, |
||||
DMA_DW_AXI_CH_PREPARED, |
||||
}; |
||||
|
||||
/* source and destination transfer width */ |
||||
enum dma_dw_axi_ch_width { |
||||
BITS_8, |
||||
BITS_16, |
||||
BITS_32, |
||||
BITS_64, |
||||
BITS_128, |
||||
BITS_256, |
||||
BITS_512, |
||||
}; |
||||
|
||||
/* transfer direction and flow controller */ |
||||
enum dma_dw_axi_tt_fc { |
||||
M2M_DMAC, |
||||
M2P_DMAC, |
||||
P2M_DMAC, |
||||
P2P_DMAC, |
||||
P2M_SRC, |
||||
P2P_SRC, |
||||
M2P_DST, |
||||
P2P_DST, |
||||
}; |
||||
|
||||
/* type of multi-block transfer */ |
||||
enum dma_dw_axi_multi_blk_type { |
||||
MULTI_BLK_CONTIGUOUS, |
||||
MULTI_BLK_RELOAD, |
||||
MULTI_BLK_SHADOW_REG, |
||||
MULTI_BLK_LLI, |
||||
}; |
||||
|
||||
/* dma driver channel specific information */ |
||||
struct dma_dw_axi_ch_data { |
||||
/* lli descriptor base */ |
||||
struct dma_lli *lli_desc_base; |
||||
/* lli current descriptor */ |
||||
struct dma_lli *lli_desc_current; |
||||
/* dma channel state */ |
||||
enum dma_dw_axi_ch_state ch_state; |
||||
/* direction of transfer */ |
||||
uint32_t direction; |
||||
/* number of descriptors */ |
||||
uint32_t lli_desc_count; |
||||
/* cfg register configuration for dma transfer */ |
||||
uint64_t cfg; |
||||
/* mask and unmask interrupts */ |
||||
uint64_t irq_unmask; |
||||
/* user call back for dma transfer completion */ |
||||
dma_callback_t dma_xfer_callback; |
||||
/* user data for dma callback for dma transfer completion */ |
||||
void *priv_data_xfer; |
||||
/* user call back for dma block transfer completion */ |
||||
dma_callback_t dma_blk_xfer_callback; |
||||
/* user data for dma callback for dma block transfer completion */ |
||||
void *priv_data_blk_tfr; |
||||
}; |
||||
|
||||
/* dma controller driver data structure */ |
||||
struct dma_dw_axi_dev_data { |
||||
/* dma context */ |
||||
struct dma_context dma_ctx; |
||||
|
||||
/* mmio address mapping info for dma controller */ |
||||
DEVICE_MMIO_NAMED_RAM(dma_mmio); |
||||
/* pointer to store channel specific info */ |
||||
struct dma_dw_axi_ch_data *chan; |
||||
/* pointer to hold descriptor base address */ |
||||
struct dma_lli *dma_desc_pool; |
||||
}; |
||||
|
||||
/* Device constant configuration parameters */ |
||||
struct dma_dw_axi_dev_cfg { |
||||
/* dma address space to map */ |
||||
DEVICE_MMIO_NAMED_ROM(dma_mmio); |
||||
|
||||
#if DT_ANY_INST_HAS_PROP_STATUS_OKAY(resets) |
||||
/* Reset controller device configurations */ |
||||
const struct reset_dt_spec reset; |
||||
#endif |
||||
/* dma controller interrupt configuration function pointer */ |
||||
void (*irq_config)(void); |
||||
}; |
||||
|
||||
/**
|
||||
* @brief get current status of the channel |
||||
* |
||||
* @param dev Pointer to the device structure for the driver instance |
||||
* @param channel channel number |
||||
* |
||||
* @retval status of the channel |
||||
*/ |
||||
static enum dma_dw_axi_ch_state dma_dw_axi_get_ch_status(const struct device *dev, uint32_t ch) |
||||
{ |
||||
uint32_t bit_status; |
||||
uint64_t ch_status; |
||||
uintptr_t reg_base = DEVICE_MMIO_NAMED_GET(dev, dma_mmio); |
||||
|
||||
ch_status = sys_read64(reg_base + DMA_DW_AXI_CHENREG); |
||||
|
||||
/* channel is active/busy in the dma transfer */ |
||||
bit_status = ((ch_status >> ch) & 1); |
||||
if (bit_status) { |
||||
return DMA_DW_AXI_CH_ACTIVE; |
||||
} |
||||
|
||||
/* channel is currently suspended */ |
||||
bit_status = ((ch_status >> (16 + ch)) & 1); |
||||
if (bit_status) { |
||||
return DMA_DW_AXI_CH_SUSPENDED; |
||||
} |
||||
|
||||
/* channel is idle */ |
||||
return DMA_DW_AXI_CH_IDLE; |
||||
} |
||||
|
||||
static void dma_dw_axi_isr(const struct device *dev) |
||||
{ |
||||
unsigned int channel; |
||||
uint64_t status, ch_status; |
||||
int ret_status = 0; |
||||
struct dma_dw_axi_ch_data *chan_data; |
||||
uintptr_t reg_base = DEVICE_MMIO_NAMED_GET(dev, dma_mmio); |
||||
struct dma_dw_axi_dev_data *const dw_dev_data = DEV_DATA(dev); |
||||
|
||||
/* read interrupt status register to find interrupt is for which channel */ |
||||
status = sys_read64(reg_base + DMA_DW_AXI_INTSTATUSREG); |
||||
channel = find_lsb_set(status) - 1; |
||||
if (channel < 0) { |
||||
LOG_ERR("Spurious interrupt received channel:%u\n", channel); |
||||
return; |
||||
} |
||||
|
||||
if (channel > (dw_dev_data->dma_ctx.dma_channels - 1)) { |
||||
LOG_ERR("Interrupt received on invalid channel:%d\n", channel); |
||||
return; |
||||
} |
||||
|
||||
/* retrieve channel specific data pointer for a channel */ |
||||
chan_data = &dw_dev_data->chan[channel]; |
||||
|
||||
/* get dma transfer status */ |
||||
ch_status = sys_read64(reg_base + DMA_DW_AXI_CH_INTSTATUS(channel)); |
||||
if (!ch_status) { |
||||
LOG_ERR("Spurious interrupt received ch_status:0x%llx\n", ch_status); |
||||
return; |
||||
} |
||||
|
||||
/* handle dma transfer errors if any */ |
||||
if (ch_status & DMA_DW_AXI_IRQ_ALL_ERR) { |
||||
sys_write64(DMA_DW_AXI_IRQ_ALL_ERR, |
||||
reg_base + DMA_DW_AXI_CH_INTCLEARREG(channel)); |
||||
LOG_ERR("DMA Error: Channel:%d Channel interrupt status:0x%llx\n", |
||||
channel, ch_status); |
||||
ret_status = -(ch_status & DMA_DW_AXI_IRQ_ALL_ERR); |
||||
} |
||||
|
||||
/* handle block transfer completion */ |
||||
if (ch_status & DMA_DW_AXI_IRQ_BLOCK_TFR) { |
||||
sys_write64(DMA_DW_AXI_IRQ_ALL_ERR | DMA_DW_AXI_IRQ_BLOCK_TFR, |
||||
reg_base + DMA_DW_AXI_CH_INTCLEARREG(channel)); |
||||
|
||||
if (chan_data->dma_blk_xfer_callback) { |
||||
chan_data->dma_blk_xfer_callback(dev, |
||||
chan_data->priv_data_blk_tfr, channel, ret_status); |
||||
} |
||||
} |
||||
|
||||
/* handle dma transfer completion */ |
||||
if (ch_status & DMA_DW_AXI_IRQ_DMA_TFR) { |
||||
sys_write64(DMA_DW_AXI_IRQ_ALL_ERR | DMA_DW_AXI_IRQ_DMA_TFR, |
||||
reg_base + DMA_DW_AXI_CH_INTCLEARREG(channel)); |
||||
|
||||
if (chan_data->dma_xfer_callback) { |
||||
chan_data->dma_xfer_callback(dev, chan_data->priv_data_xfer, |
||||
channel, ret_status); |
||||
chan_data->ch_state = dma_dw_axi_get_ch_status(dev, channel); |
||||
} |
||||
} |
||||
} |
||||
|
||||
/**
|
||||
* @brief set data source and destination data width |
||||
* |
||||
* @param lli_desc Pointer to the descriptor |
||||
* @param src_data_width source data width |
||||
* @param dest_data_width destination data width |
||||
* |
||||
* @retval 0 on success, -ENOTSUP if the data width is not supported |
||||
*/ |
||||
static int dma_dw_axi_set_data_width(struct dma_lli *lli_desc, |
||||
uint32_t src_data_width, uint32_t dest_data_width) |
||||
{ |
||||
if (src_data_width > CONFIG_DMA_DW_AXI_DATA_WIDTH || |
||||
dest_data_width > CONFIG_DMA_DW_AXI_DATA_WIDTH) { |
||||
LOG_ERR("transfer width more than %u not supported", CONFIG_DMA_DW_AXI_DATA_WIDTH); |
||||
return -ENOTSUP; |
||||
} |
||||
|
||||
switch (src_data_width) { |
||||
case 1: |
||||
/* one byte transfer */ |
||||
lli_desc->ctl |= DMA_DW_AXI_CTL_SRC_WIDTH(BITS_8); |
||||
break; |
||||
case 2: |
||||
/* 2-bytes transfer width */ |
||||
lli_desc->ctl |= DMA_DW_AXI_CTL_SRC_WIDTH(BITS_16); |
||||
break; |
||||
case 4: |
||||
/* 4-bytes transfer width */ |
||||
lli_desc->ctl |= DMA_DW_AXI_CTL_SRC_WIDTH(BITS_32); |
||||
break; |
||||
case 8: |
||||
/* 8-bytes transfer width */ |
||||
lli_desc->ctl |= DMA_DW_AXI_CTL_SRC_WIDTH(BITS_64); |
||||
break; |
||||
case 16: |
||||
/* 16-bytes transfer width */ |
||||
lli_desc->ctl |= DMA_DW_AXI_CTL_SRC_WIDTH(BITS_128); |
||||
break; |
||||
case 32: |
||||
/* 32-bytes transfer width */ |
||||
lli_desc->ctl |= DMA_DW_AXI_CTL_SRC_WIDTH(BITS_256); |
||||
break; |
||||
case 64: |
||||
/* 64-bytes transfer width */ |
||||
lli_desc->ctl |= DMA_DW_AXI_CTL_SRC_WIDTH(BITS_512); |
||||
break; |
||||
default: |
||||
LOG_ERR("Source transfer width not supported"); |
||||
return -ENOTSUP; |
||||
} |
||||
|
||||
switch (dest_data_width) { |
||||
case 1: |
||||
/* one byte transfer */ |
||||
lli_desc->ctl |= DMA_DW_AXI_CTL_DST_WIDTH(BITS_8); |
||||
break; |
||||
case 2: |
||||
/* 2-bytes transfer width */ |
||||
lli_desc->ctl |= DMA_DW_AXI_CTL_DST_WIDTH(BITS_16); |
||||
break; |
||||
case 4: |
||||
/* 4-bytes transfer width */ |
||||
lli_desc->ctl |= DMA_DW_AXI_CTL_DST_WIDTH(BITS_32); |
||||
break; |
||||
case 8: |
||||
/* 8-bytes transfer width */ |
||||
lli_desc->ctl |= DMA_DW_AXI_CTL_DST_WIDTH(BITS_64); |
||||
break; |
||||
case 16: |
||||
/* 16-bytes transfer width */ |
||||
lli_desc->ctl |= DMA_DW_AXI_CTL_DST_WIDTH(BITS_128); |
||||
break; |
||||
case 32: |
||||
/* 32-bytes transfer width */ |
||||
lli_desc->ctl |= DMA_DW_AXI_CTL_DST_WIDTH(BITS_256); |
||||
break; |
||||
case 64: |
||||
/* 64-bytes transfer width */ |
||||
lli_desc->ctl |= DMA_DW_AXI_CTL_DST_WIDTH(BITS_512); |
||||
break; |
||||
default: |
||||
LOG_ERR("Destination transfer width not supported"); |
||||
return -ENOTSUP; |
||||
} |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
static int dma_dw_axi_config(const struct device *dev, uint32_t channel, |
||||
struct dma_config *cfg) |
||||
{ |
||||
int ret; |
||||
uint32_t msize_src, msize_dst, i, ch_state; |
||||
struct dma_dw_axi_ch_data *chan_data; |
||||
struct dma_block_config *blk_cfg; |
||||
struct dma_lli *lli_desc; |
||||
struct dma_dw_axi_dev_data *const dw_dev_data = DEV_DATA(dev); |
||||
|
||||
/* check for invalid parameters before dereferencing them. */ |
||||
if (cfg == NULL) { |
||||
LOG_ERR("invalid dma config :%p", cfg); |
||||
return -ENODATA; |
||||
} |
||||
|
||||
/* check if the channel is valid */ |
||||
if (channel > (dw_dev_data->dma_ctx.dma_channels - 1)) { |
||||
LOG_ERR("invalid dma channel %d", channel); |
||||
return -EINVAL; |
||||
} |
||||
|
||||
/* return if the channel is not idle */ |
||||
ch_state = dma_dw_axi_get_ch_status(dev, channel); |
||||
if (ch_state != DMA_DW_AXI_CH_IDLE) { |
||||
LOG_ERR("DMA channel:%d is not idle(status:%d)", channel, ch_state); |
||||
return -EBUSY; |
||||
} |
||||
|
||||
if (!cfg->block_count) { |
||||
LOG_ERR("no blocks to transfer"); |
||||
return -EINVAL; |
||||
} |
||||
|
||||
/* descriptor should be less than max configured descriptor */ |
||||
if (cfg->block_count > CONFIG_DMA_DW_AXI_MAX_DESC) { |
||||
LOG_ERR("dma:%s channel %d descriptor block count: %d larger than" |
||||
" max descriptors in pool: %d", dev->name, channel, |
||||
cfg->block_count, CONFIG_DMA_DW_AXI_MAX_DESC); |
||||
return -EINVAL; |
||||
} |
||||
|
||||
if (cfg->source_burst_length > CONFIG_DMA_DW_AXI_MAX_BURST_TXN_LEN || |
||||
cfg->dest_burst_length > CONFIG_DMA_DW_AXI_MAX_BURST_TXN_LEN || |
||||
cfg->source_burst_length == 0 || cfg->dest_burst_length == 0) { |
||||
LOG_ERR("dma:%s burst length not supported", dev->name); |
||||
return -ENOTSUP; |
||||
} |
||||
|
||||
/* get channel specific data pointer */ |
||||
chan_data = &dw_dev_data->chan[channel]; |
||||
|
||||
/* check if the channel is currently idle */ |
||||
if (chan_data->ch_state != DMA_DW_AXI_CH_IDLE) { |
||||
LOG_ERR("DMA channel:%d is busy", channel); |
||||
return -EBUSY; |
||||
} |
||||
|
||||
/* burst transaction length for source and destination */ |
||||
msize_src = DMA_DW_AXI_GET_MSIZE(cfg->source_burst_length); |
||||
msize_dst = DMA_DW_AXI_GET_MSIZE(cfg->dest_burst_length); |
||||
|
||||
chan_data->cfg = 0; |
||||
chan_data->irq_unmask = 0; |
||||
|
||||
chan_data->direction = cfg->channel_direction; |
||||
|
||||
chan_data->lli_desc_base = |
||||
&dw_dev_data->dma_desc_pool[channel * CONFIG_DMA_DW_AXI_MAX_DESC]; |
||||
chan_data->lli_desc_count = cfg->block_count; |
||||
memset(chan_data->lli_desc_base, 0, |
||||
sizeof(struct dma_lli) * chan_data->lli_desc_count); |
||||
|
||||
lli_desc = chan_data->lli_desc_base; |
||||
blk_cfg = cfg->head_block; |
||||
|
||||
/* max channel priority can be MAX_CHANNEL - 1 */ |
||||
if (cfg->channel_priority < dw_dev_data->dma_ctx.dma_channels) { |
||||
chan_data->cfg |= DMA_DW_AXI_CFG_PRIORITY(cfg->channel_priority); |
||||
} |
||||
|
||||
/* configure all the descriptors in a loop */ |
||||
for (i = 0; i < cfg->block_count; i++) { |
||||
|
||||
ret = dma_dw_axi_set_data_width(lli_desc, cfg->source_data_size, |
||||
cfg->dest_data_size); |
||||
if (ret) { |
||||
return ret; |
||||
} |
||||
|
||||
lli_desc->ctl |= DMA_DW_AXI_CTL_SRC_STAT_EN | |
||||
DMA_DW_AXI_CTL_DST_STAT_EN | DMA_DW_AXI_CTL_IOC_BLK_TFR; |
||||
|
||||
lli_desc->sar = blk_cfg->source_address; |
||||
lli_desc->dar = blk_cfg->dest_address; |
||||
|
||||
/* set block transfer size*/ |
||||
lli_desc->block_ts_lo = (blk_cfg->block_size / cfg->source_data_size) - 1; |
||||
if (lli_desc->block_ts_lo > CONFIG_DMA_DW_AXI_MAX_BLOCK_TS) { |
||||
LOG_ERR("block transfer size more than %u not supported", |
||||
CONFIG_DMA_DW_AXI_MAX_BLOCK_TS); |
||||
return -ENOTSUP; |
||||
} |
||||
|
||||
/* configuration based on channel direction */ |
||||
if (cfg->channel_direction == MEMORY_TO_MEMORY) { |
||||
chan_data->cfg |= DMA_DW_AXI_CFG_TT_FC(M2M_DMAC); |
||||
|
||||
lli_desc->ctl |= DMA_DW_AXI_CTL_SRC_MSIZE(msize_src) | |
||||
DMA_DW_AXI_CTL_DST_MSIZE(msize_dst); |
||||
|
||||
} else if (cfg->channel_direction == MEMORY_TO_PERIPHERAL) { |
||||
|
||||
chan_data->cfg |= DMA_DW_AXI_CFG_TT_FC(M2P_DMAC); |
||||
lli_desc->ctl |= DMA_DW_AXI_CTL_SRC_MSIZE(msize_src) | |
||||
DMA_DW_AXI_CTL_DST_MSIZE(msize_dst); |
||||
WRITE_BIT(chan_data->cfg, DMA_DW_AXI_CFG_HW_HS_DST_BIT_POS, 0); |
||||
|
||||
/* assign a hardware handshake interface */ |
||||
chan_data->cfg |= DMA_DW_AXI_CFG_DST_PER(cfg->dma_slot); |
||||
|
||||
} else if (cfg->channel_direction == PERIPHERAL_TO_MEMORY) { |
||||
lli_desc->ctl |= DMA_DW_AXI_CTL_SRC_MSIZE(msize_src) | |
||||
DMA_DW_AXI_CTL_DST_MSIZE(msize_dst); |
||||
chan_data->cfg |= DMA_DW_AXI_CFG_TT_FC(P2M_DMAC); |
||||
WRITE_BIT(chan_data->cfg, DMA_DW_AXI_CFG_HW_HS_SRC_BIT_POS, 0); |
||||
|
||||
/* assign a hardware handshake interface */ |
||||
chan_data->cfg |= DMA_DW_AXI_CFG_SRC_PER(cfg->dma_slot); |
||||
|
||||
} else { |
||||
LOG_ERR("%s: dma %s channel %d invalid direction %d", |
||||
__func__, dev->name, channel, cfg->channel_direction); |
||||
|
||||
return -EINVAL; |
||||
} |
||||
|
||||
/* set pointer to the next descriptor */ |
||||
lli_desc->llp = ((uint64_t)(lli_desc + 1)); |
||||
|
||||
#if defined(CONFIG_DMA_DW_AXI_LLI_SUPPORT) |
||||
/* configure multi block transfer size as linked list */ |
||||
chan_data->cfg |= DMA_DW_AXI_CFG_SRC_MULTBLK_TYPE(MULTI_BLK_LLI) | |
||||
DMA_DW_AXI_CFG_DST_MULTBLK_TYPE(MULTI_BLK_LLI); |
||||
|
||||
lli_desc->ctl |= DMA_DW_AXI_CTL_LLI_VALID; |
||||
/* last descriptor*/ |
||||
if ((i + 1) == chan_data->lli_desc_count) { |
||||
lli_desc->ctl |= DMA_DW_AXI_CTL_LLI_LAST | DMA_DW_AXI_CTL_LLI_VALID; |
||||
lli_desc->llp = 0; |
||||
} |
||||
#else |
||||
/* configure multi-block transfer as contiguous mode */ |
||||
chan_data->cfg |= DMA_DW_AXI_CFG_SRC_MULTBLK_TYPE(MULTI_BLK_CONTIGUOUS) | |
||||
DMA_DW_AXI_CFG_DST_MULTBLK_TYPE(MULTI_BLK_CONTIGUOUS); |
||||
#endif |
||||
|
||||
/* next descriptor to configure*/ |
||||
lli_desc++; |
||||
blk_cfg = blk_cfg->next_block; |
||||
} |
||||
|
||||
arch_dcache_flush_range((void *)chan_data->lli_desc_base, |
||||
sizeof(struct dma_lli) * cfg->block_count); |
||||
|
||||
chan_data->lli_desc_current = chan_data->lli_desc_base; |
||||
|
||||
/* enable an interrupt depending on whether the callback is requested after dma transfer
|
||||
* completion or dma block transfer completion |
||||
* |
||||
* disable an interrupt if callback is not requested |
||||
*/ |
||||
if (cfg->dma_callback && cfg->complete_callback_en) { |
||||
chan_data->dma_blk_xfer_callback = cfg->dma_callback; |
||||
chan_data->priv_data_blk_tfr = cfg->user_data; |
||||
|
||||
chan_data->irq_unmask = DMA_DW_AXI_IRQ_BLOCK_TFR | DMA_DW_AXI_IRQ_DMA_TFR; |
||||
} else if (cfg->dma_callback && !cfg->complete_callback_en) { |
||||
chan_data->dma_xfer_callback = cfg->dma_callback; |
||||
chan_data->priv_data_xfer = cfg->user_data; |
||||
|
||||
chan_data->irq_unmask = DMA_DW_AXI_IRQ_DMA_TFR; |
||||
} else { |
||||
chan_data->irq_unmask = DMA_DW_AXI_IRQ_NONE; |
||||
} |
||||
|
||||
/* unmask error interrupts when error_callback_dis is 0 */ |
||||
if (!cfg->error_callback_dis) { |
||||
chan_data->irq_unmask |= DMA_DW_AXI_IRQ_ALL_ERR; |
||||
} |
||||
|
||||
/* dma descriptors are configured, ready to start dma transfer */ |
||||
chan_data->ch_state = DMA_DW_AXI_CH_PREPARED; |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
static int dma_dw_axi_start(const struct device *dev, uint32_t channel) |
||||
{ |
||||
uint32_t ch_state; |
||||
struct dma_dw_axi_ch_data *chan_data; |
||||
struct dma_lli *lli_desc; |
||||
struct dma_dw_axi_dev_data *const dw_dev_data = DEV_DATA(dev); |
||||
uintptr_t reg_base = DEVICE_MMIO_NAMED_GET(dev, dma_mmio); |
||||
|
||||
/* validate channel number */ |
||||
if (channel > (dw_dev_data->dma_ctx.dma_channels - 1)) { |
||||
LOG_ERR("invalid dma channel %d", channel); |
||||
return -EINVAL; |
||||
} |
||||
|
||||
/* check whether channel is idle before initiating DMA transfer */ |
||||
ch_state = dma_dw_axi_get_ch_status(dev, channel); |
||||
if (ch_state != DMA_DW_AXI_CH_IDLE) { |
||||
LOG_ERR("DMA channel:%d is not idle", channel); |
||||
return -EBUSY; |
||||
} |
||||
|
||||
/* get channel specific data pointer */ |
||||
chan_data = &dw_dev_data->chan[channel]; |
||||
|
||||
if (chan_data->ch_state != DMA_DW_AXI_CH_PREPARED) { |
||||
LOG_ERR("DMA descriptors not configured"); |
||||
return -EINVAL; |
||||
} |
||||
|
||||
/* enable dma controller and global interrupt bit */ |
||||
sys_write64(DMA_DW_AXI_CFG_INT_EN | DMA_DW_AXI_CFG_EN, reg_base + DMA_DW_AXI_CFGREG); |
||||
|
||||
sys_write64(chan_data->cfg, reg_base + DMA_DW_AXI_CH_CFG(channel)); |
||||
|
||||
sys_write64(chan_data->irq_unmask, |
||||
reg_base + DMA_DW_AXI_CH_INTSTATUS_ENABLEREG(channel)); |
||||
sys_write64(chan_data->irq_unmask, |
||||
reg_base + DMA_DW_AXI_CH_INTSIGNAL_ENABLEREG(channel)); |
||||
|
||||
lli_desc = chan_data->lli_desc_current; |
||||
|
||||
#if defined(CONFIG_DMA_DW_AXI_LLI_SUPPORT) |
||||
sys_write64(((uint64_t)lli_desc), reg_base + DMA_DW_AXI_CH_LLP(channel)); |
||||
#else |
||||
/* Program Source and Destination addresses */ |
||||
sys_write64(lli_desc->sar, reg_base + DMA_DW_AXI_CH_SAR(channel)); |
||||
sys_write64(lli_desc->dar, reg_base + DMA_DW_AXI_CH_DAR(channel)); |
||||
|
||||
sys_write64(lli_desc->block_ts_lo & BLOCK_TS_MASK, |
||||
reg_base + DMA_DW_AXI_CH_BLOCK_TS(channel)); |
||||
|
||||
/* Program CH.CTL register */ |
||||
sys_write64(lli_desc->ctl, reg_base + DMA_DW_AXI_CH_CTL(channel)); |
||||
#endif |
||||
|
||||
/* Enable the channel which will initiate DMA transfer */ |
||||
sys_write64(CH_EN(channel), reg_base + DMA_DW_AXI_CHENREG); |
||||
|
||||
chan_data->ch_state = dma_dw_axi_get_ch_status(dev, channel); |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
static int dma_dw_axi_stop(const struct device *dev, uint32_t channel) |
||||
{ |
||||
bool is_channel_busy; |
||||
uint32_t ch_state; |
||||
struct dma_dw_axi_dev_data *const dw_dev_data = DEV_DATA(dev); |
||||
uintptr_t reg_base = DEVICE_MMIO_NAMED_GET(dev, dma_mmio); |
||||
|
||||
/* channel should be valid */ |
||||
if (channel > (dw_dev_data->dma_ctx.dma_channels - 1)) { |
||||
LOG_ERR("invalid dma channel %d", channel); |
||||
return -EINVAL; |
||||
} |
||||
|
||||
/* return if the channel is idle as there is nothing to stop */ |
||||
ch_state = dma_dw_axi_get_ch_status(dev, channel); |
||||
if (ch_state == DMA_DW_AXI_CH_IDLE) { |
||||
/* channel is already idle */ |
||||
return 0; |
||||
} |
||||
|
||||
/* To stop transfer or abort the channel in case of abnormal state:
|
||||
* 1. To disable channel, first suspend channel and drain the FIFO |
||||
* 2. Disable the channel. Channel may get hung and can't be disabled |
||||
* if there is no response from peripheral |
||||
* 3. If channel is not disabled, Abort the channel. Aborting channel will |
||||
* Flush out FIFO and data will be lost. Then corresponding interrupt will |
||||
* be raised for abort and CH_EN bit will be cleared from CHENREG register |
||||
*/ |
||||
sys_write64(CH_SUSP(channel), reg_base + DMA_DW_AXI_CHENREG); |
||||
|
||||
/* Try to disable the channel */ |
||||
sys_clear_bit(reg_base + DMA_DW_AXI_CHENREG, channel); |
||||
|
||||
is_channel_busy = WAIT_FOR((sys_read64(reg_base + DMA_DW_AXI_CHENREG)) & (BIT(channel)), |
||||
CONFIG_DMA_CHANNEL_STATUS_TIMEOUT, k_busy_wait(10)); |
||||
if (is_channel_busy) { |
||||
LOG_WRN("No response from handshaking interface... Aborting a channel..."); |
||||
sys_write64(CH_ABORT(channel), reg_base + DMA_DW_AXI_CHENREG); |
||||
|
||||
is_channel_busy = WAIT_FOR((sys_read64(reg_base + DMA_DW_AXI_CHENREG)) & |
||||
(BIT(channel)), CONFIG_DMA_CHANNEL_STATUS_TIMEOUT, |
||||
k_busy_wait(10)); |
||||
if (is_channel_busy) { |
||||
LOG_ERR("Channel abort failed"); |
||||
return -EBUSY; |
||||
} |
||||
} |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
static int dma_dw_axi_resume(const struct device *dev, uint32_t channel) |
||||
{ |
||||
uint32_t reg; |
||||
uintptr_t reg_base = DEVICE_MMIO_NAMED_GET(dev, dma_mmio); |
||||
struct dma_dw_axi_dev_data *const dw_dev_data = DEV_DATA(dev); |
||||
uint32_t ch_state; |
||||
|
||||
/* channel should be valid */ |
||||
if (channel > (dw_dev_data->dma_ctx.dma_channels - 1)) { |
||||
LOG_ERR("invalid dma channel %d", channel); |
||||
return -EINVAL; |
||||
} |
||||
|
||||
ch_state = dma_dw_axi_get_ch_status(dev, channel); |
||||
if (ch_state != DMA_DW_AXI_CH_SUSPENDED) { |
||||
LOG_INF("channel %u is not in suspended state so cannot resume channel", channel); |
||||
return 0; |
||||
} |
||||
|
||||
reg = sys_read64(reg_base + DMA_DW_AXI_CHENREG); |
||||
/* channel susp write enable bit has to be asserted */ |
||||
WRITE_BIT(reg, CH_RESUME_WE(channel), 1); |
||||
/* channel susp bit must be cleared to resume a channel*/ |
||||
WRITE_BIT(reg, CH_RESUME(channel), 0); |
||||
/* resume a channel by writing 0: ch_susp and 1: ch_susp_we */ |
||||
sys_write64(reg, reg_base + DMA_DW_AXI_CHENREG); |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
/* suspend a dma channel */ |
||||
static int dma_dw_axi_suspend(const struct device *dev, uint32_t channel) |
||||
{ |
||||
int ret; |
||||
uintptr_t reg_base = DEVICE_MMIO_NAMED_GET(dev, dma_mmio); |
||||
struct dma_dw_axi_dev_data *const dw_dev_data = DEV_DATA(dev); |
||||
uint32_t ch_state; |
||||
|
||||
/* channel should be valid */ |
||||
if (channel > (dw_dev_data->dma_ctx.dma_channels - 1)) { |
||||
LOG_ERR("invalid dma channel %u", channel); |
||||
return -EINVAL; |
||||
} |
||||
|
||||
ch_state = dma_dw_axi_get_ch_status(dev, channel); |
||||
if (ch_state != DMA_DW_AXI_CH_ACTIVE) { |
||||
LOG_INF("nothing to suspend as dma channel %u is not busy", channel); |
||||
return 0; |
||||
} |
||||
|
||||
/* suspend dma transfer */ |
||||
sys_write64(CH_SUSP(channel), reg_base + DMA_DW_AXI_CHENREG); |
||||
|
||||
ret = WAIT_FOR(dma_dw_axi_get_ch_status(dev, channel) & |
||||
DMA_DW_AXI_CH_SUSPENDED, CONFIG_DMA_CHANNEL_STATUS_TIMEOUT, |
||||
k_busy_wait(10)); |
||||
if (ret == 0) { |
||||
LOG_ERR("channel suspend failed"); |
||||
return ret; |
||||
} |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
static int dma_dw_axi_init(const struct device *dev) |
||||
{ |
||||
DEVICE_MMIO_NAMED_MAP(dev, dma_mmio, K_MEM_CACHE_NONE); |
||||
int i, ret; |
||||
struct dma_dw_axi_ch_data *chan_data; |
||||
const struct dma_dw_axi_dev_cfg *dw_dma_config = DEV_CFG(dev); |
||||
struct dma_dw_axi_dev_data *const dw_dev_data = DEV_DATA(dev); |
||||
|
||||
#if DT_ANY_INST_HAS_PROP_STATUS_OKAY(resets) |
||||
|
||||
if (dw_dma_config->reset.dev != NULL) { |
||||
/* check if reset manager is in ready state */ |
||||
if (!device_is_ready(dw_dma_config->reset.dev)) { |
||||
LOG_ERR("reset controller device not found"); |
||||
return -ENODEV; |
||||
} |
||||
|
||||
/* assert and de-assert dma controller */ |
||||
ret = reset_line_toggle(dw_dma_config->reset.dev, dw_dma_config->reset.id); |
||||
if (ret != 0) { |
||||
LOG_ERR("failed to reset dma controller"); |
||||
return ret; |
||||
} |
||||
} |
||||
#endif |
||||
|
||||
/* initialize channel state variable */ |
||||
for (i = 0; i < dw_dev_data->dma_ctx.dma_channels; i++) { |
||||
chan_data = &dw_dev_data->chan[i]; |
||||
/* initialize channel state */ |
||||
chan_data->ch_state = DMA_DW_AXI_CH_IDLE; |
||||
} |
||||
|
||||
/* configure and enable interrupt lines */ |
||||
dw_dma_config->irq_config(); |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
static const struct dma_driver_api dma_dw_axi_driver_api = { |
||||
.config = dma_dw_axi_config, |
||||
.start = dma_dw_axi_start, |
||||
.stop = dma_dw_axi_stop, |
||||
.suspend = dma_dw_axi_suspend, |
||||
.resume = dma_dw_axi_resume, |
||||
}; |
||||
|
||||
/* enable irq lines */ |
||||
#define CONFIGURE_DMA_IRQ(idx, inst) \ |
||||
IF_ENABLED(DT_INST_IRQ_HAS_IDX(inst, idx), ( \ |
||||
IRQ_CONNECT(DT_INST_IRQ_BY_IDX(inst, idx, irq), \ |
||||
DT_INST_IRQ_BY_IDX(inst, idx, priority), \ |
||||
dma_dw_axi_isr, \ |
||||
DEVICE_DT_INST_GET(inst), 0); \ |
||||
irq_enable(DT_INST_IRQ_BY_IDX(inst, idx, irq)); \ |
||||
)) |
||||
|
||||
#define DW_AXI_DMA_RESET_SPEC_INIT(inst) \ |
||||
.reset = RESET_DT_SPEC_INST_GET(inst), \ |
||||
|
||||
#define DW_AXI_DMAC_INIT(inst) \ |
||||
static struct dma_dw_axi_ch_data chan_##inst[DT_INST_PROP(inst, dma_channels)]; \ |
||||
static struct dma_lli \ |
||||
dma_desc_pool_##inst[DT_INST_PROP(inst, dma_channels) * \ |
||||
CONFIG_DMA_DW_AXI_MAX_DESC]; \ |
||||
ATOMIC_DEFINE(dma_dw_axi_atomic##inst, \ |
||||
DT_INST_PROP(inst, dma_channels)); \ |
||||
static struct dma_dw_axi_dev_data dma_dw_axi_data_##inst = { \ |
||||
.dma_ctx = { \ |
||||
.magic = DMA_MAGIC, \ |
||||
.atomic = dma_dw_axi_atomic##inst, \ |
||||
.dma_channels = DT_INST_PROP(inst, dma_channels), \ |
||||
}, \ |
||||
.chan = chan_##inst, \ |
||||
.dma_desc_pool = dma_desc_pool_##inst, \ |
||||
}; \ |
||||
static void dw_dma_irq_config_##inst(void); \ |
||||
static const struct dma_dw_axi_dev_cfg dma_dw_axi_config_##inst = { \ |
||||
DEVICE_MMIO_NAMED_ROM_INIT(dma_mmio, DT_DRV_INST(inst)), \ |
||||
IF_ENABLED(DT_INST_NODE_HAS_PROP(inst, resets), \ |
||||
(DW_AXI_DMA_RESET_SPEC_INIT(inst))) \ |
||||
.irq_config = dw_dma_irq_config_##inst, \ |
||||
}; \ |
||||
\ |
||||
DEVICE_DT_INST_DEFINE(inst, \ |
||||
&dma_dw_axi_init, \ |
||||
NULL, \ |
||||
&dma_dw_axi_data_##inst, \ |
||||
&dma_dw_axi_config_##inst, POST_KERNEL, \ |
||||
CONFIG_DMA_INIT_PRIORITY, \ |
||||
&dma_dw_axi_driver_api); \ |
||||
\ |
||||
static void dw_dma_irq_config_##inst(void) \ |
||||
{ \ |
||||
LISTIFY(DT_NUM_IRQS(DT_DRV_INST(inst)), CONFIGURE_DMA_IRQ, (), inst) \ |
||||
} |
||||
|
||||
DT_INST_FOREACH_STATUS_OKAY(DW_AXI_DMAC_INIT) |
@ -0,0 +1,36 @@
@@ -0,0 +1,36 @@
|
||||
# Copyright (c) 2023 Intel Corporation. All rights reserved. |
||||
# SPDX-License-Identifier: Apache-2.0 |
||||
|
||||
description: Synopsys Designware axi DMA Controller node |
||||
|
||||
compatible: "snps,designware-dma-axi" |
||||
|
||||
include: [dma-controller.yaml, reset-device.yaml] |
||||
|
||||
properties: |
||||
reg: |
||||
required: true |
||||
|
||||
dma-channels: |
||||
required: true |
||||
|
||||
interrupts: |
||||
required: true |
||||
|
||||
"#dma-cells": |
||||
const: 1 |
||||
|
||||
# #dma-cells : Must be <1>. |
||||
# The 1st cell specifies the hardware handshaking signal ID |
||||
# Example of device-tree dma channel configuration: |
||||
# |
||||
# &spi0 { |
||||
# /* Configure DMA */ |
||||
# dmas = <&dma0 18>, <&dma0 19>; |
||||
# dma-names = "tx", "rx"; |
||||
# }; |
||||
# |
||||
# In above spi node numbers 18 and 19 represents the |
||||
# peripheral handshaking interface ID |
||||
dma-cells: |
||||
- slot |
Loading…
Reference in new issue