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

4.104.114.84.95
Last change on this file since 692b9f7 was 692b9f7, checked in by Joel Sherrill <joel.sherrill@…>, on 10/28/98 at 19:17:16

Merged Vista SCORE603e, Radstone PPCn_60x, and DY-4 DMV177 BSPs along
with libchip.

  • Property mode set to 100644
File size: 15.9 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}
428
429/*
430 *  ns16550_process
431 *
432 *  This routine is the console interrupt handler for A port.
433 */
434
435NS16550_STATIC void ns16550_process(
436        int             minor
437)
438{
439  unsigned32              pNS16550;
440  volatile unsigned8      ucLineStatus;
441  volatile unsigned8      ucInterruptId;
442  unsigned char           cChar;
443  getRegister_f           getReg;
444  setRegister_f           setReg;
445
446  pNS16550 = Console_Port_Tbl[minor].ulCtrlPort1;
447  getReg   = Console_Port_Tbl[minor].getRegister;
448  setReg   = Console_Port_Tbl[minor].setRegister;
449
450  do {
451    /*
452     * Deal with any received characters
453     */
454    while(TRUE) {
455      ucLineStatus = (*getReg)(pNS16550, NS16550_LINE_STATUS);
456      if(~ucLineStatus & SP_LSR_RDY) {
457        break;
458      }
459      cChar = (*getReg)(pNS16550, NS16550_RECEIVE_BUFFER);
460      rtems_termios_enqueue_raw_characters(
461        Console_Port_Data[minor].termios_data,
462        &cChar,
463        1
464      );
465    }
466
467    /*
468     *  TX all the characters we can
469     */
470
471    while(TRUE) {
472        ucLineStatus = (*getReg)(pNS16550, NS16550_LINE_STATUS);
473        if(~ucLineStatus & SP_LSR_THOLD) {
474          /*
475           * We'll get another interrupt when
476           * the transmitter holding reg. becomes
477           * free again
478           */
479          break;
480        }
481
482#if 0
483        /* XXX flow control not completely supported in libchip */
484
485        if(Console_Port_Tbl[minor].pDeviceFlow != &ns16550_flow_RTSCTS) {
486          ns16550_negate_RTS(minor);
487        }
488#endif
489
490    rtems_termios_dequeue_characters(Console_Port_Data[minor].termios_data, 1);
491    if (rtems_termios_dequeue_characters(
492         Console_Port_Data[minor].termios_data, 1)) {
493        if (Console_Port_Tbl[minor].pDeviceFlow != &ns16550_flow_RTSCTS) {
494          ns16550_negate_RTS(minor);
495        }
496        Console_Port_Data[minor].bActive = FALSE;
497        ns16550_enable_interrupts(minor, NS16550_ENABLE_ALL_INTR_EXCEPT_TX);
498        break;
499      }
500
501      ucInterruptId = (*getReg)(pNS16550, NS16550_INTERRUPT_ID);
502    }
503  } while((ucInterruptId&0xf)!=0x1);
504}
505
506/*
507 *  ns16550_isr
508 */
509
510NS16550_STATIC rtems_isr ns16550_isr(
511  rtems_vector_number vector
512)
513{
514  int     minor;
515
516  for(minor=0;minor<Console_Port_Count;minor++) {
517    if(Console_Port_Tbl[minor].ulIntVector == vector &&
518       Console_Port_Tbl[minor].deviceType == SERIAL_NS16550 ) {
519      ns16550_process(minor);
520    }
521  }
522}
523
524/*
525 *  ns16550_enable_interrupts
526 *
527 *  This routine initializes the port to have the specified interrupts masked.
528 */
529
530NS16550_STATIC void ns16550_enable_interrupts(
531  int minor,
532  int mask
533)
534{
535  unsigned32     pNS16550;
536  setRegister_f  setReg;
537
538  pNS16550 = Console_Port_Tbl[minor].ulCtrlPort1;
539  setReg   = Console_Port_Tbl[minor].setRegister;
540
541  (*setReg)(pNS16550, NS16550_INTERRUPT_ENABLE, mask);
542}
543
544/*
545 *  ns16550_initialize_interrupts
546 *
547 *  This routine initializes the port to operate in interrupt driver mode.
548 */
549
550NS16550_STATIC void ns16550_initialize_interrupts(int minor)
551{
552  ns16550_init(minor);
553
554  Console_Port_Data[minor].bActive = FALSE;
555
556  set_vector(ns16550_isr, Console_Port_Tbl[minor].ulIntVector, 1);
557
558  ns16550_enable_interrupts(minor, NS16550_ENABLE_ALL_INTR);
559}
560
561/*
562 *  ns16550_write_support_int
563 *
564 *  Console Termios output entry point.
565 */
566
567NS16550_STATIC int ns16550_write_support_int(
568  int   minor,
569  const char *buf,
570  int   len
571)
572{
573  unsigned32     Irql;
574  unsigned32     pNS16550;
575  setRegister_f  setReg;
576
577  setReg   = Console_Port_Tbl[minor].setRegister;
578  pNS16550 = Console_Port_Tbl[minor].ulCtrlPort1;
579
580  /*
581   *  We are using interrupt driven output and termios only sends us
582   *  one character at a time.
583   */
584
585  if ( !len )
586    return 0;
587
588  if(Console_Port_Tbl[minor].pDeviceFlow != &ns16550_flow_RTSCTS) {
589    ns16550_assert_RTS(minor);
590  }
591
592  rtems_interrupt_disable(Irql);
593    if ( Console_Port_Data[minor].bActive == FALSE) {
594      Console_Port_Data[minor].bActive = TRUE;
595      ns16550_enable_interrupts(minor, NS16550_ENABLE_ALL_INTR);
596    }
597    (*setReg)(pNS16550, NS16550_TRANSMIT_BUFFER, *buf);
598  rtems_interrupt_enable(Irql);
599
600  return 1;
601}
602
603/*
604 *  ns16550_write_support_polled
605 *
606 *  Console Termios output entry point.
607 *
608 */
609
610NS16550_STATIC int ns16550_write_support_polled(
611  int         minor,
612  const char *buf,
613  int         len
614)
615{
616  int nwrite = 0;
617
618  /*
619   * poll each byte in the string out of the port.
620   */
621  while (nwrite < len) {
622    /*
623     * transmit character
624     */
625    ns16550_write_polled(minor, *buf++);
626    nwrite++;
627  }
628
629  /*
630   * return the number of bytes written.
631   */
632  return nwrite;
633}
634
635/*
636 *  ns16550_inbyte_nonblocking_polled
637 *
638 *  Console Termios polling input entry point.
639 */
640
641NS16550_STATIC int ns16550_inbyte_nonblocking_polled(
642  int minor
643)
644{
645  unsigned32           pNS16550;
646  unsigned char        ucLineStatus;
647  char                 cChar;
648  getRegister_f        getReg;
649
650  pNS16550 = Console_Port_Tbl[minor].ulCtrlPort1;
651  getReg   = Console_Port_Tbl[minor].getRegister;
652
653  ucLineStatus = (*getReg)(pNS16550, NS16550_LINE_STATUS);
654  if(ucLineStatus & SP_LSR_RDY) {
655    cChar = (*getReg)(pNS16550, NS16550_RECEIVE_BUFFER);
656    return (int)cChar;
657  } else {
658    return -1;
659  }
660}
Note: See TracBrowser for help on using the repository browser.