版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
1、Linux下spi驱动开发 一、概述基于子系统去开发驱动程序已经是linux内核中普遍的做法了。前面写过基于I2C子系统的驱动开发。本文介绍另外一种常用总线SPI的开发方法。SPI子系统的开发和I2C有很多的相似性,大家可以对比学习。本主题分为两个部分叙述,第一部分介绍基于SPI子系统开发的理论框架;第二部分以华清远见教学平台FS_S5PC100上的M25P10芯片为例(内核版本2.6.29),编写一个SPI驱动程序实例。二、SPI总线协议简介介绍驱动开发前,需要先熟悉下SPI通讯协议中的几个关键的地方,后面在编写驱动时,需要考虑相关因素。SPI总线由MISO(串行数据输入)、MOS
2、I(串行数据输出)、SCK(串行移位时钟)、CS(使能信号)4个信号线组成。如FS_S5PC100上的M25P10芯片接线为: 上图中M25P10的D脚为它的数据输入脚,Q为数据输出脚,C为时钟脚。SPI常用四种数据传输模式,主要差别在于:输出串行同步时钟极性(CPOL)和相位(CPHA)可以进行配置。如果CPOL= 0,串行同步时钟的空闲状态为低电平;如果CPOL= 1,串行同步时钟的空闲状态为高电平。如果CPHA= 0,在串行同步时钟的前沿(上升或下降)数据被采样;如果CPHA = 1,在串行同步时钟的后沿(上升或下降)数据被采样。 这四种模式中究竟选择哪种
3、模式取决于设备。如M25P10的手册中明确它可以支持的两种模式为:CPOL=0 CPHA=0 和 CPOL=1 CPHA=1 三、linux下SPI驱动开发首先明确SPI驱动层次,如下图: 我们以上面的这个图为思路1、 Platform busPlatform bus对应的结构是platform_bus_type,这个内核开始就定义好的。我们不需要定义。2、Platform_deviceSPI控制器对应platform_device的定义方式,同样以S5PC100中的SPI控制器
4、为例,参看arch/arm/plat-s5pc1xx/dev-spi.c文件点击(此处)折叠或打开1. struct platform_device s3c_device_spi0 = 2. .name = "s3c64xx-spi&qu
5、ot;, /名称,要和Platform_driver匹配3. .id = 0, /第0个控制器,S5PC100中有3个控制器4. .num_resources =
6、160;ARRAY_SIZE(s5pc1xx_spi0_resource), /占用资源的种类5. .resource = s5pc1xx_spi0_resource, /指向资源结构数组的指针6.
7、 .dev = 7. .dma_mask = &spi_dmamask, /dma寻址范围 8.
8、160; .coherent_dma_mask = DMA_BIT_MASK(32), /可以通过关闭cache等措施保证一致性的dma寻址范围9. &
9、#160; .platform_data = &s5pc1xx_spi0_pdata, /特殊的平台数据,参看后文10. ,11.
10、 12.13. static struct s3c64xx_spi_cntrlr_info s5pc1xx_spi0_pdata = 14. .cfg_gpio = s5pc1xx_spi_cfg_gpio, /用于控制器管脚的IO配置15.
11、0; .fifo_lvl_mask = 0x7f,16. .rx_lvl_offset = 13,17.
12、 18.19. static int s5pc1xx_spi_cfg_gpio(struct platform_device *pdev)20. 21.
13、; switch (pdev->id) 22. case 0:23.
14、160; s3c_gpio_cfgpin(S5PC1XX_GPB(0), S5PC1XX_GPB0_SPI_MISO0);24. s3c_gpio_cfgpin(S5PC1XX_GPB(1), S5
15、PC1XX_GPB1_SPI_CLK0);25. s3c_gpio_cfgpin(S5PC1XX_GPB(2), S5PC1XX_GPB2_SPI_MOSI0);26.
16、 s3c_gpio_setpull(S5PC1XX_GPB(0), S3C_GPIO_PULL_UP);27.
17、 s3c_gpio_setpull(S5PC1XX_GPB(1), S3C_GPIO_PULL_UP);28. s3c_gpio_setpull(S5PC1XX_GPB(2), S3C_GPIO_PULL_UP);29.
18、 break;30.31. case 1:32. &
19、#160; s3c_gpio_cfgpin(S5PC1XX_GPB(4), S5PC1XX_GPB4_SPI_MISO1);33. s3c_gpio_cfgpin(S5PC1XX_GPB(5), S5PC1XX_GPB5_SPI_CL
20、K1);34. s3c_gpio_cfgpin(S5PC1XX_GPB(6), S5PC1XX_GPB6_SPI_MOSI1);35.
21、; s3c_gpio_setpull(S5PC1XX_GPB(4), S3C_GPIO_PULL_UP);36.
22、;s3c_gpio_setpull(S5PC1XX_GPB(5), S3C_GPIO_PULL_UP);37. s3c_gpio_setpull(S5PC1XX_GPB(6), S3C_GPIO_PULL_UP);38.
23、; break;39.40. case 2:41.
24、 s3c_gpio_cfgpin(S5PC1XX_GPG3(0), S5PC1XX_GPG3_0_SPI_CLK2);42. s3c_gpio_cfgpin(S5PC1XX_GPG3(2), S5PC1XX_GPG3_2_SPI_MISO2);43.
25、60; s3c_gpio_cfgpin(S5PC1XX_GPG3(3), S5PC1XX_GPG3_3_SPI_MOSI2);44. &
26、#160; s3c_gpio_setpull(S5PC1XX_GPG3(0), S3C_GPIO_PULL_UP);45. s3c_gp
27、io_setpull(S5PC1XX_GPG3(2), S3C_GPIO_PULL_UP);46. s3c_gpio_setpull(S5PC1XX_GPG3(3), S3C_GPIO_PULL_UP);47.
28、0; break;48.49. default:50. dev
29、_err(&pdev->dev, "Invalid SPI Controller number!");51. return -EINVAL;52. &
30、#160; 3、Platform_driver再看platform_driver,参看drivers/spi/spi_s3c64xx.c文件点击(此处)折叠或打开1. static struct platform_driver s3c64xx_spi_driver = 2.
31、60; .driver = 3. .name
32、;= "s3c64xx-spi", /名称,和platform_device对应4. .owner = THIS_MODULE,5.
33、; ,6. .remove = s3c64xx_spi_remove,7. &
34、#160; .suspend = s3c64xx_spi_suspend,8.
35、; .resume = s3c64xx_spi_resume,9. 10.11. platform_driver_probe(&s3c64xx_spi_driver, s3c64xx_spi_probe);/注册s3c64xx_spi_driver和平台中注册的p
36、latform_device匹配后,调用s3c64xx_spi_probe。然后根据传入的platform_device参数,构建一个用于描述SPI控制器的结构体spi_master,并注册。spi_register_master(master)。后续注册的spi_device需要选定自己的spi_master,并利用spi_master提供的传输功能传输spi数据。和I2C类似,SPI也有一个描述控制器的对象叫spi_master。其主要成员是主机控制器的序号(系统中可能存在多个SPI主机控制器)、片选数量、SPI模式和时钟设置用到的函数、数据传输用到的函数等。点击(此处)折叠或打开1. s
37、truct spi_master 2. struct device dev;3.
38、160; s16 bus_num; /表示是SPI主机控制器的编号。由平台代码决定4. u16 num_chipselect; /控制器支持的片选数量,即能支持多少个spi设备5.
39、160; int (*setup)(struct spi_device *spi); /针对设备设置SPI的工作时钟及数据传输模式等。在spi_add_device函数中调用。6. int (*transfer)(struct spi_device *spi,7.
40、; struct spi_message *mesg); /实现数据的双向传输,可能会睡眠8.
41、60;void (*cleanup)(struct spi_device *spi); /注销时调用9. 4、Spi busSpi总线对应的总线类型为spi_bus_type,在内核的drivers/spi/spi.c中定义点击(此处)折叠或打开1. struct bus_type spi_bus_type = 2. &
42、#160; .name = "spi",3. .dev_attrs =&
43、#160;spi_dev_attrs,4. .match = spi_match_device,5.
44、; .uevent = spi_uevent,6. .suspend = spi_suspend,7. &
45、#160; .resume = spi_resume,8. 对应的匹配规则是(高版本中的匹
46、配规则会稍有变化,引入了id_table,可以匹配多个spi设备名称):点击(此处)折叠或打开1. static int spi_match_device(struct device *dev, struct device_driver *drv)2. 3. &
47、#160; const struct spi_device *spi = to_spi_device(dev);4.
48、 return strcmp(spi->modalias, drv->name) = 0;5. 5、spi_device下面该讲到spi_device的构建与注册了。spi_device对应的含义是挂接在spi总线上的一个设备,所以描述它的时候应该明确它自身的设备特性、传
49、输要求、及挂接在哪个总线上。点击(此处)折叠或打开1. static struct spi_board_info s3c_spi_devs _initdata = 2. 3.
50、; .modalias = "m25p10", 4.
51、60; .mode = SPI_MODE_0, /CPOL=0, CPHA=0 此处选择具体数据传输模式5.
52、0; .max_speed_hz = 10000000, /最大的spi时钟频率6. /* Connected to SPI-0 as 1st Slave */7.
53、60; .bus_num = 0, /设备连接在spi控制器0上8.
54、; .chip_select = 0, /片选线号,在S5PC100的控制器驱动中没有使用它作为片选的依据,而是选择了下文controller_data里的方法。9.
55、0; .controller_data = &smdk_spi0_csi0,10. ,11.
56、 12. static struct s3c64xx_spi_csinfo smdk_spi0_csi = 13.
57、; 0 = 14.
58、;.set_level = smdk_m25p10_cs_set_level,15. .fb_delay = 0x3,16. &
59、#160; ,17. 18.
60、0; static void smdk_m25p10_cs_set_level(int high) /spi控制器会用这个方法设置cs19. 20.
61、 u32 val;21. val = re
62、adl(S5PC1XX_GPBDAT);22. if (high)23.
63、 val |= (1<<3);24. else25.
64、 val &= (1<<3);26.
65、0; writel(val, S5PC1XX_GPBDAT);27. 28.29. spi_register_board_info(s3c_spi_devs, ARRAY_SIZE(s3c_spi_
66、devs);/注册spi_board_info。这个代码会把spi_board_info注册要链表board_list上。事实上上文提到的spi_master的注册会在spi_register_board_info之后,spi_master注册的过程中会调用scan_boardinfo扫描board_list,找到挂接在它上面的spi设备,然后创建并注册spi_device。点击(此处)折叠或打开1. static void scan_boardinfo(struct spi_master *master)2.
67、 3. struct boardinfo *bi;4.
68、0; mutex_lock(&board_lock);5.
69、60;list_for_each_entry(bi, &board_list, list) 6. struct spi_board_info *
70、chip = bi->board_info;7. unsigned n;8.
71、160; for (n = bi->n_board_info; n > 0; n-, chip+) 9.
72、; if (chip->bus_num != master->bus_num)10.
73、60; continue;11.
74、160; /* NOTE: this relies on spi_new_device to12.
75、0; * issue diagnostics when given bogus inputs13.
76、 */14.
77、 (void) spi_new_device(master, chip); /创建并注册了spi_device15.
78、; 16. &
79、#160; 17. mutex_unlock(&board_lock);18.
80、;6、spi_driver本文先以linux内核中的/driver/mtd/devices/m25p80.c驱动为参考。点击(此处)折叠或打开1. static struct spi_driver m25p80_driver = /spi_driver的构建2. .driver = 3.
81、60; .name = "m25p80",4.
82、; .bus = &spi_bus_type,5.
83、60; .owner = THIS_MODULE,6. ,7. &
84、#160; .probe = m25p_probe,8. .rem
85、ove = _devexit_p(m25p_remove),9. */10.
86、;11.12. spi_register_driver(&m25p80_driver);/spi driver的注册13.14. 在有匹配的spi device时,会调用m25p_probe15.16. static int _devinit m25p_probe(struct spi_device *spi)17. 18.
87、60; 19. 根据传入的spi_device参数,可以找到对应的spi_master。接下来就可以利用spi子系统为我们完成数据交互了。可以参看m25p80_read函数。要完成传输,先理解下面几个结构的含义:(这两个结构的定义及详细注释参见include/linux/spi/spi.h)spi_message
88、:描述一次完整的传输,即cs信号从高->底->高的传输 spi_transfer:多个spi_transfer够成一个spi_message 举例说明:m25p80的读过程如下图 可以分解为两个spi_ transfer一个是写命令,另一个是读数据。具体实现参见m25p80.c中的m25p80_read函数。下面
89、内容摘取之此函数。点击(此处)折叠或打开1. struct spi_transfer t2; /定义了两个spi_transfer2. struct spi_message m; /定义了两个spi_message3.
90、 spi_message_init(&m); /初始化其transfers链表4.5. t0.tx_buf = flash->command;6. t0.len = CMD_SIZE + FAST_READ_DUMMY_BYTE; /定义第一个
91、transfer的写指针和长度7. spi_message_add_tail(&t0, &m); /添加到spi_message8. t1.rx_buf = buf;9.
92、 t1.len = len; /定义第二个transfer的读指针和长度10.11. spi_message_add_tail(&t1, &m); /添加到spi_message12. f
93、lash->command0 = OPCODE_READ;13. flash->command1 = from >> 16;14.
94、 flash->command2 = from >> 8;15. flash->command3 = from; /初始化前面写buf的内容16.17. spi_sync(flash->spi, &m); /调用spi_master发
95、送spi_message18.19. / spi_sync为同步方式发送,还可以用spi_async异步方式,那样的话,需要设置回调完成函数。20.21. 另外你也可以选择一些封装好的更容易使用的函数,这些函数可以在include/linux/spi/spi.h文件中找到,如:22.23. extern int spi_write_then_read(struct spi_device *spi,24.
96、0; const u8 *txbuf, unsigned n_tx,25. u8 *rxbuf
97、, unsigned n_rx);这篇博文就到这了,下篇给出一个针对m25p10完整的驱动程序。 Linux下spi驱动开发之m25p10驱动测试目标:在华清远见的FS_S5PC100平台上编写一个简单的spi驱动模块,在probe阶段实现对m25p10的ID号探测、flash擦除、flash状态读取、flash写入、flash读取等操作。代码已经经过测试,运行于2.6.35内核。理解下面代码需要参照m25p10的芯片手册。其实下面的代码和处理器没有太大关系,这也是spi子系统的分层特点。点击(此处)折叠或打开1. #include <linux/platf
98、orm_device.h> 2. #include <linux/spi/spi.h> 3. #include <linux/init.h>4. #include <linux/module.h>5.
99、60; #include <linux/device.h>6. #include <linux/interrupt.h>7. #include <linux/mutex.h>8.
100、0;#include <linux/slab.h> / kzalloc9. #include <linux/delay.h>10.11. #define FLASH_PAGE_SIZE 25612.13. /* Flash Operating Commands */14. #define CMD_READ_ID 0x9f1
101、5. #define CMD_WRITE_ENABLE 0x06 16. #define CMD_BULK_ERASE 0xc717. #define CMD_READ_BYTES 0x0318. #def
102、ine CMD_PAGE_PROGRAM 0x0219. #define CMD_RDSR 0x05 20.21. /* Status Register bits. */22. #define SR_WIP 1 /* Write in progress */23. &
103、#160; #define SR_WEL 2 /* Write enable latch */24.25. /* ID Numbers */26. #define MANUFACTURER_ID 0x2027. #define DEVICE_ID 0x112028.29. /* Define max times t
104、o check status register before we give up. */30. #define MAX_READY_WAIT_COUNT 10000031. #define CMD_SZ 432.33. struct m25p10a 34.
105、; struct spi_device *spi;35. struct mutex lock;36. char erase_opcode
106、;37. char cmd CMD_SZ 38. 39.40. /*41. * Internal Helper functions 42.
107、 */43.44. /*45. * Read the status register, returning its value in the location46. * Return the status register value.47.
108、; * Returns negative if error occurred.48. */49. static int read_sr(struct m25p10a *flash)50. 51.
109、 ssize_t retval;52. u8 code = CMD_RDSR;53.
110、160; u8 val;54.55. retval = spi_write_then_read(flash->spi, &code, 1, &val, 1);56.57. if (retval < 0) 58.
111、 dev_err(&flash->spi->dev, "error %d reading SRn", (int) retval);59.
112、0; return retval;60. 61.62. return val;63.
113、 64.65. /*66. * Service routine to read status register until ready, or timeout occurs.67. * Returns non-zero if error.68.
114、160; */69. static int wait_till_ready(struct m25p10a *flash)70. 71.
115、160;int count;72. int sr;73.74. /* one chip guarantees max 5 msec wait here after page writes,75. &
116、#160; * but potentially three seconds (!) after page erase.76. */77.
117、160; for (count = 0; count < MAX_READY_WAIT_COUNT; count+) 78. if (sr = rea
118、d_sr(flash) < 0)79. break;80.
119、; else if (!(sr & SR_WIP)81.
120、160; return 0;82.83. /* REVISIT sometimes sleeping would be best */84. 85.
121、 printk( "in (%s): count = %dn", count );86.87. return 1;88. 89.90. /*91.
122、0; * Set write enable latch with Write Enable command.92. * Returns negative if error occurred.93. */94.
123、0; static inline int write_enable( struct m25p10a *flash )95. 96. flash->cmd0 = CMD_WRITE_ENABLE;97.
124、 return spi_write( flash->spi, flash->cmd, 1 );98. 99.100. /*101. * Erase
125、;the whole flash memory102. *103. * Returns 0 if successful, non-zero otherwise.104. */105.
126、160;static int erase_chip( struct m25p10a *flash )106. 107. /* Wait until finished previous write command. */10
127、8. if (wait_till_ready(flash)109. return -1;110.11
128、1. /* Send write enable, then erase commands. */112. write_enable( flash );113.
129、; flash->cmd0 = CMD_BULK_ERASE;114. return spi_write( flash->spi, flash->cmd, 1 );115. &
130、#160; 116.117. /*118. * Read an address range from the flash chip. The address range119. * may be any size provided it is within the physical boundaries.120.
131、0; */121. static int m25p10a_read( struct m25p10a *flash, loff_t from, size_t len, char *buf )122. 123.
132、60; int r_count = 0, i;124.125. flash->cmd0 = CMD_READ_BYTES;126.
133、0; flash->cmd1 = from >> 16;127. flash->cmd2 = from >> 8;128. &
134、#160; flash->cmd3 = from;129. 130. #if 1131. struct spi_transfer st2;132.
135、; struct spi_message msg;133. 134. spi_message_init( &msg );135.
136、 memset( st, 0, sizeof(st) );136.137. flash->cmd0 = CMD_READ_BYTES;138.
137、0; flash->cmd1 = from >> 16;139. flash->cmd2 = from >> 8;140. &
138、#160; flash->cmd3 = from;141.142. st 0 .tx_buf = flash->cmd;143. st 0 .len =
139、 CMD_SZ;144. spi_message_add_tail( &st0, &msg );145.146. st 1 .rx_buf = buf;147.
140、60; st 1 .len = len;148. spi_message_add_tail( &st1, &msg );149.150.
141、60; mutex_lock( &flash->lock );151. 152. /* Wait until finished previous write command. */153. &
142、#160; if (wait_till_ready(flash) 154. mutex_unlock( &flash->lock );155
143、. return -1;156. 157.158.
144、; spi_sync( flash->spi, &msg );159. r_count = msg.actual_length - CMD_SZ;160. &
145、#160; printk( "in (%s): read %d bytesn", _func_, r_count );161. for( i = 0; i < r_count; i+ ) 162.
146、0; printk( "0x%02xn", buf i );163.
147、160;164.165. mutex_unlock( &flash->lock );166. #endif167.168. return 0;169. 170.171. /*172.
148、 * Write an address range to the flash chip. Data must be written in173. * FLASH_PAGE_SIZE chunks. The address range may be any size provided174.
149、; * it is within the physical boundaries.175. */176. static int m25p10a_write( struct m25p10a *flash, loff_t to, size_t len,
150、160;const char *buf )177. 178. int w_count = 0, i, page_offset;179. struct spi_transfer st2;180.
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 2025四川科瑞软件有限责任公司招聘商务专员测试笔试历年典型考点题库附带答案详解
- 2025四川宜宾钲兴智造科技有限公司第一批项目制员工招聘4人笔试历年备考题库附带答案详解
- 2024-2025学年反射疗法师大赛理论综合提升测试卷附完整答案详解【名校卷】
- 2024-2025学年度电梯考试复习提分资料附答案详解(轻巧夺冠)
- 2024-2025学年临床执业医师试题预测试卷含答案详解【基础题】
- 2024-2025学年度临床执业医师综合提升测试卷附完整答案详解(有一套)
- 2026新疆图木舒克市前海农业开发有限责任公司招聘笔试备考题库及答案解析
- 2026广东江门市新会美丽乡村投资开发有限公司招聘1人考试备考题库及答案解析
- 2024-2025学年度反射疗法师大赛理论经典例题及参考答案详解(达标题)
- 2026广西玉林市兴业县残疾人联合会招聘2人笔试备考题库及答案解析
- 江苏省2025年接受高级访问学者的高等学校
- 村民自治课件
- 2024注册核安全工程师考试历年机考真题集附完整答案详解
- gmp规范培训课件
- 腰椎术后伤口感染管理要点
- 狱内案件立案表宁夏警官职业应用法律系87课件
- -世界水日主题班会课件
- 2022公共图书馆服务外包要求
- 2025新人教版七年级下册英语 Unit 6知识点梳理及语法讲义(答案版)
- 考古调查勘探辅助工程方案投标文件(技术方案)
- 补办离婚委托书范本
评论
0/150
提交评论