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

4.104.11
Last change on this file since d374492 was d374492, checked in by Thomas Doerfler <Thomas.Doerfler@…>, on Jul 21, 2009 at 8:38:04 AM

Update for MPC55XX changes

  • Property mode set to 100644
File size: 7.5 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#include <bsp/utility.h>
29
30#define RTEMS_STATUS_CHECKS_USE_PRINTK
31
32#include <rtems/status-checks.h>
33
34#define MPC55XX_EDMA_CHANNEL_NUMBER 64U
35
36#define MPC55XX_EDMA_INVALID_CHANNEL MPC55XX_EDMA_CHANNEL_NUMBER
37
38#define MPC55XX_EDMA_IS_CHANNEL_INVALID( i) ((unsigned) (i) >= MPC55XX_EDMA_CHANNEL_NUMBER)
39
40#define MPC55XX_EDMA_IS_CHANNEL_VALID( i) ((unsigned) (i) < MPC55XX_EDMA_CHANNEL_NUMBER)
41
42#define MPC55XX_EDMA_IRQ_PRIORITY MPC55XX_INTC_DEFAULT_PRIORITY
43
44#define MPC55XX_EDMA_CHANNEL_FLAG( channel) ((uint64_t) 1 << (channel))
45
46static uint64_t mpc55xx_edma_channel_occupation = 0;
47
48static rtems_chain_control mpc55xx_edma_channel_chain;
49
50static void mpc55xx_edma_interrupt_handler( rtems_vector_number vector, void *arg)
51{
52        mpc55xx_edma_channel_entry *e = (mpc55xx_edma_channel_entry *) arg;
53
54#ifdef DEBUG
55        uint32_t citer = EDMA.TCD [e->channel].CITERE_LINK ? EDMA.TCD [e->channel].CITER & EDMA_TCD_BITER_LINKED_MASK : EDMA.TCD [e->channel].CITER;
56        RTEMS_DEBUG_PRINT( "channel %i (CITER = %i)\n", e->channel, citer);
57#endif /* DEBUG */
58
59        /* Clear interrupt */
60        EDMA.CIRQR.R = (uint8_t) e->channel;
61
62        /* Notify user */
63        e->done( e, 0);
64}
65
66static void mpc55xx_edma_interrupt_error_handler( rtems_vector_number vector, void *arg)
67{
68        rtems_chain_control *chain = &mpc55xx_edma_channel_chain;
69        rtems_chain_node *node = chain->first;
70        unsigned i = 0;
71        uint64_t error_status = EDMA.ESR.R;
72        uint64_t error_channels = ((uint64_t) EDMA.ERH.R << 32) | EDMA.ERL.R;
73        uint64_t error_channels_update = 0;
74
75        RTEMS_DEBUG_PRINT( "error channels: %08x %08x\n", (unsigned) (error_channels >> 32), (unsigned) error_channels);
76
77        /* Mark all channels that are linked to a channel with errors */
78        do {
79                error_channels_update = 0;
80
81                for (i = 0; i < MPC55XX_EDMA_CHANNEL_NUMBER; ++i) {
82                        uint64_t channel_flags = 0;
83                        unsigned minor_link = i;
84                        unsigned major_link = i;
85
86                        /* Check if we have linked channels */
87                        if (EDMA.TCD [i].BMF.B.BITERE_LINK) {
88                                minor_link = EDMA_TCD_BITER_LINK( i);
89                        }
90                        if (EDMA.TCD [i].BMF.B.MAJORE_LINK) {
91                                major_link = EDMA.TCD [i].BMF.B.MAJORLINKCH;
92                        }
93
94                        /* Set flags related to this channel */
95                        channel_flags = MPC55XX_EDMA_CHANNEL_FLAG( i) | MPC55XX_EDMA_CHANNEL_FLAG( minor_link) | MPC55XX_EDMA_CHANNEL_FLAG( major_link);
96
97                        /* Any errors in these channels? */
98                        if (IS_ANY_FLAG_SET( error_channels, channel_flags)) {
99                                /* Get new error channels */
100                                uint64_t update = (error_channels & channel_flags) ^ channel_flags;
101
102                                /* Update error channels */
103                                error_channels = SET_FLAGS( error_channels, channel_flags);
104
105                                /* Contribute to the update of this round */
106                                error_channels_update = SET_FLAGS( error_channels_update, update);
107                        }
108                }
109        } while (error_channels_update != 0);
110
111        RTEMS_DEBUG_PRINT( "error channels (all): %08x %08x\n", (unsigned) (error_channels >> 32), (unsigned) error_channels);
112
113        /* Process the channels related to errors */
114        while (!rtems_chain_is_tail( chain, node)) {
115                mpc55xx_edma_channel_entry *e = (mpc55xx_edma_channel_entry *) node;
116
117                if (IS_FLAG_SET( error_channels, MPC55XX_EDMA_CHANNEL_FLAG( e->channel))) {
118                        mpc55xx_edma_enable_hardware_requests( e->channel, false);
119
120                        /* Notify user */
121                        e->done( e, error_status);
122                }
123
124                node = node->next;
125        }
126
127        /* Clear the error interrupt requests */
128        for (i = 0; i < MPC55XX_EDMA_CHANNEL_NUMBER; ++i) {
129                if (IS_FLAG_SET( error_channels, MPC55XX_EDMA_CHANNEL_FLAG( i))) {
130                        EDMA.CER.R = (uint8_t) i;
131                }
132        }
133}
134
135void mpc55xx_edma_enable_hardware_requests( unsigned channel, bool enable)
136{
137        if (MPC55XX_EDMA_IS_CHANNEL_VALID( channel)) {
138                if (enable) {
139                        EDMA.SERQR.R = (uint8_t) channel;
140                } else {
141                        EDMA.CERQR.R = (uint8_t) channel;
142                }
143        } else {
144                RTEMS_SYSLOG_ERROR( "invalid channel number\n");
145        }
146}
147
148void mpc55xx_edma_enable_error_interrupts( unsigned channel, bool enable)
149{
150        if (MPC55XX_EDMA_IS_CHANNEL_VALID( channel)) {
151                if (enable) {
152                        EDMA.SEEIR.R = (uint8_t) channel;
153                } else {
154                        EDMA.CEEIR.R = (uint8_t) channel;
155                }
156        } else {
157                RTEMS_SYSLOG_ERROR( "invalid channel number\n");
158        }
159}
160
161rtems_status_code mpc55xx_edma_init()
162{
163        rtems_status_code sc = RTEMS_SUCCESSFUL;
164
165        /* Initialize channel chain */
166        rtems_chain_initialize_empty( &mpc55xx_edma_channel_chain);
167
168        /* Arbitration mode: round robin */
169        EDMA.CR.B.ERCA = 1;
170        EDMA.CR.B.ERGA = 1;
171
172        /* Clear TCDs */
173        memset( &EDMA.TCD [0], 0, sizeof( EDMA.TCD));
174
175        /* Error interrupt handlers */
176        sc = mpc55xx_interrupt_handler_install(
177                MPC55XX_IRQ_EDMA_ERROR_LOW,
178                "eDMA Error (Low)",
179                RTEMS_INTERRUPT_UNIQUE,
180                MPC55XX_EDMA_IRQ_PRIORITY,
181                mpc55xx_edma_interrupt_error_handler,
182                NULL
183        );
184        RTEMS_CHECK_SC( sc, "install low error interrupt handler");
185        sc = mpc55xx_interrupt_handler_install(
186                MPC55XX_IRQ_EDMA_ERROR_HIGH,
187                "eDMA Error (High)",
188                RTEMS_INTERRUPT_UNIQUE,
189                MPC55XX_EDMA_IRQ_PRIORITY,
190                mpc55xx_edma_interrupt_error_handler,
191                NULL
192        );
193        RTEMS_CHECK_SC( sc, "install high error interrupt handler");
194
195        return RTEMS_SUCCESSFUL;
196}
197
198rtems_status_code mpc55xx_edma_obtain_channel( mpc55xx_edma_channel_entry *e)
199{
200        rtems_status_code sc = RTEMS_SUCCESSFUL;
201        rtems_interrupt_level level;
202        uint64_t channel_occupation = 0;
203
204        if (MPC55XX_EDMA_IS_CHANNEL_INVALID( e->channel)) {
205                return RTEMS_INVALID_NUMBER;
206        }
207
208        /* Test and set channel occupation flag */
209        rtems_interrupt_disable( level);
210        channel_occupation = mpc55xx_edma_channel_occupation;
211        if (IS_FLAG_CLEARED( channel_occupation, MPC55XX_EDMA_CHANNEL_FLAG( e->channel))) {
212                mpc55xx_edma_channel_occupation = SET_FLAG( channel_occupation, MPC55XX_EDMA_CHANNEL_FLAG( e->channel));
213        }
214        rtems_interrupt_enable( level);
215
216        /* Check channel occupation flag */
217        if (IS_FLAG_SET( channel_occupation, MPC55XX_EDMA_CHANNEL_FLAG( e->channel))) {
218                return RTEMS_RESOURCE_IN_USE;
219        }
220
221        /* Interrupt handler */
222        sc = mpc55xx_interrupt_handler_install(
223                MPC55XX_IRQ_EDMA_GET_REQUEST( e->channel),
224                "eDMA Channel",
225                RTEMS_INTERRUPT_SHARED,
226                MPC55XX_EDMA_IRQ_PRIORITY,
227                mpc55xx_edma_interrupt_handler,
228                e
229        );
230        RTEMS_CHECK_SC( sc, "install channel interrupt handler");
231
232        /* Enable error interrupts */
233        mpc55xx_edma_enable_error_interrupts( e->channel, true);
234
235        /* Prepend channel entry to channel list */
236        rtems_chain_prepend( &mpc55xx_edma_channel_chain, &e->node);
237
238        return RTEMS_SUCCESSFUL;
239}
240
241rtems_status_code mpc55xx_edma_release_channel( mpc55xx_edma_channel_entry *e)
242{
243        rtems_status_code sc = RTEMS_SUCCESSFUL;
244        rtems_interrupt_level level;
245
246        /* Clear channel occupation flag */
247        rtems_interrupt_disable( level);
248        mpc55xx_edma_channel_occupation = CLEAR_FLAG( mpc55xx_edma_channel_occupation, MPC55XX_EDMA_CHANNEL_FLAG( e->channel));
249        rtems_interrupt_enable( level);
250
251        /* Disable hardware requests */
252        mpc55xx_edma_enable_hardware_requests( e->channel, false);
253
254        /* Disable error interrupts */
255        mpc55xx_edma_enable_error_interrupts( e->channel, false);
256
257        /* Extract channel entry from channel chain */
258        rtems_chain_extract( &e->node);
259
260        /* Remove interrupt handler */
261        sc = rtems_interrupt_handler_remove(
262                MPC55XX_IRQ_EDMA_GET_REQUEST( e->channel),
263                mpc55xx_edma_interrupt_handler,
264                e
265        );
266        RTEMS_CHECK_SC( sc, "remove channel interrupt handler");
267
268        /* Notify user */
269        e->done( e, 0);
270
271        return RTEMS_SUCCESSFUL;
272}
Note: See TracBrowser for help on using the repository browser.