spi总线驱动详解

     Spi总线在实际应用得比较多,所以这篇为文章讲解以实际应用为主,bus总线类型细节不做讲解,感兴趣的读者可以分析源码,研究内核源码我觉得是一件非常有趣的事情,同时也是一门艺术。Spi总线上可以挂flash、wifi网卡等常见设备。本文讲解以挂在flash作为实例。

    Spi总线硬件电路如下图所示。一般有四根线– MOSI, MISO, SCK, SS;spi下可以挂在多个设备,SS作为CS片选信号选择那个设备进行通讯。

Spi驱动分为三个层次:Core核心层、SPI控制器驱动、SPI设备驱动。

Core核心层:这个是spi驱动的框架,是通用的spi驱动程序,是剥离出来的代码,与硬件设备没有关系,同时也是SPI控制器与SPI设备之间通讯的桥梁。这一块东西比较复杂,作为开发者来说需要明确怎么使用,spi core代码是不需要修改的,是由linux内核开发人员自行维护。

SPI控制器:实现SPI控制器的操作,必须使能这个spi控制器,如何处理中断、如何发送数据、配置寄存等操作;驱动代码中主要是构造spi_master这个结构,然后注册到spi驱动就行。

SPI设备:这个驱动程序是根据具体的设备而定的,比如flash,就应该按照Mtd块设备方式去放编写驱动程序。

Spi驱动设备树编写如下,这里读者可能看不太明白,我会做一些简单介绍,后期专门拿一篇文章讲解设备树文件。看到图中标红部分的compatible变量名,这个是用来做匹配的;在platform中已经提到过。其它参数是一些在probe函数中获取得到;还可以看到flash是挂在spi控制器的cs 1上的。

首先我们分析spi 控制器驱动,该驱动采用的是platform总线模型,dev部分是通过设备树表现出来的,在上图中可以看到spi节点和一些参数信息。对于platform来说最关心compatible = "xlnx,zynq-qspi-1.0";

Platform drv驱动注册如下:

在图中能够清楚看到与设备树文件匹配的compatible = "xlnx,zynq-qspi-1.0",这个只是众多匹配规则的一种。匹配成功后会调用probe函数,下面分析probe函数。

     图中定义了我们需要构造的spi_master结构,zynq_qspi数据结构是中途需要使用的,属于自定义变量;我们分析680~685这个两个函数到底做了什么。

Spi_alloc实际是分配了一个spi_master+ zynq_qspi大小的空间,初始化mater中最大片选为1,不是从设备;这里的slave在函数调用过程中传入的是false,为了节省篇幅,作者只列举出重点函数接口。读者请这个spi_controller_set_devdata函数,该函数其实是吧&ctlr[1](该指针指向了多分配的zynq_qspi空间首地址),这里将master中的dev->driver_data=zynq_qspi大小空间。这里再platform总线中讲解数据结构时已经说过这个指针,属于内核使用的指针。spi_master_get_devdata函数其实是把master中的dev->driver_data取出赋值给zynq_qspi指针,分析源码一目了然。

687:又用到了那个内核使用的指针,用来存放master指针,很多读者在想为什么内核需要这样设计,是不是多此一举;这里我肯定回答读者不是,这样内核的好处在于代码内部的耦合性,代码更加紧凑,数据传递仅仅是指针的传递;内核经常使用这个数据结构思想,同时读者需要查看container_of函数。

689:获取platform dev的内存资源信息,级设备树中描述的reg

690:请求io作为内存访问区域,并且映射该地址空间

剩下的是获取参数信息,时钟信息,并且使能等相关操作。这些操作只需要调用内核接口函数。

731:从设备树获取中断管脚信息(怕引起读者误会,设备树在写代码阶段编写的,在内核启动后会将设备树文件解析成platfrom dev文件,有的可能解析成其他类型文件)。

737:注册中断

745:得到片选管脚

752~761:设置spi 控制器硬件相关函数

764:注册master。

2162:创建spi发送数据队列

2176:把该spi控制器添加到spi_controller_list全局链表中

2177~2178:匹配该spi总线下挂了那些设备,如果匹配成功就创建设备;board_list是设备树解析时候赋值的,就这样spi控制器与spi设备驱动之间建立了链接,在spi 设备驱动只需要使用spi_driver结构去编写驱动程序,那么匹配成功后的probe函数会传入spi_device指针,该指针已经和spi控制器做了关联,当spi 设备驱动需要发送数据时,可以通过该spi_device指针去发送数据,也不需要我们手动去选择片选信号,因为在注册spi控制器驱动时已经为每一个设备指定了片选信号。