/** * @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 * * * 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 #include #include #include #include #include #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); } }