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