I2C适配器驱动及设备驱动代码详解

实例Accelerometer Sensors: bma2x2

1.简述

根据i2C驱动的开发流程去分析i2c从机设备bma2x2(加速度sensors)的驱动;以及分析其挂载的控制器(适配器)i2c-msm-v2的驱动。
在这里插入图片描述

2.I2C总线驱动i2c-msm-v2

2.1 定义总线驱动:platform_driver

static struct platform_driver i2c_msm_driver = {
    .probe  = i2c_msm_probe,
    .remove = i2c_msm_remove,
    .driver = {
        .name           = "i2c-msm-v2",
        .owner          = THIS_MODULE,
        .pm             = &i2c_msm_pm_ops,
        .of_match_table = i2c_msm_dt_match,
    },
};

2.2 设备树匹配方法

static struct of_device_id i2c_msm_dt_match[] = {
	{
		.compatible = "qcom,i2c-msm-v2",
	},
	{}
};

2.3 注册平台驱动(加载模块)

static int i2c_msm_init(void)
{
	return platform_driver_register(&i2c_msm_driver);
}
arch_initcall(i2c_msm_init);

2.4 总线驱动probe函数

当调用platform_driver_register()函数注册平台驱动结构体时,如果平台设备(i2c下的compatible)和平台驱动匹配成功后,会调用probe()函数,来初始化适配器硬件、添加i2c_adapter。实现模板如下。

static int xxx_i2c_probe(structplatform_device *pdev) 
{
    ......
    //从设备树展开的资源树的device_node中获取信息  
    if (of_property_read_u32(np, "zzz", &yyy))
     {
        xxx_i2c->speed_mode=xxx;
        xxx_i2c->hs_clock=xxx;
        ......
     }
    /*初始化适配器信息 */  
    strlcpy(i2c_xxx->adap.name,"xxx-i2c",sizeof(i2c_xxx->adap.name));  
    i2c_xxx->adap.owner   = THIS_MODULE;  
    i2c_xxx->adap.algo    = &xxx_i2c_algorithm;  
    i2c_xxx->adap.retries= 2; 
    ......   
    /* 获取资源信息(I/O、中断等),映射寄存器 */  
    res= platform_get_resource(pdev, IORESOURCE_MEM, 0);  
i2c_xxx->ioarea= request_mem_region(res->start,
resource_size(res),  pdev->name);  
    i2c_xxx->regs= ioremap(res->start, resource_size(res));  
......
    /*设置I2C核心需要的信息 */  
    i2c_xxx->adap.algo_data= i2c_xxx;  
    i2c_xxx->adap.dev.parent= &pdev->dev;  
    i2c_xxx->adap.dev.of_node = np 
    ......
    /*申请中断 */  
    i2c_xxx->irq= ret = platform_get_irq(pdev, 0);  
    ret= request_irq(i2c_xxx->irq, s3c24xx_i2c_irq, 0,  
                                  dev_name(&pdev->dev), i2c_xxx);  
    ......
    /*注册I2C控制器 */  
    ret = i2c_add_adapter(&i2c_xxx->adap); 
......
    /* 注册I2C适配器 */  
    ret= i2c_add_numbered_adapter(&i2c_xxx->adap);  
......
    /* 设置驱动数据 */
    platform_set_drvdata(pdev, i2c);
    ......
}

2.5 总线驱动remove函数

static int i2c_msm_remove(struct platform_device *pdev)
{
    struct i2c_msm_ctrl *ctrl = platform_get_drvdata(pdev);

//确保注销总线驱动前数据发送完毕
    mutex_lock(&ctrl->xfer.mtx);
    ctrl->pwr_state = I2C_MSM_PM_SYS_SUSPENDED;
    pm_runtime_disable(ctrl->dev); //禁用pm管理相关操作
    i2c_msm_frmwrk_unreg(ctrl);     //注销adapter
    mutex_unlock(&ctrl->xfer.mtx);
    mutex_destroy(&ctrl->xfer.mtx);

    i2c_msm_dma_teardown(ctrl);
    i2c_msm_dbgfs_teardown(ctrl);
    i2c_msm_rsrcs_irq_teardown(ctrl);  //释放中断资源
    i2c_msm_rsrcs_clk_teardown(ctrl);  //释放时钟资源
    i2c_msm_rsrcs_mem_teardown(ctrl);  //释放IO内存
    i2x_msm_blk_free_cache(ctrl);       //释放cache
return 0;
}

2.6 algorithm通信方法

static const struct i2c_algorithm i2c_msm_frmwrk_algrtm = {
.master_xfer	 = i2c_msm_frmwrk_xfer, //通信方法
	.functionality = i2c_msm_frmwrk_func, //检测通信方法支持的功能或协议
};

