Skip to content
LinuxDeep Dive Published Updated 7 min readViews unavailable

Linux SPI Messages: Chip-Select Boundaries, Transfer Limits, and Bus Evidence

Debug Linux SPI protocol failures by tracing queued messages, chip-select continuity, clock mode, transfer limits, and userspace spidev semantics.

SPI failures often look deceptively simple: a peripheral returns all zeroes, a command works only at low speed, or a register read returns the previous address. The Linux SPI subsystem separates the controller driver, which moves bits through a hardware controller, from protocol drivers, which know what a particular peripheral expects. Between them, a spi_message represents one queued transaction made of one or more transfers. Understanding those boundaries is essential because clock mode, chip-select timing, word width, DMA constraints, and transfer-size limits all affect the bytes a device actually sees.

This article focuses on operational diagnosis and driver contracts. It does not assume every SPI controller implements every mode or timing option, and it does not recommend probing undocumented registers on production devices.

Identify the controller and peripheral relationship

An SPI controller is registered with the SPI core and exposes child spi_device objects for attached peripherals. The peripheral’s protocol driver binds through normal driver-model matching. Device instantiation commonly comes from firmware description, such as Device Tree, or platform-specific board data. A /dev/spidevB.D node is a userspace interface for devices explicitly bound to spidev; it is not proof that a peripheral’s production driver is loaded.

Record the kernel version, controller driver, peripheral compatible/modalias, bus and chip-select number, maximum clock rate, and any firmware properties for mode or word width. Read-only inspection can include:

ls -l /sys/bus/spi/devices/
cat /sys/bus/spi/devices/spiB.C/modalias
readlink -f /sys/bus/spi/devices/spiB.C/driver
journalctl -k -b --no-pager | grep -i spi

Replace B and C with the discovered bus and chip-select identifiers. Some files may be absent, and sysfs naming alone is not a substitute for the board schematic. Confirm that the chip-select is physically connected to the expected device and that the pinmux assigns SCK, MOSI, MISO, and chip-select correctly.

SPI is full duplex at the wire level: each transmitted clock shifts a bit out and a bit in. Protocols often use one direction at a time, but a read command can require sending an opcode and dummy bytes while capturing returned data in the same transaction. Treat MOSI and MISO independently in a logic-analyzer capture.

Mode, speed, and words are protocol properties

SPI mode combines clock polarity and clock phase. Modes 0 through 3 specify idle clock level and sampling edge. A peripheral datasheet defines the expected mode; Linux mode flags must agree. A wrong mode can produce data that is nearly plausible because transitions are sampled on the wrong edge. Confirm actual SCK idle level and sample edge on the wire rather than trusting a software configuration dump alone.

The requested speed is a ceiling or target mediated by the controller and device contract; actual clock generation can be rounded to a supported divider. A peripheral’s maximum rating may vary with voltage, temperature, trace length, and bus loading. Begin below the specified maximum during bring-up, then increase only after signal integrity and timing margins are established. spi-max-frequency in firmware describes a device limit; it does not guarantee the controller can generate that exact frequency.

Word width and bit order also need exact agreement. Many devices use 8-bit words and MSB-first order, but a protocol may specify another width. A controller advertises supported bits-per-word and speed constraints; requests outside those constraints can be rejected or handled differently by a particular driver. Validate the peripheral data sheet, controller capabilities, and protocol driver’s setup before changing a register.

Understand messages and chip-select continuity

A spi_message is queued to the controller and contains one or more spi_transfer segments. Transfers in one message can preserve transaction sequencing and often keep chip select asserted between segments unless the protocol or transfer flags request a change. Separate messages usually represent separate transactions and may release chip select between them. The exact controller and core semantics for chip-select changes, delays, and timing constraints should be checked in the API documentation and source for the running kernel.

This difference explains a common bug: a driver sends a command in one call and reads the response in a second call, but the peripheral requires chip select to remain asserted across both phases. The bus waveform then contains a deassertion gap, and the device treats the read as a new command. Put transfers into one message when the protocol defines a continuous transaction, and verify the wire-level CS trace. Conversely, keeping CS asserted across a gap can violate a peripheral’s maximum inter-byte timing or protocol framing.

