没有合适的资源?快使用搜索试试~ 我知道了~
linuxSPI驱动框架源码分析.pdf
1.该资源内容由用户上传,如若侵权请联系客服进行举报
2.虚拟产品一经售出概不退款(资源遇到问题,请及时私信上传者)
2.虚拟产品一经售出概不退款(资源遇到问题,请及时私信上传者)
版权申诉
0 下载量 140 浏览量
2021-11-25
10:05:13
上传
评论
收藏 746KB PDF 举报
温馨提示
试读
31页
linuxSPI驱动框架源码分析.pdf
资源推荐
资源详情
资源评论
SPI 协议是一种同步的串行数据连接标准,由摩托罗拉公司命名,可工作于全双工模式。相
关通讯设备可工作于 m/s 模式。主设备发起数据帧,允许多个从设备的存在。每个从设备
有独立的片选信号, SPI 一般来说是四线串行总线结构。
接口:
SCLK —— Serial Clock(output from master) 时钟 (主设备发出 )
MOSI/SIMO —— Master Output, Slave Input(output from master) 数据信号线 mosi( 主设备
发出 )
MISO/SOMI —— Master Input,Slave Outpu(output from slave) 数据信号线 (从设备 )
SS —— Slave Select(active low;output from master) 片选信号
下面来看一下 Linux 中的 SPI 驱动。在 Linux 设备驱动框架的设计中,有一个重要的主机,
外设驱动框架分离的思想,如下图。
外设 a,b,c 的驱动与主机控制器 A,B,C 的驱动不相关,主机控制器驱动不关心外设, 而外设
驱动也不关心主机,外设只是访问核心层的通用的 API 进行数据的传输,主机和外设之间
可以进行任意的组合。如果我们不进行如图的主机和外设分离,外设 a,b,c 和主机 A,B,C 进
行组合的时候,需要 9 种不同的驱动。设想一共有个主机控制器, n 个外设,分离的结构是
需要 m+n 个驱动,不分离则需要 m*n 个驱动。
下面介绍 spi 子系统的数据结构:
在 Linux 中,使用 spi_master 结构来描述一个 SPI 主机控制器的驱动。
view plain
1. <span style= "font-size:18px;" >struct spi_master {
2. struct device dev; /* 总线编号,从 0 开始 */
3. s16 bus_num; /* 支持的片选的数量,从设备的片选号不能大于这个数量 */
4. u16 num_chipselect;
5. u16 dma_alignment; /* 改变 spi_device 的特性如:传输模式,字长,时钟频率 */
6. int (*setup)( struct spi_device *spi); /* 添加消息到队列的方法,这个函数不可睡眠,他的任务是安排发生的传送
并且调用注册的回调函数 complete()*/
7. int (*transfer)( struct spi_device *spi, struct spi_message *mesg);
8. void (*cleanup)( struct spi_device *spi);
9. };</span>
分配,注册和注销的 SPI 主机的 API 由 SPI 核心提供:
view plain
1. struct spi_master *spi_alloc_master( struct device *host, unsigned size);
2. int spi_register_master( struct spi_master *master);
3. void spi_unregister_master( struct spi_master *master);
在 Linux 中用 spi_driver 来描述一个 SPI 外设驱动。
view plain
1. struct spi_driver {
2. int (*probe)( struct spi_device *spi);
3. int (*remove)( struct spi_device *spi);
4. void (*shutdown)( struct spi_device *spi);
5. int (*suspend)( struct spi_device *spi, pm_message_t mesg);
6. int (*resume)( struct spi_device *spi);
7. struct device_driver driver;
8. };
可以看出, spi_driver 结构体和 platform_driver 结构体有极大的相似性,都有
probe(),remove(),suspend(),resume() 这样的接口。
Linux 用 spi_device 来描述一个 SPI 外设设备。
view plain
1. struct spi_device {
2. struct device dev;
3. struct spi_master *master; // 对应的控制器指针 u32
4. max_speed_hz; //spi 通信的时钟 u8
5. chip_select; // 片选,用于区分同一总线上的不同设备
6. u8 mode;
7. #define SPI_CPHA 0x01 /* clock phase */
8. #define SPI_CPOL 0x02 /* clock polarity */
9. #define SPI_MODE_0 (0|0) /* (original MicroWire) */#define SPI_MODE_1 (0|SPI_CPHA)
10. #define SPI_MODE_2 (SPI_CPOL|0)
11. #define SPI_MODE_3 (SPI_CPOL|SPI_CPHA)#define SPI_CS_HIGH 0x04 /* chipselect active h
igh? */
12. #define SPI_LSB_FIRST 0x08 /* per-word bits-on-wire */
13. #define SPI_3WIRE 0x10 /* SI/SO signals shared */
14. #define SPI_LOOP 0x20 /* loopback mode */
15. u8 bits_per_word; // 每个字长的比特数
16. int irq; // 使用的中断
17. void *controller_state;
18. void *controller_data;
19. char modalias[32]; // 名字
20. };
如下图,看这三个结构的关系, 这里 spi_device 与 spi_master 是同一个父设备, 这是在 spi_new_device函数中设定的,
一般这个设备是一个物理设备。
这里的 spi_master_class,spi_bus_type 又是什么呢,看下边两个结构体:
view plain
1. struct bus_type spi_bus_type = {
2. .name = "spi" ,
3. .dev_attrs = spi_dev_attrs,
4. .match = spi_match_device,
5. .uevent = spi_uevent,
6. .suspend = spi_suspend,
7. .resume = spi_resume,
8. };
9. static struct class spi_master_class = {
10. .name = "spi_master" ,
11. .owner = THIS_MODULE,
12. .dev_release = spi_master_release,
13. };
spi_bus_type 对应 spi 中的 spi bus 总线, spidev 的类定义如下:
view plain
1. static struct class *spidev_class;
创建这个类的主要目的是使 mdev/udev 能在 /dev 下创建设备节点 /dev/spiB.C 。B 代表总线,C 代表片外设备的片选号。
下边来看两个板级的结构, 其中 spi_board_info 用来初始化 spi_device ,s3c2410_spi_info
用来初始化 spi_master 。这两个板级的结构需要在移植的时候在
arch/arm/mach-s3c2440/mach-smdk2440.c 中初始化。
view plain
1. struct spi_board_info {
2. char modalias[32]; // 设备与驱动匹配的唯一标识
3. const void *platform_data;
4. void *controller_data;
5. int irq;
6. u32 max_speed_hz;
7. u16 bus_num; // 设备所归属的总线编号
8. u16 chip_select;
9. u8 mode;
10. };
11. struct s3c2410_spi_info {
12. int pin_cs; // 芯片选择管脚
13. unsigned int num_cs; // 总线上的设备数
14. int bus_num; // 总线号
15. void (*gpio_setup)( struct s3c2410_spi_info *spi, int enable); //spi 管脚配置函数
16. void (*set_cs)( struct s3c2410_spi_info *spi, int cs, int pol);
17. };
boardinfo 是用来管理 spi_board_info 的结构,spi_board_info 通过 spi_register_board_info(struct spi_board_info const
*info, unsigned n) 交由 boardinfo 来管理,并挂到 board_list 链表上, list_add_tail(&bi->list,&board_list);
view plain
1. struct boardinfo {
2. /* 用于挂到链表头 board_list 上 */
3. struct list_head list;
4. /* 管理的 spi_board_info 的数量 */
5. unsigned n_board_info;
6. /* 存放结构体 spi_board_info*/
7. struct spi_board_info board_info[0];
8. };
s3c24xx_spi 是 S3C2440 的 SPI 控制器在 Linux 内核中的具体描述,该结构包含 spi_bitbang 内嵌结构,控制器时钟频
率和占用的中断资源等重要成员,其中 spi_bitbang 具体负责 SPI 数据的传输。
view plain
1. struct s3c24xx_spi {
2. /* bitbang has to be first */
3. struct spi_bitbang bitbang;
4. struct completion done;
5. void __iomem *regs;
6. int irq;
7. int len;
8. int count;
9. void (*set_cs)( struct s3c2410_spi_info *spi, int cs, int pol);
10. /* data buffers */ const unsigned char *tx;
11. unsigned char *rx;
12. struct clk *clk;
13. struct resource *ioarea;
14. struct spi_master *master;
15. struct spi_device *curdev;
16. struct device *dev;
17. struct s3c2410_spi_info *pdata;
18. };
为了解决多个不同的 SPI 设备共享 SPI 控制器而带来的访问冲突, spi_bitbang 使用内核提供的工作队列 (workqueue) 。
workqueue 是 Linux 内核中定义的一种回调处理方式。采用这种方式需要传输数据时,不直接完成数据的传输,而是将
要传输的工作分装成相应的消息 (spi_message) ,发送给对应的 workqueue ,由与 workqueue 关联的内核守护线程
(daemon) 负责具体的执行。 由于 workqueue 会将收到的消息按时间先后顺序排列, 这样就是对设备的访问严格串行化,
解决了冲突。
view plain
1. <span style= "font-size:18px;" >struct spi_bitbang {
2. struct workqueue_struct *workqueue; // 工作队列头
3. struct work_struct work; // 每一次传输都传递下来一个 spi_message ,都向工作队列头添加一个
4. workspinlock_t lock;
5. struct list_head queue; // 挂接 spi_message ,如果上一次的 spi_message 还没有处理完,接下来的
spi_message 就挂接在 queue 上等待处理
6. u8 busy; // 忙碌标志
7. u8 use_dma;
8. u8 flags;
9. struct spi_master *master; /* 一下 3 个函数都是在函数 s3c24xx_spi_probe() 中被初始化 */
10. int (*setup_transfer)( struct spi_device *spi, struct spi_transfer *t); // 设置传输模式
11. void (*chipselect)( struct spi_device *spi, int is_on); // 片选
12. #define BITBANG_CS_ACTIVE 1 /* normally nCS, active low */
13. #define BITBANG_CS_INACTIVE 0/* 传输函数,由 s3c24xx_spi_txrx 来实现 */
14. int (*txrx_bufs)( struct spi_device *spi, struct spi_transfer *t);
15. u32 (*txrx_word[4])( struct spi_device *spi,unsigned nsecs,u32 word, u8 bits);
16. };</span>
下面来看看 spi_message :
view plain
1. struct spi_message {
2. struct list_head transfers; // 此次消息的传输队列,一个消息可以包含多个传输段
3. struct spi_device *spi; // 传输的目的设备
4. unsigned is_dma_mapped:1; // 如果为真,此次调用提供 dma 和 cpu 虚拟地址
5. void (*complete)( void *context); // 异步调用完成后的回调函数
6. void *context; // 回调函数的参数
7. unsigned actual_length; // 此次传输的实际长度
8. int status; // 执行的结果,成功被置 0,否则是一个负的错误码
9. struct list_head queue;
10. void *state;
11. };
剩余30页未读,继续阅读
资源评论
yusuyuan1
- 粉丝: 1
- 资源: 3万+
下载权益
C知道特权
VIP文章
课程特权
开通VIP
上传资源 快速赚钱
- 我的内容管理 展开
- 我的资源 快来上传第一个资源
- 我的收益 登录查看自己的收益
- 我的积分 登录查看自己的积分
- 我的C币 登录后查看C币余额
- 我的收藏
- 我的下载
- 下载帮助
安全验证
文档复制为VIP权益,开通VIP直接复制
信息提交成功