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

4.104.114.84.95
Last change on this file since dd5d2f04 was 991a1ab4, checked in by Joel Sherrill <joel.sherrill@…>, on 07/15/98 at 23:21:55

Added check for proper deviceType to interrupt processing code.

  • 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#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(Console_Port_Tbl[minor].ulIntVector == vector &&
406       Console_Port_Tbl[minor].deviceType == SERIAL_NS16550 ) {
407      ns16550_process(minor);
408    }
409  }
410}
411
412/*
413 *  ns16550_flush
414 */
415NS16550_STATIC int ns16550_flush(int major, int minor, void *arg)
416{
417  while(!Ring_buffer_Is_empty(&Console_Port_Data[minor].TxBuffer)) {
418    /*
419     * Yield while we wait
420     */
421    if(_System_state_Is_up(_System_state_Get())) {
422      rtems_task_wake_after(RTEMS_YIELD_PROCESSOR);
423    }
424  }
425
426  ns16550_close(major, minor, arg);
427
428  return(RTEMS_SUCCESSFUL);
429}
430
431/*
432 *  ns16550_initialize_interrupts
433 *
434 *  This routine initializes the console's receive and transmit
435 *  ring buffers and loads the appropriate vectors to handle the interrupts.
436 *
437 *  Input parameters:  NONE
438 *
439 *  Output parameters: NONE
440 *
441 *  Return values:     NONE
442 */
443
444NS16550_STATIC void ns16550_enable_interrupts(
445  int minor
446)
447{
448  unsigned32            pNS16550;
449  unsigned8             ucDataByte;
450  setRegister_f           setReg;
451
452  pNS16550 = Console_Port_Tbl[minor].ulCtrlPort1;
453  setReg   = Console_Port_Tbl[minor].setRegister;
454
455  /*
456   * Enable interrupts
457   */
458  ucDataByte = SP_INT_RX_ENABLE | SP_INT_TX_ENABLE;
459  (*setReg)(pNS16550, NS16550_INTERRUPT_ENABLE, ucDataByte);
460
461}
462
463NS16550_STATIC void ns16550_initialize_interrupts(int minor)
464{
465  ns16550_init(minor);
466
467  Ring_buffer_Initialize(&Console_Port_Data[minor].TxBuffer);
468
469  Console_Port_Data[minor].bActive = FALSE;
470
471  set_vector(ns16550_isr, Console_Port_Tbl[minor].ulIntVector, 1);
472
473  ns16550_enable_interrupts(minor);
474}
475
476/*
477 *  ns16550_write_support_int
478 *
479 *  Console Termios output entry point.
480 *
481 */
482NS16550_STATIC int ns16550_write_support_int(
483  int   minor,
484  const char *buf,
485  int   len
486)
487{
488  int i;
489  unsigned32 Irql;
490
491  for(i=0; i<len;) {
492    if(Ring_buffer_Is_full(&Console_Port_Data[minor].TxBuffer)) {
493      if(!Console_Port_Data[minor].bActive) {
494        /*
495         * Wake up the device
496         */
497        rtems_interrupt_disable(Irql);
498        Console_Port_Data[minor].bActive = TRUE;
499        if(Console_Port_Tbl[minor].pDeviceFlow != &ns16550_flow_RTSCTS) {
500          ns16550_assert_RTS(minor);
501        }
502        ns16550_process(minor);
503        rtems_interrupt_enable(Irql);
504      } else {
505        /*
506         * Yield
507         */
508        rtems_task_wake_after(RTEMS_YIELD_PROCESSOR);
509      }
510
511      /*
512       * Wait for ring buffer to empty
513       */
514      continue;
515    }
516    else {
517      Ring_buffer_Add_character( &Console_Port_Data[minor].TxBuffer, buf[i]);
518      i++;
519    }
520  }
521
522  /*
523   * Ensure that characters are on the way
524   */
525  if(!Console_Port_Data[minor].bActive) {
526    /*
527     * Wake up the device
528     */
529    rtems_interrupt_disable(Irql);
530    Console_Port_Data[minor].bActive = TRUE;
531    if(Console_Port_Tbl[minor].pDeviceFlow !=&ns16550_flow_RTSCTS) {
532      ns16550_assert_RTS(minor);
533    }
534    ns16550_process(minor);
535    rtems_interrupt_enable(Irql);
536  }
537
538  return (len);
539}
540
541/*
542 *  ns16550_write_support_polled
543 *
544 *  Console Termios output entry point.
545 *
546 */
547NS16550_STATIC int ns16550_write_support_polled(
548  int         minor,
549  const char *buf,
550  int         len
551)
552{
553  int nwrite = 0;
554
555  /*
556   * poll each byte in the string out of the port.
557   */
558  while (nwrite < len) {
559    /*
560     * transmit character
561     */
562    ns16550_write_polled(minor, *buf++);
563    nwrite++;
564  }
565
566  /*
567   * return the number of bytes written.
568   */
569  return nwrite;
570}
571
572/*
573 *  ns16550_inbyte_nonblocking_polled
574 *
575 *  Console Termios polling input entry point.
576 */
577
578NS16550_STATIC int ns16550_inbyte_nonblocking_polled(
579  int minor
580)
581{
582  unsigned32           pNS16550;
583  unsigned char        ucLineStatus;
584  char                 cChar;
585  getRegister_f        getReg;
586
587  pNS16550 = Console_Port_Tbl[minor].ulCtrlPort1;
588  getReg   = Console_Port_Tbl[minor].getRegister;
589
590  ucLineStatus = (*getReg)(pNS16550, NS16550_LINE_STATUS);
591  if(ucLineStatus & SP_LSR_RDY) {
592    cChar = (*getReg)(pNS16550, NS16550_RECEIVE_BUFFER);
593    return (int)cChar;
594  } else {
595    return -1;
596  }
597}
Note: See TracBrowser for help on using the repository browser.