linux驱动系列学习之spi框架源码分析

这篇具有很好参考价值的文章主要介绍了linux驱动系列学习之spi框架源码分析。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

一、说明

        spi通信协议的原理、硬件之类的,请参考其他博主的文章,网上很多大佬都写得比较详细,通俗易懂。Linux下的spi框架的使用部分,可以参考其他的博主文章,也可以参考笔者之前写的文章。linux驱动系列学习之spi子系统(五)

        本文介绍的是Linux下的spi框架,更多的集中在对框架的分析、运行逻辑的介绍。

        本文使用的Linux内核源码时Linux5.4.31版本。

二、spi框架

1. 整体结构

介绍spi框架之前,先来看一张整体图。图1是spi框架的整体结构图。

linux spi框架,linux驱动学习,linux,驱动开发,嵌入式硬件,arm开发,arm

    图1

我们使用的spi_register_driver在图1的右下角。使用这个函数去注册spi驱动,.of_match_table 里面写着设备树上的compatible属性,用于match函数里面进行匹配过程。spi_register_driver里面,添加几个函数,spi_bus_type里面的spi_match_device用于和设备树上的节点进行匹配,匹配完成之后会调用spi_drv_probe函数,spi_drv_probe函数调用sdrv->probe即驱动里面注册的probe函数。具体的可以参考博主写的platform总线:Linux驱动系列学习之platform。这里不做具体分析。

2.结构重要的结构体

        spi框架里面有几个比较重要的结构体struct spi_controller、struct spi_driver、struct spi_device、struct spi_board_info、struct spi_transfer、spi_message等,spi框架就是围绕这几个结构体进行开发、管理,下面分别介绍。

2.1 struct spi_controller

        Soc里面有不少spi外设,如spi0、spi1、spi2,对每一个spi外设都需要进行抽象和管理。 struct spi_controller用于描述一个spi外设。结构体主要部分如下:

struct spi_controller:描述spi控制器,对应与spi总线的主机
    dev:spi_controller 是一个 device,所以包含了一个 device 的实例,设备模型使用
    list:链接到全局的 spi_controller list
	bus_num:spi bus 的编号,比如某 SoC有3个 SPI 控制,那么这个结构描述的是第几个
    num_chipselect:片选数量,决定该控制器下面挂接多少个SPI设备,从设备的片选号不能大于这个数量
    mode_bits:SPI 控制器支持的 slave 的模式
    min_speed_hz/max_speed_hz:最大最小速率
    slave:是否是 slave
    (*setup):主要设置SPI控制器和工作方式、clock等
    (*transfer):添加消息到队列的方法。这个函数不可睡眠。它的职责是安排发生的传送并且调用注册的回    调函 complete()。这个不同的控制器要具体实现,传输数据最后都要调用这个函数
    (*cleanup):在spidev_release函数中被调用,spidev_release被登记为spi dev的release函数
    struct kthread_worker		kworker;    //这些和内核的数据使用有关,用到再说
	struct task_struct		*kworker_task;
	struct kthread_work		pump_messages;  //内核线程
	spinlock_t			queue_lock;    //队列使用用到锁
	struct list_head		queue;   
	struct spi_message		*cur_msg;
    cs_gpiods;  片选
    transfer        //三者必须有一个,用于使用spi控制器发送数据
    transfer_one
    transfer_one_message
	struct dma_chan		*dma_tx;  //dma部分
	struct dma_chan		*dma_rx;

}

简单的说,就是将芯片的Soc各种资源,用数据描述处理,使用了哪些东西。包括队列(发送消息用到)、锁(互斥访问)、spi的模式、速率等等。值得注意的是,在实现spi发送数据的接口时,如今的内核提供三种方式:transfer 、transfer_one 、transfer_one_message,必须要有一个被实现,以便使用spi_message发送信息使用(后面会介绍到)。

2.2 struct spi_driver

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

struct spi_driver就是我们平常使用spi驱动框架注册时用到的,很简单,probe函数是驱动的入口函数,放一个博主之前写的驱动的struct spi_driver样例。和平常驱动类似,不多介绍。

linux spi框架,linux驱动学习,linux,驱动开发,嵌入式硬件,arm开发,arm

图2 

2.3 struct spi_device

struct spi_device:spi从设备
{
    dev:device 结构,设备模型使用
    controller:这个 spi device 挂在那个 SPI Controller 下
    max_speed_hz:通讯时钟最大频率
    chip_select:片选号,每个 master 支持多个 spi_device
    mode:SPI device 的模式,时钟极性和时钟相位
    bits_per_word:每个通信字的字长的比特数,默认是 8
    irq:使用到的中断号
    modalias:设备驱动的名字
}

