spi驱动

核心思想:房东与租客

把这个关系想象成房东和租客:

  • SPI 控制器 (SPI Controller):这是 SoC 内部的硬件,好比一栋公寓楼
  • platform_driver (控制器驱动):这是房东。他的职责是管理这栋公寓楼(初始化硬件、设置时钟、注册总线),并把房间挂出去招租。
  • SPI 设备 (SPI Device):这是连接在 SPI 接口上的外部芯片(如 Flash、传感器),好比一个租客
  • spi_driver (设备驱动):这是租客自己。他知道如何使用房间里的设施(通过 SPI 协议读写数据)。

房东 (platform_driver) 并不关心租客 (spi_driver) 在房间里做什么,他只负责提供可用的房间 (spi_device)。租客也无需关心公寓楼是怎么建的,他只需要拿到钥匙 (struct spi_device) 就能入住。


1. 设备树 (DTS) 中的描述

设备树清晰地描述了这种“公寓楼”和“租客”的硬件关系。

// 根节点
/ {
    soc {
        // ... 其他SoC内部设备

        // 1. 定义 "公寓楼" (SPI控制器)
        // 这是 SoC 内部的一个设备,所以它是一个 platform_device
        spi1: ecspi@02008000 {
            compatible = "fsl,imx6ul-ecspi"; // 用于匹配 "房东" (platform_driver)
            reg = <0x02008000 0x4000>;       // 控制器寄存器地址
            interrupts = <GIC_SPI 30 IRQ_TYPE_LEVEL_HIGH>;
            clocks = <&clks IMX6UL_CLK_ECSPI1>, <&clks IMX6UL_CLK_ECSPI1>;
            clock-names = "ipg", "per";
            status = "disabled";

            // 2. 在 "公寓楼" 里定义 "租客" (SPI设备)
            // 这是一个子节点,表示它连接在这个SPI控制器上
            flash: m25p80@0 {
                compatible = "jedec,spi-nor"; // 用于匹配 "租客" (spi_driver)
                reg = <0>;                   // 片选(Chip Select)编号,这里是 CS0
                spi-max-frequency = <20000000>; // 最大SPI时钟频率
            };
        };

        // ...
    };
};
  • spi1: ecspi@02008000 节点
    • 它描述了 SoC 内部的 SPI 控制器硬件。
    • 内核会为它创建一个 struct platform_device
    • 它的 compatible = "fsl,imx6ul-ecspi" 将用于匹配一个 platform_driver
  • flash: m25p80@0 节点
    • 它是 spi1子节点,表示这个 Flash 芯片物理上连接在 spi1 控制器上。
    • 内核不会立即为它创建设备。它会等待它的父节点(spi1)准备就绪。
    • 它的 compatible = "jedec,spi-nor" 将用于匹配一个 spi_driver

2. 关系图解

这张图展示了从硬件到驱动的完整流程:

+--------------------------------------------------------------------------------------------------+
|                                         硬件层 (Hardware)                                        |
|  +---------------------------------+                      +------------------------------------+ |
|  |   SoC (e.g., i.MX6ULL)          |                      |      外部电路板 (Board)            | |
|  |                                 |                      |                                    | |
|  |  +---------------------------+  | --(SPI Bus Lines)--> |  +-------------------------------+ | |
|  |  |   SPI Controller #1       |  | (MISO, MOSI, CLK)    |  |   SPI Flash Chip (e.g., W25Q64) | | |
|  |  +---------------------------+  |                      |  +-------------------------------+ | |
|  +---------------------------------+                      +------------------------------------+ |
+--------------------------------------------------------------------------------------------------+
                                                  | (由设备树描述)
+--------------------------------------------------------------------------------------------------+
|                                        设备树 (Device Tree)                                      |
|  +---------------------------------------------------------------------------------------------+ |
|  | spi1: ecspi@02008000 {                                                                      | |
|  |     compatible = "fsl,imx6ul-ecspi";  <-----------------+                                   | |
|  |     ...                                                 |                                   | |
|  |     flash: m25p80@0 {                                   |                                   | |
|  |         compatible = "jedec,spi-nor"; <------------+    |                                   | |
|  |         ...                                       |    |                                   | |
|  |     };                                            |    |                                   | |
|  | };                                                  |    |                                   | |
|  +---------------------------------------------------------------------------------------------+ |
+--------------------------------------------------------------------------------------------------+
                                                  | (内核解析并创建对象)
