下面有两个大的模块:

一个是SPI总线驱动的分析            (研究了具体实现的过程)

另一个是SPI总线驱动的编写(不用研究具体的实现过程)

SPI总线驱动分析

 

1 SPI概述
      SPI是英语Serial Peripheral interface的缩写,顾名思义就是串行外围设备接口,是Motorola首先在其MC68HCXX系列处理器上定义的。SPI接口主要应用在 EEPROM,FLASH,实时时钟,AD转换器,还有数字信号处理器和数字信号解码器之间。SPI是一种高速的,全双工,同步的通信总线,并且在芯片的管脚上只占用四根线,节约了芯片的管脚,同时为PCB的布局上节省空间,提供方便。
      SPI的通信原理很简单,它以主从方式工作,这种模式通常有一个主设备和一个或多个从设备,需要4根线,事实上3根也可以。也是所有基于SPI的设备共有的,它们是SDI(数据输入),SDO(数据输出),SCLK(时钟),CS(片选)。
      MOSI(SDO):主器件数据输出,从器件数据输入。
      MISO(SDI):主器件数据输入,从器件数据输出。
      SCLK :时钟信号,由主器件产生。
      CS:从器件使能信号,由主器件控制。
      其中CS是控制芯片是否被选中的,也就是说只有片选信号为预先规定的使能信号时(高电位或低电位),对此芯片的操作才有效,这就允许在同一总线上连接多个SPI设备成为可能。需要注意的是,在具体的应用中,当一条SPI总线上连接有多个设备时,SPI本身的CS有可能被其他的GPIO脚代替,即每个设备的CS脚被连接到处理器端不同的GPIO,通过操作不同的GPIO口来控制具体的需要操作的SPI设备,减少各个SPI设备间的干扰。
      SPI是串行通讯协议,也就是说数据是一位一位从MSB或者LSB开始传输的,这就是SCK时钟线存在的原因,由SCK提供时钟脉冲,MISO、MOSI则基于此脉冲完成数据传输。 SPI支持4-32bits的串行数据传输,支持MSB和LSB,每次数据传输时当从设备的大小端发生变化时需要重新设置SPI Master的大小端。

2 Linux SPI驱动总体架构
      在2.6的linux内核中,SPI的驱动架构可以分为如下三个层次:SPI 核心层、SPI控制器驱动层和SPI设备驱动层。
      Linux 中SPI驱动代码位于drivers/spi目录。
2.1 SPI核心层
      SPI核心层是Linux的SPI核心部分,提供了核心数据结构的定义、SPI控制器驱动和设备驱动的注册、注销管理等API。其为硬件平台无关层,向下屏蔽了物理总线控制器的差异,定义了统一的访问策略和接口;其向上提供了统一的接口,以便SPI设备驱动通过总线控制器进行数据收发。
      Linux中,SPI核心层的代码位于driver/spi/ spi.c。由于该层是平台无关层,本文将不再叙述,有兴趣可以查阅相关资料。
2.2 SPI控制器驱动层
      SPI控制器驱动层,每种处理器平台都有自己的控制器驱动,属于平台移植相关层。它的职责是为系统中每条SPI总线实现相应的读写方法。在物理上,每个SPI控制器可以连接若干个SPI从设备。
      在系统开机时,SPI控制器驱动被首先装载。一个控制器驱动用于支持一条特定的SPI总线的读写。一个控制器驱动可以用数据结构struct spi_master来描述。

在include/liunx/spi/spi.h文件中,在数据结构struct spi_master定义如下:

  1. struct spi_master {
  2. struct device   dev;
  3. s16         bus_num;
  1. u16         num_chipselect;
  2. int         (*setup)(struct spi_device *spi);
  3. int         (*transfer)(struct spi_device *spi, struct spi_message *mesg);
  4. void        (*cleanup)(struct spi_device *spi);
  5. };

bus_num为该控制器对应的SPI总线号。
      num_chipselect 控制器支持的片选数量,即能支持多少个spi设备 
      setup函数是设置SPI总线的模式,时钟等的初始化函数, 针对设备设置SPI的工作时钟及数据传输模式等。在spi_add_device函数中调用。 
      transfer函数是实现SPI总线读写方法的函数。实现数据的双向传输,可能会睡眠

 cleanup注销时候调用