struct spi_device是对spi期间进行描述,需要用到哪些,包括spi器件通信时使用到的通信协议配置--时钟、位数、中断、极性、相位等等。

2.4 struct spi_board_info

struct spi_board_info用于描述板机spi设备信息,和spi_device里面的信息类似,该结构体常用与不支持设备树的内核代码,支持设备树的内核代码里面,在加载内核时,会自动读取内核,生成spi_device信息,从而与spi_driver完成匹配。

2.5 struct spi_transfer

        spi_transfer“读写缓冲对,包含读、写缓冲区。将spi_transfer中的链表transfer_list链接到spi_message中的transfers,再以spi_message形势向底层发送数据。每个spi_transfer都可以对传输的一些参数进行设置,使得master controller按照它要求的参数进行数据发送。

struct spi_transfer{
    tx_buf:发送缓冲区,要写入设备的数据(必须是dma_safe),或者为NULL
    rx_buf:接收缓冲区,要读取的数据缓冲(必须是dma_safe),或者为NULL
    len:缓冲区长度,tx和rx的大小(字节数)。这里不是指它的和,而是各自的长度,它们总是相等的
    tx_dma:如果spi_message.is_dma_mapped是真,这个是tx的dma地址
    rx_dma:如果spi_message.is_dma_mapped是真,这个是rx的dma地址
    cs_change:1 :当前spi_transfer发送完成之后重新片选。影响此次传输之后的片选。指示本次transfer结束之后是否要重新片选并调用setup改变设置。这个标志可以减少系统开销
    bits_per_word:每个字长的比特数,0代表使用spi_device中的默认值 8
    delay_usecs:发送完成一个spi_transfer后延时时间,此次传输结束和片选改变之间的延时,之后就会启动另一个传输或者结束整个消息
    speed_hz:通信时钟。如果是0,使用默认值
    transfer_list:用于链接到spi_message,用来连接的双向链接节点
}

2.6 struct spi_message

struct spi_message,用于发送一次完成的传输,里面包含多个spi_transfer,其用于cs信号从高电平到低电平(选择spi器件)时,spi主机发送一个spi_message,之后cs拉高。

struct spi_message:{
    transfer:这个 mesage 含的 transfer 链表
    spi:传输的目标设备
    is_dma_mapped:spi_transfer 中 tx_dma 和 rx_dma 是否已经 mapped
    complete:数据传输完成的回调函数
    context:提供给complete的可选参数
    actual_length:spi_message已经传输了的字节数
    status:出错与否,错误时返回 errorcode
    queue 、state:供controller驱动内部使用
}

3 spi流程

3.1 芯片底层初始化       

        从图1上看,spi框架也是基于platform总线,在spi框架最底层,是芯片厂家写的驱动,包括对spi外设的配置(时钟、极性、相位等),使用

platform_driver_register(stm32_spi_driver)

进行注册,在对应的probe函数(stm32_spi_probe)中,分配一个master(就是controller,老版本叫做master)。

linux spi框架,linux驱动学习,linux,驱动开发,嵌入式硬件,arm开发,arm

图3

有意思的是,这个函数里面会多分配一些内存,用于给芯片厂家使用,用于芯片厂家自定义的结构体,如

master = spi_alloc_master(&pdev->dev, sizeof(struct stm32_spi));
platform_set_drvdata(pdev, master);
spi = spi_master_get_devdata(master);

struct stm32_spi是st公司定义的结构体,用于管理自己的数据,经过这两部分操作之后,直接给spi分配好了内存,并指向。spi就是struct stm32_spi *spi。剩下的就是初始化用到的IO、中断、dma、自旋锁、完成量、配置spi等等,值得一提的是stm32_spi_transfer_one这个部分,spi控制器发送数据具体实现函数。spi_register_master函数,就是spi_register_controller。

3.2 注册controller

        这一部分主要是初始化用到的自旋锁、队列、互斥锁、完成量等,将3.1初始化的部分,和后面spi driver用到的部分结合起来。spi_register_controller做的事情如图4:

linux spi框架,linux驱动学习,linux,驱动开发,嵌入式硬件,arm开发,arm

 图4

在spi框架里面,所有的spi controller会连接到一个链表spi_controller_list,如图5

