linux i2c驱动

本文深入探讨Linux系统下I2C总线的初始化过程,包括i2c驱动的注册、初始化i2c适配器、创建i2c客户端设备等关键步骤。同时,详细介绍了针对特定芯片的I2C初始化函数和注册过程,以及I2C设备的注册流程。
摘要由CSDN通过智能技术生成

Linux下面有很多设备都使用到了i2c,所以看了一下i2c的驱动,虽然现在理解的也可能还是人力物力的,但至少还是有了一些基本的概念

参考:

http://blog.csdn.net/ylyuanlu/article/details/6705942

http://blog.csdn.net/hongjiujing/article/details/4098547


看下i2c初始化过程:


static int __init i2c_init(void)
{
	int retval;
	LEAVES_DEBUG_FLAG_DRIVER_I2c_BUSSES_I2c_I2c_CORE_FUNCTION_ENTER
	retval = bus_register(&i2c_bus_type);
	if (retval)
		return retval;
#ifdef CONFIG_I2C_COMPAT
	i2c_adapter_compat_class = class_compat_register("i2c-adapter");
	if (!i2c_adapter_compat_class) {
		retval = -ENOMEM;
		goto bus_err;
	}
#endif
	printk("leaves before i2c_add_driver\n");
	retval = i2c_add_driver(&dummy_driver);
	printk("leaves after i2c_add_driver\n");
	if (retval)
		goto class_err;
#ifdef CONFIG_I2C_DEV_RK29
		init_completion(&i2c_dev_complete);
#endif
	LEAVES_DEBUG_FLAG_DRIVER_I2c_BUSSES_I2c_I2c_CORE_FUNCTION_EXIT
	return 0;

class_err:
#ifdef CONFIG_I2C_COMPAT
	class_compat_unregister(i2c_adapter_compat_class);
bus_err:
#endif
	bus_unregister(&i2c_bus_type);
	LEAVES_DEBUG_FLAG_DRIVER_I2c_BUSSES_I2c_I2c_CORE_FUNCTION_EXIT
	return retval;
}

这个函数主要就是注册了一下i2c的bus

2、


这个是针对RockChip芯片3066一款板子的初始化,注册了5个i2c设备

static void __init rk30_init_i2c(void)
{
	printk("leaves Enter %s.\n", __FUNCTION__);
#ifdef CONFIG_I2C0_RK30
	platform_device_register(&device_i2c0);
#endif
#ifdef CONFIG_I2C1_RK30
	platform_device_register(&device_i2c1);
#endif
#ifdef CONFIG_I2C2_RK30
	platform_device_register(&device_i2c2);
#endif
#ifdef CONFIG_I2C3_RK30
	platform_device_register(&device_i2c3);
#endif
#ifdef CONFIG_I2C4_RK30
	platform_device_register(&device_i2c4);
#endif
#ifdef CONFIG_I2C_GPIO_RK30
	platform_device_register(&device_i2c_gpio);
#endif
	printk("leaves Exit %s.\n", __FUNCTION__);
}

3、


这里注册 i2c的驱动

会匹配到上面注册的i2c设备,然后调用rk30_i2c_probe

static struct platform_driver rk30_i2c_driver = {
	.probe		= rk30_i2c_probe,
	.remove		= rk30_i2c_remove,
	.driver		= {
		.owner	= THIS_MODULE,
		.name	= "rk30_i2c",
		.pm	= rk30_DEV_PM_OPS,
	},
};
static int __init i2c_adap_init(void)
{
	int ret = -1;
	LEAVES_DEBUG_FLAG_DRIVER_I2c_BUSSES_I2c_RK30_FUNCTION_ENTER
	ret =  platform_driver_register(&rk30_i2c_driver);
	LEAVES_DEBUG_FLAG_DRIVER_I2c_BUSSES_I2c_RK30_FUNCTION_EXIT
	return ret;
}

4、



rk30_i2c_probe的流程有点小复杂,我们一步步来看

/* rk30_i2c_probe
 *
 * called by the bus driver when a suitable device is found
*/

