]> git.kernelconcepts.de Git - karo-tx-redboot.git/blob - packages/devs/serial/powerpc/quicc/v2_0/src/quicc_smc_serial.c
unified MX27, MX25, MX37 trees
[karo-tx-redboot.git] / packages / devs / serial / powerpc / quicc / v2_0 / src / quicc_smc_serial.c
1 //==========================================================================
2 //
3 //      io/serial/powerpc/quicc_smc_serial.c
4 //
5 //      PowerPC QUICC (SMC/SCC) 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 // 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:      QUICC SMC Serial I/O module (interrupt driven version)
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/quicc/ppc8xx.h>
64 #include CYGBLD_HAL_PLATFORM_H
65
66 #ifdef CYGPKG_IO_SERIAL_POWERPC_QUICC_SMC
67
68 #include "quicc_smc_serial.h"
69
70 typedef struct quicc_sxx_serial_info {
71     CYG_ADDRWORD          channel;                   // Which channel SMCx/SCCx
72     short                 int_num;                   // Interrupt number
73     short                 type;                      // Channel type - SCC or SMC
74     unsigned long         *brg;                      // Which baud rate generator
75     void                  *pram;                     // Parameter RAM pointer
76     void                  *ctl;                      // SMC/SCC control registers
77     volatile struct cp_bufdesc     *txbd, *rxbd;     // Next Tx,Rx descriptor to use
78     struct cp_bufdesc     *tbase, *rbase;            // First Tx,Rx descriptor
79     int                   txsize, rxsize;            // Length of individual buffers
80     cyg_interrupt         serial_interrupt;
81     cyg_handle_t          serial_interrupt_handle;
82 } quicc_sxx_serial_info;
83
84 static bool quicc_sxx_serial_init(struct cyg_devtab_entry *tab);
85 static bool quicc_sxx_serial_putc(serial_channel *chan, unsigned char c);
86 static Cyg_ErrNo quicc_sxx_serial_lookup(struct cyg_devtab_entry **tab, 
87                                    struct cyg_devtab_entry *sub_tab,
88                                    const char *name);
89 static unsigned char quicc_sxx_serial_getc(serial_channel *chan);
90 static Cyg_ErrNo quicc_sxx_serial_set_config(serial_channel *chan,
91                                              cyg_uint32 key, const void *xbuf,
92                                              cyg_uint32 *len);
93 static void quicc_sxx_serial_start_xmit(serial_channel *chan);
94 static void quicc_sxx_serial_stop_xmit(serial_channel *chan);
95
96 static cyg_uint32 quicc_sxx_serial_ISR(cyg_vector_t vector, cyg_addrword_t data);
97 static void       quicc_sxx_serial_DSR(cyg_vector_t vector, cyg_ucount32 count, cyg_addrword_t data);
98
99 static SERIAL_FUNS(quicc_sxx_serial_funs, 
100                    quicc_sxx_serial_putc, 
101                    quicc_sxx_serial_getc,
102                    quicc_sxx_serial_set_config,
103                    quicc_sxx_serial_start_xmit,
104                    quicc_sxx_serial_stop_xmit
105     );
106
107 #ifdef CYGPKG_IO_SERIAL_POWERPC_QUICC_SMC_SMC1
108 static quicc_sxx_serial_info quicc_sxx_serial_info_smc1 = {
109     QUICC_CPM_SMC1,                 // Channel indicator
110     CYGNUM_HAL_INTERRUPT_CPM_SMC1,  // interrupt
111     _SMC_CHAN
112 };
113 #if CYGNUM_IO_SERIAL_POWERPC_QUICC_SMC_SMC1_BUFSIZE > 0
114 static unsigned char quicc_smc_serial_out_buf_smc1[CYGNUM_IO_SERIAL_POWERPC_QUICC_SMC_SMC1_BUFSIZE];
115 static unsigned char quicc_smc_serial_in_buf_smc1[CYGNUM_IO_SERIAL_POWERPC_QUICC_SMC_SMC1_BUFSIZE];
116
117 static SERIAL_CHANNEL_USING_INTERRUPTS(quicc_sxx_serial_channel_smc1,
118                                        quicc_sxx_serial_funs, 
119                                        quicc_sxx_serial_info_smc1,
120                                        CYG_SERIAL_BAUD_RATE(CYGNUM_IO_SERIAL_POWERPC_QUICC_SMC_SMC1_BAUD),
121                                        CYG_SERIAL_STOP_DEFAULT,
122                                        CYG_SERIAL_PARITY_DEFAULT,
123                                        CYG_SERIAL_WORD_LENGTH_DEFAULT,
124                                        CYG_SERIAL_FLAGS_DEFAULT,
125                                        &quicc_smc_serial_out_buf_smc1[0], sizeof(quicc_smc_serial_out_buf_smc1),
126                                        &quicc_smc_serial_in_buf_smc1[0], sizeof(quicc_smc_serial_in_buf_smc1)
127     );
128 #else
129 static SERIAL_CHANNEL(quicc_sxx_serial_channel_smc1,
130                       quicc_sxx_serial_funs, 
131                       quicc_sxx_serial_info_smc1,
132                       CYG_SERIAL_BAUD_RATE(CYGNUM_IO_SERIAL_POWERPC_QUICC_SMC_SMC1_BAUD),
133                       CYG_SERIAL_STOP_DEFAULT,
134                       CYG_SERIAL_PARITY_DEFAULT,
135                       CYG_SERIAL_WORD_LENGTH_DEFAULT,
136                       CYG_SERIAL_FLAGS_DEFAULT
137     );
138 #endif
139
140 static unsigned char quicc_smc1_txbuf[CYGNUM_IO_SERIAL_POWERPC_QUICC_SMC_SMC1_TxNUM*CYGNUM_IO_SERIAL_POWERPC_QUICC_SMC_SMC1_TxSIZE] __attribute__((aligned(HAL_DCACHE_LINE_SIZE)));
141 static unsigned char quicc_smc1_rxbuf[CYGNUM_IO_SERIAL_POWERPC_QUICC_SMC_SMC1_RxNUM*CYGNUM_IO_SERIAL_POWERPC_QUICC_SMC_SMC1_RxSIZE] __attribute__((aligned(HAL_DCACHE_LINE_SIZE)));
142
143 DEVTAB_ENTRY(quicc_smc_serial_io_smc1, 
144              CYGDAT_IO_SERIAL_POWERPC_QUICC_SMC_SMC1_NAME,
145              0,                     // Does not depend on a lower level interface
146              &cyg_io_serial_devio, 
147              quicc_sxx_serial_init, 
148              quicc_sxx_serial_lookup,     // Serial driver may need initializing
149              &quicc_sxx_serial_channel_smc1
150     );
151 #endif //  CYGPKG_IO_SERIAL_POWERPC_QUICC_SMC_SMC1
152
153 #ifdef CYGPKG_IO_SERIAL_POWERPC_QUICC_SMC_SMC2
154 static quicc_sxx_serial_info quicc_sxx_serial_info_smc2 = {
155     QUICC_CPM_SMC2,                     // Channel indicator
156     CYGNUM_HAL_INTERRUPT_CPM_SMC2_PIP,  // interrupt
157     _SMC_CHAN
158 };
159 #if CYGNUM_IO_SERIAL_POWERPC_QUICC_SMC_SMC2_BUFSIZE > 0
160 static unsigned char quicc_smc_serial_out_buf_smc2[CYGNUM_IO_SERIAL_POWERPC_QUICC_SMC_SMC2_BUFSIZE];
161 static unsigned char quicc_smc_serial_in_buf_smc2[CYGNUM_IO_SERIAL_POWERPC_QUICC_SMC_SMC2_BUFSIZE];
162
163 static SERIAL_CHANNEL_USING_INTERRUPTS(quicc_sxx_serial_channel_smc2,
164                                        quicc_sxx_serial_funs, 
165                                        quicc_sxx_serial_info_smc2,
166                                        CYG_SERIAL_BAUD_RATE(CYGNUM_IO_SERIAL_POWERPC_QUICC_SMC_SMC2_BAUD),
167                                        CYG_SERIAL_STOP_DEFAULT,
168                                        CYG_SERIAL_PARITY_DEFAULT,
169                                        CYG_SERIAL_WORD_LENGTH_DEFAULT,
170                                        CYG_SERIAL_FLAGS_DEFAULT,
171                                        &quicc_smc_serial_out_buf_smc2[0], sizeof(quicc_smc_serial_out_buf_smc2),
172                                        &quicc_smc_serial_in_buf_smc2[0], sizeof(quicc_smc_serial_in_buf_smc2)
173     );
174 #else
175 static SERIAL_CHANNEL(quicc_sxx_serial_channel_smc2,
176                       quicc_sxx_serial_funs, 
177                       quicc_sxx_serial_info_smc2,
178                       CYG_SERIAL_BAUD_RATE(CYGNUM_IO_SERIAL_POWERPC_QUICC_SMC_SMC2_BAUD),
179                       CYG_SERIAL_STOP_DEFAULT,
180                       CYG_SERIAL_PARITY_DEFAULT,
181                       CYG_SERIAL_WORD_LENGTH_DEFAULT,
182                       CYG_SERIAL_FLAGS_DEFAULT
183     );
184 #endif
185 static unsigned char quicc_smc2_txbuf[CYGNUM_IO_SERIAL_POWERPC_QUICC_SMC_SMC2_TxNUM*CYGNUM_IO_SERIAL_POWERPC_QUICC_SMC_SMC2_TxSIZE] __attribute__((aligned(HAL_DCACHE_LINE_SIZE)));
186 static unsigned char quicc_smc2_rxbuf[CYGNUM_IO_SERIAL_POWERPC_QUICC_SMC_SMC2_RxNUM*CYGNUM_IO_SERIAL_POWERPC_QUICC_SMC_SMC2_RxSIZE] __attribute__((aligned(HAL_DCACHE_LINE_SIZE)));
187
188 DEVTAB_ENTRY(quicc_smc_serial_io_smc2, 
189              CYGDAT_IO_SERIAL_POWERPC_QUICC_SMC_SMC2_NAME,
190              0,                     // Does not depend on a lower level interface
191              &cyg_io_serial_devio, 
192              quicc_sxx_serial_init, 
193              quicc_sxx_serial_lookup,     // Serial driver may need initializing
194              &quicc_sxx_serial_channel_smc2
195     );
196 #endif //  CYGPKG_IO_SERIAL_POWERPC_QUICC_SMC_SMC2
197
198 #ifdef CYGPKG_IO_SERIAL_POWERPC_QUICC_SMC_SCC1
199 static quicc_sxx_serial_info quicc_sxx_serial_info_scc1 = {
200     QUICC_CPM_SCC1,                     // Channel indicator
201     CYGNUM_HAL_INTERRUPT_CPM_SCC1,      // interrupt
202     _SCC_CHAN
203 };
204 #if CYGNUM_IO_SERIAL_POWERPC_QUICC_SMC_SCC1_BUFSIZE > 0
205 static unsigned char quicc_smc_serial_out_buf_scc1[CYGNUM_IO_SERIAL_POWERPC_QUICC_SMC_SCC1_BUFSIZE];
206 static unsigned char quicc_smc_serial_in_buf_scc1[CYGNUM_IO_SERIAL_POWERPC_QUICC_SMC_SCC1_BUFSIZE];
207
208 static SERIAL_CHANNEL_USING_INTERRUPTS(quicc_sxx_serial_channel_scc1,
209                                        quicc_sxx_serial_funs, 
210                                        quicc_sxx_serial_info_scc1,
211                                        CYG_SERIAL_BAUD_RATE(CYGNUM_IO_SERIAL_POWERPC_QUICC_SMC_SCC1_BAUD),
212                                        CYG_SERIAL_STOP_DEFAULT,
213                                        CYG_SERIAL_PARITY_DEFAULT,
214                                        CYG_SERIAL_WORD_LENGTH_DEFAULT,
215                                        CYG_SERIAL_FLAGS_DEFAULT,
216                                        &quicc_smc_serial_out_buf_scc1[0], sizeof(quicc_smc_serial_out_buf_scc1),
217                                        &quicc_smc_serial_in_buf_scc1[0], sizeof(quicc_smc_serial_in_buf_scc1)
218     );
219 #else
220 static SERIAL_CHANNEL(quicc_sxx_serial_channel_scc1,
221                       quicc_sxx_serial_funs, 
222                       quicc_sxx_serial_info_scc1,
223                       CYG_SERIAL_BAUD_RATE(CYGNUM_IO_SERIAL_POWERPC_QUICC_SMC_SCC1_BAUD),
224                       CYG_SERIAL_STOP_DEFAULT,
225                       CYG_SERIAL_PARITY_DEFAULT,
226                       CYG_SERIAL_WORD_LENGTH_DEFAULT,
227                       CYG_SERIAL_FLAGS_DEFAULT
228     );
229 #endif
230 static unsigned char quicc_scc1_txbuf[CYGNUM_IO_SERIAL_POWERPC_QUICC_SMC_SCC1_TxNUM*CYGNUM_IO_SERIAL_POWERPC_QUICC_SMC_SCC1_TxSIZE] __attribute__((aligned(HAL_DCACHE_LINE_SIZE)));
231 static unsigned char quicc_scc1_rxbuf[CYGNUM_IO_SERIAL_POWERPC_QUICC_SMC_SCC1_RxNUM*CYGNUM_IO_SERIAL_POWERPC_QUICC_SMC_SCC1_RxSIZE] __attribute__((aligned(HAL_DCACHE_LINE_SIZE)));
232
233 DEVTAB_ENTRY(quicc_smc_serial_io_scc1, 
234              CYGDAT_IO_SERIAL_POWERPC_QUICC_SMC_SCC1_NAME,
235              0,                     // Does not depend on a lower level interface
236              &cyg_io_serial_devio, 
237              quicc_sxx_serial_init, 
238              quicc_sxx_serial_lookup,     // Serial driver may need initializing
239              &quicc_sxx_serial_channel_scc1
240     );
241 #endif //  CYGPKG_IO_SERIAL_POWERPC_QUICC_SMC_SCC1
242
243 #ifdef CYGPKG_IO_SERIAL_POWERPC_QUICC_SMC_SCC2
244 static quicc_sxx_serial_info quicc_sxx_serial_info_scc2 = {
245     QUICC_CPM_SCC2,                     // Channel indicator
246     CYGNUM_HAL_INTERRUPT_CPM_SCC2,      // interrupt
247     _SCC_CHAN
248 };
249 #if CYGNUM_IO_SERIAL_POWERPC_QUICC_SMC_SCC2_BUFSIZE > 0
250 static unsigned char quicc_smc_serial_out_buf_scc2[CYGNUM_IO_SERIAL_POWERPC_QUICC_SMC_SCC2_BUFSIZE];
251 static unsigned char quicc_smc_serial_in_buf_scc2[CYGNUM_IO_SERIAL_POWERPC_QUICC_SMC_SCC2_BUFSIZE];
252
253 static SERIAL_CHANNEL_USING_INTERRUPTS(quicc_sxx_serial_channel_scc2,
254                                        quicc_sxx_serial_funs, 
255                                        quicc_sxx_serial_info_scc2,
256                                        CYG_SERIAL_BAUD_RATE(CYGNUM_IO_SERIAL_POWERPC_QUICC_SMC_SCC2_BAUD),
257                                        CYG_SERIAL_STOP_DEFAULT,
258                                        CYG_SERIAL_PARITY_DEFAULT,
259                                        CYG_SERIAL_WORD_LENGTH_DEFAULT,
260                                        CYG_SERIAL_FLAGS_DEFAULT,
261                                        &quicc_smc_serial_out_buf_scc2[0], sizeof(quicc_smc_serial_out_buf_scc2),
262                                        &quicc_smc_serial_in_buf_scc2[0], sizeof(quicc_smc_serial_in_buf_scc2)
263     );
264 #else
265 static SERIAL_CHANNEL(quicc_sxx_serial_channel_scc2,
266                       quicc_sxx_serial_funs, 
267                       quicc_sxx_serial_info_scc2,
268                       CYG_SERIAL_BAUD_RATE(CYGNUM_IO_SERIAL_POWERPC_QUICC_SMC_SCC2_BAUD),
269                       CYG_SERIAL_STOP_DEFAULT,
270                       CYG_SERIAL_PARITY_DEFAULT,
271                       CYG_SERIAL_WORD_LENGTH_DEFAULT,
272                       CYG_SERIAL_FLAGS_DEFAULT
273     );
274 #endif
275 static unsigned char quicc_scc2_txbuf[CYGNUM_IO_SERIAL_POWERPC_QUICC_SMC_SCC2_TxNUM*CYGNUM_IO_SERIAL_POWERPC_QUICC_SMC_SCC2_TxSIZE] __attribute__((aligned(HAL_DCACHE_LINE_SIZE)));
276 static unsigned char quicc_scc2_rxbuf[CYGNUM_IO_SERIAL_POWERPC_QUICC_SMC_SCC2_RxNUM*CYGNUM_IO_SERIAL_POWERPC_QUICC_SMC_SCC2_RxSIZE] __attribute__((aligned(HAL_DCACHE_LINE_SIZE)));
277
278 DEVTAB_ENTRY(quicc_smc_serial_io_scc2, 
279              CYGDAT_IO_SERIAL_POWERPC_QUICC_SMC_SCC2_NAME,
280              0,                     // Does not depend on a lower level interface
281              &cyg_io_serial_devio, 
282              quicc_sxx_serial_init, 
283              quicc_sxx_serial_lookup,     // Serial driver may need initializing
284              &quicc_sxx_serial_channel_scc2
285     );
286 #endif //  CYGPKG_IO_SERIAL_POWERPC_QUICC_SMC_SCC2
287
288 #ifdef CYGPKG_IO_SERIAL_POWERPC_QUICC_SMC_SCC3
289 static quicc_sxx_serial_info quicc_sxx_serial_info_scc3 = {
290     QUICC_CPM_SCC3,                     // Channel indicator
291     CYGNUM_HAL_INTERRUPT_CPM_SCC3,      // interrupt
292     _SCC_CHAN
293 };
294 #if CYGNUM_IO_SERIAL_POWERPC_QUICC_SMC_SCC3_BUFSIZE > 0
295 static unsigned char quicc_smc_serial_out_buf_scc3[CYGNUM_IO_SERIAL_POWERPC_QUICC_SMC_SCC3_BUFSIZE];
296 static unsigned char quicc_smc_serial_in_buf_scc3[CYGNUM_IO_SERIAL_POWERPC_QUICC_SMC_SCC3_BUFSIZE];
297
298 static SERIAL_CHANNEL_USING_INTERRUPTS(quicc_sxx_serial_channel_scc3,
299                                        quicc_sxx_serial_funs, 
300                                        quicc_sxx_serial_info_scc3,
301                                        CYG_SERIAL_BAUD_RATE(CYGNUM_IO_SERIAL_POWERPC_QUICC_SMC_SCC3_BAUD),
302                                        CYG_SERIAL_STOP_DEFAULT,
303                                        CYG_SERIAL_PARITY_DEFAULT,
304                                        CYG_SERIAL_WORD_LENGTH_DEFAULT,
305                                        CYG_SERIAL_FLAGS_DEFAULT,
306                                        &quicc_smc_serial_out_buf_scc3[0], sizeof(quicc_smc_serial_out_buf_scc3),
307                                        &quicc_smc_serial_in_buf_scc3[0], sizeof(quicc_smc_serial_in_buf_scc3)
308     );
309 #else
310 static SERIAL_CHANNEL(quicc_sxx_serial_channel_scc3,
311                       quicc_sxx_serial_funs, 
312                       quicc_sxx_serial_info_scc3,
313                       CYG_SERIAL_BAUD_RATE(CYGNUM_IO_SERIAL_POWERPC_QUICC_SMC_SCC3_BAUD),
314                       CYG_SERIAL_STOP_DEFAULT,
315                       CYG_SERIAL_PARITY_DEFAULT,
316                       CYG_SERIAL_WORD_LENGTH_DEFAULT,
317                       CYG_SERIAL_FLAGS_DEFAULT
318     );
319 #endif
320 static unsigned char quicc_scc3_txbuf[CYGNUM_IO_SERIAL_POWERPC_QUICC_SMC_SCC3_TxNUM*CYGNUM_IO_SERIAL_POWERPC_QUICC_SMC_SCC3_TxSIZE] __attribute__((aligned(HAL_DCACHE_LINE_SIZE)));
321 static unsigned char quicc_scc3_rxbuf[CYGNUM_IO_SERIAL_POWERPC_QUICC_SMC_SCC3_RxNUM*CYGNUM_IO_SERIAL_POWERPC_QUICC_SMC_SCC3_RxSIZE] __attribute__((aligned(HAL_DCACHE_LINE_SIZE)));
322
323 DEVTAB_ENTRY(quicc_smc_serial_io_scc3, 
324              CYGDAT_IO_SERIAL_POWERPC_QUICC_SMC_SCC3_NAME,
325              0,                     // Does not depend on a lower level interface
326              &cyg_io_serial_devio, 
327              quicc_sxx_serial_init, 
328              quicc_sxx_serial_lookup,     // Serial driver may need initializing
329              &quicc_sxx_serial_channel_scc3
330     );
331 #endif //  CYGPKG_IO_SERIAL_POWERPC_QUICC_SMC_SCC3
332
333 // Internal function to actually configure the hardware to desired baud rate, etc.
334 static bool
335 quicc_smc_serial_config_port(serial_channel *chan, cyg_serial_info_t *new_config, bool init)
336 {
337     quicc_sxx_serial_info *smc_chan = (quicc_sxx_serial_info *)chan->dev_priv;
338     unsigned int baud_divisor = select_baud[new_config->baud];
339     cyg_uint32 _lcr;
340     EPPC *eppc = eppc_base();
341     volatile struct smc_regs *ctl = (volatile struct smc_regs *)smc_chan->ctl;
342
343     if (baud_divisor == 0) return false;
344     // Disable channel during setup
345     ctl->smc_smcmr = QUICC_SMCMR_UART;  // Disabled, UART mode
346     HAL_IO_BARRIER();  // Inforce I/O ordering
347     // Disable port interrupts while changing hardware
348     _lcr = QUICC_SMCMR_CLEN(new_config->word_length + ((new_config->parity == CYGNUM_SERIAL_PARITY_NONE)? 0: 1) + ((new_config->stop == CYGNUM_SERIAL_STOP_2)? 2: 1)) |
349         smc_select_stop_bits[new_config->stop] |
350         smc_select_parity[new_config->parity];
351     // Stop transmitter while changing baud rate
352     eppc->cp_cr = smc_chan->channel | QUICC_SMC_CMD_Go | QUICC_SMC_CMD_StopTx;
353     HAL_IO_BARRIER();  // Inforce I/O ordering
354     // Set baud rate generator
355     *smc_chan->brg = 0x10000 | (UART_BITRATE(baud_divisor)<<1);
356
357     // Enable channel with new configuration
358     ctl->smc_smcmr = QUICC_SMCMR_UART|QUICC_SMCMR_TEN|QUICC_SMCMR_REN|_lcr;
359     HAL_IO_BARRIER();  // Inforce I/O ordering
360     eppc->cp_cr = smc_chan->channel | QUICC_SMC_CMD_Go | QUICC_SMC_CMD_RestartTx;
361     if (new_config != &chan->config) {
362         chan->config = *new_config;
363     }
364     return true;
365 }
366
367 // Function to set up internal tables for device.
368 static void
369 quicc_smc_serial_init_info(quicc_sxx_serial_info *smc_chan,
370                            volatile struct smc_uart_pram *uart_pram,
371                            volatile struct smc_regs *ctl,
372                            int TxBD, int TxNUM, int TxSIZE,
373                            cyg_uint8 *TxBUF,
374                            int RxBD, int RxNUM, int RxSIZE,
375                            cyg_uint8 *RxBUF,
376                            int portBmask,
377                            int port)
378 {
379     EPPC *eppc = eppc_base();
380     struct cp_bufdesc *txbd, *rxbd;
381     int i;
382     
383     smc_chan->pram = (void *)uart_pram;
384     smc_chan->ctl = (void *)ctl;
385
386     // Set up baud rate generator
387     smc_chan->brg = _mpc8xx_allocate_brg(port);
388
389     // Disable channel during setup
390     ctl->smc_smcmr = QUICC_SMCMR_UART;  // Disabled, UART mode
391
392     /*
393      *  Set up the PortB pins for UART operation.
394      *  Set PAR and DIR to allow SMCTXDx and SMRXDx
395      *  (Table 16-39)
396      */
397     eppc->pip_pbpar |= portBmask;
398     eppc->pip_pbdir &= ~portBmask;
399     /*
400      *  SDMA & LCD bus request level 5
401      *  (Section 16.10.2.1)
402      */
403     eppc->dma_sdcr = 1;
404     /*
405      *  Set Rx and Tx function code
406      *  (Section 16.15.4.2)
407      */
408     uart_pram->rfcr = 0x18;
409     uart_pram->tfcr = 0x18;
410     /*
411      *  Set pointers to buffer descriptors.
412      *  (Sections 16.15.4.1, 16.15.7.12, and 16.15.7.13)
413      */
414     uart_pram->rbase = RxBD;
415     uart_pram->tbase = TxBD;
416     /* tx and rx buffer descriptors */
417     txbd = (struct cp_bufdesc *)((char *)eppc + TxBD);
418     rxbd = (struct cp_bufdesc *)((char *)eppc + RxBD);
419     smc_chan->txbd = txbd;
420     smc_chan->tbase = txbd;
421     smc_chan->txsize = TxSIZE;
422     smc_chan->rxbd = rxbd;
423     smc_chan->rbase = rxbd;
424     smc_chan->rxsize = RxSIZE;
425     /* max receive buffer length */
426     uart_pram->mrblr = RxSIZE;
427     /* set max_idle feature - generate interrupt after 4 chars idle period */
428     uart_pram->max_idl = 4;
429     /* no last brk char received */
430     uart_pram->brkln = 0;
431     /* no break condition occurred */
432     uart_pram->brkec = 0;
433     /* 1 break char sent on top XMIT */
434     uart_pram->brkcr = 1;
435     /* setup RX buffer descriptors */
436     for (i = 0;  i < RxNUM;  i++) {
437         rxbd->length = 0;
438         rxbd->buffer = RxBUF;
439         rxbd->ctrl   = QUICC_BD_CTL_Ready | QUICC_BD_CTL_Int;
440         if (i == (RxNUM-1)) rxbd->ctrl |= QUICC_BD_CTL_Wrap;  // Last buffer
441         RxBUF += RxSIZE;
442         rxbd++;
443     }
444     /* setup TX buffer descriptors */
445     for (i = 0;  i < TxNUM;  i++) {
446         txbd->length = 0;
447         txbd->buffer = TxBUF;
448         txbd->ctrl   = 0;
449         if (i == (TxNUM-1)) txbd->ctrl |= QUICC_BD_CTL_Wrap;  // Last buffer
450         TxBUF += TxSIZE;
451         txbd++;
452     }
453     /*
454      *  Reset Rx & Tx params
455      */
456     HAL_IO_BARRIER();  // Inforce I/O ordering
457     eppc->cp_cr = smc_chan->channel | QUICC_SMC_CMD_Go | QUICC_SMC_CMD_InitTxRx;
458     HAL_IO_BARRIER();  // Inforce I/O ordering
459     /*
460      *  Clear any previous events. Enable interrupts.
461      *  (Section 16.15.7.14 and 16.15.7.15)
462      */
463     ctl->smc_smce = 0xFF;
464     ctl->smc_smcm = QUICC_SMCE_BSY|QUICC_SMCE_TX|QUICC_SMCE_RX;
465 }
466
467 // Internal function to actually configure the hardware to desired baud rate, etc.
468 static bool
469 quicc_scc_serial_config_port(serial_channel *chan, cyg_serial_info_t *new_config, bool init)
470 {
471     quicc_sxx_serial_info *scc_chan = (quicc_sxx_serial_info *)chan->dev_priv;
472     unsigned int baud_divisor = select_baud[new_config->baud];
473     EPPC *eppc = eppc_base();
474     volatile struct scc_regs *regs = (volatile struct scc_regs *)scc_chan->ctl;
475
476     if (baud_divisor == 0) return false;
477     // Set baud rate generator
478     *scc_chan->brg = 0x10000 | (UART_BITRATE(baud_divisor)<<1);
479     // Disable channel during setup
480     HAL_IO_BARRIER();  // Inforce I/O ordering
481     regs->scc_gsmr_l = 0;
482     regs->scc_psmr = QUICC_SCC_PSMR_ASYNC | 
483         scc_select_word_length[new_config->word_length - CYGNUM_SERIAL_WORD_LENGTH_5] | 
484         scc_select_stop_bits[new_config->stop] |
485         scc_select_parity[new_config->parity];
486
487     // Enable channel with new configuration
488     regs->scc_gsmr_h = 0x20;          // 8bit FIFO
489     regs->scc_gsmr_l = 0x00028004;    // 16x TxCLK, 16x RxCLK, UART
490
491     /*
492      *  Init Rx & Tx params for SCCX
493      */
494     HAL_IO_BARRIER();  // Inforce I/O ordering
495     eppc->cp_cr = QUICC_CPM_CR_INIT_TXRX | scc_chan->channel | QUICC_CPM_CR_BUSY;
496
497     HAL_IO_BARRIER();  // Inforce I/O ordering
498     regs->scc_gsmr_l |= (QUICC_SCC_GSMR_L_Tx | QUICC_SCC_GSMR_L_Rx);  // Enable Rx, Tx
499     if (new_config != &chan->config) {
500         chan->config = *new_config;
501     }
502     return true;
503 }
504
505 // Function to set up internal tables for device.
506 static void
507 quicc_scc_serial_init_info(quicc_sxx_serial_info *scc_chan,
508                            volatile struct uart_pram *uart_pram,
509                            volatile struct scc_regs *ctl,
510                            int TxBD, int TxNUM, int TxSIZE,
511                            cyg_uint8 *TxBUF,
512                            int RxBD, int RxNUM, int RxSIZE,
513                            cyg_uint8 *RxBUF,
514                            int portAmask, int portBmask, int portCmask,
515                            int port)
516 {
517     EPPC *eppc = eppc_base();
518     struct cp_bufdesc *txbd, *rxbd;
519     int i;
520
521     // Disable channel during setup
522     ctl->scc_gsmr_l = 0;
523     scc_chan->pram = (void *)uart_pram;
524     scc_chan->ctl = (void *)ctl;
525
526     // Set up baud rate generator
527     scc_chan->brg = _mpc8xx_allocate_brg(port);
528
529     /*
530      *  Set up the PortA/B/C pins for UART operation.
531      */
532     eppc->pio_papar |= portAmask;
533     eppc->pio_padir &= ~portAmask;
534     eppc->pio_paodr &= ~portAmask;
535
536     eppc->pio_pcdir &= portCmask;
537     eppc->pio_pcpar &= portCmask;
538     eppc->pio_pcso  |= portCmask;
539
540     eppc->pip_pbpar |= portBmask;
541     eppc->pip_pbdir |= portBmask;
542
543     /*
544      *  SDMA & LCD bus request level 5
545      *  (Section 16.10.2.1)
546      */
547     eppc->dma_sdcr = 1;
548     /*
549      *  Set Rx and Tx function code
550      *  (Section 16.15.4.2)
551      */
552     uart_pram->rfcr = 0x18;
553     uart_pram->tfcr = 0x18;
554     /*
555      *  Set pointers to buffer descriptors.
556      *  (Sections 16.15.4.1, 16.15.7.12, and 16.15.7.13)
557      */
558     uart_pram->rbase = RxBD;
559     uart_pram->tbase = TxBD;
560     /* tx and rx buffer descriptors */
561     txbd = (struct cp_bufdesc *)((char *)eppc + TxBD);
562     rxbd = (struct cp_bufdesc *)((char *)eppc + RxBD);
563     scc_chan->txbd = txbd;
564     scc_chan->tbase = txbd;
565     scc_chan->txsize = TxSIZE;
566     scc_chan->rxbd = rxbd;
567     scc_chan->rbase = rxbd;
568     scc_chan->rxsize = RxSIZE;
569     /* max receive buffer length */
570     uart_pram->mrblr = RxSIZE;
571     /* set max_idle feature - generate interrupt after 4 chars idle period */
572     uart_pram->max_idl = 4;
573     /* no last brk char received */
574     uart_pram->brkln = 0;
575     /* no break condition occurred */
576     uart_pram->brkec = 0;
577     /* 1 break char sent on top XMIT */
578     uart_pram->brkcr = 1;
579     /* character mask */
580     uart_pram->rccm  = 0xC0FF;
581     /* control characters */
582     for (i = 0;  i < 8;  i++) {
583         uart_pram->cc[i] = 0x8000;  // Mark unused
584     }
585     /* setup RX buffer descriptors */
586     for (i = 0;  i < RxNUM;  i++) {
587         rxbd->length = 0;
588         rxbd->buffer = RxBUF;
589         rxbd->ctrl   = QUICC_BD_CTL_Ready | QUICC_BD_CTL_Int;
590         if (i == (RxNUM-1)) rxbd->ctrl |= QUICC_BD_CTL_Wrap;  // Last buffer
591         RxBUF += RxSIZE;
592         rxbd++;
593     }
594     /* setup TX buffer descriptors */
595     for (i = 0;  i < TxNUM;  i++) {
596         txbd->length = 0;
597         txbd->buffer = TxBUF;
598         txbd->ctrl   = 0;
599         if (i == (TxNUM-1)) txbd->ctrl |= QUICC_BD_CTL_Wrap;  // Last buffer
600         TxBUF += TxSIZE;
601         txbd++;
602     }
603     /*
604      *  Reset Rx & Tx params
605      */
606     HAL_IO_BARRIER();  // Inforce I/O ordering
607     eppc->cp_cr = scc_chan->channel | QUICC_SMC_CMD_Go | QUICC_SMC_CMD_InitTxRx;
608     /*
609      *  Clear any previous events. Enable interrupts.
610      *  (Section 16.15.7.14 and 16.15.7.15)
611      */
612     HAL_IO_BARRIER();  // Inforce I/O ordering
613     ctl->scc_scce = 0xFFFF;
614     ctl->scc_sccm = (QUICC_SCCE_BSY | QUICC_SCCE_TX | QUICC_SCCE_RX);
615 }
616
617 // Function to initialize the device.  Called at bootstrap time.
618 static bool 
619 quicc_sxx_serial_init(struct cyg_devtab_entry *tab)
620 {
621     serial_channel *chan = (serial_channel *)tab->priv;
622     quicc_sxx_serial_info *smc_chan = (quicc_sxx_serial_info *)chan->dev_priv;
623     volatile EPPC *eppc = (volatile EPPC *)eppc_base();
624     int TxBD, RxBD;
625     int cache_state;
626
627     HAL_DCACHE_IS_ENABLED(cache_state);
628     HAL_DCACHE_SYNC();
629     HAL_DCACHE_DISABLE();
630 #ifdef CYGDBG_IO_INIT
631     diag_printf("QUICC_SMC SERIAL init - dev: %x.%d = %s\n", smc_chan->channel, smc_chan->int_num, tab->name);
632 #endif
633 #ifdef CYGPKG_IO_SERIAL_POWERPC_QUICC_SMC_SMC1
634     if (chan == &quicc_sxx_serial_channel_smc1) {
635         TxBD = _mpc8xx_allocBd(sizeof(struct cp_bufdesc)*CYGNUM_IO_SERIAL_POWERPC_QUICC_SMC_SMC1_TxNUM);
636         RxBD = _mpc8xx_allocBd(sizeof(struct cp_bufdesc)*CYGNUM_IO_SERIAL_POWERPC_QUICC_SMC_SMC1_RxNUM);
637         quicc_smc_serial_init_info(&quicc_sxx_serial_info_smc1,
638                                    &eppc->pram[2].scc.pothers.smc_modem.psmc.u, // PRAM
639                                    &eppc->smc_regs[0], // Control registers
640                                    TxBD, 
641                                    CYGNUM_IO_SERIAL_POWERPC_QUICC_SMC_SMC1_TxNUM,
642                                    CYGNUM_IO_SERIAL_POWERPC_QUICC_SMC_SMC1_TxSIZE,
643                                    &quicc_smc1_txbuf[0],
644                                    RxBD, 
645                                    CYGNUM_IO_SERIAL_POWERPC_QUICC_SMC_SMC1_RxNUM,
646                                    CYGNUM_IO_SERIAL_POWERPC_QUICC_SMC_SMC1_RxSIZE,
647                                    &quicc_smc1_rxbuf[0],
648                                    0xC0, // PortB mask
649                                    QUICC_CPM_SMC1
650             );
651     }
652 #endif
653 #ifdef CYGPKG_IO_SERIAL_POWERPC_QUICC_SMC_SMC2
654     if (chan == &quicc_sxx_serial_channel_smc2) {
655         TxBD = _mpc8xx_allocBd(sizeof(struct cp_bufdesc)*CYGNUM_IO_SERIAL_POWERPC_QUICC_SMC_SMC2_TxNUM);
656         RxBD = _mpc8xx_allocBd(sizeof(struct cp_bufdesc)*CYGNUM_IO_SERIAL_POWERPC_QUICC_SMC_SMC2_RxNUM);
657         quicc_smc_serial_init_info(&quicc_sxx_serial_info_smc2,
658                                    &eppc->pram[3].scc.pothers.smc_modem.psmc.u, // PRAM
659                                    &eppc->smc_regs[1], // Control registers
660                                    TxBD, 
661                                    CYGNUM_IO_SERIAL_POWERPC_QUICC_SMC_SMC2_TxNUM,
662                                    CYGNUM_IO_SERIAL_POWERPC_QUICC_SMC_SMC2_TxSIZE,
663                                    &quicc_smc2_txbuf[0],
664                                    RxBD, 
665                                    CYGNUM_IO_SERIAL_POWERPC_QUICC_SMC_SMC2_RxNUM,
666                                    CYGNUM_IO_SERIAL_POWERPC_QUICC_SMC_SMC2_RxSIZE,
667                                    &quicc_smc2_rxbuf[0],
668                                    0xC00, // PortB mask
669                                    QUICC_CPM_SMC2
670             );
671     }
672 #endif
673 #ifdef CYGPKG_IO_SERIAL_POWERPC_QUICC_SMC_SCC1
674     if (chan == &quicc_sxx_serial_channel_scc1) {
675         TxBD = _mpc8xx_allocBd(sizeof(struct cp_bufdesc)*CYGNUM_IO_SERIAL_POWERPC_QUICC_SMC_SCC1_TxNUM);
676         RxBD = _mpc8xx_allocBd(sizeof(struct cp_bufdesc)*CYGNUM_IO_SERIAL_POWERPC_QUICC_SMC_SCC1_RxNUM);
677         quicc_scc_serial_init_info(&quicc_sxx_serial_info_scc1,
678                                    &eppc->pram[0].scc.pscc.u, // PRAM
679                                    &eppc->scc_regs[0],        // Control registersn
680                                    TxBD, 
681                                    CYGNUM_IO_SERIAL_POWERPC_QUICC_SMC_SCC1_TxNUM,
682                                    CYGNUM_IO_SERIAL_POWERPC_QUICC_SMC_SCC1_TxSIZE,
683                                    &quicc_scc1_txbuf[0],
684                                    RxBD, 
685                                    CYGNUM_IO_SERIAL_POWERPC_QUICC_SMC_SCC1_RxNUM,
686                                    CYGNUM_IO_SERIAL_POWERPC_QUICC_SMC_SCC1_RxSIZE,
687                                    &quicc_scc1_rxbuf[0],
688                                    0x0003, // PortA mask
689                                    0x1000, // PortB mask
690                                    0x0800, // PortC mask
691                                    QUICC_CPM_SCC1
692             );
693     }
694 #endif
695 #ifdef CYGPKG_IO_SERIAL_POWERPC_QUICC_SMC_SCC2
696     if (chan == &quicc_sxx_serial_channel_scc2) {
697         TxBD = _mpc8xx_allocBd(sizeof(struct cp_bufdesc)*CYGNUM_IO_SERIAL_POWERPC_QUICC_SMC_SCC2_TxNUM);
698         RxBD = _mpc8xx_allocBd(sizeof(struct cp_bufdesc)*CYGNUM_IO_SERIAL_POWERPC_QUICC_SMC_SCC2_RxNUM);
699         quicc_scc_serial_init_info(&quicc_sxx_serial_info_scc2,
700                                    &eppc->pram[1].scc.pscc.u, // PRAM
701                                    &eppc->scc_regs[1],        // Control registersn
702                                    TxBD, 
703                                    CYGNUM_IO_SERIAL_POWERPC_QUICC_SMC_SCC2_TxNUM,
704                                    CYGNUM_IO_SERIAL_POWERPC_QUICC_SMC_SCC2_TxSIZE,
705                                    &quicc_scc2_txbuf[0],
706                                    RxBD, 
707                                    CYGNUM_IO_SERIAL_POWERPC_QUICC_SMC_SCC2_RxNUM,
708                                    CYGNUM_IO_SERIAL_POWERPC_QUICC_SMC_SCC2_RxSIZE,
709                                    &quicc_scc2_rxbuf[0],
710                                    0x000C, // PortA mask
711                                    0x2000, // PortB mask
712                                    0x0C00, // PortC mask
713                                    QUICC_CPM_SCC2
714             );
715     }
716 #endif
717 #ifdef CYGPKG_IO_SERIAL_POWERPC_QUICC_SMC_SCC3
718     if (chan == &quicc_sxx_serial_channel_scc3) {
719         TxBD = _mpc8xx_allocBd(sizeof(struct cp_bufdesc)*CYGNUM_IO_SERIAL_POWERPC_QUICC_SMC_SCC3_TxNUM);
720         RxBD = _mpc8xx_allocBd(sizeof(struct cp_bufdesc)*CYGNUM_IO_SERIAL_POWERPC_QUICC_SMC_SCC3_RxNUM);
721         quicc_scc_serial_init_info(&quicc_sxx_serial_info_scc3,
722                                    &eppc->pram[2].scc.pscc.u, // PRAM
723                                    &eppc->scc_regs[2],        // Control registersn
724                                    TxBD, 
725                                    CYGNUM_IO_SERIAL_POWERPC_QUICC_SMC_SCC3_TxNUM,
726                                    CYGNUM_IO_SERIAL_POWERPC_QUICC_SMC_SCC3_TxSIZE,
727                                    &quicc_scc3_txbuf[0],
728                                    RxBD, 
729                                    CYGNUM_IO_SERIAL_POWERPC_QUICC_SMC_SCC3_RxNUM,
730                                    CYGNUM_IO_SERIAL_POWERPC_QUICC_SMC_SCC3_RxSIZE,
731                                    &quicc_scc3_rxbuf[0],
732 #if defined(CYGHWR_HAL_POWERPC_MPC8XX_850)
733                                    0x0000, // PortA mask
734                                    0x00C0, // PortB mask
735                                    0x0000, // PortC mask
736 #elif defined(CYGHWR_HAL_POWERPC_MPC8XX_852T)
737                                    0x0030, // PortA mask
738                                    0x0000, // PortB mask
739                                    0x0000, // PortC mask
740 #elif defined(CYGHWR_HAL_POWERPC_MPC8XX_823)
741                                    0x0000, // PortA mask
742                                    0x00C0, // PortB mask
743                                    0x0000, // PortC mask
744 #else
745 #error "Cannot route SCC3"
746 #endif
747                                    QUICC_CPM_SCC3
748             );
749     }
750 #endif
751     (chan->callbacks->serial_init)(chan);  // Really only required for interrupt driven devices
752     if (chan->out_cbuf.len != 0) {
753         cyg_drv_interrupt_create(smc_chan->int_num,
754                                  CYGARC_SIU_PRIORITY_HIGH, // Priority - unused (but asserted)
755                                  (cyg_addrword_t)chan,   //  Data item passed to interrupt handler
756                                  quicc_sxx_serial_ISR,
757                                  quicc_sxx_serial_DSR,
758                                  &smc_chan->serial_interrupt_handle,
759                                  &smc_chan->serial_interrupt);
760         cyg_drv_interrupt_attach(smc_chan->serial_interrupt_handle);
761         cyg_drv_interrupt_unmask(smc_chan->int_num);
762     }
763     if (smc_chan->type == _SMC_CHAN) {
764         quicc_smc_serial_config_port(chan, &chan->config, true);
765     } else {
766         quicc_scc_serial_config_port(chan, &chan->config, true);
767     }
768     if (cache_state)
769         HAL_DCACHE_ENABLE();
770     return true;
771 }
772
773 // This routine is called when the device is "looked" up (i.e. attached)
774 static Cyg_ErrNo 
775 quicc_sxx_serial_lookup(struct cyg_devtab_entry **tab, 
776                   struct cyg_devtab_entry *sub_tab,
777                   const char *name)
778 {
779     serial_channel *chan = (serial_channel *)(*tab)->priv;
780     (chan->callbacks->serial_init)(chan);  // Really only required for interrupt driven devices
781     return ENOERR;
782 }
783
784 // Force the current transmit buffer to be sent
785 static void
786 quicc_sxx_serial_flush(quicc_sxx_serial_info *smc_chan)
787 {
788     volatile struct cp_bufdesc *txbd = smc_chan->txbd;
789     int cache_state;
790                                        
791     HAL_DCACHE_IS_ENABLED(cache_state);
792     if (cache_state) {
793       HAL_DCACHE_FLUSH(txbd->buffer, smc_chan->txsize);
794     }
795     if ((txbd->length > 0) && 
796         ((txbd->ctrl & (QUICC_BD_CTL_Ready|QUICC_BD_CTL_Int)) == 0)) {
797         txbd->ctrl |= QUICC_BD_CTL_Ready|QUICC_BD_CTL_Int;  // Signal buffer ready
798         if (txbd->ctrl & QUICC_BD_CTL_Wrap) {
799             txbd = smc_chan->tbase;
800         } else {
801             txbd++;
802         }
803         smc_chan->txbd = txbd;
804     }
805 }
806
807 // Send a character to the device output buffer.
808 // Return 'true' if character is sent to device
809 static bool
810 quicc_sxx_serial_putc(serial_channel *chan, unsigned char c)
811 {
812     quicc_sxx_serial_info *smc_chan = (quicc_sxx_serial_info *)chan->dev_priv;
813     volatile struct cp_bufdesc *txbd, *txfirst;
814     volatile struct smc_uart_pram *pram = (volatile struct smc_uart_pram *)smc_chan->pram;
815     EPPC *eppc = eppc_base();
816     bool res;
817
818     cyg_drv_dsr_lock();  // Avoid race condition testing pointers
819     txbd = (struct cp_bufdesc *)((char *)eppc + pram->tbptr);
820     txfirst = txbd;
821     // Scan for a non-busy buffer
822     while (txbd->ctrl & QUICC_BD_CTL_Ready) {
823         // This buffer is busy, move to next one
824         if (txbd->ctrl & QUICC_BD_CTL_Wrap) {
825             txbd = smc_chan->tbase;
826         } else {
827             txbd++;
828         }
829         if (txbd == txfirst) break;  // Went all the way around
830     }
831     smc_chan->txbd = txbd;
832     if ((txbd->ctrl & (QUICC_BD_CTL_Ready|QUICC_BD_CTL_Int)) == 0) {
833         // Transmit buffer is not full/busy
834         txbd->buffer[txbd->length++] = c;
835         if (txbd->length == smc_chan->txsize) {
836             // This buffer is now full, tell SMC to start processing it
837             quicc_sxx_serial_flush(smc_chan);
838         }
839         res = true;
840     } else {
841         // No space
842         res = false;
843     }
844     cyg_drv_dsr_unlock();
845     return res;
846 }
847
848 // Fetch a character from the device input buffer, waiting if necessary
849 static unsigned char 
850 quicc_sxx_serial_getc(serial_channel *chan)
851 {
852     unsigned char c;
853     quicc_sxx_serial_info *smc_chan = (quicc_sxx_serial_info *)chan->dev_priv;
854     volatile struct cp_bufdesc *rxbd = smc_chan->rxbd;
855
856     while ((rxbd->ctrl & QUICC_BD_CTL_Ready) != 0) ;
857     c = rxbd->buffer[0];
858     rxbd->length = smc_chan->rxsize;
859     rxbd->ctrl |= QUICC_BD_CTL_Ready;
860     if (rxbd->ctrl & QUICC_BD_CTL_Wrap) {
861         rxbd = smc_chan->rbase;
862     } else {
863         rxbd++;
864     }
865     smc_chan->rxbd = (struct cp_bufdesc *)rxbd;
866     return c;
867 }
868
869 // Set up the device characteristics; baud rate, etc.
870 static Cyg_ErrNo
871 quicc_sxx_serial_set_config(serial_channel *chan, cyg_uint32 key,
872                             const void *xbuf, cyg_uint32 *len)
873 {
874     int res;
875
876     switch (key) {
877     case CYG_IO_SET_CONFIG_SERIAL_INFO:
878     {
879         // FIXME - The documentation says that you can't change the baud rate
880         // again until at least two BRG input clocks have occurred.
881         cyg_serial_info_t *config = (cyg_serial_info_t *)xbuf;
882         quicc_sxx_serial_info *smc_chan = (quicc_sxx_serial_info *)chan->dev_priv;
883         if ( *len < sizeof(cyg_serial_info_t) ) {
884             return -EINVAL;
885         }
886         *len = sizeof(cyg_serial_info_t);
887         if (smc_chan->type == _SMC_CHAN) {
888             res = quicc_smc_serial_config_port(chan, config, true);
889         } else {
890             res = quicc_scc_serial_config_port(chan, config, true);
891         }
892         if ( true != res )
893             return -EINVAL;
894     }
895     break;
896     default:
897         return -EINVAL;
898     }
899     return ENOERR;
900 }
901
902 // Enable the transmitter (interrupt) on the device
903 static void
904 quicc_sxx_serial_start_xmit(serial_channel *chan)
905 {
906     quicc_sxx_serial_info *smc_chan = (quicc_sxx_serial_info *)chan->dev_priv;
907     cyg_drv_dsr_lock();
908     if (smc_chan->txbd->length == 0) {
909         // See if there is anything to put in this buffer, just to get it going
910         (chan->callbacks->xmt_char)(chan);
911     }
912     if (smc_chan->txbd->length != 0) {
913         // Make sure it gets started
914         quicc_sxx_serial_flush(smc_chan);
915     }
916     cyg_drv_dsr_unlock();
917 }
918
919 // Disable the transmitter on the device
920 static void 
921 quicc_sxx_serial_stop_xmit(serial_channel *chan)
922 {
923     quicc_sxx_serial_info *smc_chan = (quicc_sxx_serial_info *)chan->dev_priv;
924     // If anything is in the last buffer, need to get it started
925     if (smc_chan->txbd->length != 0) {
926         quicc_sxx_serial_flush(smc_chan);
927     }
928 }
929
930 // Serial I/O - low level interrupt handler (ISR)
931 static cyg_uint32 
932 quicc_sxx_serial_ISR(cyg_vector_t vector, cyg_addrword_t data)
933 {
934     serial_channel *chan = (serial_channel *)data;
935     quicc_sxx_serial_info *smc_chan = (quicc_sxx_serial_info *)chan->dev_priv;
936     cyg_drv_interrupt_mask(smc_chan->int_num);
937     return (CYG_ISR_HANDLED|CYG_ISR_CALL_DSR);  // Cause DSR to be run
938 }
939
940 // Serial I/O - high level interrupt handler (DSR)
941 static void
942 quicc_smc_serial_DSR(serial_channel *chan)
943 {
944     quicc_sxx_serial_info *smc_chan = (quicc_sxx_serial_info *)chan->dev_priv;
945     volatile struct smc_regs *ctl = (volatile struct smc_regs *)smc_chan->ctl;
946     volatile struct cp_bufdesc *txbd;
947     volatile struct cp_bufdesc *rxbd = smc_chan->rxbd;
948     volatile struct smc_uart_pram *pram = (volatile struct smc_uart_pram *)smc_chan->pram;
949     struct cp_bufdesc *rxlast;
950     int i, cache_state;
951
952     if (ctl->smc_smce & QUICC_SMCE_TX) {
953         // Transmit interrupt
954         ctl->smc_smce = QUICC_SMCE_TX;  // Reset interrupt state;
955         txbd = smc_chan->tbase;  // First buffer
956         while (true) {
957             if ((txbd->ctrl & (QUICC_BD_CTL_Ready|QUICC_BD_CTL_Int)) == QUICC_BD_CTL_Int) {
958                 txbd->length = 0;
959                 txbd->ctrl &= ~QUICC_BD_CTL_Int;  // Reset interrupt bit
960             }
961             if (txbd->ctrl & QUICC_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->smc_smce & QUICC_SMCE_RX) {
971         // Receive interrupt
972         ctl->smc_smce = QUICC_SMCE_RX;  // Reset interrupt state;
973         rxlast = (struct cp_bufdesc *) ((char *)eppc_base() + pram->rbptr);
974         while (rxbd != rxlast) {
975             if ((rxbd->ctrl & QUICC_BD_CTL_Ready) == 0) {
976                 if((rxbd->ctrl & (QUICC_BD_CTL_Frame | QUICC_BD_CTL_Parity)) == 0) {
977                     for (i = 0;  i < rxbd->length;  i++) {
978                         (chan->callbacks->rcv_char)(chan, rxbd->buffer[i]);
979                     }
980                 } else {
981                     // is this necessary?
982                     rxbd->ctrl &= QUICC_BD_CTL_MASK;
983                     // should we report the error?
984                 }
985                 // Note: the MBX860 does not seem to snoop/invalidate the data cache properly!
986                 HAL_DCACHE_IS_ENABLED(cache_state);
987                 if (cache_state) {
988                     HAL_DCACHE_INVALIDATE(rxbd->buffer, smc_chan->rxsize);  // Make sure no stale data
989                 }
990                 rxbd->length = 0;
991                 rxbd->ctrl |= QUICC_BD_CTL_Ready;
992             }
993             if (rxbd->ctrl & QUICC_BD_CTL_Wrap) {
994                 rxbd = smc_chan->rbase;
995             } else {
996                 rxbd++;
997             }
998         }
999         smc_chan->rxbd = (struct cp_bufdesc *)rxbd;
1000     }
1001     if (ctl->smc_smce & QUICC_SMCE_BSY) {
1002         ctl->smc_smce = QUICC_SMCE_BSY;  // Reset interrupt state;
1003     }
1004     cyg_drv_interrupt_acknowledge(smc_chan->int_num);
1005     cyg_drv_interrupt_unmask(smc_chan->int_num);
1006 }
1007
1008 static void
1009 quicc_scc_serial_DSR(serial_channel *chan)
1010 {
1011     quicc_sxx_serial_info *smc_chan = (quicc_sxx_serial_info *)chan->dev_priv;
1012     volatile struct scc_regs *ctl = (volatile struct scc_regs *)smc_chan->ctl;
1013     volatile struct cp_bufdesc *txbd;
1014     volatile struct cp_bufdesc *rxbd = smc_chan->rxbd;
1015     volatile struct uart_pram *pram = (volatile struct uart_pram *)smc_chan->pram;
1016     struct cp_bufdesc *rxlast;
1017     int i, cache_state;
1018
1019     if (ctl->scc_scce & QUICC_SCCE_TX) {
1020         // Transmit interrupt
1021         ctl->scc_scce = QUICC_SCCE_TX;  // Reset interrupt state;
1022         txbd = smc_chan->tbase;  // First buffer
1023         while (true) {
1024             if ((txbd->ctrl & (QUICC_BD_CTL_Ready|QUICC_BD_CTL_Int)) == QUICC_BD_CTL_Int) {
1025                 txbd->length = 0;
1026                 txbd->ctrl &= ~QUICC_BD_CTL_Int;  // Reset interrupt bit
1027             }
1028             if (txbd->ctrl & QUICC_BD_CTL_Wrap) {
1029                 txbd = smc_chan->tbase;
1030                 break;
1031             } else {
1032                 txbd++;
1033             }
1034         }
1035         (chan->callbacks->xmt_char)(chan);
1036     }
1037     while (ctl->scc_scce & QUICC_SCCE_RX) {
1038         // Receive interrupt
1039         ctl->scc_scce = QUICC_SCCE_RX;  // Reset interrupt state;
1040         rxlast = (struct cp_bufdesc *) ((char *)eppc_base() + pram->rbptr);
1041         while (rxbd != rxlast) {
1042             if ((rxbd->ctrl & QUICC_BD_CTL_Ready) == 0) {
1043                 if((rxbd->ctrl & (QUICC_BD_CTL_Frame | QUICC_BD_CTL_Parity)) == 0) {
1044                     for (i = 0;  i < rxbd->length;  i++) {
1045                         (chan->callbacks->rcv_char)(chan, rxbd->buffer[i]);
1046                     }
1047                 } else {
1048                     // is this necessary?
1049                     rxbd->ctrl &= QUICC_BD_CTL_MASK;
1050                     // should we report the error?
1051                 }
1052                 // Note: the MBX860 does not seem to snoop/invalidate the data cache properly!
1053                 HAL_DCACHE_IS_ENABLED(cache_state);
1054                 if (cache_state) {
1055                     HAL_DCACHE_INVALIDATE(rxbd->buffer, smc_chan->rxsize);  // Make sure no stale data
1056                 }
1057                 rxbd->length = 0;
1058                 rxbd->ctrl |= QUICC_BD_CTL_Ready;
1059             }
1060             if (rxbd->ctrl & QUICC_BD_CTL_Wrap) {
1061                 rxbd = smc_chan->rbase;
1062             } else {
1063                 rxbd++;
1064             }
1065         }
1066         smc_chan->rxbd = (struct cp_bufdesc *)rxbd;
1067     }
1068     if (ctl->scc_scce & QUICC_SCCE_BSY) {
1069         ctl->scc_scce = QUICC_SCCE_BSY;  // Reset interrupt state;
1070     }
1071     cyg_drv_interrupt_acknowledge(smc_chan->int_num);
1072     cyg_drv_interrupt_unmask(smc_chan->int_num);
1073 }
1074
1075 static void       
1076 quicc_sxx_serial_DSR(cyg_vector_t vector, cyg_ucount32 count, cyg_addrword_t data)
1077 {
1078     serial_channel *chan = (serial_channel *)data;
1079     quicc_sxx_serial_info *smc_chan = (quicc_sxx_serial_info *)chan->dev_priv;
1080
1081     if (smc_chan->type == _SMC_CHAN) {
1082         quicc_smc_serial_DSR(chan);
1083     } else {
1084         quicc_scc_serial_DSR(chan);
1085     }
1086 }
1087
1088 void
1089 show_rxbd(int dump_all)
1090 {
1091 #ifdef CYGDBG_DIAG_BUF
1092     EPPC *eppc = eppc_base();
1093     struct smc_uart_pram *pram = &eppc->pram[2].scc.pothers.smc_modem.psmc.u;
1094     struct cp_bufdesc *rxbd = (struct cp_bufdesc *)((char *)eppc+pram->rbase);
1095     int _enable = enable_diag_uart;
1096     enable_diag_uart = 0;
1097 #if 1
1098     diag_printf("SMC Mask: %x, Events: %x, Rbase: %x, Rbptr: %x\n", 
1099                 eppc->smc_regs[0].smc_smcm, eppc->smc_regs[0].smc_smce,
1100                 pram->rbase, pram->rbptr);
1101     while (true) {
1102         diag_printf("Rx BD: %x, ctl: %x, length: %d\n", rxbd, rxbd->ctrl, rxbd->length);
1103         if (rxbd->ctrl & QUICC_BD_CTL_Wrap) break;
1104         rxbd++;
1105     }
1106 #endif
1107     enable_diag_uart = _enable;
1108     if (dump_all) dump_diag_buf();
1109 #endif // CYGDBG_DIAG_BUF
1110 }
1111 #endif // CYGPKG_IO_SERIAL_POWERPC_QUICC_SMC
1112
1113 // ------------------------------------------------------------------------
1114 // EOF powerpc/quicc_smc_serial.c