source: rtems/c/src/lib/libchip/serial/ns16550.c @ 064b9be

4.104.114.84.95
Last change on this file since 064b9be was 064b9be, checked in by Joel Sherrill <joel.sherrill@…>, on 07/09/98 at 23:30:32

Switched to shared default probe routine.

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