i2c子系统深入理解(二)

i2c子系统整理的图

i2c子系统整体概览参考上图,可以整理上看清i2c子系统的脉络框架。下面后部分代码跟踪,或补充各个细节的地方。

代码补充

这里是不使用设备树情况下,设备信息的代码

	static struct i2c_board_info mini2440_i2c_devs[] __initdata = {                                               
		{
			I2C_BOARD_INFO("24c08", 0x50),
			.platform_data = &at24c08,
		},
	};

	struct platform_device s3c_device_rtc = {
		.name       = "s3c2410-rtc",                                                                              
		.id     = -1,
		.num_resources  = ARRAY_SIZE(s3c_rtc_resource),
		.resource   = s3c_rtc_resource,
	};
	
	struct platform_device s3c_device_i2c0 = {                                                                    
		.name       = "s3c2410-i2c",
		.id     = 0,
		.num_resources  = ARRAY_SIZE(s3c_i2c0_resource),
		.resource   = s3c_i2c0_resource,
	}; 

	static struct platform_device *mini2440_devices[] __initdata = {
		&s3c_device_ohci,
		&s3c_device_wdt,
		&s3c_device_i2c0,
		&s3c_device_rtc,
		...
	};
		__init mini2440_init(void)
	{
		i2c_register_board_info(0, mini2440_i2c_devs,
            ARRAY_SIZE(mini2440_i2c_devs));

		platform_add_devices(mini2440_devices, ARRAY_SIZE(mini2440_devices));
	}

注意这里为什么,24c08使用的是i2c_board_info ,而rtc和i2c适配器使用的都是platform_device 。原因是24c08使用了是i2c子系统,rtc和i2c使用的platform子系统。
一下代码中可以看出,以海思的i2c适配器为例,代码在busses目录中:


struct platform_device hi_i2c0_device = {
    .name           = HI_I2C,
    .id             = 0,
    .resource       = hi_i2c0_resources,
    .num_resources  = ARRAY_SIZE(hi_i2c0_resources),
    .dev            = {
        .platform_data  = &hi_i2c0_platform_data,
    }
};

static struct platform_device *hi_i2c_devices[] __initdata = {
    &hi_i2c0_device,
};

static int __init hi_i2c_module_init(void)
{
    int ret;


    ret = platform_add_devices(hi_i2c_devices, ARRAY_SIZE(hi_i2c_devices));
    if (ret) {
        hi_err("i2c device register failed!\n");
        return ret;
    }

    ret = platform_driver_register(&hi_i2c_driver);
    if (ret) {
        platform_device_unregister(&hi_i2c0_device);
        hi_err("i2c driver register failed!\n");
        return ret;
    }

    return ret;
}

这里可以看出来i2c适配器的驱动使用的platform设备,尤其需要关心的参数是id号,后面i2c设备驱动,需要匹配i2c适配器,需要使用这个id,单个i2c不需要考虑,多个i2c需要关心,不要对应错啦。
继续看i2c_dev.c这里为i2c适配器,创建了,设备节点:


static int i2cdev_attach_adapter(struct device *dev, void *dummy)
{
    struct i2c_adapter *adap;
    struct i2c_dev *i2c_dev;
    int res;
    adap = to_i2c_adapter(dev);

    i2c_dev = get_free_i2c_dev(adap);

    /* register this i2c device with the driver core */
    i2c_dev->dev = device_create(i2c_dev_class, &adap->dev,
                     MKDEV(I2C_MAJOR, adap->nr), NULL,
                     "i2c-%d", adap->nr);//设备节点的序号
}
//这里提供了file_operation的接口
static const struct file_operations i2cdev_fops = {                                                           
    .owner      = THIS_MODULE,
    .llseek     = no_llseek,
    .read       = i2cdev_read,
    .write      = i2cdev_write,
    .unlocked_ioctl = i2cdev_ioctl,
    .open       = i2cdev_open,
    .release    = i2cdev_release,
};

static int __init i2c_dev_init(void)
{
    int res;
    res = register_chrdev(I2C_MAJOR, "i2c", &i2cdev_fops);
    if (res)
        goto out;
    res = bus_register_notifier(&i2c_bus_type, &i2cdev_notifier);
    if (res)
        goto out_unreg_class;

    /* Bind to already existing adapters right away */
    i2c_for_each_dev(NULL, i2cdev_attach_adapter);//遍历创建设备节点

    return 0;
    }

这里贴出的部分代码详细介绍了,/dev/i2c-x,节点创建的原理,过程。
接下来看一下i2c_core.c函数:

struct bus_type i2c_bus_type = {
    .name       = "i2c",
    .match      = i2c_device_match,
    .probe      = i2c_device_probe,
    .remove     = i2c_device_remove,
    .shutdown   = i2c_device_shutdown,
    .pm     = &i2c_device_pm_ops,
}; 
static int __init i2c_init(void)
{
    int retval;                                                                                               
    retval = bus_register(&i2c_bus_type);
}

这里主要是注册了i2c总线
下面在看一下,i2c_core.c提供给设备驱动的接口:
首先看驱动代码,以at24.c为例

static struct i2c_driver at24_driver = {
    .driver = {               
        .name = "at24",       
        .owner = THIS_MODULE, 
    },                        
    .probe = at24_probe,      
    .remove = at24_remove,                                                                                    
    .id_table = at24_ids,     
};

static int __init at24_init(void)
{
    if (!io_limit) {          
        pr_err("at24: io_limit must not be 0!\n");
        return -EINVAL;
    }  

    io_limit = rounddown_pow_of_two(io_limit);
    return i2c_add_driver(&at24_driver);
}

可以看出主要调用的是i2c_add_driver

#define i2c_add_driver(driver) \                                                                              
    i2c_register_driver(THIS_MODULE, driver)
int i2c_register_driver(struct module *owner, struct i2c_driver *driver)                                      
{                                                               
    /* When registration returns, the driver core
     * will have called probe() for all matching-but-unbound devices.                                         
     */
    res = driver_register(&driver->driver);                                                                                                  

    INIT_LIST_HEAD(&driver->clients);
    /* Walk the adapters that are already present */
    i2c_for_each_dev(driver, __process_new_driver);                                                           

    return 0;
}

最终调用如下:

	i2c_register_driver
		__process_new_driver
			i2c_do_add_adapter
				{
					i2c_detect(adap, driver);
					if (driver->attach_adapter)
				}
				i2c_detect
					i2c_detect_address
						err = driver->detect(temp_client, &info);
						if (err) { 
							return err == -ENODEV ? 0 : err;
						}
						i2c_new_device(adapter, &info);
							device_register

也是是用了,device_register注册驱动。
至于i2c_register_board_info,比较简单,只是将板级信息放到了链表中。

应用程序访问,驱动的接口最终都是调用i2c_transfer,

	i2c_transfer
		__i2c_transfer
			ret = adap->algo->master_xfer(adap, msgs, num)

继续看一下适配器,算法是怎样组织的:

	static const struct i2c_algorithm hi_i2c_algo = {
		.master_xfer    = hi_i2c_xfer,
		.functionality  = hi_i2c_func, 
	};
	
	hi_i2c_probe
	{
		mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
		adap->algo = &hi_i2c_algo;
		adap->nr = pdev->id;
		errorcode = i2c_add_numbered_adapter(adap); 
	}
		
	i2c_module_init
	{
		ret = platform_add_devices(hi_i2c_devices, ARRAY_SIZE(hi_i2c_devices));
		ret = platform_driver_register(&hi_i2c_driver);
	}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值