SPI驱动(三) -- SPI设备树处理过程

ops/2025/3/6 15:31:52/

文章目录

  • 参考资料:
  • 一、SPI设备树节点构成
  • 二、SPI设备树示例
    • 2.1 SPI控制器节点属性
    • 2.2 SPI设备节点属性
  • 三、SPI设备树处理过程
  • 四、总结


参考资料:

  • 内核头文件:include\linux\spi\spi.h
  • 内核文档:Documentation\devicetree\bindings\spi\spi-bus.txt
  • 内核源码:drivers\spi\spi.c
  • 内核源码:drivers\spi\spi-gpio.c

一、SPI设备树节点构成

在这里插入图片描述
根据SPI的硬件连接框图,我们知道,当SPI控制器作为主设备时,SPI控制器下面可以挂接多个从设备。对应,设备树中就用一个SPI控制器节点作为父节点,在其下面包含子节点来表示从设备。设备树构成如下:

//根节点
/{//spi控制器节点xxx_spi{...//子节点1flash{...};//子节点2oled {...};};
}

二、SPI设备树示例

参考Documentation\devicetree\bindings\spi\spi-bus.txt示例:

spi@f00 {#address-cells = <1>;#size-cells = <0>;compatible = "fsl,mpc5200b-spi","fsl,mpc5200-spi";reg = <0xf00 0x20>;interrupts = <2 13 0 2 14 0>;cs-gpios = <&gpio1 20 0>,  <&gpio1 21 0>;//子节点ethernet-switch@0 {compatible = "micrel,ks8995m";spi-max-frequency = <1000000>;reg = <0>;};//子节点codec@1 {compatible = "ti,tlv320aic26";spi-max-frequency = <100000>;reg = <1>;};};

2.1 SPI控制器节点属性

必须的属性:

  • #address-cells:这个SPI Master下的SPI设备,需要多少个cell来表述它的片选引脚,1就表示用一个32位数据来表示。
  • #size-cells:必须设置为0。
  • compatible :和SPI Master驱动进行比较配备。

可选的属性:

  • reg : 表示SPI Master 的寄存器地址和大小。
  • interrupts : 描述中断。
  • cs-gpios:SPI Master可以使用多个GPIO引脚当做片选,可以在这个属性列出那些GPIO引脚。
  • num-cs:片选引脚总数。

注:还有一些其他和驱动程序相关的属性,不同的SPI Master驱动程序要求的属性可能不一样。

2.2 SPI设备节点属性

必须的属性:

  • compatible:和SPI Device驱动进行比较匹配。
  • reg:用来表示它使用哪个片选引脚。
  • spi-max-frequency:该SPI设备支持的最大SPI时钟频率。

可选的属性:

  • spi-cpol:这是一个空属性(没有值),设置时钟起始电平,如果设置了表示值为1(高电平),没设置则默认0(低电平)。
  • spi-cpha:这是一个空属性(没有值),设置第几个时钟沿采集数据。
  • spi-cs-high:这是一个空属性(没有值),表示片选引脚高电平有效。
  • spi-3wire:这是一个空属性(没有值),表示使用SPI 三线模式。
  • spi-lsb-first:这是一个空属性(没有值),表示使用SPI传输数据时先传输最低位(LSB)。
  • spi-tx-bus-width:表示有几条MOSI引脚;没有这个属性时默认只有1条MOSI引脚。
  • spi-rx-bus-width:表示有几条MISO引脚;没有这个属性时默认只有1条MISO引脚。
  • spi-rx-delay-us:单位是毫秒,表示每次读传输后要延时多久。
  • spi-tx-delay-us:单位是毫秒,表示每次写传输后要延时多久。

三、SPI设备树处理过程

对于SPI控制器节点,会对应有一个SPI控制器驱动程序,SPI控制器驱动程序会解析父节点,构造出一个spi_master结构体并注册它,另外还会解析子节点,每个子节点会生成一个spi_device结构体。因此,整个SPI设备树节点,都是由SPI控制器驱动程序来解析的 。下面以GPIO模拟的SPI控制器:spi-gpio为例,看代码处理过程:
spi-gpio 设备树:

	spi-gpio {compatible = "spi-gpio";#address-cells = <0x1>;gpio-sck = <&gpio 95 0>;gpio-miso = <&gpio 98 0>;gpio-mosi = <&gpio 97 0>;cs-gpios = <&gpio 125 0>;num-chipselects = <1>;/* clients */ };codec@1 {compatible = "ti,tlv320aic26";spi-max-frequency = <100000>;reg = <1>;};

驱动程序 spi-gpio.c:

  • 从入口函数进入,这里的入口函数在这个宏module_platform_driver里面。入口函数里面注册了一个platform_driver结构体。
//这是一个宏
module_platform_driver(spi_gpio_driver); 
//宏展开后
static int __init spi_gpio_driver_init(void)
{return platform_driver_register(&spi_gpio_driver);
}
module_init(spi_gpio_driver_init);
static void __exit xxx_init(void)
{return platform_driver_unregister(&spi_gpio_driver);
}
module_exit(spi_gpio_driver_exit);
  • platform_driver和设备树匹配成功之后调用probe函数。
//platform_driver结构
static struct platform_driver spi_gpio_driver = {.driver = {.name	= DRIVER_NAME,.of_match_table = of_match_ptr(spi_gpio_dt_ids),},.probe		= spi_gpio_probe, //和设备树匹配成功后调用.remove		= spi_gpio_remove,
};
//of_match_table
static const struct of_device_id spi_gpio_dt_ids[] = {{ .compatible = "spi-gpio" }, //和设备树进行比较{}
};
  • probe函数会分配、设置、注册一个spi_master
static int spi_gpio_probe(struct platform_device *pdev)
{...//分配spi_mastermaster = spi_alloc_master(&pdev->dev, sizeof(*spi_gpio) +(sizeof(unsigned long) * num_devices));...//设置spi_mastermaster->bits_per_word_mask = SPI_BPW_RANGE_MASK(1, 32);master->flags = master_flags;master->bus_num = pdev->id;master->num_chipselect = num_devices;master->setup = spi_gpio_setup;master->cleanup = spi_gpio_cleanup;...//调用这个函数,里面会注册spi_masterstatus = spi_bitbang_start(&spi_gpio->bitbang);...
}
  • spi_bitbang_start函数里面注册spi_master
int spi_bitbang_start(struct spi_bitbang *bitbang)
{...//注册spi_masterret = spi_register_master(spi_master_get(master));...
}
  • spi_register_master里面调用of_spi_register_master解析设备树,并调用of_register_spi_devices注册spi_device
int spi_register_master(struct spi_master *master)
{...//解析设备树status = of_spi_register_master(master);...//注册spi_deviceof_register_spi_devices(master);
}
  • of_register_spi_devices是复数,它里面会遍历调用of_register_spi_device注册spi_device
tatic void of_register_spi_devices(struct spi_master *master)
{
...//遍历注册每一个子节点for_each_available_child_of_node(master->dev.of_node, nc) {
...//注册单个spi_devicespi = of_register_spi_device(master, nc);
...}}
}
  • of_register_spi_device里面解析子节点并调用spi_add_device添加spi_device