2.3 SPI设备驱动层
      SPI设备驱动层为用户接口层,其为用户提供了通过SPI总线访问具体设备的接口。
      SPI设备驱动层可以用两个模块来描述,struct spi_driver和struct spi_device。
      相关的数据结构如下:

  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. };

Driver是为device服务的,spi_driver注册时会扫描SPI bus上的设备,进行驱动和设备的绑定,probe函数用于驱动和设备匹配时被调用。从上面的结构体注释中我们可以知道,SPI的通信是通过消息队列机制,而不是像I2C那样通过与从设备进行对话的方式。

  1. struct spi_device {
  2. struct device       dev;
  3. struct spi_master   *master;
  4. u32         max_speed_hz;
  5. u8          chip_select;
  6. u8          mode;
  7. u8          bits_per_word;
  8. int         irq;
  9. void            *controller_state;
  10. void            *controller_data;
  11. char            modalias[32];
  12. };

.modalias   = "m25p10",

.mode   =SPI_MODE_0,   //CPOL=0, CPHA=0 此处选择具体数据传输模式

.max_speed_hz    = 10000000, //最大的spi时钟频率

/* Connected to SPI-0 as 1st Slave */

.bus_num    = 0,   //设备连接在spi控制器0上

.chip_select    = 0, //片选线号,在S5PC100的控制器驱动中没有使用它作为片选的依据,而是选择了下文controller_data里的方法。

.controller_data = &smdk_spi0_csi[0],

通常来说spi_device对应着SPI总线上某个特定的slave。并且spi_device封装了一个spi_master结构体。spi_device结构体包含了私有的特定的slave设备特性,包括它最大的频率,片选那个,输入输出模式等等

3 OMAP3630 SPI控制器
      OMAP3630上SPI是一个主/从的同步串行总线,这边有4个独立的SPI模块(SPI1,SPI2,SPI3,SPI4),各个模块之间的区别在于SPI1支持多达4个SPI设备,SPI2和SPI3支持2个SPI设备,而SPI4只支持1个SPI设备。

SPI控制器具有以下特征:
     1.可编程的串行时钟,包括频率,相位,极性。
     2.支持4到32位数据传输
     3.支持4通道或者单通道的从模式
     4.支持主的多通道模式
         4.1全双工/半双工
         4.2只发送/只接收/收发都支持模式
         4.3灵活的I/O端口控制
         4.4每个通道都支持DMA读写
     5.支持多个中断源的中断时间
     6.支持wake-up的电源管理
     7.内置64字节的FIFO

4 spi_device以下一系列的操作是在platform板文件中完成!

spi_device的板信息用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;
};

这个结构体记录了SPI外设使用的主机控制器序号、片选信号、数据比特率、SPI传输方式等

构建的操作是以下的两个步骤:

1.

static struct spi_board_info s3c_spi_devs[] __initdata = {

{

.modalias = "m25p10a",

.mode = SPI_MODE_0,

.max_speed_hz = 1000000,

.bus_num = 0,

.chip_select = 0,

.controller_data = &smdk_spi0_csi[SMDK_MMCSPI_CS],

},

};

2.

而这个info在init函数调用的时候会初始化:

spi_register_board_info(s3c_spi_devs,ARRAY_SIZE(s3c_spi_devs));

spi_register_board_info(s3c_spi_devs,ARRAY_SIZE(s3c_spi_devs));//注册spi_board_info。这个代码会把spi_board_info注册到链表board_list上。spi_device封装了一个spi_master结构体,事实上spi_master的注册会在spi_register_board_info之后,spi_master注册的过程中会调用scan_boardinfo扫描board_list,找到挂接在它上面的spi设备,然后创建并注册spi_device。

至此spi_device就构建并注册完成了!!!!!!!!!!!!!

 

5 spi_driver的构建与注册

 

driver有几个重要的结构体:spi_driver、spi_transfer、spi_message

driver有几个重要的函数    :spi_message_init、spi_message_add_tail、spi_sync

//spi_driver的构建

static struct spi_driver   m25p80_driver = { 

.driver = {

        .name   ="m25p80",

        .bus    =&spi_bus_type,

        .owner  = THIS_MODULE,

    },

    .probe  = m25p_probe,

    .remove =__devexit_p(m25p_remove),

};

//spidriver的注册

