1 //==========================================================================
5 // Freescale sSCI Serial I/O Interface Module (interrupt driven)
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) 2996 eCosCentric Ltd
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.
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
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.
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.
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 //####ECOSGPLCOPYRIGHTEND####
38 //==========================================================================
39 //#####DESCRIPTIONBEGIN####
41 // Author(s): Ilija Koco <ilijak@siva.com.mk>
44 // Purpose: eSCI Serial I/O module (interrupt driven version)
48 //####DESCRIPTIONEND####
49 //==========================================================================
50 #include <pkgconf/io_serial.h>
51 #include <pkgconf/io.h>
53 #include <cyg/io/io.h>
54 #include <cyg/hal/hal_intr.h>
55 #include <cyg/hal/hal_arbiter.h>
56 #include <cyg/hal/var_io.h>
57 #include <cyg/io/devtab.h>
58 #include <cyg/infra/diag.h>
59 #include <cyg/io/serial.h>
60 #include <cyg/devs/ser_esci.h>
62 // Only build this driver for if ESCI is needed.
63 #ifdef CYGPKG_IO_SERIAL_FREESCALE_ESCI
65 typedef struct esci_serial_info {
66 CYG_ADDRWORD esci_base; // Base address of the esci port
67 CYG_WORD interrupt_num; // INTC interrupt vector
68 cyg_priority_t interrupt_priority; // INTC interupt priority
69 cyg_interrupt interrupt_obj; // Interrupt object
70 cyg_handle_t interrupt_handle; // Interrupt handle
73 static bool esci_serial_init(struct cyg_devtab_entry * tab);
74 static bool esci_serial_putc(serial_channel * chan, unsigned char c);
75 static Cyg_ErrNo esci_serial_lookup(struct cyg_devtab_entry ** tab,
76 struct cyg_devtab_entry * sub_tab,
78 static unsigned char esci_serial_getc(serial_channel *chan);
79 static Cyg_ErrNo esci_serial_set_config(serial_channel *chan, cyg_uint32 key,
80 const void *xbuf, cyg_uint32 *len);
81 static void esci_serial_start_xmit(serial_channel *chan);
82 static void esci_serial_stop_xmit(serial_channel *chan);
85 static cyg_uint32 esci_serial_ISR(cyg_vector_t vector, cyg_addrword_t data);
86 static void esci_serial_DSR(cyg_vector_t vector, cyg_ucount32 count,
89 static SERIAL_FUNS(esci_serial_funs,
92 esci_serial_set_config,
93 esci_serial_start_xmit,
94 esci_serial_stop_xmit);
96 // Available baud rates
97 static unsigned short select_baud[] = {
99 0, // 50 bps unsupported
100 0, // 75 bps unsupported
101 0, // 110 bps unsupported
102 0, // 134_5 bps unsupported
103 0, // 150 bps unsupported
104 0, // 200 bps unsupported
105 FREESCALE_ESCI_BAUD(300), // 300 bps
106 FREESCALE_ESCI_BAUD(600), // 600 bps
107 FREESCALE_ESCI_BAUD(1200), // 1200 bps
108 0, // 1800 bps unsupported
109 FREESCALE_ESCI_BAUD(2400), // 2400 bps
110 0, // 3600 bps unsupported
111 FREESCALE_ESCI_BAUD(4800), // 4800 bps
112 0, // 7200 bps unsupported
113 FREESCALE_ESCI_BAUD(9600), // 9600 bps
114 FREESCALE_ESCI_BAUD(14400), // 14400 bps
115 FREESCALE_ESCI_BAUD(19200), // 19200 bps
116 FREESCALE_ESCI_BAUD(38400), // 38400 bps
117 FREESCALE_ESCI_BAUD(57600), // 57600 bps
118 FREESCALE_ESCI_BAUD(115200), // 115200 bps
119 0 // 230400 bps unsupported
122 #if defined CYGPKG_IO_SERIAL_FREESCALE_ESCI_A
123 static esci_serial_info esci_serial_info0 = {
124 esci_base : CYGADDR_IO_SERIAL_FREESCALE_ESCI_A_BASE,
125 interrupt_num : CYGNUM_IO_SERIAL_FREESCALE_ESCI_A_INT_VECTOR,
126 interrupt_priority : CYGNUM_IO_SERIAL_FREESCALE_ESCI_A_INT_PRIORITY
128 #if CYGNUM_IO_SERIAL_FREESCALE_ESCI_A_BUFSIZE > 0
130 esci_serial_out_buf0[CYGNUM_IO_SERIAL_FREESCALE_ESCI_A_BUFSIZE];
132 esci_serial_in_buf0[CYGNUM_IO_SERIAL_FREESCALE_ESCI_A_BUFSIZE];
135 SERIAL_CHANNEL_USING_INTERRUPTS(
136 esci_serial_channel0,
139 CYG_SERIAL_BAUD_RATE(CYGNUM_IO_SERIAL_FREESCALE_ESCI_A_BAUD),
140 CYG_SERIAL_STOP_DEFAULT,
141 CYG_SERIAL_PARITY_DEFAULT,
142 CYG_SERIAL_WORD_LENGTH_DEFAULT,
143 CYG_SERIAL_FLAGS_DEFAULT,
144 &esci_serial_out_buf0[0],
145 sizeof(esci_serial_out_buf0),
146 &esci_serial_in_buf0[0],
147 sizeof(esci_serial_in_buf0));
150 SERIAL_CHANNEL(esci_serial_channel0,
153 CYG_SERIAL_BAUD_RATE(CYGNUM_IO_SERIAL_FREESCALE_ESCI_A_BAUD),
154 CYG_SERIAL_STOP_DEFAULT,
155 CYG_SERIAL_PARITY_DEFAULT,
156 CYG_SERIAL_WORD_LENGTH_DEFAULT,
157 CYG_SERIAL_FLAGS_DEFAULT);
159 DEVTAB_ENTRY(esci_serial_io0,
160 CYGDAT_IO_SERIAL_FREESCALE_ESCI_A_NAME,
161 0, // does not depend on a lower level device driver
162 &cyg_io_serial_devio,
165 &esci_serial_channel0);
166 #endif // ifdef CYGPKG_IO_SERIAL_FREESCALE_ESCI_A
168 #if defined CYGPKG_IO_SERIAL_FREESCALE_ESCI_B
169 static esci_serial_info esci_serial_info1 = {
170 esci_base : CYGADDR_IO_SERIAL_FREESCALE_ESCI_B_BASE,
171 interrupt_num : CYGNUM_IO_SERIAL_FREESCALE_ESCI_B_INT_VECTOR,
172 interrupt_priority : CYGNUM_IO_SERIAL_FREESCALE_ESCI_B_INT_PRIORITY
174 #if CYGNUM_IO_SERIAL_FREESCALE_ESCI_B_BUFSIZE > 0
176 esci_serial_out_buf1[CYGNUM_IO_SERIAL_FREESCALE_ESCI_B_BUFSIZE];
178 esci_serial_in_buf1[CYGNUM_IO_SERIAL_FREESCALE_ESCI_B_BUFSIZE];
181 SERIAL_CHANNEL_USING_INTERRUPTS(esci_serial_channel1,
184 CYG_SERIAL_BAUD_RATE(CYGNUM_IO_SERIAL_FREESCALE_ESCI_B_BAUD),
185 CYG_SERIAL_STOP_DEFAULT,
186 CYG_SERIAL_PARITY_DEFAULT,
187 CYG_SERIAL_WORD_LENGTH_DEFAULT,
188 CYG_SERIAL_FLAGS_DEFAULT,
189 &esci_serial_out_buf1[0],
190 sizeof(esci_serial_out_buf1),
191 &esci_serial_in_buf1[0],
192 sizeof(esci_serial_in_buf1));
195 SERIAL_CHANNEL(esci_serial_channel1,
198 CYG_SERIAL_BAUD_RATE(CYGNUM_IO_SERIAL_FREESCALE_ESCI_B_BAUD),
199 CYG_SERIAL_STOP_DEFAULT,
200 CYG_SERIAL_PARITY_DEFAULT,
201 CYG_SERIAL_WORD_LENGTH_DEFAULT,
202 CYG_SERIAL_FLAGS_DEFAULT);
204 DEVTAB_ENTRY(esci_serial_io1,
205 CYGDAT_IO_SERIAL_FREESCALE_ESCI_B_NAME,
206 0, // does not depend on a lower level device driver
207 &cyg_io_serial_devio,
210 &esci_serial_channel1);
211 #endif // ifdef CYGPKG_IO_SERIAL_FREESCALE_ESCI_B
213 #if defined CYGPKG_IO_SERIAL_FREESCALE_ESCI_C
214 static esci_serial_info esci_serial_info2 = {
215 esci_base : CYGADDR_IO_SERIAL_FREESCALE_ESCI_C_BASE,
216 interrupt_num : CYGNUM_IO_SERIAL_FREESCALE_ESCI_C_INT_VECTOR,
217 interrupt_priority : CYGNUM_IO_SERIAL_FREESCALE_ESCI_C_INT_PRIORITY
219 #if CYGNUM_IO_SERIAL_FREESCALE_ESCI_C_BUFSIZE > 0
221 esci_serial_out_buf2[CYGNUM_IO_SERIAL_FREESCALE_ESCI_C_BUFSIZE];
223 esci_serial_in_buf2[CYGNUM_IO_SERIAL_FREESCALE_ESCI_C_BUFSIZE];
226 SERIAL_CHANNEL_USING_INTERRUPTS(esci_serial_channel2,
229 CYG_SERIAL_BAUD_RATE(CYGNUM_IO_SERIAL_FREESCALE_ESCI_C_BAUD),
230 CYG_SERIAL_STOP_DEFAULT,
231 CYG_SERIAL_PARITY_DEFAULT,
232 CYG_SERIAL_WORD_LENGTH_DEFAULT,
233 CYG_SERIAL_FLAGS_DEFAULT,
234 &esci_serial_out_buf2[0],
235 sizeof(esci_serial_out_buf2),
236 &esci_serial_in_buf2[0],
237 sizeof(esci_serial_in_buf2));
240 SERIAL_CHANNEL(esci_serial_channel2,
243 CYG_SERIAL_BAUD_RATE(CYGNUM_IO_SERIAL_FREESCALE_ESCI_C_BAUD),
244 CYG_SERIAL_STOP_DEFAULT,
245 CYG_SERIAL_PARITY_DEFAULT,
246 CYG_SERIAL_WORD_LENGTH_DEFAULT,
247 CYG_SERIAL_FLAGS_DEFAULT);
249 DEVTAB_ENTRY(esci_serial_io2,
250 CYGDAT_IO_SERIAL_FREESCALE_ESCI_C_NAME,
251 0, // does not depend on a lower level device driver
252 &cyg_io_serial_devio,
255 &esci_serial_channel2);
256 #endif // ifdef CYGPKG_IO_SERIAL_FREESCALE_ESCI_C
259 #if defined CYGPKG_IO_SERIAL_FREESCALE_ESCI_D
260 static esci_serial_info esci_serial_info3 = {
261 esci_base : CYGADDR_IO_SERIAL_FREESCALE_ESCI_D_BASE,
262 interrupt_num : CYGNUM_IO_SERIAL_FREESCALE_ESCI_D_INT_VECTOR,
263 interrupt_priority : CYGNUM_IO_SERIAL_FREESCALE_ESCI_D_INT_PRIORITY
265 #if CYGNUM_IO_SERIAL_FREESCALE_ESCI_D_BUFSIZE > 0
267 esci_serial_out_buf3[CYGNUM_IO_SERIAL_FREESCALE_ESCI_D_BUFSIZE];
269 esci_serial_in_buf3[CYGNUM_IO_SERIAL_FREESCALE_ESCI_D_BUFSIZE];
272 SERIAL_CHANNEL_USING_INTERRUPTS(esci_serial_channel3,
275 CYG_SERIAL_BAUD_RATE(CYGNUM_IO_SERIAL_FREESCALE_ESCI_D_BAUD),
276 CYG_SERIAL_STOP_DEFAULT,
277 CYG_SERIAL_PARITY_DEFAULT,
278 CYG_SERIAL_WORD_LENGTH_DEFAULT,
279 CYG_SERIAL_FLAGS_DEFAULT,
280 &esci_serial_out_buf3[0],
281 sizeof(esci_serial_out_buf3),
282 &esci_serial_in_buf3[0],
283 sizeof(esci_serial_in_buf3));
286 SERIAL_CHANNEL(esci_serial_channel3,
289 CYG_SERIAL_BAUD_RATE(CYGNUM_IO_SERIAL_FREESCALE_ESCI_D_BAUD),
290 CYG_SERIAL_STOP_DEFAULT,
291 CYG_SERIAL_PARITY_DEFAULT,
292 CYG_SERIAL_WORD_LENGTH_DEFAULT,
293 CYG_SERIAL_FLAGS_DEFAULT);
295 DEVTAB_ENTRY(esci_serial_io3,
296 CYGDAT_IO_SERIAL_FREESCALE_ESCI_D_NAME,
297 0, // does not depend on a lower level device driver
298 &cyg_io_serial_devio,
301 &esci_serial_channel3);
302 #endif // ifdef CYGPKG_IO_SERIAL_FREESCALE_ESCI_D
304 //----------------------------------------------------------------------------
305 // Internal function to actually configure the hardware to desired
307 //----------------------------------------------------------------------------
309 esci_serial_config_port(serial_channel * chan, cyg_serial_info_t * new_config,
312 esci_serial_info * esci_chan = (esci_serial_info *)(chan->dev_priv);
313 cyg_addrword_t esci_base = esci_chan->esci_base;
314 cyg_uint16 baud_rate = ((new_config->baud >= 0) &&
315 (new_config->baud < (sizeof(select_baud)/
316 sizeof(select_baud[0]))))
317 ? select_baud[new_config->baud] : 0;
319 cyg_uint16 esci_cr12=0, esci_cr12_old;
321 HAL_WRITE_UINT8(FREESCALE_ESCI_CR3(esci_base), 0);
322 HAL_WRITE_UINT16(FREESCALE_ESCI_LINCTRL(esci_base), 0);
323 HAL_WRITE_UINT16(FREESCALE_ESCI_CR12(esci_base), 0);
325 if(!baud_rate) return false; // Invalid baud rate selected
327 switch(new_config->word_length){
329 default: return false;
332 switch(new_config->parity){
333 case CYGNUM_SERIAL_PARITY_ODD:
334 esci_cr12 |= FREESCALE_ESCI_CR12_PT;
335 case CYGNUM_SERIAL_PARITY_EVEN:
336 esci_cr12 |= FREESCALE_ESCI_CR12_PE;
337 case CYGNUM_SERIAL_PARITY_NONE:
339 default: return false;
342 if(new_config->stop!=CYGNUM_SERIAL_STOP_1) return false;
345 esci_cr12 |= FREESCALE_ESCI_CR12_TE | FREESCALE_ESCI_CR12_RE;
347 if(init){ // Enable the receiver interrupt
348 esci_cr12 |= FREESCALE_ESCI_CR12_RIE;
349 }else{ // Restore the old interrupt state
350 HAL_READ_UINT16(FREESCALE_ESCI_CR12(esci_base), esci_cr12_old);
351 esci_cr12 |= (esci_cr12_old & FREESCALE_ESCI_CR12_RIE);
353 HAL_WRITE_UINT16(FREESCALE_ESCI_CR12(esci_base), esci_cr12);
355 if(new_config != &chan->config)
356 chan->config = *new_config;
361 //--------------------------------------------------------------
362 // Function to initialize the device. Called at bootstrap time.
363 //--------------------------------------------------------------
365 esci_serial_init(struct cyg_devtab_entry * tab)
367 serial_channel * chan = (serial_channel *)tab->priv;
368 esci_serial_info * esci_chan = (esci_serial_info *)chan->dev_priv;
370 // Really only required for interrupt driven devices
371 (chan->callbacks->serial_init)(chan);
372 if(chan->out_cbuf.len != 0){
373 cyg_drv_interrupt_create(esci_chan->interrupt_num,
374 esci_chan->interrupt_priority,
375 // Data item passed to interrupt handler
376 (cyg_addrword_t)chan,
379 &esci_chan->interrupt_handle,
380 &esci_chan->interrupt_obj);
382 cyg_drv_interrupt_attach(esci_chan->interrupt_handle);
383 cyg_drv_interrupt_unmask(esci_chan->interrupt_num);
385 return esci_serial_config_port(chan, &chan->config, true);
388 //----------------------------------------------------------------------
389 // This routine is called when the device is "looked" up (i.e. attached)
390 //----------------------------------------------------------------------
392 esci_serial_lookup(struct cyg_devtab_entry ** tab,
393 struct cyg_devtab_entry * sub_tab, const char * name)
395 serial_channel * chan = (serial_channel *)(*tab)->priv;
396 // Really only required for interrupt driven devices
397 (chan->callbacks->serial_init)(chan);
402 //-----------------------------------------------------------------
403 // Send a character to Tx
404 //-----------------------------------------------------------------
406 esci_serial_putc(serial_channel * chan, unsigned char ch_out)
408 esci_serial_info * esci_chan = (esci_serial_info *)chan->dev_priv;
409 cyg_addrword_t esci_base = esci_chan->esci_base;
412 HAL_READ_UINT16(FREESCALE_ESCI_SR(esci_base), esci_sr);
413 if(esci_sr & FREESCALE_ESCI_SR_TDRE){
414 HAL_WRITE_UINT16(FREESCALE_ESCI_SR(esci_base), FREESCALE_ESCI_SR_TDRE);
415 HAL_WRITE_UINT8(FREESCALE_ESCI_DRL(esci_base), ch_out);
421 //---------------------------------------------------------------------
422 // Fetch a character Rx (for polled operation only)
423 //---------------------------------------------------------------------
425 esci_serial_getc(serial_channel * chan)
428 esci_serial_info * esci_chan = (esci_serial_info *)chan->dev_priv;
429 cyg_addrword_t esci_base = esci_chan->esci_base;
434 HAL_READ_UINT16(FREESCALE_ESCI_SR(esci_base), esci_sr);
435 }while(esci_sr & FREESCALE_ESCI_SR_RDRF);
437 HAL_READ_UINT8(FREESCALE_ESCI_DRL(esci_base), ch_in);
438 HAL_WRITE_UINT16(FREESCALE_ESCI_SR(esci_base), FREESCALE_ESCI_SR_RDRF);
443 //---------------------------------------------------
444 // Set up the device characteristics; baud rate, etc.
445 //---------------------------------------------------
447 esci_serial_set_config(serial_channel * chan, cyg_uint32 key,
448 const void *xbuf, cyg_uint32 * len)
451 case CYG_IO_SET_CONFIG_SERIAL_INFO:{
452 cyg_serial_info_t *config = (cyg_serial_info_t *)xbuf;
453 if(*len < sizeof(cyg_serial_info_t)) {
456 *len = sizeof(cyg_serial_info_t);
457 if(true != esci_serial_config_port(chan, config, false))
467 //-------------------------------------
468 // Enable the transmitter on the device
469 //-------------------------------------
470 static void esci_serial_start_xmit(serial_channel * chan)
472 esci_serial_info * esci_chan = (esci_serial_info *)chan->dev_priv;
473 cyg_addrword_t esci_base = esci_chan->esci_base;
474 cyg_uint16 esci_cr12;
476 HAL_READ_UINT16(FREESCALE_ESCI_CR12(esci_base), esci_cr12);
477 esci_cr12 |= FREESCALE_ESCI_CR12_TIE;
478 HAL_WRITE_UINT16(FREESCALE_ESCI_CR12(esci_base), esci_cr12);
481 //--------------------------------------
482 // Disable the transmitter on the device
483 //--------------------------------------
484 static void esci_serial_stop_xmit(serial_channel * chan)
486 esci_serial_info * esci_chan = (esci_serial_info *)chan->dev_priv;
488 cyg_addrword_t esci_base = esci_chan->esci_base;
489 cyg_uint16 esci_cr12;
491 HAL_READ_UINT16(FREESCALE_ESCI_CR12(esci_base), esci_cr12);
492 esci_cr12 &= ~FREESCALE_ESCI_CR12_TIE;
493 HAL_WRITE_UINT16(FREESCALE_ESCI_CR12(esci_base), esci_cr12);
496 //-----------------------------------------
497 // The low level interrupt handler
498 //-----------------------------------------
500 cyg_uint32 esci_serial_ISR(cyg_vector_t vector, cyg_addrword_t data)
502 serial_channel * chan = (serial_channel *)data;
503 esci_serial_info * esci_chan = (esci_serial_info *)chan->dev_priv;
505 cyg_drv_interrupt_mask(esci_chan->interrupt_num);
506 cyg_drv_interrupt_acknowledge(esci_chan->interrupt_num);
508 return CYG_ISR_CALL_DSR; // cause the DSR to run
512 //------------------------------------------
513 // The high level interrupt handler
514 //------------------------------------------
516 #define FREESCALE_ESCI_SR_ERRORS (FREESCALE_ESCI_SR_OR | \
517 FREESCALE_ESCI_SR_NF | \
518 FREESCALE_ESCI_SR_FE | \
519 FREESCALE_ESCI_SR_PF)
522 esci_serial_DSR(cyg_vector_t vector, cyg_ucount32 count, cyg_addrword_t data)
524 serial_channel * chan = (serial_channel *)data;
525 esci_serial_info * esci_chan = (esci_serial_info *)chan->dev_priv;
526 cyg_addrword_t esci_base = esci_chan->esci_base;
530 HAL_READ_UINT16(FREESCALE_ESCI_SR(esci_base), esci_sr);
531 if(esci_sr & FREESCALE_ESCI_SR_RDRF){ // Receiver full
532 HAL_READ_UINT8(FREESCALE_ESCI_DRL(esci_base), esci_dr);
533 HAL_WRITE_UINT16(FREESCALE_ESCI_SR(esci_base), FREESCALE_ESCI_SR_RDRF);
534 if(esci_sr &= (cyg_uint16)FREESCALE_ESCI_SR_ERRORS){
535 HAL_WRITE_UINT16(FREESCALE_ESCI_SR(esci_base), esci_sr);
537 (chan->callbacks->rcv_char)(chan, (cyg_uint8)esci_dr);
539 }else if(esci_sr & FREESCALE_ESCI_SR_TDRE){ //Transmitter empty
540 (chan->callbacks->xmt_char)(chan);
543 cyg_drv_interrupt_unmask(esci_chan->interrupt_num);
546 #endif // CYGPKG_IO_SERIAL_FREESCALE_ESCI_[ABCD]