Uboot驱动模型之spi-uclass(2/3)

概述

spi-uclass提供对SPI总线的访问,并包括SPI所需的操作。spi-uclass只是提供方法,spi的设备驱动需要调用这些方法来驱动设备以及操作总线/设备。

定义uclass driver

如上篇《Uboot驱动模型之框架driver-model(1/3)》分析的那样uclass的定义依赖于uclass_driver。所以使用UCLASS_DIRVER来定义struct uclass_driver spi,即补充struct uclass_driver的各个你需要用到的成员。

spi的struct uclass_driver的定义如下:

471 UCLASS_DRIVER(spi) = {
472     .id     = UCLASS_SPI,
473     .name       = "spi",
474     .flags      = DM_UC_FLAG_SEQ_ALIAS,
475 #if !CONFIG_IS_ENABLED(OF_PLATDATA)
476     .post_bind  = dm_scan_fdt_dev,
477 #endif
478     .post_probe = spi_post_probe,
479     .child_pre_probe = spi_child_pre_probe,
480     .per_device_auto_alloc_size = sizeof(struct dm_spi_bus),
481     .per_child_auto_alloc_size = sizeof(struct spi_slave),
482     .per_child_platdata_auto_alloc_size =
483             sizeof(struct dm_spi_slave_platdata),
484 #if !CONFIG_IS_ENABLED(OF_PLATDATA)
485     .child_post_bind = spi_child_post_bind,
486 #endif
487 };

成员解析:

#if !CONFIG_IS_ENABLED(OF_PLATDATA)

        .post_bind = dm_scan_fdt_dev:

        作用:dm_scan_fdt_dev的作用就是扫描父设备下的子设备,意味着在spi设备绑定spi uclass后会去扫描它的子设备。此时递归下去就会把该设备下的所有子设备,孙设备。

        调用时机:在扫描设备树创建udevice的时候。该阶段比较早,《Uboot驱动模型之框架driver-model(1/3)》中有介绍(Uboot驱动模型之框架driver-model(1/3)_uboot驱动框架-优快云博客)。