static int rk30_i2c_probe(struct platform_device *pdev)
{
	struct rk30_i2c *i2c = NULL;
	struct rk30_i2c_platform_data *pdata = NULL;
	struct resource *res;
	int ret;
	LEAVES_DEBUG_FLAG_DRIVER_I2c_BUSSES_I2c_RK30_FUNCTION_ENTER
	pdata = pdev->dev.platform_data;
	if (!pdata) {
		dev_err(&pdev->dev, "no platform data\n");
		return -EINVAL;
	}


	i2c = kzalloc(sizeof(struct rk30_i2c), GFP_KERNEL);
	if (!i2c) {
		dev_err(&pdev->dev, "no memory for state\n");
		return -ENOMEM;
	}
        i2c->con_base = (void __iomem *)GRF_I2C_CON_BASE;
        i2c_adap_sel(i2c, pdata->bus_num, pdata->adap_type);

        if(pdata->io_init)
		pdata->io_init();
        if(pdata->check_idle){
                i2c->check_idle = pdata->check_idle;
        }

	strlcpy(i2c->adap.name, "rk30_i2c", sizeof(i2c->adap.name));
	i2c->adap.owner   = THIS_MODULE; //初始化adapter
	i2c->adap.class   = I2C_CLASS_HWMON | I2C_CLASS_SPD;
	i2c->tx_setup     = TX_SETUP;
	i2c->adap.retries = 2;
        i2c->adap.timeout = msecs_to_jiffies(100);

	spin_lock_init(&i2c->lock);
	init_waitqueue_head(&i2c->wait);
        mutex_init(&i2c->m_lock); 

	/* find the clock and enable it */

	i2c->dev = &pdev->dev;
	i2c->clk = clk_get(&pdev->dev, "i2c");
	if (IS_ERR(i2c->clk)) {
		dev_err(&pdev->dev, "cannot get clock\n");
		ret = -ENOENT;
		goto err_noclk;
	}

	i2c_dbg(&pdev->dev, "clock source %p\n", i2c->clk);

	clk_enable(i2c->clk);

	/* map the registers */

	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);//获取平台资源(I2C)
	if (res == NULL) {
		dev_err(&pdev->dev, "cannot find IO resource\n");
		ret = -ENOENT;
		goto err_get_resource;
	}

    //http://blog.csdn.net/skyflying2012/article/details/8672011
	i2c->ioarea = request_mem_region(res->start, resource_size(res),
					 pdev->name);//申请I/O内存

	if (i2c->ioarea == NULL) {
		dev_err(&pdev->dev, "cannot request IO\n");
		ret = -ENXIO;
		goto err_ioarea;
	}

    //用来将I/O内存资源的物理地址映射到核心虚地址空间。
	i2c->regs = ioremap(res->start, resource_size(res));

	if (i2c->regs == NULL) {
		dev_err(&pdev->dev, "cannot map IO\n");
		ret = -ENXIO;
		goto err_ioremap;
	}

	i2c_dbg(&pdev->dev, "registers %p (%p, %p)\n",
		i2c->regs, i2c->ioarea, res);

	/* setup info block for the i2c core */

	i2c->adap.algo_data = i2c;
	i2c->adap.dev.parent = &pdev->dev;
	i2c->adap.nr = pdata->bus_num;
	printk("pdata->bus_num = %d.\n",pdata->bus_num);
        if(pdata->adap_type == I2C_RK29_ADAP)
                ret = i2c_add_rk29_adapter(&i2c->adap);
        else // I2C_RK30_ADAP
                ret = i2c_add_rk30_adapter(&i2c->adap);//添加i2c adapter

	if (ret < 0) {
		dev_err(&pdev->dev, "failed to add adapter\n");
		goto err_add_adapter;
	}

	/* find the IRQ for this unit (note, this relies on the init call to
	 * ensure no current IRQs pending
	 */

	i2c->irq = ret = platform_get_irq(pdev, 0);//获取中断号
	if (ret <= 0) {
		dev_err(&pdev->dev, "cannot find IRQ\n");
		goto err_get_irq;
	}

	ret = request_irq(i2c->irq, i2c->i2c_irq, IRQF_DISABLED,
			  dev_name(&pdev->dev), i2c);//注册中断服务函数

	if (ret != 0) {
		dev_err(&pdev->dev, "cannot claim IRQ %d\n", i2c->irq);
		goto err_request_irq;
	}

	ret = rk30_i2c_register_cpufreq(i2c);
	if (ret < 0) {
		dev_err(&pdev->dev, "failed to register cpufreq notifier\n");
		goto err_register_cpufreq;
	}

	platform_set_drvdata(pdev, i2c);

        i2c->is_div_from_arm[i2c->adap.nr] = pdata->is_div_from_arm;
        if(i2c->is_div_from_arm[i2c->adap.nr])
                wake_lock_init(&i2c->idlelock[i2c->adap.nr], WAKE_LOCK_IDLE, dev_name(&pdev->dev));

        i2c->i2c_init_hw(i2c, 100 * 1000);
	dev_info(&pdev->dev, "%s: RK30 I2C adapter\n", dev_name(&i2c->adap.dev));
	LEAVES_DEBUG_FLAG_DRIVER_I2c_BUSSES_I2c_RK30_FUNCTION_EXIT
	return 0;
//err_none:
//	rk30_i2c_deregister_cpufreq(i2c);
err_register_cpufreq:
	free_irq(i2c->irq, i2c);
