]> git.kernelconcepts.de Git - karo-tx-redboot.git/blob - packages/devs/serial/powerpc/ec555/v2_0/src/ec555_serial_with_ints.c
Initial revision
[karo-tx-redboot.git] / packages / devs / serial / powerpc / ec555 / v2_0 / src / ec555_serial_with_ints.c
1 //==========================================================================
2 //
3 //      ec555_serial_with_ints.c
4 //
5 //      PowerPC 5xx EC555 Serial I/O Interface Module (interrupt driven)
6 //
7 //==========================================================================
8 //####ECOSGPLCOPYRIGHTBEGIN####
9 // -------------------------------------------
10 // This file is part of eCos, the Embedded Configurable Operating System.
11 // Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc.
12 //
13 // eCos is free software; you can redistribute it and/or modify it under
14 // the terms of the GNU General Public License as published by the Free
15 // Software Foundation; either version 2 or (at your option) any later version.
16 //
17 // eCos is distributed in the hope that it will be useful, but WITHOUT ANY
18 // WARRANTY; without even the implied warranty of MERCHANTABILITY or
19 // FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
20 // for more details.
21 //
22 // You should have received a copy of the GNU General Public License along
23 // with eCos; if not, write to the Free Software Foundation, Inc.,
24 // 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
25 //
26 // As a special exception, if other files instantiate templates or use macros
27 // or inline functions from this file, or you compile this file and link it
28 // with other works to produce a work based on this file, this file does not
29 // by itself cause the resulting work to be covered by the GNU General Public
30 // License. However the source code for this file must still be made available
31 // in accordance with section (3) of the GNU General Public License.
32 //
33 // This exception does not invalidate any other reasons why a work based on
34 // this file might be covered by the GNU General Public License.
35 //
36 // Alternative licenses for eCos may be arranged by contacting Red Hat, Inc.
37 // at http://sources.redhat.com/ecos/ecos-license/
38 // -------------------------------------------
39 //####ECOSGPLCOPYRIGHTEND####
40 //==========================================================================
41 //#####DESCRIPTIONBEGIN####
42 //
43 // Author(s):   Bob Koninckx
44 // Contributors:
45 // Date:        2002-04-25
46 // Purpose:     EC555 Serial I/O module (interrupt driven version)
47 // Description: 
48 //
49 //   
50 //####DESCRIPTIONEND####
51 //==========================================================================
52 //----------------------------------
53 // Includes and forward declarations
54 //----------------------------------
55 #include <pkgconf/io_serial.h>
56 #include <pkgconf/io.h>
57
58 #include <cyg/io/io.h>
59 #include <cyg/hal/hal_intr.h>
60 #include <cyg/hal/hal_arbiter.h>
61 #include <cyg/io/devtab.h>
62 #include <cyg/infra/diag.h>
63 #include <cyg/io/serial.h>
64
65 // Only build this driver for the MPC555 based EC555 board
66 #ifdef CYGPKG_IO_SERIAL_POWERPC_EC555
67
68 #include "ec555_serial.h"
69
70 //-----------------
71 // Type definitions
72 //-----------------
73 typedef struct mpc555_serial_info {
74   CYG_ADDRWORD   base;                  // The base address of the serial port
75   CYG_WORD       tx_interrupt_num;      // trivial
76   CYG_WORD       rx_interrupt_num;      // trivial
77   cyg_priority_t tx_interrupt_priority; // trivial
78   cyg_priority_t rx_interrupt_priority; // trivial
79   bool           tx_interrupt_enable;   // tells if the transmit interrupt may be re-enabled
80   cyg_interrupt  tx_interrupt;          // the tx interrupt object
81   cyg_handle_t   tx_interrupt_handle;   // the tx interrupt handle
82   cyg_interrupt  rx_interrupt;          // the rx interrupt object
83   cyg_handle_t   rx_interrupt_handle;   // the rx interrupt handle
84 } mpc555_serial_info;
85
86 //--------------------
87 // Function prototypes
88 //--------------------
89 static bool mpc555_serial_init(struct cyg_devtab_entry * tab);
90 static bool mpc555_serial_putc(serial_channel * chan, unsigned char c);
91 static Cyg_ErrNo mpc555_serial_lookup(struct cyg_devtab_entry ** tab, 
92                                       struct cyg_devtab_entry * sub_tab,
93                                       const char * name);
94 static unsigned char mpc555_serial_getc(serial_channel *chan);
95 static Cyg_ErrNo mpc555_serial_set_config(serial_channel *chan, cyg_uint32 key,
96                                           const void *xbuf, cyg_uint32 *len);
97 static void mpc555_serial_start_xmit(serial_channel *chan);
98 static void mpc555_serial_stop_xmit(serial_channel *chan);
99
100 // The interrupt servers
101 static cyg_uint32 mpc555_serial_tx_ISR(cyg_vector_t vector, cyg_addrword_t data);
102 static cyg_uint32 mpc555_serial_rx_ISR(cyg_vector_t vector, cyg_addrword_t data);
103 static void       mpc555_serial_tx_DSR(cyg_vector_t vector, cyg_ucount32 count, cyg_addrword_t data);
104 static void       mpc555_serial_rx_DSR(cyg_vector_t vector, cyg_ucount32 count, cyg_addrword_t data);
105
106 //-------------------------------------------
107 // Register the device driver with the kernel
108 //-------------------------------------------
109 static SERIAL_FUNS(mpc555_serial_funs, 
110                    mpc555_serial_putc, 
111                    mpc555_serial_getc,
112                    mpc555_serial_set_config,
113                    mpc555_serial_start_xmit,
114                    mpc555_serial_stop_xmit);
115
116 //-------------------
117 // Device driver data
118 //-------------------
119 #ifdef CYGPKG_IO_SERIAL_POWERPC_EC555_SERIAL_A
120 static mpc555_serial_info mpc555_serial_info0 = {MPC555_SERIAL_BASE_A,
121                                                  CYGNUM_HAL_INTERRUPT_IMB3_SCI0_TX,
122                                                  CYGNUM_HAL_INTERRUPT_IMB3_SCI0_RX,
123                                                  CYGNUM_HAL_INTERRUPT_IMB3_SCI0_TX_PRIORITY,
124                                                  CYGNUM_HAL_INTERRUPT_IMB3_SCI0_RX_PRIORITY,
125                                                  false};
126 #if CYGNUM_IO_SERIAL_POWERPC_EC555_SERIAL_A_BUFSIZE > 0
127 static unsigned char mpc555_serial_out_buf0[CYGNUM_IO_SERIAL_POWERPC_EC555_SERIAL_A_BUFSIZE]; 
128 static unsigned char mpc555_serial_in_buf0[CYGNUM_IO_SERIAL_POWERPC_EC555_SERIAL_A_BUFSIZE];
129
130 static SERIAL_CHANNEL_USING_INTERRUPTS(mpc555_serial_channel0,
131                                        mpc555_serial_funs,
132                                        mpc555_serial_info0,
133                                        CYG_SERIAL_BAUD_RATE(CYGNUM_IO_SERIAL_POWERPC_EC555_SERIAL_A_BAUD),
134                                        CYG_SERIAL_STOP_DEFAULT,
135                                        CYG_SERIAL_PARITY_DEFAULT,
136                                        CYG_SERIAL_WORD_LENGTH_DEFAULT,
137                                        CYG_SERIAL_FLAGS_DEFAULT,
138                                        &mpc555_serial_out_buf0[0],
139                                        sizeof(mpc555_serial_out_buf0),
140                                        &mpc555_serial_in_buf0[0],
141                                        sizeof(mpc555_serial_in_buf0));
142 #else 
143 static SERIAL_CHANNEL(mpc555_serial_channel0,
144                       mpc555_serial_funs,
145                       mpc555_serial_info0,
146                       CYG_SERIAL_BAUD_RATE(CYGNUM_IO_SERIAL_POWERPC_EC555_SERIAL_A_BAUD),
147                       CYG_SERIAL_STOP_DEFAULT,
148                       CYG_SERIAL_PARITY_DEFAULT,
149                       CYG_SERIAL_WORD_LENGTH_DEFAULT,
150                       CYG_SERIAL_FLAGS_DEFAULT);
151 #endif
152 DEVTAB_ENTRY(mpc555_serial_io0,
153              CYGDAT_IO_SERIAL_POWERPC_EC555_SERIAL_A_NAME,
154              0, // does not depend on a lower level device driver
155              &cyg_io_serial_devio,
156              mpc555_serial_init,
157              mpc555_serial_lookup,
158              &mpc555_serial_channel0);
159 #endif // ifdef CYGPKG_IO_SERIAL_POWERPC_EC555_SERIAL_A
160
161 #ifdef CYGPKG_IO_SERIAL_POWERPC_EC555_SERIAL_B
162 static mpc555_serial_info mpc555_serial_info1 = {MPC555_SERIAL_BASE_B,
163                                                  CYGNUM_HAL_INTERRUPT_IMB3_SCI1_TX,
164                                                  CYGNUM_HAL_INTERRUPT_IMB3_SCI1_RX,
165                                                  CYGNUM_HAL_INTERRUPT_IMB3_SCI1_TX_PRIORITY,
166                                                  CYGNUM_HAL_INTERRUPT_IMB3_SCI1_RX_PRIORITY,
167                                                  false};
168 #if CYGNUM_IO_SERIAL_POWERPC_EC555_SERIAL_B_BUFSIZE > 0
169 static unsigned char mpc555_serial_out_buf1[CYGNUM_IO_SERIAL_POWERPC_EC555_SERIAL_B_BUFSIZE]; 
170 static unsigned char mpc555_serial_in_buf1[CYGNUM_IO_SERIAL_POWERPC_EC555_SERIAL_B_BUFSIZE];
171
172 static SERIAL_CHANNEL_USING_INTERRUPTS(mpc555_serial_channel1,
173                                        mpc555_serial_funs,
174                                        mpc555_serial_info1,
175                                        CYG_SERIAL_BAUD_RATE(CYGNUM_IO_SERIAL_POWERPC_EC555_SERIAL_B_BAUD),
176                                        CYG_SERIAL_STOP_DEFAULT,
177                                        CYG_SERIAL_PARITY_DEFAULT,
178                                        CYG_SERIAL_WORD_LENGTH_DEFAULT,
179                                        CYG_SERIAL_FLAGS_DEFAULT,
180                                        &mpc555_serial_out_buf1[0],
181                                        sizeof(mpc555_serial_out_buf1),
182                                        &mpc555_serial_in_buf1[0],
183                                        sizeof(mpc555_serial_in_buf1));
184 #else
185 static SERIAL_CHANNEL(mpc555_serial_channel1,
186                       mpc555_serial_funs,
187                       mpc555_serial_info1,
188                       CYG_SERIAL_BAUD_RATE(CYGNUM_IO_SERIAL_POWERPC_EC555_SERIAL_B_BAUD),
189                       CYG_SERIAL_STOP_DEFAULT,
190                       CYG_SERIAL_PARITY_DEFAULT,
191                       CYG_SERIAL_WORD_LENGTH_DEFAULT,
192                       CYG_SERIAL_FLAGS_DEFAULT);
193 #endif
194 DEVTAB_ENTRY(mpc555_serial_io1,
195              CYGDAT_IO_SERIAL_POWERPC_EC555_SERIAL_B_NAME,
196              0, // does not depend on a lower level device driver
197              &cyg_io_serial_devio,
198              mpc555_serial_init,
199              mpc555_serial_lookup,
200              &mpc555_serial_channel1);
201 #endif // ifdef CYGPKG_IO_SERIAL_POWERPC_EC555_SERIAL_B
202
203 //-----------------------------
204 // Device driver implementation
205 //-----------------------------
206
207 // The arbitration isr. 
208 // I think this is the best place to implement it. The device driver is the only place
209 // in the code where the knowledge is present about how the hardware is used
210 //
211 // Always check receiver interrupts. Some rom monitor might be listening to CTRL-C
212 static cyg_uint32 hal_arbitration_isr_qsci(CYG_ADDRWORD a_vector, CYG_ADDRWORD a_data)
213 {
214   cyg_uint16 status;
215   cyg_uint16 control;
216
217   HAL_READ_UINT16(CYGARC_REG_IMM_SC1SR, status);
218   HAL_READ_UINT16(CYGARC_REG_IMM_SCC1R1, control);
219   if((status & CYGARC_REG_IMM_SCxSR_RDRF) && (control & CYGARC_REG_IMM_SCCxR1_RIE))
220     return hal_call_isr(CYGNUM_HAL_INTERRUPT_IMB3_SCI0_RX);
221   
222 #ifdef CYGPKG_IO_SERIAL_POWERPC_EC555_SERIAL_A // Do not waist time on unused hardware
223   if((status & CYGARC_REG_IMM_SCxSR_TDRE) && (control & CYGARC_REG_IMM_SCCxR1_TIE))
224     return hal_call_isr(CYGNUM_HAL_INTERRUPT_IMB3_SCI0_TX);
225 // Don't waist time on unused interrupts
226 //  if((status & CYGARC_REG_IMM_SCxSR_TC) && (control & CYGARC_REG_IMM_SCCxR1_TCIE))
227 //    return hal_call_isr(CYGNUM_HAL_INTERRUPT_IMB3_SCI0_TXC);
228 // Don't waist time on unused interrupts
229 //  if((status & CYGARC_REG_IMM_SCxSR_IDLE) && (control & CYGARC_REG_IMM_SCCxR1_ILIE))
230 //    return hal_call_isr(CYGNUM_HAL_INTERRUPT_IMB3_SCI0_IDLE);
231 #endif
232
233   HAL_READ_UINT16(CYGARC_REG_IMM_SC2SR, status);
234   HAL_READ_UINT16(CYGARC_REG_IMM_SCC2R1, control);
235   if((status & CYGARC_REG_IMM_SCxSR_RDRF) && (control & CYGARC_REG_IMM_SCCxR1_RIE))
236     return hal_call_isr(CYGNUM_HAL_INTERRUPT_IMB3_SCI1_RX);
237   
238 #ifdef CYGPKG_IO_SERIAL_POWERPC_EC555_SERIAL_B // Do not waist time on unused hardware
239   if((status & CYGARC_REG_IMM_SCxSR_TDRE) && (control & CYGARC_REG_IMM_SCCxR1_TIE))
240     return hal_call_isr(CYGNUM_HAL_INTERRUPT_IMB3_SCI1_TX);
241 // Don't waist time on unused interrupts
242 //  if((status & CYGARC_REG_IMM_SCxSR_TC) && (control & CYGARC_REG_IMM_SCCxR1_TCIE))
243 //    return hal_call_isr(CYGNUM_HAL_INTERRUPT_IMB3_SCI1_TXC);
244 // Don't waist time on unused interrupts
245 //  if((status & CYGARC_REG_IMM_SCxSR_IDLE) && (control & CYGARC_REG_IMM_SCCxR1_ILIE))
246 //    return hal_call_isr(CYGNUM_HAL_INTERRUPT_IMB3_SCI1_IDLE);
247
248 #if 0
249   // The driver doesn't use the queue operation of the hardware (It would need different code for serial 1 and 2
250   // since oly one port supports queue mode). So the following is not needed.
251   // Leave it there. It is easyer for later implementations to remove the comments than finding
252   // out how the hardware works again.
253   HAL_READ_UINT16(CYGARC_REG_IMM_QSCI1SR, status);
254   HAL_READ_UINT16(CYGARC_REG_IMM_QSCI1CR, control);
255   if((status & CYGARC_REG_IMM_QSCI1SR_QTHF) && (control & CYGARC_REG_IMM_QSCI1CR_QTHFI))
256     return hal_call_isr(CYGNUM_HAL_INTERRUPT_IMB3_SCI1_RXQTHF);
257   if((status & CYGARC_REG_IMM_QSCI1SR_QBHF) && (control & CYGARC_REG_IMM_QSCI1CR_QBHFI))
258     return hal_call_isr(CYGNUM_HAL_INTERRUPT_IMB3_SCI1_RXQBHF);
259   if((status & CYGARC_REG_IMM_QSCI1SR_QTHE) && (control & CYGARC_REG_IMM_QSCI1CR_QTHEI))
260     return hal_call_isr(CYGNUM_HAL_INTERRUPT_IMB3_SCI1_TXQTHE);
261   if((status & CYGARC_REG_IMM_QSCI1SR_QBHE) && (control & CYGARC_REG_IMM_QSCI1CR_QBHEI))
262     return hal_call_isr(CYGNUM_HAL_INTERRUPT_IMB3_SCI1_TXQBHE);
263
264   cyg_uint16 status;
265   cyg_uint16 control;
266
267   HAL_READ_UINT16(CYGARC_REG_IMM_SPSR, status);
268   HAL_READ_UINT16(CYGARC_REG_IMM_SPCR2, control);
269   if((status & CYGARC_REG_IMM_SPSR_SPIF) && (control & CYGARC_REG_IMM_SPCR2_SPIFIE))
270     return hal_call_isr(CYGNUM_HAL_INTERRUPT_IMB3_SPI_FI);
271
272   HAL_READ_UINT16(CYGARC_REG_IMM_SPCR3, control);
273   if((status & CYGARC_REG_IMM_SPSR_MODF) && (control & CYGARC_REG_IMM_SPCR3_HMIE))
274     return hal_call_isr(CYGNUM_HAL_INTERRUPT_IMB3_SPI_MODF);
275
276   if((status & CYGARC_REG_IMM_SPSR_HALTA) && (control & CYGARC_REG_IMM_SPCR3_HMIE))
277     return hal_call_isr(CYGNUM_HAL_INTERRUPT_IMB3_SPI_HALTA);
278 #endif
279   
280 #endif
281
282   return 0;
283 }
284
285 //--------------------------------------------------------------------------------
286 // Internal function to actually configure the hardware to desired baud rate, etc.
287 //--------------------------------------------------------------------------------
288 static bool mpc555_serial_config_port(serial_channel * chan, cyg_serial_info_t * new_config, bool init)
289 {
290   mpc555_serial_info * mpc555_chan = (mpc555_serial_info *)(chan->dev_priv);
291
292   cyg_addrword_t port = mpc555_chan->base;
293   cyg_uint16 baud_rate = select_baud[new_config->baud];
294   unsigned char frame_length = 1; // The start bit
295
296   cyg_uint16 old_isrstate;
297   cyg_uint16 sccxr;
298
299   if(!baud_rate)
300     return false;    // Invalid baud rate selected
301
302   if((new_config->word_length != CYGNUM_SERIAL_WORD_LENGTH_7) &&
303      (new_config->word_length != CYGNUM_SERIAL_WORD_LENGTH_8))
304     return false;    // Invalid word length selected
305
306   if((new_config->parity != CYGNUM_SERIAL_PARITY_NONE) &&
307      (new_config->parity != CYGNUM_SERIAL_PARITY_EVEN) &&
308      (new_config->parity != CYGNUM_SERIAL_PARITY_ODD))
309     return false;    // Invalid parity selected
310
311   if((new_config->stop != CYGNUM_SERIAL_STOP_1) &&
312      (new_config->stop != CYGNUM_SERIAL_STOP_2))
313     return false;    // Invalid stop bits selected
314
315   frame_length += select_word_length[new_config->word_length - CYGNUM_SERIAL_WORD_LENGTH_5]; 
316   frame_length += select_stop_bits[new_config->stop];
317   frame_length += select_parity[new_config->parity];
318
319   if((frame_length != 10) && (frame_length != 11))
320     return false;    // Invalid frame format selected
321
322   // Disable port interrupts while changing hardware
323   HAL_READ_UINT16(port + MPC555_SERIAL_SCCxR1, sccxr);
324   old_isrstate = sccxr;
325   old_isrstate &= ~((cyg_uint16)MPC555_SERIAL_SCCxR1_LOOPS);
326   old_isrstate &= ~((cyg_uint16)MPC555_SERIAL_SCCxR1_WOMS);
327   old_isrstate &= ~((cyg_uint16)MPC555_SERIAL_SCCxR1_ILT);
328   old_isrstate &= ~((cyg_uint16)MPC555_SERIAL_SCCxR1_PT);
329   old_isrstate &= ~((cyg_uint16)MPC555_SERIAL_SCCxR1_PE);
330   old_isrstate &= ~((cyg_uint16)MPC555_SERIAL_SCCxR1_M);
331   old_isrstate &= ~((cyg_uint16)MPC555_SERIAL_SCCxR1_WAKE);
332   old_isrstate &= ~((cyg_uint16)MPC555_SERIAL_SCCxR1_TE);
333   old_isrstate &= ~((cyg_uint16)MPC555_SERIAL_SCCxR1_RE);
334   old_isrstate &= ~((cyg_uint16)MPC555_SERIAL_SCCxR1_RWU);
335   old_isrstate &= ~((cyg_uint16)MPC555_SERIAL_SCCxR1_SBK);
336   sccxr &= ~((cyg_uint16)MPC555_SERIAL_SCCxR1_TIE);
337   sccxr &= ~((cyg_uint16)MPC555_SERIAL_SCCxR1_TCIE);
338   sccxr &= ~((cyg_uint16)MPC555_SERIAL_SCCxR1_RIE);
339   sccxr &= ~((cyg_uint16)MPC555_SERIAL_SCCxR1_ILIE);
340   HAL_WRITE_UINT16(port + MPC555_SERIAL_SCCxR1, sccxr);
341
342   // Set databits, stopbits and parity.
343   HAL_READ_UINT16(port + MPC555_SERIAL_SCCxR1, sccxr);
344
345   if(frame_length == 11)
346     sccxr |= (cyg_uint16)MPC555_SERIAL_SCCxR1_M;
347   else
348     sccxr &= ~((cyg_uint16)MPC555_SERIAL_SCCxR1_M);
349
350   switch(new_config->parity)
351   {
352     case CYGNUM_SERIAL_PARITY_NONE:
353       sccxr &= ~((cyg_uint16)MPC555_SERIAL_SCCxR1_PE);
354       break;
355     case CYGNUM_SERIAL_PARITY_EVEN:
356       sccxr |= (cyg_uint16)MPC555_SERIAL_SCCxR1_PE;
357       sccxr &= ~((cyg_uint16)MPC555_SERIAL_SCCxR1_PT);
358       break;
359     case CYGNUM_SERIAL_PARITY_ODD:
360       sccxr |= (cyg_uint16)MPC555_SERIAL_SCCxR1_PE;
361       sccxr |= (cyg_uint16)MPC555_SERIAL_SCCxR1_PT;
362       break;
363     default:
364       break;
365   }
366   HAL_WRITE_UINT16(port + MPC555_SERIAL_SCCxR1, sccxr);
367
368   // Set baud rate.
369   baud_rate &= ~((cyg_uint16)MPC555_SERIAL_SCCxR0_OTHR);
370   baud_rate &= ~((cyg_uint16)MPC555_SERIAL_SCCxR0_LINKBD);
371   HAL_READ_UINT16(port + MPC555_SERIAL_SCCxR0, sccxr);
372   sccxr &= ~(MPC555_SERIAL_SCCxR0_SCxBR);
373   sccxr |= baud_rate;
374   HAL_WRITE_UINT16(port + MPC555_SERIAL_SCCxR0, sccxr);
375
376   // Enable the device
377   HAL_READ_UINT16(port + MPC555_SERIAL_SCCxR1, sccxr);
378   sccxr |= MPC555_SERIAL_SCCxR1_TE;
379   sccxr |= MPC555_SERIAL_SCCxR1_RE;
380   HAL_WRITE_UINT16(port + MPC555_SERIAL_SCCxR1, sccxr);
381
382   if(init) 
383   { // enable the receiver interrupt
384     HAL_READ_UINT16(port + MPC555_SERIAL_SCCxR1, sccxr);
385     sccxr |= MPC555_SERIAL_SCCxR1_RIE;
386     HAL_WRITE_UINT16(port + MPC555_SERIAL_SCCxR1, sccxr);
387   } 
388   else // Restore the old interrupt state
389   {
390     HAL_READ_UINT16(port + MPC555_SERIAL_SCCxR1, sccxr);
391     sccxr |= old_isrstate;
392     HAL_WRITE_UINT16(port + MPC555_SERIAL_SCCxR1, sccxr);
393   }
394
395   if(new_config != &chan->config) 
396     chan->config = *new_config;
397
398   return true;
399 }
400
401 //--------------------------------------------------------------
402 // Function to initialize the device.  Called at bootstrap time.
403 //--------------------------------------------------------------
404 static hal_mpc5xx_arbitration_data arbiter;
405
406 static bool mpc555_serial_init(struct cyg_devtab_entry * tab)
407 {
408    serial_channel * chan = (serial_channel *)tab->priv;
409    mpc555_serial_info * mpc555_chan = (mpc555_serial_info *)chan->dev_priv;
410
411    if(!mpc555_serial_config_port(chan, &chan->config, true))
412      return false;
413
414    (chan->callbacks->serial_init)(chan);  // Really only required for interrupt driven devices
415    if(chan->out_cbuf.len != 0)
416    { 
417      arbiter.priority = CYGNUM_HAL_ISR_SOURCE_PRIORITY_QSCI;
418      arbiter.data     = 0;
419      arbiter.arbiter  = hal_arbitration_isr_qsci;
420      
421      // Install the arbitration isr, Make sure that is is not installed twice
422      hal_mpc5xx_remove_arbitration_isr(CYGNUM_HAL_ISR_SOURCE_PRIORITY_QSCI);
423      hal_mpc5xx_install_arbitration_isr(&arbiter); 
424
425      // Create the Tx interrupt, do not enable it yet
426      cyg_drv_interrupt_create(mpc555_chan->tx_interrupt_num,
427                               mpc555_chan->tx_interrupt_priority,
428                               (cyg_addrword_t)chan,   //  Data item passed to interrupt handler
429                               mpc555_serial_tx_ISR,
430                               mpc555_serial_tx_DSR,
431                               &mpc555_chan->tx_interrupt_handle,
432                               &mpc555_chan->tx_interrupt);
433      cyg_drv_interrupt_attach(mpc555_chan->tx_interrupt_handle);
434
435      // Create the Rx interrupt, this can be safely unmasked now
436      cyg_drv_interrupt_create(mpc555_chan->rx_interrupt_num,
437                               mpc555_chan->rx_interrupt_priority,
438                               (cyg_addrword_t)chan,
439                               mpc555_serial_rx_ISR,
440                               mpc555_serial_rx_DSR,
441                               &mpc555_chan->rx_interrupt_handle,
442                               &mpc555_chan->rx_interrupt);
443      cyg_drv_interrupt_attach(mpc555_chan->rx_interrupt_handle);
444      cyg_drv_interrupt_unmask(mpc555_chan->rx_interrupt_num);
445     }
446
447     return true;
448 }
449
450 //----------------------------------------------------------------------
451 // This routine is called when the device is "looked" up (i.e. attached)
452 //----------------------------------------------------------------------
453 static Cyg_ErrNo mpc555_serial_lookup(struct cyg_devtab_entry ** tab, 
454                                       struct cyg_devtab_entry * sub_tab,
455                                       const char * name)
456 {
457   serial_channel * chan = (serial_channel *)(*tab)->priv;
458   (chan->callbacks->serial_init)(chan);  // Really only required for interrupt driven devices
459
460   return ENOERR;
461 }
462
463 //----------------------------------------------
464 // Send a character to the device output buffer.
465 // Return 'true' if character is sent to device
466 //----------------------------------------------
467 static bool mpc555_serial_putc(serial_channel * chan, unsigned char c)
468 {
469   mpc555_serial_info * mpc555_chan = (mpc555_serial_info *)chan->dev_priv;
470   cyg_addrword_t port = mpc555_chan->base;
471
472   cyg_uint16 scsr;
473   cyg_uint16 scdr;
474
475   HAL_READ_UINT16(port + MPC555_SERIAL_SCxSR, scsr);
476   if(scsr & MPC555_SERIAL_SCxSR_TDRE)
477   { // Ok, we have space, write the character and return success
478     scdr = (cyg_uint16)c;
479     HAL_WRITE_UINT16(port + MPC555_SERIAL_SCxDR, scdr);
480     return true;
481   }  
482   else
483     // We cannot write to the transmitter, return failure
484     return false;
485 }
486
487 //---------------------------------------------------------------------
488 // Fetch a character from the device input buffer, waiting if necessary
489 //---------------------------------------------------------------------
490 static unsigned char mpc555_serial_getc(serial_channel * chan)
491 {
492   unsigned char c;
493   mpc555_serial_info * mpc555_chan = (mpc555_serial_info *)chan->dev_priv;
494   cyg_addrword_t port = mpc555_chan->base;
495
496   cyg_uint16 scsr;
497   cyg_uint16 scdr;
498
499   do {
500     HAL_READ_UINT16(port + MPC555_SERIAL_SCxSR, scsr);
501   } while(!(scsr & MPC555_SERIAL_SCxSR_RDRF));
502
503   // Ok, data is received, read it out and return
504   HAL_READ_UINT16(port + MPC555_SERIAL_SCxDR, scdr);
505   c = (unsigned char)scdr;
506
507   return c;
508 }
509
510 //---------------------------------------------------
511 // Set up the device characteristics; baud rate, etc.
512 //---------------------------------------------------
513 static bool mpc555_serial_set_config(serial_channel * chan, cyg_uint32 key,
514                                      const void *xbuf, cyg_uint32 * len)
515 {
516   switch(key) {
517   case CYG_IO_SET_CONFIG_SERIAL_INFO:
518     {
519       cyg_serial_info_t *config = (cyg_serial_info_t *)xbuf;
520       if(*len < sizeof(cyg_serial_info_t)) {
521         return -EINVAL;
522       }
523       *len = sizeof(cyg_serial_info_t);
524       if(true != mpc555_serial_config_port(chan, config, false))
525         return -EINVAL;
526     }
527     break;
528   default:
529     return -EINVAL;
530   }
531   return ENOERR;
532 }
533
534 //-------------------------------------
535 // Enable the transmitter on the device
536 //-------------------------------------
537 static void mpc555_serial_start_xmit(serial_channel * chan)
538 {
539   mpc555_serial_info * mpc555_chan = (mpc555_serial_info *)chan->dev_priv;
540
541   mpc555_chan->tx_interrupt_enable = true;
542   cyg_drv_interrupt_unmask(mpc555_chan->tx_interrupt_num);
543
544   // No need to call xmt_char, this will generate an interrupt immediately.
545 }
546
547 //--------------------------------------
548 // Disable the transmitter on the device
549 //--------------------------------------
550 static void mpc555_serial_stop_xmit(serial_channel * chan)
551 {
552   mpc555_serial_info * mpc555_chan = (mpc555_serial_info *)chan->dev_priv;
553
554   cyg_drv_dsr_lock();
555   mpc555_chan->tx_interrupt_enable = false;
556   cyg_drv_interrupt_mask(mpc555_chan->tx_interrupt_num);
557   cyg_drv_dsr_unlock();
558 }
559
560 //-----------------------------------------
561 // The low level transmit interrupt handler
562 //-----------------------------------------
563 static cyg_uint32 mpc555_serial_tx_ISR(cyg_vector_t vector, cyg_addrword_t data)
564 {
565   serial_channel * chan = (serial_channel *)data;
566   mpc555_serial_info * mpc555_chan = (mpc555_serial_info *)chan->dev_priv;
567
568   cyg_drv_interrupt_mask(mpc555_chan->tx_interrupt_num);
569   cyg_drv_interrupt_acknowledge(mpc555_chan->tx_interrupt_num);
570
571   return CYG_ISR_CALL_DSR; // cause the DSR to run
572 }
573
574 //----------------------------------------
575 // The low level receive interrupt handler
576 //----------------------------------------
577 static cyg_uint32 mpc555_serial_rx_ISR(cyg_vector_t vector, cyg_addrword_t data)
578 {
579   serial_channel * chan = (serial_channel *)data;
580   mpc555_serial_info * mpc555_chan = (mpc555_serial_info *)chan->dev_priv;
581
582   cyg_drv_interrupt_mask(mpc555_chan->rx_interrupt_num);
583   cyg_drv_interrupt_acknowledge(mpc555_chan->rx_interrupt_num);
584
585   return CYG_ISR_CALL_DSR; // cause the DSR to run
586 }
587
588 //------------------------------------------
589 // The high level transmit interrupt handler
590 //------------------------------------------
591 static void mpc555_serial_tx_DSR(cyg_vector_t vector, cyg_ucount32 count, cyg_addrword_t data)
592 {
593   serial_channel * chan = (serial_channel *)data;
594   mpc555_serial_info * mpc555_chan = (mpc555_serial_info *)chan->dev_priv;
595
596   (chan->callbacks->xmt_char)(chan);
597   if(mpc555_chan->tx_interrupt_enable)
598     cyg_drv_interrupt_unmask(mpc555_chan->tx_interrupt_num);
599 }
600
601 //-----------------------------------------
602 // The high level receive interrupt handler
603 //-----------------------------------------
604 #define MPC555_SERIAL_SCxSR_ERRORS (MPC555_SERIAL_SCxSR_OR | \
605                                     MPC555_SERIAL_SCxSR_NF | \
606                                     MPC555_SERIAL_SCxSR_FE | \
607                                     MPC555_SERIAL_SCxSR_PF)
608
609 static void mpc555_serial_rx_DSR(cyg_vector_t vector, cyg_ucount32 count, cyg_addrword_t data)
610 {
611   serial_channel * chan = (serial_channel *)data;
612   mpc555_serial_info * mpc555_chan = (mpc555_serial_info *)chan->dev_priv;
613   cyg_addrword_t port = mpc555_chan->base;
614   cyg_uint16 scdr;
615   cyg_uint16 scsr;
616
617   // Allways read out the received character, in order to clear receiver flags
618   HAL_READ_UINT16(port + MPC555_SERIAL_SCxDR, scdr);
619
620   HAL_READ_UINT16(port + MPC555_SERIAL_SCxSR, scsr);
621   if(scsr & (cyg_uint16)MPC555_SERIAL_SCxSR_ERRORS)
622   {
623     scsr &= ~((cyg_uint16)MPC555_SERIAL_SCxSR_ERRORS);
624     HAL_WRITE_UINT16(port + MPC555_SERIAL_SCxSR, scsr);
625   }
626   else
627   {
628     (chan->callbacks->rcv_char)(chan, (cyg_uint8)scdr);
629   }
630
631   cyg_drv_interrupt_unmask(mpc555_chan->rx_interrupt_num);
632 }
633
634 #endif // CYGPKG_IO_SERIAL_POWERPC_EC555
635
636 // EOF ec555_serial_with_ints.c