linux spi框架,linux驱动学习,linux,驱动开发,嵌入式硬件,arm开发,arm

图5 

board_list是用与spi的device信息链表。

linux spi框架,linux驱动学习,linux,驱动开发,嵌入式硬件,arm开发,arm

 图6

在spi_controller_initialize_queue中,添加spi_transfer_one_message,初始化队列spi_init_queue。

linux spi框架,linux驱动学习,linux,驱动开发,嵌入式硬件,arm开发,arm

 图7

这里是初始化了一个内核线程,配置内核线程SCHED_FIFO,并将spi_pump_messages作为线程入口函数,这个函数就是用于真正发送spi数据包,里面调用了transfer_one_message函数。

linux spi框架,linux驱动学习,linux,驱动开发,嵌入式硬件,arm开发,arm

图8 

transfer_one_message中发送数据,调用的是stm32_spi_transfer_one,具体流程如图9. 

linux spi框架,linux驱动学习,linux,驱动开发,嵌入式硬件,arm开发,arm

图9

3.3 spi驱动

         这里就是我们写的驱动了,在spi驱动中,spi_register_driver注册一个spi_driver,配置一下结构体。即可非常方便的使用spi框架管理。spi驱动发送数据时,需要初始化spi_message和spi_transfer,流程如图10,最后会在内核线程的spi_pump_messages中完成发送任务。

linux spi框架,linux驱动学习,linux,驱动开发,嵌入式硬件,arm开发,arm图10

 3.4 spi框架初始化

        spi框架基本流程已经介绍的差不多了,还有一个就是spi框架初始化,使用字符框架写驱动的同学应该很清楚,就是注册类、设备这些。贴一下代码把,不做介绍了。使用

postcore_initcall(spi_init);

初始化spi框架。

      linux spi框架,linux驱动学习,linux,驱动开发,嵌入式硬件,arm开发,arm

 3.5 spi中断部分

        linux的中断分为上下部,spi中断由芯片厂商已经写好了,在图3部分会进行注册中断。截取stm32芯片的irq注册部分。

linux spi框架,linux驱动学习,linux,驱动开发,嵌入式硬件,arm开发,arm

 spi->cfg->irq_handler_event和spi->cfg->irq_handler_thread就是spi中断的入口了,分成两部分,中断上下文。irq_handler_event是用于中断部分,对芯片的寄存器进行操作,若是返回IRQ_WAKE_THREAD,内核会自动地调用irq_handler_thread部分,完成中断。里面的具体内容就不说了,有兴趣的自己看看源码。

中断是一个比较复杂的部分,有时间另外更新。

三、总结

        相比较Linux其他的子系统,spi框架算是一个比较简单的框架了,用到的核心结构体也没几个,代码量大概在5k行左右。本文只是介绍了spi框架的基本部分,还有一些细节未曾介绍到,对Linux上的数据结构、互斥量、信号量等也未曾详细介绍,这个部分属于操作系统原理的知识了,感兴趣的同学请参考其他的博文。

        spi框架总体山可以看作是platform类的继承,在底层匹配机制上依然延续了platform总线,并对它做了一些扩展。其他的Linux子系统也是基于platform总线,并且有许多相通之处,下一个驱动框架将会晚不少时间,因为太复杂了,博主估计要看很久才能理顺。

        正是在分层分离、面向对象思想的指导下,Linux产生了大量的驱动子系统,由全世界的各位大佬完成了基本的操作,简化了驱动的开发,让驱动开发只需要完成一些简单工作,如配置xxx_driver结构体、使用xxx_register注册等,就可以完成一个稳定性高、效率高的代码,同时不容易出现各种问题如死锁。看完spi框架代码,不得不对前人的工作感慨,向各位大佬致敬并学习!文章来源地址https://www.toymoban.com/news/detail-778391.html

