说明:本文将分析Linux-2.6.17源代码中的spi驱动程序,其内容为本人阅读源代码后的一些理解。由于本人水平有限,所以内容可能比较杂乱零散,权当个人笔记记录之用。而以下内容均以powerpc架构为例说明。
在Linux系统中,spi驱动的设计采用了分层设计模式的思想,将spi驱动分为三层:
- spi主控制器层:即对cpu所集成的spi控制器进行初始化设置的代码,所以对cpu的spi控制器的寄存器设置也在此代码中实现;
- spi通用层:提供一些spi主控制器层和spi从设备层中所需的设备注册函数,以及spi总线类型等数据结构;
- spi从设备层:将从设备作为字符设备类型进行注册,并提供操作从设备的方法,以及从设备spidev_data等数据结构。
首先分析spi主控制器层:
spi_mpc83xx.c文件中有如下主要结构体:
/* SPI Controller registers */
struct mpc83xx_spi_reg {
//u8 res1[0x20]; /* 此寄存器保留 */
__be32 mode;
__be32 event;
__be32 mask;
__be32 command;
__be32 transmit;
__be32 receive;
/* 以下寄存器对应四个从设备的模式设置 */
__be32 res[2];
__be32 mode0;
__be32 mode1;
__be32 mode2;
__be32 mode3;
};
以上结构体为spi主控制器的几个寄存器,对其进行赋值即可操作spi寄存器。
/* SPI Controller driver's private data. */
struct mpc83xx_spi {
/* bitbang has to be first */
struct spi_bitbang bitbang;
struct completion done;
struct mpc83xx_spi_reg __iomem *base;
/* rx & tx bufs from the spi_transfer */
const void *tx;
void *rx;
/* functions to deal with different sized buffers */
void (*get_rx) (u32 rx_data, struct mpc83xx_spi *);
u32(*get_tx) (struct mpc83xx_spi *);
unsigned int count;
u32 irq;
unsigned nsecs; /* (clock cycle time)/2 */
u32 sysclk;
void (*activate_cs) (u8 cs, u8 polarity);
void (*deactivate_cs) (u8 cs, u8 polarity);
};
以上结构体为spi主控制器数据结构(spi_master)中cdev的私有数据,即可以认为其为spi主控制器数据结构中的私有数据。此结构体中包含了spi控制寄存器的基地址、传输和接受的buf、中断号以及系统时钟值。
/* SPI platform_driver */
static struct platform_driver mpc83xx_spi_driver = {
.probe = mpc83xx_spi_probe,
.remove = __devexit_p(mpc83xx_spi_remove),
.driver = {
.name = "mpc83xx_spi",
},
};
以上结构体为spi的平台驱动结构体,其代表在平台总线上spi主控制器对应的驱动数据结构。probe函数用于探测总线上是否有注册过的对应设备。
以我的理解:在系统中注册设备,是指对设备的数据结构进行初始化赋值,并调用相关函数进行注册。而设备注册过之后,其所初始化赋值的结构体就能被平台驱动探测到,驱动从而找到对应的设备结构体,再根据设备结构体的初始化值来进行spi寄存器的相关设置。
spi_mpc83xx.c文件中有如下主要函数:
static int mpc83xx_spi_setup_transfer(struct spi_device *spi, struct spi_transfer *t);
static int mpc83xx_spi_setup(struct spi_device *spi);
static int __init mpc83xx_spi_probe(struct platform_device *dev);
static int __init mpc83xx_spi_init(void);
首先,在boot内核的过程中,系统会直接调用被module_init(mpc83xx_spi_init)过的mpc83xx_spi_init(void)函数,而此函数的定义如下所示:
static int __init mpc83xx_spi_init(void)
{
return platform_driver_register(&mpc83xx_spi_driver);
}
其调用平台驱动注册函数,将上文提到过的mpc83xx_spi_driver平台驱动结构体注册到平台总线上。而platform_driver_register()函数又会把已初始化的platform_bus_type赋值给mpc83xx_spi_driver平台驱动结构体中device的bus成员,以及调用driver_register(&mpc83xx_spi_driver.driver)来注册驱动。platform_driver_register()函数定义如下:
/**
* platform_driver_register
* @drv: platform driver structure
*/
int platform_driver_register(struct platform_driver *drv)
{
drv->driver.bus = &platform_bus_type; /* 此bus中的match函数能检测已经注册的各个平台设备和此平台驱动是否匹配 */
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);
}
其中几个if语句表示如果mpc83xx_spi_driver平台驱动结构体中probe等函数指针已被赋值的话,则mpc83xx_spi_driver平台驱动结构中的driver结构相应的函数指针也会被初始化。而platform_drv_probe()函数功能为调用mpc83xx_spi_driver平台驱动结构体中的probe函数。
而driver_register(&drv->driver)函数将此平台驱动结构中的设备驱动添加到平台bus总线上,其函数定义如下所示:
/**
* driver_register - register driver with bus
* @drv: driver to register
**/
int driver_register(struct device_driver * drv)
{
INIT_LIST_HEAD(&drv->devices);
init_completion(&drv->unloaded);
return bus_add_driver(drv);
}
之后将以此顺序来依次调用接下来的这些函数:bus_add_driver(drv) -> driver_attach(drv) -> driver_probe_device(drv, dev) -> drv->probe
由以上函数的调用顺序可知,当driver_probe_device(drv, dev)函数检测到系统中有与平台驱动相对应的平台设备时,将调用drv->probe函数。而drv->probe函数就是上文中提到的platform_drv_probe()函数,所以最终调用的函数是mpc83xx_spi_probe(struct platform_device *dev),其定义如下所示:
static int __init mpc83xx_spi_probe(struct platform_device *dev)
{
struct spi_master *master;
struct mpc83xx_spi *mpc83xx_spi;
struct fsl_spi_platform_data *pdata;
struct resource *r;
u32 regval;
int ret = 0;
/* Get resources(memory, IRQ) associated with the device */
master = spi_alloc_master(&dev->dev, sizeof(struct mpc83xx_spi));
if (master == NULL) {
ret = -ENOMEM;
goto err;
}
platform_set_drvdata(dev, master);
pdata = dev->dev.platform_data;
if (pdata == NULL) {
ret = -ENODEV;
goto free_master;
}
r = platform_get_resource(dev, IORESOURCE_MEM, 0);
if (r == NULL) {
ret = -ENODEV;
goto free_master;
}
mpc83xx_spi = spi_master_get_devdata(master);
mpc83xx_spi->bitbang.master = spi_master_get(master);
mpc83xx_spi->bitbang.chipselect = mpc83xx_spi_chipselect;
mpc83xx_spi->bitbang.setup_transfer = mpc83xx_spi_setup_transfer;
mpc83xx_spi->bitbang.txrx_bufs = mpc83xx_spi_bufs;
mpc83xx_spi->sysclk = pdata->sysclk;
mpc83xx_spi->activate_cs = pdata->activate_cs;
mpc83xx_spi->deactivate_cs = pdata->deactivate_cs;
mpc83xx_spi->get_rx = mpc83xx_spi_rx_buf_u8;
mpc83xx_spi->get_tx = mpc83xx_spi_tx_buf_u8;
mpc83xx_spi->bitbang.master->setup = mpc83xx_spi_setup;
init_completion(&mpc83xx_spi->done);
mpc83xx_spi->base = ioremap(r->start, r->end - r->start + 1);
if (mpc83xx_spi->base == NULL) {
ret = -ENOMEM;
goto put_master;
}
mpc83xx_spi->irq = platform_get_irq(dev, 0);
if (mpc83xx_spi->irq < 0) {
ret = -ENXIO;
goto unmap_io;
}
/* Register for SPI Interrupt */
ret = request_irq(mpc83xx_spi->irq, mpc83xx_spi_irq,
0, "mpc83xx_spi", mpc83xx_spi);
if (ret != 0)
goto unmap_io;
master->bus_num = pdata->bus_num;
master->num_chipselect = pdata->max_chipselect;
/* SPI controller initializations */
mpc83xx_spi_write_reg(&mpc83xx_spi->base->mode, 0);
mpc83xx_spi_write_reg(&mpc83xx_spi->base->mode0, 0);
mpc83xx_spi_write_reg(&mpc83xx_spi->base->mask, 0);
mpc83xx_spi_write_reg(&mpc83xx_spi->base->command, 0);
mpc83xx_spi_write_reg(&mpc83xx_spi->base->event, 0xffffffff);
/* Enable SPI interface */
regval = pdata->initial_spmode | SPMODE_INIT_VAL;
mpc83xx_spi_write_reg(&mpc83xx_spi->base->mode0, regval);
mpc83xx_spi_write_reg(&mpc83xx_spi->base->mode, 0x8000100F);
ret = spi_bitbang_start(&mpc83xx_spi->bitbang);
if (ret != 0)
goto free_irq;
printk(KERN_INFO
"%s: MPC83xx SPI Controller driver at 0x%p (irq = %d)\n",
dev->dev.bus_id, mpc83xx_spi->base, mpc83xx_spi->irq);
return ret;
free_irq:
free_irq(mpc83xx_spi->irq, mpc83xx_spi);
unmap_io:
iounmap(mpc83xx_spi->base);
put_master:
spi_master_put(master);
free_master:
kfree(master);
err:
return ret;
}
此函数的作用在于映射spi控制寄存器的基地址,注册spi控制器的中断号,填充mpc83xx_spi结构体以及初始化spi控制寄存器。
而mpc83xx_spi_setup_transfer(struct spi_device *spi, struct spi_transfer *t)与mpc83xx_spi_setup(struct spi_device *spi)函数在注册spi从设备时才被调用,其作用为根据从设备传入的参数来初始化spi控制寄存器,所以这两个函数将放在spi从设备驱动中进行讲解。
spi主控制器驱动层基本就这些内容,而还有一些数据传输等函数并未进行分析,可能会再后面的文章中再进行详细的分析。总之spi主控制器驱动层的主要功能在于注册spi的平台驱动,将spi的平台驱动加入到平台总线中,然后再进行spi控制寄存器的地址映射和初始化赋值。