/* * Copyright (c) 2023 Renesas Electronics Corporation * * SPDX-License-Identifier: Apache-2.0 */ #include #include #include #include #include #include #include #include #include #include LOG_MODULE_REGISTER(dma_smartbond, CONFIG_DMA_LOG_LEVEL); #define DT_DRV_COMPAT renesas_smartbond_dma #define SMARTBOND_IRQN DT_INST_IRQN(0) #define SMARTBOND_IRQ_PRIO DT_INST_IRQ(0, priority) #define DMA_CHANNELS_COUNT DT_PROP(DT_NODELABEL(dma), dma_channels) #define DMA_BLOCK_COUNT DT_PROP(DT_NODELABEL(dma), block_count) #define DMA_SECURE_CHANNEL 7 #define DMA_CTRL_REG_SET_FIELD(_field, _var, _val) \ (_var) = \ (((_var) & ~DMA_DMA0_CTRL_REG_ ## _field ## _Msk) | \ (((_val) << DMA_DMA0_CTRL_REG_ ## _field ## _Pos) & DMA_DMA0_CTRL_REG_ ## _field ## _Msk)) #define DMA_CTRL_REG_GET_FIELD(_field, _var) \ (((_var) & DMA_DMA0_CTRL_REG_ ## _field ## _Msk) >> DMA_DMA0_CTRL_REG_ ## _field ## _Pos) #define DMA_CHN2REG(_idx) (&((struct channel_regs *)DMA)[(_idx)]) #define DMA_MUX_SHIFT(_idx) (((_idx) >> 1) * 4) #define DMA_REQ_MUX_REG_SET(_idx, _val) \ DMA->DMA_REQ_MUX_REG = \ (DMA->DMA_REQ_MUX_REG & ~(0xf << DMA_MUX_SHIFT((_idx)))) | \ (((_val) & 0xf) << DMA_MUX_SHIFT((_idx))) #define DMA_REQ_MUX_REG_GET(_idx) \ ((DMA->DMA_REQ_MUX_REG >> DMA_MUX_SHIFT((_idx))) & 0xf) #define CRYPTO_KEYS_BUF_ADDR 0x30040100 #define CRYPTO_KEYS_BUF_SIZE 0x100 #define IS_AES_KEYS_BUF_RANGE(_a) ((uint32_t)(_a) >= (uint32_t)(CRYPTO_KEYS_BUF_ADDR)) && \ ((uint32_t)(_a) < (uint32_t)(CRYPTO_KEYS_BUF_ADDR + CRYPTO_KEYS_BUF_SIZE)) /* * DMA channel priority level. The smaller the value the lower the priority granted to a channel * when two or more channels request the bus at the same time. For channels of same priority an * inherent mechanism is applied in which the lower the channel number the higher the priority. */ enum dma_smartbond_channel_prio { DMA_SMARTBOND_CHANNEL_PRIO_0 = 0x0, /* Lowest channel priority */ DMA_SMARTBOND_CHANNEL_PRIO_1, DMA_SMARTBOND_CHANNEL_PRIO_2, DMA_SMARTBOND_CHANNEL_PRIO_3, DMA_SMARTBOND_CHANNEL_PRIO_4, DMA_SMARTBOND_CHANNEL_PRIO_5, DMA_SMARTBOND_CHANNEL_PRIO_6, DMA_SMARTBOND_CHANNEL_PRIO_7, /* Highest channel priority */ DMA_SMARTBOND_CHANNEL_PRIO_MAX }; enum dma_smartbond_channel { DMA_SMARTBOND_CHANNEL_0 = 0x0, DMA_SMARTBOND_CHANNEL_1, DMA_SMARTBOND_CHANNEL_2, DMA_SMARTBOND_CHANNEL_3, DMA_SMARTBOND_CHANNEL_4, DMA_SMARTBOND_CHANNEL_5, DMA_SMARTBOND_CHANNEL_6, DMA_SMARTBOND_CHANNEL_7, DMA_SMARTBOND_CHANNEL_MAX }; enum dma_smartbond_burst_len { DMA_SMARTBOND_BURST_LEN_1B = 0x1, /* Burst mode is disabled */ DMA_SMARTBOND_BURST_LEN_4B = 0x4, /* Perform bursts of 4 beats (INCR4) */ DMA_SMARTBOND_BURST_LEN_8B = 0x8 /* Perform bursts of 8 beats (INCR8) */ }; /* * DMA bus width indicating how many bytes are retrived/written per transfer. * Note that the bus width is the same for the source and destination. */ enum dma_smartbond_bus_width { DMA_SMARTBOND_BUS_WIDTH_1B = 0x1, DMA_SMARTBOND_BUS_WIDTH_2B = 0x2, DMA_SMARTBOND_BUS_WIDTH_4B = 0x4 }; enum dreq_mode { DREQ_MODE_SW = 0x0, DREQ_MODE_HW }; enum burst_mode { BURST_MODE_0B = 0x0, BURST_MODE_4B = 0x1, BURST_MODE_8B = 0x2 }; enum bus_width { BUS_WIDTH_1B = 0x0, BUS_WIDTH_2B = 0x1, BUS_WIDTH_4B = 0x2 }; enum addr_adj { ADDR_ADJ_NO_CHANGE = 0x0, ADDR_ADJ_INCR }; enum copy_mode { COPY_MODE_BLOCK = 0x0, COPY_MODE_INIT }; enum req_sense { REQ_SENSE_LEVEL = 0x0, REQ_SENSE_EDGE }; struct channel_regs { __IO uint32_t DMA_A_START; __IO uint32_t DMA_B_START; __IO uint32_t DMA_INT_REG; __IO uint32_t DMA_LEN_REG; __IO uint32_t DMA_CTRL_REG; __I uint32_t DMA_IDX_REG; __I uint32_t RESERVED[2]; }; struct dma_channel_data { dma_callback_t cb; void *user_data; enum dma_smartbond_bus_width bus_width; enum dma_smartbond_burst_len burst_len; enum dma_channel_direction dir; bool is_dma_configured; }; struct dma_smartbond_data { /* Should be the first member of the driver data */ struct dma_context dma_ctx; ATOMIC_DEFINE(channels_atomic, DMA_CHANNELS_COUNT); /* User callbacks and data to be stored per channel */ struct dma_channel_data channel_data[DMA_CHANNELS_COUNT]; }; /* True if there is any DMA activity on any channel, false otheriwise. */ static bool dma_smartbond_is_dma_active(void) { int idx; struct channel_regs *regs; for (idx = 0; idx < DMA_CHANNELS_COUNT; idx++) { regs = DMA_CHN2REG(idx); if (DMA_CTRL_REG_GET_FIELD(DMA_ON, regs->DMA_CTRL_REG)) { return true; } } return false; } static void dma_smartbond_set_channel_status(uint32_t channel, bool status) { unsigned int key; struct channel_regs *regs = DMA_CHN2REG(channel); key = irq_lock(); if (status) { /* Make sure the status register for the requested channel is cleared. */ DMA->DMA_CLEAR_INT_REG |= BIT(channel); /* Enable interrupts for the requested channel. */ DMA->DMA_INT_MASK_REG |= BIT(channel); /* Check if this is the first attempt to enable DMA interrupts. */ if (!irq_is_enabled(SMARTBOND_IRQN)) { irq_enable(SMARTBOND_IRQN); } DMA_CTRL_REG_SET_FIELD(DMA_ON, regs->DMA_CTRL_REG, 0x1); } else { DMA_CTRL_REG_SET_FIELD(DMA_ON, regs->DMA_CTRL_REG, 0x0); /* * It might happen that DMA is already in progress. Make sure the current * on-going transfer is complete (cannot be interrupted). */ while (DMA_CTRL_REG_GET_FIELD(DMA_ON, regs->DMA_CTRL_REG)) { } /* Disable interrupts for the requested channel */ DMA->DMA_INT_MASK_REG &= ~(BIT(channel)); /* Clear the status register; the requested channel should be considered obsolete */ DMA->DMA_CLEAR_INT_REG |= BIT(channel); /* DMA interrupts should be disabled only if all channels are disabled. */ if (!dma_smartbond_is_dma_active()) { irq_disable(SMARTBOND_IRQN); } } irq_unlock(key); } static bool dma_channel_dst_addr_check_and_adjust(uint32_t channel, uint32_t *dst) { uint32_t phy_address; uint32_t secure_boot_reg; bool is_aes_keys_protected, is_qspic_keys_protected; phy_address = black_orca_phy_addr(*dst); secure_boot_reg = CRG_TOP->SECURE_BOOT_REG; is_aes_keys_protected = (secure_boot_reg & CRG_TOP_SECURE_BOOT_REG_PROT_AES_KEY_READ_Msk); is_qspic_keys_protected = (secure_boot_reg & CRG_TOP_SECURE_BOOT_REG_PROT_QSPI_KEY_READ_Msk); /* * If the destination address reflects the AES key buffer area and secure keys are protected * then only the secure channel #7 can be used to transfer data to AES key buffer. */ if ((IS_AES_KEYS_BUF_RANGE(phy_address) && (is_aes_keys_protected || is_qspic_keys_protected) && (channel != DMA_SECURE_CHANNEL))) { LOG_ERR("Keys are protected. Only secure channel #7 can be employed."); return false; } if (IS_QSPIF_ADDRESS(phy_address) || IS_QSPIF_CACHED_ADDRESS(phy_address) || IS_OTP_ADDRESS(phy_address) || IS_OTP_P_ADDRESS(phy_address)) { LOG_ERR("Invalid destination location."); return false; } *dst = phy_address; return true; } static bool dma_channel_src_addr_check_and_adjust(uint32_t channel, uint32_t *src) { uint32_t phy_address; uint32_t secure_boot_reg; bool is_aes_keys_protected, is_qspic_keys_protected; /* DMA can only access physical addresses, not remapped. */ phy_address = black_orca_phy_addr(*src); if (IS_QSPIF_CACHED_ADDRESS(phy_address)) { /* * To achiebe max. perfomance, peripherals should not access the Flash memory * through the instruction cache controller (avoid cache misses). */ phy_address += (MCU_QSPIF_M_BASE - MCU_QSPIF_M_CACHED_BASE); } else if (IS_OTP_ADDRESS(phy_address)) { /* Peripherals should access OTP through its peripheral address space. */ phy_address += (MCU_OTP_M_P_BASE - MCU_OTP_M_BASE); } secure_boot_reg = CRG_TOP->SECURE_BOOT_REG; is_aes_keys_protected = (secure_boot_reg & CRG_TOP_SECURE_BOOT_REG_PROT_AES_KEY_READ_Msk); is_qspic_keys_protected = (secure_boot_reg & CRG_TOP_SECURE_BOOT_REG_PROT_QSPI_KEY_READ_Msk); /* * If the source address reflects protected area in OTP then only the * secure channel #7 can be used to fetch secure keys data. */ if (((IS_ADDRESS_USER_DATA_KEYS_SEGMENT(phy_address) && is_aes_keys_protected) || (IS_ADDRESS_QSPI_FW_KEYS_SEGMENT(phy_address) && is_qspic_keys_protected)) && (channel != DMA_SECURE_CHANNEL)) { LOG_ERR("Keys are protected. Only secure channel #7 can be employed."); return false; } *src = phy_address; return true; } static bool dma_channel_update_dreq_mode(enum dma_channel_direction direction, uint32_t *dma_ctrl_reg) { switch (direction) { case MEMORY_TO_HOST: case HOST_TO_MEMORY: case MEMORY_TO_MEMORY: /* DMA channel starts immediately */ DMA_CTRL_REG_SET_FIELD(DREQ_MODE, *dma_ctrl_reg, DREQ_MODE_SW); break; case PERIPHERAL_TO_MEMORY: case PERIPHERAL_TO_PERIPHERAL: /* DMA channels starts by peripheral DMA req */ DMA_CTRL_REG_SET_FIELD(DREQ_MODE, *dma_ctrl_reg, DREQ_MODE_HW); break; default: return false; }; return true; } static bool dma_channel_update_src_addr_adj(enum dma_addr_adj addr_adj, uint32_t *dma_ctrl_reg) { switch (addr_adj) { case DMA_ADDR_ADJ_NO_CHANGE: DMA_CTRL_REG_SET_FIELD(AINC, *dma_ctrl_reg, ADDR_ADJ_NO_CHANGE); break; case DMA_ADDR_ADJ_INCREMENT: DMA_CTRL_REG_SET_FIELD(AINC, *dma_ctrl_reg, ADDR_ADJ_INCR); break; default: return false; } return true; } static bool dma_channel_update_dst_addr_adj(enum dma_addr_adj addr_adj, uint32_t *dma_ctrl_reg) { switch (addr_adj) { case DMA_ADDR_ADJ_NO_CHANGE: DMA_CTRL_REG_SET_FIELD(BINC, *dma_ctrl_reg, ADDR_ADJ_NO_CHANGE); break; case DMA_ADDR_ADJ_INCREMENT: DMA_CTRL_REG_SET_FIELD(BINC, *dma_ctrl_reg, ADDR_ADJ_INCR); break; default: return false; } return true; } static bool dma_channel_update_bus_width(uint16_t bw, uint32_t *dma_ctrl_reg) { switch (bw) { case DMA_SMARTBOND_BUS_WIDTH_1B: DMA_CTRL_REG_SET_FIELD(BW, *dma_ctrl_reg, BUS_WIDTH_1B); break; case DMA_SMARTBOND_BUS_WIDTH_2B: DMA_CTRL_REG_SET_FIELD(BW, *dma_ctrl_reg, BUS_WIDTH_2B); break; case DMA_SMARTBOND_BUS_WIDTH_4B: DMA_CTRL_REG_SET_FIELD(BW, *dma_ctrl_reg, BUS_WIDTH_4B); break; default: return false; } return true; } static bool dma_channel_update_burst_mode(uint16_t burst, uint32_t *dma_ctrl_reg) { switch (burst) { case DMA_SMARTBOND_BURST_LEN_1B: DMA_CTRL_REG_SET_FIELD(BURST_MODE, *dma_ctrl_reg, BURST_MODE_0B); break; case DMA_SMARTBOND_BURST_LEN_4B: DMA_CTRL_REG_SET_FIELD(BURST_MODE, *dma_ctrl_reg, BURST_MODE_4B); break; case DMA_SMARTBOND_BURST_LEN_8B: DMA_CTRL_REG_SET_FIELD(BURST_MODE, *dma_ctrl_reg, BURST_MODE_8B); break; default: return false; } return true; } static void dma_channel_update_req_sense(enum dma_smartbond_trig_mux trig_mux, uint32_t channel, uint32_t *dma_ctrl_reg) { switch (trig_mux) { case DMA_SMARTBOND_TRIG_MUX_UART: case DMA_SMARTBOND_TRIG_MUX_UART2: case DMA_SMARTBOND_TRIG_MUX_UART3: case DMA_SMARTBOND_TRIG_MUX_I2C: case DMA_SMARTBOND_TRIG_MUX_I2C2: case DMA_SMARTBOND_TRIG_MUX_USB: /* Odd channel numbers should reflect TX path */ if (channel & BIT(0)) { DMA_CTRL_REG_SET_FIELD(REQ_SENSE, *dma_ctrl_reg, REQ_SENSE_EDGE); break; } default: DMA_CTRL_REG_SET_FIELD(REQ_SENSE, *dma_ctrl_reg, REQ_SENSE_LEVEL); } } static void dma_set_mux_request(enum dma_smartbond_trig_mux trig_mux, uint32_t channel) { unsigned int key; key = irq_lock(); DMA_REQ_MUX_REG_SET(channel, trig_mux); /* * Having same trigger for different channels can cause unpredictable results. * The audio triggers (src and pcm) are an exception, as they use 2 pairs each * for DMA access. * The lesser significant selector has higher priority and will control * the DMA acknowledge signal driven to the selected peripheral. Make sure * the current selector does not match with selectors of * higher priorities (dma channels of lower indexing). It's OK if a * channel of higher indexing defines the same peripheral request source * (should be ignored as it has lower priority). */ if (trig_mux != DMA_SMARTBOND_TRIG_MUX_NONE) { switch (channel) { case DMA_SMARTBOND_CHANNEL_7: case DMA_SMARTBOND_CHANNEL_6: if (DMA_REQ_MUX_REG_GET(DMA_SMARTBOND_CHANNEL_5) == trig_mux) { DMA_REQ_MUX_REG_SET(DMA_SMARTBOND_CHANNEL_5, DMA_SMARTBOND_TRIG_MUX_NONE); } /* fall-through */ case DMA_SMARTBOND_CHANNEL_5: case DMA_SMARTBOND_CHANNEL_4: if (DMA_REQ_MUX_REG_GET(DMA_SMARTBOND_CHANNEL_3) == trig_mux) { DMA_REQ_MUX_REG_SET(DMA_SMARTBOND_CHANNEL_3, DMA_SMARTBOND_TRIG_MUX_NONE); } /* fall-through */ case DMA_SMARTBOND_CHANNEL_3: case DMA_SMARTBOND_CHANNEL_2: if (DMA_REQ_MUX_REG_GET(DMA_SMARTBOND_CHANNEL_1) == trig_mux) { DMA_REQ_MUX_REG_SET(DMA_SMARTBOND_CHANNEL_1, DMA_SMARTBOND_TRIG_MUX_NONE); } case DMA_SMARTBOND_CHANNEL_1: case DMA_SMARTBOND_CHANNEL_0: break; } } irq_unlock(key); } static int dma_smartbond_config(const struct device *dev, uint32_t channel, struct dma_config *cfg) { struct dma_smartbond_data *data = dev->data; struct channel_regs *regs; uint32_t dma_ctrl_reg; uint32_t src_dst_address; if (channel >= DMA_CHANNELS_COUNT) { LOG_ERR("Inavlid DMA channel index"); return -EINVAL; } regs = DMA_CHN2REG(channel); dma_ctrl_reg = regs->DMA_CTRL_REG; if (DMA_CTRL_REG_GET_FIELD(DMA_ON, dma_ctrl_reg)) { LOG_ERR("Requested channel is enabled. It should first be disabled"); return -EIO; } if (cfg == NULL || cfg->head_block == NULL) { LOG_ERR("Missing configuration structure"); return -EINVAL; } /* Error handling is not supported; just warn user. */ if (cfg->error_callback_en) { LOG_WRN("Error handling is not supported"); } if (!cfg->complete_callback_en) { data->channel_data[channel].cb = cfg->dma_callback; data->channel_data[channel].user_data = cfg->user_data; } else { LOG_WRN("User callback can only be called at completion only and not per block."); /* Nulify pointers to indicate notifications are disabled. */ data->channel_data[channel].cb = NULL; data->channel_data[channel].user_data = NULL; } data->channel_data[channel].dir = cfg->channel_direction; if (cfg->block_count > DMA_BLOCK_COUNT) { LOG_WRN("A single block is supported. The rest blocks will be discarded"); } if (cfg->channel_priority >= DMA_SMARTBOND_CHANNEL_PRIO_MAX) { cfg->channel_priority = DMA_SMARTBOND_CHANNEL_PRIO_7; LOG_WRN("Channel priority exceeded max. Setting to highest valid level"); } DMA_CTRL_REG_SET_FIELD(DMA_PRIO, dma_ctrl_reg, cfg->channel_priority); if (((cfg->source_burst_length != cfg->dest_burst_length) || !dma_channel_update_burst_mode(cfg->source_burst_length, &dma_ctrl_reg))) { LOG_ERR("Invalid burst mode or source and destination mode mismatch"); return -EINVAL; } data->channel_data[channel].burst_len = cfg->source_burst_length; if (cfg->source_data_size != cfg->dest_data_size || !dma_channel_update_bus_width(cfg->source_data_size, &dma_ctrl_reg)) { LOG_ERR("Invalid bus width or source and destination bus width mismatch"); return -EINVAL; } data->channel_data[channel].bus_width = cfg->source_data_size; if (cfg->source_chaining_en || cfg->dest_chaining_en || cfg->head_block->source_gather_en || cfg->head_block->dest_scatter_en || cfg->head_block->source_reload_en || cfg->head_block->dest_reload_en) { LOG_WRN("Chainning, scattering, gathering or reloading is not supported"); } if (!dma_channel_update_src_addr_adj(cfg->head_block->source_addr_adj, &dma_ctrl_reg)) { LOG_ERR("Invalid source address adjustment"); return -EINVAL; } if (!dma_channel_update_dst_addr_adj(cfg->head_block->dest_addr_adj, &dma_ctrl_reg)) { LOG_ERR("Invalid destination address adjustment"); return -EINVAL; } if (!dma_channel_update_dreq_mode(cfg->channel_direction, &dma_ctrl_reg)) { LOG_ERR("Inavlid channel direction"); return -EINVAL; } /* Cyclic is valid only when DREQ_MODE is set */ if (cfg->cyclic && DMA_CTRL_REG_GET_FIELD(DREQ_MODE, dma_ctrl_reg) != DREQ_MODE_HW) { LOG_ERR("Circular mode is only supported for non memory-memory transfers"); return -EINVAL; } DMA_CTRL_REG_SET_FIELD(CIRCULAR, dma_ctrl_reg, cfg->cyclic); if (DMA_CTRL_REG_GET_FIELD(DREQ_MODE, dma_ctrl_reg) == DREQ_MODE_SW && DMA_CTRL_REG_GET_FIELD(AINC, dma_ctrl_reg) == ADDR_ADJ_NO_CHANGE && DMA_CTRL_REG_GET_FIELD(BINC, dma_ctrl_reg) == ADDR_ADJ_INCR) { /* * Valid for memory initialization to a specific value. This process * cannot be interrupted by other DMA channels. */ DMA_CTRL_REG_SET_FIELD(DMA_INIT, dma_ctrl_reg, COPY_MODE_INIT); } else { DMA_CTRL_REG_SET_FIELD(DMA_INIT, dma_ctrl_reg, COPY_MODE_BLOCK); } dma_channel_update_req_sense(cfg->dma_slot, channel, &dma_ctrl_reg); regs->DMA_CTRL_REG = dma_ctrl_reg; /* Requested address might be changed */ src_dst_address = cfg->head_block->source_address; if (!dma_channel_src_addr_check_and_adjust(channel, &src_dst_address)) { return -EINVAL; } if (src_dst_address % cfg->source_data_size) { LOG_ERR("Source address is not bus width aligned"); return -EINVAL; } regs->DMA_A_START = src_dst_address; src_dst_address = cfg->head_block->dest_address; if (!dma_channel_dst_addr_check_and_adjust(channel, &src_dst_address)) { return -EINVAL; } if (src_dst_address % cfg->dest_data_size) { LOG_ERR("Destination address is not bus width aligned"); return -EINVAL; } regs->DMA_B_START = src_dst_address; if (cfg->head_block->block_size % (cfg->source_data_size * cfg->source_burst_length)) { LOG_ERR("Requested data size is not multiple of bus width"); return -EINVAL; } regs->DMA_LEN_REG = (cfg->head_block->block_size / cfg->source_data_size) - 1; /* Interrupt will be raised once all transfers are complete. */ regs->DMA_INT_REG = (cfg->head_block->block_size / cfg->source_data_size) - 1; if ((cfg->source_handshake != cfg->dest_handshake) || (cfg->source_handshake != 0)/*HW*/) { LOG_ERR("Source/destination handshakes mismatch or invalid"); return -EINVAL; } dma_set_mux_request(cfg->dma_slot, channel); /* Designate that channel has been configured */ data->channel_data[channel].is_dma_configured = true; return 0; } static int dma_smartbond_reload(const struct device *dev, uint32_t channel, uint32_t src, uint32_t dst, size_t size) { struct dma_smartbond_data *data = dev->data; struct channel_regs *regs; if (channel >= DMA_CHANNELS_COUNT) { LOG_ERR("Inavlid DMA channel index"); return -EINVAL; } regs = DMA_CHN2REG(channel); if (!data->channel_data[channel].is_dma_configured) { LOG_ERR("Requested DMA channel should first be configured"); return -EINVAL; } if (size == 0) { LOG_ERR("Min. transfer size is one"); return -EINVAL; } if (DMA_CTRL_REG_GET_FIELD(DMA_ON, regs->DMA_CTRL_REG)) { LOG_ERR("Channel is busy, settings cannot be changed mid-transfer"); return -EBUSY; } if (src % data->channel_data[channel].bus_width) { LOG_ERR("Source address is not bus width aligned"); return -EINVAL; } if (!dma_channel_src_addr_check_and_adjust(channel, &src)) { return -EINVAL; } regs->DMA_A_START = src; if (dst % data->channel_data[channel].bus_width) { LOG_ERR("Destination address is not bus width aligned"); return -EINVAL; } if (!dma_channel_dst_addr_check_and_adjust(channel, &dst)) { return -EINVAL; } regs->DMA_B_START = dst; if (size % (data->channel_data[channel].burst_len * data->channel_data[channel].bus_width)) { LOG_ERR("Requested data size is not multiple of bus width"); return -EINVAL; } regs->DMA_LEN_REG = (size / data->channel_data[channel].bus_width) - 1; /* Interrupt will be raised once all transfers are complete. */ regs->DMA_INT_REG = (size / data->channel_data[channel].bus_width) - 1; return 0; } static int dma_smartbond_start(const struct device *dev, uint32_t channel) { struct channel_regs *regs; struct dma_smartbond_data *data = dev->data; if (channel >= DMA_CHANNELS_COUNT) { LOG_ERR("Inavlid DMA channel index"); return -EINVAL; } regs = DMA_CHN2REG(channel); if (!data->channel_data[channel].is_dma_configured) { LOG_ERR("Requested DMA channel should first be configured"); return -EINVAL; } /* Should return succss if the requested channel is already started. */ if (DMA_CTRL_REG_GET_FIELD(DMA_ON, regs->DMA_CTRL_REG)) { return 0; } dma_smartbond_set_channel_status(channel, true); return 0; } static int dma_smartbond_stop(const struct device *dev, uint32_t channel) { struct channel_regs *regs; if (channel >= DMA_CHANNELS_COUNT) { LOG_ERR("Inavlid DMA channel index"); return -EINVAL; } regs = DMA_CHN2REG(channel); /* * In normal mode DMA_ON is cleared automatically. However we need to clear * the corresponding register mask and disable NVIC if there is no other * channel in use. */ dma_smartbond_set_channel_status(channel, false); return 0; } static int dma_smartbond_suspend(const struct device *dev, uint32_t channel) { if (channel >= DMA_CHANNELS_COUNT) { LOG_ERR("Inavlid DMA channel index"); return -EINVAL; } /* * Freezing the DMA engine is valid for memory-to-memory operations. * Valid memory locations are SYSRAM and/or PSRAM. */ LOG_WRN("DMA is freezed globally"); /* * Freezing the DMA engine can be done universally and not per channel!. * An attempt to disable the channel would result in resetting the IDX * register next time the channel was re-enabled. */ GPREG->SET_FREEZE_REG = GPREG_SET_FREEZE_REG_FRZ_DMA_Msk; return 0; } static int dma_smartbond_resume(const struct device *dev, uint32_t channel) { if (channel >= DMA_CHANNELS_COUNT) { LOG_ERR("Inavlid DMA channel index"); return -EINVAL; } LOG_WRN("DMA is unfreezed globally"); /* Unfreezing the DMA engine can be done unviversally and not per channel! */ GPREG->RESET_FREEZE_REG = GPREG_RESET_FREEZE_REG_FRZ_DMA_Msk; return 0; } static int dma_smartbond_get_status(const struct device *dev, uint32_t channel, struct dma_status *stat) { struct channel_regs *regs; int key; struct dma_smartbond_data *data = dev->data; uint8_t bus_width; uint32_t dma_ctrl_reg, dma_idx_reg, dma_len_reg; if (channel >= DMA_CHANNELS_COUNT) { LOG_ERR("Inavlid DMA channel index"); return -EINVAL; } if (stat == NULL) { LOG_ERR("User should provide a valid pointer to store the status info requested"); } if (!data->channel_data[channel].is_dma_configured) { LOG_ERR("Requested DMA channel should first be configured"); return -EINVAL; } regs = DMA_CHN2REG(channel); /* * The DMA is running in parallel with CPU and so it might happen that an on-going transfer * might be completed the moment user parses the status results. Disable interrupts globally * so there is no chance for a new transfer to be initiated from within ISR and so changing * the channel registers values. */ key = irq_lock(); dma_ctrl_reg = regs->DMA_CTRL_REG; dma_idx_reg = regs->DMA_IDX_REG; dma_len_reg = regs->DMA_LEN_REG; /* Calculate how many byes each transfer consists of. */ bus_width = DMA_CTRL_REG_GET_FIELD(BW, dma_ctrl_reg); if (bus_width == BUS_WIDTH_1B) { bus_width = 1; } else { bus_width <<= 1; } /* Convert transfers to bytes. */ stat->total_copied = dma_idx_reg * bus_width; stat->pending_length = ((dma_len_reg + 1) - dma_idx_reg) * bus_width; stat->busy = DMA_CTRL_REG_GET_FIELD(DMA_ON, dma_ctrl_reg); stat->dir = data->channel_data[channel].dir; /* DMA does not support circular buffer functionality */ stat->free = 0; stat->read_position = 0; stat->write_position = 0; irq_unlock(key); return 0; } static int dma_smartbond_get_attribute(const struct device *dev, uint32_t type, uint32_t *value) { if (value == NULL) { LOG_ERR("User should provide a valid pointer to attribute value"); return -EINVAL; } switch (type) { /* * Source and destination addresses should be multiple of a channel's bus width. * This info could be provided at runtime given that attributes of a specific * channel could be requested. */ case DMA_ATTR_BUFFER_ADDRESS_ALIGNMENT: case DMA_ATTR_COPY_ALIGNMENT: /* * Buffer size should be multiple of a channel's bus width multiplied by burst length. * This info could be provided at runtime given that attributes of a specific channel * could be requested. */ case DMA_ATTR_BUFFER_SIZE_ALIGNMENT: return -ENOSYS; case DMA_ATTR_MAX_BLOCK_COUNT: *value = DMA_BLOCK_COUNT; return 0; default: return -EINVAL; } } static bool dma_smartbond_chan_filter(const struct device *dev, int channel, void *filter_param) { uint32_t requested_channel; if (channel >= DMA_CHANNELS_COUNT) { LOG_ERR("Inavlid DMA channel index"); return -EINVAL; } /* If user does not provide any channel request explicitly, return true. */ if (filter_param == NULL) { return true; } requested_channel = *(uint32_t *)filter_param; if (channel == requested_channel) { return true; } return false; } static struct dma_driver_api dma_smartbond_driver_api = { .config = dma_smartbond_config, .reload = dma_smartbond_reload, .start = dma_smartbond_start, .stop = dma_smartbond_stop, .suspend = dma_smartbond_suspend, .resume = dma_smartbond_resume, .get_status = dma_smartbond_get_status, .get_attribute = dma_smartbond_get_attribute, .chan_filter = dma_smartbond_chan_filter }; static void smartbond_dma_isr(const void *arg) { uint16_t dma_int_status_reg; int i; struct channel_regs *regs; struct dma_smartbond_data *data = ((const struct device *)arg)->data; /* * A single interrupt line is generated for all channels and so each channel * should be parsed separately. */ for (i = 0, dma_int_status_reg = DMA->DMA_INT_STATUS_REG; i < DMA_CHANNELS_COUNT && dma_int_status_reg != 0; ++i, dma_int_status_reg >>= 1) { /* Check if the selected channel has raised the interrupt line */ if (dma_int_status_reg & BIT(0)) { regs = DMA_CHN2REG(i); /* * Should be valid if callbacks are explicitly enabled by users. * Interrupt should be triggered only when the total size of * bytes has been transferred. Bus errors cannot raise interrupts. */ if (data->channel_data[i].cb) { data->channel_data[i].cb((const struct device *)arg, data->channel_data[i].user_data, i, DMA_STATUS_COMPLETE); } /* Channel line should be cleared otherwise ISR will keep firing! */ DMA->DMA_CLEAR_INT_REG = BIT(i); } } } static int dma_smartbond_init(const struct device *dev) { #ifdef CONFIG_DMA_64BIT LOG_ERR("64-bit addressing mode is not supported\n"); return -ENOSYS; #endif int idx; struct dma_smartbond_data *data; data = dev->data; data->dma_ctx.magic = DMA_MAGIC; data->dma_ctx.dma_channels = DMA_CHANNELS_COUNT; data->dma_ctx.atomic = data->channels_atomic; /* Make sure that all channels are disabled. */ for (idx = 0; idx < DMA_CHANNELS_COUNT; idx++) { dma_smartbond_set_channel_status(idx, false); data->channel_data[idx].is_dma_configured = false; } IRQ_CONNECT(SMARTBOND_IRQN, SMARTBOND_IRQ_PRIO, smartbond_dma_isr, DEVICE_DT_INST_GET(0), 0); return 0; } #define SMARTBOND_DMA_INIT(inst) \ BUILD_ASSERT((inst) == 0, "multiple instances are not supported"); \ \ static struct dma_smartbond_data dma_smartbond_data_ ## inst; \ \ DEVICE_DT_INST_DEFINE(0, dma_smartbond_init, NULL, \ &dma_smartbond_data_ ## inst, NULL, \ POST_KERNEL, \ CONFIG_DMA_INIT_PRIORITY, \ &dma_smartbond_driver_api); DT_INST_FOREACH_STATUS_OKAY(SMARTBOND_DMA_INIT)