2.7 xfer通信方法

master_xfer()函数实现总线上数据传输,与具体的适配器有关,其实现模板如下。

static int i2c_adapter_xxx_xfer(structi2c_adapter *adap, struct i2c_msg *msgs, int num)  
{  
    ......  
    for (i = 0; i < num; i++) {  
        i2c_adapter_xxx_start(); 	/*产生开始位START*/ 
        
if (msgs[i]->flags & I2C_M_RD) {    /*若是读取消息*/  
            /*发送从机设备读地址*/
i2c_adapter_xxx_setaddr((msg->addr << 1) | 1);
            i2c_adapter_xxx_wait_ack();   /*获得从机设备的ACK*/  
            /*读取len长度的数据到buf中*/
            i2c_adapter_xxx_readbytes(msgs[i]->buf,msgs[i]->len);    
        } else {    //若是写消息
            i2c_adapter_xxx_setaddr(msg->addr << 1);  //发送从机设备写地址
            i2c_adapter_xxx_wait_ack();  /*获得从机设备的ACK*/
            i2c_adapter_xxx_writebytes(msgs[i]->buf, msgs[i]->len);//写
        }  
    }  
    i2c_adapter_xxx_stop(); 	/*产生停止位STOP*/  
}

函数模板中的i2c_adapter_xxx_start()、i2c_adapter_xxx_setaddr()等函数用于完成适配器的底层硬件操作,与I2C适配器和CPU的具体硬件直接相关,需要由工程师根据芯片的datasheet来实现。

2.8 功能检测函数func

static u32 i2c_msm_frmwrk_func(struct i2c_adapter *adap)
{
return I2C_FUNC_I2C | (I2C_FUNC_SMBUS_EMUL & 
~ I2C_FUNC_SMBUS_QUICK);
}

返回与自己选用的协议相关的宏(数值)。这些宏是在内核中定义好的,可直接用。

2.9 总线驱动模块初始化、卸载函数

static int i2c_msm_init(void)
{
    return platform_driver_register(&i2c_msm_driver);
}
arch_initcall(i2c_msm_init);

static void i2c_msm_exit(void)
{
    platform_driver_unregister(&i2c_msm_driver);
}
module_exit(i2c_msm_exit);

3.I2C设备驱动bma2x2

3.1 定义设备驱动i2c_driver

static struct i2c_driver bma2x2_driver = {
    .driver = {
        .owner  = THIS_MODULE,
        .name   = SENSOR_NAME,
        .of_match_table = bma2x2_of_match,
    },
    .suspend    = bma2x2_suspend,
    .resume     = bma2x2_resume,
    .id_table   = bma2x2_id,
    .probe      = bma2x2_probe,
    .remove     = bma2x2_remove,
    .shutdown   = bma2x2_shutdown,
};

3.2 设备树匹配

static const struct of_device_id bma2x2_of_match[] = {
    { .compatible = "vendor, SENSOR_NAME", },
    { },
};

3.3 设备驱动probe函数

static int bma2x2_probe(struct i2c_client *client,
                             const struct i2c_device_id *id) 
{   
if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) 
{
    		adap->algo->functionality(); //总线驱动部分
    		if (client->dev.of_node) {
    		//省略了:利用client->dev初始化bma2x2_data数据
    		//从设备树中获取相关资源信息如gpio、中断(具体看dts文件)
    		err = bma2x2_parse_dt(&client->dev, pdata);
    		}
    } else {
    //若dts中不存在数据,获取平台数据
    pdata = client->dev.platform_data;
    }
    err = bma2x2_power_ctl(data, true);//电源管理控制
    if (bma2x2_soft_reset(client) < 0)//重启sensors
    bma2x2_check_chip_id(client, data);/* read and check chip id */
    bma2x2_open_init(client, data);  //设置bandwidth、range(会在sysfs中留有接口)
    err = bma2x2_pinctrl_init(data);//根据dts获取的信息初始化pinctrl   
    /* check interrupt feature enable state */
    if ((pdata->use_int2 && (!BMA2x2_IS_INT2_ENABLED())) ||
         (!pdata->use_int2 && (!BMA2x2_IS_INT1_ENABLED()))) {
        ......
    }
    //初始化GPIO为中断引脚
    if (pdata->use_int2) {
            data->int_flag = pdata->int2_flag;
            err = bma2x2_get_interrupt_gpio(data,
                    pdata->gpio_int2);
    } else {
            data->int_flag = pdata->int1_flag;
            err = bma2x2_get_interrupt_gpio(data,
                    pdata->gpio_int1);
    }
    //申请中断号
    err = request_irq(data->IRQ, bma2x2_irq_handler,
                         data->int_flag, "bma2x2", data);
    //申请input device
    dev = devm_input_allocate_device(&client->dev);
    //申请input interrupt device
    dev_interrupt = devm_input_allocate_device(&client->dev);

    dev->name = SENSOR_NAME;
    dev->id.bustype = BUS_I2C;
    input_set_capability(dev, EV_ABS, ABS_MISC);
    input_set_abs_params(dev, ABS_X, ABSMIN, ABSMAX, 0, 0);
    input_set_abs_params(dev, ABS_Y, ABSMIN, ABSMAX, 0, 0);
    input_set_abs_params(dev, ABS_Z, ABSMIN, ABSMAX, 0, 0);

    input_set_drvdata(dev, data);
    
    err = input_register_device(dev);//注册input设备
    
    dev_interrupt->name = "bma_interrupt";
    dev_interrupt->id.bustype = BUS_I2C;
    input_set_capability(dev_interrupt, EV_REL,
                             SLOW_NO_MOTION_INTERRUPT);
    ......
    input_set_capability(dev_interrupt, EV_ABS,
        FLAT_INTERRUPT);
    input_set_drvdata(dev_interrupt, data);
