您的位置 首页 解答

Linux下spi驱动技能剖析

Linux下spi驱动技术分析-主设备对应SOC芯片中的SPI控制器,通常,一个SOC中可能存在多个SPI控制器,像上面的例子所示,SOC芯片中有3个SPI控制器。每个控制器下可以连接多个SPI从设备,每个从设备有各自独立的CS引脚。每个从设备共享另外3个信号引脚:SCK、MISO、MOSI。任何时刻,只有一个CS引脚处于有效状态,与该有效CS引脚连接的设备此时可以与主设备(SPI控制器)通信,其它的从设备处于等待状态,并且它们的3个引脚必须处于高阻状态。

SPI是“Serial Peripheral Interface” 的缩写,是一种四线制的同步串行通讯接口,用来衔接微操控器、传感器、存储设备,SPI设备分为主设备和从设备两种,用于通讯和操控的四根线分别是:

CS 片选信号

SCK 时钟信号

MISO 主设备的数据输入、从设备的数据输出脚

MOSI 主设备的数据输出、从设备的数据输入脚

因为在大多数情况下,CPU或SOC一侧一般都是作业在主设备形式,所以,现在的Linux内核版别中,只完结了主形式的驱动结构。

一、硬件结构

一般,担任宣布时钟信号的设备咱们称之为主设备,另一方则作为从设备,下图是一个SPI体系的硬件衔接示例:

Linux下spi驱动技能剖析

Linux下spi驱动技能剖析

如上图所示,主设备对应SOC芯片中的SPI操控器,一般,一个SOC中或许存在多个SPI操控器,像上面的比方所示,SOC芯片中有3个SPI操控器。每个操控器下能够衔接多个SPI从设备,每个从设备有各自独立的CS引脚。每个从设备同享别的3个信号引脚:SCK、MISO、MOSI。任何时刻,只要一个CS引脚处于有用状况,与该有用CS引脚衔接的设备此刻能够与主设备(SPI操控器)通讯,其它的从设备处于等候状况,而且它们的3个引脚有必要处于高阻状况。

二、作业时序

依照时钟信号和数据信号之间的相位联系,SPI有4种作业时序形式:

Linux下spi驱动技能剖析

咱们用CPOL表明时钟信号的初始电平的状况,CPOL为0表明时钟信号初始状况为低电平,为1表明时钟信号的初始电平是高电平。别的,咱们用CPHA来表明在那个时钟沿采样数据,CPHA为0表明在首个时钟改动沿采样数据,而CPHA为1则表明要在第二个时钟改动沿来采样数据。内核用CPOL和CPHA的组合来表明当时SPI需求的作业形式:

CPOL=0,CPHA=1 形式0

CPOL=0,CPHA=1 形式1

CPOL=1,CPHA=0 形式2

CPOL=1,CPHA=1 形式3

三、确认驱动文件

SPI作为linux里边比较小的一个子体系,其驱动程序坐落/drivers/spi/*目录,首要,咱们能够经过Makefile及Kconfig来确认咱们需求看的源文件。

[plain] view plain copy#

# Makefile for kernel SPI drivers.

#

# small core, mostly translaTIng board-specific

# config declaraTIons into driver model code

obj-$(CONFIG_SPI_MASTER) += spi.o

obj-$(CONFIG_SPI_SPIDEV) += spidev.o

[plain] view plain copy# SPI master controller drivers (bus)

obj-$(CONFIG_SPI_BITBANG) += spi-bitbang.o

obj-$(CONFIG_SPI_IMX) += spi-imx.o

对应的Kconfig去装备内核

Linux下spi驱动技能剖析

编译生成的方针文件如下

Linux下spi驱动技能剖析

经过以上剖析咱们知道,spi驱动由三部分组成,分别是core(spi.c),master controller driver (spi_imx.c)以及SPIprotocol drivers (spidev.c)。

四、数据结构剖析

Spi驱动触及的数据结构首要坐落/include/linux/spi.h,其间spi.c,spi-imx.c,spidev.c均用到了spi.h里的结构体。

1.spi_master

spi_master代表一个主机操控器,此处表明imx的SPI操控器。一般不需求自己编写spi操控器驱动,可是了解这个结构体仍是必要的。

[objc] view plain copystruct spi_master {

struct device dev; //设备模型运用

struct list_head list;

/* other than negaTIve (== assign one dynamically), bus_num is fully

* board-specific. usually that simplifies to being SOC-specific.

* example: one SOC has three SPI controllers, numbered 0..2,

* and one board‘s schemaTIcs might show it using SPI-2. software

* would normally use bus_num=2 for that controller.

*/

