Back to home page

LXR

 
 

    


File indexing completed on 2025-05-11 08:24:05

0001 /**
0002  *  @file
0003  *
0004  *  This file contains the TTY driver for the National Semiconductor NS16550.
0005  *
0006  *  This part is widely cloned and second sourced.  It is found in a number
0007  *  of "Super IO" controllers.
0008  *
0009  *  This driver uses the termios pseudo driver.
0010  */
0011 
0012 /*
0013  *  COPYRIGHT (c) 1998 by Radstone Technology
0014  *
0015  *  THIS FILE IS PROVIDED TO YOU, THE USER, "AS IS", WITHOUT WARRANTY OF ANY
0016  *  KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
0017  *  IMPLIED WARRANTY OF FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK
0018  *  AS TO THE QUALITY AND PERFORMANCE OF ALL CODE IN THIS FILE IS WITH YOU.
0019  *
0020  *  You are hereby granted permission to use, copy, modify, and distribute
0021  *  this file, provided that this notice, plus the above copyright notice
0022  *  and disclaimer, appears in all copies. Radstone Technology will provide
0023  *  no support for this code.
0024  *
0025  *  COPYRIGHT (c) 1989-2012.
0026  *  On-Line Applications Research Corporation (OAR).
0027  *
0028  *  The license and distribution terms for this file may be
0029  *  found in the file LICENSE in this distribution or at
0030  *  http://www.rtems.org/license/LICENSE.
0031  */
0032 
0033 #include <stdlib.h>
0034 
0035 #include <rtems.h>
0036 #include <rtems/libio.h>
0037 #include <rtems/ringbuf.h>
0038 #include <rtems/bspIo.h>
0039 #include <rtems/termiostypes.h>
0040 
0041 #include <libchip/serial.h>
0042 #include <libchip/sersupp.h>
0043 
0044 #include <bsp.h>
0045 
0046 #include <libchip/ns16550_p.h>
0047 #include <libchip/ns16550.h>
0048 
0049 #if defined(BSP_FEATURE_IRQ_EXTENSION)
0050   #include <bsp/irq.h>
0051 #elif defined(BSP_FEATURE_IRQ_LEGACY)
0052   #include <bsp/irq.h>
0053 #elif defined(__PPC__) || defined(__i386__)
0054   #include <bsp/irq.h>
0055   #define BSP_FEATURE_IRQ_LEGACY
0056   #ifdef BSP_SHARED_HANDLER_SUPPORT
0057     #define BSP_FEATURE_IRQ_LEGACY_SHARED_HANDLER_SUPPORT
0058   #endif
0059 #endif
0060 
0061 typedef struct {
0062   uint8_t ucModemCtrl;
0063   int transmitFifoChars;
0064 } NS16550Context;
0065 
0066 /*
0067  * Driver functions
0068  */
0069 
0070 NS16550_STATIC void ns16550_init(int minor);
0071 
0072 NS16550_STATIC int ns16550_open(
0073   int major,
0074   int minor,
0075   void  * arg
0076 );
0077 
0078 NS16550_STATIC int ns16550_close(
0079   int major,
0080   int minor,
0081   void  * arg
0082 );
0083 
0084 NS16550_STATIC void ns16550_write_polled(
0085   int   minor,
0086   char  cChar
0087 );
0088 
0089 NS16550_STATIC int ns16550_assert_RTS(
0090   int minor
0091 );
0092 
0093 NS16550_STATIC int ns16550_negate_RTS(
0094   int minor
0095 );
0096 
0097 NS16550_STATIC int ns16550_assert_DTR(
0098   int minor
0099 );
0100 
0101 NS16550_STATIC int ns16550_negate_DTR(
0102   int minor
0103 );
0104 
0105 NS16550_STATIC void ns16550_initialize_interrupts(int minor);
0106 
0107 NS16550_STATIC void ns16550_cleanup_interrupts(int minor);
0108 
0109 NS16550_STATIC ssize_t ns16550_write_support_int(
0110   int   minor,
0111   const char *buf,
0112   size_t len
0113 );
0114 
0115 NS16550_STATIC ssize_t ns16550_write_support_polled(
0116   int   minor,
0117   const char *buf,
0118   size_t len
0119   );
0120 
0121 int ns16550_inbyte_nonblocking_polled(
0122   int minor
0123 );
0124 
0125 NS16550_STATIC void ns16550_enable_interrupts(
0126   console_tbl *c,
0127   int         mask
0128 );
0129 
0130 NS16550_STATIC int ns16550_set_attributes(
0131   int                   minor,
0132   const struct termios *t
0133 );
0134 
0135 #if defined(BSP_FEATURE_IRQ_EXTENSION) || defined(BSP_FEATURE_IRQ_LEGACY)
0136   NS16550_STATIC void ns16550_isr(void *arg);
0137 #endif
0138 
0139 RTEMS_INTERRUPT_LOCK_DEFINE(static, ns16550_lock, "NS16550")
0140 
0141 /*
0142  * Flow control is only supported when using interrupts
0143  */
0144 
0145 const console_flow ns16550_flow_RTSCTS = {
0146   ns16550_negate_RTS,             /* deviceStopRemoteTx */
0147   ns16550_assert_RTS              /* deviceStartRemoteTx */
0148 };
0149 
0150 const console_flow ns16550_flow_DTRCTS = {
0151   ns16550_negate_DTR,             /* deviceStopRemoteTx */
0152   ns16550_assert_DTR              /* deviceStartRemoteTx */
0153 };
0154 
0155 const console_fns ns16550_fns = {
0156   libchip_serial_default_probe,   /* deviceProbe */
0157   ns16550_open,                   /* deviceFirstOpen */
0158   ns16550_close,                  /* deviceLastClose */
0159   NULL,                           /* deviceRead */
0160   ns16550_write_support_int,      /* deviceWrite */
0161   ns16550_init,                   /* deviceInitialize */
0162   ns16550_write_polled,           /* deviceWritePolled */
0163   ns16550_set_attributes,         /* deviceSetAttributes */
0164   true                            /* deviceOutputUsesInterrupts */
0165 };
0166 
0167 const console_fns ns16550_fns_polled = {
0168   libchip_serial_default_probe,        /* deviceProbe */
0169   ns16550_open,                        /* deviceFirstOpen */
0170   ns16550_close,                       /* deviceLastClose */
0171   ns16550_inbyte_nonblocking_polled,   /* deviceRead */
0172   ns16550_write_support_polled,        /* deviceWrite */
0173   ns16550_init,                        /* deviceInitialize */
0174   ns16550_write_polled,                /* deviceWritePolled */
0175   ns16550_set_attributes,              /* deviceSetAttributes */
0176   false                                /* deviceOutputUsesInterrupts */
0177 };
0178 
0179 static uint32_t NS16550_GetBaudDivisor(const console_tbl *c, uint32_t baud)
0180 {
0181   uint32_t clock = c->ulClock;
0182   uint32_t baudDivisor = (clock != 0 ? clock : 115200) / (baud * 16);
0183 
0184   if (c->deviceType == SERIAL_NS16550_WITH_FDR) {
0185     uint32_t fractionalDivider = 0x10;
0186     uint32_t err = baud;
0187     uint32_t mulVal;
0188     uint32_t divAddVal;
0189 
0190     clock /= 16 * baudDivisor;
0191     for (mulVal = 1; mulVal < 16; ++mulVal) {
0192       for (divAddVal = 0; divAddVal < mulVal; ++divAddVal) {
0193         uint32_t actual = (mulVal * clock) / (mulVal + divAddVal);
0194         uint32_t newErr = actual > baud ? actual - baud : baud - actual;
0195 
0196         if (newErr < err) {
0197           err = newErr;
0198           fractionalDivider = (mulVal << 4) | divAddVal;
0199         }
0200       }
0201     }
0202 
0203     (*c->setRegister)(
0204       c->ulCtrlPort1,
0205       NS16550_FRACTIONAL_DIVIDER,
0206       fractionalDivider
0207     );
0208   }
0209 
0210   return baudDivisor;
0211 }
0212 
0213 /*
0214  *  ns16550_init
0215  */
0216 
0217 void ns16550_init(int minor)
0218 {
0219   uintptr_t               pNS16550;
0220   uint8_t                 ucDataByte;
0221   uint32_t                ulBaudDivisor;
0222   NS16550Context        *pns16550Context;
0223   setRegister_f           setReg;
0224   getRegister_f           getReg;
0225   console_tbl             *c = Console_Port_Tbl [minor];
0226 
0227   pns16550Context=(NS16550Context *)malloc(sizeof(NS16550Context));
0228 
0229   if (pns16550Context == NULL) {
0230     printk( "%s: Error: Not enough memory\n", __func__);
0231     rtems_fatal_error_occurred( 0xdeadbeef);
0232   }
0233 
0234   Console_Port_Data[minor].pDeviceContext=(void *)pns16550Context;
0235   pns16550Context->ucModemCtrl=SP_MODEM_IRQ;
0236 
0237   pNS16550 = c->ulCtrlPort1;
0238   setReg   = c->setRegister;
0239   getReg   = c->getRegister;
0240 
0241   /* Clear the divisor latch, clear all interrupt enables,
0242    * and reset and
0243    * disable the FIFO's.
0244    */
0245 
0246   (*setReg)(pNS16550, NS16550_LINE_CONTROL, 0x0);
0247   ns16550_enable_interrupts( c, NS16550_DISABLE_ALL_INTR );
0248 
0249   /* Set the divisor latch and set the baud rate. */
0250 
0251   ulBaudDivisor = NS16550_GetBaudDivisor(c, (uintptr_t) c->pDeviceParams);
0252   ucDataByte = SP_LINE_DLAB;
0253   (*setReg)(pNS16550, NS16550_LINE_CONTROL, ucDataByte);
0254 
0255   /* XXX */
0256   (*setReg)(pNS16550,NS16550_TRANSMIT_BUFFER,(uint8_t)(ulBaudDivisor & 0xffU));
0257   (*setReg)(
0258     pNS16550,NS16550_INTERRUPT_ENABLE,
0259     (uint8_t)(( ulBaudDivisor >> 8 ) & 0xffU )
0260   );
0261 
0262   /* Clear the divisor latch and set the character size to eight bits */
0263   /* with one stop bit and no parity checking. */
0264   ucDataByte = EIGHT_BITS;
0265   (*setReg)(pNS16550, NS16550_LINE_CONTROL, ucDataByte);
0266 
0267   /* Enable and reset transmit and receive FIFOs. TJA     */
0268   ucDataByte = SP_FIFO_ENABLE;
0269   (*setReg)(pNS16550, NS16550_FIFO_CONTROL, ucDataByte);
0270 
0271   ucDataByte = SP_FIFO_ENABLE | SP_FIFO_RXRST | SP_FIFO_TXRST;
0272   (*setReg)(pNS16550, NS16550_FIFO_CONTROL, ucDataByte);
0273 
0274   ns16550_enable_interrupts(c, NS16550_DISABLE_ALL_INTR);
0275 
0276   /* Set data terminal ready. */
0277   /* And open interrupt tristate line */
0278   (*setReg)(pNS16550, NS16550_MODEM_CONTROL,pns16550Context->ucModemCtrl);
0279 
0280   (*getReg)(pNS16550, NS16550_LINE_STATUS );
0281   (*getReg)(pNS16550, NS16550_RECEIVE_BUFFER );
0282 }
0283 
0284 /*
0285  *  ns16550_open
0286  */
0287 
0288 int ns16550_open(
0289   int major,
0290   int minor,
0291   void *arg
0292 )
0293 {
0294   rtems_libio_open_close_args_t *oc = (rtems_libio_open_close_args_t *) arg;
0295   struct rtems_termios_tty *tty = (struct rtems_termios_tty *) oc->iop->data1;
0296   console_tbl *c = Console_Port_Tbl [minor];
0297   console_data *d = &Console_Port_Data [minor];
0298 
0299   d->termios_data = tty;
0300 
0301   /* Assert DTR */
0302   if (c->pDeviceFlow != &ns16550_flow_DTRCTS) {
0303     ns16550_assert_DTR( minor);
0304   }
0305 
0306   /* Set initial baud */
0307   rtems_termios_set_initial_baud( tty, (intptr_t) c->pDeviceParams);
0308 
0309   if (c->pDeviceFns->deviceOutputUsesInterrupts) {
0310     ns16550_initialize_interrupts( minor);
0311     ns16550_enable_interrupts( c, NS16550_ENABLE_ALL_INTR_EXCEPT_TX);
0312   }
0313 
0314   return RTEMS_SUCCESSFUL;
0315 }
0316 
0317 /*
0318  *  ns16550_close
0319  */
0320 
0321 int ns16550_close(
0322   int      major,
0323   int      minor,
0324   void    * arg
0325 )
0326 {
0327   console_tbl *c = Console_Port_Tbl [minor];
0328 
0329   /*
0330    * Negate DTR
0331    */
0332   if (c->pDeviceFlow != &ns16550_flow_DTRCTS) {
0333     ns16550_negate_DTR(minor);
0334   }
0335 
0336   ns16550_enable_interrupts(c, NS16550_DISABLE_ALL_INTR);
0337 
0338   if (c->pDeviceFns->deviceOutputUsesInterrupts) {
0339     ns16550_cleanup_interrupts(minor);
0340   }
0341 
0342   return(RTEMS_SUCCESSFUL);
0343 }
0344 
0345 /**
0346  * @brief Polled write for NS16550.
0347  */
0348 void ns16550_outch_polled(console_tbl *c, char out)
0349 {
0350   uintptr_t port = c->ulCtrlPort1;
0351   getRegister_f get = c->getRegister;
0352   setRegister_f set = c->setRegister;
0353   uint32_t status = 0;
0354   rtems_interrupt_lock_context lock_context;
0355 
0356   /* Save port interrupt mask */
0357   uint32_t interrupt_mask = get( port, NS16550_INTERRUPT_ENABLE);
0358 
0359   /* Disable port interrupts */
0360   ns16550_enable_interrupts( c, NS16550_DISABLE_ALL_INTR);
0361 
0362   while (true) {
0363     /* Try to transmit the character in a critical section */
0364     rtems_interrupt_lock_acquire(&ns16550_lock, &lock_context);
0365 
0366     /* Read the transmitter holding register and check it */
0367     status = get( port, NS16550_LINE_STATUS);
0368     if ((status & SP_LSR_THOLD) != 0) {
0369       /* Transmit character */
0370       set( port, NS16550_TRANSMIT_BUFFER, out);
0371 
0372       /* Finished */
0373       rtems_interrupt_lock_release(&ns16550_lock, &lock_context);
0374       break;
0375     } else {
0376       rtems_interrupt_lock_release(&ns16550_lock, &lock_context);
0377     }
0378 
0379     /* Wait for transmitter holding register to be empty */
0380     do {
0381       status = get( port, NS16550_LINE_STATUS);
0382     } while ((status & SP_LSR_THOLD) == 0);
0383   }
0384 
0385   /* Restore port interrupt mask */
0386   set( port, NS16550_INTERRUPT_ENABLE, interrupt_mask);
0387 }
0388 
0389 void ns16550_write_polled(int minor, char out)
0390 {
0391   console_tbl *c = Console_Port_Tbl [minor];
0392 
0393   ns16550_outch_polled( c, out );
0394 }
0395 
0396 /*
0397  * These routines provide control of the RTS and DTR lines
0398  */
0399 
0400 /*
0401  *  ns16550_assert_RTS
0402  */
0403 
0404 NS16550_STATIC int ns16550_assert_RTS(int minor)
0405 {
0406   uint32_t                pNS16550;
0407   rtems_interrupt_lock_context lock_context;
0408   NS16550Context        *pns16550Context;
0409   setRegister_f           setReg;
0410 
0411   pns16550Context=(NS16550Context *) Console_Port_Data[minor].pDeviceContext;
0412 
0413   pNS16550 = Console_Port_Tbl[minor]->ulCtrlPort1;
0414   setReg   = Console_Port_Tbl[minor]->setRegister;
0415 
0416   /*
0417    * Assert RTS
0418    */
0419   rtems_interrupt_lock_acquire(&ns16550_lock, &lock_context);
0420   pns16550Context->ucModemCtrl|=SP_MODEM_RTS;
0421   (*setReg)(pNS16550, NS16550_MODEM_CONTROL, pns16550Context->ucModemCtrl);
0422   rtems_interrupt_lock_release(&ns16550_lock, &lock_context);
0423   return 0;
0424 }
0425 
0426 /*
0427  *  ns16550_negate_RTS
0428  */
0429 
0430 NS16550_STATIC int ns16550_negate_RTS(int minor)
0431 {
0432   uint32_t                pNS16550;
0433   rtems_interrupt_lock_context lock_context;
0434   NS16550Context        *pns16550Context;
0435   setRegister_f           setReg;
0436 
0437   pns16550Context=(NS16550Context *) Console_Port_Data[minor].pDeviceContext;
0438 
0439   pNS16550 = Console_Port_Tbl[minor]->ulCtrlPort1;
0440   setReg   = Console_Port_Tbl[minor]->setRegister;
0441 
0442   /*
0443    * Negate RTS
0444    */
0445   rtems_interrupt_lock_acquire(&ns16550_lock, &lock_context);
0446   pns16550Context->ucModemCtrl&=~SP_MODEM_RTS;
0447   (*setReg)(pNS16550, NS16550_MODEM_CONTROL, pns16550Context->ucModemCtrl);
0448   rtems_interrupt_lock_release(&ns16550_lock, &lock_context);
0449   return 0;
0450 }
0451 
0452 /*
0453  * These flow control routines utilise a connection from the local DTR
0454  * line to the remote CTS line
0455  */
0456 
0457 /*
0458  *  ns16550_assert_DTR
0459  */
0460 
0461 NS16550_STATIC int ns16550_assert_DTR(int minor)
0462 {
0463   uint32_t                pNS16550;
0464   rtems_interrupt_lock_context lock_context;
0465   NS16550Context        *pns16550Context;
0466   setRegister_f           setReg;
0467 
0468   pns16550Context=(NS16550Context *) Console_Port_Data[minor].pDeviceContext;
0469 
0470   pNS16550 = Console_Port_Tbl[minor]->ulCtrlPort1;
0471   setReg   = Console_Port_Tbl[minor]->setRegister;
0472 
0473   /*
0474    * Assert DTR
0475    */
0476   rtems_interrupt_lock_acquire(&ns16550_lock, &lock_context);
0477   pns16550Context->ucModemCtrl|=SP_MODEM_DTR;
0478   (*setReg)(pNS16550, NS16550_MODEM_CONTROL, pns16550Context->ucModemCtrl);
0479   rtems_interrupt_lock_release(&ns16550_lock, &lock_context);
0480   return 0;
0481 }
0482 
0483 /*
0484  *  ns16550_negate_DTR
0485  */
0486 
0487 NS16550_STATIC int ns16550_negate_DTR(int minor)
0488 {
0489   uint32_t                pNS16550;
0490   rtems_interrupt_lock_context lock_context;
0491   NS16550Context        *pns16550Context;
0492   setRegister_f           setReg;
0493 
0494   pns16550Context=(NS16550Context *) Console_Port_Data[minor].pDeviceContext;
0495 
0496   pNS16550 = Console_Port_Tbl[minor]->ulCtrlPort1;
0497   setReg   = Console_Port_Tbl[minor]->setRegister;
0498 
0499   /*
0500    * Negate DTR
0501    */
0502   rtems_interrupt_lock_acquire(&ns16550_lock, &lock_context);
0503   pns16550Context->ucModemCtrl&=~SP_MODEM_DTR;
0504   (*setReg)(pNS16550, NS16550_MODEM_CONTROL,pns16550Context->ucModemCtrl);
0505   rtems_interrupt_lock_release(&ns16550_lock, &lock_context);
0506   return 0;
0507 }
0508 
0509 /*
0510  *  ns16550_set_attributes
0511  *
0512  *  This function sets the channel to reflect the requested termios
0513  *  port settings.
0514  */
0515 
0516 int ns16550_set_attributes(
0517   int                   minor,
0518   const struct termios *t
0519 )
0520 {
0521   uint32_t                pNS16550;
0522   uint32_t                ulBaudDivisor;
0523   uint8_t                 ucLineControl;
0524   uint32_t                baud_requested;
0525   setRegister_f           setReg;
0526   rtems_interrupt_lock_context lock_context;
0527   const console_tbl      *c = Console_Port_Tbl [minor];
0528 
0529   pNS16550 = c->ulCtrlPort1;
0530   setReg   = c->setRegister;
0531 
0532   /*
0533    *  Calculate the baud rate divisor
0534    *
0535    *  Assert ensures there is no division by 0.
0536    */
0537 
0538   baud_requested = rtems_termios_baud_to_number(t->c_ospeed);
0539   _Assert( baud_requested != 0 );
0540   ulBaudDivisor = NS16550_GetBaudDivisor(c, baud_requested);
0541 
0542   ucLineControl = 0;
0543 
0544   /*
0545    *  Parity
0546    */
0547 
0548   if (t->c_cflag & PARENB) {
0549     ucLineControl |= SP_LINE_PAR;
0550     if (!(t->c_cflag & PARODD))
0551       ucLineControl |= SP_LINE_ODD;
0552   }
0553 
0554   /*
0555    *  Character Size
0556    */
0557 
0558   if (t->c_cflag & CSIZE) {
0559     switch (t->c_cflag & CSIZE) {
0560       case CS5:  ucLineControl |= FIVE_BITS;  break;
0561       case CS6:  ucLineControl |= SIX_BITS;   break;
0562       case CS7:  ucLineControl |= SEVEN_BITS; break;
0563       case CS8:  ucLineControl |= EIGHT_BITS; break;
0564     }
0565   } else {
0566     ucLineControl |= EIGHT_BITS;               /* default to 9600,8,N,1 */
0567   }
0568 
0569   /*
0570    *  Stop Bits
0571    */
0572 
0573   if (t->c_cflag & CSTOPB) {
0574     ucLineControl |= SP_LINE_STOP;              /* 2 stop bits */
0575   } else {
0576     ;                                           /* 1 stop bit */
0577   }
0578 
0579   /*
0580    *  Now actually set the chip
0581    */
0582 
0583   rtems_interrupt_lock_acquire(&ns16550_lock, &lock_context);
0584 
0585     /*
0586      *  Set the baud rate
0587      *
0588      *  NOTE: When the Divisor Latch Access Bit (DLAB) is set to 1,
0589      *        the transmit buffer and interrupt enable registers
0590      *        turn into the LSB and MSB divisor latch registers.
0591      */
0592 
0593     (*setReg)(pNS16550, NS16550_LINE_CONTROL, SP_LINE_DLAB);
0594     (*setReg)(pNS16550, NS16550_TRANSMIT_BUFFER, ulBaudDivisor&0xff);
0595     (*setReg)(pNS16550, NS16550_INTERRUPT_ENABLE, (ulBaudDivisor>>8)&0xff);
0596 
0597     /*
0598      *  Now write the line control
0599      */
0600     (*setReg)(pNS16550, NS16550_LINE_CONTROL, ucLineControl );
0601 
0602   rtems_interrupt_lock_release(&ns16550_lock, &lock_context);
0603 
0604   return 0;
0605 }
0606 
0607 #if defined(BSP_FEATURE_IRQ_EXTENSION) || defined(BSP_FEATURE_IRQ_LEGACY)
0608 
0609 /**
0610  * @brief Process interrupt.
0611  */
0612 NS16550_STATIC void ns16550_process( int minor)
0613 {
0614   console_tbl *c = Console_Port_Tbl [minor];
0615   console_data *d = &Console_Port_Data [minor];
0616   NS16550Context *ctx = d->pDeviceContext;
0617   uint32_t port = c->ulCtrlPort1;
0618   getRegister_f get = c->getRegister;
0619   int i;
0620   char buf [SP_FIFO_SIZE];
0621 
0622   /* Iterate until no more interrupts are pending */
0623   do {
0624     /* Fetch received characters */
0625     i = 0;
0626     while ((get(port, NS16550_LINE_STATUS) & SP_LSR_RDY) != 0) {
0627       buf[i++] = (char) get(port, NS16550_RECEIVE_BUFFER);
0628       if (i == SP_FIFO_SIZE) {
0629         /* Enqueue fetched characters */
0630         rtems_termios_enqueue_raw_characters( d->termios_data, buf, i);
0631         i = 0;
0632       }
0633     }
0634 
0635     if (i > 0)
0636       rtems_termios_enqueue_raw_characters( d->termios_data, buf, i);
0637 
0638     /* Check if we can dequeue transmitted characters */
0639     if (ctx->transmitFifoChars > 0
0640         && (get( port, NS16550_LINE_STATUS) & SP_LSR_THOLD) != 0) {
0641       /* Dequeue transmitted characters */
0642       rtems_termios_dequeue_characters(
0643         d->termios_data,
0644         ctx->transmitFifoChars
0645       );
0646     }
0647   } while ((get( port, NS16550_INTERRUPT_ID) & SP_IID_0) == 0);
0648 }
0649 #endif
0650 
0651 /**
0652  * @brief Transmits up to @a len characters from @a buf.
0653  *
0654  * This routine is invoked either from task context with disabled interrupts to
0655  * start a new transmission process with exactly one character in case of an
0656  * idle output state or from the interrupt handler to refill the transmitter.
0657  *
0658  * Returns always zero.
0659  */
0660 ssize_t ns16550_write_support_int(
0661   int minor,
0662   const char *buf,
0663   size_t len
0664 )
0665 {
0666   console_tbl *c = Console_Port_Tbl [minor];
0667   console_data *d = &Console_Port_Data [minor];
0668   NS16550Context *ctx = d->pDeviceContext;
0669   uint32_t port = c->ulCtrlPort1;
0670   setRegister_f set = c->setRegister;
0671   int i = 0;
0672   int out = len > SP_FIFO_SIZE ? SP_FIFO_SIZE : len;
0673 
0674   for (i = 0; i < out; ++i) {
0675     set( port, NS16550_TRANSMIT_BUFFER, buf [i]);
0676   }
0677 
0678   ctx->transmitFifoChars = out;
0679 
0680   if (out > 0) {
0681     ns16550_enable_interrupts( c, NS16550_ENABLE_ALL_INTR);
0682   } else {
0683     ns16550_enable_interrupts( c, NS16550_ENABLE_ALL_INTR_EXCEPT_TX);
0684   }
0685 
0686   return 0;
0687 }
0688 
0689 /*
0690  *  ns16550_enable_interrupts
0691  *
0692  *  This routine initializes the port to have the specified interrupts masked.
0693  */
0694 NS16550_STATIC void ns16550_enable_interrupts(
0695   console_tbl *c,
0696   int         mask
0697 )
0698 {
0699   uint32_t       pNS16550;
0700   setRegister_f  setReg;
0701 
0702   pNS16550 = c->ulCtrlPort1;
0703   setReg   = c->setRegister;
0704 
0705   (*setReg)(pNS16550, NS16550_INTERRUPT_ENABLE, mask);
0706 }
0707 
0708 #if defined(BSP_FEATURE_IRQ_EXTENSION) || defined(BSP_FEATURE_IRQ_LEGACY)
0709   void ns16550_isr(void *arg)
0710   {
0711     int minor = (intptr_t) arg;
0712 
0713     ns16550_process( minor);
0714   }
0715 #endif
0716 
0717 /*
0718  *  ns16550_initialize_interrupts
0719  *
0720  *  This routine initializes the port to operate in interrupt driver mode.
0721  */
0722 NS16550_STATIC void ns16550_initialize_interrupts( int minor)
0723 {
0724 #if defined(BSP_FEATURE_IRQ_EXTENSION) || defined(BSP_FEATURE_IRQ_LEGACY)
0725   console_tbl *c = Console_Port_Tbl [minor];
0726 #endif
0727 
0728   #ifdef BSP_FEATURE_IRQ_EXTENSION
0729     {
0730       rtems_status_code sc = RTEMS_SUCCESSFUL;
0731       sc = rtems_interrupt_handler_install(
0732         c->ulIntVector,
0733         "NS16550",
0734         RTEMS_INTERRUPT_SHARED,
0735         ns16550_isr,
0736         (void *) (intptr_t) minor
0737       );
0738       if (sc != RTEMS_SUCCESSFUL) {
0739         /* FIXME */
0740         printk( "%s: Error: Install interrupt handler\n", __func__);
0741         rtems_fatal_error_occurred( 0xdeadbeef);
0742       }
0743     }
0744   #elif defined(BSP_FEATURE_IRQ_LEGACY)
0745     {
0746       int rv = 0;
0747       #ifdef BSP_FEATURE_IRQ_LEGACY_SHARED_HANDLER_SUPPORT
0748         rtems_irq_connect_data cd = {
0749           c->ulIntVector,
0750           ns16550_isr,
0751           (void *) minor,
0752           NULL,
0753           NULL,
0754           NULL,
0755           NULL
0756         };
0757         rv = BSP_install_rtems_shared_irq_handler( &cd);
0758       #else
0759         rtems_irq_connect_data cd = {
0760           c->ulIntVector,
0761           ns16550_isr,
0762           (void *) minor,
0763           NULL,
0764           NULL,
0765           NULL
0766         };
0767         rv = BSP_install_rtems_irq_handler( &cd);
0768       #endif
0769       if (rv == 0) {
0770         /* FIXME */
0771         printk( "%s: Error: Install interrupt handler\n", __func__);
0772         rtems_fatal_error_occurred( 0xdeadbeef);
0773       }
0774     }
0775   #endif
0776 }
0777 
0778 NS16550_STATIC void ns16550_cleanup_interrupts(int minor)
0779 {
0780   #if defined(BSP_FEATURE_IRQ_EXTENSION)
0781     rtems_status_code sc = RTEMS_SUCCESSFUL;
0782     console_tbl *c = Console_Port_Tbl [minor];
0783     sc = rtems_interrupt_handler_remove(
0784       c->ulIntVector,
0785       ns16550_isr,
0786       (void *) (intptr_t) minor
0787     );
0788     if (sc != RTEMS_SUCCESSFUL) {
0789       /* FIXME */
0790       printk("%s: Error: Remove interrupt handler\n", __func__);
0791       rtems_fatal_error_occurred(0xdeadbeef);
0792     }
0793   #elif defined(BSP_FEATURE_IRQ_LEGACY)
0794     int rv = 0;
0795     console_tbl *c = Console_Port_Tbl [minor];
0796     rtems_irq_connect_data cd = {
0797       .name = c->ulIntVector,
0798       .hdl = ns16550_isr,
0799       .handle = (void *) minor
0800     };
0801     rv = BSP_remove_rtems_irq_handler(&cd);
0802     if (rv == 0) {
0803       /* FIXME */
0804       printk("%s: Error: Remove interrupt handler\n", __func__);
0805       rtems_fatal_error_occurred(0xdeadbeef);
0806     }
0807   #endif
0808 }
0809 
0810 /*
0811  *  ns16550_write_support_polled
0812  *
0813  *  Console Termios output entry point.
0814  *
0815  */
0816 
0817 ssize_t ns16550_write_support_polled(
0818   int         minor,
0819   const char *buf,
0820   size_t      len
0821 )
0822 {
0823   int nwrite = 0;
0824 
0825   /*
0826    * poll each byte in the string out of the port.
0827    */
0828   while (nwrite < len) {
0829     /*
0830      * transmit character
0831      */
0832     ns16550_write_polled(minor, *buf++);
0833     nwrite++;
0834   }
0835 
0836   /*
0837    * return the number of bytes written.
0838    */
0839   return nwrite;
0840 }
0841 
0842 /*
0843  *  Debug gets() support
0844  */
0845 int ns16550_inch_polled(
0846   console_tbl *c
0847 )
0848 {
0849   uint32_t             pNS16550;
0850   unsigned char        ucLineStatus;
0851   uint8_t              cChar;
0852   getRegister_f        getReg;
0853 
0854   pNS16550 = c->ulCtrlPort1;
0855   getReg   = c->getRegister;
0856 
0857   ucLineStatus = (*getReg)(pNS16550, NS16550_LINE_STATUS);
0858   if (ucLineStatus & SP_LSR_RDY) {
0859     cChar = (*getReg)(pNS16550, NS16550_RECEIVE_BUFFER);
0860     return (int)cChar;
0861   }
0862   return -1;
0863 }
0864 
0865 /*
0866  *  ns16550_inbyte_nonblocking_polled
0867  *
0868  *  Console Termios polling input entry point.
0869  */
0870 int ns16550_inbyte_nonblocking_polled(int minor)
0871 {
0872   console_tbl *c = Console_Port_Tbl [minor];
0873 
0874   return ns16550_inch_polled( c );
0875 }