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

4.104.114.84.95
Last change on this file since 235d9e0 was 235d9e0, checked in by Joel Sherrill <joel.sherrill@…>, on 10/05/99 at 20:59:53

Invalid value used for baud rate per bug report from
Jay Kulpinski <jskulpin@…>.

  • 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 = NS16550_Baud(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.