Skip to content

Day 27 - SPI Sensor Driver (Dual-Bus Architecture)

Overview

This day extends the existing I2C-based sensor driver to support SPI, while keeping a shared sensor core layer.

Goal: - Reuse the same mysensor_core - Add a new SPI transport layer - Verify Device Tree + SPI driver binding - Ensure compatibility with STM32 fake sensor (SPI slave)


Architecture

Application / hwmon │ mysensor_core (bus-agnostic) │ ┌───────────────┬───────────────┐ │ │ │ I2C bus SPI bus (future: UART, etc.) mysensor_i2c mysensor_spi


Key Design Concepts

1. Bus Abstraction Layer

Core does not depend on I2C/SPI directly.

Example:

struct mysensor_bus_ops {
    int (*read_reg)(void *ctx, u8 reg, u8 *val);
    int (*write_reg)(void *ctx, u8 reg, u8 val);
};

Each bus driver provides its own implementation.


2. SPI Protocol Design (STM32 fake sensor)

SPI is full-duplex and frame-based.

Read operation requires two transfers:

1st frame (command) MOSI: [0x80 | reg] [0x00]

2nd frame (fetch data) MOSI: [NOP] [0x00] MISO: [xx] [value]


3. Why spi_write_then_read() is NOT suitable

spi_write_then_read() performs:

  • write phase
  • separate read phase

But STM32 slave expects:

  • fixed 2-byte full-duplex frames

Therefore must use:

spi_sync()
spi_transfer

4. SPI Transfer Implementation

Each read consists of 2 transfers:

transfer[0] → send command
transfer[1] → fetch result

cs_change = 1 is used to separate frames.


5. Device Tree Binding

Example:

mysensor@0 {
    compatible = "myvendor,mysensor-spi";
    reg = <0>;   // CS0
};

Driver must provide:

  • of_match_table
  • spi_device_id (recommended)

6. spidev Conflict

Default Raspberry Pi enables:

spidev@0

This conflicts with custom driver using same CS.

Solution:

dtoverlay=spi0-1cs,cs0_spidev=disabled
dtoverlay=myspi_sensor

7. Boot vs Runtime Overlay

  • Boot-time: /boot/firmware/config.txt
  • Runtime: dtoverlay command

SPI driver should use boot-time overlay for stability.


8. Verification Strategy

Do NOT rely only on dmesg.

Correct validation order:

  1. /sys/bus/spi/devices/
  2. driver bind
  3. hwmon output
  4. sensor data

Debug Lessons Learned

Chip ID = 0x00

Cause: - wrong SPI transfer pattern

Fix: - switch to spi_sync + 2-phase transfer


"chipselect already in use"

Cause: - spidev occupying CS0

Fix: - disable spidev


temp1_input returns -EAGAIN

Cause: - cache not ready - no IRQ, polling disabled

Fix: - enable polling


Summary

  • SPI bus successfully integrated into existing architecture
  • Core layer reused without modification
  • Device Tree binding validated
  • STM32 SPI slave protocol aligned with Linux SPI driver

This confirms the design is scalable to multiple transport layers.