16c95x Serial Port Driver Direct

The driver calculates divisor values using the enhanced baud rate generator. For non-standard rates, the driver can switch to a fractional divisor mode.

# Unbind the generic driver
echo -n "0000:01:00.0" > /sys/bus/pci/drivers/serial/unbind
# Bind to the 16C95x-capable driver
echo -n "0000:01:00.0" > /sys/bus/pci/drivers/8250-pci/bind
  • Implement struct uart_port for each port, mapping to MMIO base + channel offset.
  • Use tty_flip_buffer_push or similar helper to hand receive data to line discipline.
  • Use uart_add_one_port/uart_remove_one_port to register ports.
  • Due to the high potential data rates (up to 15 Mbps), the CPU must service interrupts very quickly. If the operating system has high interrupt latency, the 128-byte RX FIFO may overflow before the ISR runs. The driver must often utilize a "Low Latency" mode or increase the service thread priority.

    setserial /dev/ttyS0 uart 16C95x fifo 112 16c95x serial port driver

    For kernel driver modification, ensure UART_CAP_FIFO_SIZE and UART_CAP_RPM are set correctly in 8250_port.c.


    For half-duplex RS-485 networks, the 16C95x can automatically assert RTS (as a direction control signal) before transmitting and deassert it after the last stop bit. The driver calculates divisor values using the enhanced

    Set the following in EFCR (Extended Features Control Register):

    The driver then simply writes to THR; the hardware handles the turnaround delay. Implement struct uart_port for each port, mapping to

    To understand the driver requirements, one must first understand the hardware capabilities it manages.

    The Tx interrupt (THRE – Transmitter Holding Register Empty) asserts when the Tx FIFO falls below the programmed trigger level (default empty). The driver’s Tx ISR should fill the FIFO up to its capacity or until the Tx buffer is empty.

    void uart_isr_tx(uart_t *dev)
    while (!(read_reg(dev->base + UART_LSR) & LSR_TXFIFO_FULL)) 
            if (ringbuf_empty(&dev->tx_ring)) 
                // No more data: disable Tx interrupt
                uint8_t ier = read_reg(dev->base + UART_IER);
                write_reg(dev->base + UART_IER, ier & ~IER_THRE);
                break;
    uint8_t data = ringbuf_pop(&dev->tx_ring);
            write_reg(dev->base + UART_THR, data);
    

    A common pitfall: After initial Tx, you must enable THRE interrupt only when there is data to send. Many drivers enable it unconditionally, causing an immediate interrupt storm.