版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
1、 1 / 13 spi slave及master接口驱动及传输时序 spi slave驱动 spi slave驱动在kernel中可以主要参考spidev.c,这是一个字符驱动,可以匹配kernel中的多个名称为“spidev”的spi设备, 分析这个文件,主要有以下几个重点: 1.如何编写多设备公用驱动 2.如何封装读写请求到spi框架层 3. spi message请求如何分发到master 自spi_board_info或者spi master注册后,两者就已经完成了匹配的工作,spi slave驱动不关心任何匹配的细节,它只需要完成 与spi slave的匹配,就可以通过slave进而
2、找到master。这里是通过 spi_register_driver(&spidev_spi_driver);注册进 kernel,而后spi框架进行name match,再调用probe,完成关于设备的一些成员初始化操作。 下面针对上面的三个问题,进行分析这个驱动, spi设备全局链及保护信号量: static LIST_HEAD(device_list); static DEFINE_MUTEX(device_list_lock); 相对与设备的驱动数据: struct spidev_data dev_t devt;/设备号 2 / 13 spinlock_t spi_lock;/
3、spi结构体的pin锁 struct spi_device *spi; struct list_head device_entry;/挂接到device_list struct mutex buf_lock;/保护数据的lock unsigned users;/使用者 u8*buffer;/实际数据区,由open时进行动态分配,release时释放 ; spi中任何会由多个使用者访问的区域,都需要使用锁保护,如这里的users,个人觉得需要使用原子变量而不应该简单的使用整形。 在probe的时候,首先分配spidev_data,并初始化其 spi/device_entry/buf_lock/s
4、pi_lock,查找一个可用的bit用作次 设备号,创建设备spidev busnum.cs,挂到全局链中,并将私有数据spidev_data放到dev->p->driver_data中。 open时,从inode中获取dev_t,然后对比整个链,找到目标数据spidev_data,放到file->private_data中,并分配缓存 读写时,直接从file中获取对应的spidev_data数据,然后通过spi device来传递spi请求。以上主要是数据如何传递的问题。 SPI读写请求的封装很简单,如下: static inline ssize_t spidev_sync
5、_write(struct spidev_data *spidev, size_t len) struct spi_transfer t = .tx_buf= spidev->buffer, .len= len, 3 / 13 ; struct spi_message m; spi_message_init(&m); spi_message_add_tail(&t, &m); return spidev_sync(spidev, &m);static inline ssize_t spidev_sync_read(struct spidev_data *s
6、pidev, size_t len) struct spi_transfer t = .rx_buf= spidev->buffer, .len= len, ; struct spi_message m; spi_message_init(&m); spi_message_add_tail(&t, &m); return spidev_sync(spidev, &m);封装的同步函数: static ssize_t spidev_sync(struct spidev_data *spidev, struct spi_message *message)DEC
7、LARE_COMPLETION_ONSTACK(done); int status; message->context = spin_lock_irq(&spidev->spi_lock); if (spidev->spi = NULL) status = -ESHUTDOWN; else 4 / 13 status = spi_async(spidev->spi, message); spin_unlock_irq(&spidev->spi_lock); if (status = 0) status = message->status; i
8、f (status = 0) status = message->actual_length;return status;只需要调用spi_async就可以完成数据读取/写入的操作。 这个函数在内部真正做了什么?如何分发/回调?我们走一遍代码: 首先master内部有两个锁: spinlock_t bus_lock_spinlock;【用于异步】spi_async struct mutex bus_lock_mutex;【用于同步】spi_sync 对于不同的场景,需要对master进行不同类型的加锁, 异步: spin_lock_irqsave(&master->bus_
9、lock_spinlock, flags); ret = _spi_async(spi, message);message->spi = spi; message->status = -EINPROGRESS; return master->transfer(spi, message);spin_unlock_irqrestore(&master->bus_lock_spinlock, flags); 同步: message->context = if (!bus_locked) mutex_lock(&master->bus_lock_mu
10、tex); 5 / 13 status = spi_async_locked(spi, message); if (!bus_locked) mutex_unlock(&master->bus_lock_mutex); if (status = 0) status = message->status;这里即在kernel内部完成了同步的工作,不需要像spidev那样需要自己等待完成量,使用的是bus_lock_mutex 内部与异步的调用方式一致: spin_lock_irqsave(&master->bus_lock_spinlock, flags); re
11、t = _spi_async(spi, message); spin_unlock_irqrestore(&master->bus_lock_spinlock, flags); 从这里可以看出,同步与异步没有本质差别,只是多了一个完成量的操作而已。 最终调用的函数为: master->transfer(spi, message); 这个函数将在spi master中分析。 在spi slave侧需要熟悉传输的参数的每个域的功能,才能很好的完成工作 struct spi_transfer const void *tx_buf;/非dma 发送地址 void *rx_buf;/
12、非dma 读取地址 unsigned len;/tx/rx bufffer size dma_addr_t tx_dma;/若 spi_message.is_dma_mapped置位,为transfer的dma address 6 / 13 dma_addr_t rx_dma;/若 spi_message.is_dma_mapped置位,为read的dma address unsigned cs_change:1;/传输完成后,修改cs信号 u8bits_per_word;/长度,优先覆盖spi_board_info的设置 (32) u16delay_usecs;/传输后继续传输或者cs结束传
13、输的中间时隙 u32speed_hz;/本次传输的速度,可以优先覆盖spi_board_info里的设置 struct list_head transfer_list;/挂接到spi_message上的连接体; struct spi_message struct list_head transfers;/transfer链 struct spi_device *spi;/对应的spi设备 unsigned is_dma_mapped:1;/是否启动dma功能 void *context;/回调参数 unsigned actual_length;/传输的真正长度 int status;/0,成功
14、 struct list_head que;/driver使用 void *state; ; 每个域的使用方法,这里直接看起来并不明确,必须结合master的驱动。 - spi master驱动 7 / 13 - SPI 设备资源: static struct resource s3c_spi0_resource = 0 = DEFINE_RES_MEM(S3C24XX_PA_SPI, SZ_32), 1 = DEFINE_RES_IRQ(IRQ_SPI0), ; struct platform_device s3c_device_spi0 = .name= s3c2410-spi, .id=
15、 0, .num_resources= ARRAY_SIZE(s3c_spi0_resource), .resource= s3c_spi0_resource, .dev= .dma_mask= &samsung_device_dma_mask, .coherent_dma_mask= DMA_BIT_MASK (32),; static struct resource s3c_spi1_resource = 0 = DEFINE_RES_MEM(S3C24XX_PA_SPI1, SZ_32), 1 = DEFINE_RES_IRQ(IRQ_SPI1), ; struct platfo
16、rm_device s3c_device_spi1 = .name= s3c2410-spi, 8 / 13 .id= 1, .num_resources= ARRAY_SIZE(s3c_spi1_resource), .resource= s3c_spi1_resource, .dev= .dma_mask= &samsung_device_dma_mask, .coherent_dma_mask= DMA_BIT_MASK (32),; 在此之间,走过了一些弯路学了verilog/modelsim,在之前一直不明白的事情在逐渐的尝试中获得了新的认识, 硬件的ip core的工作是由
17、clock来驱动的,而不是软件意义上的过程,在同步时钟的上升/下降沿中进行数据处理,移位等 在SPI 的ip core设计中,主要有三个模块: 1. clock generate 2. data shift 3. register control 首先通过AP过来的系统时钟及设备能够支持的最大时钟频率,计算出对应的最接近的分频系数,而模块1就是根据这个分频系数 来通过系统的源clock产生对应的目标clock。【因为对于SPI IP不需要独立的精确的晶振】 生成了与slave同步的clock之后,输出到模块2,模块2负责具体的发送数据功能。具体的采数发数时序见第三节。 而register co
18、ntrol则负责所有的可配置接口,如: 分频系数,支持位宽,FIFO深度,支持的片选数,以及相应的MSB/LSB 9 / 13 设置选项。 在SPI的协议中,最大的误区在于master与slave之间的私有协议: SPI master的本生设计中并不支持具体的传输协议,而是简单的提供了一个传输数据的通路,而协议则是由实现的slave端,以及 slave端驱动来决定的。下面来总结这个数据发送与接收的具体过程: 读指定地址: read(addr, &value, len); 1.配置相关读取操作的寄存器 2. slave驱动封装协议CMD【描述base + addr + len + fla
19、gs】 3.拉下slave对应在master上的cs【低电平有效】 4.发送指定位宽cmd到slave端【由master来驱动,而slave只需要发送数据到对应fifo并启动发送即可】 5. slave端接收到指定的cmd,在MISO线上回应对应的数据 6. slave驱动从master 的fifo中等待数据,当master 读取到对应的线上数据,并放于FIFO中 7.读取到数据,拉高CS /*make sure len is word units*/ int gps_spi_read_bytes_test3( u32 len,u32 addr,u32 *data, bool sys, u32
20、base) u32 read_cmd2; local_spi_init(); read_cmd0 = SPI_READ_CMD(len,addr >> 2 + base); spi_assert_function 10 / 13 (0); local_spi_write(u8*)read_cmd, 4); local_spi_read(u8 *)data, 4); spi_assert_function (1); return 0;写指定地址: write(addr, &value, len); int gps_spi_write_bytes_test2( u32 len,
21、u32 addr,u32 data) u32 write_cmd2,one_read; local_spi_init(); write_cmd0 = SPI_WRITE_SYS_CMD(len*1,addr >> 2); write_cmd1 = data; spi_assert_function (0); local_spi_write(u8 *)write_cmd, 8); spi_assert_function (1); return 0;一些读写的实现细节: int local_spi_read(u8 *buf, u32 size) /*wait for data*/ wh
22、ile (reg->sts2 & (0x1 << 5) 11 / 13 if (time_out+ > READ_TIME_OUT) goto read_exit; *p = (u32)reg->txd; p+; /*if tx fifo is not full*/ while (reg->sts2 & (0x1 << 6); writel(*p, SPRD_SPI1_BASE); p+; /*bitlen is 4*/ 以上为master 驱动需要实现传输的一些流程 - spi传输时序问题 - 主要关注两个参数: CPOL: Clock初始电平(0:低电平,1:高电平) CPHA: 采样位置(0:第一个跳变边沿,1:第二个跳变边沿):上升沿采样,下降沿发送:下降沿采样,上升沿发送:下降沿采样,上升沿发送:上升沿采样,下降沿发送 这里需要注意,在双工状态下,不可同沿采样及发送,必须错开。 12 / 13 采
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
评论
0/150
提交评论