source: rtems/c/src/libchip/serial/ns16550.c @ aa0da6b

4.104.114.84.9
Last change on this file since aa0da6b was aa0da6b, checked in by Joel Sherrill <joel.sherrill@…>, on Jun 22, 1998 at 9:56:09 AM

Added a comment and cleaned up spacing

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