+--------------------------------------------------------------------------------------------------+
|                                         内核对象与驱动层                                         |
|                                                                                                  |
|  [platform_bus]                                             [spi_bus]                            |
|       |                                                          |                               |
| +--------------------+      (匹配 compatible)      +--------------------------+                    |
| | platform_device  | <------------------------- | platform_driver          |                    |
| | (for spi1)       |                            | (e.g., spi-imx.c)        |                    |
| +--------------------+                            +--------------------------+                    |
|       |                                                          |                               |
|       |                                                          | probe() 函数被调用             |
|       |                                                          |                               |
|       +----------------------------------------------------------+                               |
|                                     |                                                            |
|                                     | 1. 初始化控制器硬件                                        |
|                                     | 2. 调用 spi_register_master() 注册一个 spi_master          |
|                                     |                                                            |
|                                     V                                                            |
|                             +--------------------+                                               |
|                             |    spi_master      | (代表一个可用的SPI总线)                        |
|                             +--------------------+                                               |
|                                     |                                                            |
|                                     | SPI核心层发现 spi_master 及其DTS子节点(flash)              |
|                                     | 为子节点创建 spi_device                                    |
|                                     V                                                            |
| +--------------------+      (匹配 compatible)      +--------------------------+                    |
| |    spi_device    | <------------------------- | spi_driver               |                    |
| | (for flash)      |                            | (e.g., spi-nor.c)        |                    |
| +--------------------+                            +--------------------------+                    |
|       |                                                          |                               |
|       +----------------------------------------------------------+                               |
|                                     |                                                            |
|                                     | probe() 函数被调用, 驱动获得 spi_device 句柄,              |
|                                     | 可以通过 spi_sync(), spi_write() 等函数与硬件通信          |
|                                     V                                                            |
|                                 与物理 Flash 芯片通信                                            |
+--------------------------------------------------------------------------------------------------+

3. 代码与工作流程详解

  1. 内核启动:内核解析设备树,发现 spi1 节点。因为它是一个顶层 SoC 设备,内核为其创建一个 struct platform_device 并将其挂在 platform_bus 上。

  2. Platform 驱动匹配platform_bus 遍历所有已注册的 platform_driverspi-imx.c 驱动的 of_match_table 包含了 "fsl,imx6ul-ecspi"。匹配成功!

  3. Platform Driver Probe 执行:内核调用 spi-imx.c 中的 probe 函数,即 imx_spi_probe()

    // filepath: drivers/spi/spi-imx.c (简化)
    static const struct of_device_id imx_spi_dt_ids[] = {
        { .compatible = "fsl,imx6ul-ecspi", }, // 匹配表
        { /* sentinel */ }
    };
    
    static int imx_spi_probe(struct platform_device *pdev)
    {
        struct spi_master *master;
        // ... 分配内存, 获取时钟, 映射寄存器 ...
    
        // 关键步骤: 注册 "公寓楼"
        // 告诉内核,这里有一个可用的 SPI 总线了
        master = spi_alloc_master(&pdev->dev, ...);
        // ... 配置 master ...
        devm_spi_register_master(&pdev->dev, master); // 注册 master
    
        return 0;
    }
    
    static struct platform_driver imx_spi_driver = {
        .driver = {
            .name = "imx-spi",
            .of_match_table = imx_spi_dt_ids, // 声明匹配表
        },
        .probe = imx_spi_probe,
        // ...
    };
    module_platform_driver(imx_spi_driver);
    
  4. SPI Device 创建:当 spi_register_master() 被调用后,SPI 核心层知道一个新的 SPI 总线(公寓楼)已经就绪。它会回头查看这个总线对应设备树节点 (spi1) 的所有子节点。它发现了 flash 节点,于是为它创建一个 struct spi_device 并将其挂在 spi_bus 上。

  5. SPI 驱动匹配spi_bus 遍历所有已注册的 spi_driverspi-nor.c 驱动的 of_match_table 包含了 "jedec,spi-nor"。匹配成功!

  6. SPI Driver Probe 执行:内核调用 spi-nor.c 中的 probe 函数。

    // filepath: drivers/mtd/spi-nor/spi-nor.c (简化)
    static const struct of_device_id spi_nor_of_ids[] = {
        { .compatible = "jedec,spi-nor" }, // 匹配表
        // ... 其他兼容的 flash 类型
        { /* sentinel */ }
    };
    
    static int spi_nor_probe(struct spi_device *spi)
    {
        struct spi_nor *nor;
        // ...
        // 参数 `spi` 就是内核为 flash 创建的 spi_device
        // 驱动现在可以通过这个 `spi` 句柄与硬件通信了
        ret = spi_nor_scan(nor, spi->modalias, SPI_NOR_DUAL);
        // ...
        return mtd_device_register(&nor->mtd, ...);
    }
    
    static struct spi_driver spi_nor_driver = {
        .driver = {
            .name = "spi-nor",
            .of_match_table = spi_nor_of_ids, // 声明匹配表
        },
        .probe = spi_nor_probe,
        // ...
    };
    module_spi_driver(spi_nor_driver);
    

