/* * Copyright (c) 2023 Microchip Technology Inc. * * SPDX-License-Identifier: Apache-2.0 */ #define DT_DRV_COMPAT microchip_xec_dmac #include #include #include #include #include #include #include #include #include #include LOG_MODULE_REGISTER(dma_mchp_xec, CONFIG_DMA_LOG_LEVEL); #define XEC_DMA_DEBUG 1 #ifdef XEC_DMA_DEBUG #include #endif #define XEC_DMA_ABORT_WAIT_LOOPS 32 #define XEC_DMA_MAIN_REGS_SIZE 0x40 #define XEC_DMA_CHAN_REGS_SIZE 0x40 #define XEC_DMA_CHAN_REGS_ADDR(base, channel) \ (((uintptr_t)(base) + (XEC_DMA_MAIN_REGS_SIZE)) + \ ((uintptr_t)(channel) * XEC_DMA_CHAN_REGS_SIZE)) /* main control */ #define XEC_DMA_MAIN_CTRL_REG_MSK 0x3u #define XEC_DMA_MAIN_CTRL_EN_POS 0 #define XEC_DMA_MAIN_CTRL_SRST_POS 1 /* channel activate register */ #define XEC_DMA_CHAN_ACTV_EN_POS 0 /* channel control register */ #define XEC_DMA_CHAN_CTRL_REG_MSK 0x037fff27u #define XEC_DMA_CHAN_CTRL_HWFL_RUN_POS 0 #define XEC_DMA_CHAN_CTRL_REQ_POS 1 #define XEC_DMA_CHAN_CTRL_DONE_POS 2 #define XEC_DMA_CHAN_CTRL_BUSY_POS 5 #define XEC_DMA_CHAN_CTRL_M2D_POS 8 #define XEC_DMA_CHAN_CTRL_HWFL_DEV_POS 9 #define XEC_DMA_CHAN_CTRL_HWFL_DEV_MSK 0xfe00u #define XEC_DMA_CHAN_CTRL_HWFL_DEV_MSK0 0x7fu #define XEC_DMA_CHAN_CTRL_INCR_MEM_POS 16 #define XEC_DMA_CHAN_CTRL_INCR_DEV_POS 17 #define XEC_DMA_CHAN_CTRL_LOCK_ARB_POS 18 #define XEC_DMA_CHAN_CTRL_DIS_HWFL_POS 19 #define XEC_DMA_CHAN_CTRL_XFR_UNIT_POS 20 #define XEC_DMA_CHAN_CTRL_XFR_UNIT_MSK 0x700000u #define XEC_DMA_CHAN_CTRL_XFR_UNIT_MSK0 0x7u #define XEC_DMA_CHAN_CTRL_SWFL_GO_POS 24 #define XEC_DMA_CHAN_CTRL_ABORT_POS 25 /* channel interrupt status and enable registers */ #define XEC_DMA_CHAN_IES_REG_MSK 0xfu #define XEC_DMA_CHAN_IES_BERR_POS 0 #define XEC_DMA_CHAN_IES_OVFL_ERR_POS 1 #define XEC_DMA_CHAN_IES_DONE_POS 2 #define XEC_DMA_CHAN_IES_DEV_TERM_POS 3 /* channel fsm (RO) */ #define XEC_DMA_CHAN_FSM_REG_MSK 0xffffu #define XEC_DMA_CHAN_FSM_ARB_STATE_POS 0 #define XEC_DMA_CHAN_FSM_ARB_STATE_MSK 0xffu #define XEC_DMA_CHAN_FSM_CTRL_STATE_POS 8 #define XEC_DMA_CHAN_FSM_CTRL_STATE_MSK 0xff00u #define XEC_DMA_CHAN_FSM_CTRL_STATE_IDLE 0 #define XEC_DMA_CHAN_FSM_CTRL_STATE_ARB_REQ 0x100u #define XEC_DMA_CHAN_FSM_CTRL_STATE_RD_ACT 0x200u #define XEC_DMA_CHAN_FSM_CTRL_STATE_WR_ACT 0x300u #define XEC_DMA_CHAN_FSM_CTRL_STATE_WAIT_DONE 0x400u #define XEC_DMA_HWFL_DEV_VAL(d) \ (((uint32_t)(d) & XEC_DMA_CHAN_CTRL_HWFL_DEV_MSK0) << XEC_DMA_CHAN_CTRL_HWFL_DEV_POS) #define XEC_DMA_CHAN_CTRL_UNIT_VAL(u) \ (((uint32_t)(u) & XEC_DMA_CHAN_CTRL_XFR_UNIT_MSK0) << XEC_DMA_CHAN_CTRL_XFR_UNIT_POS) struct dma_xec_chan_regs { volatile uint32_t actv; volatile uint32_t mem_addr; volatile uint32_t mem_addr_end; volatile uint32_t dev_addr; volatile uint32_t control; volatile uint32_t istatus; volatile uint32_t ienable; volatile uint32_t fsm; uint32_t rsvd_20_3f[8]; }; struct dma_xec_regs { volatile uint32_t mctrl; volatile uint32_t mpkt; uint32_t rsvd_08_3f[14]; }; struct dma_xec_irq_info { uint8_t gid; /* GIRQ id [8, 26] */ uint8_t gpos; /* bit position in GIRQ [0, 31] */ uint8_t anid; /* aggregated external NVIC input */ uint8_t dnid; /* direct NVIC input */ }; struct dma_xec_config { struct dma_xec_regs *regs; uint8_t dma_channels; uint8_t dma_requests; uint8_t pcr_idx; uint8_t pcr_pos; int irq_info_size; const struct dma_xec_irq_info *irq_info_list; void (*irq_connect)(void); }; struct dma_xec_channel { uint32_t control; uint32_t mstart; uint32_t mend; uint32_t dstart; uint32_t isr_hw_status; uint32_t block_count; uint8_t unit_size; uint8_t dir; uint8_t flags; uint8_t rsvd[1]; struct dma_block_config *head; struct dma_block_config *curr; dma_callback_t cb; void *user_data; uint32_t total_req_xfr_len; uint32_t total_curr_xfr_len; }; #define DMA_XEC_CHAN_FLAGS_CB_EOB_POS 0 #define DMA_XEC_CHAN_FLAGS_CB_ERR_DIS_POS 1 struct dma_xec_data { struct dma_context ctx; struct dma_xec_channel *channels; }; #ifdef XEC_DMA_DEBUG static void xec_dma_debug_clean(void); #endif static inline struct dma_xec_chan_regs *xec_chan_regs(struct dma_xec_regs *regs, uint32_t chan) { uint8_t *pregs = (uint8_t *)regs + XEC_DMA_MAIN_REGS_SIZE; pregs += (chan * (XEC_DMA_CHAN_REGS_SIZE)); return (struct dma_xec_chan_regs *)pregs; } static inline struct dma_xec_irq_info const *xec_chan_irq_info(const struct dma_xec_config *devcfg, uint32_t channel) { return &devcfg->irq_info_list[channel]; } static int is_dma_data_size_valid(uint32_t datasz) { if ((datasz == 1U) || (datasz == 2U) || (datasz == 4U)) { return 1; } return 0; } /* HW requires if unit size is 2 or 4 bytes the source/destination addresses * to be aligned >= 2 or 4 bytes. */ static int is_data_aligned(uint32_t src, uint32_t dest, uint32_t unitsz) { if (unitsz == 1) { return 1; } if ((src | dest) & (unitsz - 1U)) { return 0; } return 1; } static void xec_dma_chan_clr(struct dma_xec_chan_regs * const chregs, const struct dma_xec_irq_info *info) { chregs->actv = 0; chregs->control = 0; chregs->mem_addr = 0; chregs->mem_addr_end = 0; chregs->dev_addr = 0; chregs->control = 0; chregs->ienable = 0; chregs->istatus = 0xffu; mchp_xec_ecia_girq_src_clr(info->gid, info->gpos); } static int is_dma_config_valid(const struct device *dev, struct dma_config *config) { const struct dma_xec_config * const devcfg = dev->config; if (config->dma_slot >= (uint32_t)devcfg->dma_requests) { LOG_ERR("XEC DMA config dma slot > exceeds number of request lines"); return 0; } if (config->source_data_size != config->dest_data_size) { LOG_ERR("XEC DMA requires source and dest data size identical"); return 0; } if (!((config->channel_direction == MEMORY_TO_MEMORY) || (config->channel_direction == MEMORY_TO_PERIPHERAL) || (config->channel_direction == PERIPHERAL_TO_MEMORY))) { LOG_ERR("XEC DMA only support M2M, M2P, P2M"); return 0; } if (!is_dma_data_size_valid(config->source_data_size)) { LOG_ERR("XEC DMA requires xfr unit size of 1, 2 or 4 bytes"); return 0; } if (config->block_count != 1) { LOG_ERR("XEC DMA block count != 1"); return 0; } return 1; } static int check_blocks(struct dma_xec_channel *chdata, struct dma_block_config *block, uint32_t block_count, uint32_t unit_size) { if (!block || !chdata) { LOG_ERR("bad pointer"); return -EINVAL; } chdata->total_req_xfr_len = 0; for (uint32_t i = 0; i < block_count; i++) { if ((block->source_addr_adj == DMA_ADDR_ADJ_DECREMENT) || (block->dest_addr_adj == DMA_ADDR_ADJ_DECREMENT)) { LOG_ERR("XEC DMA HW does not support address decrement. Block index %u", i); return -EINVAL; } if (!is_data_aligned(block->source_address, block->dest_address, unit_size)) { LOG_ERR("XEC DMA block at index %u violates source/dest unit size", i); return -EINVAL; } chdata->total_req_xfr_len += block->block_size; } return 0; } /* * struct dma_config flags * dma_slot - peripheral source/target ID. Not used for Mem2Mem * channel_direction - HW supports Mem2Mem, Mem2Periph, and Periph2Mem * complete_callback_en - if true invoke callback on completion (no error) * error_callback_dis - if true disable callback on error * source_handshake - 0=HW, 1=SW * dest_handshake - 0=HW, 1=SW * channel_priority - 4-bit field. HW implements round-robin only. * source_chaining_en - Chaining channel together * dest_chaining_en - HW does not support channel chaining. * linked_channel - HW does not support * cyclic - HW does not support cyclic buffer. Would have to emulate with SW. * source_data_size - unit size of source data. HW supports 1, 2, or 4 bytes * dest_data_size - unit size of dest data. HW requires same as source_data_size * source_burst_length - HW does not support * dest_burst_length - HW does not support * block_count - * user_data - * dma_callback - * head_block - pointer to struct dma_block_config * * struct dma_block_config * source_address - * source_gather_interval - N/A * dest_address - * dest_scatter_interval - N/A * dest_scatter_count - N/A * source_gather_count - N/A * block_size * config - flags * source_gather_en - N/A * dest_scatter_en - N/A * source_addr_adj - 0(increment), 1(decrement), 2(no change) * dest_addr_adj - 0(increment), 1(decrement), 2(no change) * source_reload_en - reload source address at end of block * dest_reload_en - reload destination address at end of block * fifo_mode_control - N/A * flow_control_mode - 0(source req service on data available) HW does this * 1(source req postposed until dest req happens) N/A * * * DMA channel implements memory start address, memory end address, * and peripheral address registers. No peripheral end address. * Transfer ends when memory start address increments and reaches * memory end address. * * Memory to Memory: copy from source_address to dest_address * chan direction = Mem2Dev. chan.control b[8]=1 * chan mem_addr = source_address * chan mem_addr_end = source_address + block_size * chan dev_addr = dest_address * * Memory to Peripheral: copy from source_address(memory) to dest_address(peripheral) * chan direction = Mem2Dev. chan.control b[8]=1 * chan mem_addr = source_address * chan mem_addr_end = chan mem_addr + block_size * chan dev_addr = dest_address * * Peripheral to Memory: * chan direction = Dev2Mem. chan.contronl b[8]=1 * chan mem_addr = dest_address * chan mem_addr_end = chan mem_addr + block_size * chan dev_addr = source_address */ static int dma_xec_configure(const struct device *dev, uint32_t channel, struct dma_config *config) { const struct dma_xec_config * const devcfg = dev->config; struct dma_xec_regs * const regs = devcfg->regs; struct dma_xec_data * const data = dev->data; uint32_t ctrl, mstart, mend, dstart, unit_size; int ret; if (!config || (channel >= (uint32_t)devcfg->dma_channels)) { return -EINVAL; } #ifdef XEC_DMA_DEBUG xec_dma_debug_clean(); #endif const struct dma_xec_irq_info *info = xec_chan_irq_info(devcfg, channel); struct dma_xec_chan_regs * const chregs = xec_chan_regs(regs, channel); struct dma_xec_channel *chdata = &data->channels[channel]; chdata->total_req_xfr_len = 0; chdata->total_curr_xfr_len = 0; xec_dma_chan_clr(chregs, info); if (!is_dma_config_valid(dev, config)) { return -EINVAL; } struct dma_block_config *block = config->head_block; ret = check_blocks(chdata, block, config->block_count, config->source_data_size); if (ret) { return ret; } unit_size = config->source_data_size; chdata->unit_size = unit_size; chdata->head = block; chdata->curr = block; chdata->block_count = config->block_count; chdata->dir = config->channel_direction; chdata->flags = 0; chdata->cb = config->dma_callback; chdata->user_data = config->user_data; /* invoke callback on completion of each block instead of all blocks ? */ if (config->complete_callback_en) { chdata->flags |= BIT(DMA_XEC_CHAN_FLAGS_CB_EOB_POS); } if (config->error_callback_dis) { /* disable callback on errors ? */ chdata->flags |= BIT(DMA_XEC_CHAN_FLAGS_CB_ERR_DIS_POS); } /* Use the control member of struct dma_xec_channel to * store control register value containing fields invariant * for all buffers: HW flow control device, direction, unit size, ... * derived from struct dma_config */ ctrl = XEC_DMA_CHAN_CTRL_UNIT_VAL(unit_size); if (config->channel_direction == MEMORY_TO_MEMORY) { ctrl |= BIT(XEC_DMA_CHAN_CTRL_DIS_HWFL_POS); } else { ctrl |= XEC_DMA_HWFL_DEV_VAL(config->dma_slot); } if (config->channel_direction == PERIPHERAL_TO_MEMORY) { mstart = block->dest_address; mend = block->dest_address + block->block_size; dstart = block->source_address; if (block->source_addr_adj == DMA_ADDR_ADJ_INCREMENT) { ctrl |= BIT(XEC_DMA_CHAN_CTRL_INCR_DEV_POS); } if (block->dest_addr_adj == DMA_ADDR_ADJ_INCREMENT) { ctrl |= BIT(XEC_DMA_CHAN_CTRL_INCR_MEM_POS); } } else { mstart = block->source_address; mend = block->source_address + block->block_size; dstart = block->dest_address; ctrl |= BIT(XEC_DMA_CHAN_CTRL_M2D_POS); if (block->source_addr_adj == DMA_ADDR_ADJ_INCREMENT) { ctrl |= BIT(XEC_DMA_CHAN_CTRL_INCR_MEM_POS); } if (block->dest_addr_adj == DMA_ADDR_ADJ_INCREMENT) { ctrl |= BIT(XEC_DMA_CHAN_CTRL_INCR_DEV_POS); } } chdata->control = ctrl; chdata->mstart = mstart; chdata->mend = mend; chdata->dstart = dstart; chregs->actv &= ~BIT(XEC_DMA_CHAN_ACTV_EN_POS); chregs->mem_addr = mstart; chregs->mem_addr_end = mend; chregs->dev_addr = dstart; chregs->control = ctrl; chregs->ienable = BIT(XEC_DMA_CHAN_IES_BERR_POS) | BIT(XEC_DMA_CHAN_IES_DONE_POS); chregs->actv |= BIT(XEC_DMA_CHAN_ACTV_EN_POS); return 0; } /* Update previously configured DMA channel with new data source address, * data destination address, and size in bytes. * src = source address for DMA transfer * dst = destination address for DMA transfer * size = size of DMA transfer. Assume this is in bytes. * We assume the caller will pass src, dst, and size that matches * the unit size from the previous configure call. */ static int dma_xec_reload(const struct device *dev, uint32_t channel, uint32_t src, uint32_t dst, size_t size) { const struct dma_xec_config * const devcfg = dev->config; struct dma_xec_data * const data = dev->data; struct dma_xec_regs * const regs = devcfg->regs; uint32_t ctrl; if (channel >= (uint32_t)devcfg->dma_channels) { return -EINVAL; } struct dma_xec_channel *chdata = &data->channels[channel]; struct dma_xec_chan_regs *chregs = xec_chan_regs(regs, channel); if (chregs->control & BIT(XEC_DMA_CHAN_CTRL_BUSY_POS)) { return -EBUSY; } ctrl = chregs->control & ~(BIT(XEC_DMA_CHAN_CTRL_HWFL_RUN_POS) | BIT(XEC_DMA_CHAN_CTRL_SWFL_GO_POS)); chregs->ienable = 0; chregs->control = 0; chregs->istatus = 0xffu; if (ctrl & BIT(XEC_DMA_CHAN_CTRL_M2D_POS)) { /* Memory to Device */ chdata->mstart = src; chdata->dstart = dst; } else { chdata->mstart = dst; chdata->dstart = src; } chdata->mend = chdata->mstart + size; chdata->total_req_xfr_len = size; chdata->total_curr_xfr_len = 0; chregs->mem_addr = chdata->mstart; chregs->mem_addr_end = chdata->mend; chregs->dev_addr = chdata->dstart; chregs->control = ctrl; return 0; } static int dma_xec_start(const struct device *dev, uint32_t channel) { const struct dma_xec_config * const devcfg = dev->config; struct dma_xec_regs * const regs = devcfg->regs; uint32_t chan_ctrl = 0U; if (channel >= (uint32_t)devcfg->dma_channels) { return -EINVAL; } struct dma_xec_chan_regs *chregs = xec_chan_regs(regs, channel); if (chregs->control & BIT(XEC_DMA_CHAN_CTRL_BUSY_POS)) { return -EBUSY; } chregs->ienable = 0u; chregs->istatus = 0xffu; chan_ctrl = chregs->control; if (chan_ctrl & BIT(XEC_DMA_CHAN_CTRL_DIS_HWFL_POS)) { chan_ctrl |= BIT(XEC_DMA_CHAN_CTRL_SWFL_GO_POS); } else { chan_ctrl |= BIT(XEC_DMA_CHAN_CTRL_HWFL_RUN_POS); } chregs->ienable = BIT(XEC_DMA_CHAN_IES_BERR_POS) | BIT(XEC_DMA_CHAN_IES_DONE_POS); chregs->control = chan_ctrl; chregs->actv |= BIT(XEC_DMA_CHAN_ACTV_EN_POS); return 0; } static int dma_xec_stop(const struct device *dev, uint32_t channel) { const struct dma_xec_config * const devcfg = dev->config; struct dma_xec_regs * const regs = devcfg->regs; int wait_loops = XEC_DMA_ABORT_WAIT_LOOPS; if (channel >= (uint32_t)devcfg->dma_channels) { return -EINVAL; } struct dma_xec_chan_regs *chregs = xec_chan_regs(regs, channel); chregs->ienable = 0; if (chregs->control & BIT(XEC_DMA_CHAN_CTRL_BUSY_POS)) { chregs->ienable = 0; chregs->control |= BIT(XEC_DMA_CHAN_CTRL_ABORT_POS); /* HW stops on next unit boundary (1, 2, or 4 bytes) */ do { if (!(chregs->control & BIT(XEC_DMA_CHAN_CTRL_BUSY_POS))) { break; } } while (wait_loops--); } chregs->mem_addr = chregs->mem_addr_end; chregs->fsm = 0; /* delay */ chregs->control = 0; chregs->istatus = 0xffu; chregs->actv = 0; return 0; } /* Get DMA transfer status. * HW supports: MEMORY_TO_MEMORY, MEMORY_TO_PERIPHERAL, or * PERIPHERAL_TO_MEMORY * current DMA runtime status structure * * busy - is current DMA transfer busy or idle * dir - DMA transfer direction * pending_length - data length pending to be transferred in bytes * or platform dependent. * We don't implement a circular buffer * free - free buffer space * write_position - write position in a circular dma buffer * read_position - read position in a circular dma buffer * */ static int dma_xec_get_status(const struct device *dev, uint32_t channel, struct dma_status *status) { const struct dma_xec_config * const devcfg = dev->config; struct dma_xec_data * const data = dev->data; struct dma_xec_regs * const regs = devcfg->regs; uint32_t chan_ctrl = 0U; if ((channel >= (uint32_t)devcfg->dma_channels) || (!status)) { LOG_ERR("unsupported channel"); return -EINVAL; } struct dma_xec_channel *chan_data = &data->channels[channel]; struct dma_xec_chan_regs *chregs = xec_chan_regs(regs, channel); chan_ctrl = chregs->control; if (chan_ctrl & BIT(XEC_DMA_CHAN_CTRL_BUSY_POS)) { status->busy = true; /* number of bytes remaining in channel */ status->pending_length = chan_data->total_req_xfr_len - (chregs->mem_addr_end - chregs->mem_addr); } else { status->pending_length = chan_data->total_req_xfr_len - chan_data->total_curr_xfr_len; status->busy = false; } if (chan_ctrl & BIT(XEC_DMA_CHAN_CTRL_DIS_HWFL_POS)) { status->dir = MEMORY_TO_MEMORY; } else if (chan_ctrl & BIT(XEC_DMA_CHAN_CTRL_M2D_POS)) { status->dir = MEMORY_TO_PERIPHERAL; } else { status->dir = PERIPHERAL_TO_MEMORY; } status->total_copied = chan_data->total_curr_xfr_len; return 0; } int xec_dma_get_attribute(const struct device *dev, uint32_t type, uint32_t *value) { if ((type == DMA_ATTR_MAX_BLOCK_COUNT) && value) { *value = 1; return 0; } return -EINVAL; } /* returns true if filter matched otherwise returns false */ static bool dma_xec_chan_filter(const struct device *dev, int ch, void *filter_param) { const struct dma_xec_config * const devcfg = dev->config; uint32_t filter = 0u; if (!filter_param && devcfg->dma_channels) { filter = GENMASK(devcfg->dma_channels-1u, 0); } else { filter = *((uint32_t *)filter_param); } return (filter & BIT(ch)); } /* API - HW does not stupport suspend/resume */ static const struct dma_driver_api dma_xec_api = { .config = dma_xec_configure, .reload = dma_xec_reload, .start = dma_xec_start, .stop = dma_xec_stop, .get_status = dma_xec_get_status, .chan_filter = dma_xec_chan_filter, .get_attribute = xec_dma_get_attribute, }; #ifdef CONFIG_PM_DEVICE /* TODO - DMA block has one PCR SLP_EN and one CLK_REQ. * If any channel is running the block's CLK_REQ is asserted. * CLK_REQ will not clear until all channels are done or disabled. * Clearing the DMA Main activate will kill DMA transactions resulting * possible data corruption and HW flow control device malfunctions. */ static int dmac_xec_pm_action(const struct device *dev, enum pm_device_action action) { const struct dma_xec_config * const devcfg = dev->config; struct dma_xec_regs * const regs = devcfg->regs; int ret = 0; switch (action) { case PM_DEVICE_ACTION_RESUME: regs->mctrl |= BIT(XEC_DMA_MAIN_CTRL_EN_POS); break; case PM_DEVICE_ACTION_SUSPEND: /* regs->mctrl &= ~BIT(XEC_DMA_MAIN_CTRL_EN_POS); */ break; default: ret = -ENOTSUP; } return ret; } #endif /* CONFIG_PM_DEVICE */ /* DMA channel interrupt handler called by ISR. * Callback flags in struct dma_config * completion_callback_en * 0 = invoke at completion of all blocks * 1 = invoke at completin of each block * error_callback_dis * 0 = invoke on all errors * 1 = disabled, do not invoke on errors */ /* DEBUG */ #ifdef XEC_DMA_DEBUG static volatile uint8_t channel_isr_idx[16]; static volatile uint8_t channel_isr_sts[16][16]; static volatile uint32_t channel_isr_ctrl[16][16]; static void xec_dma_debug_clean(void) { memset((void *)channel_isr_idx, 0, sizeof(channel_isr_idx)); memset((void *)channel_isr_sts, 0, sizeof(channel_isr_sts)); memset((void *)channel_isr_ctrl, 0, sizeof(channel_isr_ctrl)); } #endif static void dma_xec_irq_handler(const struct device *dev, uint32_t channel) { const struct dma_xec_config * const devcfg = dev->config; const struct dma_xec_irq_info *info = devcfg->irq_info_list; struct dma_xec_data * const data = dev->data; struct dma_xec_channel *chan_data = &data->channels[channel]; struct dma_xec_chan_regs * const regs = xec_chan_regs(devcfg->regs, channel); uint32_t sts = regs->istatus; int cb_status = 0; #ifdef XEC_DMA_DEBUG uint8_t idx = channel_isr_idx[channel]; if (idx < 16) { channel_isr_sts[channel][idx] = sts; channel_isr_ctrl[channel][idx] = regs->control; channel_isr_idx[channel] = ++idx; } #endif LOG_DBG("maddr=0x%08x mend=0x%08x daddr=0x%08x ctrl=0x%08x sts=0x%02x", regs->mem_addr, regs->mem_addr_end, regs->dev_addr, regs->control, sts); regs->ienable = 0u; regs->istatus = 0xffu; mchp_xec_ecia_girq_src_clr(info[channel].gid, info[channel].gpos); chan_data->isr_hw_status = sts; chan_data->total_curr_xfr_len += (regs->mem_addr - chan_data->mstart); if (sts & BIT(XEC_DMA_CHAN_IES_BERR_POS)) {/* Bus Error? */ if (!(chan_data->flags & BIT(DMA_XEC_CHAN_FLAGS_CB_ERR_DIS_POS))) { cb_status = -EIO; } } if (chan_data->cb) { chan_data->cb(dev, chan_data->user_data, channel, cb_status); } } static int dma_xec_init(const struct device *dev) { const struct dma_xec_config * const devcfg = dev->config; struct dma_xec_regs * const regs = devcfg->regs; LOG_DBG("driver init"); z_mchp_xec_pcr_periph_sleep(devcfg->pcr_idx, devcfg->pcr_pos, 0); /* soft reset, self-clearing */ regs->mctrl = BIT(XEC_DMA_MAIN_CTRL_SRST_POS); regs->mpkt = 0u; /* I/O delay, write to read-only register */ regs->mctrl = BIT(XEC_DMA_MAIN_CTRL_EN_POS); devcfg->irq_connect(); return 0; } /* n = node-id, p = property, i = index */ #define DMA_XEC_GID(n, p, i) MCHP_XEC_ECIA_GIRQ(DT_PROP_BY_IDX(n, p, i)) #define DMA_XEC_GPOS(n, p, i) MCHP_XEC_ECIA_GIRQ_POS(DT_PROP_BY_IDX(n, p, i)) #define DMA_XEC_GIRQ_INFO(n, p, i) \ { \ .gid = DMA_XEC_GID(n, p, i), \ .gpos = DMA_XEC_GPOS(n, p, i), \ .anid = MCHP_XEC_ECIA_NVIC_AGGR(DT_PROP_BY_IDX(n, p, i)), \ .dnid = MCHP_XEC_ECIA_NVIC_DIRECT(DT_PROP_BY_IDX(n, p, i)), \ }, /* n = node-id, p = property, i = index(channel?) */ #define DMA_XEC_IRQ_DECLARE(node_id, p, i) \ static void dma_xec_chan_##i##_isr(const struct device *dev) \ { \ dma_xec_irq_handler(dev, i); \ } \ #define DMA_XEC_IRQ_CONNECT_SUB(node_id, p, i) \ IRQ_CONNECT(DT_IRQ_BY_IDX(node_id, i, irq), \ DT_IRQ_BY_IDX(node_id, i, priority), \ dma_xec_chan_##i##_isr, \ DEVICE_DT_GET(node_id), 0); \ irq_enable(DT_IRQ_BY_IDX(node_id, i, irq)); \ mchp_xec_ecia_enable(DMA_XEC_GID(node_id, p, i), DMA_XEC_GPOS(node_id, p, i)); /* i = instance number of DMA controller */ #define DMA_XEC_IRQ_CONNECT(inst) \ DT_INST_FOREACH_PROP_ELEM(inst, girqs, DMA_XEC_IRQ_DECLARE) \ void dma_xec_irq_connect##inst(void) \ { \ DT_INST_FOREACH_PROP_ELEM(inst, girqs, DMA_XEC_IRQ_CONNECT_SUB) \ } #define DMA_XEC_DEVICE(i) \ BUILD_ASSERT(DT_INST_PROP(i, dma_channels) <= 16, "XEC DMA dma-channels > 16"); \ BUILD_ASSERT(DT_INST_PROP(i, dma_requests) <= 16, "XEC DMA dma-requests > 16"); \ \ static struct dma_xec_channel \ dma_xec_ctrl##i##_chans[DT_INST_PROP(i, dma_channels)]; \ ATOMIC_DEFINE(dma_xec_atomic##i, DT_INST_PROP(i, dma_channels)); \ \ static struct dma_xec_data dma_xec_data##i = { \ .ctx.magic = DMA_MAGIC, \ .ctx.dma_channels = DT_INST_PROP(i, dma_channels), \ .ctx.atomic = dma_xec_atomic##i, \ .channels = dma_xec_ctrl##i##_chans, \ }; \ \ DMA_XEC_IRQ_CONNECT(i) \ \ static const struct dma_xec_irq_info dma_xec_irqi##i[] = { \ DT_INST_FOREACH_PROP_ELEM(i, girqs, DMA_XEC_GIRQ_INFO) \ }; \ static const struct dma_xec_config dma_xec_cfg##i = { \ .regs = (struct dma_xec_regs *)DT_INST_REG_ADDR(i), \ .dma_channels = DT_INST_PROP(i, dma_channels), \ .dma_requests = DT_INST_PROP(i, dma_requests), \ .pcr_idx = DT_INST_PROP_BY_IDX(i, pcrs, 0), \ .pcr_pos = DT_INST_PROP_BY_IDX(i, pcrs, 1), \ .irq_info_size = ARRAY_SIZE(dma_xec_irqi##i), \ .irq_info_list = dma_xec_irqi##i, \ .irq_connect = dma_xec_irq_connect##i, \ }; \ PM_DEVICE_DT_DEFINE(i, dmac_xec_pm_action); \ DEVICE_DT_INST_DEFINE(i, &dma_xec_init, \ PM_DEVICE_DT_GET(i), \ &dma_xec_data##i, &dma_xec_cfg##i, \ PRE_KERNEL_1, CONFIG_DMA_INIT_PRIORITY, \ &dma_xec_api); DT_INST_FOREACH_STATUS_OKAY(DMA_XEC_DEVICE)