日期:2014-05-16  浏览次数:20598 次

linux下CPU注册i2c控制器(adapter)过程

无论是三星的s3c2410, 还是cavium 的octeon, AMD的amd8111等等,  任何处理器在linux下添加自己的adapter都是大致的方法, 都是实现自己的driver,  最后调用i2c-core提供的API完成整个注册.  广泛地讲,  linux将任何类型的设备, 任何类型的总线等都作为文件来处理,  只不过使用了不同的数据结构的driver和device.    

I2c的逻辑简单实用. 在linux精妙的架构下, 代码量非常小. 现在大部分的IC都有I2C接口. 至于spi, uart, can, usb, pci, stat等等各种各样的, 虽然协议不同,  特点不用, 但本质上都是一样的. 

至于I2C具体的协议, 时序等请参考其他资料. 这里只做软件上的架构分析. 

下面以octeon处理器为例,  重点介绍下octeon_i2c_probe()中部分重要的代码, 在其他处理器的xxx_i2c_probe() 函数中, 无论是获取设备资源, 获取中断, CPU时钟, 配置adapter 等等操作在任何处理器中的步骤大致都是相同的, 也都是不可或缺的. 

    												By 韩大卫@吉林师范大学

内核代码drivers/i2c/busses/i2c-octeon.c

static int __init octeon_i2c_init(void)
{               
    int rv;     
/*
i2c 控制器是被集成在CPU上的, 寄存器地址可以被CPU直接寻址到,  linux将这个adapter抽象为一个platfrom device , 其驱动使用数据结构: struct  platfrom_driver.   一般将usb host,  serial 控制器等也做同样处理. 
实现好driver后, 使用  platform_driver_register()函数将其注册到linux内核的设备树中.  
*/
    rv = platform_driver_register(&octeon_i2c_driver);                                                                                       
    return rv;  
}   

static struct platform_driver octeon_i2c_driver = {
    .probe      = octeon_i2c_probe,

    .remove     = __devexit_p(octeon_i2c_remove),
    .driver     = {
        .owner  = THIS_MODULE,
        .name   = DRV_NAME,
        .of_match_table = octeon_i2c_match,
    },          
};              
  
 
#define DRV_NAME "i2c-octeon"   


static int __devinit octeon_i2c_probe(struct platform_device *pdev)
{           
    int irq, result = 0;
    struct octeon_i2c *i2c;
    struct resource *res_mem;
    const __be32 *data;
    int len;

/*
获取设备的中断号
*/
 /* All adaptors have an irq.  */                                                                                                         
    irq = platform_get_irq(pdev, 0);


/*
为其数据结构分配内存
*/
    i2c = devm_kzalloc(&pdev->dev, sizeof(*i2c), GFP_KERNEL);
    if (!i2c) {   
        dev_err(&pdev->dev, "kzalloc failed\n");
        result = -ENOMEM;
        goto out; 
    }             
    i2c->dev = &pdev->dev;
                  
/* platform_driver_register() 注册时会对所有已注册的所有 platform_device 中的 name 和当前注册的