static struct spi_device *
of_register_spi_device(struct spi_master *master, struct device_node *nc)
{struct spi_device *spi;int rc;u32 value;/* Alloc an spi_device */spi = spi_alloc_device(master); //分配spi_deviceif (!spi) {dev_err(&master->dev, "spi_device alloc error for %s\n",nc->full_name);rc = -ENOMEM;goto err_out;}/* Select device driver */rc = of_modalias_node(nc, spi->modalias,sizeof(spi->modalias));if (rc < 0) {dev_err(&master->dev, "cannot find modalias for %s\n",nc->full_name);goto err_out;}/* Device address */rc = of_property_read_u32(nc, "reg", &value); //取出reg属性if (rc) {dev_err(&master->dev, "%s has no valid 'reg' property (%d)\n",nc->full_name, rc);goto err_out;}spi->chip_select = value;  //chip_select值来之reg属性/* Mode (clock phase/polarity/etc.) */ //模式设置if (of_find_property(nc, "spi-cpha", NULL))spi->mode |= SPI_CPHA;if (of_find_property(nc, "spi-cpol", NULL))spi->mode |= SPI_CPOL;if (of_find_property(nc, "spi-cs-high", NULL))spi->mode |= SPI_CS_HIGH;if (of_find_property(nc, "spi-3wire", NULL))spi->mode |= SPI_3WIRE;if (of_find_property(nc, "spi-lsb-first", NULL))spi->mode |= SPI_LSB_FIRST;/* Device DUAL/QUAD mode */if (!of_property_read_u32(nc, "spi-tx-bus-width", &value)) {switch (value) {case 1:break;case 2:spi->mode |= SPI_TX_DUAL;break;case 4:spi->mode |= SPI_TX_QUAD;break;default:dev_warn(&master->dev,"spi-tx-bus-width %d not supported\n",value);break;}}if (!of_property_read_u32(nc, "spi-rx-bus-width", &value)) {switch (value) {case 1:break;case 2:spi->mode |= SPI_RX_DUAL;break;case 4:spi->mode |= SPI_RX_QUAD;break;default:dev_warn(&master->dev,"spi-rx-bus-width %d not supported\n",value);break;}}/* Device speed */ //最大频率rc = of_property_read_u32(nc, "spi-max-frequency", &value);if (rc) {dev_err(&master->dev, "%s has no valid 'spi-max-frequency' property (%d)\n",nc->full_name, rc);goto err_out;}spi->max_speed_hz = value;/* Store a pointer to the node in the device structure */of_node_get(nc);spi->dev.of_node = nc;/* Register the new device */ //注册spi_devicerc = spi_add_device(spi);if (rc) {dev_err(&master->dev, "spi_device register error %s\n",nc->full_name);goto err_of_node_put;}return spi;err_of_node_put:of_node_put(nc);
err_out:spi_dev_put(spi);return ERR_PTR(rc);
}

