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

4.104.114.84.95
Last change on this file since ee4f57d was ee4f57d, checked in by Ralf Corsepius <ralf.corsepius@…>, on 03/23/04 at 09:59:52

2004-03-23 Ralf Corsepius <ralf_corsepius@…>

  • libchip/ide/ata.c, libchip/ide/ata_internal.h, libchip/ide/ide_controller.c, libchip/ide/ide_ctrl_cfg.h, libchip/ide/ide_ctrl_io.h, libchip/network/cs8900.c, libchip/network/dec21140.c, libchip/network/elnk.c, libchip/network/if_fxp.c, libchip/network/open_eth.c, libchip/network/open_eth.h, libchip/network/sonic.c, libchip/network/sonic.h, libchip/rtc/icm7170.c, libchip/rtc/icm7170.h, libchip/rtc/icm7170_reg.c, libchip/rtc/icm7170_reg2.c, libchip/rtc/icm7170_reg4.c, libchip/rtc/icm7170_reg8.c, libchip/rtc/m48t08.c, libchip/rtc/m48t08.h, libchip/rtc/m48t08_reg.c, libchip/rtc/m48t08_reg2.c, libchip/rtc/m48t08_reg4.c, libchip/rtc/m48t08_reg8.c, libchip/rtc/rtc.h, libchip/serial/mc68681.c, libchip/serial/mc68681.h, libchip/serial/mc68681_reg.c, libchip/serial/mc68681_reg2.c, libchip/serial/mc68681_reg4.c, libchip/serial/mc68681_reg8.c, libchip/serial/ns16550.c, libchip/serial/ns16550_p.h, libchip/serial/serial.h, libchip/serial/z85c30.c, libchip/serial/z85c30.h, libchip/serial/z85c30_p.h, libchip/serial/z85c30_reg.c, libchip/shmdr/addlq.c, libchip/shmdr/cnvpkt.c, libchip/shmdr/dump.c, libchip/shmdr/fatal.c, libchip/shmdr/getlq.c, libchip/shmdr/init.c, libchip/shmdr/initlq.c, libchip/shmdr/intr.c, libchip/shmdr/poll.c, libchip/shmdr/send.c, libchip/shmdr/shm_driver.h: Convert to using c99 fixed-size types.
  • Property mode set to 100644
