source: rtems/c/src/libchip/serial/ns16550.c @ cb1b4060

4.104.115
Last change on this file since cb1b4060 was 69be08ee, checked in by Ralf Corsepius <ralf.corsepius@…>, on 12/17/08 at 12:48:04

2008-12-17 Ralf Corsépius <ralf.corsepius@…>

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