四、总结

本文介绍分析了SPI设备树及其处理过程。


http://www.ppmy.cn/ops/163628.html

相关文章

【AI Guide】AI面试攻略只用看这一篇就够了!力争做全网最全的AI面试攻略——大模型(四十) 模型并行(Model Parallelism)

【AI Guide】AI面试攻略只用看这一篇就够了!力争做全网最全的AI面试攻略——大模型(四十) 模型并行(Model Parallelism) 模型并行(Model Parallelism)模型并行的工作原理按层划分模型并行按块划分模型并行数据并行和模型并行结合模型并行的优点模型并行的挑战模型并行的…

python全栈-Linux基础

python全栈-Linux基础 文章目录 Linux安装/配置网络配置配置Linux远程登录配置虚拟机内部ip配置真机的ip安装XShell和Xftp目录结构用户和用户组用户管理添加用户useradd查看用户id修改用户usermod (选项)(参数)用户密码设置passed (选项)(参数)删除用户userdel [选项] 用户名 用…

Excel-to-JSON v2.0.0发布,可以在Excel内部,把Excel表格转换成JSON,嵌套的JSON也能转

本文是Excel-to-JSON插件的官方文档 https://excel-to-json.wtsolutions.cn 简化浓缩翻译的中文版&#xff0c;仅供参考。详细的还请查看官方文档。 在数据处理和交换的过程中&#xff0c;将Excel文件转换为JSON格式是一项常见需求。Excel-to-JSON作为一款Microsoft Excel插件…

端到端自动驾驶——cnn网络搭建

论文参考&#xff1a;https://arxiv.org/abs/1604.07316 demo 今天主要来看一个如何通过图像直接到控制的自动驾驶端到端的项目&#xff0c;首先需要配置好我的仿真环境&#xff0c;下载软件udacity&#xff1a; https://d17h27t6h515a5.cloudfront.net/topher/2016/November…

C高级linux

#!/bin/bash# 提示用户输入成绩 echo "请输入一个成绩&#xff08;0 - 100&#xff09;&#xff1a;" read score# 检查输入是否为有效的数字 if ! [[ $score ~ ^[0-9]$ ]]; thenecho "输入无效&#xff0c;请输入一个有效的数字。"exit 1 fi# 检查成绩是否…

浙江大学《数据结构》第一章 笔记

第一讲 基本概念 1.1什么是数据结构 1.1.1 关于数据组织--例&#xff1a;图书摆放 例&#xff1a;如何在书架上摆放图书&#xff1f; 乱放按拼音顺序放先分类再按拼音顺序不方便查二分查找先定类别&#xff0c;再二分查找&#xff08;方便插入取出&#xff09; 空间要如何…

第三节:基于Winform框架的串口助手小项目---串口操作《C#编程》

知识是无尽的宝藏&#xff0c;学习的过程虽有挑战&#xff0c;但每一次突破都是对自我的升华&#xff0c;向着更优秀的自己全力进发。 -----------WHAPPY 本节将重点介绍&#xff0c;如何修改控件的属性、SerialPort类的使用及实现串口初始化的操作 1.修改控件属性 修改属性…

大语言模型学习--本地部署DeepSeek

本地部署一个DeepSeek大语言模型 研究学习一下。 本地快速部署大模型的一个工具 先根据操作系统版本下载Ollama客户端 1.Ollama安装 ollama是一个开源的大型语言模型&#xff08;LLM&#xff09;本地化部署与管理工具&#xff0c;旨在简化在本地计算机上运行和管理大语言模型…