/** * @file * * @ingroup mpc55xx * * @brief Enhanced Direct Memory Access (eDMA). */ /* * Copyright (c) 2008-2011 embedded brains GmbH. All rights reserved. * * embedded brains GmbH * Obere Lagerstr. 30 * 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.com/license/LICENSE. */ #include #include #include #include #include #include #if MPC55XX_CHIP_TYPE / 10 == 551 #define EDMA_CHANNEL_COUNT 16U #elif MPC55XX_CHIP_TYPE / 10 == 564 #define EDMA_CHANNEL_COUNT 16U #elif MPC55XX_CHIP_TYPE / 10 == 567 #define EDMA_CHANNEL_COUNT 96U #else #define EDMA_CHANNEL_COUNT 64U #endif #define EDMA_CHANNELS_PER_GROUP 32U #define EDMA_CHANNELS_PER_MODULE 64U #define EDMA_GROUP_COUNT ((EDMA_CHANNEL_COUNT + 31U) / 32U) #define EDMA_MODULE_COUNT ((EDMA_CHANNEL_COUNT + 63U) / 64U) #define EDMA_INVALID_CHANNEL EDMA_CHANNEL_COUNT #define EDMA_IS_CHANNEL_INVALID(i) ((unsigned) (i) >= EDMA_CHANNEL_COUNT) #define EDMA_IS_CHANNEL_VALID(i) ((unsigned) (i) < EDMA_CHANNEL_COUNT) #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) #define EDMA_MODULE_BIT(channel) (1U << ((channel) % EDMA_CHANNELS_PER_MODULE)) static uint32_t edma_channel_occupation [EDMA_GROUP_COUNT]; static RTEMS_CHAIN_DEFINE_EMPTY(edma_channel_chain); volatile struct EDMA_tag *edma_get_regs_by_channel(unsigned channel) { #if EDMA_MODULE_COUNT == 1 return &EDMA; #elif EDMA_MODULE_COUNT == 2 return channel < EDMA_CHANNELS_PER_MODULE ? &EDMA_A : &EDMA_B; #else #error "unsupported module count" #endif } 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) { mpc55xx_edma_channel_entry *e = arg; unsigned channel = e->channel; volatile struct EDMA_tag *edma = edma_get_regs_by_channel(channel); edma->CIRQR.R = (uint8_t) channel; e->done(e, 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)) { mpc55xx_edma_channel_entry *e = (mpc55xx_edma_channel_entry *) node; unsigned channel = e->channel; unsigned group_index = EDMA_GROUP_INDEX(channel); unsigned group_bit = EDMA_GROUP_BIT(channel); if ((error_channels [group_index] & group_bit) != 0) { unsigned module_index = EDMA_MODULE_INDEX(channel); e->done(e, error_status [module_index]); } node = rtems_chain_next(node); } } void mpc55xx_edma_enable_hardware_requests(unsigned channel, bool enable) { volatile struct EDMA_tag *edma = edma_get_regs_by_channel(channel); assert(EDMA_IS_CHANNEL_VALID(channel)); if (enable) { edma->SERQR.R = (uint8_t) channel; } else { edma->CERQR.R = (uint8_t) channel; } } void mpc55xx_edma_enable_error_interrupts(unsigned channel, bool enable) { volatile struct EDMA_tag *edma = edma_get_regs_by_channel(channel); assert(EDMA_IS_CHANNEL_VALID(channel)); if (enable) { edma->SEEIR.R = (uint8_t) channel; } else { edma->CEEIR.R = (uint8_t) channel; } } 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; /* Arbitration mode: group round robin, channel fixed */ edma->CR.B.ERGA = 1; edma->CR.B.ERCA = 0; for (channel = 0; channel < channel_count; ++channel) { edma->CPR [channel].R = 0x80U | (channel & 0xfU); } /* Clear TCDs */ memset((void *) &edma->TCD [0], 0, channel_count * sizeof(edma->TCD [0])); } 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) { /* FIXME */ rtems_fatal_error_occurred(0xdeadbeef); } } } rtems_status_code mpc55xx_edma_obtain_channel( mpc55xx_edma_channel_entry *e, unsigned irq_priority ) { rtems_status_code sc = RTEMS_SUCCESSFUL; rtems_interrupt_level level; unsigned channel = e->channel; uint32_t channel_occupation = 0; if (EDMA_IS_CHANNEL_INVALID(channel)) { return RTEMS_INVALID_ID; } rtems_interrupt_disable(level); channel_occupation = edma_bit_array_set( channel, &edma_channel_occupation [0] ); rtems_interrupt_enable(level); if ((channel_occupation & EDMA_GROUP_BIT(channel))) { return RTEMS_RESOURCE_IN_USE; } sc = mpc55xx_interrupt_handler_install( MPC55XX_IRQ_EDMA(channel), "eDMA Channel", RTEMS_INTERRUPT_SHARED, irq_priority, edma_interrupt_handler, e ); if (sc != RTEMS_SUCCESSFUL) { rtems_interrupt_disable(level); edma_bit_array_clear(channel, &edma_channel_occupation [0]); rtems_interrupt_enable(level); return RTEMS_IO_ERROR; } rtems_chain_prepend(&edma_channel_chain, &e->node); mpc55xx_edma_enable_error_interrupts(channel, true); return RTEMS_SUCCESSFUL; } void mpc55xx_edma_release_channel(mpc55xx_edma_channel_entry *e) { rtems_status_code sc = RTEMS_SUCCESSFUL; rtems_interrupt_level level; unsigned channel = e->channel; rtems_interrupt_disable(level); edma_bit_array_clear(channel, &edma_channel_occupation [0]); rtems_interrupt_enable(level); mpc55xx_edma_enable_hardware_requests(channel, false); mpc55xx_edma_enable_error_interrupts(channel, false); rtems_chain_extract(&e->node); sc = rtems_interrupt_handler_remove( MPC55XX_IRQ_EDMA(e->channel), edma_interrupt_handler, e ); if (sc != RTEMS_SUCCESSFUL) { /* FIXME */ rtems_fatal_error_occurred(0xdeadbeef); } e->done(e, 0); }