总结

  • platform_driverspi_driver并行的、服务于不同总线类型的驱动。
  • 它们通过设备树的父子关系内核的分步初始化流程联系在一起。
  • platform_driver 作为基础设施提供者(房东),负责准备好 SPI 总线。
  • spi_driver 作为设备使用者(租客),在总线就绪后,负责与具体的 SPI 从设备通信。
### LinuxSPI 驱动的实现原理 在 Linux 系统中,SPI 驱动的核心功能在于管理与控制 SPI 总线上的设备通信。其工作流程主要包括初始化、配置以及数据传输三个主要部分。 #### 初始化阶段 当系统启动时,SPI 主控制器会注册到 `spi_master` 结构体中,并调用相应的函数完成硬件资源分配和初始化操作。此过程中,驱动程序需要向内核注册自己支持的 SPI 设备及其对应的驱动模块[^1]。 #### 配置阶段 为了确保能够正常与其他外设进行交互,在实际应用之前还需要进一步设定具体的通讯参数,比如波特率(即 SCK 的频率)、CPOL 和 CPHA 属性等。这些属性决定了何时采样输入比特流以及如何同步输出比特流的时间点[^4]。 以下是关于如何设置上述提到的一些重要选项的一个简单例子: ```c static struct spi_board_info boardinfo[] __initdata = { { .modalias = "example_spi_device", .max_speed_hz = 500*1000, /* 设置最大速度为500kHz */ .bus_num = 0, .chip_select = 0, .mode = SPI_MODE_0 | SPI_CS_HIGH, }, }; ``` 在此代码片段中可以看到我们定义了一个名为boardinfo数组用来描述连接至第一个SPI总线上编号为零号CS引脚的目标器件;其中指定该目标运行于模式0之下并且保持高电平有效作为默认状态[^2]。 #### 数据传输阶段 一旦完成了前期准备工作之后就可以开始真正的信息交换环节了。这通常涉及到构建消息队列并通过submit命令提交给底层处理单元执行具体动作直到全部完成后才会返回结果给上层应用程序接口(API)层面使用[^3]。 下面展示了一段用于发起一次典型事务请求的小型示范源码: ```c struct spi_message msg; struct spi_transfer xfer; memset(&msg, 0, sizeof(msg)); memset(&xfer, 0, sizeof(xfer)); /* Prepare transfer details here... */ spi_message_init(&msg); spi_setup(spi); list_add_tail(&xfer.transfer_list,&msg.transfers); status=spi_sync(spi,&msg); if(status!=0){ printk(KERN_ERR"Error during SPI transaction\n"); } else{ // Handle successful completion... } ``` 以上就是有关Linux环境下开发定制化专用集成电路(ASICs)或者微控制器(MCU)所必需掌握的基础知识点概述。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值