diff --git a/boards/microchip/sam/sama7g54_ek/sama7g54_ek.dts b/boards/microchip/sam/sama7g54_ek/sama7g54_ek.dts index d17d3ac722e42..39b37debd9e81 100644 --- a/boards/microchip/sam/sama7g54_ek/sama7g54_ek.dts +++ b/boards/microchip/sam/sama7g54_ek/sama7g54_ek.dts @@ -37,6 +37,18 @@ }; }; +&dma0 { + status = "okay"; +}; + +&dma1 { + status = "okay"; +}; + +&dma2 { + status = "okay"; +}; + &flx3 { mchp,flexcom-mode = ; status = "okay"; diff --git a/drivers/dma/dma_sam_xdmac.c b/drivers/dma/dma_sam_xdmac.c index 436991a482af2..73efb2dd7a7eb 100644 --- a/drivers/dma/dma_sam_xdmac.c +++ b/drivers/dma/dma_sam_xdmac.c @@ -26,7 +26,7 @@ LOG_MODULE_REGISTER(dma_sam_xdmac); #define XDMAC_INT_ERR (XDMAC_CIE_RBIE | XDMAC_CIE_WBIE | XDMAC_CIE_ROIE) -#define DMA_CHANNELS_NO XDMACCHID_NUMBER +#define DMA_CHANNELS_MAX 31 /* DMA channel configuration */ struct sam_xdmac_channel_cfg { @@ -45,13 +45,15 @@ struct sam_xdmac_dev_cfg { /* Device run time data */ struct sam_xdmac_dev_data { - struct sam_xdmac_channel_cfg dma_channels[DMA_CHANNELS_NO]; + struct dma_context dma_ctx; + struct sam_xdmac_channel_cfg dma_channels[DMA_CHANNELS_MAX + 1]; }; static void sam_xdmac_isr(const struct device *dev) { const struct sam_xdmac_dev_cfg *const dev_cfg = dev->config; struct sam_xdmac_dev_data *const dev_data = dev->data; + uint32_t channel_num = dev_data->dma_ctx.dma_channels; Xdmac * const xdmac = dev_cfg->regs; struct sam_xdmac_channel_cfg *channel_cfg; @@ -61,7 +63,7 @@ static void sam_xdmac_isr(const struct device *dev) /* Get global interrupt status */ isr_status = xdmac->XDMAC_GIS; - for (int channel = 0; channel < DMA_CHANNELS_NO; channel++) { + for (int channel = 0; channel < channel_num; channel++) { if (!(isr_status & (1 << channel))) { continue; } @@ -83,10 +85,13 @@ int sam_xdmac_channel_configure(const struct device *dev, uint32_t channel, struct sam_xdmac_channel_config *param) { const struct sam_xdmac_dev_cfg *const dev_cfg = dev->config; + struct sam_xdmac_dev_data *const dev_data = dev->data; + uint32_t channel_num = dev_data->dma_ctx.dma_channels; Xdmac * const xdmac = dev_cfg->regs; - if (channel >= DMA_CHANNELS_NO) { + if (channel >= channel_num) { + LOG_ERR("Channel %d out of range", channel); return -EINVAL; } @@ -126,10 +131,13 @@ int sam_xdmac_transfer_configure(const struct device *dev, uint32_t channel, struct sam_xdmac_transfer_config *param) { const struct sam_xdmac_dev_cfg *const dev_cfg = dev->config; + struct sam_xdmac_dev_data *const dev_data = dev->data; + uint32_t channel_num = dev_data->dma_ctx.dma_channels; Xdmac * const xdmac = dev_cfg->regs; - if (channel >= DMA_CHANNELS_NO) { + if (channel >= channel_num) { + LOG_ERR("Channel %d out of range", channel); return -EINVAL; } @@ -179,13 +187,15 @@ static int sam_xdmac_config(const struct device *dev, uint32_t channel, struct dma_config *cfg) { struct sam_xdmac_dev_data *const dev_data = dev->data; + uint32_t channel_num = dev_data->dma_ctx.dma_channels; struct sam_xdmac_channel_config channel_cfg; struct sam_xdmac_transfer_config transfer_cfg; uint32_t burst_size; uint32_t data_size; int ret; - if (channel >= DMA_CHANNELS_NO) { + if (channel >= channel_num) { + LOG_ERR("Channel %d out of range", channel); return -EINVAL; } @@ -233,6 +243,10 @@ static int sam_xdmac_config(const struct device *dev, uint32_t channel, | XDMAC_CC_MBSIZE(burst_size == 0U ? 0 : burst_size - 1) | XDMAC_CC_SAM_INCREMENTED_AM | XDMAC_CC_DAM_INCREMENTED_AM; +#if defined(CONFIG_SOC_SERIES_SAMA7G5) + /* When a memory-to-memory transfer is performed, configure PERID to 0x7F. */ + cfg->dma_slot = 0x7F; +#endif break; case MEMORY_TO_PERIPHERAL: channel_cfg.cfg = @@ -254,11 +268,14 @@ static int sam_xdmac_config(const struct device *dev, uint32_t channel, return -EINVAL; } - channel_cfg.cfg |= - XDMAC_CC_DWIDTH(data_size) - | XDMAC_CC_SIF_AHB_IF1 - | XDMAC_CC_DIF_AHB_IF1 - | XDMAC_CC_PERID(cfg->dma_slot); + channel_cfg.cfg |= XDMAC_CC_DWIDTH(data_size) | +#ifdef XDMAC_CC_SIF_AHB_IF1 + XDMAC_CC_SIF_AHB_IF1 | +#endif +#ifdef XDMAC_CC_DIF_AHB_IF1 + XDMAC_CC_DIF_AHB_IF1 | +#endif + XDMAC_CC_PERID(cfg->dma_slot); channel_cfg.ds_msp = 0U; channel_cfg.sus = 0U; channel_cfg.dus = 0U; @@ -300,11 +317,13 @@ static int sam_xdmac_transfer_reload(const struct device *dev, uint32_t channel, int sam_xdmac_transfer_start(const struct device *dev, uint32_t channel) { const struct sam_xdmac_dev_cfg *config = dev->config; + struct sam_xdmac_dev_data *const dev_data = dev->data; + uint32_t channel_num = dev_data->dma_ctx.dma_channels; Xdmac * const xdmac = config->regs; - if (channel >= DMA_CHANNELS_NO) { - LOG_DBG("Channel %d out of range", channel); + if (channel >= channel_num) { + LOG_ERR("Channel %d out of range", channel); return -EINVAL; } @@ -325,10 +344,13 @@ int sam_xdmac_transfer_start(const struct device *dev, uint32_t channel) int sam_xdmac_transfer_stop(const struct device *dev, uint32_t channel) { const struct sam_xdmac_dev_cfg *config = dev->config; + struct sam_xdmac_dev_data *const dev_data = dev->data; + uint32_t channel_num = dev_data->dma_ctx.dma_channels; Xdmac * const xdmac = config->regs; - if (channel >= DMA_CHANNELS_NO) { + if (channel >= channel_num) { + LOG_ERR("Channel %d out of range", channel); return -EINVAL; } @@ -352,9 +374,16 @@ int sam_xdmac_transfer_stop(const struct device *dev, uint32_t channel) static int sam_xdmac_initialize(const struct device *dev) { const struct sam_xdmac_dev_cfg *const dev_cfg = dev->config; + struct sam_xdmac_dev_data *const dev_data = dev->data; Xdmac * const xdmac = dev_cfg->regs; + dev_data->dma_ctx.dma_channels = FIELD_GET(XDMAC_GTYPE_NB_CH_Msk, xdmac->XDMAC_GTYPE) + 1; + if (dev_data->dma_ctx.dma_channels > DMA_CHANNELS_MAX + 1) { + LOG_ERR("Maximum supported channels is %d", DMA_CHANNELS_MAX + 1); + return -EINVAL; + } + /* Configure interrupts */ dev_cfg->irq_config(); @@ -375,6 +404,74 @@ static int sam_xdmac_initialize(const struct device *dev) return 0; } +static int xdmac_suspend(const struct device *dev, uint32_t channel) +{ + const struct sam_xdmac_dev_cfg *config = dev->config; + struct sam_xdmac_dev_data *const dev_data = dev->data; + uint32_t channel_num = dev_data->dma_ctx.dma_channels; + + Xdmac * const xdmac = config->regs; + + if (channel >= channel_num) { + LOG_ERR("Channel %d out of range", channel); + return -EINVAL; + } + + if (!(xdmac->XDMAC_GS & BIT(channel))) { + LOG_DBG("Channel %d not enabled", channel); + return -EINVAL; + } + +#if defined(CONFIG_SOC_SERIES_SAMX7X) + if (xdmac->XDMAC_GRS & BIT(channel) || xdmac->XDMAC_GWS & BIT(channel)) { +#elif defined(CONFIG_SOC_SERIES_SAMA7G5) + if (xdmac->XDMAC_GRSS & BIT(channel) || xdmac->XDMAC_GWSS & BIT(channel)) { +#else +#error Unsupported SoC family +#endif + LOG_DBG("Channel %d already suspended", channel); + return 0; + } + + xdmac->XDMAC_GRWS |= BIT(channel); + + return 0; +} + +static int xdmac_resume(const struct device *dev, uint32_t channel) +{ + const struct sam_xdmac_dev_cfg *config = dev->config; + struct sam_xdmac_dev_data *const dev_data = dev->data; + uint32_t channel_num = dev_data->dma_ctx.dma_channels; + + Xdmac * const xdmac = config->regs; + + if (channel >= channel_num) { + LOG_ERR("Channel %d out of range", channel); + return -EINVAL; + } + + if (!(xdmac->XDMAC_GS & BIT(channel))) { + LOG_DBG("Channel %d not enabled", channel); + return -EINVAL; + } + +#if defined(CONFIG_SOC_SERIES_SAMX7X) + if (!(xdmac->XDMAC_GRS & BIT(channel) || xdmac->XDMAC_GWS & BIT(channel))) { +#elif defined(CONFIG_SOC_SERIES_SAMA7G5) + if (!(xdmac->XDMAC_GRSS & BIT(channel) || xdmac->XDMAC_GWSS & BIT(channel))) { +#else +#error Unsupported SoC family +#endif + LOG_DBG("Channel %d not suspended", channel); + return 0; + } + + xdmac->XDMAC_GRWR |= BIT(channel); + + return 0; +} + static int sam_xdmac_get_status(const struct device *dev, uint32_t channel, struct dma_status *status) { @@ -404,26 +501,34 @@ static DEVICE_API(dma, sam_xdmac_driver_api) = { .reload = sam_xdmac_transfer_reload, .start = sam_xdmac_transfer_start, .stop = sam_xdmac_transfer_stop, + .suspend = xdmac_suspend, + .resume = xdmac_resume, .get_status = sam_xdmac_get_status, }; -/* DMA0 */ - -static void dma0_sam_irq_config(void) -{ - IRQ_CONNECT(DT_INST_IRQN(0), DT_INST_IRQ(0, priority), sam_xdmac_isr, - DEVICE_DT_INST_GET(0), 0); -} - -static const struct sam_xdmac_dev_cfg dma0_sam_config = { - .regs = (Xdmac *)DT_INST_REG_ADDR(0), - .irq_config = dma0_sam_irq_config, - .clock_cfg = SAM_DT_INST_CLOCK_PMC_CFG(0), - .irq_id = DT_INST_IRQN(0), -}; - -static struct sam_xdmac_dev_data dma0_sam_data; - -DEVICE_DT_INST_DEFINE(0, sam_xdmac_initialize, NULL, - &dma0_sam_data, &dma0_sam_config, POST_KERNEL, - CONFIG_DMA_INIT_PRIORITY, &sam_xdmac_driver_api); +#define DMA_INIT(n) \ + static void dma##n##_irq_config(void) \ + { \ + IRQ_CONNECT(DT_INST_IRQN(n), DT_INST_IRQ(n, priority), \ + sam_xdmac_isr, DEVICE_DT_INST_GET(n), 0); \ + } \ + \ + static const struct sam_xdmac_dev_cfg dma##n##_config = { \ + .regs = (Xdmac *)DT_INST_REG_ADDR(n), \ + .irq_config = dma##n##_irq_config, \ + .clock_cfg = SAM_DT_INST_CLOCK_PMC_CFG(n), \ + .irq_id = DT_INST_IRQN(n), \ + }; \ + \ + static ATOMIC_DEFINE(dma_channels_atomic_##n, DMA_CHANNELS_MAX); \ + \ + static struct sam_xdmac_dev_data dma##n##_data = { \ + .dma_ctx.magic = DMA_MAGIC, \ + .dma_ctx.atomic = dma_channels_atomic_##n, \ + }; \ + \ + DEVICE_DT_INST_DEFINE(n, &sam_xdmac_initialize, NULL, \ + &dma##n##_data, &dma##n##_config, POST_KERNEL, \ + CONFIG_DMA_INIT_PRIORITY, &sam_xdmac_driver_api); + +DT_INST_FOREACH_STATUS_OKAY(DMA_INIT) diff --git a/dts/arm/microchip/sam/sama7g5.dtsi b/dts/arm/microchip/sam/sama7g5.dtsi index 5a68520614541..d1c191262f00b 100644 --- a/dts/arm/microchip/sam/sama7g5.dtsi +++ b/dts/arm/microchip/sam/sama7g5.dtsi @@ -46,6 +46,39 @@ #clock-cells = <1>; }; + dma0: dma-controller@e2808000 { + compatible = "atmel,sam-xdmac"; + reg = <0xe2808000 0x1000>; + interrupt-parent = <&gic>; + interrupts = ; + #dma-cells = <2>; + clocks = <&pmc PMC_TYPE_PERIPHERAL 22>; + clock-names = "dma_clk"; + status = "disabled"; + }; + + dma1: dma-controller@e280c000 { + compatible = "atmel,sam-xdmac"; + reg = <0xe280c000 0x1000>; + interrupt-parent = <&gic>; + interrupts = ; + #dma-cells = <2>; + clocks = <&pmc PMC_TYPE_PERIPHERAL 23>; + clock-names = "dma_clk"; + status = "disabled"; + }; + + dma2: dma-controller@e1200000 { + compatible = "atmel,sam-xdmac"; + reg = <0xe1200000 0x1000>; + interrupt-parent = <&gic>; + interrupts = ; + #dma-cells = <2>; + clocks = <&pmc PMC_TYPE_PERIPHERAL 24>; + clock-names = "dma_clk"; + status = "disabled"; + }; + flx0: flexcom@e1818000 { compatible = "microchip,sam-flexcom"; reg = <0xe1818000 0x200>; diff --git a/soc/microchip/sam/sama7g5/Kconfig.defconfig b/soc/microchip/sam/sama7g5/Kconfig.defconfig index dd102f1621cc6..067cabe8f301f 100644 --- a/soc/microchip/sam/sama7g5/Kconfig.defconfig +++ b/soc/microchip/sam/sama7g5/Kconfig.defconfig @@ -14,4 +14,7 @@ config SYS_CLOCK_HW_CYCLES_PER_SEC config MMU default y +config CACHE_MANAGEMENT + default y + endif # SOC_SERIES_SAMA7G5 diff --git a/soc/microchip/sam/sama7g5/soc.c b/soc/microchip/sam/sama7g5/soc.c index 169d9e551b26b..555890cfd841b 100644 --- a/soc/microchip/sam/sama7g5/soc.c +++ b/soc/microchip/sam/sama7g5/soc.c @@ -15,6 +15,12 @@ MT_STRONGLY_ORDERED | MPERM_R | MPERM_W),), \ ()) +#define MMU_REGION_XDMAC_DEFN(idx, n) \ + COND_CODE_1(DT_NODE_HAS_STATUS_OKAY(DT_NODELABEL(dma##n)), \ + (MMU_REGION_FLAT_ENTRY("xdmac"#n, XDMAC##n##_BASE_ADDRESS, 0x4000, \ + MT_STRONGLY_ORDERED | MPERM_R | MPERM_W),), \ + ()) + static const struct arm_mmu_region mmu_regions[] = { MMU_REGION_FLAT_ENTRY("vectors", CONFIG_KERNEL_VM_BASE, 0x1000, MT_STRONGLY_ORDERED | MPERM_R | MPERM_X), @@ -35,6 +41,8 @@ static const struct arm_mmu_region mmu_regions[] = { MMU_REGION_FLAT_ENTRY("sckc", SCKC_BASE_ADDRESS, 0x4, MT_STRONGLY_ORDERED | MPERM_R | MPERM_W), + + FOR_EACH_IDX(MMU_REGION_XDMAC_DEFN, (), 0, 1, 2) }; const struct arm_mmu_config mmu_config = {