#endif

 33 static int device_bind_common(struct udevice *parent, const struct driver *drv,
 34                   const char *name, void *platdata,
 35                   ulong driver_data, ofnode node,
 36                   uint of_platdata_size, struct udevice **devp)
 37 {
 38     struct udevice *dev;
 39     struct uclass *uc;
 40     int size, ret = 0;
 41
 42     if (devp)
 43         *devp = NULL;
 44     if (!name)
 45         return -EINVAL;
 46
 47     ret = uclass_get(drv->id, &uc);
 48     if (ret) {
 49         debug("Missing uclass for driver %s\n", drv->name);
 50         return ret;
 51     }
 52
 53     dev = calloc(1, sizeof(struct udevice));
 54     if (!dev)
 55         return -ENOMEM;
 56
 57     INIT_LIST_HEAD(&dev->sibling_node);
 58     INIT_LIST_HEAD(&dev->child_head);
 59     INIT_LIST_HEAD(&dev->uclass_node);
 60 #ifdef CONFIG_DEVRES
 61     INIT_LIST_HEAD(&dev->devres_head);
 62 #endif
 63     dev->platdata = platdata;
 64     dev->driver_data = driver_data;
 65     dev->name = name;
 66     dev->node = node;
 67     dev->parent = parent;
 68     dev->driver = drv;
 69     dev->uclass = uc;
 70
 71     dev->seq = -1;
 72     dev->req_seq = -1;
 73
 74     ...
 75
 76     if (parent && parent->driver->child_post_bind) {
 77         ret = parent->driver->child_post_bind(dev);
 78         if (ret)
 79             goto fail_child_post_bind;
 80     }
 81     if (uc->uc_drv->post_bind) {
 82         ret = uc->uc_drv->post_bind(dev);
 83         if (ret)
 84             goto fail_uclass_post_bind;
 85     }
 86
 87     if (parent)
 88         pr_debug("Bound device %s to %s\n", dev->name, parent->name);
 89     if (devp)
 90         *devp = dev;
 91
 92     dev->flags |= DM_FLAG_BOUND;
 93
 94     return 0;

        .post_probe = spi_post_probe:

        作用:主要是读取spi总线支持的最大频率以及看是否需要对定义的spi总线的ops接口重定位。

        调用时机:在触发spi driver的probe后调用(e.g. U_BOOT_DERIVER(xx),device_probe(udevice)后先调用xx.probe(),然后再调用此处post_probe),主要是读dts中的spi-max-frequency节点。

143 static int spi_post_probe(struct udevice *bus)
144 {
145 #if !CONFIG_IS_ENABLED(OF_PLATDATA)
146     struct dm_spi_bus *spi = dev_get_uclass_priv(bus);
147
148     spi->max_hz = dev_read_u32_default(bus, "spi-max-frequency", 0);
149 #endif
150 #if defined(CONFIG_NEEDS_MANUAL_RELOC)
151     struct dm_spi_ops *ops = spi_get_ops(bus);
152
153     if (ops->claim_bus)
154         ops->claim_bus += gd->reloc_off;
155     if (ops->release_bus)
156         ops->release_bus += gd->reloc_off;
157     if (ops->set_wordlen)
158         ops->set_wordlen += gd->reloc_off;
159     if (ops->xfer)
160         ops->xfer += gd->reloc_off;
161     if (ops->set_speed)
162         ops->set_speed += gd->reloc_off;
163     if (ops->set_mode)
164         ops->set_mode += gd->reloc_off;
165     if (ops->cs_info)
166         ops->cs_info += gd->reloc_off;
167 #endif
168
172     return 0;
173 }

        .child_pre_probe = spi_child_pre_probe:

        作用:主要是设置这个device(从设备)支持的最大频率,模式(极性,线宽等)

        调用时机:在spi driver的probe调用之前会先调用这个spi device 的uclass的.child_pre_probe执行,然后再调用这个spi device的parent的uclass的.child_pre_probe(即这里的spi_child_pre_probe;spi device的parent是spi控制器,spi控制器的uclass就是这里的UCLASS_DRIVER(spi))执行。

175 static int spi_child_pre_probe(struct udevice *dev)
176 {
177     struct dm_spi_slave_platdata *plat = dev_get_parent_platdata(dev);
178     struct spi_slave *slave = dev_get_parent_priv(dev);
179
180     /*
181      * This is needed because we pass struct spi_slave around the place
182      * instead slave->dev (a struct udevice). So we have to have some
183      * way to access the slave udevice given struct spi_slave. Once we
184      * change the SPI API to use udevice instead of spi_slave, we can
185      * drop this.
186      */
187     slave->dev = dev;
188
189     slave->max_hz = plat->max_hz;
190     slave->mode = plat->mode;
191     slave->wordlen = SPI_DEFAULT_WORDLEN;
192
193     return 0;
194 }

        .per_device_auto_alloc_size = sizeof(struct dm_spi_bus):

        作用:保存spi总线的最大频率,在设置device的频率的时候,不能大于总线的频率,dm_spi_claim_bus的时候会做这个处理。dm_spi_bus数据结构如下:

v2019:
 41 /* TODO(sjg@chromium.org): Remove this and use max_hz from struct spi_slave */
 42 struct dm_spi_bus {
 43     uint max_hz;
 44 };
v2023:
 42 /**
 43  * struct dm_spi_bus - SPI bus info
 44  *
 45  * This contains information about a SPI bus. To obtain this structure, use
 46  * dev_get_uclass_priv(bus) where bus is the SPI bus udevice.
 47  *
 48  * @max_hz: Maximum speed that the bus can tolerate.
 49  * @speed:  Current bus speed. This is 0 until the bus is first claimed.
 50  * @mode:   Current bus mode. This is 0 until the bus is first claimed.
 51  *
 52  * TODO(sjg@chromium.org): Remove this and use max_hz from struct spi_slave.
 53  */
 54 struct dm_spi_bus {
 55     uint max_hz;
 56     uint speed;
 57     uint mode;
 58 };

        调用时机:在spi device触发后(device_prob

<think>好的,我现在需要帮助用户分析、配置和测试U-Boot中的DW SPI驱动。首先,我需要回忆一下用户提供的引用内容,看看有哪些信息可以利用。引用[1]提到了在U-Boot中找到了winbond.c的驱动,说明U-Boot已经支持某些SPI NAND Flash。引用[2]涉及到设备树的修改和bootargs的配置,这可能和SPI的分区设置有关。引用[3]和[4]讨论了U-Boot的SPI驱动模型和核心层结构,尤其是关于spi_slave和设备树的配置。引用[5]提到了模块参数,但可能关联性不大。 用户的问题是针对DW SPI驱动的分析、配置和测试。DW可能指的是DesignWare SPI控制器,这是常见于SoC中的IP核。首先,我需要确定U-Boot中DW SPI驱动的代码位置。通常,驱动会在drivers/spi或drivers/mtd/spi下。根据引用[4],spi.c是核心层,而具体控制器驱动可能在类似drivers/spi/designware_spi.c的位置。 接下来,配置部分。根据引用[2],设备树需要正确配置SPI节点,包括寄存器地址、时钟频率、片选引脚等。同时,可能需要启用相关的配置选项,比如CONFIG_SPI、CONFIG_DM_SPI和CONFIG_DESIGNWARE_SPI。在U-Boot的menuconfig中,用户需要进入SPI驱动配置,启用DesignWare SPI控制器支持。 然后是测试方法。用户可能需要通过SPI命令进行读写测试,比如使用sf probe探测设备,sf read和sf write进行数据传输。如果涉及到SPI Flash,还需要确认分区信息是否正确,如引用[2]中的mtdparts设置。 此外,引用[3]提到per_child_auto_alloc_size的设置,这可能涉及到SPI从设备的结构体分配,确保在设备树中正确声明SPI设备时,U-Boot能正确分配资源。 需要注意的问题包括时钟配置是否正确,片选信号是否有效,以及GPIO的设置是否匹配硬件连接。调试时可以通过增加调试输出,比如在驱动中启用DEBUG选项,或者通过示波器检查SPI信号波形。 总结步骤应该是:1. 确认驱动代码存在并启用配置;2. 修改设备树添加SPI控制器和从设备节点;3. 编译并烧写U-Boot;4. 使用U-Boot命令行工具测试SPI通信。过程中需要参考具体的硬件手册,确保寄存器地址和引脚配置正确。</think>### U-Boot DW SPI驱动分析、配置与测试教程 #### 一、驱动框架分析 DW SPI(DesignWare SPI驱动遵循U-Boot的驱动模型,核心结构包括: 1. **控制器驱动**:位于`drivers/spi/designware_spi.c`,实现`struct dm_spi_ops`接口[^4] 2. **设备树绑定**:需定义寄存器地址、时钟、工作模式等参数 3. **从设备管理**:通过`struct spi_slave`封装设备参数[^3] 典型驱动注册代码: ```c U_BOOT_DRIVER(designware_spi) { .name = "designware_spi", .id = UCLASS_SPI, .of_match = designware_spi_ids, .ops = &designware_spi_ops, .priv_auto_alloc_size = sizeof(struct dw_spi_priv), .per_child_auto_alloc_size = sizeof(struct spi_slave), // [^3] }; ``` #### 二、配置步骤 1. **Kconfig配置**: ```bash make menuconfig # 路径:SPI Support -> # [*] Enable SPI support # [*] DesignWare SPI controller # [*] DM_SPI (Driver Model) ``` 2. **设备树配置**(示例V3s): ```dts &spi0 { compatible = "snps,dw-apb-ssi"; reg = <0x01c68000 0x1000>; clocks = <&ccu CLK_BUS_SPI0>; resets = <&ccu RST_BUS_SPI0>; #address-cells = <1>; #size-cells = <0>; status = "okay"; flash@0 { compatible = "winbond,w25q128"; spi-max-frequency = <50000000>; reg = <0>; }; }; ``` 需确认寄存器地址、时钟与SoC手册一致[^2] 3. **编译验证**: ```bash make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- -j8 ``` #### 三、测试方法 1. **基础功能测试**: ```bash # 进入U-Boot命令行 => sf probe 0:0 # 初始化SPI0 CS0 => sf read 82000000 0 1000 # 读取前4KB数据到内存 => md 82000000 # 查看内存数据 => sf erase 0 1000 # 擦除前4KB => sf write 82000000 0 1000 # 写入测试数据 ``` 2. **性能测试**: ```bash => time sf read 82000000 0 100000 # 测量读取64KB耗时 ``` 3. **调试技巧**: - 启用驱动调试: ```c #define DEBUG // 在驱动文件中添加调试输出 ``` - 示波器验证CLK/MOSI信号时序 #### 四、常见问题排查 | 现象 | 排查点 | 工具 | |------|--------|------| | 无法探测设备 | 1. 设备树寄存器地址<br>2. 时钟使能状态<br>3. 片选GPIO配置 | `dm tree`命令<br>示波器 | | 数据传输错误 | 1. SPI模式(CPOL/CPHA)<br>2. 最大频率设置<br>3. 电源稳定性 | 逻辑分析仪 | | 驱动未加载 | 1. Kconfig配置状态<br>2. 设备树status属性<br>3. 兼容性字符串匹配 | `sf scan`命令 |
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值