OA-TC6 no-OS driver
See drivers/net/oa_tc6 (doxygen) for the Doxygen documentation.
Supported Devices
The OA-TC6 layer is a transport driver rather than a device driver: it implements the OPEN Alliance TC6 (10BASE-T1x MAC-PHY Serial Interface, or "OA-SPI") protocol and is shared by the no-OS MAC-PHY drivers that speak it.
Overview
OA-TC6 defines a chunk-based SPI protocol for exchanging Ethernet frames and control registers with a MAC-PHY that has no parallel MII/RMII interface. All traffic — frame data and register access alike — is multiplexed over the same 4-wire SPI link.
Two kinds of SPI transaction are used:
Control transactions read and write the MAC-PHY register space. Each register lives in a Memory Map Selector (MMS) block and is addressed with the
OA_MMS_REG(mms, reg)helper. Control transactions are optionally protected (prote_spi), in which case each register is transferred together with its bit-wise complement for integrity checking.Data transactions move Ethernet frames as a stream of data chunks of 8, 16, 32 or 64 bytes (
CONFIG0.CPS). Every transmit chunk is prefixed with a 4-byte data header and every receive chunk is suffixed with a 4-byte data footer, each protected by a parity bit. The header/footer carry the framing bits (DVdata valid,SV/SWOstart valid and offset,EV/EBOend valid and offset) that let a frame span an arbitrary number of chunks.
Flow control is credit-based. The footer reports the number of receive chunks
available (RCA) and the remaining transmit credit (TXC); the driver
never sends more data chunks than the MAC-PHY has buffer space for, and reads
exactly the number of chunks the device has queued. The footer also latches
the EXST (extended status), HDRB (header bad) and SYNC bits that
surface device-level events.
Driver Architecture
The caller allocates the descriptor and the frame buffer pools. The receive
pool and each transmit queue are FIFO rings, so frames are serviced strictly
in order. A pump function performs one service cycle: it frames the buffers in
the OA_BUFF_TX_READY state out to SPI and de-frames the received chunks
back into free receive buffers. oa_tc6_thread blocks for each SPI
transaction; oa_tc6_thread_async starts it with DMA and returns, and
processes the received chunks on a later call. The caller invokes the pump
from its main loop or in response to the MAC-PHY's interrupt pin, setting
irq_pending in the descriptor so the next cycle reads a footer.
A transmit buffer is released only once the MAC-PHY accepted all its chunks.
A failed SPI transfer, a rejected header (HDRB) or a footer with
SYNC = 0 leaves the frame queued, and it is sent again.
When the host has no data to transmit but the device has frames waiting, the
driver issues empty data chunks (data header with DV = 0) purely to clock
receive chunks out of the MAC-PHY, so receive is never starved by an idle
transmit path. When the receive pool is full, the driver sets NORX in the
headers.
Transmit Queues
Transmit frames go through one or more priority queues, each with its own
frame buffer ring and wire VS value. The transmit credit of a queue is only
reported in the footer of a chunk stamped with its VS, so a queue waiting
for credit has an empty chunk with its VS sent to solicit it.
tx_schedselects strict priority (OA_TC6_TX_SCHED_STRICT_PRIO, the highest-index queue first) or round-robin (OA_TC6_TX_SCHED_ROUND_ROBIN).oa_tc6_set_tx_queues replaces the queue set after init.
oa_tc6_set_tx_credit_reserve keeps a number of chunks free in every transmit FIFO, for a MAC-PHY whose footer
TXCreports only the FIFO that last received anSV.
OA-TC6 Driver Configuration
Driver Initialization
The caller must first bring up a CAPI SPI device (struct capi_spi_device,
SPI mode 0). The first API to call is oa_tc6_init, passing a descriptor
and struct oa_tc6_init_param with:
comm_desc— the CAPI SPI device.rx_pool/rx_pool_size— the receive frame buffer pool.tx_pool/tx_pool_sizefor a single transmit queue, ortx_q/tx_q_countfor priority queues, andtx_sched.prote_spi— use protected (complement-checked) control transactions. A MAC-PHY that boots protected is detected and kept protected.cps— the chunk payload size, anOA_TC6_CONFIG0_CPS_*value (default 64 bytes).no_reset— skip the soft reset, for a MAC-PHY fresh out of a power-on reset.sw_fcs/tx_no_pad— compute and check the Ethernet FCS on the host, and optionally leave short transmit frames unpadded.async_mode/async_xfer_done_cb— enable oa_tc6_thread_async, with an optional callback run from the SPI completion interrupt.ctrl_mutex_cfg/data_mutex_cfg— CAPI mutexes for multi-threaded use.ctrl_lockmust be recursive. Leave them NULL for bare-metal single-threaded use.
Make sure oa_tc6_init returns 0, which means the driver was initialized correctly.
Register Access
oa_tc6_reg_read, oa_tc6_reg_write and oa_tc6_reg_update provide
32-bit access to the MMS-encoded register space (read-modify-write for the
update helper). Addresses are built with OA_MMS_REG(mms, reg).
oa_tc6_reg_read_multiple and oa_tc6_reg_write_multiple access up to
OA_CTRL_MAX_REGS consecutive registers in one control transaction. The
echo of every control transaction is checked.
Runtime Configuration
oa_tc6_set_config and oa_tc6_get_config program and read back the
CONFIG0 fields that may change after init (struct oa_tc6_config):
txcthresh, cut-through (txcte/rxcte), receive frame alignment
(csarfe/zarfe) and frame timestamping (ftse/ftss). Unless
zarfe is set, the chunk that ends a transmit frame also starts the next
frame of the same queue.
Frame Transmit and Receive
Frames are exchanged through borrow/return buffer handles rather than copies:
oa_tc6_get_tx_frame hands out a free transmit buffer of a queue; the caller fills
data/lenand returns it with oa_tc6_put_tx_frame, which marks itOA_BUFF_TX_READY.oa_tc6_get_rx_frame returns the oldest completed receive frame (or oa_tc6_get_rx_frame_match_vs to fetch one whose Vendor-Specific field matches). After consuming it, the caller returns the buffer to the pool with oa_tc6_put_rx_frame. The FCS is not included in
len.oa_tc6_thread or oa_tc6_thread_async must be called periodically to make progress on both directions.
The struct oa_tc6_frame_buffer handle also exposes per-frame metadata:
vs (Vendor Specific), tsc (transmit timestamp capture request),
frame_drop (receive frame flagged invalid), and rtsa/rtsp
(receive timestamp added / parity).
Events and Callbacks
oa_tc6_register_callback installs a user callback that the driver invokes
from the pump, never from an interrupt, with an enum oa_tc6_event code, a
detail pointer and the user argument:
OA_TC6_EVENT_RX/OA_TC6_EVENT_TX— a frame was received / the MAC-PHY accepted queued transmit frames, freeing their buffers.OA_TC6_EVENT_RX_READY— the MAC-PHY has receive chunks waiting.OA_TC6_EVENT_TX_CREDITS— transmit credit recovered from zero.OA_TC6_EVENT_RESET— the MAC-PHY reset and lost its configuration (SYNC = 0orSTATUS0.RESETC). Reconfigure it; the driver does not setCONFIG0.SYNCagain on its own.OA_TC6_EVENT_ERR_*— one event per error bit set inSTATUS0/STATUS1, with the detail pointing atstruct oa_tc6_error_detail, and the footer parity (OA_TC6_EVENT_ERR_RX_PARITY) and frame drop (OA_TC6_EVENT_ERR_RX_FRAME_DROP) errors.OA_TC6_EVENT_ERROR— aSTATUS0bit set that maps to no other event.
STATUS0 and STATUS1 are read and cleared once per service cycle after
a footer latches EXST or HDRB.
Transfer Flags
oa_tc6_get_xfer_flags returns the latched footer status (exst,
hdrb, sync) and optionally clears the latch. sync reflects the
instantaneous data-synchronization state; exst and hdrb stay latched
until read so a transient event is not missed between service cycles.
Software Reset
oa_tc6_sw_reset writes the RESET register and polls
STATUS0.RESETC until the MAC-PHY reports the reset has completed.
Statistics
The driver keeps software counters in struct oa_tc6_stats, read with
oa_tc6_get_stats and cleared with oa_tc6_reset_stats:
rx_frames/rx_bytes— frames and payload bytes delivered to the receive pool.tx_frames/tx_bytes— frames and payload bytes accepted by the MAC-PHY.rx_drop_fd— receive frames the footer flagged invalid (FD).rx_drop_nobuf— receive frames dropped because the buffer pool was full.exst_events— number of data-chunk footers that latchedEXST.rx_footer_parity_err— data-chunk footers with a parity error.
Compile-time Configuration
The service limits and buffer sizes are tunable through Kconfig (or by overriding the macros before including the header):
CONFIG_OA_CHUNK_BUFFER_SIZE(default 1530) — per-frame payload capacity.CONFIG_OA_SPI_BUFF_LEN(default 1632) — SPI data transaction buffer size, which bounds the chunks per transaction.CONFIG_OA_THREAD_RX_LIMIT(default 5) — maximum data transactions per service cycle while receive chunks are waiting.CONFIG_OA_ASYNC_XFER_TIMEOUT_US(default 50000) — watchdog for an asynchronous transfer; 0 disables it.
OA-TC6 Driver Initialization Example
static struct oa_tc6_frame_buffer tx_pool[2];
static struct oa_tc6_frame_buffer rx_pool[5];
static struct oa_tc6_desc oa_desc;
struct oa_tc6_init_param oa_ip = {
.comm_desc = spi_dev, /* CAPI SPI device, SPI mode 0 */
.tx_pool = tx_pool,
.tx_pool_size = NO_OS_ARRAY_SIZE(tx_pool),
.rx_pool = rx_pool,
.rx_pool_size = NO_OS_ARRAY_SIZE(rx_pool),
};
ret = oa_tc6_init(&oa_desc, &oa_ip);
if (ret)
goto error;