SPI驱动的框架​

SPI总线的核心文件:\drivers\spi\spi.c​

厂家提供

linux下SPI驱动核心分析_Linux

linux标准 用户针对不同的设备自己实现

linux下SPI驱动核心分析_SPI驱动_02

linux下SPI驱动核心分析_SPI驱动_03

linux下SPI驱动核心分析_SPI驱动_04

linux下SPI驱动核心分析_SPI驱动_05

linux下SPI驱动核心分析_Linux_06

linux下SPI驱动核心分析_Linux_07

linux下SPI驱动核心分析_Linux_08

linux下SPI驱动核心分析_Linux_09

linux下SPI驱动核心分析_Linux_10

linux下SPI驱动核心分析_SPI驱动_11


linux下SPI驱动核心分析_Linux_12


linux下SPI驱动核心分析_SPI驱动_13


SPI总线设备层的注册一般由厂家完成,用户只需要编写SPI的驱动层即可。

linux下SPI驱动核心分析_Linux_14


在spi.c中调用了总线注册函数,注册了SPI总线。

status = bus_register(&spi_bus_type);


总线类型定义:

struct bus_type spi_bus_type = {

.name= "spi",

.dev_attrs= spi_dev_attrs,

.match= spi_match_device, /*匹配设备端与驱动端处理函数*/

.uevent= spi_uevent,

.pm= &spi_pm,

};


spi的函数接口

1.2.1 spi 驱动注册与注销

int spi_register_driver(struct spi_driver *sdrv) //注册SPI的驱动端


static inline void spi_unregister_driver(struct spi_driver *sdrv) //注销SPI驱动端

以上两个函数在SPI驱动端使用。注册SPI的驱动。​

SPI驱动的注册参考:\drivers\spi\spidev.c


1.2.2 分配SPI主控制器

struct spi_master *spi_alloc_master(struct device *dev, unsigned size);

在该函数里创建了新的SPI设备,最终注册了SPI设备。

创建SPI设备调用流程:spi_match_master_to_boardinfo() --> spi_new_device

最终在of_register_spi_devices函数里注册添加了SPI设备端。


1.2.3 获取SPI设备数据

static inline void spi_master_set_devdata(struct spi_master *master, void *data) //设备数据设置

static inline void *spi_master_get_devdata(struct spi_master *master) //设备数据获取

在分配SPI主控制器的时候已经调用了SPI设备数据设置函数进行了设置,所以获取数据的函数就将设置的数据取出来。


1.2.4. 注册SPI主控制器​

int spi_register_master(struct spi_master *master) //注册SPI控制器


void spi_unregister_master(struct spi_master *master) //注销SPI控制器

SPI的主控器一般由厂家提供注册。​

exynos4412的SPI主控制器注册参考:\drivers\spi\spi-s3c64xx.c​

一个SPI主控制器结构就表示一路CPU的SPI总线接口。


  • struct spi_master结构原型如下:

struct spi_master {

struct devicedev; //驱动设备接口

struct list_head list; //链表头

s16bus_num; //总线编号

u16num_chipselect; //SPI片选编号

u16dma_alignment; //DMA模式

u16mode_bits;

u16flags;

#define SPI_MASTER_HALF_DUPLEXBIT(0)/* can't do full duplex */

#define SPI_MASTER_NO_RXBIT(1)/* can't do buffer read */

#define SPI_MASTER_NO_TXBIT(2)/* can't do buffer write */

spinlock_tbus_lock_spinlock;

struct mutexbus_lock_mutex;

boolbus_lock_flag;

int(*setup)(struct spi_device *spi); 更新SPI采样时钟

int(*transfer)(struct spi_device *spi,struct spi_message *mesg); 添加消息到SPI控制器的队列

void(*cleanup)(struct spi_device *spi);

boolqueued;

struct kthread_workerkworker;

struct task_struct*kworker_task;

struct kthread_workpump_messages;

spinlock_tqueue_lock;

struct list_headqueue;

struct spi_message*cur_msg;

boolbusy;

boolrunning;

boolrt;

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);

};


1.2.5 SPI控制器的使用计数

static inline void spi_master_put(struct spi_master *master) //取消SPI控制器的使用计数


1.2.6 注册SPI板级设备信息

int __devinitspi_register_board_info(struct spi_board_info const *info, unsigned n)

该函数一般在板级初始化代码里调用。比如:exynos4412开发板的SPI板级信息设置在mach-tiny4412.c文件的底层初始化函数里调用。struct spi_board_info结构体里保存了与SPI驱动端匹配的名称。

该函数里最终将传入的struct spi_board_info结构加入到struct boardinfo 结构的链表中。


  • struct spi_board_info 结构体原型:

struct spi_board_info {

charmodalias[SPI_NAME_SIZE];设备名称,与驱动端做匹配使用*/

const void*platform_data;/*平台数据*/

void*controller_data;

intirq;

u32max_speed_hz; //最大时钟频率

u16bus_num; //总线编号

u16chip_select;

u8mode; //数据传输模式

};

  • struct spi_board_info 结构体填充示例:

