[574fb67] | 1 | /** |
---|
| 2 | * @file |
---|
| 3 | * |
---|
| 4 | * @ingroup mpc55xx |
---|
| 5 | * |
---|
| 6 | * @brief Enhanced Direct Memory Access (eDMA). |
---|
| 7 | */ |
---|
| 8 | |
---|
| 9 | /* |
---|
| 10 | * Copyright (c) 2008 |
---|
| 11 | * Embedded Brains GmbH |
---|
| 12 | * Obere Lagerstr. 30 |
---|
| 13 | * D-82178 Puchheim |
---|
| 14 | * Germany |
---|
| 15 | * rtems@embedded-brains.de |
---|
| 16 | * |
---|
| 17 | * The license and distribution terms for this file may be found in the file |
---|
| 18 | * LICENSE in this distribution or at http://www.rtems.com/license/LICENSE. |
---|
| 19 | */ |
---|
| 20 | |
---|
| 21 | #include <mpc55xx/regs.h> |
---|
| 22 | #include <mpc55xx/edma.h> |
---|
| 23 | #include <mpc55xx/mpc55xx.h> |
---|
| 24 | |
---|
| 25 | #include <string.h> |
---|
| 26 | |
---|
[d374492] | 27 | #include <bsp/irq.h> |
---|
| 28 | |
---|
[574fb67] | 29 | #define RTEMS_STATUS_CHECKS_USE_PRINTK |
---|
| 30 | |
---|
| 31 | #include <rtems/status-checks.h> |
---|
| 32 | |
---|
[88919d0] | 33 | #if ((MPC55XX_CHIP_TYPE >= 5510) && (MPC55XX_CHIP_TYPE <= 5517)) |
---|
[29313369] | 34 | #define MPC55XX_EDMA_CHANNEL_COUNT 16U |
---|
[88919d0] | 35 | #else /* ((MPC55XX_CHIP_TYPE >= 5510) && (MPC55XX_CHIP_TYPE <= 5517)) */ |
---|
[29313369] | 36 | #define MPC55XX_EDMA_CHANNEL_COUNT 64U |
---|
[88919d0] | 37 | #endif /* ((MPC55XX_CHIP_TYPE >= 5510) && (MPC55XX_CHIP_TYPE <= 5517)) */ |
---|
[574fb67] | 38 | |
---|
[29313369] | 39 | #define MPC55XX_EDMA_INVALID_CHANNEL MPC55XX_EDMA_CHANNEL_COUNT |
---|
[574fb67] | 40 | |
---|
[29313369] | 41 | #define MPC55XX_EDMA_IS_CHANNEL_INVALID( i) ((unsigned) (i) >= MPC55XX_EDMA_CHANNEL_COUNT) |
---|
[574fb67] | 42 | |
---|
[29313369] | 43 | #define MPC55XX_EDMA_IS_CHANNEL_VALID( i) ((unsigned) (i) < MPC55XX_EDMA_CHANNEL_COUNT) |
---|
[574fb67] | 44 | |
---|
[d374492] | 45 | #define MPC55XX_EDMA_IRQ_PRIORITY MPC55XX_INTC_DEFAULT_PRIORITY |
---|
[574fb67] | 46 | |
---|
[d374492] | 47 | #define MPC55XX_EDMA_CHANNEL_FLAG( channel) ((uint64_t) 1 << (channel)) |
---|
[574fb67] | 48 | |
---|
[d374492] | 49 | static uint64_t mpc55xx_edma_channel_occupation = 0; |
---|
[574fb67] | 50 | |
---|
[d374492] | 51 | static rtems_chain_control mpc55xx_edma_channel_chain; |
---|
[574fb67] | 52 | |
---|
[60e5832] | 53 | static void mpc55xx_edma_interrupt_handler( void *arg) |
---|
[574fb67] | 54 | { |
---|
[d374492] | 55 | mpc55xx_edma_channel_entry *e = (mpc55xx_edma_channel_entry *) arg; |
---|
| 56 | |
---|
[574fb67] | 57 | #ifdef DEBUG |
---|
| 58 | uint32_t citer = EDMA.TCD [e->channel].CITERE_LINK ? EDMA.TCD [e->channel].CITER & EDMA_TCD_BITER_LINKED_MASK : EDMA.TCD [e->channel].CITER; |
---|
[d374492] | 59 | RTEMS_DEBUG_PRINT( "channel %i (CITER = %i)\n", e->channel, citer); |
---|
[574fb67] | 60 | #endif /* DEBUG */ |
---|
| 61 | |
---|
[d374492] | 62 | /* Clear interrupt */ |
---|
| 63 | EDMA.CIRQR.R = (uint8_t) e->channel; |
---|
| 64 | |
---|
| 65 | /* Notify user */ |
---|
| 66 | e->done( e, 0); |
---|
[574fb67] | 67 | } |
---|
| 68 | |
---|
[60e5832] | 69 | static void mpc55xx_edma_interrupt_error_handler( void *arg) |
---|
[574fb67] | 70 | { |
---|
[d374492] | 71 | rtems_chain_control *chain = &mpc55xx_edma_channel_chain; |
---|
[95b18d0] | 72 | rtems_chain_node *node = rtems_chain_first( chain ); |
---|
[d374492] | 73 | unsigned i = 0; |
---|
| 74 | uint64_t error_status = EDMA.ESR.R; |
---|
| 75 | uint64_t error_channels = ((uint64_t) EDMA.ERH.R << 32) | EDMA.ERL.R; |
---|
| 76 | uint64_t error_channels_update = 0; |
---|
| 77 | |
---|
| 78 | RTEMS_DEBUG_PRINT( "error channels: %08x %08x\n", (unsigned) (error_channels >> 32), (unsigned) error_channels); |
---|
| 79 | |
---|
| 80 | /* Mark all channels that are linked to a channel with errors */ |
---|
| 81 | do { |
---|
| 82 | error_channels_update = 0; |
---|
| 83 | |
---|
[29313369] | 84 | for (i = 0; i < MPC55XX_EDMA_CHANNEL_COUNT; ++i) { |
---|
[d374492] | 85 | uint64_t channel_flags = 0; |
---|
| 86 | unsigned minor_link = i; |
---|
| 87 | unsigned major_link = i; |
---|
| 88 | |
---|
| 89 | /* Check if we have linked channels */ |
---|
| 90 | if (EDMA.TCD [i].BMF.B.BITERE_LINK) { |
---|
| 91 | minor_link = EDMA_TCD_BITER_LINK( i); |
---|
| 92 | } |
---|
| 93 | if (EDMA.TCD [i].BMF.B.MAJORE_LINK) { |
---|
| 94 | major_link = EDMA.TCD [i].BMF.B.MAJORLINKCH; |
---|
| 95 | } |
---|
[574fb67] | 96 | |
---|
[d374492] | 97 | /* Set flags related to this channel */ |
---|
| 98 | channel_flags = MPC55XX_EDMA_CHANNEL_FLAG( i) | MPC55XX_EDMA_CHANNEL_FLAG( minor_link) | MPC55XX_EDMA_CHANNEL_FLAG( major_link); |
---|
| 99 | |
---|
| 100 | /* Any errors in these channels? */ |
---|
[b450e0c] | 101 | if ( error_channels & channel_flags ) { |
---|
[d374492] | 102 | /* Get new error channels */ |
---|
| 103 | uint64_t update = (error_channels & channel_flags) ^ channel_flags; |
---|
[574fb67] | 104 | |
---|
[d374492] | 105 | /* Update error channels */ |
---|
[b450e0c] | 106 | error_channels |= channel_flags; |
---|
[d374492] | 107 | |
---|
| 108 | /* Contribute to the update of this round */ |
---|
[b450e0c] | 109 | error_channels_update |= update; |
---|
[d374492] | 110 | } |
---|
[574fb67] | 111 | } |
---|
[d374492] | 112 | } while (error_channels_update != 0); |
---|
| 113 | |
---|
| 114 | RTEMS_DEBUG_PRINT( "error channels (all): %08x %08x\n", (unsigned) (error_channels >> 32), (unsigned) error_channels); |
---|
[574fb67] | 115 | |
---|
| 116 | /* Process the channels related to errors */ |
---|
[d374492] | 117 | while (!rtems_chain_is_tail( chain, node)) { |
---|
| 118 | mpc55xx_edma_channel_entry *e = (mpc55xx_edma_channel_entry *) node; |
---|
| 119 | |
---|
[b450e0c] | 120 | if ( error_channels & MPC55XX_EDMA_CHANNEL_FLAG( e->channel)) { |
---|
[d374492] | 121 | mpc55xx_edma_enable_hardware_requests( e->channel, false); |
---|
| 122 | |
---|
| 123 | /* Notify user */ |
---|
| 124 | e->done( e, error_status); |
---|
[574fb67] | 125 | } |
---|
[d374492] | 126 | |
---|
| 127 | node = node->next; |
---|
[574fb67] | 128 | } |
---|
| 129 | |
---|
| 130 | /* Clear the error interrupt requests */ |
---|
[29313369] | 131 | for (i = 0; i < MPC55XX_EDMA_CHANNEL_COUNT; ++i) { |
---|
[b450e0c] | 132 | if ( error_channels & MPC55XX_EDMA_CHANNEL_FLAG( i)) { |
---|
[1dab788] | 133 | EDMA.CER.R = (uint8_t) i; |
---|
[574fb67] | 134 | } |
---|
| 135 | } |
---|
| 136 | } |
---|
| 137 | |
---|
[d374492] | 138 | void mpc55xx_edma_enable_hardware_requests( unsigned channel, bool enable) |
---|
[574fb67] | 139 | { |
---|
[d374492] | 140 | if (MPC55XX_EDMA_IS_CHANNEL_VALID( channel)) { |
---|
| 141 | if (enable) { |
---|
| 142 | EDMA.SERQR.R = (uint8_t) channel; |
---|
| 143 | } else { |
---|
| 144 | EDMA.CERQR.R = (uint8_t) channel; |
---|
| 145 | } |
---|
[574fb67] | 146 | } else { |
---|
[d374492] | 147 | RTEMS_SYSLOG_ERROR( "invalid channel number\n"); |
---|
[574fb67] | 148 | } |
---|
| 149 | } |
---|
| 150 | |
---|
[d374492] | 151 | void mpc55xx_edma_enable_error_interrupts( unsigned channel, bool enable) |
---|
[574fb67] | 152 | { |
---|
[d374492] | 153 | if (MPC55XX_EDMA_IS_CHANNEL_VALID( channel)) { |
---|
| 154 | if (enable) { |
---|
| 155 | EDMA.SEEIR.R = (uint8_t) channel; |
---|
| 156 | } else { |
---|
| 157 | EDMA.CEEIR.R = (uint8_t) channel; |
---|
| 158 | } |
---|
[574fb67] | 159 | } else { |
---|
[d374492] | 160 | RTEMS_SYSLOG_ERROR( "invalid channel number\n"); |
---|
[574fb67] | 161 | } |
---|
| 162 | } |
---|
| 163 | |
---|
[2f5435a4] | 164 | rtems_status_code mpc55xx_edma_init(void) |
---|
[574fb67] | 165 | { |
---|
| 166 | rtems_status_code sc = RTEMS_SUCCESSFUL; |
---|
[d374492] | 167 | |
---|
| 168 | /* Initialize channel chain */ |
---|
| 169 | rtems_chain_initialize_empty( &mpc55xx_edma_channel_chain); |
---|
[574fb67] | 170 | |
---|
| 171 | /* Arbitration mode: round robin */ |
---|
| 172 | EDMA.CR.B.ERCA = 1; |
---|
| 173 | EDMA.CR.B.ERGA = 1; |
---|
| 174 | |
---|
| 175 | /* Clear TCDs */ |
---|
[29313369] | 176 | memset( (void *)&EDMA.TCD [0], 0, |
---|
| 177 | MPC55XX_EDMA_CHANNEL_COUNT * sizeof( EDMA.TCD[0])); |
---|
[574fb67] | 178 | |
---|
[d374492] | 179 | /* Error interrupt handlers */ |
---|
[574fb67] | 180 | sc = mpc55xx_interrupt_handler_install( |
---|
| 181 | MPC55XX_IRQ_EDMA_ERROR_LOW, |
---|
| 182 | "eDMA Error (Low)", |
---|
| 183 | RTEMS_INTERRUPT_UNIQUE, |
---|
[d374492] | 184 | MPC55XX_EDMA_IRQ_PRIORITY, |
---|
| 185 | mpc55xx_edma_interrupt_error_handler, |
---|
| 186 | NULL |
---|
[574fb67] | 187 | ); |
---|
[d374492] | 188 | RTEMS_CHECK_SC( sc, "install low error interrupt handler"); |
---|
[29313369] | 189 | |
---|
| 190 | #if defined(MPC55XX_IRQ_EDMA_ERROR_HIGH) |
---|
[574fb67] | 191 | sc = mpc55xx_interrupt_handler_install( |
---|
| 192 | MPC55XX_IRQ_EDMA_ERROR_HIGH, |
---|
| 193 | "eDMA Error (High)", |
---|
| 194 | RTEMS_INTERRUPT_UNIQUE, |
---|
[d374492] | 195 | MPC55XX_EDMA_IRQ_PRIORITY, |
---|
| 196 | mpc55xx_edma_interrupt_error_handler, |
---|
| 197 | NULL |
---|
[574fb67] | 198 | ); |
---|
[d374492] | 199 | RTEMS_CHECK_SC( sc, "install high error interrupt handler"); |
---|
[29313369] | 200 | #endif /* defined(MPC55XX_IRQ_EDMA_ERROR_HIGH) */ |
---|
[574fb67] | 201 | |
---|
| 202 | return RTEMS_SUCCESSFUL; |
---|
| 203 | } |
---|
| 204 | |
---|
[d374492] | 205 | rtems_status_code mpc55xx_edma_obtain_channel( mpc55xx_edma_channel_entry *e) |
---|
[574fb67] | 206 | { |
---|
| 207 | rtems_status_code sc = RTEMS_SUCCESSFUL; |
---|
[d374492] | 208 | rtems_interrupt_level level; |
---|
| 209 | uint64_t channel_occupation = 0; |
---|
[574fb67] | 210 | |
---|
[d374492] | 211 | if (MPC55XX_EDMA_IS_CHANNEL_INVALID( e->channel)) { |
---|
[574fb67] | 212 | return RTEMS_INVALID_NUMBER; |
---|
| 213 | } |
---|
| 214 | |
---|
[d374492] | 215 | /* Test and set channel occupation flag */ |
---|
| 216 | rtems_interrupt_disable( level); |
---|
| 217 | channel_occupation = mpc55xx_edma_channel_occupation; |
---|
[b450e0c] | 218 | if ( (channel_occupation & MPC55XX_EDMA_CHANNEL_FLAG( e->channel)) == 0 ) { |
---|
| 219 | mpc55xx_edma_channel_occupation = channel_occupation | MPC55XX_EDMA_CHANNEL_FLAG( e->channel); |
---|
[574fb67] | 220 | } |
---|
[d374492] | 221 | rtems_interrupt_enable( level); |
---|
| 222 | |
---|
| 223 | /* Check channel occupation flag */ |
---|
[b450e0c] | 224 | if ( channel_occupation & MPC55XX_EDMA_CHANNEL_FLAG( e->channel)) { |
---|
[574fb67] | 225 | return RTEMS_RESOURCE_IN_USE; |
---|
| 226 | } |
---|
| 227 | |
---|
| 228 | /* Interrupt handler */ |
---|
| 229 | sc = mpc55xx_interrupt_handler_install( |
---|
[d374492] | 230 | MPC55XX_IRQ_EDMA_GET_REQUEST( e->channel), |
---|
[574fb67] | 231 | "eDMA Channel", |
---|
| 232 | RTEMS_INTERRUPT_SHARED, |
---|
[d374492] | 233 | MPC55XX_EDMA_IRQ_PRIORITY, |
---|
| 234 | mpc55xx_edma_interrupt_handler, |
---|
| 235 | e |
---|
[574fb67] | 236 | ); |
---|
[d374492] | 237 | RTEMS_CHECK_SC( sc, "install channel interrupt handler"); |
---|
[574fb67] | 238 | |
---|
| 239 | /* Enable error interrupts */ |
---|
[d374492] | 240 | mpc55xx_edma_enable_error_interrupts( e->channel, true); |
---|
| 241 | |
---|
| 242 | /* Prepend channel entry to channel list */ |
---|
| 243 | rtems_chain_prepend( &mpc55xx_edma_channel_chain, &e->node); |
---|
[574fb67] | 244 | |
---|
| 245 | return RTEMS_SUCCESSFUL; |
---|
| 246 | } |
---|
| 247 | |
---|
[d374492] | 248 | rtems_status_code mpc55xx_edma_release_channel( mpc55xx_edma_channel_entry *e) |
---|
[574fb67] | 249 | { |
---|
[d374492] | 250 | rtems_status_code sc = RTEMS_SUCCESSFUL; |
---|
| 251 | rtems_interrupt_level level; |
---|
| 252 | |
---|
| 253 | /* Clear channel occupation flag */ |
---|
| 254 | rtems_interrupt_disable( level); |
---|
[b450e0c] | 255 | mpc55xx_edma_channel_occupation &= ~MPC55XX_EDMA_CHANNEL_FLAG( e->channel); |
---|
[d374492] | 256 | rtems_interrupt_enable( level); |
---|
| 257 | |
---|
| 258 | /* Disable hardware requests */ |
---|
| 259 | mpc55xx_edma_enable_hardware_requests( e->channel, false); |
---|
| 260 | |
---|
| 261 | /* Disable error interrupts */ |
---|
| 262 | mpc55xx_edma_enable_error_interrupts( e->channel, false); |
---|
| 263 | |
---|
| 264 | /* Extract channel entry from channel chain */ |
---|
| 265 | rtems_chain_extract( &e->node); |
---|
| 266 | |
---|
| 267 | /* Remove interrupt handler */ |
---|
| 268 | sc = rtems_interrupt_handler_remove( |
---|
| 269 | MPC55XX_IRQ_EDMA_GET_REQUEST( e->channel), |
---|
| 270 | mpc55xx_edma_interrupt_handler, |
---|
| 271 | e |
---|
| 272 | ); |
---|
| 273 | RTEMS_CHECK_SC( sc, "remove channel interrupt handler"); |
---|
| 274 | |
---|
| 275 | /* Notify user */ |
---|
| 276 | e->done( e, 0); |
---|
| 277 | |
---|
| 278 | return RTEMS_SUCCESSFUL; |
---|
[574fb67] | 279 | } |
---|