SPI 驱动

一、Linux SPI 子系统

应用(spidev)
  ↓
SPI 核心(spi.c)
  ↓
spi_controller(控制器驱动,如 spi-imx.c、spi-stm32.c)
  ↓
spi_device → 设备驱动(如 mysensor.c)
  ↓
SPI 硬件

二、关键结构

struct spi_controller {
    struct device dev;
    int bus_num;
    int num_chipselect;
    const struct spi_controller_ops *ops;
    struct list_head queue;
    struct spi_message *cur_msg;
    // ...
};

struct spi_device {
    struct device dev;
    struct spi_controller *controller;
    u32 max_speed_hz;
    u8 chip_select;
    u8 bits_per_word;
    u32 mode;
#define SPI_CPHA    0x01
#define SPI_CPOL    0x02
#define SPI_CS_HIGH 0x04
#define SPI_LSB_FIRST 0x08
#define SPI_3WIRE   0x10
#define SPI_LOOP    0x20
#define SPI_NO_CS   0x40
#define SPI_READY   0x80
#define SPI_TX_DUAL 0x100
#define SPI_TX_QUAD 0x200
#define SPI_RX_DUAL 0x400
#define SPI_RX_QUAD 0x800
    // ...
};

struct spi_driver {
    int (*probe)(struct spi_device *spi);
    int (*remove)(struct spi_device *spi);
    void (*shutdown)(struct spi_device *spi);
    struct device_driver driver;
    const struct spi_device_id *id_table;
};

struct spi_transfer {
    const void *tx_buf;
    void *rx_buf;
    unsigned len;
    u32 speed_hz;
    u8 bits_per_word;
    u16 delay_usecs;
    u8 cs_change;
    // ...
};

struct spi_message {
    struct list_head transfers;
    struct spi_device *spi;
    void (*complete)(void *context);
    void *context;
    // ...
};

三、设备驱动示例

1. 设备树

&spi1 {
    status = "okay";
    pinctrl-0 = <&pinctrl_spi1>;
    clock-frequency = <10000000>;

    flash@0 {
        compatible = "jedec,spi-nor";
        reg = <0>;
        spi-max-frequency = <20000000>;
        spi-cpol; spi-cpha;
        // ...
    };

    mysensor: mysensor@1 {
        compatible = "myvendor,myspi";
        reg = <1>;
        spi-max-frequency = <1000000>;
        spi-cpha;
        interrupt-parent = <&gpio2>;
        interrupts = <10 IRQ_TYPE_LEVEL_HIGH>;
    };
};

2. 驱动代码

#include <linux/spi/spi.h>

struct my_priv {
    struct spi_device *spi;
    struct mutex lock;
};

static int my_probe(struct spi_device *spi)
{
    struct my_priv *priv;
    priv = devm_kzalloc(&spi->dev, sizeof(*priv), GFP_KERNEL);
    if (!priv) return -ENOMEM;
    priv->spi = spi;
    mutex_init(&priv->lock);
    spi_set_drvdata(spi, priv);

    // 配置已在设备树中设置(mode、speed)
    // 也可以再设:
    spi->mode |= SPI_CPHA;
    spi->max_speed_hz = 1000000;
    spi_setup(spi);

    return 0;
}

static int my_remove(struct spi_device *spi)
{
    return 0;
}

static const struct spi_device_id my_id[] = {
    { "myspi", 0 },
    { }
};
MODULE_DEVICE_TABLE(spi, my_id);

static const struct of_device_id my_of[] = {
    { .compatible = "myvendor,myspi" },
    { }
};

static struct spi_driver my_driver = {
    .driver = {
        .name = "myspi",
        .of_match_table = my_of,
    },
    .probe    = my_probe,
    .remove   = my_remove,
    .id_table = my_id,
};
module_spi_driver(my_driver);

四、SPI 通信

1. 同步(spi_sync)

// 单字节读写
u8 tx[2] = { reg, 0 };
u8 rx[2];
struct spi_transfer t = {
    .tx_buf = tx,
    .rx_buf = rx,
    .len = 2,
    .speed_hz = 1000000,
};
struct spi_message m;
spi_message_init(&m);
spi_message_add_tail(&t, &m);
ret = spi_sync(spi, &m);

2. 异步(spi_async)

ret = spi_async(spi, &m);
// m.complete 回调中处理

3. 工具函数

// 单字节读写
int spi_write_then_read(struct spi_device *spi,
                        const void *txbuf, unsigned n_tx,
                        void *rxbuf, unsigned n_rx);

// 单向写
int spi_write(struct spi_device *spi, const void *buf, int len);

// 单向读(写 0xFF 触发)
int spi_read(struct spi_device *spi, void *buf, int len);

4. 多 transfer 消息

struct spi_message m;
struct spi_transfer t1 = { .len = 1, .tx_buf = &reg };
struct spi_transfer t2 = { .len = 4, .rx_buf = buf };
spi_message_init(&m);
spi_message_add_tail(&t1, &m);
spi_message_add_tail(&t2, &m);
spi_sync(spi, &m);

五、控制器驱动

static int my_transfer_one_message(struct spi_controller *ctlr,
                                   struct spi_message *msg)
{
    // 处理每个 transfer
    struct spi_transfer *t;
    list_for_each_entry(t, &msg->transfers, transfer_list) {
        // 配置 DMA / FIFO
        // 启动
        // 等待完成
    }
    spi_finalize_current_message(ctlr);
    return 0;
}

static const struct spi_controller_ops my_ops = {
    .transfer_one_message = my_transfer_one_message,
    // ...
};

static int my_probe(struct platform_device *pdev)
{
    struct spi_controller *ctlr = spi_alloc_master(&pdev->dev, sizeof(*priv));
    ctlr->bus_num = -1;                  // 自动分配
    ctlr->num_chipselect = 4;
    ctlr->ops = &my_ops;
    devm_spi_register_controller(&pdev->dev, ctlr);
    return 0;
}

六、用户态驱动(spidev)

int fd = open("/dev/spidev1.0", O_RDWR);

u8 mode = SPI_CPHA;
ioctl(fd, SPI_IOC_WR_MODE, &mode);

u32 speed = 1000000;
ioctl(fd, SPI_IOC_WR_MAX_SPEED_HZ, &speed);

u8 tx[] = { 0x10, 0 };
u8 rx[2];
struct spi_ioc_transfer tr = {
    .tx_buf = (unsigned long)tx,
    .rx_buf = (unsigned long)rx,
    .len = 2,
    .speed_hz = 1000000,
};
ioctl(fd, SPI_IOC_MESSAGE(1), &tr);

七、调试

# 工具
spidev_test -D /dev/spidev1.0 -v -p "\xAA\x55"

# 内核
dmesg | grep spi
cat /sys/bus/spi/devices/spi1.0/

# 时序问题
- 用逻辑分析仪看 MISO/MOSI/SCK/CS
- 降低频率排查
- 检查 cs_change、bits_per_word