s16 bus_num; //总线(或操控器)编号,imx6q有5个spi操控器,0~4

/* chipselects will be integral to many controllers; some others

* might use board-specific GPIOs.

*/

u16 num_chipselect; //片选数量,决议该操控器下面挂接多少个SPI设备,从设备的片选号不能大于这个数量

/* some SPI controllers pose alignment requirements on DMAable

* buffers; let protocol drivers know about these requirements.

*/

u16 dma_alignment;

/* spi_device.mode flags understood by this controller driver */

u16 mode_bits; //master支撑的设备形式

/* bitmask of supported bits_per_word for transfers */

u32 bits_per_word_mask;

#define SPI_BPW_MASK(bits) BIT((bits) – 1)

#define SPI_BIT_MASK(bits) (((bits) == 32) ? ~0U : (BIT(bits) – 1))

#define SPI_BPW_RANGE_MASK(min, max) (SPI_BIT_MASK(max) – SPI_BIT_MASK(min – 1))

/* limits on transfer speed */

u32 min_speed_hz;

u32 max_speed_hz;

/* other constraints relevant to this driver */

u16 flags;

#define SPI_MASTER_HALF_DUPLEX BIT(0) /* can’t do full duplex */

#define SPI_MASTER_NO_RX BIT(1) /* can‘t do buffer read */

#define SPI_MASTER_NO_TX BIT(2) /* can’t do buffer write */

#define SPI_MASTER_MUST_RX BIT(3) /* requires rx */

#define SPI_MASTER_MUST_TX BIT(4) /* requires tx */

/* lock and mutex for SPI bus locking */

spinlock_t bus_lock_spinlock;

struct mutex bus_lock_mutex;

/* flag indicating that the SPI bus is locked for exclusive use */

bool bus_lock_flag;

/* Setup mode and clock, etc (spi driver may call many times)。

*

* IMPORTANT: this may be called when transfers to another

* device are active. DO NOT UPDATE SHARED REGISTERS in ways

* which could break those transfers.

*/

int (*setup)(struct spi_device *spi); //依据spi设备更新硬件装备。设置形式、时钟等,这个需求咱们自己详细完结,首要设置SPI操控器和作业方式

/* bidirectional bulk transfers

*

* + The transfer() method may not sleep; its main role is

* just to add the message to the queue.

* + For now there‘s no remove-from-queue operation, or

* any other request management

* + To a given spi_device, message queueing is pure fifo

*

* + The master’s main job is to process its message queue,

* selecting a chip then transferring data

* + If there are multiple spi_device children, the i/o queue

* arbitration algorithm is unspecified (round robin, fifo,

* priority, reservations, preemption, etc)

*

* + Chipselect stays active during the entire message

* (unless modified by spi_transfer.cs_change != 0)。

* + The message transfers use clock and SPI mode parameters

* previously established by setup() for this device

*/

int (*transfer)(struct spi_device *spi,

struct spi_message *mesg); //增加音讯到行列的办法。这个函数不行睡觉。它的责任是组织发生的传送而且调用注册的回调函数complete()。这个不同的操控器要详细完结,传输数据最终都要调用这个函数

/* called on release() to free memory provided by spi_master */

void (*cleanup)(struct spi_device *spi); //cleanup函数会在spidev_release函数中被调用,spidev_release被登记为spi dev的release函数。

/*

* Used to enable core support for DMA handling, if can_dma()

* exists and returns true then the transfer will be mapped

* prior to transfer_one() being called. The driver should

* not modify or store xfer and dma_tx and dma_rx must be set

* while the device is prepared.

*/

bool (*can_dma)(struct spi_master *master,

struct spi_device *spi,

struct spi_transfer *xfer);

/*

* These hooks are for drivers that want to use the generic

* master transfer queueing mechanism. If these are used, the

* transfer() function above must NOT be specified by the driver.

* Over time we expect SPI drivers to be phased over to this API.

*/

bool queued;

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;

bool busy;

bool running;

bool rt;

bool auto_runtime_pm;

bool cur_msg_prepared;

bool cur_msg_mapped;

struct completion xfer_completion;

size_t max_dma_len;

int (*prepare_transfer_hardware)(struct spi_master *master);

int (*transfer_one_message)(struct spi_master *master,

struct spi_message *mesg);

int (*unprepare_transfer_hardware)(struct spi_master *master);

int (*prepare_message)(struct spi_master *master,

struct spi_message *message);

int (*unprepare_message)(struct spi_master *master,

struct spi_message *message);

/*

* These hooks are for drivers that use a generic implementation

* of transfer_one_message() provied by the core.

*/

void (*set_cs)(struct spi_device *spi, bool enable);

