source: rtems/c/src/libchip/serial/ns16550.c @ 8c0cab0

4.104.11
Last change on this file since 8c0cab0 was 8c0cab0, checked in by Joel Sherrill <joel.sherrill@…>, on Sep 23, 2008 at 2:07:19 PM

2008-09-23 Sebastian Huber <sebastian.huber@…>

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