Skip to content

Universal UART FIFO Control API Needed for Low-Latency Protocols (e.g., LIN) #3

@kbader94

Description

@kbader94

Kernel level uart fifo control

Background & Motivation

Low-Latency RX Needed for Real-Time Protocols

Many serial UART devices (e.g., 16550A) trigger RX interrupts only when their FIFO reaches a certain threshold (often more than 8 bytes). However, many communication protocols or sensor controls require immediate response to each individual byte received. Delayed interrupts leads to unacceptable latency, breaking protocols that expect immediate responses. The ability to set serial fifo levels is commonly available on modern chipsets. To improve availability of low latency protocols in the linux kernel, it would be beneficial to have a universal API for configuring serial FIFO options/

For example:

The LIN protocol is a low-speed serial communication protocol meant to augment CAN protocol in automotive applications. While initial work to implement the protocol into linux was completed years ago, the slave response mode has never been completely implemented due to the difficulty of achieving latency requirements of the LIN protocol within a linux system.

A discussion on lin-bus implementation in linux highlights the requirements:

"It is necessary to set UART into mode where Rx FIFO is disabled or Rx threshold is set to one. Unfortunately, there is no unified API for this parameter or even its availability which is supported across multiple/all UART drivers implementations."


Current Userspace Implementation for Fifo Size Control via sysfs on 16550 Devices

Yoshihiro Yunomae implemented a user-space interface (ie. /sys/class/tty/ttyS*/rx_trig_bytes) to adjust the RX trigger level in serial devices, enabling select values (eg: 1, 4, 8, or 14 bytes). While this approach successfully implements user-space fifo trigger level control in 16550A-compatible serial devices, implementation in other devices is limited or missing altogether. The current implementation takes advantage of the fact that nearly every 8250-based device after the 16550A uses bits 6 and 7 of the same 'FCR' register to set the RX FIFO trigger level to one of 4 predefined values, in order to offer some degree of control. However, many devices offer further configuration options available with other registers. For example, in addition to the legacy 16550A compatible RX trigger levels, the 16C750 also offers an enhanced 64 byte FIFO mode, which requires setting an additional flag in the FCR[5] as well as the DLAB bit from the LCR[7]. The 16850 offers fully programmable FIFOs(1-127 byte levels) via a completely seperate 'TRG' register.

The diversity of devices represented within the 8250 serial driver core warrants a more flexible approach to FIFO configuration within the tty subsystem. Furthermore, the prevalence of fifo control in serial drivers outside of the 8250 core (such as PL011) warrants extending this interface to the broader tty subsystem. I propose implementing a universal fifo_control callback in the uart_ops to hook into device-specific fifo control functions for each port type which supports . It may be desirable to implement a series of device-specific functions within the tty subsystem to enable setting fifo configuration. Additionally, it may be desirable to disable the FIFO completely (ie. 16450 mode), change the DMA transfer mode to single byte, or set the FIFO timeout where available. Furthermore, many common serial devices which do contain completely programmable FIFOs (eg. the PCI-based WCH384), are currently unsupported in the current sysfs implementation because they are misconfigured. The CH382 is a perfect example, as a 16750-based serial device it SHOULD be supported by the current rx_trig_bytes sysfs interface, but is currently configured as a 16850 which the current rx_trig_bytes does not support.

FIFO Control Scheme Variance

There are considerable implementation differences between how different 16550A-based serial devices implement FIFO control.

UART Model FIFO Size (max) RX Trigger Levels TX Trigger Levels Registers Used for FIFO Control Notes
8250 None N/A N/A N/A No FIFO support
16450 None N/A N/A N/A No FIFO support
16550 16 (buggy) N/A N/A FCR exists but doesn't function FIFO is broken - not widely used
16550A 16 1, 4, 8, 14 bytes N/A FCR[7:6] FCR controls FIFO
16650 32 8, 16, 24, 28 bytes 16, 8, 24, 30 FCR Tx and Rx programmable FIFOs
16750 64 1, 4, 8, 14 (legacy)
1, 16, 32, 56 (enhanced)
N/A FCR[7:6:5]
LCR[7]
Supports legacy 16 byte FIFO and enhanced 64 byte mode via FCR[5]
NOTE: LCR[7] MUST = 1 to enable FCR[5]
16850/16950 128 Fully programmable (1–127 bytes) Fully programmable (1–127 bytes) FCR
FCTR
TRG
FCTR to select the trigger table
TRG to program the FIFO in 'table D'

Userspace Interface Considerations

Current serial settings such as baudrate, parity, etc, are sent from userspace to the serial subsystem via IOCTL and a termios data struct. However, to avoid implementing a termios3, and to maintain compatability with the current rx_trig_bytes API, we'll implement a series of new sysfs entries, but we'll move everything to the serial_core to keep consistency with other serial sysfs entries.

Reference Documentation

This patch series was designed to work universally, with ANY linux serial device. However, the following datasheets have been specifically used and referenced throughout the design and development of this patch series.


Sub-issues

Metadata

Metadata

Assignees

Labels

enhancementNew feature or request

Projects

Status

In Progress

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions