需要了解Linux下SPI从设备驱动的编写

spi(serial peripheral interface)是一个同步的四线制串行线,用于连接微控制器和传感器、存储器及外围设备。三条信号线持有时钟信号(sclk,经常在10mhz左右)和并行数据线带有“主出,从进(mosi)”或是“主进,从出(miso)”信号。数据交换的时候有四种时钟模式,模式0和模式3是最经常使用的。每个时钟周期将会传递数据进和出。如果没有数据传递的话,时钟将不会循环。
spi驱动分为两类:
控制器驱动:它们通常内嵌于片上系统处理器,通常既支持主设备,又支持从设备。这些驱动涉及硬件寄存器,可能使用dma。或它们使用gpio引脚成为pio bitbangers。这部分通常会由特定的开发板提供商提供,不用自己写。
协议驱动:它们通过控制器驱动,以spi连接的方式在主从设备之间传递信息。这部分涉及具体的spi从设备,通常需要自己编写。
那么特定的目标板如何让linux操控spi设备?下面以at91sam9260系列can设备驱动为例,linux内核版本为2.6.19。本文不涉及控制器驱动分析。
board_info提供足够的信息使得系统正常工作而不需要芯片驱动加载
[cpp]view plaincopy
在arch/arm/mach-at91rm9200/board-at91sam9260.c中有如下代码:
#include
#include
…….
staticstructspi_board_infoek_spi_devices[]={
/*spican,addbymrz*/
#ifdefined(config_can_mcp2515)
{
.modalias=mcp2515,
.chip_select=0,
//.controller_data=at91_pin_pb3,
.irq=at91_pin_pc6,//at91sam9260_id_irq0,
.platform_data=&mcp251x_data,
.max_speed_hz=10*1000*1000,
.bus_num=1,
.mode=0,
}
#endif
};.
………
staticvoid__initek_board_init(void)
{
……
/*spi*/
at91_add_device_spi(ek_spi_devices,array_size(ek_spi_devices));
}
这样在linux初始化时候就可以加载spi can驱动。
下面来看mcp2515 can驱动的结构,协议驱动有点类似平台设备驱动,本文只列出框架,不涉及具体实现代码,在/driver/can/mcp2515.c中:
[c-sharp]view plaincopy
staticstructspi_drivermcp251x_driver={
.driver={
.name=mcp2515,
.bus=&spi_bus_type,
.owner=this_module,
},
.probe=mcp251x_probe,
.remove=__devexit_p(mcp251x_remove),
#ifdefconfig_pm
.suspend=mcp251x_suspend,
.resume=mcp251x_resume,
#endif
};
驱动将自动试图绑定驱动到任何board_info给定别名为mcp2515的spi设备。
staticint__devinitmcp251x_probe(structspi_device*spi)
{
structmcp251x*chip;
intret=0;
dev_dbg(&spi->dev,%s:start/n,__function__);
chip=kmalloc(sizeof(structmcp251x),gfp_kernel);
if(!chip){
ret=-enomem;
gotoerror_alloc;
}
dev_set_drvdata(&spi->dev,chip);
chip->txbin=chip->txbout=0;
chip->rxbin=chip->rxbout=0;
chip->count=0;
chip->spi=spi;
init_mutex(&chip->lock);
init_mutex(&chip->txblock);
init_mutex(&chip->rxblock);
init_waitqueue_head(&chip->wq);
#if(linux_version_code>=kernel_version(2,6,20))
init_work(&chip->irq_work,mcp251x_irq_handler);
#else
init_work(&chip->irq_work,mcp251x_irq_handler,spi);
#endif
chip->spi_transfer_buf=kmalloc(spi_transfer_buf_len,gfp_kernel);
if(!chip->spi_transfer_buf){
ret=-enomem;
gotoerror_buf;
}
ret=request_irq(spi->irq,mcp251x_irq,sa_sample_random,driver_name,spi);
if(retdev,requestirq%dfailed(ret=%d)/n,spi->irq,ret);
gotoerror_irq;
}
cdev_init(&chip->cdev,&mcp251x_fops);
chip->cdev.owner=this_module;
ret=cdev_add(&chip->cdev,mkdev(major(can_devt),can_minor),1);
if(retdev,registerchardevicefailed(ret=%d)/n,ret);
gotoerror_register;
}
chip->class_dev=class_device_create(can_class,null,
mkdev(major(can_devt),can_minor),
&spi->dev,can%d,can_minor);
if(is_err(chip->class_dev)){
dev_err(&spi->dev,cannotcreatecanclassdevice/n);
ret=ptr_err(chip->class_dev);
gotoerror_class_reg;
}
dev_info(&spi->dev,deviceregisteratdev(%d:%d)/n,
major(can_devt),can_minor);
mcp251x_hw_init(spi);
mcp251x_set_bit_rate(spi,125000);/*areasonabledefault*/
mcp251x_hw_sleep(spi);
can_minor++;
return0;
error_class_reg:
cdev_del(&chip->cdev);
error_register:
free_irq(spi->irq,spi);
error_irq:
kfree(chip->spi_transfer_buf);
error_buf:
kfree(chip);
error_alloc:
returnret;
}
一旦进入probe(),驱动使用struct spi_message向spi设备要求i/o。当remove()返回时,驱动保证将不会提交任何这种信息。
一个spi_message是协议操作序列,以一个原子序列执行。spi驱动控制包括:
(1)当双向读写开始,是根据spi_transfer要求序列是怎样安排的。
(2)随意设定传递后的短延时,使用spi_transfer.delay_usecs设定。
(3)在一次传递和任何延时之后,无论片选是否活跃,使用spi_transfer.cs_change标志,暗示下条信息是否进入这个同样的设备,使用原子组中最后一次传输上的spi_transfer.cs_change标志位,可能节省芯片选择取消操作的成本。

丰田研发自动驾驶燃料电池月球车 进军太空指日可待
东芝芯片业务超预期,第二财季运营利润增长76%
AMD处理器市场重回巅峰
小米推出微控制器AI推理引擎MACE Micro
STM32CUBEMX(10)--Flash读写
需要了解Linux下SPI从设备驱动的编写
什么是机器学习问题 普适逼近定理介绍
关于PCB的三种分类方法
注册资本5000万 长城汽车拟与魏建军成立合资芯片公司芯动半导体
欧盟首台百亿亿次级超级计算机JUPITER建设成果显著,进入新阶段
大事件:昕诺飞再宣布照明产品涨价3%-9%
无线应用协议(WAP)工作原理
不断增长的存储需求推动更多NAND进入汽车设计
使用单片机和计数电路设计一个数字频率计的资料和代码概述
Intel的TVB技术是什么
FLIR红外热像仪积极维护修复古建筑项目
一种新颖的大功率LED驱动电路
阿尔法蛋2019三款新品发布 重新定义了AI学习机器人
功率型LED封装技术的性能要求分析
一文详解DCDC环路补偿