static struct spi_board_info spi1_board_info[] __initdata = {

{

.modalias = "spidev",

.platform_data = NULL,

.max_speed_hz = 10*1000*1000,

.bus_num = 1,

.chip_select = 0,

.mode = SPI_MODE_0,

.controller_data = &spi1_csi[0],

}

1.2.7 设置SPI驱动私有数据

static inline void spi_set_drvdata(struct spi_device *spi, void *data) //设置私有数据


static inline void *spi_get_drvdata(struct spi_device *spi) //获取设置的私有数据

spi_set_drvdata功能:

将data指针赋值给spidev->p->driver_data成员。

spi_get_drvdata功能:

获取之前设置的数据指针。

1.2.8 初始化SPI信息结构

static inline void spi_message_init(struct spi_message *m)

  • struct spi_message结构:

struct spi_message {

struct list_headtransfers;

struct spi_device*spi;加到传输队列中的spi设备

unsignedis_dma_mapped:1;

void(*complete)(void *context);传输完成回调函数

void*context; //complete函数的参数

unsignedactual_length;//所有成功传输字段的总长度

intstatus;//传输成功返回0,否则返回错误

struct list_headqueue;

void*state;

};

1.2.9 添加信息结构

static inline voidspi_message_add_tail(struct spi_transfer *t, struct spi_message *m)

功能:将struct spi_message m结构添加到struct spi_transfer 结构的transfer_list成员中。

  • struct spi_transfer结构原型:

struct spi_transfer {

const void*tx_buf; //写数据的缓冲区

void*rx_buf; //读数据的缓冲区

unsignedlen; //数据长度


dma_addr_ttx_dma; //DMA地址

dma_addr_trx_dma;


unsignedcs_change:1;

u8bits_per_word; //传输的字节数,不设置使用默认的

u16delay_usecs;//微秒延时,用以传输数据后,改变片选信号前

u32speed_hz;//传输速率,不选就用默认的

struct list_head transfer_list;

}


1.2.10 SPI异步传输数据

int spi_async(struct spi_device *spi, struct spi_message *message)

参数:

struct spi_device *spi :交换数据的SPI设备

struct spi_message *message :传输的信息结构


1.2.11 等待传输完成

void __sched wait_for_completion(struct completion *x)

该函数在SPI发送的时候有调用到,等待SPi的数据发送完成。


1.2.12 设置SPI的时钟与速率

int spi_setup(struct spi_device *spi)


1.2.13 SPI同步传输

int spi_sync(struct spi_device *spi, struct spi_message *message)

该函数传输数据的过程中会进行阻塞。


1.2.14 SPI同步写函数

static inline int spi_write(struct spi_device *spi, const void *buf, size_t len);

参数:

@spi: 要写的SPI设备

@buf: 写入的数据缓冲区

@len: 写入的数据缓冲区大小


1.2.15 SPI同步读

static inline int spi_read(struct spi_device *spi, void *buf, size_t len)

参数:

将被读的设备

读出存放的数据缓冲区

读出的数据缓冲区大小


1.2.16 SPI同步8位写紧随其后的8位读--(不是同步,写和读是分开两次进行的)

static inline ssize_t spi_w8r8(struct spi_device *spi, u8 cmd)

参数:

数据交换的SPI设备

寄存器命令


1.2.17 SPI同步8位写紧随其后的16位读-(不是同步,写和读是分开两次进行的)

static inline ssize_t spi_w8r16(struct spi_device *spi, u8 cmd)

参数:

数据交换的SPI设备

寄存器命令


1.2.18 先写后读同步

int spi_write_then_read(struct spi_device *spi,const void *txbuf, unsigned n_tx,void *rxbuf, unsigned n_rx)

参数:

数据交换的设备

写数据(不需要dma-safe)

大小,以字节为单位

将读数据的缓冲区(不需要dma-safe)

大小,以字节为单位


1.3 linxu内核的SPI测试代码


Linxu内核自带的SPI驱动注册示例代码:\drivers\spi\spidev.c

Linxu内核自带的SPI APP注册示例代码:\Documentation\spi



配置exynos 4412内核支持SPI总线

linux下SPI驱动核心分析_Linux_15



Exynos4412设备的板级资源在mach-tiny4412.c中定义,如果要保证SPI驱动端的资源探测函数能够正常调用,需要保证mach-tiny4412.中的SPI设备名称与SPI驱动端的一样。


1.4 SPI子系统使用测试


/*

函数功能:读取设备ID和制造商ID

W25Q64: EF16

W25QQ128:EF17


参数:0x90表示读取ID号的指令

*/

unsigned short W25Q64_ReadDeviceID(void)

{

/*使用硬件SPI同步读写时序*/

char tx_buf[6]={0x90,0x0,0x0,0x0,0xFF,0xFF};

char rx_buf[6];


struct spi_message m;

struct spi_transfer t=

{

.tx_buf=tx_buf,

.rx_buf=rx_buf,

.len=6,

.delay_usecs=0,

.speed_hz=1000000,

.bits_per_word=8

};

spi_message_init(&m);

spi_message_add_tail(&t,&m);

spi_sync(w25q64_spi_Device,&m);

return rx_buf[4]<<8|rx_buf[5]; /*得到ID值*/

}