int (*transfer_one)(struct spi_master *master, struct spi_device *spi,

struct spi_transfer *transfer);

/* gpio chip select */

int *cs_gpios;

/* DMA channels for use with core dmaengine helpers */

struct dma_chan *dma_tx;

struct dma_chan *dma_rx;

/* dummy data for full duplex devices */

void *dummy_rx;

void *dummy_tx;

};

2. spi_device

spi_device代表一个外围spi设备,由master controller driver注册完结后扫描BSP中注册设备发生的设备链表并向spi_bus注册发生。在内核中,每个spi_device代表一个物理的spi设备。

[objc] view plain copystruct spi_device {

struct device dev; //设备模型运用

struct spi_master *master; //设备运用的master结构,挂在哪个主操控器下

u32 max_speed_hz; //通讯时钟最大频率

u8 chip_select; //片选号,每个master支撑多个spi_device

u16 mode; //设备支撑的形式,如片选是高or低?

#define SPI_CPHA 0x01 /* clock phase */

#define SPI_CPOL 0x02 /* clock polarity */

#define SPI_MODE_0 (0|0) /* (original MicroWire) */

#define SPI_MODE_1 (0|SPI_CPHA)

#define SPI_MODE_2 (SPI_CPOL|0)

#define SPI_MODE_3 (SPI_CPOL|SPI_CPHA)

#define SPI_CS_HIGH 0x04 /* chipselect active high? 为1时片选的有用信号是高电平*/

#define SPI_LSB_FIRST 0x08 /* per-word bits-on-wire 发送时低比特在前*/

#define SPI_3WIRE 0x10 /* SI/SO signals shared 输入输出信号运用同一根信号线*/

#define SPI_LOOP 0x20 /* loopback mode 回环形式*/

#define SPI_NO_CS 0x40 /* 1 dev/bus, no chipselect */

#define SPI_READY 0x80 /* slave pulls low to pause */

#define SPI_TX_DUAL 0x100 /* transmit with 2 wires */

#define SPI_TX_QUAD 0x200 /* transmit with 4 wires */

#define SPI_RX_DUAL 0x400 /* receive with 2 wires */

#define SPI_RX_QUAD 0x800 /* receive with 4 wires */

u8 bits_per_word; //每个字长的比特数,默许是8

int irq; //中止号

void *controller_state; //操控寄存器状况

void *controller_data;

char modalias[SPI_NAME_SIZE]; //设备驱动的姓名

int cs_gpio; /* chip select gpio */

/*

* likely need more hooks for more protocol options affecting how

* the controller talks to each chip, like:

* – memory packing (12 bit samples into low bits, others zeroed)

* – priority

* – drop chipselect after each word

* – chipselect delays

* – 。。。

*/

};

因为一个SPI总线上能够有多个SPI设备,因而需求片选号来区别它们,SPI操控器依据片选号来挑选不同的片选线,然后完结每次只同一个设备通讯。

spi_device的mode成员有两个比特位意义很重要。SPI_CPHA挑选对数据线采样的机遇,0挑选每个时钟周期的第一个沿跳变时采样数据,1挑选第二个时钟沿采样数据;SPI_CPOL挑选每个时钟周期开端的极性,0表明时钟以低电平开端,1挑选高电平开端。这两个比特有四种组合,对应SPI_MODE_0~SPI_MODE_3。

另一个比较重要的成员是bits_per_word。这个成员指定每次读写的字长,单位是比特。尽管大部分SPI接口的字长是8或许16,依然会有一些特别的比方。需求阐明的是,假如这个成员为零的话,默许运用8作为字长。

最终一个成员并不是设备的姓名,而是需求绑定的驱动的姓名。

3.spi_driver

spi_driver代表一个SPI protocol drivers,即外设驱动。

[objc] view plain copystruct spi_driver {

const struct spi_device_id *id_table; //支撑的spi_device设备表

int (*probe)(struct spi_device *spi); //和spi匹配成功之后会调用这个办法。因而这个办法需求对设备和私有数据进行初始化。

int (*remove)(struct spi_device *spi); //免除spi_device和spi_driver的绑定,开释probe恳求的资源。

void (*shutdown)(struct spi_device *spi); //封闭

int (*suspend)(struct spi_device *spi, pm_message_t mesg); //挂起

int (*resume)(struct spi_device *spi); //康复

struct device_driver driver; //设备模型运用

};

一般关于从事Linux驱动作业人员来说,spi设备的驱动首要便是完结这个结构体中的各个接口,并将之注册到spi子体系中去。

4.spi_transfer

