linux SPI源码剖析
来源:互联网 发布:战龙三国黄忠进阶数据 编辑:程序博客网 时间:2024/05/05 01:10
1. SPI
1.1. Linux框架
Spi总线是主从控制方式,总体结构如下:
1.1.1. Spi bus
<drivers/spi/spi.c>
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,
};
/*总线match方法:name匹配方法*/
static int spi_match_device(struct device*dev, struct device_driver *drv)
{
conststruct spi_device *spi =to_spi_device(dev);
conststruct spi_driver *sdrv =to_spi_driver(drv);
if(sdrv->id_table)/*根据驱动中定义一组name,匹配*/
return!!spi_match_id(sdrv->id_table, spi);
returnstrcmp(spi->modalias, drv->name) == 0;/*根据别名进行匹配*/
}
1.1.2. Spi驱动注册
<drivers/spi/spi.c>
int spi_register_driver(struct spi_driver*sdrv)
{
sdrv->driver.bus= &spi_bus_type;
if(sdrv->probe)
sdrv->driver.probe= spi_drv_probe;
if(sdrv->remove)
sdrv->driver.remove= spi_drv_remove;
if(sdrv->shutdown)
sdrv->driver.shutdown= spi_drv_shutdown;
returndriver_register(&sdrv->driver);
}
/*驱动probe方法*/
static int spi_drv_probe(struct device *dev)
{
conststruct spi_driver *sdrv =to_spi_driver(dev->driver);
returnsdrv->probe(to_spi_device(dev));//直接调用平台probe方法
}
1.1.3. Master 控制器注册
<linux/spi/spi.h>
struct spi_master {
structdevice dev;
s16 bus_num;/*控制器序号*/
u16 num_chipselect;/*片选数量*/
u16 dma_alignment;
/*spi_device.mode flags understood by this controller driver */
u16 mode_bits;
/*other constraints relevant to this driver */
u16 flags;
#define SPI_MASTER_HALF_DUPLEX BIT(0) /*can't do full duplex */
#define SPI_MASTER_NO_RX BIT(1) /*can't do buffer read */
#define SPI_MASTER_NO_TX BIT(2) /*can't do buffer write */
/*安装spi设备,配置如mode,clock等*/
int (*setup)(structspi_device *spi);
/*双向传输*/
int (*transfer)(structspi_device *spi,
structspi_message *mesg);
/*called on release() to free memory provided by spi_master */
void (*cleanup)(struct spi_device*spi);
};
/*master注册*/
int spi_register_master(struct spi_master*master)
{
staticatomic_t dyn_bus_id =ATOMIC_INIT((1<<15) - 1);
structdevice *dev =master->dev.parent;
int status = -ENODEV;
int dynamic = 0;
if(!dev)
return-ENODEV;
if(master->num_chipselect == 0)//至少有一个片选
return-EINVAL;
/*convention: dynamically assigned bus IDscount down from the max */
if(master->bus_num < 0) {
/*FIXME switch to an IDR based scheme, something like
* I2C now uses, so we can't run out of"dynamic" IDs
*/
master->bus_num= atomic_dec_return(&dyn_bus_id);
dynamic= 1;
}
/*mater的名字为:spi控制器号*/
dev_set_name(&master->dev,"spi%u", master->bus_num);
status= device_add(&master->dev);
if(status < 0)
gotodone;
dev_dbg(dev,"registered master %s%s\n", dev_name(&master->dev),
dynamic? " (dynamic)" : "");
/*populate children from any spi device tables */
scan_boardinfo(master);/*遍历控制器下的spi设备*/
status= 0;
done:
returnstatus;
}
static void scan_boardinfo(structspi_master *master)
{
structboardinfo *bi;
mutex_lock(&board_lock);
list_for_each_entry(bi,&board_list,list) {/*spi_register_board_info中入队列*/
structspi_board_info *chip =bi->board_info;
unsigned n;
for(n = bi->n_board_info; n > 0; n--, chip++) {
if(chip->bus_num != master->bus_num)/*匹配条件为控制器编号相同*/
continue;
/*NOTE: this relies on spi_new_device to
* issue diagnostics when given bogus inputs
*/
(void)spi_new_device(master, chip);
}
}
mutex_unlock(&board_lock);
}
1.1.4. Spi device注册
设备注册时靠在board文件中定义spi_board_info来实现的
struct spi_board_info {//代表spi设备信息
/*the device name and module name are coupled, like platform_bus;
* "modalias" is normally the drivername.
*
* platform_data goes tospi_device.dev.platform_data,
* controller_data goes tospi_device.controller_data,
* irq is copied too
*/
char modalias[SPI_NAME_SIZE];
constvoid *platform_data;
void *controller_data;
int irq;
/*slower signaling on noisy or low voltage boards */
u32 max_speed_hz;
/*bus_num is board specific and matches the bus_num of some
* spi_master that will probably be registeredlater.
*
* chip_select reflects how this chip is wiredto that master;
* it's less than num_chipselect.
*/
u16 bus_num;
u16 chip_select;
/*mode becomes spi_device.mode, and is essential for chips
* where the default of SPI_CS_HIGH = 0 iswrong.
*/
u8 mode;
/*... may need additional spi_device chip config data here.
* avoid stuff protocol drivers can set; butinclude stuff
* needed to behave without being bound to adriver:
* -quirks like clock rate mattering when not selected
*/
};
/*注册board信息*/
int __init spi_register_board_info(structspi_board_info const *info, unsigned n)
{
structboardinfo *bi;
bi= kmalloc(sizeof(*bi) + n * sizeof *info, GFP_KERNEL);
if(!bi)
return-ENOMEM;
bi->n_board_info= n;
memcpy(bi->board_info,info, n * sizeof *info);
mutex_lock(&board_lock);
list_add_tail(&bi->list,&board_list);//加入到此列表中,等待master注册时遍历
mutex_unlock(&board_lock);
return0;
}
struct spi_device {
structdevice dev;
structspi_master *master;/*所属控制器*/
u32 max_speed_hz;
u8 chip_select;
u8 mode;
#define SPI_CPHA 0x01 /*clock phase */
#define SPI_CPOL 0x02 /*clock polarity */
#define SPI_MODE_0 (0|0) /*(original MicroWire) */
#define SPI_MODE_1 (0|SPI_CPHA)
#define SPI_MODE_2 (SPI_CPOL|0)
#define SPI_MODE_3 (SPI_CPOL|SPI_CPHA)
#define SPI_CS_HIGH 0x04 /*chipselect active high? */
#define SPI_LSB_FIRST 0x08 /*per-word bits-on-wire */
#define SPI_3WIRE 0x10 /*SI/SO signals shared */
#define SPI_LOOP 0x20 /*loopback mode */
#define SPI_NO_CS 0x40 /*1 dev/bus, no chipselect */
#define SPI_READY 0x80 /*slave pulls low to pause */
u8 bits_per_word;
int irq;
void *controller_state;
void *controller_data;
char modalias[SPI_NAME_SIZE];/*别名*/
/*
* likely need more hooks for more protocoloptions affecting how
* the controller talks to each chip, like:
* -memory packing (12 bit samples into low bits, others zeroed)
* -priority
* -drop chipselect after each word
* -chipselect delays
* - ...
*/
};
/*添加新的spi设备到系统中*/
struct spi_device *spi_new_device(structspi_master *master,
struct spi_board_info *chip)
{
structspi_device *proxy;
int status;
/*NOTE: caller did any chip->bus_numchecks necessary.
*
* Also, unless we change the return valueconvention to use
* error-or-pointer (not NULL-or-pointer),troubleshootability
* suggests syslogged diagnostics are best here(ugh).
*/
proxy= spi_alloc_device(master);//组建spi_device
if(!proxy)
returnNULL;
WARN_ON(strlen(chip->modalias)>= sizeof(proxy->modalias));
proxy->chip_select= chip->chip_select;
proxy->max_speed_hz= chip->max_speed_hz;
proxy->mode= chip->mode;
proxy->irq= chip->irq;
strlcpy(proxy->modalias,chip->modalias, sizeof(proxy->modalias));
proxy->dev.platform_data= (void *) chip->platform_data;
proxy->controller_data= chip->controller_data;
proxy->controller_state= NULL;
status= spi_add_device(proxy);
if(status < 0) {
spi_dev_put(proxy);
returnNULL;
}
returnproxy;
}
/*安装及初始化spi设备*/
int spi_add_device(struct spi_device *spi)
{
staticDEFINE_MUTEX(spi_add_lock);
structdevice *dev = spi->master->dev.parent;
structdevice *d;
intstatus;
/*Chipselects are numbered 0..max; validate. */
if(spi->chip_select >= spi->master->num_chipselect) {
dev_err(dev,"cs%d >= max %d\n",
spi->chip_select,
spi->master->num_chipselect);
return-EINVAL;
}
/*Set the bus ID string */
dev_set_name(&spi->dev,"%s.%u", dev_name(&spi->master->dev),
spi->chip_select);
/*We need to make sure there's no other device with this
* chipselect **BEFORE** we call setup(), elsewe'll trash
* its configuration. Lock against concurrent add() calls.
*/
mutex_lock(&spi_add_lock);
/*根据spi设备的名字,从总线上找驱动*/
d= bus_find_device_by_name(&spi_bus_type, NULL, dev_name(&spi->dev));
if(d != NULL) {
dev_err(dev,"chipselect %d already in use\n",
spi->chip_select);
put_device(d);
status= -EBUSY;
gotodone;
}
/*Drivers may modify this initial i/o setup, but will
* normally rely on the device beingsetup. Devices
* using SPI_CS_HIGH can't coexist wellotherwise...
*/
status= spi_setup(spi);/*安装spi设备*/
if(status < 0) {
dev_err(dev,"can't %s %s, status %d\n",
"setup",dev_name(&spi->dev), status);
gotodone;
}
/*Device may be bound to an active driver when this returns */
status= device_add(&spi->dev);
if(status < 0)
dev_err(dev,"can't %s %s, status %d\n",
"add",dev_name(&spi->dev), status);
else
dev_dbg(dev,"registered child %s\n", dev_name(&spi->dev));
done:
mutex_unlock(&spi_add_lock);
returnstatus;
}
/*调用master的setup函数*/
int spi_setup(struct spi_device *spi)
{
unsigned bad_bits;
int status;
/*help drivers fail *cleanly* when they need options
* that aren't supported with their currentmaster
*/
bad_bits= spi->mode & ~spi->master->mode_bits;
if(bad_bits) {
dev_dbg(&spi->dev,"setup: unsupported mode bits %x\n",
bad_bits);
return-EINVAL;
}
if(!spi->bits_per_word)
spi->bits_per_word= 8;
status= spi->master->setup(spi);
dev_dbg(&spi->dev,"setup mode %d, %s%s%s%s"
"%ubits/w, %u Hz max --> %d\n",
(int)(spi->mode & (SPI_CPOL | SPI_CPHA)),
(spi->mode& SPI_CS_HIGH) ? "cs_high, " : "",
(spi->mode& SPI_LSB_FIRST) ? "lsb, " : "",
(spi->mode& SPI_3WIRE) ? "3wire, " : "",
(spi->mode& SPI_LOOP) ? "loopback, " : "",
spi->bits_per_word,spi->max_speed_hz,
status);
returnstatus;
}
1.1.5. User space访问接口
<linux/spi/spidev.h>
#define SPI_CPHA 0x01 //极性
#define SPI_CPOL 0x02 //相位
#define SPI_MODE_0 (0|0)
#define SPI_MODE_1 (0|SPI_CPHA)
#define SPI_MODE_2 (SPI_CPOL|0)
#define SPI_MODE_3 (SPI_CPOL|SPI_CPHA)
SCLK极性和相位关系
<drivers/spi/spidev.c>
/*probe驱动,spi*/
static struct spi_driver spidev_spi_driver ={
.driver= {
.name= "spidev",//在board文件中定义,同spi 设备匹配
.owner= THIS_MODULE,
},
.probe= spidev_probe,
.remove= __devexit_p(spidev_remove),
/*NOTE: suspend/resume methods are notnecessary here.
* We don't do anything except pass therequests to/from
* the underlying controller. The refrigerator handles
* most issues; the controller driver handlesthe rest.
*/
};
static int __init spidev_init(void)
{
intstatus;
/*Claim our 256 reserved 0device numbers. Then register a class
* that will key udev/mdev to add/remove /devnodes. Last, register
* the driver which manages those devicenumbers.
*/
BUILD_BUG_ON(N_SPI_MINORS> 256);
status= register_chrdev(SPIDEV_MAJOR, "spi", &spidev_fops);//注册为字符设备
if(status < 0)
returnstatus;
spidev_class= class_create(THIS_MODULE, "spidev");
if(IS_ERR(spidev_class)) {
unregister_chrdev(SPIDEV_MAJOR,spidev_spi_driver.driver.name);
returnPTR_ERR(spidev_class);
}
status= spi_register_driver(&spidev_spi_driver);
if(status < 0) {
class_destroy(spidev_class);
unregister_chrdev(SPIDEV_MAJOR,spidev_spi_driver.driver.name);
}
returnstatus;
}
1.1.6. bitbang软件模拟spi
bitbang为软件模拟spi时序
<drivers/spi/bitbang.c>
- linux SPI源码剖析
- Linux SPI驱动框架剖析
- Linux SPI驱动框架剖析
- Linux设备驱动剖析之SPI(一)
- Linux设备驱动剖析之SPI(二)
- Linux设备驱动剖析之SPI(三)
- Linux设备驱动剖析之SPI(四)
- Linux设备驱动剖析之SPI(一)
- Linux设备驱动剖析之SPI(二)
- Linux设备驱动剖析之SPI(三)
- Linux设备驱动剖析之SPI(四)
- Linux设备驱动剖析之SPI(一)
- Linux设备驱动剖析之SPI(二)
- Linux设备驱动剖析之SPI(三)
- Linux设备驱动剖析之SPI(四)
- Linux内存管理源码剖析
- linux 线程 pthread_create 源码 剖析
- linux 线程 pthread_create 源码 剖析
- Leap Motion用户体验指南
- Map、Queue、Stack的遍历
- c#中ref 与 out关键字的几点总结
- hdu 3436 Queue-jumpers 树状数组
- poj 4044 Score Sequence(暴力,12年金华邀请赛A题)
- linux SPI源码剖析
- 使用Leap Motion可视化工具
- STM32外设使用要点
- LIB和DLL的区别与使用
- 康奈尔大学做笔记方式
- mysql+memcached【续】
- Struts2.x+JFreeChart搭建框架出错(三)
- 多线程命名管道
- Poj 1067 取石子游戏(NIM,威佐夫博奕)