err_request_irq:
err_get_irq:
	i2c_del_adapter(&i2c->adap);
err_add_adapter:
	iounmap(i2c->regs);
err_ioremap:
	kfree(i2c->ioarea);
err_ioarea:
	release_resource(i2c->ioarea);
err_get_resource:
	clk_put(i2c->clk);
err_noclk:
	kfree(i2c);
	LEAVES_DEBUG_FLAG_DRIVER_I2c_BUSSES_I2c_RK30_FUNCTION_EXIT
	return ret;
}

这里主要的就是i2c_add_rk30_adapter


int i2c_add_rk30_adapter(struct i2c_adapter *adap)
{
        int ret = 0;
        struct rk30_i2c *i2c = (struct rk30_i2c *)adap->algo_data;
		LEAVES_DEBUG_FLAG_DRIVER_I2c_BUSSES_I2c_RK30_ADAPTER_FUNCTION_ENTER
        adap->algo = &rk30_i2c_algorithm;

        i2c->i2c_init_hw = &rk30_i2c_init_hw;
        i2c->i2c_set_clk = &rk30_i2c_set_clk;
        i2c->i2c_irq = &rk30_i2c_irq;

        ret = i2c_add_numbered_adapter(adap);//注册I2C adapter驱动
		LEAVES_DEBUG_FLAG_DRIVER_I2c_BUSSES_I2c_RK30_ADAPTER_FUNCTION_EXIT
        return ret;
}

这里赋值了一下传输算法

再来看一下i2c_register_adapter


static int i2c_register_adapter(struct i2c_adapter *adap)
{
	int res = 0;
	LEAVES_DEBUG_FLAG_DRIVER_I2c_BUSSES_I2c_I2c_CORE_FUNCTION_ENTER
	/* Can't register until after driver model init */
	if (unlikely(WARN_ON(!i2c_bus_type.p))) {
		res = -EAGAIN;
		goto out_list;
	}

	/* Sanity checks */
	if (unlikely(adap->name[0] == '\0')) {
		pr_err("i2c-core: Attempt to register an adapter with "
		       "no name!\n");
		return -EINVAL;
	}
	if (unlikely(!adap->algo)) {
		pr_err("i2c-core: Attempt to register adapter '%s' with "
		       "no algo!\n", adap->name);
		return -EINVAL;
	}

	rt_mutex_init(&adap->bus_lock);
	mutex_init(&adap->userspace_clients_lock);
	INIT_LIST_HEAD(&adap->userspace_clients);

	/* Set default timeout to 1 second if not already set */
	if (adap->timeout == 0)
		adap->timeout = HZ;

	dev_set_name(&adap->dev, "i2c-%d", adap->nr);
    printk("adap->dev.kobj.name = %s.\n", adap->dev.kobj.name);
	adap->dev.bus = &i2c_bus_type;
	adap->dev.type = &i2c_adapter_type;
	printk("before device_register.\n");
	res = device_register(&adap->dev);
	printk("after device_register.\n");
	if (res)
		goto out_list;

	dev_dbg(&adap->dev, "adapter [%s] registered\n", adap->name);

#ifdef CONFIG_I2C_COMPAT
	res = class_compat_create_link(i2c_adapter_compat_class, &adap->dev,
				       adap->dev.parent);
	if (res)
		dev_warn(&adap->dev,
			 "Failed to create compatibility class link\n");
#endif
	printk("__i2c_first_dynamic_bus_num = %d.\n", __i2c_first_dynamic_bus_num);
	/* create pre-declared device nodes */
	if (adap->nr < __i2c_first_dynamic_bus_num)
		i2c_scan_static_board_info(adap);

	/* Notify drivers */
	mutex_lock(&core_lock);
    //遍历该总线上所有的driver,执行一次__process_new_adapter
	bus_for_each_drv(&i2c_bus_type, NULL, adap, __process_new_adapter);
	mutex_unlock(&core_lock);
	LEAVES_DEBUG_FLAG_DRIVER_I2c_BUSSES_I2c_I2c_CORE_FUNCTION_EXIT
	return 0;

out_list:
	mutex_lock(&core_lock);
	idr_remove(&i2c_adapter_idr, adap->nr);
	mutex_unlock(&core_lock);
	LEAVES_DEBUG_FLAG_DRIVER_I2c_BUSSES_I2c_I2c_CORE_FUNCTION_EXIT
	return res;
}

i2c_register_adapter主要做了4件事

1、  继续adap初始化

2、  注册adap设备

3、  遍历I2C设备链表,并创建链表上相应总线的设备

4、  遍历该总线上所有的driver,执行一次__process_new_adapter

我们看一下遍历i2c链表过程

//函数中遍历I2C设备链表__i2c_board_list,设备的总线号和adapter的总线号相等,(属于该总线的)
//则使用函数i2c_new_device()创建该设备。
static void i2c_scan_static_board_info(struct i2c_adapter *adapter)//
{
	struct i2c_devinfo	*devinfo;
	LEAVES_DEBUG_FLAG_DRIVER_I2c_BUSSES_I2c_I2c_CORE_FUNCTION_ENTER
	down_read(&__i2c_board_lock);
	list_for_each_entry(devinfo, &__i2c_board_list, list) {
		if (devinfo->busnum == adapter->nr
				&& !i2c_new_device(adapter,
						&devinfo->board_info))
			dev_err(&adapter->dev,
				"Can't create device at 0x%02x\n",
				devinfo->board_info.addr);
	}
	up_read(&__i2c_board_lock);
	LEAVES_DEBUG_FLAG_DRIVER_I2c_BUSSES_I2c_I2c_CORE_FUNCTION_EXIT
}

这里调用i2c_new_device创建相应的i2c client,一个i2c_client结构相当于i2c总线上的一个设备

struct i2c_client *
i2c_new_device(struct i2c_adapter *adap, struct i2c_board_info const *info)
{
	struct i2c_client	*client;
	int			status;
	LEAVES_DEBUG_FLAG_DRIVER_I2c_BUSSES_I2c_I2c_CORE_FUNCTION_ENTER
	client = kzalloc(sizeof *client, GFP_KERNEL);
	if (!client)
		return NULL;

	client->adapter = adap;

	client->dev.platform_data = info->platform_data;

	if (info->archdata)
		client->dev.archdata = *info->archdata;

	client->flags = info->flags;
	client->addr = info->addr; /* 取得I2C器件地址 */
	client->irq = info->irq;
	client->udelay = info->udelay;  // add by kfx

	strlcpy(client->name, info->type, sizeof(client->name));
	/* Check for address validity */
	status = i2c_check_client_addr_validity(client);
	if (status) {
		dev_err(&adap->dev, "Invalid %d-bit I2C address 0x%02hx\n",
			client->flags & I2C_CLIENT_TEN ? 10 : 7, client->addr);
		goto out_err_silent;
	}

	/* Check for address business */
    #if 0
	status = i2c_check_addr_busy(adap, client->addr);
	if (status)
		goto out_err;
    #else
	/* ddl@rock-chips.com : Devices which have some i2c addr can work in same i2c bus, 
	   if devices havn't work at the same time.*/
	status = i2c_check_addr_ex(adap, client->addr);
	if (status != 0)
		dev_err(&adap->dev, "%d i2c clients have been registered at 0x%02x",
			status, client->addr);   
    #endif

	client->dev.parent = &client->adapter->dev;
	client->dev.bus = &i2c_bus_type;
	client->dev.type = &i2c_client_type;
	client->dev.of_node = info->of_node;

    /* ddl@rock-chips.com : Devices which have some i2c addr can work in same i2c bus, 
      if devices havn't work at the same time.*/
    #if 0
    dev_set_name(&client->dev, "%d-%04x", i2c_adapter_id(adap),
    		     client->addr);
    #else
    if (status == 0)
    	dev_set_name(&client->dev, "%d-%04x", i2c_adapter_id(adap),
    		     client->addr);
    else 
        dev_set_name(&client->dev, "%d-%04x-%01x", i2c_adapter_id(adap),
    		     client->addr,status);
    #endif
    printk("device_register before \n");
	status = device_register(&client->dev);
	printk("device_register after \n");
	if (status)
		goto out_err;

	printk( "client [%s] registered with bus id %s\n",
		client->name, dev_name(&client->dev));
	LEAVES_DEBUG_FLAG_DRIVER_I2c_BUSSES_I2c_I2c_CORE_FUNCTION_EXIT
	return client;

out_err:
	dev_err(&adap->dev, "Failed to register i2c client %s at 0x%02x "
		"(%d)\n", client->name, client->addr, status);
out_err_silent:
	kfree(client);
	return NULL;
}

这里调用了device_register注册了相应的i2c设备,如我们后面讲到的陀螺仪在i2c0总线上定义的

static struct i2c_board_info __initdata i2c0_info[] = {	
…
#if defined (CONFIG_GYRO_L3G4200D)
	{
		.type          = "l3g4200d_gryo",
		.addr          = 0x69,
		.flags         = 0,
		.irq           = L3G4200D_INT_PIN,
		.platform_data = &l3g4200d_info,
	},
…
}

这里进行了陀螺设备的注册

__process_new_adapter会调用i2c_do_add_adapter,但此时只有一个dummy_driver,而且他没有定义attach_adapter,所以其实什么事也没有做

 

 

这样,i2c设备初始化就已经完成了,就可以等待注册i2c驱动




评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值