原文地址:http://fpcfjf.blog.163.com/blog/static/5546979320129249855818/
平台设备的驱动,设备和总线的变量基本都直接静态变量或提前注册。比如目前的NAND,
NAND的DEVICE是在mach-MINI2440.c中调用的dev_nand.c中的定义的
struct platform_device s3c_device_nand = {
.name = “s3c2410-nand”,
.id = -1,
.num_resources = ARRAY_SIZE(s3c_nand_resource),
.resource = s3c_nand_resource,
};
而在SPI中,这两个都需要自己注册,象上面看到的bus_register,后面会讲到spi_device的设备的创建和注册以及spi_driver的注册到内核。
在向下看代码时,闹了个个笑话,把__driver_attach和__device_attach这两个函数给混淆了,他们长得确实也太一样了,一个用在platform_driver_register 注册函数中,一个用在spi_master_probe 这个函数中,路上还想呢这不成了递归调用了么。一定要引起注意。看代码一定要细心,再细心。
先看一下其注册的基本流程函数图:
然后按照这个流程图捡重要的来讲分析SPI controller驱动的注册与初始化过程:
首先是spi_s3c24xx.c中的函数s3c24xx_spi_init:
1. static int __init s3c24xx_spi_init(void)
2. {
3. return platform_driver_probe(&s3c24xx_spi_driver, s3c24xx_spi_probe);
4. }
在Devs.c中,有具体的:
static struct resource s3c_spi0_resource[] = {
[0] = {
.start = S3C24XX_PA_SPI,
.end = S3C24XX_PA_SPI + 0x1f,
.flags = IORESOURCE_MEM,
},
[1] = {
.start = IRQ_SPI0,
.end = IRQ_SPI0,
.flags = IORESOURCE_IRQ,
}
};
static u64 s3c_device_spi0_dmamask = 0xffffffffUL;
struct platform_device s3c_device_spi0 = {
.name = “s3c2410-spi”,
.id = 0,
.num_resources = ARRAY_SIZE(s3c_spi0_resource),
.resource = s3c_spi0_resource,
.dev = {
.dma_mask = &s3c_device_spi0_dmamask,
.coherent_dma_mask = 0xffffffffUL
}
};
其下还有spi1的定义,这里就不再粘贴了。这和上一节中讲的resource用函数获取,是两个地方,这个是具体的设备的资源定义。看来都一样,都得提前定义好。
在相应的文件(比如mach-smdk2240.c或者mach-mini2440.c中的设备数组中,如smdk2440_devices中添加&s3c_device_spi0,&s3c_device_spi1,这就生成如图所示的s3c24xx-spi.0与s3c24xx-spi.1,当然了这图是在网上找的,所以是6410的。这里s3c24xx-spi.0表示s3c2440的spi controller的0号接口,s3c24xx-spi.1表示s3c2440的spi controller的1号接口。注册了s3c24xx_spi_driver后,赋值了平台驱动的probe函数为s3c24xx_spi_probe。所以当match成功后,调用s3c24xx_spi_probe。看其实现的过程如下:
static int __init s3c24xx_spi_probe(struct platform_device pdev)
{
struct s3c2410_spi_info *pdata;
struct s3c24xx_spi *hw;
struct spi_master *master;
struct resource *res;
int err = 0;
/分配struct spi_master+struct s3c24xx_spi大小的数据,把s3c24xx_spi设为spi_master的私有数据 /
master = spi_alloc_master(&pdev->dev, sizeof(struct s3c24xx_spi));
if (master == NULL) {
dev_err(&pdev->dev, “No memory for spi_master\n”);
err = -ENOMEM;
goto err_nomem;
}
/从master中获得s3c24xx_spi*/
hw = spi_master_get_devdata(master);
memset(hw, 0, sizeof(struct s3c24xx_spi));
hw->master = spi_master_get(master);
/ 驱动移植的时候需要实现的重要结构,初始化为&s3c2410_spi0_platdata/
hw->pdatapdata = pdata = pdev->dev.platform_data;
hw->dev = &pdev->dev;
if (pdata == NULL) {
dev_err(&pdev->dev, “No platform data supplied\n”);
err = -ENOENT;
goto err_no_pdata;
}
/ 设置平台的私有数据为s3c24xx_spi/
platform_set_drvdata(pdev, hw);
init_completion(&hw->done);
/ setup the master state. /
/ 该总线上的设备数/
master->num_chipselect = hw->pdata->num_cs;
/ 总线号/
master->bus_num = pdata->bus_num;
/ setup the state for the bitbang driver /
/ spi_bitbang专门负责数据的传输/
hw->bitbang.master = hw->master;
hw->bitbang.setup_transfer = s3c24xx_spi_setupxfer;
hw->bitbang.chipselect = s3c24xx_spi_chipsel;
hw->bitbang.txrx_bufs = s3c24xx_spi_txrx;
hw->bitbang.master->setup = s3c24xx_spi_setup;
dev_dbg(hw->dev, “bitbang at %p\n”, &hw->bitbang);
。。。。。。。。。。。。。。。。。。。。。。。。
/ 初始化设置寄存器,包括对SPIMOSI,SPIMISO,SPICLK引脚的设置/
s3c24xx_spi_initialsetup(hw);
/ register our spi controller /
err = spi_bitbang_start(&hw->bitbang);
。。。。。。。。。。。。。。。。。。。。。
}
spi controller的register在spi_bitbang_start函数中实现:
[html] view plaincopy
int spi_bitbang_start(struct spi_bitbang bitbang)
{
int status;
if (!bitbang->master || !bitbang->chipselect)
return -EINVAL;
/动态创建一个work_struct结构,它的处理函数是bitbang_work*/
INIT_WORK(&bitbang->work, bitbang_work);
spin_lock_init(&bitbang->lock);
INIT_LIST_HEAD(&bitbang->queue);
/ spi的数据传输就是用这个方法/
if (!bitbang->master->transfer)
bitbang->master->transfer = spi_bitbang_transfer;
if (!bitbang->txrx_bufs) {
bitbang->use_dma = 0;
/ spi_s3c24xx.c中有spi_bitbang_bufs方法,在bitbang_work中被调用/
bitbang->txrx_bufs = spi_bitbang_bufs;
if (!bitbang->master->setup) {
if (!bitbang->setup_transfer)
bitbang->setup_transfer =
spi_bitbang_setup_transfer;
/ 在spi_s3c24xx.c中有setup的处理方法,在spi_new_device中被调用/
bitbang->master->setup = spi_bitbang_setup;
bitbang->master->cleanup = spi_bitbang_cleanup;
}
} else if (!bitbang->master->setup)
return -EINVAL;
/ this task is the only thing to touch the SPI bits /
bitbang->busy = 0;
/调用create_singlethread_workqueue创建单个工作线程/
bitbang->workqueue = create_singlethread_workqueue(
dev_name(bitbang->master->dev.parent));
if (bitbang->workqueue == NULL) {
status = -EBUSY;
goto err1;
}
status = spi_register_master(bitbang->master);
if (status < 0)
goto err2;
return status;
err2:
destroy_workqueue(bitbang->workqueue);
err1:
return status;
}
然后看这里是怎样注册spi主机控制器驱动的:
[cpp] view plaincopy
int spi_register_master(struct spi_master master)
{
。。。。。。。。。。。。。。。。
/将spi添加到内核,这也是sys/class/Spi_master下产生Spi0,Spi1的原因 /
dev_set_name(&master->dev, “spi%u”, master->bus_num);
status = device_add(&master->dev);
scan_boardinfo(master);
}
这里跟踪scan_boardinfo函数:
[cpp] view plaincopy
static void scan_boardinfo(struct spi_master *master)
{
struct boardinfo *bi;
mutex_lock(&board_lock);
/遍历所有挂在board_list上的struct boardinfo*/
list_for_each_entry(bi, &board_list, list) {
struct spi_board_info chip = bi->board_info;
unsigned n;
/遍历每个boardinfo管理的spi_board_info,如果设备的总线号与控制器的总线好相等,则创建新设备 /
for (n = bi->n_board_info; n > 0; n–, chip++) {
if (chip->bus_num != master->bus_num)
continue;
(void) spi_new_device(master, chip);
}
}
mutex_unlock(&board_lock);
}
在移植的时候我们会在相应的文件(如mach-smdk2440.c中的smdk2440_machine_init中添加spi_register_board_info)添加相应的注册函数。
这个函数完成了将spi_board_info交由boardinfo管理,并把boardinfo挂载到board_list链表上。也就是说在系统初始化的时候将spi_device交由到挂在board_list上的boardinfo管理,在spi controller的driver注册的时候不但注册这个主机控制器的驱动,还要遍历这个主机控制器的总线上的spi_device,将总线上的spi_device全部注册进内核。当注册进内核并且spi_driver已经注册的时候,如果总线match成功,则会调用spi_driver的probe函数,这个将在后边进行分析。
[html] view plaincopy
int __init
spi_register_board_info(struct spi_board_info const *info, unsigned n)
{
struct boardinfo *bi;
bi = kmalloc(sizeof(*bi) + n sizeof info, GFP_KERNEL);
if (!bi)
return -ENOMEM;
bi->nn_board_info = n;
memcpy(bi->board_info, info, n sizeof *info);
mutex_lock(&board_lock);
list_add_tail(&bi->list, &board_list);
mutex_unlock(&board_lock);
return 0;
}
其实在scan_boardinfo中就调用了 (void) spi_new_device(master, chip),这说明新的工作要来了,但是,今天只讲到这里,这个留给明天。
郑重说明:图都是从网上找的,非常感谢,如有不妥,请指出,道歉并删除之。
只要坚持就会有结果。