source: rtems/c/src/lib/libcpu/powerpc/mpc55xx/edma/edma.c @ 95b18d0

4.115
Last change on this file since 95b18d0 was 95b18d0, checked in by Joel Sherrill <joel.sherrill@…>, on Nov 30, 2010 at 5:13:28 PM

2010-11-30 Joel Sherrill <joel.sherrilL@…>

  • mpc55xx/edma/edma.c: Use rtems_chain_first() and do not directly access the structure.
  • Property mode set to 100644
File size: 7.6 KB
Line 
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
27#include <bsp/irq.h>
28
29#define RTEMS_STATUS_CHECKS_USE_PRINTK
30
31#include <rtems/status-checks.h>
32
33#if   ((MPC55XX_CHIP_TYPE >= 5510) && (MPC55XX_CHIP_TYPE <= 5517))
34#define MPC55XX_EDMA_CHANNEL_COUNT 16U
35#else /* ((MPC55XX_CHIP_TYPE >= 5510) && (MPC55XX_CHIP_TYPE <= 5517)) */
36#define MPC55XX_EDMA_CHANNEL_COUNT 64U
37#endif /* ((MPC55XX_CHIP_TYPE >= 5510) && (MPC55XX_CHIP_TYPE <= 5517)) */
38
39#define MPC55XX_EDMA_INVALID_CHANNEL MPC55XX_EDMA_CHANNEL_COUNT
40
41#define MPC55XX_EDMA_IS_CHANNEL_INVALID( i) ((unsigned) (i) >= MPC55XX_EDMA_CHANNEL_COUNT)
42
43#define MPC55XX_EDMA_IS_CHANNEL_VALID( i) ((unsigned) (i) < MPC55XX_EDMA_CHANNEL_COUNT)
44
45#define MPC55XX_EDMA_IRQ_PRIORITY MPC55XX_INTC_DEFAULT_PRIORITY
46
47#define MPC55XX_EDMA_CHANNEL_FLAG( channel) ((uint64_t) 1 << (channel))
48
49static uint64_t mpc55xx_edma_channel_occupation = 0;
50
51static rtems_chain_control mpc55xx_edma_channel_chain;
52
53static void mpc55xx_edma_interrupt_handler( void *arg)
54{
55        mpc55xx_edma_channel_entry *e = (mpc55xx_edma_channel_entry *) arg;
56
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;
59        RTEMS_DEBUG_PRINT( "channel %i (CITER = %i)\n", e->channel, citer);
60#endif /* DEBUG */
61
62        /* Clear interrupt */
63        EDMA.CIRQR.R = (uint8_t) e->channel;
64
65        /* Notify user */
66        e->done( e, 0);
67}
68
69static void mpc55xx_edma_interrupt_error_handler( void *arg)
70{
71        rtems_chain_control *chain = &mpc55xx_edma_channel_chain;
72        rtems_chain_node *node = rtems_chain_first( chain );
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
84                for (i = 0; i < MPC55XX_EDMA_CHANNEL_COUNT; ++i) {
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                        }
96
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? */
101                        if ( error_channels & channel_flags ) {
102                                /* Get new error channels */
103                                uint64_t update = (error_channels & channel_flags) ^ channel_flags;
104
105                                /* Update error channels */
106                                error_channels |= channel_flags;
107
108                                /* Contribute to the update of this round */
109                                error_channels_update |=  update;
110                        }
111                }
112        } while (error_channels_update != 0);
113
114        RTEMS_DEBUG_PRINT( "error channels (all): %08x %08x\n", (unsigned) (error_channels >> 32), (unsigned) error_channels);
115
116        /* Process the channels related to errors */
117        while (!rtems_chain_is_tail( chain, node)) {
118                mpc55xx_edma_channel_entry *e = (mpc55xx_edma_channel_entry *) node;
119
120                if ( error_channels & MPC55XX_EDMA_CHANNEL_FLAG( e->channel)) {
121                        mpc55xx_edma_enable_hardware_requests( e->channel, false);
122
123                        /* Notify user */
124                        e->done( e, error_status);
125                }
126
127                node = node->next;
128        }
129
130        /* Clear the error interrupt requests */
131        for (i = 0; i < MPC55XX_EDMA_CHANNEL_COUNT; ++i) {
132                if ( error_channels & MPC55XX_EDMA_CHANNEL_FLAG( i)) {
133                        EDMA.CER.R = (uint8_t) i;
134                }
135        }
136}
137
138void mpc55xx_edma_enable_hardware_requests( unsigned channel, bool enable)
139{
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                }
146        } else {
147                RTEMS_SYSLOG_ERROR( "invalid channel number\n");
148        }
149}
150
151void mpc55xx_edma_enable_error_interrupts( unsigned channel, bool enable)
152{
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                }
159        } else {
160                RTEMS_SYSLOG_ERROR( "invalid channel number\n");
161        }
162}
163
164rtems_status_code mpc55xx_edma_init(void)
165{
166        rtems_status_code sc = RTEMS_SUCCESSFUL;
167
168        /* Initialize channel chain */
169        rtems_chain_initialize_empty( &mpc55xx_edma_channel_chain);
170
171        /* Arbitration mode: round robin */
172        EDMA.CR.B.ERCA = 1;
173        EDMA.CR.B.ERGA = 1;
174
175        /* Clear TCDs */
176        memset( (void *)&EDMA.TCD [0], 0, 
177                MPC55XX_EDMA_CHANNEL_COUNT * sizeof( EDMA.TCD[0]));
178
179        /* Error interrupt handlers */
180        sc = mpc55xx_interrupt_handler_install(
181                MPC55XX_IRQ_EDMA_ERROR_LOW,
182                "eDMA Error (Low)",
183                RTEMS_INTERRUPT_UNIQUE,
184                MPC55XX_EDMA_IRQ_PRIORITY,
185                mpc55xx_edma_interrupt_error_handler,
186                NULL
187        );
188        RTEMS_CHECK_SC( sc, "install low error interrupt handler");
189
190#if defined(MPC55XX_IRQ_EDMA_ERROR_HIGH)
191        sc = mpc55xx_interrupt_handler_install(
192                MPC55XX_IRQ_EDMA_ERROR_HIGH,
193                "eDMA Error (High)",
194                RTEMS_INTERRUPT_UNIQUE,
195                MPC55XX_EDMA_IRQ_PRIORITY,
196                mpc55xx_edma_interrupt_error_handler,
197                NULL
198        );
199        RTEMS_CHECK_SC( sc, "install high error interrupt handler");
200#endif /* defined(MPC55XX_IRQ_EDMA_ERROR_HIGH) */
201
202        return RTEMS_SUCCESSFUL;
203}
204
205rtems_status_code mpc55xx_edma_obtain_channel( mpc55xx_edma_channel_entry *e)
206{
207        rtems_status_code sc = RTEMS_SUCCESSFUL;
208        rtems_interrupt_level level;
209        uint64_t channel_occupation = 0;
210
211        if (MPC55XX_EDMA_IS_CHANNEL_INVALID( e->channel)) {
212                return RTEMS_INVALID_NUMBER;
213        }
214
215        /* Test and set channel occupation flag */
216        rtems_interrupt_disable( level);
217        channel_occupation = mpc55xx_edma_channel_occupation;
218        if ( (channel_occupation & MPC55XX_EDMA_CHANNEL_FLAG( e->channel)) == 0 ) {
219                mpc55xx_edma_channel_occupation = channel_occupation | MPC55XX_EDMA_CHANNEL_FLAG( e->channel);
220        }
221        rtems_interrupt_enable( level);
222
223        /* Check channel occupation flag */
224        if ( channel_occupation & MPC55XX_EDMA_CHANNEL_FLAG( e->channel)) {
225                return RTEMS_RESOURCE_IN_USE;
226        }
227
228        /* Interrupt handler */
229        sc = mpc55xx_interrupt_handler_install(
230                MPC55XX_IRQ_EDMA_GET_REQUEST( e->channel),
231                "eDMA Channel",
232                RTEMS_INTERRUPT_SHARED,
233                MPC55XX_EDMA_IRQ_PRIORITY,
234                mpc55xx_edma_interrupt_handler,
235                e
236        );
237        RTEMS_CHECK_SC( sc, "install channel interrupt handler");
238
239        /* Enable error interrupts */
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);
244
245        return RTEMS_SUCCESSFUL;
246}
247
248rtems_status_code mpc55xx_edma_release_channel( mpc55xx_edma_channel_entry *e)
249{
250        rtems_status_code sc = RTEMS_SUCCESSFUL;
251        rtems_interrupt_level level;
252
253        /* Clear channel occupation flag */
254        rtems_interrupt_disable( level);
255        mpc55xx_edma_channel_occupation &= ~MPC55XX_EDMA_CHANNEL_FLAG( e->channel);
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;
279}
Note: See TracBrowser for help on using the repository browser.