//注册input interrupt设备
    err = input_register_device(dev_interrupt); 
    
    data->dev_interrupt = dev_interrupt;
    data->input = dev;

    return 0;
    ......  //error处理
}

3.4 设备驱动模块初始化、卸载函数

static int __init BMA2X2_init(void)

{
    pr_crit_once("NWY %s: test for debug.\n", __FUNCTION__);
    return i2c_add_driver(&bma2x2_driver);
}
module_init(BMA2X2_init);

static void __exit BMA2X2_exit(void)
{
    i2c_del_driver(&bma2x2_driver);
}
module_exit(BMA2X2_exit);

4.初始化匹配流程分析

在内核中增加log,查看打印信息可知:
(1)平台总线接收到78b6000.i2c平台设备的注册后,调用总线驱动的probe()函数,初始化i2c控制器对象adapter;注册adapter到i2c总线,添加到其设备链表中;匹配关联总线驱动。
(2)注册设备驱动bma2x2。
(3)调用设备驱动bma2x2_probe()函数,在注册i2c_client前会初始化i2c_client->adap,因此将i2c_client和adapter关联起来,注册驱动到i2c总线时,匹配关联绑定i2c_client,此时i2c_client和i2c_driver关联起来。

5.小结

总线驱动platform_driver:msm_i2c_ driver通过platform_bus与设备树i2c-4节点进行匹配,完成适配器i2c_adapter的实现,具体是在总线驱动的probe()函数中通过平台设备信息初始化adapter,并注册adapter;
设备驱动i2c_driver:bna2x2_driver通过i2c_bus与设备树i2c-4节点下的子节点进行匹配,完成设备i2c_client的实现。注意,adapter和i2c_client都被放到i2c总线的设备链表中。四者联系的总流程:
(1)内核初始化platform_bus,具体是在driver_init()中,比任何xxx_initcall都要早;
(2)初始化i2c_bus(postcore_initcall,等级2);
(3)展开设备树成各个平台设备信息,具体是调用等级为3的arch_initcall中的customize_machine()函数,再调用各个平台的init_machine()函数实现;
(4)注册总线驱动xxx_i2c_driver(等级4的subsys_initcall或等级6的module_init),遍历platform_bus中设备链表的每个平台设备(dts展开得到),以找到与该驱动匹配的设备,若找到则调用总线驱动的probe()函数,即xxx_i2c_driver->probe(),在此通过找到的平台设备的信息初始化adapter设备的信息,然后调用了i2c_add_adapter()函数;
(5)在i2c_add_adapter()函数中,调用device_register()函数对adapter设备进行注册,添加到i2c总线的设备链表中进行管理,并匹配、关联、绑定对应总线驱动。此时i2c_adapter与总线驱动联系起来。
(6)其后遍历i2c_bus上的i2c_driver链表,都调用__process_new_adapter()函数,其又调用了i2c_new_device()函数,使得i2c_driver去探测并实例化注册i2c_client,在注册i2c_client前会初始化i2c_client->adap,因此将i2c_client和adapter关联起来;在注册i2c_client时会遍历i2c_bus上驱动链表,匹配、关联、绑定合适的i2c_driver,此时i2c_driver和i2c_client联系起来;若此时i2c_driver仍未注册,则等待其注册时由i2c_driver匹配i2c_client。

小结:所有的联系始于总线驱动的注册,关键在于i2c_add_adapter()函数,以实例化i2c_client为目的,先联系adapter,再联系i2c_driver。

  • 1
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值