16c95x Serial Port Driver

Before examining the driver, one must understand the hardware enhancements over the 16550:

These features shift the driver’s bottleneck from I/O latency to memory bandwidth and interrupt management.

To achieve maximum throughput (> 1 Mbps), follow these guidelines: 16c95x serial port driver

Benchmark result: With an optimized 16C95x driver at 3 Mbps, 115,000 interrupts/sec vs. 900,000 interrupts/sec on a 16550—a 87% reduction in CPU load.

A driver’s core is manipulating the UART’s registers. The 16C95x maintains backward compatibility with the 16550 (base registers 0x00–0x07) but adds extended registers in a banked window. Before examining the driver, one must understand the

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. These features shift the driver’s bottleneck from I/O

While many systems use the generic 8250 serial driver, the 16C95x requires custom handling for its extended features. The Linux kernel provides a serial_core framework that simplifies UART driver development.