到了这里,关于linux驱动系列学习之spi框架源码分析的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • linux驱动系列学习之温湿度显示(十)

            本篇博文写的是一个最近做的小项目。具体的功能为:检测温湿度传感器(dht11)的数据并显示到oled上面,通过mqtt协议传输到onenet平台。本项目功能简单,只是将之前博文介绍的一些东西整合起来。         具体使用的技术有:linux单总线驱动dht11、i2c框架控制ole

    2024年02月16日
    浏览(42)
  • 【IMX6ULL驱动开发学习】11.Linux之SPI驱动

    参考:驱动程序开发:SPI设备驱动_spi驱动_邓家文007的博客-CSDN博客 目录 一、SPI驱动简介 1.1 SPI架构概述 1.2 SPI适配器(控制器)数据结构 1.2 SPI设备数据结构 1.3 SIP设备驱动 1.4 接口函数  二、SPI驱动模板 SPI驱动框架和I2C驱动框架是十分相似的,不同的是因为SPI是通过片选引

    2024年02月11日
    浏览(49)
  • 【IMX6ULL驱动开发学习】12.Linux SPI驱动实战:DAC驱动设计流程

    基础回顾: 【IMX6ULL驱动开发学习】10.Linux I2C驱动实战:AT24C02驱动设计流程_阿龙还在写代码的博客-CSDN博客 【IMX6ULL驱动开发学习】11.Linux之SPI驱动_阿龙还在写代码的博客-CSDN博客 查看芯片手册,有两种DAC数据格式,12位和16位,这里选用16位数据(2字节)编写驱动。  重点在

    2024年02月11日
    浏览(52)
  • 【Linux】驱动学习,先啃框架

    目录 前言: 一、驱动设计 (1)面向对象: (2)分层: (3)分离: 二、驱动框架 (1)传统框架  (2)总线设备驱动框架: (3)设备树 经典环节: 我一直深信,带着问题思考和实践,能够更容易理解并学习到 。 (1)驱动设计的核心思想 面向对象 分层 分离 (2)驱动

    2024年02月05日
    浏览(28)
  • ESP32S3系列--SPI主机驱动详解(一)

    SPI是一种串行同步接口,可用于与外围设备进行通信。 ESP32S3自带4个SPI外设,其中SPI0/SPI1内部专用,共用一组信号线,通过一个仲裁器访问外部Flash和PSRAM;SPI2/3各自使用一组独立的信号线;开发者可以使用SPI2/3控制外部SPI从设备(Slave device);其中SPI2作为主设备有6个片选,数据

    2023年04月09日
    浏览(44)
  • Flutter框架性泛学习系列之一、Flutter框架概述与源码获取

    Flutter是一种跨平台的移动应用开发框架,它使用Dart语言编写,并且具有高性能和灵活的UI设计能力。Flutter的源码是开源的,可以在GitHub上找到。 任何知识体系,都需要系统的去学习,有一个大概的框架,学习才能如遇得水。知道自己学习的是什么,属于知识体系中的哪一环

    2024年02月21日
    浏览(40)
  • Linux设备驱动之SPI驱动

    Linux下SPI驱动分成两部分:主机驱动和设备驱动。 主机驱动:         主机侧SPI控制器使用 struct spi_master 描述,该结构体中包含了SPI控制器的序号(很多SoC中存在多个SPI控制器),片选数量,SPI信息传输的速率,配置SPI模式的函数指针(4种模式),实现数据传输的函数指针

    2023年04月11日
    浏览(53)
  • 普冉PY32系列(十三) SPI驱动WS2812全彩LED

    普冉PY32系列(一) PY32F0系列32位Cortex M0+ MCU简介 普冉PY32系列(二) Ubuntu GCC Toolchain和VSCode开发环境 普冉PY32系列(三) PY32F002A资源实测 - 这个型号不简单 普冉PY32系列(四) PY32F002A/003/030的时钟设置 普冉PY32系列(五) 使用JLink RTT代替串口输出日志 普冉PY32系列(六) 通过I2C接口驱动PCF8574扩

    2024年02月05日
    浏览(48)
  • Linux SPI 驱动

    Linux 内核将 SPI 驱动分为两部分: SPI 总线驱动 : SOC 的 SPI 控制器驱动,也叫做 SPI 适配器驱动。一旦编写完成就不再需要再做修改,其他的SPI 设备直接调用主机驱动提供的API 函数完成读写操作即可。 SPI 设备驱动 :SPI 设备驱动就是针对具体的SPI 设备而编写的驱动。 Linux 内

    2023年04月18日
    浏览(31)
  • Linux 下spi设备驱动

    参考: Linux kernel 有关 spi 设备树参数解析 - 走看看 Linux SPI驱动框架(1)——核心层_linux spi驱动模型_绍兴小贵宁的博客-CSDN博客 Linux SPI驱动框架(2)——控制器驱动层_全志h3 spi驱动_绍兴小贵宁的博客-CSDN博客 Linux SPI驱动框架(3)——设备驱动层_linux spi字符设备驱动_绍兴小贵宁的

    2024年02月09日
    浏览(35)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包