注册 登录  
 加关注
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

还东国的博客

行之苟有恒,久久自芬芳

 
 
 

日志

 
 

LINUX驱动之SPI子系统之四spi_master的注册流程  

2012-10-24 21:08:55|  分类: LINUX内核驱动 |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |
 

LINUX驱动之SPI子系统之四spi_master的注册流程

 

平台设备的驱动,设备和总线的变量基本都直接静态变量或提前注册。比如目前的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  这个函数中,路上还想呢这不成了递归调用了么。一定要引起注意。看代码一定要细心,再细心。

先看一下其注册的基本流程函数图:

LINUX驱动之SPI子系统之四spi_master的注册流程 - 还东国 - 还东国的博客

 

然后按照这个流程图捡重要的来讲分析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

        }

};

LINUX驱动之SPI子系统之四spi_master的注册流程 - 还东国 - 还东国的博客

 

其下还有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),这说明新的工作要来了,但是,今天只讲到这里,这个留给明天。

郑重说明:图都是从网上找的,非常感谢,如有不妥,请指出,道歉并删除之。

只要坚持就会有结果。

  评论这张
 
阅读(2370)| 评论(0)
推荐 转载

历史上的今天

在LOFTER的更多文章

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2017