您的位置:首页 > 运维架构 > Linux

linux spi子系统 .

2014-01-08 20:25 656 查看
============================================

作者:yuanlulu
http://blog.csdn.net/yuanlulu
版权没有,但是转载请保留此段声明

============================================

spi_master/spi_device和spi_driver的关系。



重要的数据结构:

~~~~~~~~~~

spi控制器的主题是spi_master,虽然一般不需要自己编写spi控制器驱动,了解这个结构体还是必要的。

[c-sharp]
view plaincopyprint?

struct spi_master {
struct device dev;

s16 bus_num;//总线编号,从零开始

u16 num_chipselect;//支持的片选的数量。从设备的片选号不能大于这个数

/* setup mode and clock, etc (spi driver may call many times) */
int (*setup)(struct spi_device *spi);//根据spi设备更新硬件配置。

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函数。

};

[cpp]
view plaincopyprint?

struct spi_driver {
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;
};

struct spi_driver {
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;
};


这个结构体是一个辅助性的数据结构。主要是提供驱动模型下的绑定方法和电源管理接口。其成员driver.name是和spi_device匹配的依据。

这个结构的用处是绑定arch/.../mach-*/board-*.c 中调用spi_register_board_info注册的信息对应的spi_device。

内核中的一个例子如下。

[cpp]
view plaincopyprint?

static struct spi_driver tle62x0_driver = {
.driver = {
.name = "tle62x0",
.owner = THIS_MODULE,
},
.probe = tle62x0_probe,
.remove = __devexit_p(tle62x0_remove),
};

static __init int tle62x0_init(void)
{
return spi_register_driver(&tle62x0_driver);
}

static __exit void tle62x0_exit(void)
{
spi_unregister_driver(&tle62x0_driver);
}

[cpp]
view plaincopyprint?

struct spi_device {
struct device  dev;
struct spi_master *master;//对应的控制器指针

u32   max_speed_hz;//spi通信时钟
u8   chip_select;//片选号,用来区分同一主控制器上的设备。

u8   mode;//各位的定义如下,主要是传输模式、片选极性。

#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? */片选电位为高

#define SPI_LSB_FIRST 0x08   /* per-word bits-on-wire */先输出低比特

#define SPI_3WIRE 0x10   /* SI/SO signals shared */输入输出共享接口,此时只能做半双工。

#define SPI_LOOP 0x20   /* loopback mode */回写/回显模式

u8   bits_per_word;//每个字长的比特数。

int   irq;//使用到的中断

void   *controller_state;
void   *controller_data;
char   modalias[32];//名字。

};

~~~~~~~~~~~~~~~~~~~~~~~
这个结构体描述设备的信息。
struct spi_board_info {
char  modalias[32];//设备名

const void *platform_data;//平台数据

void  *controller_data;
int  irq;//中断

/* slower signaling on noisy or low voltage boards */
u32  max_speed_hz;//通信时钟

u16  bus_num;//总线号
u16  chip_select;//片选号

u8  mode;//参考spi_device中的成员
};

struct spi_device {
struct device  dev;
struct spi_master *master;//对应的控制器指针
u32   max_speed_hz;//spi通信时钟
u8   chip_select;//片选号,用来区分同一主控制器上的设备。
u8   mode;//各位的定义如下,主要是传输模式、片选极性。
#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? */片选电位为高
#define SPI_LSB_FIRST 0x08   /* per-word bits-on-wire */先输出低比特
#define SPI_3WIRE 0x10   /* SI/SO signals shared */输入输出共享接口,此时只能做半双工。
#define SPI_LOOP 0x20   /* loopback mode */回写/回显模式
u8   bits_per_word;//每个字长的比特数。
int   irq;//使用到的中断
void   *controller_state;
void   *controller_data;
char   modalias[32];//名字。
};

~~~~~~~~~~~~~~~~~~~~~~~
这个结构体描述设备的信息。
struct spi_board_info {
char  modalias[32];//设备名
const void *platform_data;//平台数据
void  *controller_data;
int  irq;//中断

/* slower signaling on noisy or low voltage boards */
u32  max_speed_hz;//通信时钟

u16  bus_num;//总线号
u16  chip_select;//片选号

u8  mode;//参考spi_device中的成员
};


如何编写一个spi驱动?(设备驱动,这里不涉及spi控制器的驱动)

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

静态声明

======

声明从设备

-----------------

arch/.../mach-*/board-*.c中一般包含如下的声明

[cpp]
view plaincopyprint?

static struct ads7846_platform_data ads_info = {
.vref_delay_usecs = 100,
.x_plate_ohms = 580,
.y_plate_ohms = 410,
};

static struct spi_board_info spi_board_info[] __initdata = {
{
.modalias = "ads7846",
.platform_data = &ads_info,
.mode = SPI_MODE_0,
.irq = GPIO_IRQ(31),
.max_speed_hz = 120000 /* max sample rate at 3V */ * 16,
.bus_num = 1,
.chip_select = 0,
},
};

[cpp]
view plaincopyprint?

static struct spi_driver CHIP_driver = {
.driver = {
.name  = "CHIP",
.owner  = THIS_MODULE,
},

.probe  = CHIP_probe,
.remove  = __devexit_p(CHIP_remove),
.suspend = CHIP_suspend,
.resume  = CHIP_resume,
};

