source: rtems/c/src/lib/libchip/serial/ns16550.c @ d1ee44e

4.104.114.84.95
Last change on this file since d1ee44e was 07a3253d, checked in by Joel Sherrill <joel.sherrill@…>, on 11/23/98 at 19:07:58

Added base version of file system infrastructure. This includes a major
overhaul of the RTEMS system call interface. This base file system is
the "In-Memory File System" aka IMFS.

The design and implementation was done by the following people:

+ Joel Sherrill (joel@…)
+ Jennifer Averett (jennifer@…)
+ Steve "Mr Mount" Salitasc (salitasc@…)
+ Kerwin Wade (wade@…)

PROBLEMS
========

+ It is VERY likely that merging this will break the UNIX port. This

can/will be fixed.

+ There is likely some reentrancy/mutual exclusion needed.

+ Eventually, there should be a "mini-IMFS" description table to

eliminate links, symlinks, etc to save memory. All you need to
have "classic RTEMS" functionality is technically directories
and device IO. All the rest could be left out to save memory.

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