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

4.104.114.84.9
Last change on this file since ee3b242b was ee3b242b, checked in by Joel Sherrill <joel.sherrill@…>, on Jun 13, 1998 at 4:03:57 PM

Initial incarnation of libchip compiles.

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