spi_transfer代表一个读写缓冲对,包括接纳缓冲区及发送缓冲区,其实,spi_transfer的发送是经过构建spi_message完结,经过将spi_transfer中的链表transfer_list链接到spi_message中的transfers,再以spi_message局势向底层发送数据。每个spi_transfer都能够对传输的一些参数进行设置,使得master controller依照它要求的参数进行数据发送。

[cpp] view plain copystruct spi_transfer {

/* it‘s ok if tx_buf == rx_buf (right?)

* for MicroWire, one buffer must be null

* buffers must work with dma_*map_single() calls, unless

* spi_message.is_dma_mapped reports a pre-existing mapping

*/

const void *tx_buf; //发送缓冲区,要写入设备的数据(有必要是dma_safe),或许为NULL。

void *rx_buf; //接纳缓冲区,要读取的数据缓冲(有必要是dma_safe),或许为NULL。

unsigned len; //缓冲区长度,tx和rx的巨细(字节数)。这儿不是指它的和,而是各自的长度,它们总是持平的。

dma_addr_t tx_dma; //假如spi_message.is_dma_mapped是真,这个是tx的dma地址

dma_addr_t rx_dma; //假如spi_message.is_dma_mapped是真,这个是rx的dma地址

struct sg_table tx_sg;

struct sg_table rx_sg;

unsigned cs_change:1; //当时spi_transfer发送完结之后重新片选。影响此次传输之后的片选。指示本次transfer完毕之后是否要重新片选并调用setup改动设置。这个标志能够削减体系开支。

u8 tx_nbits;

u8 rx_nbits;

#define SPI_NBITS_SINGLE 0x01 /* 1bit transfer */

#define SPI_NBITS_DUAL 0x02 /* 2bits transfer */

#define SPI_NBITS_QUAD 0x04 /* 4bits transfer */

u8 bits_per_word; //每个字长的比特数,0代表运用spi_device中的默许值8

u16 delay_usecs; //发送完结一个spi_transfer后延时时刻,此次传输完毕和片选改动之间的延时,之后就会发动另一个传输或许完毕整个音讯

u32 speed_hz; //通讯时钟。假如是0,运用默许值

struct list_head transfer_list; //用于链接到spi_message,用来衔接的双向链接节点

};《/span》

5.spi_message

spi_message代表spi音讯,由多个spi_transfer段组成。

spi_message用来原子的履行spi_transfer表明的一串数组传输恳求。

这个传输行列是原子的,这意味着在这个音讯完结之前不会有其它音讯占用总线。

音讯的履行总是依照FIFO的次序。

向底层提交spi_message的代码要担任办理它的内存空间。未显现初始化的内存需求运用0来初始化。

[cpp] view plain copystruct spi_message {

struct list_head transfers; //spi_transfer链表行列,此次音讯的传输段行列,一个音讯能够包括多个传输段。

struct spi_device *spi; //传输的意图设备

unsigned is_dma_mapped:1; //假如为真,此次调用供给dma和cpu虚拟地址。

/* REVISIT: we might want a flag affecting the behavior of the

* last transfer 。。。 allowing things like “read 16 bit length L”

* immediately followed by “read L bytes”。 Basically imposing

* a specific message scheduling algorithm.

*

* Some controller drivers (message-at-a-time queue processing)

* could provide that as their default scheduling algorithm. But

* others (with multi-message pipelines) could need a flag to

* tell them about such special cases.

*/

/* completion is reported through a callback */

void (*complete)(void *context); //异步调用完结后的回调函数

void *context; //回调函数的参数

unsigned frame_length;

unsigned actual_length; //《span style=“font-family: Arial, Helvetica, sans-serif;”》实践传输的数据长度《/span》

int status; //该音讯的发送成果,成功被置0,否则是一个负的过错码。

/* for optional use by whatever driver currently owns the

* spi_message 。。。 between calls to spi_async and then later

* complete(), that’s the spi_master controller driver.

*/

struct list_head queue; //下面两个成员是给具有本音讯的驱动选用的。spi_master会运用它们。自己最好不要运用。

void *state;

};

操控器驱动会先写入tx的数据,然后读取相同长度的数据。长度指示是len。

假如tx_buff是空指针,填充rx_buff的时分会输出0(为了发生接纳的时钟),假如rx_buff是NULL,接纳到的数据将被丢掉。

只要len长读的数据会被输出和接纳。

输出不完整的字长是过错的(比方字长为2字节的时分输出三个字节,最终一个字节凑不成一个整字)。

本地内存中的数据总是运用本地cpu的字节序,不管spi的字节序是大段形式仍是小段形式(运用SPI_LSB_FIRS)

