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

4.104.115
Last change on this file since f7cb3cf was 9151ec6, checked in by Thomas Doerfler <Thomas.Doerfler@…>, on 11/18/08 at 09:20:18

libchip/serial/ns16550.c: Transmit the character in the polled write
function within a critical section for printk() compatibility.

  • Property mode set to 100644
File size: 17.4 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 * @brief Polled write for NS16550.
201 */
202NS16550_STATIC void ns16550_write_polled( int minor, char c)
203{
204  uint32_t port = Console_Port_Tbl [minor].ulCtrlPort1;
205  getRegister_f get = Console_Port_Tbl [minor].getRegister;
206  setRegister_f set = Console_Port_Tbl [minor].setRegister;
207  uint32_t status;
208  rtems_interrupt_level level;
209
210  while (1) {
211    /* Try to transmit the character in a critical section */
212    rtems_interrupt_disable( level);
213
214    /* Read the transmitter holding register and check it */
215    status = get( port, NS16550_LINE_STATUS);
216    if ((status & SP_LSR_THOLD) != 0) {
217      /* Transmit character */
218      set( port, NS16550_TRANSMIT_BUFFER, c);
219
220      /* Finished */
221      rtems_interrupt_enable( level);
222      break;
223    } else {
224      rtems_interrupt_enable( level);
225    }
226
227    /* Wait for transmitter holding register to be empty */
228    do {
229      status = get( port, NS16550_LINE_STATUS);
230    } while ((status & SP_LSR_THOLD) == 0);
231  }
232}
233
234/*
235 * These routines provide control of the RTS and DTR lines
236 */
237
238/*
239 *  ns16550_assert_RTS
240 */
241
242NS16550_STATIC int ns16550_assert_RTS(int minor)
243{
244  uint32_t                pNS16550;
245  uint32_t                Irql;
246  ns16550_context        *pns16550Context;
247  setRegister_f           setReg;
248
249  pns16550Context=(ns16550_context *) Console_Port_Data[minor].pDeviceContext;
250
251  pNS16550 = Console_Port_Tbl[minor].ulCtrlPort1;
252  setReg   = Console_Port_Tbl[minor].setRegister;
253
254  /*
255   * Assert RTS
256   */
257  rtems_interrupt_disable(Irql);
258  pns16550Context->ucModemCtrl|=SP_MODEM_RTS;
259  (*setReg)(pNS16550, NS16550_MODEM_CONTROL, pns16550Context->ucModemCtrl);
260  rtems_interrupt_enable(Irql);
261  return 0;
262}
263
264/*
265 *  ns16550_negate_RTS
266 */
267
268NS16550_STATIC int ns16550_negate_RTS(int minor)
269{
270  uint32_t                pNS16550;
271  uint32_t                Irql;
272  ns16550_context        *pns16550Context;
273  setRegister_f           setReg;
274
275  pns16550Context=(ns16550_context *) Console_Port_Data[minor].pDeviceContext;
276
277  pNS16550 = Console_Port_Tbl[minor].ulCtrlPort1;
278  setReg   = Console_Port_Tbl[minor].setRegister;
279
280  /*
281   * Negate RTS
282   */
283  rtems_interrupt_disable(Irql);
284  pns16550Context->ucModemCtrl&=~SP_MODEM_RTS;
285  (*setReg)(pNS16550, NS16550_MODEM_CONTROL, pns16550Context->ucModemCtrl);
286  rtems_interrupt_enable(Irql);
287  return 0;
288}
289
290/*
291 * These flow control routines utilise a connection from the local DTR
292 * line to the remote CTS line
293 */
294
295/*
296 *  ns16550_assert_DTR
297 */
298
299NS16550_STATIC int ns16550_assert_DTR(int minor)
300{
301  uint32_t                pNS16550;
302  uint32_t                Irql;
303  ns16550_context        *pns16550Context;
304  setRegister_f           setReg;
305
306  pns16550Context=(ns16550_context *) Console_Port_Data[minor].pDeviceContext;
307
308  pNS16550 = Console_Port_Tbl[minor].ulCtrlPort1;
309  setReg   = Console_Port_Tbl[minor].setRegister;
310
311  /*
312   * Assert DTR
313   */
314  rtems_interrupt_disable(Irql);
315  pns16550Context->ucModemCtrl|=SP_MODEM_DTR;
316  (*setReg)(pNS16550, NS16550_MODEM_CONTROL, pns16550Context->ucModemCtrl);
317  rtems_interrupt_enable(Irql);
318  return 0;
319}
320
321/*
322 *  ns16550_negate_DTR
323 */
324
325NS16550_STATIC int ns16550_negate_DTR(int minor)
326{
327  uint32_t                pNS16550;
328  uint32_t                Irql;
329  ns16550_context        *pns16550Context;
330  setRegister_f           setReg;
331
332  pns16550Context=(ns16550_context *) Console_Port_Data[minor].pDeviceContext;
333
334  pNS16550 = Console_Port_Tbl[minor].ulCtrlPort1;
335  setReg   = Console_Port_Tbl[minor].setRegister;
336
337  /*
338   * Negate DTR
339   */
340  rtems_interrupt_disable(Irql);
341  pns16550Context->ucModemCtrl&=~SP_MODEM_DTR;
342  (*setReg)(pNS16550, NS16550_MODEM_CONTROL,pns16550Context->ucModemCtrl);
343  rtems_interrupt_enable(Irql);
344  return 0;
345}
346
347/*
348 *  ns16550_set_attributes
349 *
350 *  This function sets the channel to reflect the requested termios
351 *  port settings.
352 */
353
354NS16550_STATIC int ns16550_set_attributes(
355  int                   minor,
356  const struct termios *t
357)
358{
359  uint32_t                pNS16550;
360  uint32_t                ulBaudDivisor;
361  uint8_t                 ucLineControl;
362  uint32_t                baud_requested;
363  setRegister_f           setReg;
364  getRegister_f           getReg;
365  uint32_t                Irql;
366
367  pNS16550 = Console_Port_Tbl[minor].ulCtrlPort1;
368  setReg   = Console_Port_Tbl[minor].setRegister;
369  getReg   = Console_Port_Tbl[minor].getRegister;
370
371  /*
372   *  Calculate the baud rate divisor
373   */
374
375  baud_requested = t->c_cflag & CBAUD;
376  if (!baud_requested)
377    baud_requested = B9600;              /* default to 9600 baud */
378
379  ulBaudDivisor = NS16550_Baud(
380    (uint32_t) Console_Port_Tbl[minor].ulClock,
381    termios_baud_to_number(baud_requested)
382  );
383
384  ucLineControl = 0;
385
386  /*
387   *  Parity
388   */
389
390  if (t->c_cflag & PARENB) {
391    ucLineControl |= SP_LINE_PAR;
392    if (!(t->c_cflag & PARODD))
393      ucLineControl |= SP_LINE_ODD;
394  }
395
396  /*
397   *  Character Size
398   */
399
400  if (t->c_cflag & CSIZE) {
401    switch (t->c_cflag & CSIZE) {
402      case CS5:  ucLineControl |= FIVE_BITS;  break;
403      case CS6:  ucLineControl |= SIX_BITS;   break;
404      case CS7:  ucLineControl |= SEVEN_BITS; break;
405      case CS8:  ucLineControl |= EIGHT_BITS; break;
406    }
407  } else {
408    ucLineControl |= EIGHT_BITS;               /* default to 9600,8,N,1 */
409  }
410
411  /*
412   *  Stop Bits
413   */
414
415  if (t->c_cflag & CSTOPB) {
416    ucLineControl |= SP_LINE_STOP;              /* 2 stop bits */
417  } else {
418    ;                                           /* 1 stop bit */
419  }
420
421  /*
422   *  Now actually set the chip
423   */
424
425  rtems_interrupt_disable(Irql);
426
427    /*
428     *  Set the baud rate
429     */
430
431    (*setReg)(pNS16550, NS16550_LINE_CONTROL, SP_LINE_DLAB);
432    /* XXX are these registers right? */
433    (*setReg)(pNS16550, NS16550_TRANSMIT_BUFFER, ulBaudDivisor&0xff);
434    (*setReg)(pNS16550, NS16550_INTERRUPT_ENABLE, (ulBaudDivisor>>8)&0xff);
435
436    /*
437     *  Now write the line control
438     */
439    (*setReg)(pNS16550, NS16550_LINE_CONTROL, ucLineControl );
440
441  rtems_interrupt_enable(Irql);
442
443  return 0;
444}
445
446/*
447 *  ns16550_process
448 *
449 *  This routine is the console interrupt handler for A port.
450 */
451
452NS16550_STATIC void ns16550_process( int minor)
453{
454  console_tbl *c = &Console_Port_Tbl [minor];
455  console_data *d = &Console_Port_Data [minor];
456
457  uint32_t                pNS16550;
458  volatile uint8_t        ucLineStatus;
459  volatile uint8_t        ucInterruptId;
460  char                    cChar;
461  getRegister_f           getReg;
462  setRegister_f           setReg;
463
464  pNS16550 = c->ulCtrlPort1;
465  getReg   = c->getRegister;
466  setReg   = c->setRegister;
467
468  do {
469    /*
470     * Deal with any received characters
471     */
472    while (true) {
473      ucLineStatus = (*getReg)(pNS16550, NS16550_LINE_STATUS);
474      if (~ucLineStatus & SP_LSR_RDY) {
475        break;
476      }
477      cChar = (*getReg)(pNS16550, NS16550_RECEIVE_BUFFER);
478      rtems_termios_enqueue_raw_characters(
479        d->termios_data,
480        &cChar,
481        1
482      );
483    }
484
485    /*
486     *  TX all the characters we can
487     */
488
489    while (true) {
490      ucLineStatus = (*getReg)(pNS16550, NS16550_LINE_STATUS);
491      if (~ucLineStatus & SP_LSR_THOLD) {
492        /*
493         * We'll get another interrupt when
494         * the transmitter holding reg. becomes
495         * free again
496         */
497        break;
498      }
499
500      if (rtems_termios_dequeue_characters( d->termios_data, 1) == 0) {
501        if (c->pDeviceFlow != &ns16550_flow_RTSCTS) {
502          ns16550_negate_RTS(minor);
503        }
504        d->bActive = false;
505        ns16550_enable_interrupts(minor, NS16550_ENABLE_ALL_INTR_EXCEPT_TX);
506        break;
507      }
508    }
509
510    ucInterruptId = (*getReg)(pNS16550, NS16550_INTERRUPT_ID);
511  } while((ucInterruptId&0xf)!=0x1);
512}
513
514/*
515 *  ns16550_enable_interrupts
516 *
517 *  This routine initializes the port to have the specified interrupts masked.
518 */
519
520NS16550_STATIC void ns16550_enable_interrupts(
521  int minor,
522  int mask
523)
524{
525  uint32_t       pNS16550;
526  setRegister_f  setReg;
527
528  pNS16550 = Console_Port_Tbl[minor].ulCtrlPort1;
529  setReg   = Console_Port_Tbl[minor].setRegister;
530
531  (*setReg)(pNS16550, NS16550_INTERRUPT_ENABLE, mask);
532}
533
534#ifdef BSP_FEATURE_IRQ_EXTENSION
535  NS16550_STATIC void ns16550_isr( rtems_vector_number vector, void *arg)
536  {
537    int minor = (int) arg;
538
539    ns16550_process( minor);
540  }
541#elif defined BSP_FEATURE_IRQ_LEGACY
542  NS16550_STATIC rtems_isr ns16550_isr( void *arg)
543  {
544    int minor = (int) arg;
545
546    ns16550_process( minor);
547  }
548#endif
549
550/*
551 *  ns16550_initialize_interrupts
552 *
553 *  This routine initializes the port to operate in interrupt driver mode.
554 */
555NS16550_STATIC void ns16550_initialize_interrupts( int minor)
556{
557  console_tbl *c = &Console_Port_Tbl [minor];
558  console_data *d = &Console_Port_Data [minor];
559
560  ns16550_init( minor);
561
562  d->bActive = false;
563
564  #ifdef BSP_FEATURE_IRQ_EXTENSION
565    {
566      rtems_status_code sc = RTEMS_SUCCESSFUL;
567      sc = rtems_interrupt_handler_install(
568        c->ulIntVector,
569        "NS16550",
570        RTEMS_INTERRUPT_SHARED,
571        ns16550_isr,
572        (void *) minor
573      );
574      if (sc != RTEMS_SUCCESSFUL) {
575        /* FIXME */
576        printk( "%s: Error: Install interrupt handler\n", __func__);
577        rtems_fatal_error_occurred( 0xdeadbeef);
578      }
579    }
580  #elif defined BSP_FEATURE_IRQ_LEGACY
581    {
582      int rv = 0;
583      #ifdef BSP_FEATURE_IRQ_LEGACY_SHARED_HANDLER_SUPPORT
584        rtems_irq_connect_data cd = {
585          c->ulIntVector,
586          ns16550_isr,
587          (void *) minor,
588          NULL,
589          NULL,
590          NULL,
591          NULL
592        };
593        rv = BSP_install_rtems_shared_irq_handler( &cd);
594      #else
595        rtems_irq_connect_data cd = {
596          c->ulIntVector,
597          ns16550_isr,
598          (void *) minor,
599          NULL,
600          NULL,
601          NULL
602        };
603        rv = BSP_install_rtems_irq_handler( &cd);
604      #endif
605      if (rv == 0) {
606        /* FIXME */
607        printk( "%s: Error: Install interrupt handler\n", __func__);
608        rtems_fatal_error_occurred( 0xdeadbeef);
609      }
610    }
611  #endif
612 
613  ns16550_enable_interrupts( minor, NS16550_ENABLE_ALL_INTR_EXCEPT_TX);
614}
615
616/*
617 *  ns16550_write_support_int
618 *
619 *  Console Termios output entry point.
620 */
621
622NS16550_STATIC int ns16550_write_support_int(
623  int   minor,
624  const char *buf,
625  int   len
626)
627{
628  uint32_t       Irql;
629  uint32_t       pNS16550;
630  setRegister_f  setReg;
631
632  setReg   = Console_Port_Tbl[minor].setRegister;
633  pNS16550 = Console_Port_Tbl[minor].ulCtrlPort1;
634
635  /*
636   *  We are using interrupt driven output and termios only sends us
637   *  one character at a time.
638   */
639
640  if ( !len )
641    return 0;
642
643  if(Console_Port_Tbl[minor].pDeviceFlow != &ns16550_flow_RTSCTS) {
644    ns16550_assert_RTS(minor);
645  }
646
647  rtems_interrupt_disable(Irql);
648    if ( Console_Port_Data[minor].bActive == false) {
649      Console_Port_Data[minor].bActive = true;
650      ns16550_enable_interrupts(minor, NS16550_ENABLE_ALL_INTR);
651    }
652    (*setReg)(pNS16550, NS16550_TRANSMIT_BUFFER, *buf);
653  rtems_interrupt_enable(Irql);
654
655  return 1;
656}
657
658/*
659 *  ns16550_write_support_polled
660 *
661 *  Console Termios output entry point.
662 *
663 */
664
665NS16550_STATIC int ns16550_write_support_polled(
666  int         minor,
667  const char *buf,
668  int         len
669)
670{
671  int nwrite = 0;
672
673  /*
674   * poll each byte in the string out of the port.
675   */
676  while (nwrite < len) {
677    /*
678     * transmit character
679     */
680    ns16550_write_polled(minor, *buf++);
681    nwrite++;
682  }
683
684  /*
685   * return the number of bytes written.
686   */
687  return nwrite;
688}
689
690/*
691 *  ns16550_inbyte_nonblocking_polled
692 *
693 *  Console Termios polling input entry point.
694 */
695
696NS16550_STATIC int ns16550_inbyte_nonblocking_polled(
697  int minor
698)
699{
700  uint32_t             pNS16550;
701  unsigned char        ucLineStatus;
702  char                 cChar;
703  getRegister_f        getReg;
704
705  pNS16550 = Console_Port_Tbl[minor].ulCtrlPort1;
706  getReg   = Console_Port_Tbl[minor].getRegister;
707
708  ucLineStatus = (*getReg)(pNS16550, NS16550_LINE_STATUS);
709  if(ucLineStatus & SP_LSR_RDY) {
710    cChar = (*getReg)(pNS16550, NS16550_RECEIVE_BUFFER);
711    return (int)cChar;
712  } else {
713    return -1;
714  }
715}
Note: See TracBrowser for help on using the repository browser.