spi_register_driver(&m25p80_driver);

在有匹配的spi_device时,会调用m25p_probe

probe里完成了spi_transfer、spi_message的构建;

spi_message_init、spi_message_add_tail、spi_sync、spi_write_then_read函数的调用

 

例如:

  1. */
  2. static int m25p10a_read( struct m25p10a *flash, loff_t from,
  3. size_t len, char *buf )
  4. {
  5. int r_count = 0, i;
  6. struct spi_transfer st[2];
  7. struct spi_message  msg;
  8. spi_message_init( &msg );
  9. memset( st, 0, sizeof(st) );
  10. flash->cmd[0] = CMD_READ_BYTES;
  11. flash->cmd[1] = from >> 16;
  12. flash->cmd[2] = from >> 8;
  13. flash->cmd[3] = from;
  14. st[ 0 ].tx_buf = flash->cmd;
  15. st[ 0 ].len = CMD_SZ;
  16. spi_message_add_tail( &st[0], &msg );
  17. st[ 1 ].rx_buf = buf;
  18. st[ 1 ].len = len;
  19. spi_message_add_tail( &st[1], &msg );
  20. mutex_lock( &flash->lock );
  21. /* Wait until finished previous write command. */
  22. if (wait_till_ready(flash)) {
  23. mutex_unlock( &flash->lock );
  24. return -1;
  25. }
  26. spi_sync( flash->spi, &msg );
  27. r_count = msg.actual_length - CMD_SZ;
  28. printk( "in (%s): read %d bytes\n", __func__, r_count );
  29. for( i = 0; i < r_count; i++ ) {
  30. printk( "0x%02x\n", buf[ i ] );
  31. }
  32. mutex_unlock( &flash->lock );
  33. return 0;
  34. }
  35. static int m25p10a_write( struct m25p10a *flash, loff_t to,
  36. size_t len, const char *buf )
  37. {
  38. int w_count = 0, i, page_offset;
  39.     struct spi_transfer st[2]; 
  40.  
  41.     struct spi_message  msg;  
  42. write_enable( flash );  //写使能

        spi_message_init( &msg );

  1. memset( st, 0, sizeof(st) );
  2. flash->cmd[0] = CMD_PAGE_PROGRAM;
  3. flash->cmd[1] = to >> 16;
  4. flash->cmd[2] = to >> 8;
  5. flash->cmd[3] = to;
  6. st[ 0 ].tx_buf = flash->cmd;
  7. st[ 0 ].len = CMD_SZ;
  8. //填充spi_transfer,将transfer放在队列后面
  9.    spi_message_add_tail( &st[0], &msg );  
  10. st[ 1 ].tx_buf = buf;
  11. st[ 1 ].len = len;
  12.   spi_message_add_tail( &st[1], &msg );  
  1.        spi_sync( flash->spi, &msg );   调用spi_master发送spi_message
  2. return 0;
  3. }
  1. static int m25p10a_probe(struct spi_device *spi)
  2. {
  3. int ret = 0;
  4. struct m25p10a  *flash;
  5. char buf[ 256 ];
  6. flash = kzalloc( sizeof(struct m25p10a), GFP_KERNEL );
  7. flash->spi = spi;
  8. /* save flash as driver's private data */
  9. spi_set_drvdata( spi, flash );
  1. memset( buf, 0x7, 256 );
  2. m25p10a_write( flash, 0, 20, buf); //0地址写入20个7  
  3. memset( buf, 0, 256 );
  4.   m25p10a_read( flash, 0, 25, buf ); //0地址读出25个数  
  5. return 0;
  6. }

 

到目前为止,完成了SPI的驱动和应用

 

转载于:https://www.cnblogs.com/liugf05/archive/2012/12/03/2800457.html