当spi_transfer的字长不是8bit的2次幂的整数倍,这些数据字就包括扩展位。在spi通讯驱动看来内存中的数据总是刚好对齐的,所以rx中位界说和rx中未运用的比特位总是最高有用位。(比方13bit的字长,每个字占2字节,rx和tx都应该如此寄存)

一切的spi传输都以使能相关的片选线为开端。一般来说片选线在本音讯完毕之前坚持有用的状况。驱动能够运用

spi_transfer中的cs_change成员来影响片选:

(i)假如transfer不是message的最终一个,这个标志量能够便利的将片选线置位无效的状况。

有时需求这种办法来奉告芯片一个指令的完毕并使芯片完结这一批处理使命。

(ii)当这个trasfer是最终一个时,片选能够一向坚持有用知道下一个transfer到来。

在多spi从机的总线上没有办法阻挠其他设备接纳数据,这种办法能够作为一个特别的提示;开端往另一个设备传输信息就要先将本芯片的片选置为无效。但在其他情况下,这能够确保正确性。一些设备后边的信息依赖于前面的信息而且在一个处理序列完结后需求禁用片选线。

上面这段是翻译的,讲的不明白。

再说一下:cs_change影响此transfer完结后是否禁用片选线并调用setup改动装备。(这个标志量便是chip select change片选改动的意思)

没有特别情况,一个spi_message应该只在最终一个transfer置位该标志量。

6.spi_board_info

spi_device的板信息用spi_board_info结构体描绘,该结构体记录着SPI外设运用的主机操控器序号、片选序号、数据比特率、SPI传输形式(即CPOL、CPHA)等。ARM Linux3.x之后的内核在改为设备树之后,不再需求在arch/arm/mach-xxx中编码SPI的板级信息了,而倾向于在SPI操控器节点下填写子节点。

[cpp] view plain copystruct spi_board_info {

/* the device name and module name are coupled, like platform_bus;

* “modalias” is normally the driver name.

*

* platform_data goes to spi_device.dev.platform_data,

* controller_data goes to spi_device.controller_data,

* irq is copied too

*/

char modalias[SPI_NAME_SIZE];

const void *platform_data;

void *controller_data;

int irq;

/* slower signaling on noisy or low voltage boards */

u32 max_speed_hz;

/* bus_num is board specific and matches the bus_num of some

* spi_master that will probably be registered later.

*

* chip_select reflects how this chip is wired to that master;

* it‘s less than num_chipselect.

*/

u16 bus_num;

u16 chip_select;

/* mode becomes spi_device.mode, and is essential for chips

* where the default of SPI_CS_HIGH = 0 is wrong.

*/

u16 mode;

/* 。。。 may need additional spi_device chip config data here.

* avoid stuff protocol drivers can set; but include stuff

* needed to behave without being bound to a driver:

* – quirks like clock rate mattering when not selected

*/

};

7.spi_bitbang

spi_bitbang是详细的担任信息传输的数据结构,它保护一个workqueue_struct,每收到一个音讯,都会向其间增加一个work_struct,由内核看护进程在将来的某个时刻调用该work_struct中的function进行音讯发送。

[cpp] view plain copystruct spi_bitbang {

spinlock_t lock;

u8 busy; //忙标志

u8 use_dma;

u8 flags; /* extra spi-》mode support */

struct spi_master *master;

/* setup_transfer() changes clock and/or wordsize to match settings

* for this transfer; zeroes restore defaults from spi_device.

*/

int (*setup_transfer)(struct spi_device *spi,

struct spi_transfer *t); //对数据传输进行设置

void (*chipselect)(struct spi_device *spi, int is_on); //操控片选

#define BITBANG_CS_ACTIVE 1 /* normally nCS, active low */

#define BITBANG_CS_INACTIVE 0

/* txrx_bufs() may handle dma mapping for transfers that don’t

* already have one (transfer.{tx,rx}_dma is zero), or use PIO

*/

int (*txrx_bufs)(struct spi_device *spi, struct spi_transfer *t); //实践的数据传输函数

/* txrx_word[SPI_MODE_*]() just looks like a shift register */

u32 (*txrx_word[4])(struct spi_device *spi,

unsigned nsecs,

u32 word, u8 bits);

};

声明:本文内容来自网络转载或用户投稿,文章版权归原作者和原出处所有。文中观点,不代表本站立场。若有侵权请联系本站删除(kf@86ic.com)https://www.86ic.net/changshang/jieda/102966.html

为您推荐

联系我们

联系我们

在线咨询: QQ交谈

邮箱: kf@86ic.com

关注微信
微信扫一扫关注我们

微信扫一扫关注我们

返回顶部