Transfer length can be limited by the controller’s FIFO, DMA engine, descriptor format, or driver callbacks. A protocol driver must not assume that one arbitrarily large buffer maps to one hardware transfer. The SPI core exposes controller constraints such as maximum transfer and message size; splitting data must preserve peripheral framing and chip-select semantics. A controller can support a large message while imposing a smaller segment limit.

DMA adds alignment and lifetime requirements. A controller may require aligned buffers or use bounce buffers; the device’s DMA address constraints are not interchangeable with CPU pointer alignment. Use kernel-managed SPI APIs and DMA mapping rules rather than programming a controller’s registers from a protocol driver. Capture whether errors occur only above a length threshold, at particular alignments, or with a specific transfer direction.

Instrument the actual transaction

For a failed register access, record the intended bytes, message composition, speed, mode, word width, CS behavior, and expected response. If userspace uses spidev, inspect its mode and speed settings and use SPI_IOC_MESSAGE when a full-duplex or multi-segment transaction requires chip-select continuity. Plain read() and write() operations are half-duplex and can deactivate CS between operations; they are not equivalent to a combined command-and-response message.

Do not treat spidev_test as proof that a real peripheral protocol works. Loopback can validate wiring and a subset of controller behavior, but it does not validate a device’s command timing, status semantics, or response format. For hardware tests, use a known-safe read-only command and record both the decoded transfer and raw captures. Avoid issuing arbitrary writes to flash, power controllers, or calibration storage.

When a transfer fails, inspect the returned status and controller logs. Distinguish queue submission failure, controller timeout, DMA mapping error, short transfer, and a protocol-level checksum or status failure. The SPI core’s asynchronous model means completion callbacks or synchronous wrappers report transport completion, not that the peripheral accepted or acted on the command. The protocol driver must validate response status separately.

Common failure patterns

All-zero or all-one reads can indicate a disconnected MISO path, wrong pinmux, inactive peripheral, incorrect CS polarity, or a device held in reset. Confirm power rails and reset sequencing before raising clock speed. A response shifted by one byte often points to opcode/dummy-cycle framing or CS discontinuity. A response that fails only under load may indicate shared-bus contention, controller queue latency, electrical signal integrity, or an overlong transfer.

If the peripheral works with a userspace test but not the kernel driver, compare all wire properties rather than only the byte sequence. The mode, word width, speed, CS delays, and message segmentation may differ. If it works only with the kernel driver, compare whether the driver uses a required IRQ, regulator, reset GPIO, runtime-PM reference, or post-transfer delay that a raw spidev test omitted.

For shared SPI buses, each peripheral should ignore clocks while its CS is inactive. Check that two devices do not claim the same chip-select and that the firmware description matches physical wiring. A device with multiple chip-selects or unusual CS control may need controller-specific support. Do not use software-controlled GPIO CS as a generic workaround without checking timing, polarity, and controller limitations.

Acceptance and safe change procedure

Before changing a deployed SPI driver or device-tree property, establish a baseline with:

  • controller and peripheral identity, driver binding, firmware description, and board revision;
  • documented mode, maximum speed, word width, CS polarity, setup/hold timing, and reset sequence;
  • maximum transfer/message size and relevant DMA/alignment constraints;
  • known-good read command, expected bytes, raw waveform capture, and error logs;
  • tests at cold boot, runtime suspend/resume, bus contention, and the largest supported transfer.

Change one property at a time on a representative development board. Keep a bootable recovery image and ensure changes cannot prevent access to the system’s management path. Compare wire captures and protocol responses after the change. A successful SPI completion with incorrect returned data is still a failed transaction.

The practical model is a chain of contracts: firmware describes the peripheral, the protocol driver constructs messages, the SPI core schedules them, and the controller produces electrical signals. Diagnose each boundary with evidence before changing clocks or rewriting device data.

Related:

Sources:

Comments