]> git.karo-electronics.de Git - karo-tx-redboot.git/blob - packages/hal/mips/malta/v2_0/src/ser16c550c.c
Initial revision
[karo-tx-redboot.git] / packages / hal / mips / malta / v2_0 / src / ser16c550c.c
1 //=============================================================================
2 //
3 //      ser16c550c.c
4 //
5 //      Simple driver for the 16c550c serial controllers on the Malta board
6 //
7 //=============================================================================
8 //####ECOSGPLCOPYRIGHTBEGIN####
9 // -------------------------------------------
10 // This file is part of eCos, the Embedded Configurable Operating System.
11 // Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc.
12 //
13 // eCos is free software; you can redistribute it and/or modify it under
14 // the terms of the GNU General Public License as published by the Free
15 // Software Foundation; either version 2 or (at your option) any later version.
16 //
17 // eCos is distributed in the hope that it will be useful, but WITHOUT ANY
18 // WARRANTY; without even the implied warranty of MERCHANTABILITY or
19 // FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
20 // for more details.
21 //
22 // You should have received a copy of the GNU General Public License along
23 // with eCos; if not, write to the Free Software Foundation, Inc.,
24 // 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
25 //
26 // As a special exception, if other files instantiate templates or use macros
27 // or inline functions from this file, or you compile this file and link it
28 // with other works to produce a work based on this file, this file does not
29 // by itself cause the resulting work to be covered by the GNU General Public
30 // License. However the source code for this file must still be made available
31 // in accordance with section (3) of the GNU General Public License.
32 //
33 // This exception does not invalidate any other reasons why a work based on
34 // this file might be covered by the GNU General Public License.
35 //
36 // Alternative licenses for eCos may be arranged by contacting Red Hat, Inc.
37 // at http://sources.redhat.com/ecos/ecos-license/
38 // -------------------------------------------
39 //####ECOSGPLCOPYRIGHTEND####
40 //=============================================================================
41 //#####DESCRIPTIONBEGIN####
42 //
43 // Author(s):   dmoseley
44 // Contributors:dmoseley, jskov
45 // Date:        2001-03-20
46 // Description: Simple driver for the 16c550c serial controller
47 //
48 //####DESCRIPTIONEND####
49 //
50 //=============================================================================
51
52 #include <pkgconf/hal.h>
53 #include <pkgconf/system.h>
54 #include CYGBLD_HAL_PLATFORM_H
55
56 #include <cyg/hal/hal_arch.h>           // SAVE/RESTORE GP macros
57 #include <cyg/hal/hal_io.h>             // IO macros
58 #include <cyg/hal/hal_if.h>             // interface API
59 #include <cyg/hal/hal_intr.h>           // HAL_ENABLE/MASK/UNMASK_INTERRUPTS
60 #include <cyg/hal/hal_misc.h>           // Helper functions
61 #include <cyg/hal/drv_api.h>            // CYG_ISR_HANDLED
62
63 //-----------------------------------------------------------------------------
64 // Define the serial registers. The Malta board is equipped with a 16550C
65 // serial chip.
66 #define MALTA_SER_CLOCK           1843200
67 #define MALTA_SER_16550_BASE_A    0xb80003f8
68 #define MALTA_SER_16550_BASE_B    0xb80002f8
69 #define SER_16550_RBR 0x00   // receiver buffer register, read, dlab = 0
70 #define SER_16550_THR 0x00   // transmitter holding register, write, dlab = 0
71 #define SER_16550_DLL 0x00   // divisor latch (LS), read/write, dlab = 1
72 #define SER_16550_IER 0x01   // interrupt enable register, read/write, dlab = 0
73 #define SER_16550_DLM 0x01   // divisor latch (MS), read/write, dlab = 1
74 #define SER_16550_IIR 0x02   // interrupt identification reg, read, dlab = 0
75 #define SER_16550_FCR 0x02   // fifo control register, write, dlab = 0
76 #define SER_16550_AFR 0x02   // alternate function reg, read/write, dlab = 1
77 #define SER_16550_LCR 0x03   // line control register, read/write
78 #define SER_16550_MCR 0x04   // modem control register, read/write
79 #define SER_16550_LSR 0x05   // line status register, read
80 #define SER_16550_MSR 0x06   // modem status register, read
81 #define SER_16550_SCR 0x07   // scratch pad register
82
83 // The interrupt enable register bits.
84 #define SIO_IER_ERDAI   0x01            // enable received data available irq
85 #define SIO_IER_ETHREI  0x02            // enable THR empty interrupt
86 #define SIO_IER_ELSI    0x04            // enable receiver line status irq
87 #define SIO_IER_EMSI    0x08            // enable modem status interrupt
88
89 // The interrupt identification register bits.
90 #define SIO_IIR_IP      0x01            // 0 if interrupt pending
91 #define SIO_IIR_ID_MASK 0x0e            // mask for interrupt ID bits
92
93 // The line status register bits.
94 #define SIO_LSR_DR      0x01            // data ready
95 #define SIO_LSR_OE      0x02            // overrun error
96 #define SIO_LSR_PE      0x04            // parity error
97 #define SIO_LSR_FE      0x08            // framing error
98 #define SIO_LSR_BI      0x10            // break interrupt
99 #define SIO_LSR_THRE    0x20            // transmitter holding register empty
100 #define SIO_LSR_TEMT    0x40            // transmitter register empty
101 #define SIO_LSR_ERR     0x80            // any error condition
102
103 // The modem status register bits.
104 #define SIO_MSR_DCTS  0x01              // delta clear to send
105 #define SIO_MSR_DDSR  0x02              // delta data set ready
106 #define SIO_MSR_TERI  0x04              // trailing edge ring indicator
107 #define SIO_MSR_DDCD  0x08              // delta data carrier detect
108 #define SIO_MSR_CTS   0x10              // clear to send
109 #define SIO_MSR_DSR   0x20              // data set ready
110 #define SIO_MSR_RI    0x40              // ring indicator
111 #define SIO_MSR_DCD   0x80              // data carrier detect
112
113 // The line control register bits.
114 #define SIO_LCR_WLS0   0x01             // word length select bit 0
115 #define SIO_LCR_WLS1   0x02             // word length select bit 1
116 #define SIO_LCR_STB    0x04             // number of stop bits
117 #define SIO_LCR_PEN    0x08             // parity enable
118 #define SIO_LCR_EPS    0x10             // even parity select
119 #define SIO_LCR_SP     0x20             // stick parity
120 #define SIO_LCR_SB     0x40             // set break
121 #define SIO_LCR_DLAB   0x80             // divisor latch access bit
122
123 // The FIFO control register
124 #define SIO_FCR_FCR0   0x01             // enable xmit and rcvr fifos
125 #define SIO_FCR_FCR1   0x02             // clear RCVR FIFO
126 #define SIO_FCR_FCR2   0x04             // clear XMIT FIFO
127
128 /////////////////////////////////////////
129 // Interrupt Enable Register
130 #define IER_RCV 0x01
131 #define IER_XMT 0x02
132 #define IER_LS  0x04
133 #define IER_MS  0x08
134
135 // Line Control Register
136 #define LCR_WL5 0x00    // Word length
137 #define LCR_WL6 0x01
138 #define LCR_WL7 0x02
139 #define LCR_WL8 0x03
140 #define LCR_SB1 0x00    // Number of stop bits
141 #define LCR_SB1_5 0x04  // 1.5 -> only valid with 5 bit words
142 #define LCR_SB2 0x04
143 #define LCR_PN  0x00    // Parity mode - none
144 #define LCR_PE  0x0C    // Parity mode - even
145 #define LCR_PO  0x08    // Parity mode - odd
146 #define LCR_PM  0x28    // Forced "mark" parity
147 #define LCR_PS  0x38    // Forced "space" parity
148 #define LCR_DL  0x80    // Enable baud rate latch
149
150 // Line Status Register
151 #define LSR_RSR 0x01
152 #define LSR_THE 0x20
153
154 // Modem Control Register
155 #define MCR_DTR 0x01
156 #define MCR_RTS 0x02
157 #define MCR_INT 0x08   // Enable interrupts
158 #define MCR_AFE 0x20
159
160 // Interrupt status register
161 #define ISR_None             0x01
162 #define ISR_Rx_Line_Status   0x06
163 #define ISR_Rx_Avail         0x04
164 #define ISR_Rx_Char_Timeout  0x0C
165 #define ISR_Tx_Empty         0x02
166 #define IRS_Modem_Status     0x00
167
168 // FIFO control register
169 #define FCR_ENABLE     0x01
170 #define FCR_CLEAR_RCVR 0x02
171 #define FCR_CLEAR_XMIT 0x04
172
173 #define CYG_DEV_SERIAL_BAUD_DIVISOR (MALTA_SER_CLOCK/16/CYGNUM_HAL_VIRTUAL_VECTOR_CHANNELS_DEFAULT_BAUD)
174
175 //-----------------------------------------------------------------------------
176 typedef struct {
177     cyg_uint8* base;
178     cyg_int32 msec_timeout;
179     int isr_vector;
180 } channel_data_t;
181
182 static channel_data_t channels[2] = {
183     { (cyg_uint8*)MALTA_SER_16550_BASE_A, 1000, CYGNUM_HAL_INTERRUPT_TTY0},
184     { (cyg_uint8*)MALTA_SER_16550_BASE_B, 1000, CYGNUM_HAL_INTERRUPT_TTY1}
185 };
186
187 //-----------------------------------------------------------------------------
188 // Set the baud rate
189
190 static void
191 cyg_hal_plf_serial_set_baud(cyg_uint8* port, cyg_uint16 baud_divisor)
192 {
193     cyg_uint8 _lcr;
194
195     HAL_READ_UINT8(port+SER_16550_LCR, _lcr);
196     _lcr |= LCR_DL;
197     HAL_WRITE_UINT8(port+SER_16550_LCR, _lcr);
198
199     HAL_WRITE_UINT8(port+SER_16550_DLM, baud_divisor >> 8);
200     HAL_WRITE_UINT8(port+SER_16550_DLL, baud_divisor & 0xff);
201
202     _lcr &= ~LCR_DL;
203     HAL_WRITE_UINT8(port+SER_16550_LCR, _lcr);
204 }
205
206 //-----------------------------------------------------------------------------
207 // The minimal init, get and put functions. All by polling.
208
209 void
210 cyg_hal_plf_serial_init_channel(void* __ch_data)
211 {
212     cyg_uint8* port;
213     cyg_uint8 _lcr;
214
215     // Some of the diagnostic print code calls through here with no idea what the ch_data is.
216     // Go ahead and assume it is channels[0].
217     if (__ch_data == 0)
218       __ch_data = (void*)&channels[0];
219
220     port = ((channel_data_t*)__ch_data)->base;
221
222     // Disable port interrupts while changing hardware
223     HAL_WRITE_UINT8(port+SER_16550_IER, 0);
224
225     // Set databits, stopbits and parity.
226     _lcr = LCR_WL8 | LCR_SB1 | LCR_PN;
227     HAL_WRITE_UINT8(port+SER_16550_LCR, _lcr);
228
229     // Set baud rate.
230     cyg_hal_plf_serial_set_baud(port, CYG_DEV_SERIAL_BAUD_DIVISOR);
231
232     // Enable and clear FIFO
233     HAL_WRITE_UINT8(port+SER_16550_FCR, (FCR_ENABLE | FCR_CLEAR_RCVR | FCR_CLEAR_XMIT));
234
235     // enable RTS to keep host side happy. Also allow interrupts
236     HAL_WRITE_UINT8( port+SER_16550_MCR, MCR_DTR | MCR_RTS | MCR_INT);
237     
238     // Don't allow interrupts.
239     HAL_WRITE_UINT8(port+SER_16550_IER, 0);
240 }
241
242 void
243 cyg_hal_plf_serial_putc(void* __ch_data, cyg_uint8 __ch)
244 {
245     cyg_uint8* port;
246     cyg_uint8 _lsr;
247
248     // Some of the diagnostic print code calls through here with no idea what the ch_data is.
249     // Go ahead and assume it is channels[0].
250     if (__ch_data == 0)
251       __ch_data = (void*)&channels[0];
252
253     port = ((channel_data_t*)__ch_data)->base;
254
255     CYGARC_HAL_SAVE_GP();
256
257     do {
258         HAL_READ_UINT8(port+SER_16550_LSR, _lsr);
259     } while ((_lsr & SIO_LSR_THRE) == 0);
260
261     // Now, the transmit buffer is empty
262     HAL_WRITE_UINT8(port+SER_16550_THR, __ch);
263
264     // Hang around until the character has been safely sent.
265     do {
266         HAL_READ_UINT8(port+SER_16550_LSR, _lsr);
267     } while ((_lsr & SIO_LSR_THRE) == 0);
268
269     CYGARC_HAL_RESTORE_GP();
270 }
271
272 static cyg_bool
273 cyg_hal_plf_serial_getc_nonblock(void* __ch_data, cyg_uint8* ch)
274 {
275     cyg_uint8* port;
276     cyg_uint8 _lsr;
277
278     // Some of the diagnostic print code calls through here with no idea what the ch_data is.
279     // Go ahead and assume it is channels[0].
280     if (__ch_data == 0)
281       __ch_data = (void*)&channels[0];
282
283     port = ((channel_data_t*)__ch_data)->base;
284
285     HAL_READ_UINT8(port+SER_16550_LSR, _lsr);
286     if ((_lsr & SIO_LSR_DR) == 0)
287         return false;
288
289     HAL_READ_UINT8(port+SER_16550_RBR, *ch);
290
291     return true;
292 }
293
294 cyg_uint8
295 cyg_hal_plf_serial_getc(void* __ch_data)
296 {
297     cyg_uint8 ch;
298     CYGARC_HAL_SAVE_GP();
299
300     // Some of the diagnostic print code calls through here with no idea what the ch_data is.
301     // Go ahead and assume it is channels[0].
302     if (__ch_data == 0)
303       __ch_data = (void*)&channels[0];
304
305     while(!cyg_hal_plf_serial_getc_nonblock(__ch_data, &ch));
306
307     CYGARC_HAL_RESTORE_GP();
308     return ch;
309 }
310
311 static void
312 cyg_hal_plf_serial_write(void* __ch_data, const cyg_uint8* __buf, 
313                          cyg_uint32 __len)
314 {
315     CYGARC_HAL_SAVE_GP();
316
317     // Some of the diagnostic print code calls through here with no idea what the ch_data is.
318     // Go ahead and assume it is channels[0].
319     if (__ch_data == 0)
320       __ch_data = (void*)&channels[0];
321
322     while(__len-- > 0)
323         cyg_hal_plf_serial_putc(__ch_data, *__buf++);
324
325     CYGARC_HAL_RESTORE_GP();
326 }
327
328 static void
329 cyg_hal_plf_serial_read(void* __ch_data, cyg_uint8* __buf, cyg_uint32 __len)
330 {
331     CYGARC_HAL_SAVE_GP();
332
333     // Some of the diagnostic print code calls through here with no idea what the ch_data is.
334     // Go ahead and assume it is channels[0].
335     if (__ch_data == 0)
336       __ch_data = (void*)&channels[0];
337
338     while(__len-- > 0)
339         *__buf++ = cyg_hal_plf_serial_getc(__ch_data);
340
341     CYGARC_HAL_RESTORE_GP();
342 }
343
344
345 cyg_bool
346 cyg_hal_plf_serial_getc_timeout(void* __ch_data, cyg_uint8* ch)
347 {
348     int delay_count;
349     channel_data_t* chan;
350     cyg_bool res;
351     CYGARC_HAL_SAVE_GP();
352
353     // Some of the diagnostic print code calls through here with no idea what the ch_data is.
354     // Go ahead and assume it is channels[0].
355     if (__ch_data == 0)
356       __ch_data = (void*)&channels[0];
357
358     chan = (channel_data_t*)__ch_data;
359
360     delay_count = chan->msec_timeout * 10; // delay in .1 ms steps
361
362     for(;;) {
363         res = cyg_hal_plf_serial_getc_nonblock(__ch_data, ch);
364         if (res || 0 == delay_count--)
365             break;
366         CYGACC_CALL_IF_DELAY_US(100);
367     }
368
369     CYGARC_HAL_RESTORE_GP();
370     return res;
371 }
372
373 static int
374 cyg_hal_plf_serial_control(void *__ch_data, __comm_control_cmd_t __func, ...)
375 {
376     static int irq_state = 0;
377     channel_data_t* chan;
378     cyg_uint8 ier;
379     int ret = 0;
380     CYGARC_HAL_SAVE_GP();
381
382     // Some of the diagnostic print code calls through here with no idea what the ch_data is.
383     // Go ahead and assume it is channels[0].
384     if (__ch_data == 0)
385       __ch_data = (void*)&channels[0];
386
387     chan = (channel_data_t*)__ch_data;
388
389     switch (__func) {
390     case __COMMCTL_IRQ_ENABLE:
391         irq_state = 1;
392
393         HAL_READ_UINT8(chan->base + SER_16550_IER, ier);
394         ier |= SIO_IER_ERDAI;
395         HAL_WRITE_UINT8(chan->base + SER_16550_IER, ier);
396
397         HAL_INTERRUPT_SET_LEVEL(chan->isr_vector, 1);
398         HAL_INTERRUPT_UNMASK(chan->isr_vector);
399         break;
400     case __COMMCTL_IRQ_DISABLE:
401         ret = irq_state;
402         irq_state = 0;
403
404         HAL_READ_UINT8(chan->base + SER_16550_IER, ier);
405         ier &= ~SIO_IER_ERDAI;
406         HAL_WRITE_UINT8(chan->base + SER_16550_IER, ier);
407
408         HAL_INTERRUPT_MASK(chan->isr_vector);
409         break;
410     case __COMMCTL_DBG_ISR_VECTOR:
411         ret = chan->isr_vector;
412         break;
413     case __COMMCTL_SET_TIMEOUT:
414     {
415         va_list ap;
416
417         va_start(ap, __func);
418
419         ret = chan->msec_timeout;
420         chan->msec_timeout = va_arg(ap, cyg_uint32);
421
422         va_end(ap);
423     }        
424     break;
425     case __COMMCTL_SETBAUD:
426     {
427         cyg_uint32 baud_rate;
428         cyg_uint16 baud_divisor;
429         cyg_uint8* port = chan->base;
430         va_list ap;
431
432         va_start(ap, __func);
433         baud_rate = va_arg(ap, cyg_uint32);
434         va_end(ap);
435
436         baud_divisor = (MALTA_SER_CLOCK / 16 / baud_rate);
437
438         HAL_WRITE_UINT32(HAL_MALTA_BRKRES, 0);
439 #if 0
440         //
441         // We may need to increase the timeout before causing a break reset.
442         // According to the Malta Users Manual (Document MD00005) The BRKRES
443         // register will need to be programmed with a value larger that 0xA (the default)
444         // if we are going to use a baud rate lower than 2400.
445         //
446         if (baud_rate <= 2400)
447         {
448             // For now, just disable the break reset entirely.
449             HAL_WRITE_UINT32(HAL_MALTA_BRKRES, 0);
450         } else {
451             // Put the break reset state back to the default
452             HAL_WRITE_UINT32(HAL_MALTA_BRKRES, HAL_MALTA_BRKRES_DEFAULT_VALUE);
453         }
454 #endif
455         // Disable port interrupts while changing hardware
456         HAL_READ_UINT8(port+SER_16550_IER, ier);
457         HAL_WRITE_UINT8(port+SER_16550_IER, 0);
458
459         // Set baud rate.
460         cyg_hal_plf_serial_set_baud(port, baud_divisor);
461
462         // Reenable interrupts if necessary
463         HAL_WRITE_UINT8(port+SER_16550_IER, ier);
464     }
465     break;
466
467     case __COMMCTL_GETBAUD:
468         break;
469     default:
470         break;
471     }
472     CYGARC_HAL_RESTORE_GP();
473     return ret;
474 }
475
476 static int
477 cyg_hal_plf_serial_isr(void *__ch_data, int* __ctrlc, 
478                        CYG_ADDRWORD __vector, CYG_ADDRWORD __data)
479 {
480     int res = 0;
481     cyg_uint8 _iir, c;
482     channel_data_t* chan;
483     CYGARC_HAL_SAVE_GP();
484
485     // Some of the diagnostic print code calls through here with no idea what the ch_data is.
486     // Go ahead and assume it is channels[0].
487     if (__ch_data == 0)
488       __ch_data = (void*)&channels[0];
489
490     chan = (channel_data_t*)__ch_data;
491
492     HAL_INTERRUPT_ACKNOWLEDGE(chan->isr_vector);
493
494     HAL_READ_UINT8(chan->base + SER_16550_IIR, _iir);
495     _iir &= SIO_IIR_ID_MASK;
496
497     *__ctrlc = 0;
498     if ((_iir == ISR_Rx_Avail) || (_iir == ISR_Rx_Char_Timeout)) {
499
500         HAL_READ_UINT8(chan->base + SER_16550_RBR, c);
501     
502         if( cyg_hal_is_break( &c , 1 ) )
503             *__ctrlc = 1;
504
505         res = CYG_ISR_HANDLED;
506     }
507
508     CYGARC_HAL_RESTORE_GP();
509     return res;
510 }
511
512 static void
513 cyg_hal_plf_serial_init(void)
514 {
515     hal_virtual_comm_table_t* comm;
516     int cur = CYGACC_CALL_IF_SET_CONSOLE_COMM(CYGNUM_CALL_IF_SET_COMM_ID_QUERY_CURRENT);
517
518     // Disable interrupts.
519     HAL_INTERRUPT_MASK(channels[0].isr_vector);
520     HAL_INTERRUPT_MASK(channels[1].isr_vector);
521
522     // Init channels
523     cyg_hal_plf_serial_init_channel((void*)&channels[0]);
524     cyg_hal_plf_serial_init_channel((void*)&channels[1]);
525     
526     // Setup procs in the vector table
527
528     // Set channel 0
529     CYGACC_CALL_IF_SET_CONSOLE_COMM(0);
530     comm = CYGACC_CALL_IF_CONSOLE_PROCS();
531     CYGACC_COMM_IF_CH_DATA_SET(*comm, &channels[0]);
532     CYGACC_COMM_IF_WRITE_SET(*comm, cyg_hal_plf_serial_write);
533     CYGACC_COMM_IF_READ_SET(*comm, cyg_hal_plf_serial_read);
534     CYGACC_COMM_IF_PUTC_SET(*comm, cyg_hal_plf_serial_putc);
535     CYGACC_COMM_IF_GETC_SET(*comm, cyg_hal_plf_serial_getc);
536     CYGACC_COMM_IF_CONTROL_SET(*comm, cyg_hal_plf_serial_control);
537     CYGACC_COMM_IF_DBG_ISR_SET(*comm, cyg_hal_plf_serial_isr);
538     CYGACC_COMM_IF_GETC_TIMEOUT_SET(*comm, cyg_hal_plf_serial_getc_timeout);
539
540     // Set channel 1
541     CYGACC_CALL_IF_SET_CONSOLE_COMM(1);
542     comm = CYGACC_CALL_IF_CONSOLE_PROCS();
543     CYGACC_COMM_IF_CH_DATA_SET(*comm, &channels[1]);
544     CYGACC_COMM_IF_WRITE_SET(*comm, cyg_hal_plf_serial_write);
545     CYGACC_COMM_IF_READ_SET(*comm, cyg_hal_plf_serial_read);
546     CYGACC_COMM_IF_PUTC_SET(*comm, cyg_hal_plf_serial_putc);
547     CYGACC_COMM_IF_GETC_SET(*comm, cyg_hal_plf_serial_getc);
548     CYGACC_COMM_IF_CONTROL_SET(*comm, cyg_hal_plf_serial_control);
549     CYGACC_COMM_IF_DBG_ISR_SET(*comm, cyg_hal_plf_serial_isr);
550     CYGACC_COMM_IF_GETC_TIMEOUT_SET(*comm, cyg_hal_plf_serial_getc_timeout);
551
552     // Restore original console
553     CYGACC_CALL_IF_SET_CONSOLE_COMM(cur);
554 }
555
556 void
557 cyg_hal_plf_comms_init(void)
558 {
559     static int initialized = 0;
560
561     if (initialized)
562         return;
563
564     initialized = 1;
565
566     cyg_hal_plf_serial_init();
567 }
568
569 //-----------------------------------------------------------------------------
570 // end of ser16c550c.c
571