source: rtems/c/src/libchip/serial/ns16550.c @ 4cd4c5a

4.104.114.84.95
Last change on this file since 4cd4c5a was 4cd4c5a, checked in by Joel Sherrill <joel.sherrill@…>, on 06/22/98 at 11:07:51

Moved set and get Register and Data type defininitions to serial.h.

Cleaned up spacing.

  • Property mode set to 100644
File size: 14.6 KB
RevLine 
[0737710]1/*
[aa0da6b]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.
[0737710]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>
[ee3b242b]26#include <ringbuf.h>
[0737710]27
[ee3b242b]28#include <libchip/serial.h>
[0737710]29#include "ns16550_p.h"
30
31/*
32 * Flow control is only supported when using interrupts
33 */
34console_flow ns16550_flow_RTSCTS =
35{
36  ns16550_negate_RTS,             /* deviceStopRemoteTx */
37  ns16550_assert_RTS              /* deviceStartRemoteTx */
38};
39
40console_flow ns16550_flow_DTRCTS =
41{
42  ns16550_negate_DTR,             /* deviceStopRemoteTx */
43  ns16550_assert_DTR              /* deviceStartRemoteTx */
44};
45
46console_fns ns16550_fns =
47{
48  ns16550_probe,                  /* deviceProbe */
49  ns16550_open,                   /* deviceFirstOpen */
50  ns16550_flush,                  /* deviceLastClose */
51  NULL,                           /* deviceRead */
52  ns16550_write_support_int,      /* deviceWrite */
53  ns16550_initialize_interrupts,  /* deviceInitialize */
54  ns16550_write_polled,           /* deviceWritePolled */
55  FALSE,                          /* deviceOutputUsesInterrupts */
56};
57
58console_fns ns16550_fns_polled =
59{
60  ns16550_probe,                       /* deviceProbe */
61  ns16550_open,                        /* deviceFirstOpen */
62  ns16550_close,                       /* deviceLastClose */
63  ns16550_inbyte_nonblocking_polled,   /* deviceRead */
64  ns16550_write_support_polled,        /* deviceWrite */
65  ns16550_init,                        /* deviceInitialize */
66  ns16550_write_polled,                /* deviceWritePolled */
67  FALSE,                               /* deviceOutputUsesInterrupts */
68};
69
70extern void set_vector( rtems_isr_entry, rtems_vector_number, int );
71
72/*
73 *  Console Device Driver Entry Points
74 */
75static boolean ns16550_probe(int minor)
76{
77  /*
[4cd4c5a]78   * If the configuration dependent probe has located the device then
[0737710]79   * assume it is there
80   */
81  return(TRUE);
82}
83
84static void ns16550_init(int minor)
85{
86  unsigned32              pNS16550;
87  unsigned8               ucTrash;
88  unsigned8               ucDataByte;
89  unsigned32              ulBaudDivisor;
90  ns16550_context        *pns16550Context;
91  setRegister_f           setReg;
92  getRegister_f           getReg;
93
94  pns16550Context=(ns16550_context *)malloc(sizeof(ns16550_context));
95
96  Console_Port_Data[minor].pDeviceContext=(void *)pns16550Context;
97  pns16550Context->ucModemCtrl=SP_MODEM_IRQ;
98
99  pNS16550 = Console_Port_Tbl[minor].ulCtrlPort1;
100  setReg   = Console_Port_Tbl[minor].setRegister;
101  getReg   = Console_Port_Tbl[minor].getRegister;
102
103  /* Clear the divisor latch, clear all interrupt enables,
104   * and reset and
105   * disable the FIFO's.
106   */
107
108  (*setReg)(pNS16550, NS16550_LINE_CONTROL, 0x0);
109  (*setReg)(pNS16550, NS16550_INTERRUPT_ENABLE, 0x0);
110
111  /* Set the divisor latch and set the baud rate. */
112
113  ulBaudDivisor=NS16550_Baud((unsigned32)Console_Port_Tbl[minor].pDeviceParams);
114  ucDataByte = SP_LINE_DLAB;
115  (*setReg)(pNS16550, NS16550_LINE_CONTROL, ucDataByte);
116  (*setReg)(pNS16550, NS16550_TRANSMIT_BUFFER, ulBaudDivisor&0xff);
117  (*setReg)(pNS16550, NS16550_INTERRUPT_ENABLE, (ulBaudDivisor>>8)&0xff);
118
119  /* Clear the divisor latch and set the character size to eight bits */
120  /* with one stop bit and no parity checking. */
121  ucDataByte = EIGHT_BITS;
122  (*setReg)(pNS16550, NS16550_LINE_CONTROL, ucDataByte);
123
124  /* Enable and reset transmit and receive FIFOs. TJA     */
125  ucDataByte = SP_FIFO_ENABLE;
126  (*setReg)(pNS16550, NS16550_FIFO_CONTROL, ucDataByte);
127
128  ucDataByte = SP_FIFO_ENABLE | SP_FIFO_RXRST | SP_FIFO_TXRST;
129  (*setReg)(pNS16550, NS16550_FIFO_CONTROL, ucDataByte);
130
131  /*
132   * Disable interrupts
133   */
134  ucDataByte = 0;
135  (*setReg)(pNS16550, NS16550_INTERRUPT_ENABLE, ucDataByte);
136
137  /* Set data terminal ready. */
138  /* And open interrupt tristate line */
139  (*setReg)(pNS16550, NS16550_MODEM_CONTROL,pns16550Context->ucModemCtrl);
140
141  ucTrash = (*getReg)(pNS16550, NS16550_LINE_STATUS );
142  ucTrash = (*getReg)(pNS16550, NS16550_RECEIVE_BUFFER );
143}
144
145static int ns16550_open(
146  int      major,
147  int      minor,
148  void    * arg
149)
150{
151  /*
152   * Assert DTR
153   */
154
155  if(Console_Port_Tbl[minor].pDeviceFlow != &ns16550_flow_DTRCTS) {
156    ns16550_assert_DTR(minor);
157  }
158
159  return(RTEMS_SUCCESSFUL);
160}
161
162static int ns16550_close(
163  int      major,
164  int      minor,
165  void    * arg
166)
167{
168  /*
169   * Negate DTR
170   */
171  if(Console_Port_Tbl[minor].pDeviceFlow != &ns16550_flow_DTRCTS) {
172    ns16550_negate_DTR(minor);
173  }
174
175  return(RTEMS_SUCCESSFUL);
176}
177
178/*
179 *  ns16550_write_polled
180 */
181static void ns16550_write_polled(
182  int   minor,
183  char  cChar
184)
185{
186  unsigned32              pNS16550;
187  unsigned char           ucLineStatus;
188  int                     iTimeout;
189  getRegister_f           getReg;
190  setRegister_f           setReg;
191
192  pNS16550 = Console_Port_Tbl[minor].ulCtrlPort1;
193  getReg   = Console_Port_Tbl[minor].getRegister;
194  setReg   = Console_Port_Tbl[minor].setRegister;
195
196  /*
197   * wait for transmitter holding register to be empty
198   */
199  iTimeout=1000;
200  ucLineStatus = (*getReg)(pNS16550, NS16550_LINE_STATUS);
201  while ((ucLineStatus & SP_LSR_THOLD) == 0) {
202    /*
203     * Yield while we wait
204     */
205     if(_System_state_Is_up(_System_state_Get())) {
206       rtems_task_wake_after(RTEMS_YIELD_PROCESSOR);
207     }
208     ucLineStatus = (*getReg)(pNS16550, NS16550_LINE_STATUS);
209     if(!--iTimeout) {
210       break;
211     }
212  }
213
214  /*
215   * transmit character
216   */
217  (*setReg)(pNS16550, NS16550_TRANSMIT_BUFFER, cChar);
218}
219
220/*
221 * These routines provide control of the RTS and DTR lines
222 */
223/*
224 *  ns16550_assert_RTS
225 */
226static int ns16550_assert_RTS(int minor)
227{
228  unsigned32              pNS16550;
229  unsigned32              Irql;
230  ns16550_context        *pns16550Context;
231  setRegister_f           setReg;
232
233  pns16550Context=(ns16550_context *) Console_Port_Data[minor].pDeviceContext;
234
235  pNS16550 = Console_Port_Tbl[minor].ulCtrlPort1;
236  setReg   = Console_Port_Tbl[minor].setRegister;
237
238  /*
239   * Assert RTS
240   */
241  rtems_interrupt_disable(Irql);
242  pns16550Context->ucModemCtrl|=SP_MODEM_RTS;
243  (*setReg)(pNS16550, NS16550_MODEM_CONTROL, pns16550Context->ucModemCtrl);
244  rtems_interrupt_enable(Irql);
245  return 0;
246}
247
248/*
249 *  ns16550_negate_RTS
250 */
251static int ns16550_negate_RTS(int minor)
252{
253  unsigned32              pNS16550;
254  unsigned32              Irql;
255  ns16550_context        *pns16550Context;
256  setRegister_f           setReg;
257
258  pns16550Context=(ns16550_context *) Console_Port_Data[minor].pDeviceContext;
259
260  pNS16550 = Console_Port_Tbl[minor].ulCtrlPort1;
261  setReg   = Console_Port_Tbl[minor].setRegister;
262
263  /*
264   * Negate RTS
265   */
266  rtems_interrupt_disable(Irql);
267  pns16550Context->ucModemCtrl&=~SP_MODEM_RTS;
268  (*setReg)(pNS16550, NS16550_MODEM_CONTROL, pns16550Context->ucModemCtrl);
269  rtems_interrupt_enable(Irql);
270  return 0;
271}
272
273/*
274 * These flow control routines utilise a connection from the local DTR
275 * line to the remote CTS line
276 */
277/*
278 *  ns16550_assert_DTR
279 */
280static int ns16550_assert_DTR(int minor)
281{
282  unsigned32              pNS16550;
283  unsigned32              Irql;
284  ns16550_context        *pns16550Context;
285  setRegister_f           setReg;
286
287  pns16550Context=(ns16550_context *) Console_Port_Data[minor].pDeviceContext;
288
289  pNS16550 = Console_Port_Tbl[minor].ulCtrlPort1;
290  setReg   = Console_Port_Tbl[minor].setRegister;
291
292  /*
293   * Assert DTR
294   */
295  rtems_interrupt_disable(Irql);
296  pns16550Context->ucModemCtrl|=SP_MODEM_DTR;
297  (*setReg)(pNS16550, NS16550_MODEM_CONTROL, pns16550Context->ucModemCtrl);
298  rtems_interrupt_enable(Irql);
299  return 0;
300}
301
302/*
303 *  ns16550_negate_DTR
304 */
305static int ns16550_negate_DTR(int minor)
306{
307  unsigned32              pNS16550;
308  unsigned32              Irql;
309  ns16550_context        *pns16550Context;
310  setRegister_f           setReg;
311
312  pns16550Context=(ns16550_context *) Console_Port_Data[minor].pDeviceContext;
313
314  pNS16550 = Console_Port_Tbl[minor].ulCtrlPort1;
315  setReg   = Console_Port_Tbl[minor].setRegister;
316
317  /*
318   * Negate DTR
319   */
320  rtems_interrupt_disable(Irql);
321  pns16550Context->ucModemCtrl&=~SP_MODEM_DTR;
322  (*setReg)(pNS16550, NS16550_MODEM_CONTROL,pns16550Context->ucModemCtrl);
323  rtems_interrupt_enable(Irql);
324  return 0;
325}
326
327/*
328 *  ns16550_isr
329 *
330 *  This routine is the console interrupt handler for COM1 and COM2
331 *
332 *  Input parameters:
333 *    vector - vector number
334 *
335 *  Output parameters: NONE
336 *
337 *  Return values:     NONE
338 */
339
340static void ns16550_process(
341        int             minor
342)
343{
344  unsigned32              pNS16550;
345  volatile unsigned8      ucLineStatus;
346  volatile unsigned8      ucInterruptId;
347  char                    cChar;
348  getRegister_f           getReg;
349  setRegister_f           setReg;
350
351  pNS16550 = Console_Port_Tbl[minor].ulCtrlPort1;
352  getReg   = Console_Port_Tbl[minor].getRegister;
353  setReg   = Console_Port_Tbl[minor].setRegister;
354
355  do {
356    /*
357     * Deal with any received characters
358     */
359    while(TRUE) {
360      ucLineStatus = (*getReg)(pNS16550, NS16550_LINE_STATUS);
361      if(~ucLineStatus & SP_LSR_RDY) {
362        break;
363      }
364      cChar = (*getReg)(pNS16550, NS16550_RECEIVE_BUFFER);
365      rtems_termios_enqueue_raw_characters(
366        Console_Port_Data[minor].termios_data,
367        &cChar,
368        1
369      );
370    }
371
372    while(TRUE) {
373      if(Ring_buffer_Is_empty(&Console_Port_Data[minor].TxBuffer)) {
374        Console_Port_Data[minor].bActive=FALSE;
375        if(Console_Port_Tbl[minor].pDeviceFlow !=&ns16550_flow_RTSCTS) {
376          ns16550_negate_RTS(minor);
377        }
378
379        /*
380         * There is no data to transmit
381         */
382        break;
383      }
384
385      ucLineStatus = (*getReg)(pNS16550, NS16550_LINE_STATUS);
386      if(~ucLineStatus & SP_LSR_THOLD) {
387        /*
388         * We'll get another interrupt when
389         * the transmitter holding reg. becomes
390         * free again
391         */
392        break;
393      }
394
395      Ring_buffer_Remove_character( &Console_Port_Data[minor].TxBuffer, cChar);
396      /*
397       * transmit character
398       */
399      (*setReg)(pNS16550, NS16550_TRANSMIT_BUFFER, cChar);
400    }
401
402    ucInterruptId = (*getReg)(pNS16550, NS16550_INTERRUPT_ID);
403  }
404  while((ucInterruptId&0xf)!=0x1);
405}
406
407static rtems_isr ns16550_isr(
408  rtems_vector_number vector
409)
410{
411  int     minor;
412
413  for(minor=0;minor<Console_Port_Count;minor++) {
414    if(vector==Console_Port_Tbl[minor].ulIntVector) {
415      ns16550_process(minor);
416    }
417  }
418}
419
420/*
421 *  ns16550_flush
422 */
423static int ns16550_flush(int major, int minor, void *arg)
424{
425  while(!Ring_buffer_Is_empty(&Console_Port_Data[minor].TxBuffer)) {
426    /*
427     * Yield while we wait
428     */
429    if(_System_state_Is_up(_System_state_Get())) {
430      rtems_task_wake_after(RTEMS_YIELD_PROCESSOR);
431    }
432  }
433
434  ns16550_close(major, minor, arg);
435
436  return(RTEMS_SUCCESSFUL);
437}
438
439/*
440 *  ns16550_initialize_interrupts
441 *
442 *  This routine initializes the console's receive and transmit
443 *  ring buffers and loads the appropriate vectors to handle the interrupts.
444 *
445 *  Input parameters:  NONE
446 *
447 *  Output parameters: NONE
448 *
449 *  Return values:     NONE
450 */
451
452static void ns16550_enable_interrupts(
453  int minor
454)
455{
456  unsigned32            pNS16550;
457  unsigned8             ucDataByte;
458  setRegister_f           setReg;
459
460  pNS16550 = Console_Port_Tbl[minor].ulCtrlPort1;
461  setReg   = Console_Port_Tbl[minor].setRegister;
462
463  /*
464   * Enable interrupts
465   */
466  ucDataByte = SP_INT_RX_ENABLE | SP_INT_TX_ENABLE;
467  (*setReg)(pNS16550, NS16550_INTERRUPT_ENABLE, ucDataByte);
468
469}
470
471static void ns16550_initialize_interrupts(int minor)
472{
473  ns16550_init(minor);
474
475  Ring_buffer_Initialize(&Console_Port_Data[minor].TxBuffer);
476
477  Console_Port_Data[minor].bActive = FALSE;
478
479  set_vector(ns16550_isr, Console_Port_Tbl[minor].ulIntVector, 1);
480
481  ns16550_enable_interrupts(minor);
482}
483
484/*
485 *  ns16550_write_support_int
486 *
487 *  Console Termios output entry point.
488 *
489 */
490static int ns16550_write_support_int(
491  int   minor,
492  const char *buf,
493  int   len
494)
495{
496  int i;
497  unsigned32 Irql;
498
499  for(i=0; i<len;) {
500    if(Ring_buffer_Is_full(&Console_Port_Data[minor].TxBuffer)) {
501      if(!Console_Port_Data[minor].bActive) {
502        /*
503         * Wake up the device
504         */
505        rtems_interrupt_disable(Irql);
506        Console_Port_Data[minor].bActive = TRUE;
507        if(Console_Port_Tbl[minor].pDeviceFlow != &ns16550_flow_RTSCTS) {
508          ns16550_assert_RTS(minor);
509        }
510        ns16550_process(minor);
511        rtems_interrupt_enable(Irql);
512      } else {
513        /*
514         * Yield
515         */
516        rtems_task_wake_after(RTEMS_YIELD_PROCESSOR);
517      }
518
519      /*
520       * Wait for ring buffer to empty
521       */
522      continue;
523    }
524    else {
525      Ring_buffer_Add_character( &Console_Port_Data[minor].TxBuffer, buf[i]);
526      i++;
527    }
528  }
529
530  /*
531   * Ensure that characters are on the way
532   */
533  if(!Console_Port_Data[minor].bActive) {
534    /*
535     * Wake up the device
536     */
537    rtems_interrupt_disable(Irql);
538    Console_Port_Data[minor].bActive = TRUE;
539    if(Console_Port_Tbl[minor].pDeviceFlow !=&ns16550_flow_RTSCTS) {
540      ns16550_assert_RTS(minor);
541    }
542    ns16550_process(minor);
543    rtems_interrupt_enable(Irql);
544  }
545
546  return (len);
547}
548
549/*
550 *  ns16550_write_support_polled
551 *
552 *  Console Termios output entry point.
553 *
554 */
555static int ns16550_write_support_polled(
556  int         minor,
557  const char *buf,
558  int         len
559)
560{
561  int nwrite = 0;
562
563  /*
564   * poll each byte in the string out of the port.
565   */
566  while (nwrite < len) {
567    /*
568     * transmit character
569     */
570    ns16550_write_polled(minor, *buf++);
571    nwrite++;
572  }
573
574  /*
575   * return the number of bytes written.
576   */
577  return nwrite;
578}
579
580/*
581 *  ns16550_inbyte_nonblocking_polled
582 *
583 *  Console Termios polling input entry point.
584 */
585
586static int ns16550_inbyte_nonblocking_polled(
587  int minor
588)
589{
590  unsigned32           pNS16550;
591  unsigned char        ucLineStatus;
592  char                 cChar;
593  getRegister_f        getReg;
594
595  pNS16550 = Console_Port_Tbl[minor].ulCtrlPort1;
596  getReg   = Console_Port_Tbl[minor].getRegister;
597
598  ucLineStatus = (*getReg)(pNS16550, NS16550_LINE_STATUS);
599  if(ucLineStatus & SP_LSR_RDY) {
600    cChar = (*getReg)(pNS16550, NS16550_RECEIVE_BUFFER);
601    return((int)cChar);
602  } else {
603    return(-1);
604  }
605}
Note: See TracBrowser for help on using the repository browser.