linux内核SPI总线驱动分析(一)相关推荐

  1. linux内核spi总线驱动分析,Linux下的SPI总线驱动(三)

    版权所有,转载请说明转自 原创作者:南京邮电大学  通信与信息系统专业 研二 魏清 五.SPI测试代码 对于SPI总线驱动,我们可以分为SPI控制设备驱动和SPI接口设备驱动.而作为驱动开发人员主要是 ...

  2. Linux内核---31.按键驱动分析(未完成)

    一. device的注册 1.0 按键设备的注册 按键设备的定义在arch/arm/mach-s3c64xx中 /* gpio buttons */ static struct gpio_keys_b ...

  3. Linux内核---30.触摸屏驱动分析

    查看input系统 root@OK6410:~# cat /proc/bus/input/devices I: Bus=0013 Vendor=dead Product=beef Version=01 ...

  4. Linux kernel SPI源码分析之SPI设备驱动源码分析(linux kernel 5.18)

    SPI基础支持此处不再赘述,直接分析linux中的SPI驱动源码. 1.SPI设备驱动架构图 2.源码分析 本次分析基于kernel5.18,linux/drivers/spi/spidev.c 设备 ...

  5. linux 驱动入门 魏清,Linux下的SPI总线驱动(三)

    版权所有,转载请说明转自 原创作者:南京邮电大学  通信与信息系统专业 研二 魏清 五.SPI测试代码 对于SPI总线驱动,我们可以分为SPI控制设备驱动和SPI接口设备驱动.而作为驱动开发人员主要是 ...

  6. Linux内核的Nand驱动流程分析

    最近在做Linux内核移植,总体的感觉是这样的,想要彻底的阅读Linux内核代码几乎是不可能的,至少这还不是嵌入式学期初期的重要任务.内核代码解压后有250M左右,据统计,有400多万行,而且涉及到了 ...

  7. 【嵌入式Linux学习七步曲之第五篇 Linux内核及驱动编程】Linux内核抢占实现机制分析

    Linux内核抢占实现机制分析 Sailor_forever  sailing_9806@163.com 转载请注明 http://blog.csdn.net/sailor_8318/archive/ ...

  8. 嵌入式linux之SPI控制器驱动

    这篇文章粗略的浏览一下linux的SPI控制器驱动,我们知道SPI的device和driver是通过虚拟总线联系在一起的,但在spi_driver中有个spi控制器,这个控制器是怎么回事呢?spi控制 ...

  9. linux pl320 mbox控制器驱动分析 - (1) pl320手册分析

    linux pl320 mbox控制器驱动分析 1 pl320简介 1.1 pl320用途 1.2 pl320 IPCM 由以下部分组成: 1.3 pl320 IPCM可配置的参数 1.4 功能操作 ...

最新文章

  1. 谁说数学不好,就不能成为编程大佬
  2. C++中const与指针
  3. jquery中Live方法不可用,Jquery中Live方法失效
  4. UVA208Firetruck 消防车(图的路径搜索)
  5. 第十一章 AtomicInteger源码解析
  6. 面试题3:二维数组中的查找
  7. 小C语言程序----词法分析程序输出单词
  8. PHP文件系统-文件的读写操作
  9. PHP+Mysql+jQuery找回密码
  10. 依赖注入原理,作用,注入方式——Spring IOC/DI(二)
  11. 用麦咖啡(mcafee)打造自己的安全服务器
  12. MapReduce-处理需求NBA球员数据(Hadoop)
  13. 学计算机拼音不好怎么办,孩子拼音基础差怎么办?告诉你学拼音技巧!
  14. PR模板 有趣的波普艺术网络自媒体宣传PR视频模板
  15. Twilight暮光之城。。。暮色。。。
  16. excel表格打印每页都有表头_excel打印如何每页都有表头 最常用的办公小技巧
  17. h5 和 微信小程序添加emoji表情处理
  18. 设计模式---装饰器模式(C++实现)
  19. Eclipse 版本对应的 JDK版本要求
  20. c语言画伯努利分布图像,C++ - 随机生成器 伯努利分布(bernoulli distribution) 的 详解 及 代码...

热门文章

  1. vue路由导航守卫控制访问权限
  2. 曲折词缀和python的功能区别_派生词缀与屈折词缀的区别
  3. web前端培训哪家好
  4. 【怎么制作ppt】Focusky教程 | 文字格式刷
  5. 坐在电脑面前,准备刷面经的你看看这篇吧!全程节奏紧凑无尿点(2020我的大厂面经合集)
  6. 多闪删掉的作品怎么恢复_快手误删除作品咋恢复
  7. 在Atlas 200DK上体验DVPP(2)DVPP、AIPP和OM推理
  8. 仿炫酷头条小视频拖拽动画
  9. cfo是什么职位么职位(cfo是什么职位么职位是什么职位)
  10. AI大模型,驶向产业何方?