source: rtems/c/src/libchip/serial/ns16550.c @ 9eef52b

4.104.114.84.95
Last change on this file since 9eef52b was 8a2d4f2b, checked in by Joel Sherrill <joel.sherrill@…>, on 06/23/98 at 14:54:09

Added NULL entry for setAttributes.

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