source: rtems/c/src/libchip/serial/ns16550.c @ 72798ee

4.104.115
Last change on this file since 72798ee was 95ef82c, checked in by Joel Sherrill <joel.sherrill@…>, on 09/22/08 at 21:51:15

2008-09-22 Joel Sherrill <joel.sherrill@…>

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