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

4.104.114.84.95
Last change on this file since c163605f was c163605f, checked in by Jennifer Averett <Jennifer.Averett@…>, on Apr 18, 2005 at 5:37:50 PM

2005-04-18 Jennifer Averett <jennifer.averett@…>

  • libchip/serial/ns16550.c: Modified ISR to work with PPC new exception processing
  • Property mode set to 100644
File size: 17.4 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 <rtems/ringbuf.h>
27
28#include <libchip/serial.h>
29#include <libchip/sersupp.h>
30#include "ns16550_p.h"
31
32/*
33 * Flow control is only supported when using interrupts
34 */
35
36console_flow ns16550_flow_RTSCTS = {
37  ns16550_negate_RTS,             /* deviceStopRemoteTx */
38  ns16550_assert_RTS              /* deviceStartRemoteTx */
39};
40
41console_flow ns16550_flow_DTRCTS = {
42  ns16550_negate_DTR,             /* deviceStopRemoteTx */
43  ns16550_assert_DTR              /* deviceStartRemoteTx */
44};
45
46console_fns ns16550_fns = {
47  libchip_serial_default_probe,   /* deviceProbe */
48  ns16550_open,                   /* deviceFirstOpen */
49  NULL,                           /* deviceLastClose */
50  NULL,                           /* deviceRead */
51  ns16550_write_support_int,      /* deviceWrite */
52  ns16550_initialize_interrupts,  /* deviceInitialize */
53  ns16550_write_polled,           /* deviceWritePolled */
54  ns16550_set_attributes,         /* deviceSetAttributes */
55  TRUE                            /* deviceOutputUsesInterrupts */
56};
57
58console_fns ns16550_fns_polled = {
59  libchip_serial_default_probe,        /* deviceProbe */
60  ns16550_open,                        /* deviceFirstOpen */
61  ns16550_close,                       /* deviceLastClose */
62  ns16550_inbyte_nonblocking_polled,   /* deviceRead */
63  ns16550_write_support_polled,        /* deviceWrite */
64  ns16550_init,                        /* deviceInitialize */
65  ns16550_write_polled,                /* deviceWritePolled */
66  ns16550_set_attributes,              /* deviceSetAttributes */
67  FALSE                                /* deviceOutputUsesInterrupts */
68};
69
70#if defined(__PPC__)
71#ifdef _OLD_EXCEPTIONS
72extern void set_vector( rtems_isr_entry, rtems_vector_number, int );
73#else
74#include <bsp/irq.h>
75#endif
76#endif
77
78/*
79 *  ns16550_init
80 */
81
82NS16550_STATIC void ns16550_init(int minor)
83{
84  uint32_t                pNS16550;
85  uint8_t                 ucTrash;
86  uint8_t                 ucDataByte;
87  uint32_t                ulBaudDivisor;
88  ns16550_context        *pns16550Context;
89  setRegister_f           setReg;
90  getRegister_f           getReg;
91
92  pns16550Context=(ns16550_context *)malloc(sizeof(ns16550_context));
93
94  Console_Port_Data[minor].pDeviceContext=(void *)pns16550Context;
95  pns16550Context->ucModemCtrl=SP_MODEM_IRQ;
96
97  pNS16550 = Console_Port_Tbl[minor].ulCtrlPort1;
98  setReg   = Console_Port_Tbl[minor].setRegister;
99  getReg   = Console_Port_Tbl[minor].getRegister;
100
101  /* Clear the divisor latch, clear all interrupt enables,
102   * and reset and
103   * disable the FIFO's.
104   */
105
106  (*setReg)(pNS16550, NS16550_LINE_CONTROL, 0x0);
107  ns16550_enable_interrupts(minor, NS16550_DISABLE_ALL_INTR);
108
109  /* Set the divisor latch and set the baud rate. */
110
111  ulBaudDivisor = NS16550_Baud(
112    (uint32_t) Console_Port_Tbl[minor].ulClock,
113    (uint32_t) Console_Port_Tbl[minor].pDeviceParams
114  );
115  ucDataByte = SP_LINE_DLAB;
116  (*setReg)(pNS16550, NS16550_LINE_CONTROL, ucDataByte);
117
118  /* XXX */
119  (*setReg)(pNS16550, NS16550_TRANSMIT_BUFFER, ulBaudDivisor&0xff);
120  (*setReg)(pNS16550, NS16550_INTERRUPT_ENABLE, (ulBaudDivisor>>8)&0xff);
121
122  /* Clear the divisor latch and set the character size to eight bits */
123  /* with one stop bit and no parity checking. */
124  ucDataByte = EIGHT_BITS;
125  (*setReg)(pNS16550, NS16550_LINE_CONTROL, ucDataByte);
126
127  /* Enable and reset transmit and receive FIFOs. TJA     */
128  ucDataByte = SP_FIFO_ENABLE;
129  (*setReg)(pNS16550, NS16550_FIFO_CONTROL, ucDataByte);
130
131  ucDataByte = SP_FIFO_ENABLE | SP_FIFO_RXRST | SP_FIFO_TXRST;
132  (*setReg)(pNS16550, NS16550_FIFO_CONTROL, ucDataByte);
133
134  ns16550_enable_interrupts(minor, NS16550_DISABLE_ALL_INTR);
135
136  /* Set data terminal ready. */
137  /* And open interrupt tristate line */
138  (*setReg)(pNS16550, NS16550_MODEM_CONTROL,pns16550Context->ucModemCtrl);
139
140  ucTrash = (*getReg)(pNS16550, NS16550_LINE_STATUS );
141  ucTrash = (*getReg)(pNS16550, NS16550_RECEIVE_BUFFER );
142}
143
144/*
145 *  ns16550_open
146 */
147
148NS16550_STATIC int ns16550_open(
149  int      major,
150  int      minor,
151  void    * arg
152)
153{
154  /*
155   * Assert DTR
156   */
157
158  if(Console_Port_Tbl[minor].pDeviceFlow != &ns16550_flow_DTRCTS) {
159    ns16550_assert_DTR(minor);
160  }
161
162  return(RTEMS_SUCCESSFUL);
163}
164
165/*
166 *  ns16550_close
167 */
168
169NS16550_STATIC 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 */
188
189NS16550_STATIC void ns16550_write_polled(
190  int   minor,
191  char  cChar
192)
193{
194  uint32_t                pNS16550;
195  unsigned char           ucLineStatus;
196  int                     iTimeout;
197  getRegister_f           getReg;
198  setRegister_f           setReg;
199
200  pNS16550 = Console_Port_Tbl[minor].ulCtrlPort1;
201  getReg   = Console_Port_Tbl[minor].getRegister;
202  setReg   = Console_Port_Tbl[minor].setRegister;
203
204  /*
205   * wait for transmitter holding register to be empty
206   */
207  iTimeout=1000;
208  ucLineStatus = (*getReg)(pNS16550, NS16550_LINE_STATUS);
209  while ((ucLineStatus & SP_LSR_THOLD) == 0) {
210    /*
211     * Yield while we wait
212     */
213#if 0
214     if(_System_state_Is_up(_System_state_Get())) {
215       rtems_task_wake_after(RTEMS_YIELD_PROCESSOR);
216     }
217#endif
218     ucLineStatus = (*getReg)(pNS16550, NS16550_LINE_STATUS);
219     if(!--iTimeout) {
220       break;
221     }
222  }
223
224  /*
225   * transmit character
226   */
227  (*setReg)(pNS16550, NS16550_TRANSMIT_BUFFER, cChar);
228}
229
230/*
231 * These routines provide control of the RTS and DTR lines
232 */
233
234/*
235 *  ns16550_assert_RTS
236 */
237
238NS16550_STATIC int ns16550_assert_RTS(int minor)
239{
240  uint32_t                pNS16550;
241  uint32_t                Irql;
242  ns16550_context        *pns16550Context;
243  setRegister_f           setReg;
244
245  pns16550Context=(ns16550_context *) Console_Port_Data[minor].pDeviceContext;
246
247  pNS16550 = Console_Port_Tbl[minor].ulCtrlPort1;
248  setReg   = Console_Port_Tbl[minor].setRegister;
249
250  /*
251   * Assert RTS
252   */
253  rtems_interrupt_disable(Irql);
254  pns16550Context->ucModemCtrl|=SP_MODEM_RTS;
255  (*setReg)(pNS16550, NS16550_MODEM_CONTROL, pns16550Context->ucModemCtrl);
256  rtems_interrupt_enable(Irql);
257  return 0;
258}
259
260/*
261 *  ns16550_negate_RTS
262 */
263
264NS16550_STATIC int ns16550_negate_RTS(int minor)
265{
266  uint32_t                pNS16550;
267  uint32_t                Irql;
268  ns16550_context        *pns16550Context;
269  setRegister_f           setReg;
270
271  pns16550Context=(ns16550_context *) Console_Port_Data[minor].pDeviceContext;
272
273  pNS16550 = Console_Port_Tbl[minor].ulCtrlPort1;
274  setReg   = Console_Port_Tbl[minor].setRegister;
275
276  /*
277   * Negate RTS
278   */
279  rtems_interrupt_disable(Irql);
280  pns16550Context->ucModemCtrl&=~SP_MODEM_RTS;
281  (*setReg)(pNS16550, NS16550_MODEM_CONTROL, pns16550Context->ucModemCtrl);
282  rtems_interrupt_enable(Irql);
283  return 0;
284}
285
286/*
287 * These flow control routines utilise a connection from the local DTR
288 * line to the remote CTS line
289 */
290
291/*
292 *  ns16550_assert_DTR
293 */
294
295NS16550_STATIC int ns16550_assert_DTR(int minor)
296{
297  uint32_t                pNS16550;
298  uint32_t                Irql;
299  ns16550_context        *pns16550Context;
300  setRegister_f           setReg;
301
302  pns16550Context=(ns16550_context *) Console_Port_Data[minor].pDeviceContext;
303
304  pNS16550 = Console_Port_Tbl[minor].ulCtrlPort1;
305  setReg   = Console_Port_Tbl[minor].setRegister;
306
307  /*
308   * Assert DTR
309   */
310  rtems_interrupt_disable(Irql);
311  pns16550Context->ucModemCtrl|=SP_MODEM_DTR;
312  (*setReg)(pNS16550, NS16550_MODEM_CONTROL, pns16550Context->ucModemCtrl);
313  rtems_interrupt_enable(Irql);
314  return 0;
315}
316
317/*
318 *  ns16550_negate_DTR
319 */
320
321NS16550_STATIC int ns16550_negate_DTR(int minor)
322{
323  uint32_t                pNS16550;
324  uint32_t                Irql;
325  ns16550_context        *pns16550Context;
326  setRegister_f           setReg;
327
328  pns16550Context=(ns16550_context *) Console_Port_Data[minor].pDeviceContext;
329
330  pNS16550 = Console_Port_Tbl[minor].ulCtrlPort1;
331  setReg   = Console_Port_Tbl[minor].setRegister;
332
333  /*
334   * Negate DTR
335   */
336  rtems_interrupt_disable(Irql);
337  pns16550Context->ucModemCtrl&=~SP_MODEM_DTR;
338  (*setReg)(pNS16550, NS16550_MODEM_CONTROL,pns16550Context->ucModemCtrl);
339  rtems_interrupt_enable(Irql);
340  return 0;
341}
342
343/*
344 *  ns16550_set_attributes
345 *
346 *  This function sets the channel to reflect the requested termios
347 *  port settings.
348 */
349
350NS16550_STATIC int ns16550_set_attributes(
351  int                   minor,
352  const struct termios *t
353)
354{
355  uint32_t                pNS16550;
356  uint32_t                ulBaudDivisor;
357  uint8_t                 ucLineControl;
358  uint32_t                baud_requested;
359  setRegister_f           setReg;
360  getRegister_f           getReg;
361  uint32_t                Irql;
362
363  pNS16550 = Console_Port_Tbl[minor].ulCtrlPort1;
364  setReg   = Console_Port_Tbl[minor].setRegister;
365  getReg   = Console_Port_Tbl[minor].getRegister;
366
367  /*
368   *  Calculate the baud rate divisor
369   */
370
371  baud_requested = t->c_cflag & CBAUD;
372  if (!baud_requested)
373    baud_requested = B9600;              /* default to 9600 baud */
374
375  ulBaudDivisor = NS16550_Baud(
376    (uint32_t) Console_Port_Tbl[minor].ulClock,
377    termios_baud_to_number(baud_requested)
378  );
379
380  ucLineControl = 0;
381
382  /*
383   *  Parity
384   */
385
386  if (t->c_cflag & PARENB) {
387    ucLineControl |= SP_LINE_PAR;
388    if (!(t->c_cflag & PARODD))
389      ucLineControl |= SP_LINE_ODD;
390  }
391
392  /*
393   *  Character Size
394   */
395
396  if (t->c_cflag & CSIZE) {
397    switch (t->c_cflag & CSIZE) {
398      case CS5:  ucLineControl |= FIVE_BITS;  break;
399      case CS6:  ucLineControl |= SIX_BITS;   break;
400      case CS7:  ucLineControl |= SEVEN_BITS; break;
401      case CS8:  ucLineControl |= EIGHT_BITS; break;
402    }
403  } else {
404    ucLineControl |= EIGHT_BITS;               /* default to 9600,8,N,1 */
405  }
406
407  /*
408   *  Stop Bits
409   */
410
411  if (t->c_cflag & CSTOPB) {
412    ucLineControl |= SP_LINE_STOP;              /* 2 stop bits */
413  } else {
414    ;                                           /* 1 stop bit */
415  }
416
417  /*
418   *  Now actually set the chip
419   */
420
421  rtems_interrupt_disable(Irql);
422
423    /*
424     *  Set the baud rate
425     */
426
427    (*setReg)(pNS16550, NS16550_LINE_CONTROL, SP_LINE_DLAB);
428    /* XXX are these registers right? */
429    (*setReg)(pNS16550, NS16550_TRANSMIT_BUFFER, ulBaudDivisor&0xff);
430    (*setReg)(pNS16550, NS16550_INTERRUPT_ENABLE, (ulBaudDivisor>>8)&0xff);
431
432    /*
433     *  Now write the line control
434     */
435    (*setReg)(pNS16550, NS16550_LINE_CONTROL, ucLineControl );
436
437  rtems_interrupt_enable(Irql);
438
439  return 0;
440}
441
442/*
443 *  ns16550_process
444 *
445 *  This routine is the console interrupt handler for A port.
446 */
447
448NS16550_STATIC void ns16550_process(
449        int             minor
450)
451{
452  uint32_t                pNS16550;
453  volatile uint8_t        ucLineStatus;
454  volatile uint8_t        ucInterruptId;
455  unsigned char           cChar;
456  getRegister_f           getReg;
457  setRegister_f           setReg;
458
459  pNS16550 = Console_Port_Tbl[minor].ulCtrlPort1;
460  getReg   = Console_Port_Tbl[minor].getRegister;
461  setReg   = Console_Port_Tbl[minor].setRegister;
462
463  do {
464    /*
465     * Deal with any received characters
466     */
467    while(TRUE) {
468      ucLineStatus = (*getReg)(pNS16550, NS16550_LINE_STATUS);
469      if(~ucLineStatus & SP_LSR_RDY) {
470        break;
471      }
472      cChar = (*getReg)(pNS16550, NS16550_RECEIVE_BUFFER);
473      rtems_termios_enqueue_raw_characters(
474        Console_Port_Data[minor].termios_data,
475        &cChar,
476        1
477      );
478    }
479
480    /*
481     *  TX all the characters we can
482     */
483
484    while(TRUE) {
485        ucLineStatus = (*getReg)(pNS16550, NS16550_LINE_STATUS);
486        if(~ucLineStatus & SP_LSR_THOLD) {
487          /*
488           * We'll get another interrupt when
489           * the transmitter holding reg. becomes
490           * free again
491           */
492          break;
493        }
494
495#if 0
496        /* XXX flow control not completely supported in libchip */
497
498        if(Console_Port_Tbl[minor].pDeviceFlow != &ns16550_flow_RTSCTS) {
499          ns16550_negate_RTS(minor);
500        }
501#endif
502
503    rtems_termios_dequeue_characters(Console_Port_Data[minor].termios_data, 1);
504    if (rtems_termios_dequeue_characters(
505         Console_Port_Data[minor].termios_data, 1)) {
506        if (Console_Port_Tbl[minor].pDeviceFlow != &ns16550_flow_RTSCTS) {
507          ns16550_negate_RTS(minor);
508        }
509        Console_Port_Data[minor].bActive = FALSE;
510        ns16550_enable_interrupts(minor, NS16550_ENABLE_ALL_INTR_EXCEPT_TX);
511        break;
512      }
513
514      ucInterruptId = (*getReg)(pNS16550, NS16550_INTERRUPT_ID);
515    }
516  } while((ucInterruptId&0xf)!=0x1);
517}
518
519#if defined(__PPC__)
520#ifdef _OLD_EXCEPTIONS
521
522/*
523 *  ns16550_isr
524 */
525
526NS16550_STATIC rtems_isr ns16550_isr(
527  rtems_vector_number vector
528)
529{
530  int     minor;
531
532  for(minor=0;minor<Console_Port_Count;minor++) {
533    if(Console_Port_Tbl[minor].ulIntVector == vector &&
534       Console_Port_Tbl[minor].deviceType == SERIAL_NS16550 ) {
535      ns16550_process(minor);
536    }
537  }
538}
539
540#else
541
542NS16550_STATIC rtems_isr ns16550_isr(
543  void *entry
544)
545{
546  console_tbl *ptr = entry;
547  int         minor;
548
549  for(minor=0;minor<Console_Port_Count;minor++) {
550    if( &Console_Port_Tbl[minor] == ptr ) {
551      ns16550_process(minor);
552    }
553  }
554
555}
556
557#endif
558#endif
559
560/*
561 *  ns16550_enable_interrupts
562 *
563 *  This routine initializes the port to have the specified interrupts masked.
564 */
565
566NS16550_STATIC void ns16550_enable_interrupts(
567  int minor,
568  int mask
569)
570{
571  uint32_t       pNS16550;
572  setRegister_f  setReg;
573
574  pNS16550 = Console_Port_Tbl[minor].ulCtrlPort1;
575  setReg   = Console_Port_Tbl[minor].setRegister;
576
577  (*setReg)(pNS16550, NS16550_INTERRUPT_ENABLE, mask);
578}
579
580/*
581 *  ns16550_initialize_interrupts
582 *
583 *  This routine initializes the port to operate in interrupt driver mode.
584 */
585
586#if defined(__PPC__)
587#ifdef _OLD_EXCEPTIONS
588NS16550_STATIC void ns16550_initialize_interrupts(int minor)
589{
590  ns16550_init(minor);
591
592  Console_Port_Data[minor].bActive = FALSE;
593
594  set_vector(ns16550_isr, Console_Port_Tbl[minor].ulIntVector, 1);
595 
596  ns16550_enable_interrupts(minor, NS16550_ENABLE_ALL_INTR);
597}
598#else
599
600static void null_fun(){}
601
602NS16550_STATIC void ns16550_initialize_interrupts(int minor)
603{
604  rtems_irq_connect_data IrqData = {0,
605                                    ns16550_isr,
606                                    &Console_Port_Data[minor],
607                                    (rtems_irq_enable)null_fun,
608                                    (rtems_irq_disable)null_fun,
609                                    (rtems_irq_is_enabled)null_fun,
610                                    NULL
611                                   };
612
613  ns16550_init(minor);
614
615  Console_Port_Data[minor].bActive = FALSE;
616
617  IrqData.name  = (rtems_irq_symbolic_name)(
618    (unsigned int)BSP_PCI_IRQ0 +  Console_Port_Tbl[minor].ulIntVector );
619
620  if (!BSP_install_rtems_shared_irq_handler (&IrqData)) {
621    printk("Error installing interrupt handler!\n");
622    rtems_fatal_error_occurred(1);
623  }
624
625  ns16550_enable_interrupts(minor, NS16550_ENABLE_ALL_INTR);
626}
627
628#endif
629#endif
630
631/*
632 *  ns16550_write_support_int
633 *
634 *  Console Termios output entry point.
635 */
636
637NS16550_STATIC int ns16550_write_support_int(
638  int   minor,
639  const char *buf,
640  int   len
641)
642{
643  uint32_t       Irql;
644  uint32_t       pNS16550;
645  setRegister_f  setReg;
646
647  setReg   = Console_Port_Tbl[minor].setRegister;
648  pNS16550 = Console_Port_Tbl[minor].ulCtrlPort1;
649
650  /*
651   *  We are using interrupt driven output and termios only sends us
652   *  one character at a time.
653   */
654
655  if ( !len )
656    return 0;
657
658  if(Console_Port_Tbl[minor].pDeviceFlow != &ns16550_flow_RTSCTS) {
659    ns16550_assert_RTS(minor);
660  }
661
662  rtems_interrupt_disable(Irql);
663    if ( Console_Port_Data[minor].bActive == FALSE) {
664      Console_Port_Data[minor].bActive = TRUE;
665      ns16550_enable_interrupts(minor, NS16550_ENABLE_ALL_INTR);
666    }
667    (*setReg)(pNS16550, NS16550_TRANSMIT_BUFFER, *buf);
668  rtems_interrupt_enable(Irql);
669
670  return 1;
671}
672
673/*
674 *  ns16550_write_support_polled
675 *
676 *  Console Termios output entry point.
677 *
678 */
679
680NS16550_STATIC int ns16550_write_support_polled(
681  int         minor,
682  const char *buf,
683  int         len
684)
685{
686  int nwrite = 0;
687
688  /*
689   * poll each byte in the string out of the port.
690   */
691  while (nwrite < len) {
692    /*
693     * transmit character
694     */
695    ns16550_write_polled(minor, *buf++);
696    nwrite++;
697  }
698
699  /*
700   * return the number of bytes written.
701   */
702  return nwrite;
703}
704
705/*
706 *  ns16550_inbyte_nonblocking_polled
707 *
708 *  Console Termios polling input entry point.
709 */
710
711NS16550_STATIC int ns16550_inbyte_nonblocking_polled(
712  int minor
713)
714{
715  uint32_t             pNS16550;
716  unsigned char        ucLineStatus;
717  char                 cChar;
718  getRegister_f        getReg;
719
720  pNS16550 = Console_Port_Tbl[minor].ulCtrlPort1;
721  getReg   = Console_Port_Tbl[minor].getRegister;
722
723  ucLineStatus = (*getReg)(pNS16550, NS16550_LINE_STATUS);
724  if(ucLineStatus & SP_LSR_RDY) {
725    cChar = (*getReg)(pNS16550, NS16550_RECEIVE_BUFFER);
726    return (int)cChar;
727  } else {
728    return -1;
729  }
730}
Note: See TracBrowser for help on using the repository browser.