File size: 16.1 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 <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
70extern void set_vector( rtems_isr_entry, rtems_vector_number, int );
71
72/*
73 *  ns16550_init
74 */
75
76NS16550_STATIC void ns16550_init(int minor)
77{
78  uint32_t                pNS16550;
79  uint8_t                 ucTrash;
80  uint8_t                 ucDataByte;
81  uint32_t                ulBaudDivisor;
82  ns16550_context        *pns16550Context;
83  setRegister_f           setReg;
84  getRegister_f           getReg;
85
86  pns16550Context=(ns16550_context *)malloc(sizeof(ns16550_context));
87
88  Console_Port_Data[minor].pDeviceContext=(void *)pns16550Context;
89  pns16550Context->ucModemCtrl=SP_MODEM_IRQ;
90
91  pNS16550 = Console_Port_Tbl[minor].ulCtrlPort1;
92  setReg   = Console_Port_Tbl[minor].setRegister;
93  getReg   = Console_Port_Tbl[minor].getRegister;
94
95  /* Clear the divisor latch, clear all interrupt enables,
96   * and reset and
97   * disable the FIFO's.
98   */
99
100  (*setReg)(pNS16550, NS16550_LINE_CONTROL, 0x0);
101  ns16550_enable_interrupts(minor, NS16550_DISABLE_ALL_INTR);
102
103  /* Set the divisor latch and set the baud rate. */
104
105  ulBaudDivisor = NS16550_Baud(
106    (uint32_t  ) Console_Port_Tbl[minor].ulClock,
107    (uint32_t  ) Console_Port_Tbl[minor].pDeviceParams
108  );
109  ucDataByte = SP_LINE_DLAB;
110  (*setReg)(pNS16550, NS16550_LINE_CONTROL, ucDataByte);
111
112  /* XXX */
113  (*setReg)(pNS16550, NS16550_TRANSMIT_BUFFER, ulBaudDivisor&0xff);
114  (*setReg)(pNS16550, NS16550_INTERRUPT_ENABLE, (ulBaudDivisor>>8)&0xff);
115
116  /* Clear the divisor latch and set the character size to eight bits */
117  /* with one stop bit and no parity checking. */
118  ucDataByte = EIGHT_BITS;
119  (*setReg)(pNS16550, NS16550_LINE_CONTROL, ucDataByte);
120
121  /* Enable and reset transmit and receive FIFOs. TJA     */
122  ucDataByte = SP_FIFO_ENABLE;
123  (*setReg)(pNS16550, NS16550_FIFO_CONTROL, ucDataByte);
124
125  ucDataByte = SP_FIFO_ENABLE | SP_FIFO_RXRST | SP_FIFO_TXRST;
126  (*setReg)(pNS16550, NS16550_FIFO_CONTROL, ucDataByte);
127
128  ns16550_enable_interrupts(minor, NS16550_DISABLE_ALL_INTR);
129
130  /* Set data terminal ready. */
131  /* And open interrupt tristate line */
132  (*setReg)(pNS16550, NS16550_MODEM_CONTROL,pns16550Context->ucModemCtrl);
133
134  ucTrash = (*getReg)(pNS16550, NS16550_LINE_STATUS );
135  ucTrash = (*getReg)(pNS16550, NS16550_RECEIVE_BUFFER );
136}
137
138/*
139 *  ns16550_open
140 */
141
142NS16550_STATIC int ns16550_open(
143  int      major,
144  int      minor,
145  void    * arg
146)
147{
148  /*
149   * Assert DTR
150   */
151
152  if(Console_Port_Tbl[minor].pDeviceFlow != &ns16550_flow_DTRCTS) {
153    ns16550_assert_DTR(minor);
154  }
155
156  return(RTEMS_SUCCESSFUL);
157}
158
159/*
160 *  ns16550_close
161 */
162
163NS16550_STATIC int ns16550_close(
164  int      major,
165  int      minor,
166  void    * arg
167)
168{
169  /*
170   * Negate DTR
171   */
172  if(Console_Port_Tbl[minor].pDeviceFlow != &ns16550_flow_DTRCTS) {
173    ns16550_negate_DTR(minor);
174  }
175
176  return(RTEMS_SUCCESSFUL);
177}
178
179/*
180 *  ns16550_write_polled
181 */
182
183NS16550_STATIC void ns16550_write_polled(
184  int   minor,
185  char  cChar
186)
187{
188  uint32_t                pNS16550;
189  unsigned char           ucLineStatus;
190  int                     iTimeout;
191  getRegister_f           getReg;
192  setRegister_f           setReg;
193
194  pNS16550 = Console_Port_Tbl[minor].ulCtrlPort1;
195  getReg   = Console_Port_Tbl[minor].getRegister;
196  setReg   = Console_Port_Tbl[minor].setRegister;
197
198  /*
199   * wait for transmitter holding register to be empty
200   */
201  iTimeout=1000;
202  ucLineStatus = (*getReg)(pNS16550, NS16550_LINE_STATUS);
203  while ((ucLineStatus & SP_LSR_THOLD) == 0) {
204    /*
205     * Yield while we wait
206     */
207#if 0
208     if(_System_state_Is_up(_System_state_Get())) {
209       rtems_task_wake_after(RTEMS_YIELD_PROCESSOR);
210     }
211#endif
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/*
229 *  ns16550_assert_RTS
230 */
231
232NS16550_STATIC int ns16550_assert_RTS(int minor)
233{
234  uint32_t                pNS16550;
235  uint32_t                Irql;
236  ns16550_context        *pns16550Context;
237  setRegister_f           setReg;
238
239  pns16550Context=(ns16550_context *) Console_Port_Data[minor].pDeviceContext;
240
241  pNS16550 = Console_Port_Tbl[minor].ulCtrlPort1;
242  setReg   = Console_Port_Tbl[minor].setRegister;
243
244  /*
245   * Assert RTS
246   */
247  rtems_interrupt_disable(Irql);
248  pns16550Context->ucModemCtrl|=SP_MODEM_RTS;
249  (*setReg)(pNS16550, NS16550_MODEM_CONTROL, pns16550Context->ucModemCtrl);
250  rtems_interrupt_enable(Irql);
251  return 0;
252}
253
254/*
255 *  ns16550_negate_RTS
256 */
257
258NS16550_STATIC int ns16550_negate_RTS(int minor)
259{
260  uint32_t                pNS16550;
261  uint32_t                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/*
286 *  ns16550_assert_DTR
287 */
288
289NS16550_STATIC int ns16550_assert_DTR(int minor)
290{
291  uint32_t                pNS16550;
292  uint32_t                Irql;
293  ns16550_context        *pns16550Context;
294  setRegister_f           setReg;
295
296  pns16550Context=(ns16550_context *) Console_Port_Data[minor].pDeviceContext;
297
298  pNS16550 = Console_Port_Tbl[minor].ulCtrlPort1;
299  setReg   = Console_Port_Tbl[minor].setRegister;
300
301  /*
302   * Assert DTR
303   */
304  rtems_interrupt_disable(Irql);
305  pns16550Context->ucModemCtrl|=SP_MODEM_DTR;
306  (*setReg)(pNS16550, NS16550_MODEM_CONTROL, pns16550Context->ucModemCtrl);
307  rtems_interrupt_enable(Irql);
308  return 0;
309}
310
311/*
312 *  ns16550_negate_DTR
313 */
314
315NS16550_STATIC int ns16550_negate_DTR(int minor)
316{
317  uint32_t                pNS16550;
318  uint32_t                Irql;
319  ns16550_context        *pns16550Context;
320  setRegister_f           setReg;
321
322  pns16550Context=(ns16550_context *) Console_Port_Data[minor].pDeviceContext;
323
324  pNS16550 = Console_Port_Tbl[minor].ulCtrlPort1;
325  setReg   = Console_Port_Tbl[minor].setRegister;
326
327  /*
328   * Negate DTR
329   */
330  rtems_interrupt_disable(Irql);
331  pns16550Context->ucModemCtrl&=~SP_MODEM_DTR;
332  (*setReg)(pNS16550, NS16550_MODEM_CONTROL,pns16550Context->ucModemCtrl);
333  rtems_interrupt_enable(Irql);
334  return 0;
335}
336
337/*
338 *  ns16550_set_attributes
339 *
340 *  This function sets the channel to reflect the requested termios
341 *  port settings.
342 */
343
344NS16550_STATIC int ns16550_set_attributes(
345  int                   minor,
346  const struct termios *t
347)
348{
349  uint32_t                pNS16550;
350  uint32_t                ulBaudDivisor;
351  uint8_t                 ucLineControl;
352  uint32_t                baud_requested;
353  setRegister_f           setReg;
354  getRegister_f           getReg;
355  uint32_t                Irql;
356
357  pNS16550 = Console_Port_Tbl[minor].ulCtrlPort1;
358  setReg   = Console_Port_Tbl[minor].setRegister;
359  getReg   = Console_Port_Tbl[minor].getRegister;
360
361  /*
362   *  Calculate the baud rate divisor
363   */
364
365  baud_requested = t->c_cflag & CBAUD;
366  if (!baud_requested)
367    baud_requested = B9600;              /* default to 9600 baud */
368
369  ulBaudDivisor = NS16550_Baud(
370    (uint32_t  ) Console_Port_Tbl[minor].ulClock,
371    termios_baud_to_number(baud_requested)
372  );
373
374  ucLineControl = 0;
375
376  /*
377   *  Parity
378   */
379
380  if (t->c_cflag & PARENB) {
381    ucLineControl |= SP_LINE_PAR;
382    if (!(t->c_cflag & PARODD))
383      ucLineControl |= SP_LINE_ODD;
384  }
385
386  /*
387   *  Character Size
388   */
389
390  if (t->c_cflag & CSIZE) {
391    switch (t->c_cflag & CSIZE) {
392      case CS5:  ucLineControl |= FIVE_BITS;  break;
393      case CS6:  ucLineControl |= SIX_BITS;   break;
394      case CS7:  ucLineControl |= SEVEN_BITS; break;
395      case CS8:  ucLineControl |= EIGHT_BITS; break;
396    }
397  } else {
398    ucLineControl |= EIGHT_BITS;               /* default to 9600,8,N,1 */
399  }
400
401  /*
402   *  Stop Bits
403   */
404
405  if (t->c_cflag & CSTOPB) {
406    ucLineControl |= SP_LINE_STOP;              /* 2 stop bits */
407  } else {
408    ;                                           /* 1 stop bit */
409  }
410
411  /*
412   *  Now actually set the chip
413   */
414
415  rtems_interrupt_disable(Irql);
416
417    /*
418     *  Set the baud rate
419     */
420
421    (*setReg)(pNS16550, NS16550_LINE_CONTROL, SP_LINE_DLAB);
422    /* XXX are these registers right? */
423    (*setReg)(pNS16550, NS16550_TRANSMIT_BUFFER, ulBaudDivisor&0xff);
424    (*setReg)(pNS16550, NS16550_INTERRUPT_ENABLE, (ulBaudDivisor>>8)&0xff);
425
426    /*
427     *  Now write the line control
428     */
429    (*setReg)(pNS16550, NS16550_LINE_CONTROL, ucLineControl );
430
431  rtems_interrupt_enable(Irql);
432
433  return 0;
434}
435
436/*
437 *  ns16550_process
438 *
439 *  This routine is the console interrupt handler for A port.
440 */
441
442NS16550_STATIC void ns16550_process(
443        int             minor
444)
445{
446  uint32_t                pNS16550;
447  volatile uint8_t        ucLineStatus;
448  volatile uint8_t        ucInterruptId;
449  unsigned char           cChar;
450  getRegister_f           getReg;
451  setRegister_f           setReg;
452
453  pNS16550 = Console_Port_Tbl[minor].ulCtrlPort1;
454  getReg   = Console_Port_Tbl[minor].getRegister;
455  setReg   = Console_Port_Tbl[minor].setRegister;
456
457  do {
458    /*
459     * Deal with any received characters
460     */
461    while(TRUE) {
462      ucLineStatus = (*getReg)(pNS16550, NS16550_LINE_STATUS);
463      if(~ucLineStatus & SP_LSR_RDY) {
464        break;
465      }
466      cChar = (*getReg)(pNS16550, NS16550_RECEIVE_BUFFER);
467      rtems_termios_enqueue_raw_characters(
468        Console_Port_Data[minor].termios_data,
469        &cChar,
470        1
471      );
472    }
473
474    /*
475     *  TX all the characters we can
476     */
477
478    while(TRUE) {
479        ucLineStatus = (*getReg)(pNS16550, NS16550_LINE_STATUS);
480        if(~ucLineStatus & SP_LSR_THOLD) {
481          /*
482           * We'll get another interrupt when
483           * the transmitter holding reg. becomes
484           * free again
485           */
486          break;
487        }
488
489#if 0
490        /* XXX flow control not completely supported in libchip */
491
492        if(Console_Port_Tbl[minor].pDeviceFlow != &ns16550_flow_RTSCTS) {
493          ns16550_negate_RTS(minor);
494        }
495#endif
496
497    rtems_termios_dequeue_characters(Console_Port_Data[minor].termios_data, 1);
498    if (rtems_termios_dequeue_characters(
499         Console_Port_Data[minor].termios_data, 1)) {
500        if (Console_Port_Tbl[minor].pDeviceFlow != &ns16550_flow_RTSCTS) {
501          ns16550_negate_RTS(minor);
502        }
503        Console_Port_Data[minor].bActive = FALSE;
504        ns16550_enable_interrupts(minor, NS16550_ENABLE_ALL_INTR_EXCEPT_TX);
505        break;
506      }
507
508      ucInterruptId = (*getReg)(pNS16550, NS16550_INTERRUPT_ID);
509    }
510  } while((ucInterruptId&0xf)!=0x1);
511}
512
513/*
514 *  ns16550_isr
515 */
516
517NS16550_STATIC rtems_isr ns16550_isr(
518  rtems_vector_number vector
519)
520{
521  int     minor;
522
523  for(minor=0;minor<Console_Port_Count;minor++) {
524    if(Console_Port_Tbl[minor].ulIntVector == vector &&
525       Console_Port_Tbl[minor].deviceType == SERIAL_NS16550 ) {
526      ns16550_process(minor);
527    }
528  }
529}
530
531/*
532 *  ns16550_enable_interrupts
533 *
534 *  This routine initializes the port to have the specified interrupts masked.
535 */
536
537NS16550_STATIC void ns16550_enable_interrupts(
538  int minor,
539  int mask
540)
541{
542  uint32_t       pNS16550;
543  setRegister_f  setReg;
544
545  pNS16550 = Console_Port_Tbl[minor].ulCtrlPort1;
546  setReg   = Console_Port_Tbl[minor].setRegister;
547
548  (*setReg)(pNS16550, NS16550_INTERRUPT_ENABLE, mask);
549}
550
551/*
552 *  ns16550_initialize_interrupts
553 *
554 *  This routine initializes the port to operate in interrupt driver mode.
555 */
556
557NS16550_STATIC void ns16550_initialize_interrupts(int minor)
558{
559  ns16550_init(minor);
560
561  Console_Port_Data[minor].bActive = FALSE;
562
563  set_vector(ns16550_isr, Console_Port_Tbl[minor].ulIntVector, 1);
564
565  ns16550_enable_interrupts(minor, NS16550_ENABLE_ALL_INTR);
566}
567
568/*
569 *  ns16550_write_support_int
570 *
571 *  Console Termios output entry point.
572 */
573
574NS16550_STATIC int ns16550_write_support_int(
575  int   minor,
576  const char *buf,
577  int   len
578)
579{
580  uint32_t       Irql;
581  uint32_t       pNS16550;
582  setRegister_f  setReg;
583
584  setReg   = Console_Port_Tbl[minor].setRegister;
585  pNS16550 = Console_Port_Tbl[minor].ulCtrlPort1;
586
587  /*
588   *  We are using interrupt driven output and termios only sends us
589   *  one character at a time.
590   */
591
592  if ( !len )
593    return 0;
594
595  if(Console_Port_Tbl[minor].pDeviceFlow != &ns16550_flow_RTSCTS) {
596    ns16550_assert_RTS(minor);
597  }
598
599  rtems_interrupt_disable(Irql);
600    if ( Console_Port_Data[minor].bActive == FALSE) {
601      Console_Port_Data[minor].bActive = TRUE;
602      ns16550_enable_interrupts(minor, NS16550_ENABLE_ALL_INTR);
603    }
604    (*setReg)(pNS16550, NS16550_TRANSMIT_BUFFER, *buf);
605  rtems_interrupt_enable(Irql);
606
607  return 1;
608}
609
610/*
611 *  ns16550_write_support_polled
612 *
613 *  Console Termios output entry point.
614 *
615 */
616
617NS16550_STATIC int ns16550_write_support_polled(
618  int         minor,
619  const char *buf,
620  int         len
621)
622{
623  int nwrite = 0;
624
625  /*
626   * poll each byte in the string out of the port.
627   */
628  while (nwrite < len) {
629    /*
630     * transmit character
631     */
632    ns16550_write_polled(minor, *buf++);
633    nwrite++;
634  }
635
636  /*
637   * return the number of bytes written.
638   */
639  return nwrite;
640}
641
642/*
643 *  ns16550_inbyte_nonblocking_polled
644 *
645 *  Console Termios polling input entry point.
646 */
647
648NS16550_STATIC int ns16550_inbyte_nonblocking_polled(
649  int minor
650)
651{
652  uint32_t             pNS16550;
653  unsigned char        ucLineStatus;
654  char                 cChar;
655  getRegister_f        getReg;
656
657  pNS16550 = Console_Port_Tbl[minor].ulCtrlPort1;
658  getReg   = Console_Port_Tbl[minor].getRegister;
659
660  ucLineStatus = (*getReg)(pNS16550, NS16550_LINE_STATUS);
661  if(ucLineStatus & SP_LSR_RDY) {
662    cChar = (*getReg)(pNS16550, NS16550_RECEIVE_BUFFER);
663    return (int)cChar;
664  } else {
665    return -1;
666  }
667}
Note: See TracBrowser for help on using the repository browser.