一、SPI原理模型
如上图所示,主设备对应SOC芯片中的SPI控制器,通常,一个SOC中可能存在多个SPI控制器,像上面的例子所示,SOC芯片中有3个SPI控制器。每个控制器下可以连接多个SPI从设备,每个从设备有各自独立的CS引脚。每个从设备共享另外3个信号引脚:SCK、MISO、MOSI。任何时刻,只有一个CS引脚处于有效状态,与该有效CS引脚连接的设备此时可以与主设备(SPI控制器)通信,其它的从设备处于等待状态,并且它们的3个引脚必须处于高阻状态。
二、SPI驱动模型
2.6的Linux内核中,SPI的驱动架构分为如下三个层次:硬件抽象层、平台依赖层和用户接口层。
硬件抽象层
spi.c为其主体框架代码,提供了核心数据结构的定义、SPI控制器驱动和设备驱动的注册、注销管理等API。其为硬件平 台无关层,向下屏蔽了物理总线控制器的差异,定义了统一的访问策略和接口;其向上提供了统一的接口,以便SPI设备驱动通过总线控制器进行数据收发。
平台依赖层
SPI总线Master就是一条SPI总线的控制器(所谓控制是相对于本CPU来说的),在物理上连接若干SPI从设备。在Linux驱动中,每种处理器 平台有自己的控制器驱动,属于平台移植相关层。PowerPC平台来说,其是spi_mpc83xx.c。其按照核心层定义的接口实现了 spi_master。
用户接口层
设备驱动层为用户接口层,其为用户提供了通过SPI总线访问具体设备的接口。
平台依赖层-总线控制器驱动
总线控制器驱动,本质上就是实现了具体的总线传输算法并向核心层注册了控制器。主要分为三个层面,platform device,platform driver及与SPI core的接口层。
Linux内核的所有SPI控制器驱动程序都在driver/SPI/下面,MPC8xxx驱动是spi_mpc83xx.c。
一个现实的linux设备和驱动通常都需要挂接在一种总线上,比较常见的总线有USB、PCI总线等。但是,在嵌入式系统里面,SoC系统中集成的独立的外设控制器、挂接在SoC内存空间的外设却不依附与此类总线。基于这样的背景下,2.6内核加入了platform虚拟总线。platform机制将设备本身的资源注册进内核,由内核统一管理,在驱动程序使用这些资源时使用统一的接口。platform总线对加入到该总线的设备和驱动分别封装了两个结构体——platform_device和platform_driver。并且提供了对应的注册函数。
在platform总线上注册设备和驱动,只要定义指定的结构体后调用platform给出的注册函数就可以了。
下面就介绍一下platform总线、设备和驱动
三、platform驱动模型分析
1、platform总线:
do_basic_setup(void)
--------->driver_init();
---------->platform_bus_init(platform总线初始化)
---------->bus_register(&platform_bus_type)
structbus_type platform_bus_type = {
.name ="platform", //定义了总线名字为platform,总线注册后新建目录sys/bus/platform
.dev_attrs= platform_dev_attrs,
.match = platform_match,
.uevent= platform_uevent,
.pm= PLATFORM_PM_OPS_PTR,
};
static int platform_match(structdevice *dev, struct device_driver *drv)
{
structplatform_device *pdev;
pdev= container_of(dev, struct platform_device, dev);
return(strcmp(pdev->name, drv->name) == 0); //配对函数检验名字是否一致
}
2、platform设备:
(/include/linux/platform_device.h)
struct platform_device {
const char * name;
int id;
struct device dev;
u32 num_resources;
struct resource * resource;
};
可以看到,platform_device的封装就是指定了一个目录的名字name,并且内嵌device。
platform_device的注册和注销使用以下函数:
(drivers/base/platform.c)
322 intplatform_device_register(struct platform_device *pdev) //需要判断返回值
337 voidplatform_device_unregister(struct platform_device *pdev)
注册后,同样会在/sys/device/目录下创建一个以name命名的目录,并且创建软连接到/sys/bus/platform/device下。
源码分析:
machine_device_initcall (mpc830x_edd, mpc830x_spi_init);
/arch/powerpc/platforms/83xx/mpc830x_edd.c
-->platform_device_alloc ("mpc83xx_spi", i)-->设备树spi设备信息获取-->platform_device_alloc("mpc83xx_spi",i);-->platform_device_add
3、platform驱动:
(linux/platform_device.h)
50 structplatform_driver {
51 int(*probe)(struct platform_device *);
52 int(*remove)(struct platform_device *);
53 void(*shutdown)(struct platform_device *);
54 int(*suspend)(struct platform_device *, pm_message_t state);
55 int(*suspend_late)(struct platform_device *, pm_message_t state);
56 int(*resume_early)(struct platform_device *);
57 int(*resume)(struct platform_device *);
58 struct device_driverdriver;
59 };
可以看到,platform_driver结构体内嵌了device_driver,并且实现了prob、remove等操作。其实,当内核需要调用probe函数时,它会调用driver->probe,在dricer->probe中再调用platform_driver->probe
platform_driver的注册和注销函数:
/*drivers/base/platform.c*/
492 intplatform_driver_register(struct platform_driver *drv)
。。。。。
513 voidplatform_driver_unregister(struct platform_driver *drv)
注册成功后内核会在/sys/bus/platform/driver/目录下创建一个名字为driver->name的目录。
源码分析:
mpc83xx_spi_init
/drivers/spi/spi_mpc83xx.c
static struct platform_driver mpc83xx_spi_driver ={
.remove = __exit_p(mpc83xx_spi_remove),
.driver = {
.name = "mpc83xx_spi",
.owner = THIS_MODULE,
},
};
-->platform_driver_probe-->platform_driver_register-->driver_register
4.源码具体分析:
1)platform设备
/arch/powerpc/platform/83xx/mpc830x_edd.c
machine_device_initcall(mpc830x_edd, mpc830x_spi_init);
mpc830x_spi_init-->fsl_spi_init(&mpc830x_spi_boardinfo,1, NULL, NULL);
static struct spi_board_info mpc830x_spi_boardinfo = {
.bus_num = 0x7000,
.chip_select = 0,
.mode = SPI_MODE_3,
.max_speed_hz = 25000000,
.modalias = "spidev",
};
(arch/powerpc/sysdev/fsl_soc.c)
fsl_spi_init
--> ret = of_fsl_spi_probe(NULL,"fsl,spi", sysclk, board_infos,
num_board_infos,activate_cs, deactivate_cs);
struct fsl_spi_platform_data pdata = {
max_chipselect = 1,
pdata.bus_num = 0x7000,
activate_cs = NULL,
deactivate_cs = NULL,
};
pdev = platform_device_alloc("mpc83xx_spi",i); //以mpc83xx_spi为name申请platform device,后续的platform driver将以mpc83xx_spi为匹配因子
ret = platform_device_add_data(pdev,&pdata, sizeof(pdata)); //将pdata等特定的属性添加到platform device中,以供相应的platform
ret = platform_device_add_resources(pdev,res, ARRAY_SIZE(res)); //将SPI相关的platform device添加到platform bus上
ret = platform_device_add(pdev);
-----> pdev->dev.parent =&platform_bus;
pdev->dev.bus= &platform_bus_type;
device_add()
(
int platform_device_register(struct platform_device *pdev)
321{
322 device_initialize(&pdev->dev);
323 return platform_device_add(pdev);
324}
注册一个platform device分为了两部分,初始化这个platform_device,然后将此platform_device添加到platform总线中。输入参数platform_device可以是静态的全局设备。
)
动态申请platform_device_alloc一个platform_device设备,然后通过platform_device_add_resources及platform_device_add_data等添加相关资源和属性。
2)platform驱动--spi实例
mpc83xx_spi_driver注册时会扫描platform bus上的所有设备,匹配因子是mpc83xx_spi,匹配成功后调用mpc83xx_spi_probe将设备和驱动绑定起来
struct platform_driver {
int (*probe)(struct platform_device *);
int (*remove)(struct platform_device *);
void (*shutdown)(struct platform_device *);
int (*suspend)(struct platform_device *, pm_message_t state);
int (*suspend_late)(struct platform_device *, pm_message_t state);
int (*resume_early)(struct platform_device *);
int (*resume)(struct platform_device *);
struct device_driver driver;
};
struct device_driver {
const char *name; “mpc83xx_spi”<----platform_driver
struct bus_type *bus;
struct module *owner; "THIS_MODULE"<---platform_driver
const char *mod_name; /* used for built-in modules */
int (*probe) (struct device *dev); platform_drv_probe<---platform_driver
int (*remove) (struct device *dev);
void (*shutdown) (struct device *dev);
int (*suspend) (struct device *dev, pm_message_t state);
int (*resume) (struct device *dev);
struct attribute_group **groups;
struct dev_pm_ops *pm;
struct driver_private *p;
};
device_driver提供了一些操作接口,但其并没有实现,相当于一些虚函数,由派生类platform_driver进行重载
device_driver结构中也有一个name变量。platform_driver从字面上来看就知道是设备驱动。设备驱动是为谁服务的呢?当然是设备了。内核正是通过这个一致性来为驱动程序找到资源,即 platform_device中的resource。
(drivers/spi/spi_mpc83xx.c)
MODULE_ALIAS("platform:mpc83xx_spi");
static struct platform_driver mpc83xx_spi_driver = {
.remove = __exit_p(mpc83xx_spi_remove),
.driver = {
.name = "mpc83xx_spi",
.owner = THIS_MODULE,
},
};
static int __init mpc83xx_spi_init(void)
{
return platform_driver_probe(&mpc83xx_spi_driver,mpc83xx_spi_probe);
}
int __init_or_module platform_driver_probe(structplatform_driver *drv,
int (*probe)(struct platform_device *))
{
int retval, code;
/* temporary section violation during probe() */
drv->probe = probe;
retval = code = platform_driver_register(drv);
。。。
}
int platform_driver_register(struct platform_driver *drv)
{
drv->driver.bus = &platform_bus_type;
if (drv->probe)
drv->driver.probe = platform_drv_probe;
if (drv->remove)
drv->driver.remove = platform_drv_remove;
if (drv->shutdown)
drv->driver.shutdown = platform_drv_shutdown;
if (drv->suspend)
drv->driver.suspend = platform_drv_suspend;
if (drv->resume)
drv->driver.resume = platform_drv_resume;
return driver_register(&drv->driver);
}
driver_register--->bus_add_driver--->driver_attach--->__driver_attach
static int __driver_attach(struct device*dev, void *data)
{
struct device_driver *drv = data;
if (drv->bus->match &&!drv->bus->match(dev, drv))
return 0;
if (dev->parent) /* Needed for USB */
down(&dev->parent->sem);
down(&dev->sem);
if (!dev->driver)
driver_probe_device(drv, dev);
up(&dev->sem);
if (dev->parent)
up(&dev->parent->sem);
return 0;
}
driver_probe_device--->really_probe--->drv->probe(dev);
此处的drv是struct device_driver *
而其probe是platform_drv_probe
static int platform_drv_probe(struct device*_dev)
{
struct platform_driver *drv =to_platform_driver(_dev->driver);
struct platform_device *dev =to_platform_device(_dev);
return drv->probe(dev);
}
此处的drv是platform_driver *
其实就是回到了
platform_driver_probe(&mpc83xx_spi_driver,mpc83xx_spi_probe);
中的mpc83xx_spi_probe
四、spi驱动模型分析
1.spi驱动模型分析
2.spi总线
static int __init spi_init(void)
postcore_initcall(spi_init);
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,
};
static struct class spi_master_class = {
.name = "spi_master",
.owner = THIS_MODULE,
.dev_release = spi_master_release,
};
static int __init spi_init(void)
{
int status;
buf = kmalloc(SPI_BUFSIZ, GFP_KERNEL);
status = bus_register(&spi_bus_type);
status = class_register(&spi_master_class);
return 0;
。。。
}
3.spi设备分析
spi_device对应着SPI总线上某个特定的slave。每个slave都有特定的大小端、速率及传输位宽,各个slave相互之间无干扰。
static int __init mpc83xx_spi_probe(structplatform_device *dev)
{
struct spi_master *master;
struct mpc83xx_spi *mpc83xx_spi;
。。。
master = spi_alloc_master(&dev->dev,sizeof(struct mpc83xx_spi));
platform_set_drvdata(dev, master);
pdata = dev->dev.platform_data;
r = platform_get_resource(dev, IORESOURCE_MEM, 0);
master->setup = mpc83xx_spi_setup;--------初始化配置
master->transfer = mpc83xx_spi_transfer;----指定中断下文的数据处理队列
master->cleanup = mpc83xx_spi_cleanup;
mpc83xx_spi = spi_master_get_devdata(master);
mpc83xx_spi->base = ioremap(r->start,r->end - r->start + 1);
mpc83xx_spi->irq = platform_get_irq(dev, 0);
ret = request_irq(mpc83xx_spi->irq,mpc83xx_spi_irq,
0, "mpc83xx_spi",mpc83xx_spi);
INIT_WORK(&mpc83xx_spi->work,mpc83xx_spi_work);
ret = spi_register_master(master);
。。。
}
struct spi_master {
struct device dev;
s16 bus_num; --->0x7000
u16 num_chipselect;
int (*setup)(struct spi_device *spi);
int (*transfer)(struct spi_device *spi,
struct spi_message *mesg);
void (*cleanup)(struct spi_device *spi);
};
Spi_master
spi_master是对某一条SPI总线的抽象,是特定总线的相关属性的集合。
每一个SPI master都要实现setup、transfer及cleanup等。
spi_register_master-->scan_boardinfo
static struct spi_board_info mpc830x_spi_boardinfo = {
.bus_num = 0x7000,
.chip_select = 0,
.mode = SPI_MODE_3,
.max_speed_hz = 25000000,
.modalias = "spidev",
};
-->spi_new_device-->spi_alloc_device
struct spi_device *spi_alloc_device(struct spi_master*master)
{
struct spi_device *spi;
struct device *dev = master->dev.parent;
if (!spi_master_get(master))
return NULL;
spi = kzalloc(sizeof *spi, GFP_KERNEL);
if (!spi) {
dev_err(dev, "cannot allocspi_device\n");
spi_master_put(master);
return NULL;
}
spi->master = master;
spi->dev.parent = dev;
spi->dev.bus = &spi_bus_type;
spi->dev.release = spidev_release;
device_initialize(&spi->dev);
return spi;
}
struct spi_device {
structdevice dev;
structspi_master *master;
u32 max_speed_hz;
u8 chip_select;
u8 mode;
。。。
u8 bits_per_word;
int irq;
void *controller_state;
void *controller_data;
。。。
};
-->spi_add_device-->device_add
4.spi驱动分析
struct spi_driver {
int (*probe)(struct spi_device *spi);
int (*remove)(struct spi_device *spi);
void (*shutdown)(struct spi_device *spi);
int (*suspend)(struct spi_device *spi,pm_message_t mesg);
int (*resume)(struct spi_device *spi);
structdevice_driver driver;
};
Driver是为device服务的,SPI_driver注册时会扫描SPI bus上的设备,进行驱动和设备的绑定。
module_init标识的入口初始化函数spidev_init,(module_exit标识的出口函数)
设备与设备驱动匹配时候调用的probe方法spidev_probe
设备驱动的操作函数集file_operations--->spidev_fops
open方法spidev_open
进行检查, 重点是以后三条语句,其他的见下面代码注释:
spidev->users++; //spidev_data使用者计数++
filp->private_data = spidev; //spidev_data放在文件的私有数据里
nonseekable_open(inode, filp); //设置文件的打开模式(文件读写指针不会跟随读写操作移动)
read方法spidev_read
spidev =filp->private_data;=========>>status = spidev_sync_read(spidev,count);===========>>
spidev_sync(spidev,&m)-->spi->master->transfer(spi, message);
mpc83xx_spi_transfer-->
list_add_tail(&m->queue,&mpc83xx_spi->queue);
queue_work(mpc83xx_spi->workqueue,&mpc83xx_spi->work);
wait_for_completion(&done);========>>到了这一步是重点,在spi_async()方法中,使用以下语句将要做的事情加到workqueue中
此后所有的处理程序便转移到在之前初始化的work方法中
mpc83xx_spi_work-->
m->status = status;
m->complete(m->context);
设置完成标志
整个platform、spi总线设备和驱动的结构如下图: