source: rtems/c/src/libchip/serial/ns16550.c @ 6aff507

4.104.114.84.9
Last change on this file since 6aff507 was 6aff507, checked in by Thomas Doerfler <Thomas.Doerfler@…>, on Jul 18, 2007 at 12:51:30 PM

added PPC_e300 to raw_exception.c
removed timed abort in ns16550.c transmit code

  • Property mode set to 100644
File size: 17.8 KB
Line 
1/*
2 *  This file contains the TTY driver for the National Semiconductor NS16550.
3 *
4 *  This part is widely cloned and second sourced.  It is found in a number
5 *  of "Super IO" controllers.
6 *
7 *  COPYRIGHT (c) 1998 by Radstone Technology
8 *
9 *
10 * THIS FILE IS PROVIDED TO YOU, THE USER, "AS IS", WITHOUT WARRANTY OF ANY
11 * KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
12 * IMPLIED WARRANTY OF FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK
13 * AS TO THE QUALITY AND PERFORMANCE OF ALL CODE IN THIS FILE IS WITH YOU.
14 *
15 * You are hereby granted permission to use, copy, modify, and distribute
16 * this file, provided that this notice, plus the above copyright notice
17 * and disclaimer, appears in all copies. Radstone Technology will provide
18 * no support for this code.
19 *
20 *  This driver uses the termios pseudo driver.
21 */
22
23#include <rtems.h>
24#include <rtems/libio.h>
25#include <stdlib.h>
26#include <rtems/ringbuf.h>
27
28#include <libchip/serial.h>
29#include <libchip/sersupp.h>
30#include <rtems/bspIo.h>
31#include "ns16550_p.h"
32
33/*
34 * Flow control is only supported when using interrupts
35 */
36
37console_flow ns16550_flow_RTSCTS = {
38  ns16550_negate_RTS,             /* deviceStopRemoteTx */
39  ns16550_assert_RTS              /* deviceStartRemoteTx */
40};
41
42console_flow ns16550_flow_DTRCTS = {
43  ns16550_negate_DTR,             /* deviceStopRemoteTx */
44  ns16550_assert_DTR              /* deviceStartRemoteTx */
45};
46
47console_fns ns16550_fns = {
48  libchip_serial_default_probe,   /* deviceProbe */
49  ns16550_open,                   /* deviceFirstOpen */
50  NULL,                           /* deviceLastClose */
51  NULL,                           /* deviceRead */
52  ns16550_write_support_int,      /* deviceWrite */
53  ns16550_initialize_interrupts,  /* deviceInitialize */
54  ns16550_write_polled,           /* deviceWritePolled */
55  ns16550_set_attributes,         /* deviceSetAttributes */
56  TRUE                            /* deviceOutputUsesInterrupts */
57};
58
59console_fns ns16550_fns_polled = {
60  libchip_serial_default_probe,        /* deviceProbe */
61  ns16550_open,                        /* deviceFirstOpen */
62  ns16550_close,                       /* deviceLastClose */
63  ns16550_inbyte_nonblocking_polled,   /* deviceRead */
64  ns16550_write_support_polled,        /* deviceWrite */
65  ns16550_init,                        /* deviceInitialize */
66  ns16550_write_polled,                /* deviceWritePolled */
67  ns16550_set_attributes,              /* deviceSetAttributes */
68  FALSE                                /* deviceOutputUsesInterrupts */
69};
70
71#if defined(__PPC__)
72#ifdef _OLD_EXCEPTIONS
73extern void set_vector( rtems_isr_entry, rtems_vector_number, int );
74#else
75#include <bsp/irq.h>
76#endif
77#endif
78
79/*
80 *  ns16550_init
81 */
82
83NS16550_STATIC void ns16550_init(int minor)
84{
85  uint32_t                pNS16550;
86  uint8_t                 ucTrash;
87  uint8_t                 ucDataByte;
88  uint32_t                ulBaudDivisor;
89  ns16550_context        *pns16550Context;
90  setRegister_f           setReg;
91  getRegister_f           getReg;
92
93  pns16550Context=(ns16550_context *)malloc(sizeof(ns16550_context));
94
95  Console_Port_Data[minor].pDeviceContext=(void *)pns16550Context;
96  pns16550Context->ucModemCtrl=SP_MODEM_IRQ;
97
98  pNS16550 = Console_Port_Tbl[minor].ulCtrlPort1;
99  setReg   = Console_Port_Tbl[minor].setRegister;
100  getReg   = Console_Port_Tbl[minor].getRegister;
101
102  /* Clear the divisor latch, clear all interrupt enables,
103   * and reset and
104   * disable the FIFO's.
105   */
106
107  (*setReg)(pNS16550, NS16550_LINE_CONTROL, 0x0);
108  ns16550_enable_interrupts(minor, NS16550_DISABLE_ALL_INTR);
109
110  /* Set the divisor latch and set the baud rate. */
111
112  ulBaudDivisor = NS16550_Baud(
113    (uint32_t) Console_Port_Tbl[minor].ulClock,
114    (uint32_t) Console_Port_Tbl[minor].pDeviceParams
115  );
116  ucDataByte = SP_LINE_DLAB;
117  (*setReg)(pNS16550, NS16550_LINE_CONTROL, ucDataByte);
118
119  /* XXX */
120  (*setReg)(pNS16550, NS16550_TRANSMIT_BUFFER, ulBaudDivisor&0xff);
121  (*setReg)(pNS16550, NS16550_INTERRUPT_ENABLE, (ulBaudDivisor>>8)&0xff);
122
123  /* Clear the divisor latch and set the character size to eight bits */
124  /* with one stop bit and no parity checking. */
125  ucDataByte = EIGHT_BITS;
126  (*setReg)(pNS16550, NS16550_LINE_CONTROL, ucDataByte);
127
128  /* Enable and reset transmit and receive FIFOs. TJA     */
129  ucDataByte = SP_FIFO_ENABLE;
130  (*setReg)(pNS16550, NS16550_FIFO_CONTROL, ucDataByte);
131
132  ucDataByte = SP_FIFO_ENABLE | SP_FIFO_RXRST | SP_FIFO_TXRST;
133  (*setReg)(pNS16550, NS16550_FIFO_CONTROL, ucDataByte);
134
135  ns16550_enable_interrupts(minor, NS16550_DISABLE_ALL_INTR);
136
137  /* Set data terminal ready. */
138  /* And open interrupt tristate line */
139  (*setReg)(pNS16550, NS16550_MODEM_CONTROL,pns16550Context->ucModemCtrl);
140
141  ucTrash = (*getReg)(pNS16550, NS16550_LINE_STATUS );
142  ucTrash = (*getReg)(pNS16550, NS16550_RECEIVE_BUFFER );
143}
144
145/*
146 *  ns16550_open
147 */
148
149NS16550_STATIC int ns16550_open(
150  int      major,
151  int      minor,
152  void    * arg
153)
154{
155  /*
156   * Assert DTR
157   */
158
159  if(Console_Port_Tbl[minor].pDeviceFlow != &ns16550_flow_DTRCTS) {
160    ns16550_assert_DTR(minor);
161  }
162
163  return(RTEMS_SUCCESSFUL);
164}
165
166/*
167 *  ns16550_close
168 */
169
170NS16550_STATIC int ns16550_close(
171  int      major,
172  int      minor,
173  void    * arg
174)
175{
176  /*
177   * Negate DTR
178   */
179  if(Console_Port_Tbl[minor].pDeviceFlow != &ns16550_flow_DTRCTS) {
180    ns16550_negate_DTR(minor);
181  }
182
183  return(RTEMS_SUCCESSFUL);
184}
185
186/*
187 *  ns16550_write_polled
188 */
189
190NS16550_STATIC void ns16550_write_polled(
191  int   minor,
192  char  cChar
193)
194{
195  uint32_t                pNS16550;
196  unsigned char           ucLineStatus;
197  getRegister_f           getReg;
198  setRegister_f           setReg;
199
200  pNS16550 = Console_Port_Tbl[minor].ulCtrlPort1;
201  getReg   = Console_Port_Tbl[minor].getRegister;
202  setReg   = Console_Port_Tbl[minor].setRegister;
203
204  /*
205   * wait for transmitter holding register to be empty
206   */
207  ucLineStatus = (*getReg)(pNS16550, NS16550_LINE_STATUS);
208  while ((ucLineStatus & SP_LSR_THOLD) == 0) {
209    /*
210     * Yield while we wait
211     */
212#if 0
213     if(_System_state_Is_up(_System_state_Get())) {
214       rtems_task_wake_after(RTEMS_YIELD_PROCESSOR);
215     }
216#endif
217     ucLineStatus = (*getReg)(pNS16550, NS16550_LINE_STATUS);
218  }
219
220  /*
221   * transmit character
222   */
223  (*setReg)(pNS16550, NS16550_TRANSMIT_BUFFER, cChar);
224}
225
226/*
227 * These routines provide control of the RTS and DTR lines
228 */
229
230/*
231 *  ns16550_assert_RTS
232 */
233
234NS16550_STATIC int ns16550_assert_RTS(int minor)
235{
236  uint32_t                pNS16550;
237  uint32_t                Irql;
238  ns16550_context        *pns16550Context;
239  setRegister_f           setReg;
240
241  pns16550Context=(ns16550_context *) Console_Port_Data[minor].pDeviceContext;
242
243  pNS16550 = Console_Port_Tbl[minor].ulCtrlPort1;
244  setReg   = Console_Port_Tbl[minor].setRegister;
245
246  /*
247   * Assert RTS
248   */
249  rtems_interrupt_disable(Irql);
250  pns16550Context->ucModemCtrl|=SP_MODEM_RTS;
251  (*setReg)(pNS16550, NS16550_MODEM_CONTROL, pns16550Context->ucModemCtrl);
252  rtems_interrupt_enable(Irql);
253  return 0;
254}
255
256/*
257 *  ns16550_negate_RTS
258 */
259
260NS16550_STATIC int ns16550_negate_RTS(int minor)
261{
262  uint32_t                pNS16550;
263  uint32_t                Irql;
264  ns16550_context        *pns16550Context;
265  setRegister_f           setReg;
266
267  pns16550Context=(ns16550_context *) Console_Port_Data[minor].pDeviceContext;
268
269  pNS16550 = Console_Port_Tbl[minor].ulCtrlPort1;
270  setReg   = Console_Port_Tbl[minor].setRegister;
271
272  /*
273   * Negate RTS
274   */
275  rtems_interrupt_disable(Irql);
276  pns16550Context->ucModemCtrl&=~SP_MODEM_RTS;
277  (*setReg)(pNS16550, NS16550_MODEM_CONTROL, pns16550Context->ucModemCtrl);
278  rtems_interrupt_enable(Irql);
279  return 0;
280}
281
282/*
283 * These flow control routines utilise a connection from the local DTR
284 * line to the remote CTS line
285 */
286
287/*
288 *  ns16550_assert_DTR
289 */
290
291NS16550_STATIC int ns16550_assert_DTR(int minor)
292{
293  uint32_t                pNS16550;
294  uint32_t                Irql;
295  ns16550_context        *pns16550Context;
296  setRegister_f           setReg;
297
298  pns16550Context=(ns16550_context *) Console_Port_Data[minor].pDeviceContext;
299
300  pNS16550 = Console_Port_Tbl[minor].ulCtrlPort1;
301  setReg   = Console_Port_Tbl[minor].setRegister;
302
303  /*
304   * Assert DTR
305   */
306  rtems_interrupt_disable(Irql);
307  pns16550Context->ucModemCtrl|=SP_MODEM_DTR;
308  (*setReg)(pNS16550, NS16550_MODEM_CONTROL, pns16550Context->ucModemCtrl);
309  rtems_interrupt_enable(Irql);
310  return 0;
311}
312
313/*
314 *  ns16550_negate_DTR
315 */
316
317NS16550_STATIC int ns16550_negate_DTR(int minor)
318{
319  uint32_t                pNS16550;
320  uint32_t                Irql;
321  ns16550_context        *pns16550Context;
322  setRegister_f           setReg;
323
324  pns16550Context=(ns16550_context *) Console_Port_Data[minor].pDeviceContext;
325
326  pNS16550 = Console_Port_Tbl[minor].ulCtrlPort1;
327  setReg   = Console_Port_Tbl[minor].setRegister;
328
329  /*
330   * Negate DTR
331   */
332  rtems_interrupt_disable(Irql);
333  pns16550Context->ucModemCtrl&=~SP_MODEM_DTR;
334  (*setReg)(pNS16550, NS16550_MODEM_CONTROL,pns16550Context->ucModemCtrl);
335  rtems_interrupt_enable(Irql);
336  return 0;
337}
338
339/*
340 *  ns16550_set_attributes
341 *
342 *  This function sets the channel to reflect the requested termios
343 *  port settings.
344 */
345
346NS16550_STATIC int ns16550_set_attributes(
347  int                   minor,
348  const struct termios *t
349)
350{
351  uint32_t                pNS16550;
352  uint32_t                ulBaudDivisor;
353  uint8_t                 ucLineControl;
354  uint32_t                baud_requested;
355  setRegister_f           setReg;
356  getRegister_f           getReg;
357  uint32_t                Irql;
358
359  pNS16550 = Console_Port_Tbl[minor].ulCtrlPort1;
360  setReg   = Console_Port_Tbl[minor].setRegister;
361  getReg   = Console_Port_Tbl[minor].getRegister;
362
363  /*
364   *  Calculate the baud rate divisor
365   */
366
367  baud_requested = t->c_cflag & CBAUD;
368  if (!baud_requested)
369    baud_requested = B9600;              /* default to 9600 baud */
370
371  ulBaudDivisor = NS16550_Baud(
372    (uint32_t) Console_Port_Tbl[minor].ulClock,
373    termios_baud_to_number(baud_requested)
374  );
375
376  ucLineControl = 0;
377
378  /*
379   *  Parity
380   */
381
382  if (t->c_cflag & PARENB) {
383    ucLineControl |= SP_LINE_PAR;
384    if (!(t->c_cflag & PARODD))
385      ucLineControl |= SP_LINE_ODD;
386  }
387
388  /*
389   *  Character Size
390   */
391
392  if (t->c_cflag & CSIZE) {
393    switch (t->c_cflag & CSIZE) {
394      case CS5:  ucLineControl |= FIVE_BITS;  break;
395      case CS6:  ucLineControl |= SIX_BITS;   break;
396      case CS7:  ucLineControl |= SEVEN_BITS; break;
397      case CS8:  ucLineControl |= EIGHT_BITS; break;
398    }
399  } else {
400    ucLineControl |= EIGHT_BITS;               /* default to 9600,8,N,1 */
401  }
402
403  /*
404   *  Stop Bits
405   */
406
407  if (t->c_cflag & CSTOPB) {
408    ucLineControl |= SP_LINE_STOP;              /* 2 stop bits */
409  } else {
410    ;                                           /* 1 stop bit */
411  }
412
413  /*
414   *  Now actually set the chip
415   */
416
417  rtems_interrupt_disable(Irql);
418
419    /*
420     *  Set the baud rate
421     */
422
423    (*setReg)(pNS16550, NS16550_LINE_CONTROL, SP_LINE_DLAB);
424    /* XXX are these registers right? */
425    (*setReg)(pNS16550, NS16550_TRANSMIT_BUFFER, ulBaudDivisor&0xff);
426    (*setReg)(pNS16550, NS16550_INTERRUPT_ENABLE, (ulBaudDivisor>>8)&0xff);
427
428    /*
429     *  Now write the line control
430     */
431    (*setReg)(pNS16550, NS16550_LINE_CONTROL, ucLineControl );
432
433  rtems_interrupt_enable(Irql);
434
435  return 0;
436}
437
438/*
439 *  ns16550_process
440 *
441 *  This routine is the console interrupt handler for A port.
442 */
443
444NS16550_STATIC void ns16550_process(
445        int             minor
446)
447{
448  uint32_t                pNS16550;
449  volatile uint8_t        ucLineStatus;
450  volatile uint8_t        ucInterruptId;
451  char                    cChar;
452  getRegister_f           getReg;
453  setRegister_f           setReg;
454
455  pNS16550 = Console_Port_Tbl[minor].ulCtrlPort1;
456  getReg   = Console_Port_Tbl[minor].getRegister;
457  setReg   = Console_Port_Tbl[minor].setRegister;
458
459  do {
460    /*
461     * Deal with any received characters
462     */
463    while(TRUE) {
464      ucLineStatus = (*getReg)(pNS16550, NS16550_LINE_STATUS);
465      if(~ucLineStatus & SP_LSR_RDY) {
466        break;
467      }
468      cChar = (*getReg)(pNS16550, NS16550_RECEIVE_BUFFER);
469      rtems_termios_enqueue_raw_characters(
470        Console_Port_Data[minor].termios_data,
471        &cChar,
472        1
473      );
474    }
475
476    /*
477     *  TX all the characters we can
478     */
479
480    while(TRUE) {
481        ucLineStatus = (*getReg)(pNS16550, NS16550_LINE_STATUS);
482        if(~ucLineStatus & SP_LSR_THOLD) {
483          /*
484           * We'll get another interrupt when
485           * the transmitter holding reg. becomes
486           * free again
487           */
488          break;
489        }
490
491#if 0
492        /* XXX flow control not completely supported in libchip */
493
494        if(Console_Port_Tbl[minor].pDeviceFlow != &ns16550_flow_RTSCTS) {
495          ns16550_negate_RTS(minor);
496        }
497#endif
498
499    rtems_termios_dequeue_characters(Console_Port_Data[minor].termios_data, 1);
500    if (rtems_termios_dequeue_characters(
501         Console_Port_Data[minor].termios_data, 1)) {
502        if (Console_Port_Tbl[minor].pDeviceFlow != &ns16550_flow_RTSCTS) {
503          ns16550_negate_RTS(minor);
504        }
505        Console_Port_Data[minor].bActive = FALSE;
506        ns16550_enable_interrupts(minor, NS16550_ENABLE_ALL_INTR_EXCEPT_TX);
507        break;
508      }
509
510      ucInterruptId = (*getReg)(pNS16550, NS16550_INTERRUPT_ID);
511    }
512  } while((ucInterruptId&0xf)!=0x1);
513}
514
515#if defined(__PPC__)
516#ifdef _OLD_EXCEPTIONS
517
518/*
519 *  ns16550_isr
520 */
521
522NS16550_STATIC rtems_isr ns16550_isr(
523  rtems_vector_number vector
524)
525{
526  int     minor;
527
528  for(minor=0;minor<Console_Port_Count;minor++) {
529    if(Console_Port_Tbl[minor].ulIntVector == vector &&
530       Console_Port_Tbl[minor].deviceType == SERIAL_NS16550 ) {
531      ns16550_process(minor);
532    }
533  }
534}
535
536#else
537
538NS16550_STATIC rtems_isr ns16550_isr(
539  void *entry
540)
541{
542  console_tbl *ptr = entry;
543  int         minor;
544
545  for(minor=0;minor<Console_Port_Count;minor++) {
546    if( &Console_Port_Tbl[minor] == ptr ) {
547      ns16550_process(minor);
548    }
549  }
550
551}
552
553#endif
554#endif
555
556/*
557 *  ns16550_enable_interrupts
558 *
559 *  This routine initializes the port to have the specified interrupts masked.
560 */
561
562NS16550_STATIC void ns16550_enable_interrupts(
563  int minor,
564  int mask
565)
566{
567  uint32_t       pNS16550;
568  setRegister_f  setReg;
569
570  pNS16550 = Console_Port_Tbl[minor].ulCtrlPort1;
571  setReg   = Console_Port_Tbl[minor].setRegister;
572
573  (*setReg)(pNS16550, NS16550_INTERRUPT_ENABLE, mask);
574}
575
576/*
577 *  ns16550_initialize_interrupts
578 *
579 *  This routine initializes the port to operate in interrupt driver mode.
580 */
581
582#if defined(__PPC__)
583#ifdef _OLD_EXCEPTIONS
584NS16550_STATIC void ns16550_initialize_interrupts(int minor)
585{
586  ns16550_init(minor);
587
588  Console_Port_Data[minor].bActive = FALSE;
589
590  set_vector(ns16550_isr, Console_Port_Tbl[minor].ulIntVector, 1);
591 
592  ns16550_enable_interrupts(minor, NS16550_ENABLE_ALL_INTR);
593}
594#else
595
596static void null_fun(){}
597
598NS16550_STATIC void ns16550_initialize_interrupts(int minor)
599{
600#ifdef BSP_SHARED_HANDLER_SUPPORT
601  rtems_irq_connect_data IrqData = {0,
602                                    ns16550_isr,
603                                    &Console_Port_Data[minor],
604                                    (rtems_irq_enable)null_fun,
605                                    (rtems_irq_disable)null_fun,
606                                    (rtems_irq_is_enabled)null_fun,
607                                    NULL
608                                   };
609#else
610  rtems_irq_connect_data IrqData = {0,
611                                    ns16550_isr,
612                                    &Console_Port_Data[minor],
613                                    (rtems_irq_enable)null_fun,
614                                    (rtems_irq_disable)null_fun,
615                                    (rtems_irq_is_enabled)null_fun
616                                   };
617#endif
618
619  ns16550_init(minor);
620
621  Console_Port_Data[minor].bActive = FALSE;
622
623  IrqData.name  = (rtems_irq_number)(Console_Port_Tbl[minor].ulIntVector );
624
625#ifdef BSP_SHARED_HANDLER_SUPPORT
626  if (!BSP_install_rtems_shared_irq_handler (&IrqData)) {
627#else
628  if (!BSP_install_rtems_irq_handler(&IrqData)) {
629#endif
630    printk("Error installing interrupt handler!\n");
631    rtems_fatal_error_occurred(1);
632  }
633
634  ns16550_enable_interrupts(minor, NS16550_ENABLE_ALL_INTR);
635}
636
637#endif
638#endif
639
640/*
641 *  ns16550_write_support_int
642 *
643 *  Console Termios output entry point.
644 */
645
646NS16550_STATIC int ns16550_write_support_int(
647  int   minor,
648  const char *buf,
649  int   len
650)
651{
652  uint32_t       Irql;
653  uint32_t       pNS16550;
654  setRegister_f  setReg;
655
656  setReg   = Console_Port_Tbl[minor].setRegister;
657  pNS16550 = Console_Port_Tbl[minor].ulCtrlPort1;
658
659  /*
660   *  We are using interrupt driven output and termios only sends us
661   *  one character at a time.
662   */
663
664  if ( !len )
665    return 0;
666
667  if(Console_Port_Tbl[minor].pDeviceFlow != &ns16550_flow_RTSCTS) {
668    ns16550_assert_RTS(minor);
669  }
670
671  rtems_interrupt_disable(Irql);
672    if ( Console_Port_Data[minor].bActive == FALSE) {
673      Console_Port_Data[minor].bActive = TRUE;
674      ns16550_enable_interrupts(minor, NS16550_ENABLE_ALL_INTR);
675    }
676    (*setReg)(pNS16550, NS16550_TRANSMIT_BUFFER, *buf);
677  rtems_interrupt_enable(Irql);
678
679  return 1;
680}
681
682/*
683 *  ns16550_write_support_polled
684 *
685 *  Console Termios output entry point.
686 *
687 */
688
689NS16550_STATIC int ns16550_write_support_polled(
690  int         minor,
691  const char *buf,
692  int         len
693)
694{
695  int nwrite = 0;
696
697  /*
698   * poll each byte in the string out of the port.
699   */
700  while (nwrite < len) {
701    /*
702     * transmit character
703     */
704    ns16550_write_polled(minor, *buf++);
705    nwrite++;
706  }
707
708  /*
709   * return the number of bytes written.
710   */
711  return nwrite;
712}
713
714/*
715 *  ns16550_inbyte_nonblocking_polled
716 *
717 *  Console Termios polling input entry point.
718 */
719
720NS16550_STATIC int ns16550_inbyte_nonblocking_polled(
721  int minor
722)
723{
724  uint32_t             pNS16550;
725  unsigned char        ucLineStatus;
726  char                 cChar;
727  getRegister_f        getReg;
728
729  pNS16550 = Console_Port_Tbl[minor].ulCtrlPort1;
730  getReg   = Console_Port_Tbl[minor].getRegister;
731
732  ucLineStatus = (*getReg)(pNS16550, NS16550_LINE_STATUS);
733  if(ucLineStatus & SP_LSR_RDY) {
734    cChar = (*getReg)(pNS16550, NS16550_RECEIVE_BUFFER);
735    return (int)cChar;
736  } else {
737    return -1;
738  }
739}
Note: See TracBrowser for help on using the repository browser.