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 fixed 64-byte data chunks (
OA_CHUNK_SIZE). 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 and a CRC. 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 driver keeps two pools of frame buffers in the descriptor — one for
transmit, one for receive — each managed as a FIFO ring so frames are
serviced strictly in order. A single pump function, oa_tc6_thread(),
performs one full service cycle: it frames every buffer in the
OA_BUFF_TX_READY state out to SPI and de-frames the received chunks back
into free receive buffers. The caller invokes it from its main loop or in
response to the MAC-PHY's interrupt pin.
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.
OA-TC6 Driver Configuration
Driver Initialization
The caller must first bring up a no_os_spi_desc (SPI mode 0). The first
API to call is oa_tc6_init, passing that SPI descriptor in
struct oa_tc6_init_param along with:
bufst_polling— how transmit/receive credits are refreshed before a data transaction:OA_TC6_REG_POLLreads theBUFST(0x0B) register, whileOA_TC6_FOOTER_POLLderives the credits from the footer of a data chunk (one fewer control transaction per cycle).prote_spi— set when the MAC-PHY is configured for protected (complement-checked) control transactions.
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).
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; 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.
oa_tc6_thread is the transfer pump described above and 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,
with an enum oa_tc6_event code, when notable conditions occur during a
service cycle:
OA_TC6_EVENT_RX/OA_TC6_EVENT_TX— a frame was received / its last chunk was transferred to the MAC-PHY, freeing its TX buffer.OA_TC6_EVENT_TXPE— transmit protocol error.OA_TC6_EVENT_TXBOE/OA_TC6_EVENT_TXBUE— transmit buffer overflow / underflow.OA_TC6_EVENT_RXBOE— receive buffer overflow.OA_TC6_EVENT_LOFE— loss of frame.OA_TC6_EVENT_HDRE— header error.OA_TC6_EVENT_SYNCE— loss of synchronization.
The error events are decoded from the STATUS0 register after the footer
latches EXST.
Events are collected during oa_tc6_thread and delivered once it has
released its lock, so the callback may use the register access and frame
buffer APIs. Each event is reported at most once per oa_tc6_thread call;
on OA_TC6_EVENT_RX, drain all frames with oa_tc6_get_rx_frame until it
returns -ENOENT.
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 de-framed to buffers.tx_frames/tx_bytes— frames and payload bytes framed out to SPI.rx_drop_fd— receive frames dropped because the footer flagged them invalid (FD).rx_drop_nobuf— receive frames dropped because the buffer pool was exhausted.exst_events— number of data-chunk footers that latchedEXST.
Compile-time Configuration
The buffer pools and service limits are tunable through Kconfig (or by overriding the macros before including the header):
CONFIG_OA_TX_FRAME_BUFF_NUM(default 10) — transmit buffer pool depth.CONFIG_OA_RX_FRAME_BUFF_NUM(default 10) — receive buffer pool depth.CONFIG_OA_CHUNK_BUFFER_SIZE(default 1514) — per-frame payload capacity.CONFIG_OA_THREAD_RX_LIMIT(default 5) — maximum frames received peroa_tc6_threadcall, bounding how long a single service cycle may run.CONFIG_OA_ZERO_SWO_ONLY(default 1) — only accept frames that start at chunk offset zero.
OA-TC6 Driver Initialization Example
struct oa_tc6_desc *oa_desc;
struct no_os_spi_desc *spi_desc;
struct no_os_spi_init_param spi_ip = {
.device_id = SPI_DEVICE_ID,
.max_speed_hz = SPI_BAUDRATE,
.chip_select = SPI_CS,
.mode = NO_OS_SPI_MODE_0,
.platform_ops = SPI_OPS,
.extra = SPI_EXTRA,
};
struct oa_tc6_init_param oa_ip = {
.bufst_polling = OA_TC6_FOOTER_POLL,
.prote_spi = false,
};
ret = no_os_spi_init(&spi_desc, &spi_ip);
if (ret)
goto error;
oa_ip.comm_desc = spi_desc;
ret = oa_tc6_init(&oa_desc, &oa_ip);
if (ret)
goto error;