summaryrefslogtreecommitdiffstats
path: root/bsps/shared/dev
diff options
context:
space:
mode:
authorChristian Mauderer <christian.mauderer@embedded-brains.de>2020-11-24 10:47:11 +0100
committerChristian Mauderer <christian.mauderer@embedded-brains.de>2021-01-21 10:17:31 +0100
commit301bbc3a4d80ab26fe0ac09a139d9c6ea0f997ee (patch)
tree71307cd4062a6aa69b2491536b3c459f9bbb0f40 /bsps/shared/dev
parentbsps/imxrt: Use standard names to avoid warnings (diff)
downloadrtems-301bbc3a4d80ab26fe0ac09a139d9c6ea0f997ee.tar.bz2
bsps/shared: Copy fsl-edma from mpc55xx
This is a preparation for making the driver universal. Update #4180
Diffstat (limited to 'bsps/shared/dev')
-rw-r--r--bsps/shared/dev/dma/fsl-edma.c329
1 files changed, 329 insertions, 0 deletions
diff --git a/bsps/shared/dev/dma/fsl-edma.c b/bsps/shared/dev/dma/fsl-edma.c
new file mode 100644
index 0000000000..c34624ed10
--- /dev/null
+++ b/bsps/shared/dev/dma/fsl-edma.c
@@ -0,0 +1,329 @@
+/**
+ * @file
+ *
+ * @ingroup RTEMSBSPsPowerPCMPC55XX
+ *
+ * @brief Enhanced Direct Memory Access (eDMA).
+ */
+
+/*
+ * Copyright (c) 2008-2013 embedded brains GmbH. All rights reserved.
+ *
+ * embedded brains GmbH
+ * Dornierstr. 4
+ * 82178 Puchheim
+ * Germany
+ * <rtems@embedded-brains.de>
+ *
+ * The license and distribution terms for this file may be
+ * found in the file LICENSE in this distribution or at
+ * http://www.rtems.org/license/LICENSE.
+ */
+
+#include <mpc55xx/edma.h>
+#include <mpc55xx/mpc55xx.h>
+
+#include <assert.h>
+
+#include <bsp.h>
+#include <bsp/fatal.h>
+#include <bsp/irq.h>
+
+#define EDMA_CHANNELS_PER_GROUP 32U
+
+#define EDMA_GROUP_COUNT ((EDMA_CHANNEL_COUNT + 31U) / 32U)
+
+#define EDMA_GROUP_INDEX(channel) ((channel) / EDMA_CHANNELS_PER_GROUP)
+
+#define EDMA_GROUP_BIT(channel) (1U << ((channel) % EDMA_CHANNELS_PER_GROUP))
+
+#define EDMA_MODULE_INDEX(channel) ((channel) / EDMA_CHANNELS_PER_MODULE)
+
+static uint32_t edma_channel_occupation [EDMA_GROUP_COUNT];
+
+static RTEMS_CHAIN_DEFINE_EMPTY(edma_channel_chain);
+
+static unsigned edma_channel_index_of_tcd(volatile struct tcd_t *edma_tcd)
+{
+ volatile struct EDMA_tag *edma = mpc55xx_edma_by_tcd(edma_tcd);
+ unsigned channel = edma_tcd - &edma->TCD[0];
+
+#if EDMA_MODULE_COUNT == 1
+ return channel;
+#elif EDMA_MODULE_COUNT == 2
+ return channel + (&EDMA_A == edma ? 0 : EDMA_CHANNELS_PER_MODULE);
+#else
+ #error "unsupported module count"
+#endif
+}
+
+static volatile struct EDMA_tag *edma_get_regs_by_module(unsigned module)
+{
+#if EDMA_MODULE_COUNT == 1
+ return &EDMA;
+#elif EDMA_MODULE_COUNT == 2
+ return module == 0 ? &EDMA_A : &EDMA_B;
+#else
+ #error "unsupported module count"
+#endif
+}
+
+static uint32_t edma_bit_array_set(unsigned channel, uint32_t *bit_array)
+{
+ unsigned array = channel / 32;
+ uint32_t bit = 1U << (channel % 32);
+ uint32_t previous = bit_array [array];
+
+ bit_array [array] = previous | bit;
+
+ return previous;
+}
+
+static uint32_t edma_bit_array_clear(unsigned channel, uint32_t *bit_array)
+{
+ unsigned array = channel / 32;
+ uint32_t bit = 1U << (channel % 32);
+ uint32_t previous = bit_array [array];
+
+ bit_array [array] = previous & ~bit;
+
+ return previous;
+}
+
+static void edma_interrupt_handler(void *arg)
+{
+ edma_channel_context *ctx = arg;
+
+ mpc55xx_edma_clear_interrupts(ctx->edma_tcd);
+
+ (*ctx->done)(ctx, 0);
+}
+
+static void edma_interrupt_error_handler(void *arg)
+{
+ rtems_chain_control *chain = &edma_channel_chain;
+ rtems_chain_node *node = rtems_chain_first(chain);
+ uint32_t error_channels [] = {
+#if EDMA_GROUP_COUNT >= 1
+ EDMA.ERL.R
+#endif
+#if EDMA_GROUP_COUNT >= 2
+ , EDMA.ERH.R
+#endif
+#if EDMA_GROUP_COUNT >= 3
+ , EDMA_B.ERL.R
+#endif
+ };
+ uint32_t error_status [] = {
+#if EDMA_GROUP_COUNT >= 1
+ EDMA.ESR.R
+#endif
+#if EDMA_GROUP_COUNT >= 3
+ , EDMA_B.ESR.R
+#endif
+ };
+
+#if EDMA_GROUP_COUNT >= 1
+ EDMA.ERL.R = error_channels [0];
+#endif
+#if EDMA_GROUP_COUNT >= 2
+ EDMA.ERH.R = error_channels [1];
+#endif
+#if EDMA_GROUP_COUNT >= 3
+ EDMA_B.ERL.R = error_channels [2];
+#endif
+
+ while (!rtems_chain_is_tail(chain, node)) {
+ edma_channel_context *ctx = (edma_channel_context *) node;
+ unsigned channel_index = edma_channel_index_of_tcd(ctx->edma_tcd);
+ unsigned group_index = EDMA_GROUP_INDEX(channel_index);
+ unsigned group_bit = EDMA_GROUP_BIT(channel_index);
+
+ if ((error_channels [group_index] & group_bit) != 0) {
+ unsigned module_index = EDMA_MODULE_INDEX(channel_index);
+
+ (*ctx->done)(ctx, error_status [module_index]);
+ }
+
+ node = rtems_chain_next(node);
+ }
+}
+
+void mpc55xx_edma_init(void)
+{
+ rtems_status_code sc = RTEMS_SUCCESSFUL;
+ unsigned channel_remaining = EDMA_CHANNEL_COUNT;
+ unsigned module = 0;
+ unsigned group = 0;
+
+ for (module = 0; module < EDMA_MODULE_COUNT; ++module) {
+ volatile struct EDMA_tag *edma = edma_get_regs_by_module(module);
+ unsigned channel_count = channel_remaining < EDMA_CHANNELS_PER_MODULE ?
+ channel_remaining : EDMA_CHANNELS_PER_MODULE;
+ unsigned channel = 0;
+
+ channel_remaining -= channel_count;
+
+ /* Disable requests */
+ edma->CERQR.B.CERQ = 0x40;
+
+ /* Arbitration mode: group round robin, channel fixed */
+ edma->CR.B.ERGA = 1;
+ edma->CR.B.ERCA = 0;
+ for (channel = 0; channel < channel_count; ++channel) {
+ volatile struct tcd_t *tcd = &edma->TCD [channel];
+ edma->CPR [channel].R = 0x80U | (channel & 0xfU);
+
+ /* Initialize TCD, stop channel first */
+ tcd->BMF.R = 0;
+ tcd->SADDR = 0;
+ tcd->SDF.R = 0;
+ tcd->NBYTES = 0;
+ tcd->SLAST = 0;
+ tcd->DADDR = 0;
+ tcd->CDF.R = 0;
+ tcd->DLAST_SGA = 0;
+ }
+
+ /* Clear interrupt requests */
+ edma->CIRQR.B.CINT = 0x40;
+ edma->CER.B.CERR = 0x40;
+ }
+
+ for (group = 0; group < EDMA_GROUP_COUNT; ++group) {
+ sc = mpc55xx_interrupt_handler_install(
+ MPC55XX_IRQ_EDMA_ERROR(group),
+ "eDMA Error",
+ RTEMS_INTERRUPT_UNIQUE,
+ MPC55XX_INTC_DEFAULT_PRIORITY,
+ edma_interrupt_error_handler,
+ NULL
+ );
+ if (sc != RTEMS_SUCCESSFUL) {
+ bsp_fatal(MPC55XX_FATAL_EDMA_IRQ_INSTALL);
+ }
+ }
+}
+
+rtems_status_code mpc55xx_edma_obtain_channel_by_tcd(
+ volatile struct tcd_t *edma_tcd
+)
+{
+ rtems_status_code sc = RTEMS_SUCCESSFUL;
+ unsigned channel_index = edma_channel_index_of_tcd(edma_tcd);
+ rtems_interrupt_level level;
+ uint32_t channel_occupation;
+
+ rtems_interrupt_disable(level);
+ channel_occupation = edma_bit_array_set(
+ channel_index,
+ &edma_channel_occupation [0]
+ );
+ rtems_interrupt_enable(level);
+
+ if ((channel_occupation & EDMA_GROUP_BIT(channel_index)) != 0) {
+ sc = RTEMS_RESOURCE_IN_USE;
+ }
+
+ return sc;
+}
+
+void mpc55xx_edma_release_channel_by_tcd(volatile struct tcd_t *edma_tcd)
+{
+ unsigned channel_index = edma_channel_index_of_tcd(edma_tcd);
+ rtems_interrupt_level level;
+
+ rtems_interrupt_disable(level);
+ edma_bit_array_clear(channel_index, &edma_channel_occupation [0]);
+ rtems_interrupt_enable(level);
+
+ mpc55xx_edma_disable_hardware_requests(edma_tcd);
+ mpc55xx_edma_disable_error_interrupts(edma_tcd);
+}
+
+rtems_status_code mpc55xx_edma_obtain_channel(
+ edma_channel_context *ctx,
+ unsigned irq_priority
+)
+{
+ rtems_status_code sc = mpc55xx_edma_obtain_channel_by_tcd(ctx->edma_tcd);
+ if (sc == RTEMS_SUCCESSFUL) {
+ unsigned channel_index = edma_channel_index_of_tcd(ctx->edma_tcd);
+
+ sc = mpc55xx_interrupt_handler_install(
+ MPC55XX_IRQ_EDMA(channel_index),
+ "eDMA Channel",
+ RTEMS_INTERRUPT_SHARED,
+ irq_priority,
+ edma_interrupt_handler,
+ ctx
+ );
+ if (sc == RTEMS_SUCCESSFUL) {
+ rtems_chain_prepend(&edma_channel_chain, &ctx->node);
+ mpc55xx_edma_enable_error_interrupts(ctx->edma_tcd);
+ } else {
+ mpc55xx_edma_release_channel_by_tcd(ctx->edma_tcd);
+ sc = RTEMS_IO_ERROR;
+ }
+ }
+
+ return sc;
+}
+
+void mpc55xx_edma_release_channel(edma_channel_context *ctx)
+{
+ rtems_status_code sc = RTEMS_SUCCESSFUL;
+ unsigned channel_index = edma_channel_index_of_tcd(ctx->edma_tcd);
+
+ mpc55xx_edma_release_channel_by_tcd(ctx->edma_tcd);
+ rtems_chain_extract(&ctx->node);
+
+ sc = rtems_interrupt_handler_remove(
+ MPC55XX_IRQ_EDMA(channel_index),
+ edma_interrupt_handler,
+ ctx
+ );
+ if (sc != RTEMS_SUCCESSFUL) {
+ bsp_fatal(MPC55XX_FATAL_EDMA_IRQ_REMOVE);
+ }
+}
+
+void mpc55xx_edma_copy(
+ volatile struct tcd_t *edma_tcd,
+ const struct tcd_t *source_tcd
+)
+{
+ /* Clear DONE flag */
+ edma_tcd->BMF.R = 0;
+
+ edma_tcd->SADDR = source_tcd->SADDR;
+ edma_tcd->SDF.R = source_tcd->SDF.R;
+ edma_tcd->NBYTES = source_tcd->NBYTES;
+ edma_tcd->SLAST = source_tcd->SLAST;
+ edma_tcd->DADDR = source_tcd->DADDR;
+ edma_tcd->CDF.R = source_tcd->CDF.R;
+ edma_tcd->DLAST_SGA = source_tcd->DLAST_SGA;
+ edma_tcd->BMF.R = source_tcd->BMF.R;
+}
+
+void mpc55xx_edma_copy_and_enable_hardware_requests(
+ volatile struct tcd_t *edma_tcd,
+ const struct tcd_t *source_tcd
+)
+{
+ mpc55xx_edma_copy(edma_tcd, source_tcd);
+ mpc55xx_edma_enable_hardware_requests(edma_tcd);
+}
+
+void mpc55xx_edma_sg_link(
+ volatile struct tcd_t *edma_tcd,
+ const struct tcd_t *source_tcd
+)
+{
+ edma_tcd->DLAST_SGA = (int32_t) source_tcd;
+ edma_tcd->BMF.B.E_SG = 1;
+
+ if (!edma_tcd->BMF.B.E_SG) {
+ mpc55xx_edma_copy(edma_tcd, source_tcd);
+ }
+}