static struct spi_driver CHIP_driver = {
.driver = {
.name  = "CHIP",
.owner  = THIS_MODULE,
},

.probe  = CHIP_probe,
.remove  = __devexit_p(CHIP_remove),
.suspend = CHIP_suspend,
.resume  = CHIP_resume,
};


驱动核心会将board_info的modalias成员为“CHIP”的设备和本驱动进行绑定。

probe的代码应该像下面这样

[cpp]
view plaincopyprint?

static int __devinit CHIP_probe(struct spi_device *spi)
{
struct CHIP *chip;
struct CHIP_platform_data *pdata;

/* assuming the driver requires board-specific data: */
pdata = &spi->dev.platform_data;
if (!pdata)
return -ENODEV;

/* get memory for driver's per-chip state */
chip = kzalloc(sizeof *chip, GFP_KERNEL);//为自己的结构体申请内存空间。

if (!chip)
return -ENOMEM;
spi_set_drvdata(spi, chip);

... etc//其他句子。
return 0;
}

[c-sharp]
view plaincopyprint?

struct spi_message {
struct list_head transfers;//此次消息的传输段队列。一个消息可以包含多个传输段。

struct spi_device *spi;//传输的目的设备

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

/* completion is reported through a callback */
void   (*complete)(void *context);//异步调用完成后的回调函数

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

unsigned  actual_length;//此次传输的实际长度

int   status;//执行的结果。成功被置0,否则是一个负的错误码。

struct list_head queue;//下面两个成员是给拥有本消息的驱动选用的。spi_master会使用它们。自己最好不要使用。

void   *state;
};

struct spi_message {
struct list_head transfers;//此次消息的传输段队列。一个消息可以包含多个传输段。

struct spi_device *spi;//传输的目的设备

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

/* completion is reported through a callback */
void   (*complete)(void *context);//异步调用完成后的回调函数
void   *context;//回调函数的参数
unsigned  actual_length;//此次传输的实际长度
int   status;//执行的结果。成功被置0,否则是一个负的错误码。

struct list_head queue;//下面两个成员是给拥有本消息的驱动选用的。spi_master会使用它们。自己最好不要使用。
void   *state;
};


spi_message用来原子的执行spi_transfer表示的一串数组传输请求。

这个传输队列是原子的,这意味着在这个消息完成之前不会有其它消息占用总线。

消息的执行总是按照FIFO的顺序。

向底层提交spi_message的代码要负责管理它的内存空间。未显示初始化的内存需要使用0来初始化。

spi_transfer

----------------

上面看到,一个spi_message是由多个spi_transfer组成的。

每个spi_transfer总是读取和写入同样长度的比特数,但是可以很容易的使用空指针舍弃读或写。

为spi_transfer和spi_message分配的内存应该在消息处理期间保证是完整的。

[c-sharp]
view plaincopyprint?

struct 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地址

unsigned cs_change:1;//影响此次传输之后的片选。指示本次transfer结束之后是否要重新片选并调用setup改变设置。这个标志可以减少系统开销。

u8 bits_per_word;//每个字长的比特数。如果是0,使用默认值

u16 delay_usecs;//此次传输结束和片选改变之间的延时,之后就会启动另一个传输或者结束整个消息。

u32 speed_hz;//通信时钟。如果是0,使用默认值

struct list_head transfer_list;//用来连接的双向链表节点。

};

struct 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地址

unsigned cs_change:1;//影响此次传输之后的片选。指示本次transfer结束之后是否要重新片选并调用setup改变设置。这个标志可以减少系统开销。
u8 bits_per_word;//每个字长的比特数。如果是0,使用默认值
u16 delay_usecs;//此次传输结束和片选改变之间的延时,之后就会启动另一个传输或者结束整个消息。
u32 speed_hz;//通信时钟。如果是0,使用默认值

struct list_head transfer_list;//用来连接的双向链表节点。
};

控制器驱动会先写入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置位该标志量。

spi_message的函数接口

-------------------------------

static inline void spi_message_init(struct spi_message *m);//初始化,实际是用0填充并初始化链表节点。

static inline void

spi_message_add_tail(struct spi_transfer *t, struct spi_message *m);//将transfer加到message的链表尾部。

static inline void

spi_transfer_del(struct spi_transfer *t);//将transfer从message链表中移除

static inline struct spi_message *spi_message_alloc(unsigned ntrans, gfp_t flags);//创建一个message并创建 ntrans个transfer加入链表,flag是申请内存时的标志。

static inline void spi_message_free(struct spi_message *m);//释放message。

bits_per_word

-----------------------

在spi_device和spi_transfer中均有bits_per_word的定义。

spi控制器驱动中会首先使用spi_transfer中的定义,如果它的值为0则使用默认值,也就是spi_device的bits_per_word定义,如果它也为零,则使用默认值8(bit).

在spi_board_info 中没有定义bits_per_word,而且spi_write/spi_read等封装接口函数都没有初始化这个变量。因此如果字长为8不能满足需要,应该显式的初始化spi_device的bits_per_word,或者使用

原始接口并初始化spi_transfer的bits_per_word成员。

原文见:/article/2338793.html
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: