Linux下spi驱动开发

上传人:daj****de 文档编号:148921896 上传时间:2022-09-06 格式:DOCX 页数:20 大小:163.40KB
返回 下载 相关 举报
Linux下spi驱动开发_第1页
第1页 / 共20页
Linux下spi驱动开发_第2页
第2页 / 共20页
Linux下spi驱动开发_第3页
第3页 / 共20页
点击查看更多>>
资源描述
Linux下spi驱动开发一、概述基于子系统去开发驱动程序已经是linux内核中普遍的做法了。前面写过基于I2C子系统的 驱动开发。本文介绍另外一种常用总线SPI的开发方法。SPI子系统的开发和I2C有很多的 相似性,大家可以对比学习。本主题分为两个部分叙述,第一部分介绍基于SPI子系统开发 的理论框架;第二部分以华清远见教学平台FS_S5PC100上的M25P10芯片为例(内核版本 2.6.29),编写一个SPI驱动程序实例。、SPI总线协议简介介绍驱动开发前,需要先熟悉下SPI通讯协议中的几个关键的地方,后面在编写驱动时,需 要考虑相关因素。SPI总线由MISO (串行数据输入)、MOSI(串行数据输出)、SCK(串行移位时钟)、CS(使能信号)4个信号线组成。如FS_S5PC100上的M25P10芯片接线为:VDDJQSPI FlashSPIO CSnSPKJ31IS0cm t R1730.1 uF)SPIO_CLKPIO_FOSI上图中M25P10的D脚为它的数据输入脚,Q为数据输出脚,C为时钟脚。SPI常用四种数据传输模式,主要差别在于:输出串行同步时钟极性(CPOL)和相位(CPHA) 可以进行配置。如果CPOL= 0,串行同步时钟的空闲状态为低电平;如果CPOL= 1,串行同 步时钟的空闲状态为高电平。如果CPHA=0,在串行同步时钟的前沿(上升或下降)数据被 采样;如果CPHA = 1,在串行同步时钟的后沿(上升或下降)数据被采样。CPOL = 0, CPHA = 0 (Format A)Cycle|l|2|3|45678|spiclkr ri ij rirjirimosi 座 misoMSB : MSB of previous frameCPOL = 0, CPHA = I (Format B)CycleSPICLKMOSI123458 4rm_ruJ 4(mCDn_/1Jnn_.Ms。LS8*: LSB of next frameCPCL = L CPHA = 0 (Format A)Cycle1234567 W |I-BIHIjIISPICLK rlllllllllmosiI khJ fe t = = * cfctdciztetetztzifc)CycEeSPICLKMOS】MISOCROC CPHA0G C11CDQ这四种模式中究竟选择哪种模式取决于设备。如M25P10的手册中明确它可以支持的两种模 式为:CPOL=0 CPHA=0 和 CPOL=1 CPHA=1一._n_rr_n.厂 _m)&p=c:);咿X xAC 14330三、linux下SPI驱动开发首先明确SPI驱动层次,如下图:SPI总线戚理解为SPI控制器弓|由的总线我们以上面的这个图为思路1、Platform busPlatform bus对应的结构是platform_bus_type,这个内核开始就定义好的。我们不需要定 义。2、Platform_deviceSPI控制器对应platform_device的定义方式,同样以S5PC100中的SPI控制器为例,参看 arch/arm/plat-s5pc1xx/dev-spi.c 文件点击(此处)折叠或打开struct platform_device s3c_device_spi0 = 匹配,name = s3c64xx-spi , /名称,要和 Platform_driver3.4.id = 0, 第0个控制器,S5PC100中有3个控制器5.6.资源的种类,num_resources = ARRAY_SIZE(s5pc1xx_spi0_resource ), /占用.resource = s5pc1xx_spi0_resource , /指向资源结构数组的指针 .dev = 7.8.,dma_mask = &spi_dmamask, /dma 寻址范围 .coherent_dma_mask = DMA_BIT_MASK (32), /可以通过关 闭cache等措施保证一致性的dma寻址范围9.据,参看后文10.11.12.13-static14.15.16.17.18.19-static20.21.22.23.MISO0);24.CLK0);25.MOSI0);26.P);27.P);28.P);29.30.platform_data = &s5pc1xx_spi0_pdata , /特殊的平台数,;struct s3c64xx_spi_cntrlr_info s5pc1xx_spi0_pdata = .cfg_gpio = s5pc1xx_spi_cfg_gpio , /用于控制器管脚的 IO 配置 .fifo_lvl_mask = 0x7f,.rx_lvl_offset = 13,;int s5pc1xx_spi_cfg_gpio (struct platform_device *pdev)switch (pdev-id) case 0:s3c_gpio_cfgpin (S5PC1XX_GPB (0), S5PC1XX_GPB0_SPI_s3c_gpio_cfgpin (S5PC1XX_GPB (1), S5PC1XX_GPB1_SPI_s3c_gpio_cfgpin (S5PC1XX_GPB (2), S5PC1XX_GPB2_SPI_s3c_gpio_setpull (S5PC1XX_GPB (0), S3C_GPIO_PULL_Us3c_gpio_setpull (S5PC1XX_GPB (1), S3C_GPIO_PULL_Us3c_gpio_setpull (S5PC1XX_GPB(2), S3C_GPIO_PULL_U31.32.case 1:s3c_gpio_cfgpin(S5PC1XX_GPB(4),S5PC1XXGPB4SPIbreak ;MISO1);33.s3c_gpio_cfgpin(S5PC1XX_GPB(5),S5PC1XXGPB5SPICLK1);34.s3c_gpio_cfgpin(S5PC1XX_GPB(6),S5PC1XXGPB6SPI35.MOSI1);s3c_gpio_setpull (S5PC1XX_GPB (4), S3C_GPIO_PULL_UP);36.s3c_gpio_setpull (S5PC1XX_GPB (5), S3C_GPIO_PULL_UP);37.P);38.39.4.case 2:41.PI_CLK2);42.PI_MISO2);43.PI_MOSI2);44.P);45.P);46.P);47.48.49-default:50.r!);51.52.3、 Platform_drivers3c_gpio_setpull (S5PC1XX_GPB(6), S3C_GPIO_PULL_U break ;s3c_gpio_cfgpin (S5PC1XX_GPG3(0), S5PC1XX_GPG3_0_S s3c_gpio_cfgpin (S5PC1XX_GPG3 (2), S5PC1XX_GPG3_2_S s3c_gpio_cfgpin (S5PC1XX_GPG3 (3), S5PC1XX_GPG3_3_S s3c_gpio_setpull (S5PC1XX_GPG3 (0), S3C_GPIO_PULL_U s3c_gpio_setpull (S5PC1XX_GPG3 (2), S3C_GPIO_PULL_U s3c_gpio_setpull (S5PC1XX_GPG3 (3), S3C_GPIO_PULL_U break ;dev_err (&pdev-dev, Invalid SPI Controller numbe return -EINVAL;再看 platform_driver,参看 drivers/spi/spi_s3c64xx.c 文件点击(此处)折叠或打开1.2.3.static struct platform_driver s3c64xx_spi_driver = ,driver = 4.5.6.7.8.9. ;10.owner = THIS_MODULE,remove = s3c64xx_spi_remove ,.suspend = s3c64xx_spi_suspend.resume = s3c64xx_spi_resume ,.name = s3c64xx-spi , /名称,和 platform device对应11.platform_driver_probe (&s3c64xx_spi_driver , s3c64xx_spi_probe ) ; /注册 s3c64xx_spi_driver和平台中注册的platform_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.2.3.4.5.6.7.8.9.struct spi_master struct device dev ;s16 bus_num ; /表示是SPI主机控制器的编号。由平台代码 决定u16 num_chipselect ; 控制器支持的片选数量,即能支持多少个spi 设备int (*setup)(struct spi_device *spi); /针对设备设置 SPI 的工 作时钟及数据传输模式等。在spi_add_device函数中调用。int (*transfer)(struct spi_device *spi, struct spi_message *mesg); /实现数据的双向传输,可 能会睡眠void (*cleanup)(struct spi_device *spi); 注销时调用 ;4、Spi busSpi总线对应的总线类型为spi_bus_type,在内核的drivers/spi/spi.c中定义点击(此处)折叠或打开1.2.3.4.5.6.7.8.struct bus_type spi_bus_type = .name = spi, .dev_attrs = spi_dev_attrs, ,match = spi_match_device, ,uevent = spi_uevent, .suspend = spi_suspend , ,resume = spi_resume,;对应的匹配规则是(高版本中的匹配规则会稍有变化,引入了 id_table,可以匹配多个spi 设备名称):点击(此处)折叠或打开1.2.3.static int spi_match_device(struct device *dev, struct device_driver *drv) v);const struct spi_device *spi = to_spi_device(de4.5.return strcmp (spi-modalias, drv-name) = 0;5、spi_device卜面该讲到spi_device的构建与注册了。spi_device对应的含义是挂接在spi总线上的一个设备,所以描述它的时候应该明确它自身的设备特性、传输要求、及挂接在哪个总线上。点击(此处)折叠或打开1.static struct spi_board_info s3c_spi_devs _initdata = 2.3.4.5.6.7.8.选择具体数据传输模式没有使用它作为片选的依据, ,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里的方法。9.10.11.12.; static.controller_data = &smdk_spi0_csi0,struct s3c64xx_spi_csinfo smdk_spi0_csi = 13.14.15.16.17.18.; static0 = .set_level = smdk_m25p10_cs_set_level , .fb_delay = 0x3,void smdk_m25p10_cs_set_level (int high) /spi 控制19.20.21.22.23.24.25.26.27.28.器会用这个方法设置csu32 val ;val = readl(S5PC1XX_GPBDAT );if (high)val |= (13);elseval &= (1board_info;7.unsigned n ;8.for (n = bi-n_board_info; n 0; n-, chip+) 9.if (chip-bus_num != master-bus_num)10.continue ;11./* NOTE: this relies on spi_new_device to12.* issue diagnostics when given bogus inputs13.*/14.(void) spi_new_device(master, chip); /创建并注册了 spi_device15.16.17.mutex_unlock (&board_lock);18.6、spi_driver本文先以linux内核中的/driver/mtd/devices/m25p80.c驱动为参考。点击(此处)折叠或打开1.2.3.4.5.static struct spi_driver m25p80_driver = /spi_driver 的构建 ,driver = .name = m25p80,.bus = &spi_bus_type ,.owner = THIS_MODULE,6.7.8.9.10.11.12.13.14.15.,.probe = m25p_probe,remove = _devexit_p(m25p_remove),*/;spi_register_driver (&m25p80_driver );/spi driver 的注册在有匹配的spi device时,会调用m25p_probe16.17.18.19.static int _devinit m25p_probe (struct spi_device *spi) 根据传入的spi_device参数,可以找到对应的spi_master。接下来就可以利用spi子系统 为我们完成数据交互了。可以参看m25p80_read函数。要完成传输,先理解下面几个结构的 含义:(这两个结构的定义及详细注释参见include/linux/spi/spi.h)spi_message:描述一次完整的传输,即cs信号从高-底-高的传输spi_transfer:多个 spi_transfer 够成一个 spi_message举例说明:m25p80的读过程如下图Q 1 2 3 4 5 6 7 S 9 109 30 31 32 33 S4 3& 36 37 38 29 Instruction 24-bit address M zzx. Data Out 1Data Oot 2口MSB可以分解为两个spi_ transfer 一个是写命令,另一个是读数据。具体实现参见m25p80.c 中的m25p80_read函数。下面内容摘取之此函数。点击(此处)折叠或打开1.2.3.struct spi_transfer t 2; /定义了两个 spi_transferstruct spi_message m ; /定义了两个 spi_message spi_message_init (&m); /初始化其 transfers 链表4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.t0.tx_buf = flash-command;t 0.len = CMD_SIZE + FAST_READ_DUMMY_BYTE ; 定义第一个 t ransfer的写指针和长度spi_message_add_tail (&t0, &m); /添加到U spi_messaget 1.rx_buf = buf;t 1. len = len; /定义第二个transfer的读指针和长度spi_message_add_tail (&t1, &m); /添加到U spi_message=OPCODE_READ;=from 16;=from 8;from; /初始化前面写buf的内容flash -command 0flash -command 1 flash -command 2 flash -command3=spi_sync(flash-spi, &m); /调用 spi_master 发送 spi_message/ spi_sync为同步方式发送,还可以用spi_async异步方式,那样的话,需要设置回调完成函数。20.21.另外你也可以选择一些封装好的更容易使用的函数,这些函数可以在include/linux/spi/sp i.h文件中找到,如:22.23.24.25.extern int spi_write_then_read (struct spi_device *spi,const u8 *txbuf, unsigned n_tx,u8 *rxbuf, unsigned n_rx);这篇博文就到这了,下篇给出一个针对m25p10完整的驱动程序。Linux下spi驱动开发之m25p10驱动测试目标:在华清远见的FS_S5PC100平台上编写一个简单的spi驱动模块,在probe阶 段实现对m25p10的ID号探测、flash擦除、flash状态读取、flash写入、flash读取等操 作。代码已经经过测试,运行于2.6.35内核。理解下面代码需要参照m25p10的芯片手册。 其实下面的代码和处理器没有太大关系,这也是spi子系统的分层特点。点击(此处)折叠或打开1.#include 2.#include3.#include4.#include5.#include6.#include7.8.9.10.11.12.13.14.15.16.17.18.19.20.21.22.23.24.25.26.27.28.29.30.31.32.33.34.35.36.37.38.39.40.41.42.43.44.45.46.47.48.49.50.#define CMD_RDSR 0x05/* Status Register bits . */#define SR_WIP 1 /* Write in progress */#define SR_WEL 2 /* Write enable latch */*/location#include #include / kzalloc#include #define FLASH_PAGE_SIZE 256/* Flash Operating Commands */#define CMD_READ_ID 0x9f#define CMD_WRITE_ENABLE 0x06#define CMD_BULK_ERASE 0xc7#define CMD_READ_BYTES 0x03#define CMD_PAGE_PROGRAM 0x02/* ID Numbers */#define MANUFACTURER_ID 0x20 #define DEVICE_ID 0x1120/* Define max times to check status register before we give up #define MAX_READY_WAIT_COUNT 100000 #define CMD_SZ 4struct m25p10a struct spi_device *spi;struct mutex lock ;char erase_opcode ;char cmd CMD_SZ ;/* Internal Helper functions*/* Read the status register , returning its value in the* Return the status register value .* Returns negative if error occurred .*/static int read_sr(struct m25p10a *flash) 51.52.ssize_t retval ;u8 code = CMD_RDSR;53.u8 val ;54.55.retval = spi_write_then_read (flash-spi, &code, 1, &val, 1);56.57.if (retval spi-dev, error %d reading SRn, (int)retval);59.return retval ;60.61.62.return val;63.64.65./*66.* Service routine to read status register until ready, or timeoutccurs.67.* Returns non-zero if error.68.*/69.static int wait_till_ready (struct m25p10a *flash)70.71.int count;72.int sr;73.74./* one chip guarantees max 5 msec wait here after page writes ,75.* but potentially three seconds (!) after page erase.76.*/77.for (count = 0; count MAX_READY_WAIT_COUNT ; count+) 78.if (sr = read_sr(flash) cmd0 = CMD_WRITE_ENABLE ;return spi_write ( flash-spi, flash-cmd, 1 );* Erase the whole flash memory * Returns 0 if successful, non-zero otherwise .*/static int erase_chip( struct m25p10a *flash )/* Wait until finished previous write command . */if (wait_till_ready(flash)return -1;/* Send write enable , then erase commands. */ write_enable ( flash );flash -cmd0 = CMD_BULK_ERASE ;return spi_write ( flash-spi, flash-cmd, 1 );/* Read an address range from the flash chip . The address range* may be any size provided it is within the physical boundaries .*/static int m25p10a_read ( struct m25p10a *flash, loff_t from, size char *buf )int r_count = 0, i;flash-cmd0 = CMD_READ_BYTES;flash -cmd1 = from 16;flash-cmd2 = from 8;flash-cmd3 = from;#if 1struct spi_transfer st 2;struct spi_message msg ;spi_message_init ( &msg );memset ( st, 0, sizeof(st);136.137.138.139.140.141.flash-cmd0 = CMD_READ_BYTES;flash -cmd1 = from 16;flash-cmd2 = from 8;flash-cmd3 = from;142.143.144.145.st 0 .tx_buf = flash-cmd;st 0 .len = CMD_SZ;spi_message_add_tail ( &st0, &msg );146.147.148.149.st 1 .rx_buf = buf;st 1 .len = len;spi_message_add_tail ( &st1, &msg );150.151.mutex_lock ( &flash-lock );152.153.154.155.156.157./* Wait until finished previous write command if (wait_till_ready(flash) mutex_unlock ( &flash-lock );return -1;*/158.159.160.161.162.163.164.165.166.167.168.169.170.spi_sync ( flash-spi, &msg );r_count = msg.actual_length - CMD_SZ;printk ( in (%s): read %d bytesn , _funcfor( i = 0; i lock );#endifreturn 0 ;r_count );171.172.n173.ed174.175.176./* Write an address range to the flash chip . Data must* FLASH_PAGE_SIZE chunks . The address range may be any* it is within the physical boundaries .*/static int m25p10a_write ( struct m25p10a *flash, loff_be written isize providt to, sizet len, const char *buf )177.178.179.180.181.182.183.184.185.186.187.188.189.190.191.192.193.194.195.196.197.198.199.200.201.202.203.204.205.206.207.208.209.210.211.212.213.0.tx_buf);int w_count = 0, i, page_offset;struct spi_transfer st 2;struct spi_message msg ;#if 1if (wait_till_ready (flash) /读状态,等待 ready mutex_unlock ( &flash-lock );return -1;#endifwrite_enable ( flash ); /写使能spi_message_init ( &msg );memset ( st, 0, sizeof(st);flash-cmd0 = CMD_PAGE_PROGRAM ;flash -cmd1 = to 16;flash-cmd2 = to 8;flash -cmd3 = to;st 0 .tx_buf = flash-cmd;st 0 .len = CMD_SZ;spi_message_add_tail ( &st0, &msg );st 1 .tx_buf = buf;st 1 .len = len;spi_message_add_tail ( &st1, &msg );mutex_lock ( &flash-lock );/* get offset address inside a page */page_offset = to % FLASH_PAGE_SIZE ;/* do all the bytes fit onto one page ? */if( page_offset + len spi, &msg );w_count = msg.actual_length - CMD_SZ;220.221.222.unc223.224.225.226.227.228.229.230.231.232.233.234.235.236.237.238.239.240.241.242.243.244.245.246.247.248.249.250.251.252.253.254.255.256.257.258.259.260.261.262.else / noprintk ( in (%s): write %d bytes to flash in totaln” , _f w_count );mutex_unlock ( &flash-lock );return 0 ;static int check_id( struct m25p10a *flash )char buf 10 = 0;flash -cmd0 = CMD_READ_ID;spi_write_then_read ( flash-spi, flash-cmd, 1, buf, 3 );printk ( Manufacture ID: 0x%xn , buf0);printk ( Device ID: 0x%xn , buf1 | buf2 8 );return buf 2 16 | buf1 spi = spi;mutex_init ( &flash-lock );/* save flash as driver s private data */ spi_set_drvdata ( spi, flash );check_id ( flash ); /读取 ID#if 1ret = erase_chip ( flash ); /擦除if( ret 0 ) printk ( erase the entirely chip failedn );printk ( erase the whole chip donen );memset ( buf, 0x7, 256 );m25p10a_write ( flash, 0, 20, buf); /0 地址写入 20 个 7memset ( buf, 0, 256 );m25p10a_read ( flash, 0, 25, buf ); /0 地址读出 25 个数263.#endif264.return 0 ;265.266.267.static int m25p10a_remove (struct spi_device *spi)268.269.return 0 ;270.271.272.static struct spi_driver m25p10a_driver = 273.probe = m25p10a_probe,274.remove = m25p10a_remove,275.,driver = 276.name = m25p10a,277.,278.279.;280.static int _init m25p10a_init (void)281.282.return spi_register_driver (&m25p10a_driver)283.284.285.static void _exit m25p10a_exit (void)286.287.spi_unregister_driver (&m25p10a_driver);288.289.290.module_init(m25p10a_init);291.292.module_exit (m25p10a_exit);293.294.MODULE_DESCRIPTION(m25p10a driver for FS_S5PC100);295.MODULE_LICENSE(GPL);
展开阅读全文
相关资源
相关搜索

最新文档


当前位置:首页 > 图纸设计 > 毕设全套


copyright@ 2023-2025  zhuangpeitu.com 装配图网版权所有   联系电话:18123376007

备案号:ICP2024067431-1 川公网安备51140202000466号


本站为文档C2C交易模式,即用户上传的文档直接被用户下载,本站只是中间服务平台,本站所有文档下载所得的收益归上传人(含作者)所有。装配图网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对上载内容本身不做任何修改或编辑。若文档所含内容侵犯了您的版权或隐私,请立即通知装配图网,我们立即给予删除!