]> git.karo-electronics.de Git - karo-tx-redboot.git/blob - packages/devs/serial/powerpc/mpc8xxx/v2_0/src/mpc8xxx_serial.c
Initial revision
[karo-tx-redboot.git] / packages / devs / serial / powerpc / mpc8xxx / v2_0 / src / mpc8xxx_serial.c
1 //==========================================================================
2 //
3 //      io/serial/powerpc/mpc8xxx_serial.c
4 //
5 //      PowerPC MPC8XXX (QUICC-II) (SMC/SCC) Serial I/O Interface Module
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 // Copyright (C) 2003 Gary Thomas
13 //
14 // eCos is free software; you can redistribute it and/or modify it under
15 // the terms of the GNU General Public License as published by the Free
16 // Software Foundation; either version 2 or (at your option) any later version.
17 //
18 // eCos is distributed in the hope that it will be useful, but WITHOUT ANY
19 // WARRANTY; without even the implied warranty of MERCHANTABILITY or
20 // FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
21 // for more details.
22 //
23 // You should have received a copy of the GNU General Public License along
24 // with eCos; if not, write to the Free Software Foundation, Inc.,
25 // 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
26 //
27 // As a special exception, if other files instantiate templates or use macros
28 // or inline functions from this file, or you compile this file and link it
29 // with other works to produce a work based on this file, this file does not
30 // by itself cause the resulting work to be covered by the GNU General Public
31 // License. However the source code for this file must still be made available
32 // in accordance with section (3) of the GNU General Public License.
33 //
34 // This exception does not invalidate any other reasons why a work based on
35 // this file might be covered by the GNU General Public License.
36 //
37 // Alternative licenses for eCos may be arranged by contacting Red Hat, Inc.
38 // at http://sources.redhat.com/ecos/ecos-license/
39 // -------------------------------------------
40 //####ECOSGPLCOPYRIGHTEND####
41 //==========================================================================
42 //#####DESCRIPTIONBEGIN####
43 //
44 // Author(s):    gthomas
45 // Contributors: gthomas
46 // Date:         1999-06-20
47 // Purpose:      MPC8XXX Serial I/O module
48 // Description: 
49 //
50 //####DESCRIPTIONEND####
51 //
52 //==========================================================================
53
54 #include <pkgconf/system.h>
55 #include <pkgconf/io_serial.h>
56 #include <pkgconf/io.h>
57 #include <cyg/io/io.h>
58 #include <cyg/hal/hal_intr.h>
59 #include <cyg/io/devtab.h>
60 #include <cyg/io/serial.h>
61 #include <cyg/infra/diag.h>
62 #include <cyg/hal/hal_cache.h>
63 #include <cyg/hal/mpc8xxx.h>
64 #include CYGBLD_HAL_PLATFORM_H
65
66 #include "mpc8xxx_serial.h"
67
68 typedef struct mpc8xxx_sxx_serial_info {
69     CYG_ADDRWORD          channel;                   // Which channel SMCx/SCCx
70     short                 int_num;                   // Interrupt number
71     short                 type;                      // Channel type - SCC or SMC
72     unsigned long         *brg;                      // Which baud rate generator
73     void                  *pram;                     // Parameter RAM pointer
74     void                  *ctl;                      // SMC/SCC control registers
75     volatile struct cp_bufdesc     *txbd, *rxbd;     // Next Tx,Rx descriptor to use
76     struct cp_bufdesc     *tbase, *rbase;            // First Tx,Rx descriptor
77     int                   txsize, rxsize;            // Length of individual buffers
78     cyg_interrupt         serial_interrupt;
79     cyg_handle_t          serial_interrupt_handle;
80 } mpc8xxx_sxx_serial_info;
81
82 static bool mpc8xxx_sxx_serial_init(struct cyg_devtab_entry *tab);
83 static bool mpc8xxx_sxx_serial_putc(serial_channel *chan, unsigned char c);
84 static Cyg_ErrNo mpc8xxx_sxx_serial_lookup(struct cyg_devtab_entry **tab, 
85                                    struct cyg_devtab_entry *sub_tab,
86                                    const char *name);
87 static unsigned char mpc8xxx_sxx_serial_getc(serial_channel *chan);
88 static Cyg_ErrNo mpc8xxx_sxx_serial_set_config(serial_channel *chan,
89                                              cyg_uint32 key, const void *xbuf,
90                                              cyg_uint32 *len);
91 static void mpc8xxx_sxx_serial_start_xmit(serial_channel *chan);
92 static void mpc8xxx_sxx_serial_stop_xmit(serial_channel *chan);
93
94 static cyg_uint32 mpc8xxx_sxx_serial_ISR(cyg_vector_t vector, cyg_addrword_t data);
95 static void       mpc8xxx_sxx_serial_DSR(cyg_vector_t vector, cyg_ucount32 count, cyg_addrword_t data);
96
97 static SERIAL_FUNS(mpc8xxx_sxx_serial_funs, 
98                    mpc8xxx_sxx_serial_putc, 
99                    mpc8xxx_sxx_serial_getc,
100                    mpc8xxx_sxx_serial_set_config,
101                    mpc8xxx_sxx_serial_start_xmit,
102                    mpc8xxx_sxx_serial_stop_xmit
103     );
104
105 #ifdef CYGPKG_IO_SERIAL_POWERPC_MPC8XXX_SMC1
106 static mpc8xxx_sxx_serial_info mpc8xxx_sxx_serial_info_smc1 = {
107     SMC1_PAGE_SUBBLOCK,                 // Channel indicator
108     CYGNUM_HAL_INTERRUPT_SMC1,    // interrupt
109     _SMC_CHAN
110 };
111 #if CYGNUM_IO_SERIAL_POWERPC_MPC8XXX_SMC1_BUFSIZE > 0
112 static unsigned char mpc8xxx_smc_serial_out_buf_smc1[CYGNUM_IO_SERIAL_POWERPC_MPC8XXX_SMC1_BUFSIZE];
113 static unsigned char mpc8xxx_smc_serial_in_buf_smc1[CYGNUM_IO_SERIAL_POWERPC_MPC8XXX_SMC1_BUFSIZE];
114
115 static SERIAL_CHANNEL_USING_INTERRUPTS(mpc8xxx_sxx_serial_channel_smc1,
116                                        mpc8xxx_sxx_serial_funs, 
117                                        mpc8xxx_sxx_serial_info_smc1,
118                                        CYG_SERIAL_BAUD_RATE(CYGNUM_IO_SERIAL_POWERPC_MPC8XXX_SMC1_BAUD),
119                                        CYG_SERIAL_STOP_DEFAULT,
120                                        CYG_SERIAL_PARITY_DEFAULT,
121                                        CYG_SERIAL_WORD_LENGTH_DEFAULT,
122                                        CYG_SERIAL_FLAGS_DEFAULT,
123                                        &mpc8xxx_smc_serial_out_buf_smc1[0], sizeof(mpc8xxx_smc_serial_out_buf_smc1),
124                                        &mpc8xxx_smc_serial_in_buf_smc1[0], sizeof(mpc8xxx_smc_serial_in_buf_smc1)
125     );
126 #else
127 static SERIAL_CHANNEL(mpc8xxx_sxx_serial_channel_smc1,
128                       mpc8xxx_sxx_serial_funs, 
129                       mpc8xxx_sxx_serial_info_smc1,
130                       CYG_SERIAL_BAUD_RATE(CYGNUM_IO_SERIAL_POWERPC_MPC8XXX_SMC1_BAUD),
131                       CYG_SERIAL_STOP_DEFAULT,
132                       CYG_SERIAL_PARITY_DEFAULT,
133                       CYG_SERIAL_WORD_LENGTH_DEFAULT,
134                       CYG_SERIAL_FLAGS_DEFAULT
135     );
136 #endif
137
138 static unsigned char mpc8xxx_smc1_txbuf[CYGNUM_IO_SERIAL_POWERPC_MPC8XXX_SMC1_TxNUM*CYGNUM_IO_SERIAL_POWERPC_MPC8XXX_SMC1_TxSIZE] __attribute__((aligned(HAL_DCACHE_LINE_SIZE)));
139 static unsigned char mpc8xxx_smc1_rxbuf[CYGNUM_IO_SERIAL_POWERPC_MPC8XXX_SMC1_RxNUM*CYGNUM_IO_SERIAL_POWERPC_MPC8XXX_SMC1_RxSIZE] __attribute__((aligned(HAL_DCACHE_LINE_SIZE)));
140
141 DEVTAB_ENTRY(mpc8xxx_smc_serial_io_smc1, 
142              CYGDAT_IO_SERIAL_POWERPC_MPC8XXX_SMC1_NAME,
143              0,                     // Does not depend on a lower level interface
144              &cyg_io_serial_devio, 
145              mpc8xxx_sxx_serial_init, 
146              mpc8xxx_sxx_serial_lookup,     // Serial driver may need initializing
147              &mpc8xxx_sxx_serial_channel_smc1
148     );
149 #endif //  CYGPKG_IO_SERIAL_POWERPC_MPC8XXX_SMC1
150
151 #ifdef CYGPKG_IO_SERIAL_POWERPC_MPC8XXX_SMC2
152 static mpc8xxx_sxx_serial_info mpc8xxx_sxx_serial_info_smc2 = {
153     SMC2_PAGE_SUBBLOCK,         // Channel indicator
154     CYGNUM_HAL_INTERRUPT_SMC2,  // interrupt
155     _SMC_CHAN
156 };
157 #if CYGNUM_IO_SERIAL_POWERPC_MPC8XXX_SMC2_BUFSIZE > 0
158 static unsigned char mpc8xxx_smc_serial_out_buf_smc2[CYGNUM_IO_SERIAL_POWERPC_MPC8XXX_SMC2_BUFSIZE];
159 static unsigned char mpc8xxx_smc_serial_in_buf_smc2[CYGNUM_IO_SERIAL_POWERPC_MPC8XXX_SMC2_BUFSIZE];
160
161 static SERIAL_CHANNEL_USING_INTERRUPTS(mpc8xxx_sxx_serial_channel_smc2,
162                                        mpc8xxx_sxx_serial_funs, 
163                                        mpc8xxx_sxx_serial_info_smc2,
164                                        CYG_SERIAL_BAUD_RATE(CYGNUM_IO_SERIAL_POWERPC_MPC8XXX_SMC2_BAUD),
165                                        CYG_SERIAL_STOP_DEFAULT,
166                                        CYG_SERIAL_PARITY_DEFAULT,
167                                        CYG_SERIAL_WORD_LENGTH_DEFAULT,
168                                        CYG_SERIAL_FLAGS_DEFAULT,
169                                        &mpc8xxx_smc_serial_out_buf_smc2[0], sizeof(mpc8xxx_smc_serial_out_buf_smc2),
170                                        &mpc8xxx_smc_serial_in_buf_smc2[0], sizeof(mpc8xxx_smc_serial_in_buf_smc2)
171     );
172 #else
173 static SERIAL_CHANNEL(mpc8xxx_sxx_serial_channel_smc2,
174                       mpc8xxx_sxx_serial_funs, 
175                       mpc8xxx_sxx_serial_info_smc2,
176                       CYG_SERIAL_BAUD_RATE(CYGNUM_IO_SERIAL_POWERPC_MPC8XXX_SMC2_BAUD),
177                       CYG_SERIAL_STOP_DEFAULT,
178                       CYG_SERIAL_PARITY_DEFAULT,
179                       CYG_SERIAL_WORD_LENGTH_DEFAULT,
180                       CYG_SERIAL_FLAGS_DEFAULT
181     );
182 #endif
183 static unsigned char mpc8xxx_smc2_txbuf[CYGNUM_IO_SERIAL_POWERPC_MPC8XXX_SMC2_TxNUM*CYGNUM_IO_SERIAL_POWERPC_MPC8XXX_SMC2_TxSIZE] __attribute__((aligned(HAL_DCACHE_LINE_SIZE)));
184 static unsigned char mpc8xxx_smc2_rxbuf[CYGNUM_IO_SERIAL_POWERPC_MPC8XXX_SMC2_RxNUM*CYGNUM_IO_SERIAL_POWERPC_MPC8XXX_SMC2_RxSIZE] __attribute__((aligned(HAL_DCACHE_LINE_SIZE)));
185
186 DEVTAB_ENTRY(mpc8xxx_smc_serial_io_smc2, 
187              CYGDAT_IO_SERIAL_POWERPC_MPC8XXX_SMC2_NAME,
188              0,                     // Does not depend on a lower level interface
189              &cyg_io_serial_devio, 
190              mpc8xxx_sxx_serial_init, 
191              mpc8xxx_sxx_serial_lookup,     // Serial driver may need initializing
192              &mpc8xxx_sxx_serial_channel_smc2
193     );
194 #endif //  CYGPKG_IO_SERIAL_POWERPC_MPC8XXX_SMC2
195
196 #ifdef CYGPKG_IO_SERIAL_POWERPC_MPC8XXX_SCC1
197 static mpc8xxx_sxx_serial_info mpc8xxx_sxx_serial_info_scc1 = {
198     SCC1_PAGE_SUBBLOCK,             // Channel indicator
199     CYGNUM_HAL_INTERRUPT_SCC1,      // interrupt
200     _SCC_CHAN
201 };
202 #if CYGNUM_IO_SERIAL_POWERPC_MPC8XXX_SCC1_BUFSIZE > 0
203 static unsigned char mpc8xxx_smc_serial_out_buf_scc1[CYGNUM_IO_SERIAL_POWERPC_MPC8XXX_SCC1_BUFSIZE];
204 static unsigned char mpc8xxx_smc_serial_in_buf_scc1[CYGNUM_IO_SERIAL_POWERPC_MPC8XXX_SCC1_BUFSIZE];
205
206 static SERIAL_CHANNEL_USING_INTERRUPTS(mpc8xxx_sxx_serial_channel_scc1,
207                                        mpc8xxx_sxx_serial_funs, 
208                                        mpc8xxx_sxx_serial_info_scc1,
209                                        CYG_SERIAL_BAUD_RATE(CYGNUM_IO_SERIAL_POWERPC_MPC8XXX_SCC1_BAUD),
210                                        CYG_SERIAL_STOP_DEFAULT,
211                                        CYG_SERIAL_PARITY_DEFAULT,
212                                        CYG_SERIAL_WORD_LENGTH_DEFAULT,
213                                        CYG_SERIAL_FLAGS_DEFAULT,
214                                        &mpc8xxx_smc_serial_out_buf_scc1[0], sizeof(mpc8xxx_smc_serial_out_buf_scc1),
215                                        &mpc8xxx_smc_serial_in_buf_scc1[0], sizeof(mpc8xxx_smc_serial_in_buf_scc1)
216     );
217 #else
218 static SERIAL_CHANNEL(mpc8xxx_sxx_serial_channel_scc1,
219                       mpc8xxx_sxx_serial_funs, 
220                       mpc8xxx_sxx_serial_info_scc1,
221                       CYG_SERIAL_BAUD_RATE(CYGNUM_IO_SERIAL_POWERPC_MPC8XXX_SCC1_BAUD),
222                       CYG_SERIAL_STOP_DEFAULT,
223                       CYG_SERIAL_PARITY_DEFAULT,
224                       CYG_SERIAL_WORD_LENGTH_DEFAULT,
225                       CYG_SERIAL_FLAGS_DEFAULT
226     );
227 #endif
228 static unsigned char mpc8xxx_scc1_txbuf[CYGNUM_IO_SERIAL_POWERPC_MPC8XXX_SCC1_TxNUM*CYGNUM_IO_SERIAL_POWERPC_MPC8XXX_SCC1_TxSIZE] __attribute__((aligned(HAL_DCACHE_LINE_SIZE)));
229 static unsigned char mpc8xxx_scc1_rxbuf[CYGNUM_IO_SERIAL_POWERPC_MPC8XXX_SCC1_RxNUM*CYGNUM_IO_SERIAL_POWERPC_MPC8XXX_SCC1_RxSIZE] __attribute__((aligned(HAL_DCACHE_LINE_SIZE)));
230
231 DEVTAB_ENTRY(mpc8xxx_smc_serial_io_scc1, 
232              CYGDAT_IO_SERIAL_POWERPC_MPC8XXX_SCC1_NAME,
233              0,                     // Does not depend on a lower level interface
234              &cyg_io_serial_devio, 
235              mpc8xxx_sxx_serial_init, 
236              mpc8xxx_sxx_serial_lookup,     // Serial driver may need initializing
237              &mpc8xxx_sxx_serial_channel_scc1
238     );
239 #endif //  CYGPKG_IO_SERIAL_POWERPC_MPC8XXX_SCC1
240
241 #ifdef CYGPKG_IO_SERIAL_POWERPC_MPC8XXX_SCC2
242 static mpc8xxx_sxx_serial_info mpc8xxx_sxx_serial_info_scc2 = {
243     SCC2_PAGE_SUBBLOCK,             // Channel indicator
244     CYGNUM_HAL_INTERRUPT_SCC2,      // interrupt
245     _SCC_CHAN
246 };
247 #if CYGNUM_IO_SERIAL_POWERPC_MPC8XXX_SCC2_BUFSIZE > 0
248 static unsigned char mpc8xxx_smc_serial_out_buf_scc2[CYGNUM_IO_SERIAL_POWERPC_MPC8XXX_SCC2_BUFSIZE];
249 static unsigned char mpc8xxx_smc_serial_in_buf_scc2[CYGNUM_IO_SERIAL_POWERPC_MPC8XXX_SCC2_BUFSIZE];
250
251 static SERIAL_CHANNEL_USING_INTERRUPTS(mpc8xxx_sxx_serial_channel_scc2,
252                                        mpc8xxx_sxx_serial_funs, 
253                                        mpc8xxx_sxx_serial_info_scc2,
254                                        CYG_SERIAL_BAUD_RATE(CYGNUM_IO_SERIAL_POWERPC_MPC8XXX_SCC2_BAUD),
255                                        CYG_SERIAL_STOP_DEFAULT,
256                                        CYG_SERIAL_PARITY_DEFAULT,
257                                        CYG_SERIAL_WORD_LENGTH_DEFAULT,
258                                        CYG_SERIAL_FLAGS_DEFAULT,
259                                        &mpc8xxx_smc_serial_out_buf_scc2[0], sizeof(mpc8xxx_smc_serial_out_buf_scc2),
260                                        &mpc8xxx_smc_serial_in_buf_scc2[0], sizeof(mpc8xxx_smc_serial_in_buf_scc2)
261     );
262 #else
263 static SERIAL_CHANNEL(mpc8xxx_sxx_serial_channel_scc2,
264                       mpc8xxx_sxx_serial_funs, 
265                       mpc8xxx_sxx_serial_info_scc2,
266                       CYG_SERIAL_BAUD_RATE(CYGNUM_IO_SERIAL_POWERPC_MPC8XXX_SCC2_BAUD),
267                       CYG_SERIAL_STOP_DEFAULT,
268                       CYG_SERIAL_PARITY_DEFAULT,
269                       CYG_SERIAL_WORD_LENGTH_DEFAULT,
270                       CYG_SERIAL_FLAGS_DEFAULT
271     );
272 #endif
273 static unsigned char mpc8xxx_scc2_txbuf[CYGNUM_IO_SERIAL_POWERPC_MPC8XXX_SCC2_TxNUM*CYGNUM_IO_SERIAL_POWERPC_MPC8XXX_SCC2_TxSIZE] __attribute__((aligned(HAL_DCACHE_LINE_SIZE)));
274 static unsigned char mpc8xxx_scc2_rxbuf[CYGNUM_IO_SERIAL_POWERPC_MPC8XXX_SCC2_RxNUM*CYGNUM_IO_SERIAL_POWERPC_MPC8XXX_SCC2_RxSIZE] __attribute__((aligned(HAL_DCACHE_LINE_SIZE)));
275
276 DEVTAB_ENTRY(mpc8xxx_smc_serial_io_scc2, 
277              CYGDAT_IO_SERIAL_POWERPC_MPC8XXX_SCC2_NAME,
278              0,                     // Does not depend on a lower level interface
279              &cyg_io_serial_devio, 
280              mpc8xxx_sxx_serial_init, 
281              mpc8xxx_sxx_serial_lookup,     // Serial driver may need initializing
282              &mpc8xxx_sxx_serial_channel_scc2
283     );
284 #endif //  CYGPKG_IO_SERIAL_POWERPC_MPC8XXX_SCC2
285
286 #ifdef CYGPKG_IO_SERIAL_POWERPC_MPC8XXX_SCC3
287 static mpc8xxx_sxx_serial_info mpc8xxx_sxx_serial_info_scc3 = {
288     SCC3_PAGE_SUBBLOCK,             // Channel indicator
289     CYGNUM_HAL_INTERRUPT_SCC3,      // interrupt
290     _SCC_CHAN
291 };
292 #if CYGNUM_IO_SERIAL_POWERPC_MPC8XXX_SCC3_BUFSIZE > 0
293 static unsigned char mpc8xxx_smc_serial_out_buf_scc3[CYGNUM_IO_SERIAL_POWERPC_MPC8XXX_SCC3_BUFSIZE];
294 static unsigned char mpc8xxx_smc_serial_in_buf_scc3[CYGNUM_IO_SERIAL_POWERPC_MPC8XXX_SCC3_BUFSIZE];
295
296 static SERIAL_CHANNEL_USING_INTERRUPTS(mpc8xxx_sxx_serial_channel_scc3,
297                                        mpc8xxx_sxx_serial_funs, 
298                                        mpc8xxx_sxx_serial_info_scc3,
299                                        CYG_SERIAL_BAUD_RATE(CYGNUM_IO_SERIAL_POWERPC_MPC8XXX_SCC3_BAUD),
300                                        CYG_SERIAL_STOP_DEFAULT,
301                                        CYG_SERIAL_PARITY_DEFAULT,
302                                        CYG_SERIAL_WORD_LENGTH_DEFAULT,
303                                        CYG_SERIAL_FLAGS_DEFAULT,
304                                        &mpc8xxx_smc_serial_out_buf_scc3[0], sizeof(mpc8xxx_smc_serial_out_buf_scc3),
305                                        &mpc8xxx_smc_serial_in_buf_scc3[0], sizeof(mpc8xxx_smc_serial_in_buf_scc3)
306     );
307 #else
308 static SERIAL_CHANNEL(mpc8xxx_sxx_serial_channel_scc3,
309                       mpc8xxx_sxx_serial_funs, 
310                       mpc8xxx_sxx_serial_info_scc3,
311                       CYG_SERIAL_BAUD_RATE(CYGNUM_IO_SERIAL_POWERPC_MPC8XXX_SCC3_BAUD),
312                       CYG_SERIAL_STOP_DEFAULT,
313                       CYG_SERIAL_PARITY_DEFAULT,
314                       CYG_SERIAL_WORD_LENGTH_DEFAULT,
315                       CYG_SERIAL_FLAGS_DEFAULT
316     );
317 #endif
318 static unsigned char mpc8xxx_scc3_txbuf[CYGNUM_IO_SERIAL_POWERPC_MPC8XXX_SCC3_TxNUM*CYGNUM_IO_SERIAL_POWERPC_MPC8XXX_SCC3_TxSIZE] __attribute__((aligned(HAL_DCACHE_LINE_SIZE)));
319 static unsigned char mpc8xxx_scc3_rxbuf[CYGNUM_IO_SERIAL_POWERPC_MPC8XXX_SCC3_RxNUM*CYGNUM_IO_SERIAL_POWERPC_MPC8XXX_SCC3_RxSIZE] __attribute__((aligned(HAL_DCACHE_LINE_SIZE)));
320
321 DEVTAB_ENTRY(mpc8xxx_smc_serial_io_scc3, 
322              CYGDAT_IO_SERIAL_POWERPC_MPC8XXX_SCC3_NAME,
323              0,                     // Does not depend on a lower level interface
324              &cyg_io_serial_devio, 
325              mpc8xxx_sxx_serial_init, 
326              mpc8xxx_sxx_serial_lookup,     // Serial driver may need initializing
327              &mpc8xxx_sxx_serial_channel_scc3
328     );
329 #endif //  CYGPKG_IO_SERIAL_POWERPC_MPC8XXX_SCC3
330
331 // Internal function to actually configure the hardware to desired baud rate, etc.
332 static bool
333 mpc8xxx_smc_serial_config_port(serial_channel *chan, cyg_serial_info_t *new_config, bool init)
334 {
335     mpc8xxx_sxx_serial_info *smc_chan = (mpc8xxx_sxx_serial_info *)chan->dev_priv;
336     unsigned int baud_divisor = select_baud[new_config->baud];
337     cyg_uint32 _lcr;
338     volatile struct smc_regs_8260 *ctl = (volatile struct smc_regs_8260*)smc_chan->ctl;
339
340     if (baud_divisor == 0) return false;
341     // Disable channel during setup
342     ctl->smc_smcmr = MPC8XXX_SMCMR_UART;  // Disabled, UART mode
343     HAL_IO_BARRIER();  // Inforce I/O ordering
344     // Disable port interrupts while changing hardware
345     _lcr = smc_select_word_length[new_config->word_length - CYGNUM_SERIAL_WORD_LENGTH_5] | 
346         smc_select_stop_bits[new_config->stop] |
347         smc_select_parity[new_config->parity];
348     // Stop transmitter while changing baud rate
349     IMM->cpm_cpcr = smc_chan->channel | CPCR_STOP_TX | CPCR_FLG;
350     HAL_IO_BARRIER();  // Inforce I/O ordering
351     // Set baud rate generator
352     *smc_chan->brg = 0x10000 | (UART_BITRATE(baud_divisor)<<1);
353
354     // Enable channel with new configuration
355     ctl->smc_smcmr = MPC8XXX_SMCMR_UART|MPC8XXX_SMCMR_TEN|MPC8XXX_SMCMR_REN|_lcr;
356     HAL_IO_BARRIER();  // Inforce I/O ordering
357     IMM->cpm_cpcr = smc_chan->channel | CPCR_INIT_TX_RX_PARAMS | CPCR_FLG;
358     if (new_config != &chan->config) {
359         chan->config = *new_config;
360     }
361     return true;
362 }
363
364 // Function to set up internal tables for device.
365 static void
366 mpc8xxx_smc_serial_init_info(mpc8xxx_sxx_serial_info *smc_chan,
367                              t_Smc_Pram *uart_pram,
368                              volatile struct smc_regs_8260 *ctl,
369                              unsigned long *brg,
370                              int TxBD, int TxNUM, int TxSIZE,
371                              cyg_uint8 *TxBUF,
372                              int RxBD, int RxNUM, int RxSIZE,
373                              cyg_uint8 *RxBUF)
374 {
375     struct cp_bufdesc *txbd, *rxbd;
376     int i;
377     
378     smc_chan->pram = (void *)uart_pram;
379     smc_chan->ctl = (void *)ctl;
380
381     // Set up baud rate generator
382     smc_chan->brg = (void *)brg;
383
384     // Disable channel during setup
385     ctl->smc_smcmr = MPC8XXX_SMCMR_UART;  // Disabled, UART mode
386
387     // Rx, Tx function codes (used for access)
388     uart_pram->rfcr = 0x18;
389     uart_pram->tfcr = 0x18;
390
391     // Pointers to Rx & Tx buffer descriptor rings
392     uart_pram->rbase = RxBD;
393     uart_pram->tbase = TxBD;
394
395     /* tx and rx buffer descriptors */
396     txbd = (struct cp_bufdesc *)((char *)IMM + TxBD);
397     rxbd = (struct cp_bufdesc *)((char *)IMM + RxBD);
398     smc_chan->txbd = txbd;
399     smc_chan->tbase = txbd;
400     smc_chan->txsize = TxSIZE;
401     smc_chan->rxbd = rxbd;
402     smc_chan->rbase = rxbd;
403     smc_chan->rxsize = RxSIZE;
404
405     /* set max_idle feature - generate interrupt after 4 chars idle period */
406     uart_pram->max_idl = 4;
407
408     /* no last brk char received */
409     uart_pram->brkln = 0;
410
411     /* no break condition occurred */
412     uart_pram->brkec = 0;
413
414     /* 1 break char sent on top XMIT */
415     uart_pram->brkcr = 1;
416
417     /* setup RX buffer descriptors */
418     for (i = 0;  i < RxNUM;  i++) {
419         rxbd->length = 0;
420         rxbd->buffer = RxBUF;
421         rxbd->ctrl   = _BD_CTL_Ready | _BD_CTL_Int;
422         if (i == (RxNUM-1)) rxbd->ctrl |= _BD_CTL_Wrap;  // Last buffer
423         RxBUF += RxSIZE;
424         rxbd++;
425     }
426     /* setup TX buffer descriptors */
427     for (i = 0;  i < TxNUM;  i++) {
428         txbd->length = 0;
429         txbd->buffer = TxBUF;
430         txbd->ctrl   = 0;
431         if (i == (TxNUM-1)) txbd->ctrl |= _BD_CTL_Wrap;  // Last buffer
432         TxBUF += TxSIZE;
433         txbd++;
434     }
435     /*
436      *  Reset Rx & Tx params
437      */
438     HAL_IO_BARRIER();  // Inforce I/O ordering
439     IMM->cpm_cpcr = smc_chan->channel | CPCR_INIT_TX_RX_PARAMS | CPCR_FLG;
440     HAL_IO_BARRIER();  // Inforce I/O ordering
441     /*
442      *  Clear any previous events. Enable interrupts.
443      *  (Section 16.15.7.14 and 16.15.7.15)
444      */
445     ctl->smc_smce = 0xFF;
446     ctl->smc_smcm = SMCE_Bsy|SMCE_Tx|SMCE_Rx;
447 }
448
449 // Internal function to actually configure the hardware to desired baud rate, etc.
450 static bool
451 mpc8xxx_scc_serial_config_port(serial_channel *chan, cyg_serial_info_t *new_config, bool init)
452 {
453     mpc8xxx_sxx_serial_info *scc_chan = (mpc8xxx_sxx_serial_info *)chan->dev_priv;
454     unsigned int baud_divisor = select_baud[new_config->baud];
455     volatile struct scc_regs_8260 *regs = (volatile struct scc_regs_8260*)scc_chan->ctl;
456
457     if (baud_divisor == 0) return false;
458     // Set baud rate generator
459     *scc_chan->brg = 0x10000 | (UART_BITRATE(baud_divisor)<<1);
460     // Disable channel during setup
461     HAL_IO_BARRIER();  // Inforce I/O ordering
462     regs->gsmr_l = 0;
463     regs->psmr = MPC8XXX_SCC_PSMR_ASYNC | 
464         scc_select_word_length[new_config->word_length - CYGNUM_SERIAL_WORD_LENGTH_5] | 
465         scc_select_stop_bits[new_config->stop] |
466         scc_select_parity[new_config->parity];
467
468     // Enable channel with new configuration
469     regs->gsmr_h = 0x20;          // 8bit FIFO
470     regs->gsmr_l = 0x00028004;    // 16x TxCLK, 16x RxCLK, UART
471
472     /*
473      *  Init Rx & Tx params for SCCX
474      */
475     HAL_IO_BARRIER();  // Inforce I/O ordering
476     IMM->cpm_cpcr = CPCR_INIT_TX_RX_PARAMS | scc_chan->channel | CPCR_FLG;
477
478     HAL_IO_BARRIER();  // Inforce I/O ordering
479     regs->gsmr_l |= GSMR_L1_ENT | GSMR_L1_ENR;  // Enable Rx, Tx
480     if (new_config != &chan->config) {
481         chan->config = *new_config;
482     }
483     return true;
484 }
485
486 // Function to set up internal tables for device.
487 static void
488 mpc8xxx_scc_serial_init_info(mpc8xxx_sxx_serial_info *scc_chan, 
489                              volatile t_Scc_Pram *uart_pram,
490                              volatile struct scc_regs_8260 *ctl,
491                              unsigned long *brg,
492                              int TxBD, int TxNUM, int TxSIZE,
493                              cyg_uint8 *TxBUF,
494                              int RxBD, int RxNUM, int RxSIZE,
495                              cyg_uint8 *RxBUF)
496 {
497     struct cp_bufdesc *txbd, *rxbd;
498     int i;
499
500     // Disable channel during setup
501     ctl->gsmr_l = 0;
502     scc_chan->pram = (void *)uart_pram;
503     scc_chan->ctl = (void *)ctl;
504
505     // Set up baud rate generator
506     scc_chan->brg = brg;
507
508     /*
509      *  Set Rx and Tx function code
510      *  (Section 16.15.4.2)
511      */
512     uart_pram->rfcr = 0x18;
513     uart_pram->tfcr = 0x18;
514     /*
515      *  Set pointers to buffer descriptors.
516      *  (Sections 16.15.4.1, 16.15.7.12, and 16.15.7.13)
517      */
518     uart_pram->rbase = RxBD;
519     uart_pram->tbase = TxBD;
520     /* tx and rx buffer descriptors */
521     txbd = (struct cp_bufdesc *)((char *)IMM + TxBD);
522     rxbd = (struct cp_bufdesc *)((char *)IMM + RxBD);
523     scc_chan->txbd = txbd;
524     scc_chan->tbase = txbd;
525     scc_chan->txsize = TxSIZE;
526     scc_chan->rxbd = rxbd;
527     scc_chan->rbase = rxbd;
528     scc_chan->rxsize = RxSIZE;
529     /* max receive buffer length */
530     uart_pram->mrblr = RxSIZE;
531     /* set max_idle feature - generate interrupt after 4 chars idle period */
532     uart_pram->SpecificProtocol.u.max_idl = 4;
533     /* no last brk char received */
534     uart_pram->SpecificProtocol.u.brkln = 0;
535     /* no break condition occurred */
536     uart_pram->SpecificProtocol.u.brkec = 0;
537     /* 1 break char sent on top XMIT */
538     uart_pram->SpecificProtocol.u.brkcr = 1;
539     /* character mask */
540     uart_pram->SpecificProtocol.u.rccm  = 0xC0FF;
541     /* control characters */
542     for (i = 0;  i < 8;  i++) {
543         uart_pram->SpecificProtocol.u.cc[i] = 0x8000;  // Mark unused
544     }
545     /* setup RX buffer descriptors */
546     for (i = 0;  i < RxNUM;  i++) {
547         rxbd->length = 0;
548         rxbd->buffer = RxBUF;
549         rxbd->ctrl   = _BD_CTL_Ready | _BD_CTL_Int;
550         if (i == (RxNUM-1)) rxbd->ctrl |= _BD_CTL_Wrap;  // Last buffer
551         RxBUF += RxSIZE;
552         rxbd++;
553     }
554     /* setup TX buffer descriptors */
555     for (i = 0;  i < TxNUM;  i++) {
556         txbd->length = 0;
557         txbd->buffer = TxBUF;
558         txbd->ctrl   = 0;
559         if (i == (TxNUM-1)) txbd->ctrl |= _BD_CTL_Wrap;  // Last buffer
560         TxBUF += TxSIZE;
561         txbd++;
562     }
563     /*
564      *  Reset Rx & Tx params
565      */
566     HAL_IO_BARRIER();  // Inforce I/O ordering
567     IMM->cpm_cpcr = scc_chan->channel | CPCR_INIT_TX_RX_PARAMS | CPCR_FLG;
568     /*
569      *  Clear any previous events. Enable interrupts.
570      *  (Section 16.15.7.14 and 16.15.7.15)
571      */
572     HAL_IO_BARRIER();  // Inforce I/O ordering
573     ctl->scce = 0xFFFF;
574     ctl->sccm = (SCCE_Bsy | SCCE_Tx | SCCE_Rx);
575 }
576
577 // Function to initialize the device.  Called at bootstrap time.
578 static bool 
579 mpc8xxx_sxx_serial_init(struct cyg_devtab_entry *tab)
580 {
581     serial_channel *chan = (serial_channel *)tab->priv;
582     mpc8xxx_sxx_serial_info *smc_chan = (mpc8xxx_sxx_serial_info *)chan->dev_priv;
583     int TxBD, RxBD;
584     int cache_state;
585
586     HAL_DCACHE_IS_ENABLED(cache_state);
587     HAL_DCACHE_SYNC();
588     HAL_DCACHE_DISABLE();
589 #ifdef CYGDBG_IO_INIT
590     diag_printf("MPC8XXX_SMC SERIAL init - dev: %x.%d = %s\n", smc_chan->channel, smc_chan->int_num, tab->name);
591 #endif
592 #ifdef CYGPKG_IO_SERIAL_POWERPC_MPC8XXX_SMC1
593     if (chan == &mpc8xxx_sxx_serial_channel_smc1) {
594         TxBD = _mpc8xxx_allocBd(sizeof(struct cp_bufdesc)*CYGNUM_IO_SERIAL_POWERPC_MPC8XXX_SMC1_TxNUM);
595         RxBD = _mpc8xxx_allocBd(sizeof(struct cp_bufdesc)*CYGNUM_IO_SERIAL_POWERPC_MPC8XXX_SMC1_RxNUM);
596         mpc8xxx_smc_serial_init_info(&mpc8xxx_sxx_serial_info_smc1,
597                                      (t_Smc_Pram *)((char *)IMM + DPRAM_SMC1_OFFSET),
598                                      &IMM->smc_regs[SMC1],
599                                      (unsigned long *)&IMM->brgs_brgc7,
600                                      TxBD, 
601                                      CYGNUM_IO_SERIAL_POWERPC_MPC8XXX_SMC1_TxNUM,
602                                      CYGNUM_IO_SERIAL_POWERPC_MPC8XXX_SMC1_TxSIZE,
603                                      &mpc8xxx_smc1_txbuf[0],
604                                      RxBD, 
605                                      CYGNUM_IO_SERIAL_POWERPC_MPC8XXX_SMC1_RxNUM,
606                                      CYGNUM_IO_SERIAL_POWERPC_MPC8XXX_SMC1_RxSIZE,
607                                      &mpc8xxx_smc1_rxbuf[0]
608             );
609     }
610 #endif
611 #ifdef CYGPKG_IO_SERIAL_POWERPC_MPC8XXX_SMC2
612 #warning "Serial driver on SMC2 is unverified"
613     if (chan == &mpc8xxx_sxx_serial_channel_smc2) {
614         TxBD = _mpc8xxx_allocBd(sizeof(struct cp_bufdesc)*CYGNUM_IO_SERIAL_POWERPC_MPC8XXX_SMC2_TxNUM);
615         RxBD = _mpc8xxx_allocBd(sizeof(struct cp_bufdesc)*CYGNUM_IO_SERIAL_POWERPC_MPC8XXX_SMC2_RxNUM);
616         mpc8xxx_smc_serial_init_info(&mpc8xxx_sxx_serial_info_smc2,
617                                      &IMM->pram[3].scc.pothers.smc_modem.psmc.u, // PRAM
618                                      &IMM->smc_regs[1], // Control registers
619                                      &IMM->brgs_brgc7,
620                                      TxBD, 
621                                      CYGNUM_IO_SERIAL_POWERPC_MPC8XXX_SMC2_TxNUM,
622                                      CYGNUM_IO_SERIAL_POWERPC_MPC8XXX_SMC2_TxSIZE,
623                                      &mpc8xxx_smc2_txbuf[0],
624                                      RxBD, 
625                                      CYGNUM_IO_SERIAL_POWERPC_MPC8XXX_SMC2_RxNUM,
626                                      CYGNUM_IO_SERIAL_POWERPC_MPC8XXX_SMC2_RxSIZE,
627                                      &mpc8xxx_smc2_rxbuf[0]
628             );
629     }
630 #endif
631 #ifdef CYGPKG_IO_SERIAL_POWERPC_MPC8XXX_SCC1
632     if (chan == &mpc8xxx_sxx_serial_channel_scc1) {
633         TxBD = _mpc8xxx_allocBd(sizeof(struct cp_bufdesc)*CYGNUM_IO_SERIAL_POWERPC_MPC8XXX_SCC1_TxNUM);
634         RxBD = _mpc8xxx_allocBd(sizeof(struct cp_bufdesc)*CYGNUM_IO_SERIAL_POWERPC_MPC8XXX_SCC1_RxNUM);
635         mpc8xxx_scc_serial_init_info(&mpc8xxx_sxx_serial_info_scc1,
636                                      &IMM->pram.serials.scc_pram[SCC1],
637                                      &IMM->scc_regs[SCC1],
638                                      (unsigned long *)&IMM->brgs_brgc1,
639                                      TxBD, 
640                                      CYGNUM_IO_SERIAL_POWERPC_MPC8XXX_SCC1_TxNUM,
641                                      CYGNUM_IO_SERIAL_POWERPC_MPC8XXX_SCC1_TxSIZE,
642                                      &mpc8xxx_scc1_txbuf[0],
643                                      RxBD, 
644                                      CYGNUM_IO_SERIAL_POWERPC_MPC8XXX_SCC1_RxNUM,
645                                      CYGNUM_IO_SERIAL_POWERPC_MPC8XXX_SCC1_RxSIZE,
646                                      &mpc8xxx_scc1_rxbuf[0]
647             );
648     }
649 #endif
650 #ifdef CYGPKG_IO_SERIAL_POWERPC_MPC8XXX_SCC2
651 #warning "Serial driver on SCC2 is unverified"
652     if (chan == &mpc8xxx_sxx_serial_channel_scc2) {
653         TxBD = _mpc8xxx_allocBd(sizeof(struct cp_bufdesc)*CYGNUM_IO_SERIAL_POWERPC_MPC8XXX_SCC2_TxNUM);
654         RxBD = _mpc8xxx_allocBd(sizeof(struct cp_bufdesc)*CYGNUM_IO_SERIAL_POWERPC_MPC8XXX_SCC2_RxNUM);
655         mpc8xxx_scc_serial_init_info(&mpc8xxx_sxx_serial_info_scc2,
656                                      &IMM->pram[1].scc.pscc.u, // PRAM
657                                      &IMM->scc_regs[1],        // Control registers
658                                      &IMM->brgs_brgc2,
659                                      TxBD, 
660                                      CYGNUM_IO_SERIAL_POWERPC_MPC8XXX_SCC2_TxNUM,
661                                      CYGNUM_IO_SERIAL_POWERPC_MPC8XXX_SCC2_TxSIZE,
662                                      &mpc8xxx_scc2_txbuf[0],
663                                      RxBD, 
664                                      CYGNUM_IO_SERIAL_POWERPC_MPC8XXX_SCC2_RxNUM,
665                                      CYGNUM_IO_SERIAL_POWERPC_MPC8XXX_SCC2_RxSIZE,
666                                      &mpc8xxx_scc2_rxbuf[0]
667             );
668     }
669 #endif
670 #ifdef CYGPKG_IO_SERIAL_POWERPC_MPC8XXX_SCC3
671 #warning "Serial driver on SCC3 is unverified"
672     if (chan == &mpc8xxx_sxx_serial_channel_scc3) {
673         TxBD = _mpc8xxx_allocBd(sizeof(struct cp_bufdesc)*CYGNUM_IO_SERIAL_POWERPC_MPC8XXX_SCC3_TxNUM);
674         RxBD = _mpc8xxx_allocBd(sizeof(struct cp_bufdesc)*CYGNUM_IO_SERIAL_POWERPC_MPC8XXX_SCC3_RxNUM);
675         mpc8xxx_scc_serial_init_info(&mpc8xxx_sxx_serial_info_scc3,
676                                      &IMM->pram[2].scc.pscc.u, // PRAM
677                                      &IMM->scc_regs[2],        // Control registersn
678                                      &IMM->brgs_brgc3,
679                                      TxBD, 
680                                      CYGNUM_IO_SERIAL_POWERPC_MPC8XXX_SCC3_TxNUM,
681                                      CYGNUM_IO_SERIAL_POWERPC_MPC8XXX_SCC3_TxSIZE,
682                                      &mpc8xxx_scc3_txbuf[0],
683                                      RxBD, 
684                                      CYGNUM_IO_SERIAL_POWERPC_MPC8XXX_SCC3_RxNUM,
685                                      CYGNUM_IO_SERIAL_POWERPC_MPC8XXX_SCC3_RxSIZE,
686                                      &mpc8xxx_scc3_rxbuf[0]
687             );
688     }
689 #endif
690     (chan->callbacks->serial_init)(chan);  // Really only required for interrupt driven devices
691     if (chan->out_cbuf.len != 0) {
692         cyg_drv_interrupt_create(smc_chan->int_num,
693                                  0,                      // Priority - unused (but asserted)
694                                  (cyg_addrword_t)chan,   //  Data item passed to interrupt handler
695                                  mpc8xxx_sxx_serial_ISR,
696                                  mpc8xxx_sxx_serial_DSR,
697                                  &smc_chan->serial_interrupt_handle,
698                                  &smc_chan->serial_interrupt);
699         cyg_drv_interrupt_attach(smc_chan->serial_interrupt_handle);
700         cyg_drv_interrupt_unmask(smc_chan->int_num);
701     }
702     if (smc_chan->type == _SMC_CHAN) {
703         mpc8xxx_smc_serial_config_port(chan, &chan->config, true);
704     } else {
705         mpc8xxx_scc_serial_config_port(chan, &chan->config, true);
706     }
707     if (cache_state)
708         HAL_DCACHE_ENABLE();
709     return true;
710 }
711
712 // This routine is called when the device is "looked" up (i.e. attached)
713 static Cyg_ErrNo 
714 mpc8xxx_sxx_serial_lookup(struct cyg_devtab_entry **tab, 
715                   struct cyg_devtab_entry *sub_tab,
716                   const char *name)
717 {
718     serial_channel *chan = (serial_channel *)(*tab)->priv;
719     (chan->callbacks->serial_init)(chan);  // Really only required for interrupt driven devices
720     return ENOERR;
721 }
722
723 // Force the current transmit buffer to be sent
724 static void
725 mpc8xxx_sxx_serial_flush(mpc8xxx_sxx_serial_info *smc_chan)
726 {
727     volatile struct cp_bufdesc *txbd = smc_chan->txbd;
728     int cache_state;
729                                        
730     HAL_DCACHE_IS_ENABLED(cache_state);
731     if (cache_state) {
732       HAL_DCACHE_FLUSH(txbd->buffer, smc_chan->txsize);
733     }
734     if ((txbd->length > 0) && 
735         ((txbd->ctrl & (_BD_CTL_Ready|_BD_CTL_Int)) == 0)) {
736         txbd->ctrl |= _BD_CTL_Ready|_BD_CTL_Int;  // Signal buffer ready
737         if (txbd->ctrl & _BD_CTL_Wrap) {
738             txbd = smc_chan->tbase;
739         } else {
740             txbd++;
741         }
742         smc_chan->txbd = txbd;
743     }
744 }
745
746 // Send a character to the device output buffer.
747 // Return 'true' if character is sent to device
748 static bool
749 mpc8xxx_sxx_serial_putc(serial_channel *chan, unsigned char c)
750 {
751     mpc8xxx_sxx_serial_info *smc_chan = (mpc8xxx_sxx_serial_info *)chan->dev_priv;
752     volatile struct cp_bufdesc *txbd, *txfirst;
753     volatile t_Smc_Pram *pram = (volatile t_Smc_Pram *)smc_chan->pram;
754     bool res;
755
756     cyg_drv_dsr_lock();  // Avoid race condition testing pointers
757     txbd = (struct cp_bufdesc *)((char *)IMM + pram->tbptr);
758     txfirst = txbd;
759     // Scan for a non-busy buffer
760     while (txbd->ctrl & _BD_CTL_Ready) {
761         // This buffer is busy, move to next one
762         if (txbd->ctrl & _BD_CTL_Wrap) {
763             txbd = smc_chan->tbase;
764         } else {
765             txbd++;
766         }
767         if (txbd == txfirst) break;  // Went all the way around
768     }
769     smc_chan->txbd = txbd;
770     if ((txbd->ctrl & (_BD_CTL_Ready|_BD_CTL_Int)) == 0) {
771         // Transmit buffer is not full/busy
772         txbd->buffer[txbd->length++] = c;
773         if (txbd->length == smc_chan->txsize) {
774             // This buffer is now full, tell SMC to start processing it
775             mpc8xxx_sxx_serial_flush(smc_chan);
776         }
777         res = true;
778     } else {
779         // No space
780         res = false;
781     }
782     cyg_drv_dsr_unlock();
783     return res;
784 }
785
786 // Fetch a character from the device input buffer, waiting if necessary
787 static unsigned char 
788 mpc8xxx_sxx_serial_getc(serial_channel *chan)
789 {
790     unsigned char c;
791     mpc8xxx_sxx_serial_info *smc_chan = (mpc8xxx_sxx_serial_info *)chan->dev_priv;
792     volatile struct cp_bufdesc *rxbd = smc_chan->rxbd;
793
794     while ((rxbd->ctrl & _BD_CTL_Ready) != 0) ;
795     c = rxbd->buffer[0];
796     rxbd->length = smc_chan->rxsize;
797     rxbd->ctrl |= _BD_CTL_Ready;
798     if (rxbd->ctrl & _BD_CTL_Wrap) {
799         rxbd = smc_chan->rbase;
800     } else {
801         rxbd++;
802     }
803     smc_chan->rxbd = (struct cp_bufdesc *)rxbd;
804     return c;
805 }
806
807 // Set up the device characteristics; baud rate, etc.
808 static Cyg_ErrNo
809 mpc8xxx_sxx_serial_set_config(serial_channel *chan, cyg_uint32 key,
810                             const void *xbuf, cyg_uint32 *len)
811 {
812     int res;
813
814     switch (key) {
815     case CYG_IO_SET_CONFIG_SERIAL_INFO:
816     {
817         // FIXME - The documentation says that you can't change the baud rate
818         // again until at least two BRG input clocks have occurred.
819         cyg_serial_info_t *config = (cyg_serial_info_t *)xbuf;
820         mpc8xxx_sxx_serial_info *smc_chan = (mpc8xxx_sxx_serial_info *)chan->dev_priv;
821         if ( *len < sizeof(cyg_serial_info_t) ) {
822             return -EINVAL;
823         }
824         *len = sizeof(cyg_serial_info_t);
825         if (smc_chan->type == _SMC_CHAN) {
826             res = mpc8xxx_smc_serial_config_port(chan, config, true);
827         } else {
828             res = mpc8xxx_scc_serial_config_port(chan, config, true);
829         }
830         if ( true != res )
831             return -EINVAL;
832     }
833     break;
834     default:
835         return -EINVAL;
836     }
837     return ENOERR;
838 }
839
840 // Enable the transmitter (interrupt) on the device
841 static void
842 mpc8xxx_sxx_serial_start_xmit(serial_channel *chan)
843 {
844     mpc8xxx_sxx_serial_info *smc_chan = (mpc8xxx_sxx_serial_info *)chan->dev_priv;
845     cyg_drv_dsr_lock();
846     if (smc_chan->txbd->length == 0) {
847         // See if there is anything to put in this buffer, just to get it going
848         (chan->callbacks->xmt_char)(chan);
849     }
850     if (smc_chan->txbd->length != 0) {
851         // Make sure it gets started
852         mpc8xxx_sxx_serial_flush(smc_chan);
853     }
854     cyg_drv_dsr_unlock();
855 }
856
857 // Disable the transmitter on the device
858 static void 
859 mpc8xxx_sxx_serial_stop_xmit(serial_channel *chan)
860 {
861     mpc8xxx_sxx_serial_info *smc_chan = (mpc8xxx_sxx_serial_info *)chan->dev_priv;
862     // If anything is in the last buffer, need to get it started
863     if (smc_chan->txbd->length != 0) {
864         mpc8xxx_sxx_serial_flush(smc_chan);
865     }
866 }
867
868 // Serial I/O - low level interrupt handler (ISR)
869 static cyg_uint32 
870 mpc8xxx_sxx_serial_ISR(cyg_vector_t vector, cyg_addrword_t data)
871 {
872     serial_channel *chan = (serial_channel *)data;
873     mpc8xxx_sxx_serial_info *smc_chan = (mpc8xxx_sxx_serial_info *)chan->dev_priv;
874     cyg_drv_interrupt_mask(smc_chan->int_num);
875     return (CYG_ISR_HANDLED|CYG_ISR_CALL_DSR);  // Cause DSR to be run
876 }
877
878 // Serial I/O - high level interrupt handler (DSR)
879 static void
880 mpc8xxx_smc_serial_DSR(serial_channel *chan)
881 {
882     mpc8xxx_sxx_serial_info *smc_chan = (mpc8xxx_sxx_serial_info *)chan->dev_priv;
883     volatile struct smc_regs_8260 *ctl = (volatile struct smc_regs_8260 *)smc_chan->ctl;
884     volatile struct cp_bufdesc *txbd;
885     volatile struct cp_bufdesc *rxbd = smc_chan->rxbd;
886     volatile t_Smc_Pram *pram = (volatile t_Smc_Pram *)smc_chan->pram;
887     struct cp_bufdesc *rxlast;
888     int i, cache_state;
889
890     if (ctl->smc_smce & SMCE_Tx) {
891         // Transmit interrupt
892         ctl->smc_smce = SMCE_Tx;  // Reset interrupt state;
893         txbd = smc_chan->tbase;  // First buffer
894         while (true) {
895             if ((txbd->ctrl & (_BD_CTL_Ready|_BD_CTL_Int)) == _BD_CTL_Int) {
896                 txbd->length = 0;
897                 txbd->ctrl &= ~_BD_CTL_Int;  // Reset interrupt bit
898             }
899             if (txbd->ctrl & _BD_CTL_Wrap) {
900                 txbd = smc_chan->tbase;
901                 break;
902             } else {
903                 txbd++;
904             }
905         }
906         (chan->callbacks->xmt_char)(chan);
907     }
908     while (ctl->smc_smce & SMCE_Rx) {
909         // Receive interrupt
910         ctl->smc_smce = SMCE_Rx;  // Reset interrupt state;
911         rxlast = (struct cp_bufdesc *) (
912             (char *)IMM + pram->rbptr );
913         while (rxbd != rxlast) {
914             if ((rxbd->ctrl & _BD_CTL_Ready) == 0) {
915                 for (i = 0;  i < rxbd->length;  i++) {
916                     (chan->callbacks->rcv_char)(chan, rxbd->buffer[i]);
917                 }
918                 // Note: the MBX860 does not seem to snoop/invalidate the data cache properly!
919                 HAL_DCACHE_IS_ENABLED(cache_state);
920                 if (cache_state) {
921                     HAL_DCACHE_INVALIDATE(rxbd->buffer, smc_chan->rxsize);  // Make sure no stale data
922                 }
923                 rxbd->length = 0;
924                 rxbd->ctrl |= _BD_CTL_Ready;
925             }
926             if (rxbd->ctrl & _BD_CTL_Wrap) {
927                 rxbd = smc_chan->rbase;
928             } else {
929                 rxbd++;
930             }
931         }
932         smc_chan->rxbd = (struct cp_bufdesc *)rxbd;
933     }
934     if (ctl->smc_smce & SMCE_Bsy) {
935         ctl->smc_smce = SMCE_Bsy;  // Reset interrupt state;
936     }
937     cyg_drv_interrupt_acknowledge(smc_chan->int_num);
938     cyg_drv_interrupt_unmask(smc_chan->int_num);
939 }
940
941 static void
942 mpc8xxx_scc_serial_DSR(serial_channel *chan)
943 {
944     mpc8xxx_sxx_serial_info *smc_chan = (mpc8xxx_sxx_serial_info *)chan->dev_priv;
945     volatile struct scc_regs_8260 *ctl = (volatile struct scc_regs_8260 *)smc_chan->ctl;
946     volatile struct cp_bufdesc *txbd;
947     volatile struct cp_bufdesc *rxbd = smc_chan->rxbd;
948     volatile t_Smc_Pram *pram = (volatile t_Smc_Pram *)smc_chan->pram;
949     struct cp_bufdesc *rxlast;
950     int i, cache_state;
951
952     if (ctl->scce & SCCE_Tx) {
953         // Transmit interrupt
954         ctl->scce = SCCE_Tx;  // Reset interrupt state;
955         txbd = smc_chan->tbase;  // First buffer
956         while (true) {
957             if ((txbd->ctrl & (_BD_CTL_Ready|_BD_CTL_Int)) == _BD_CTL_Int) {
958                 txbd->length = 0;
959                 txbd->ctrl &= ~_BD_CTL_Int;  // Reset interrupt bit
960             }
961             if (txbd->ctrl & _BD_CTL_Wrap) {
962                 txbd = smc_chan->tbase;
963                 break;
964             } else {
965                 txbd++;
966             }
967         }
968         (chan->callbacks->xmt_char)(chan);
969     }
970     while (ctl->scce & SCCE_Rx) {
971         // Receive interrupt
972         ctl->scce = SCCE_Rx;  // Reset interrupt state;
973         rxlast = (struct cp_bufdesc *) ((char *)IMM + pram->rbptr);
974         while (rxbd != rxlast) {
975             if ((rxbd->ctrl & _BD_CTL_Ready) == 0) {
976                 for (i = 0;  i < rxbd->length;  i++) {
977                     (chan->callbacks->rcv_char)(chan, rxbd->buffer[i]);
978                 }
979                 // Note: the MBX860 does not seem to snoop/invalidate the data cache properly!
980                 HAL_DCACHE_IS_ENABLED(cache_state);
981                 if (cache_state) {
982                     HAL_DCACHE_INVALIDATE(rxbd->buffer, smc_chan->rxsize);  // Make sure no stale data
983                 }
984                 rxbd->length = 0;
985                 rxbd->ctrl |= _BD_CTL_Ready;
986             }
987             if (rxbd->ctrl & _BD_CTL_Wrap) {
988                 rxbd = smc_chan->rbase;
989             } else {
990                 rxbd++;
991             }
992         }
993         smc_chan->rxbd = (struct cp_bufdesc *)rxbd;
994     }
995     if (ctl->scce & SCCE_Bsy) {
996         ctl->scce = SCCE_Bsy;  // Reset interrupt state;
997     }
998     cyg_drv_interrupt_acknowledge(smc_chan->int_num);
999     cyg_drv_interrupt_unmask(smc_chan->int_num);
1000 }
1001
1002 static void       
1003 mpc8xxx_sxx_serial_DSR(cyg_vector_t vector, cyg_ucount32 count, cyg_addrword_t data)
1004 {
1005     serial_channel *chan = (serial_channel *)data;
1006     mpc8xxx_sxx_serial_info *smc_chan = (mpc8xxx_sxx_serial_info *)chan->dev_priv;
1007
1008     if (smc_chan->type == _SMC_CHAN) {
1009         mpc8xxx_smc_serial_DSR(chan);
1010     } else {
1011         mpc8xxx_scc_serial_DSR(chan);
1012     }
1013 }
1014
1015 // ------------------------------------------------------------------------
1016 // EOF powerpc/mpc8xxx_smc_serial.c