0.1 关于i2c驱动上的一些术语自己理解
i2c_adapter --> i2c 总线控制器 (板子上有5个i2c总线控制器,即有5个adapter)
i2c_algorithm --> i2c 总线控制器的核心通信算法数据传输方法 (1个algroithm)
i2c_client --> 挂在i2c总线上的一个个设备 (如:bma2x2, sp0a19)
0.2 i2c控制器的目录结构
cong@ubuntu:/rk/rk3188/kernel/drivers/i2c$ tree
├── busses
│ ├── i2c-rk30-adapter.c
│ ├── i2c-rk30.c
│ └── i2c-rk30.h
├── i2c-boardinfo.c
├── i2c-core.c
├── i2c-core.h
├── i2c-dev.c
├── i2c-mux.c
└── i2c-smbus.c
i2c控制器的初始化包括以下三个方面:
a. i2c-rk30.c --> rk30_i2c_probe
这个是rk3188中i2c控制器的初始化,主要是形成一个i2c_adapter结构体并与i2c_algorithm关联起来
b. i2c-dev.c --> i2c_dev_init --> i2cdev_attach_adapter
c. i2c-rk30.c --> i2c_detect_rk610
一. i2c控制器的初始化
1. i2c 控制器硬件设备的定义及注册过程
在arch/arm/mach-rk30/devices.c中
一共定义了5个i2c的设备
- static struct rk30_i2c_platform_data default_i2c0_data = {
- .bus_num = 0, //0-4 共5个adapter
- .is_div_from_arm = 1,
- .adap_type = I2C0_ADAP_TYPE,
- .sda_mode = I2C0_SDA,
- .scl_mode = I2C0_SCL,
- };
- static struct resource resources_i2c0[] = {
- {
- .start = IRQ_I2C0,
- .end = IRQ_I2C0,
- .flags = IORESOURCE_IRQ,
- },
- {
- .start = I2C0_START,
- .end = I2C0_END,
- .flags = IORESOURCE_MEM,
- },
- };
- static struct platform_device device_i2c0 = {
- .name = "rk30_i2c",
- .id = 0, //id从0-4
- .num_resources = ARRAY_SIZE(resources_i2c0),
- .resource = resources_i2c0,
- .dev = {
- .platform_data = &default_i2c0_data,
- },
- }
2. i2c 驱动器驱动的初始化
在drivers/i2c/busses/i2c-rk30.c中
- static struct platform_driver rk30_i2c_driver = {
- .probe = rk30_i2c_probe,
- .remove = rk30_i2c_remove,
- .driver = {
- .owner = THIS_MODULE,
- .name = "rk30_i2c", //这个name跟i2c_devices的name是一样的
- .pm = rk30_DEV_PM_OPS,
- },
- };
- static int __init i2c_adap_init(void)
- {
- return platform_driver_register(&rk30_i2c_driver);
- }
- subsys_initcall(i2c_adap_init);
这儿一共是5个i2c 设备,所以这个rk30_i2c_probe要调用5次
- 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;
- //读取i2c dvices中的读取
- pdata = pdev->dev.platform_data;
- i2c = kzalloc(sizeof(struct rk30_i2c), GFP_KERNEL);
- i2c->con_base = (void __iomem *)GRF_I2C_CON_BASE;
- i2c_adap_sel(i2c, pdata->bus_num, pdata->adap_type);
- i2c->sda_mode = pdata->sda_mode;
- i2c->scl_mode = pdata->scl_mode;
-
- //初始化结构体i2c
- strlcpy(i2c->adap.name, "rk30_i2c", sizeof(i2c->adap.name));
- i2c->adap.owner = THIS_MODULE;
- 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);
- //使能i2c时钟
- i2c->dev = &pdev->dev;
- i2c->clk = clk_get(&pdev->dev, "i2c");
- clk_enable(i2c->clk);
- //ioreamp i2c控制器寄存器
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- i2c->ioarea = request_mem_region(res->start, resource_size(res), pdev->name);
- i2c->regs = ioremap(res->start, resource_size(res));
- i2c->adap.algo_data = i2c;
- i2c->adap.dev.parent = &pdev->dev;
- i2c->adap.nr = pdata->bus_num;
-
- //将i2c adapter与i2c algorithm关联起来
- i2c_add_rk30_adapter(&i2c->adap);
- --> i2c_add_numbered_adapter
- -->i2c_register_adapter
- //此处设置了adapter的name为:i2c-0,i2c-1,i2c-2 ..
- //然后通过 device_register创建了设备结点 /dev/i2c-0 /dev-i2c-1 ..
- //class_create 是在 drivers/i2c/i2c_dev.c中
- --> i2c_scan_static_board_info
- //将i2c_board_list上的设备用i2c_new_devices添加到i2c总线上去.
- //这也年出为什么i2c_client的注册一定要先于i2c_adapter的注册,
- //因为只有i2c_client都注册过了,i2c_adapter才可以在注册时搜到i2c_client.//申请i2c中断,因为每次进入probe时pdev都不一样,所以这儿的中断号也不会重复
- static void i2c_scan_static_board_info(struct i2c_adapter *adapter)
- {
- struct i2c_devinfo *devinfo;
- down_read(&__i2c_board_lock);
- list_for_each_entry(devinfo, &__i2c_board_list, list) {
- //dbmsg("boad_info.type=%s", devinfo->board_info.type);
- //将i2c_board_list上的设备用i2c_new_devices添加到i2c总线上去.
- 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);
- }
- static void i2c_scan_static_board_info(struct i2c_adapter *adapter)
- i2c->irq = ret = platform_get_irq(pdev, 0);
- request_irq(i2c->irq, i2c->i2c_irq, IRQF_DISABLED, dev_name(&pdev->dev), i2c);
- ret = rk30_i2c_register_cpufreq(i2c);
- platform_set_drvdata(pdev, i2c);
- i2c->is_div_from_arm[i2c->adap.nr] = pdata->is_div_from_arm;
- i2c->i2c_init_hw(i2c, 100 * 1000);
- i2c_max_adap++;
- return 0;
- }
二.I2c设备的初始化
2.1 i2c设备的静态注册
注册过程如下:
a. 初始化 i2c_board_info结构体,即声明i2c_client
b. 调用i2c_register_board_info将i2c_board_info结构体添加到i2c_board_list中
c. 在i2c_add_adapter的最后,即添加完成i2c_adapter之后对i2c_board_list进行遍历,将找到的i2c_client添加
a.定义i2c_board_info结构体
在arch/arm/mach-rk3188/board-rk3188-ds1006h.c中,
- static struct i2c_board_info __initdata i2c0_info[] = {
- {
- .type = "gs_mma8452",
- .addr = 0x1d,
- .flags = 0,
- .irq = MMA8452_INT_PIN,
- .platform_data = &mma8452_info,
- },
- }
在arch/arm/mach-rk3188/board-rk3188-ds1006h.c中
machine_rk30_board_init //在平台的初始化函数中调用register
--> rk30_i2c_register_board_info
- static void __init rk30_i2c_register_board_info(void)
- {
- i2c_register_board_info(0, i2c0_info, ARRAY_SIZE(i2c0_info)); i2c_register_board_info(1, i2c1_info, ARRAY_SIZE(i2c1_info));
- int __init i2c_register_board_info(int busnum, struct i2c_board_info const *info, unsigned len)
- {
- //这个函数的作用就是把定义的i2c_board_info添加到i2c_board_list中去
- for (status = 0; len; len--, info++) {
- struct i2c_devinfo *devinfo;
- devinfo = kzalloc(sizeof(*devinfo), GFP_KERNEL);
- devinfo->busnum = busnum;
- devinfo->board_info = *info;
- list_add_tail(&devinfo->list, &__i2c_board_list);
- }
- return status;
- }
- int __init i2c_register_board_info(int busnum, struct i2c_board_info const *info, unsigned len)
- i2c_register_board_info(2, i2c2_info, ARRAY_SIZE(i2c2_info));
- i2c_register_board_info(3, i2c3_info, ARRAY_SIZE(i2c3_info));
- i2c_register_board_info(4, i2c4_info, ARRAY_SIZE(i2c4_info));
- i2c_register_board_info(5, i2c_gpio_info, ARRAY_SIZE(i2c_gpio_info));
- }
这一步要先于i2c_adapter的注册,因为只有i2c_client都注册过了,i2c_adapter才可以在注册时搜到i2c_client.
c. 调用i2c_new_device添加i2c_client
然后在i2c_adapter的注册最后,调用i2c_scan_static_board_info遍历i2c_board_list添加i2c_client.
- static int i2c_register_adapter(struct i2c_adapter *adap)
- {
- rt_mutex_init(&adap->bus_lock);
- mutex_init(&adap->userspace_clients_lock);
- INIT_LIST_HEAD(&adap->userspace_clients);
- dev_set_name(&adap->dev, "i2c-%d", adap->nr);
- adap->dev.bus = &i2c_bus_type;
- adap->dev.type = &i2c_adapter_type;
- res = device_register(&adap->dev); //注册完i2c_adapter之后
- if (adap->nr < __i2c_first_dynamic_bus_num)
- i2c_scan_static_board_info(adap); //对i2c_register_board_info添加到i2c_board_list的设备
- //调用i2c_new_device添加设备
- mutex_lock(&core_lock);
- bus_for_each_drv(&i2c_bus_type, NULL, adap, __process_new_adapter);
- mutex_unlock(&core_lock);
- return 0;
- }
- static void i2c_scan_static_board_info(struct i2c_adapter *adapter)
- {
- struct i2c_devinfo *devinfo;
- 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);
- }
2.2 i2c设备的动态注册
一个问题: 不知道camera 的 i2c 设备是哪儿定义的.
a. 我在看sp0a19 camera驱动的时候发现在i2c0_info, i2c1_info, ... , i2c4_info的时候没有发现sp0a19
b. 在注册i2c 设备的时候 i2c_register_board_info中 ARRAY_SIZE(i2c3_info) =0;
c. 但是在 sp0a19的i2c_driver_probe函数中 adapter->nr==3;
然后在 drivers/media/video/sp0a19.c中的i2c_driver_probe加上dump_stack();发现打印如下
- <5>[ 3.604848] cong:drivers/media/video/sp0a19.c:sensor_probe[2921]: in i2c->probe sp0a19:adapter->nr = 3
- <4>[ 3.604910] [<c043e3b0>] (unwind_backtrace+0x0/0xf8) from [<c06f9a7c>] (sensor_probe+0x38/0x384)
- <4>[ 3.604960] [<c06f9a7c>] (sensor_probe+0x38/0x384) from [<c06ddf74>] (i2c_device_probe+0xc0/0xfc)
- <4>[ 3.605009] [<c06ddf74>] (i2c_device_probe+0xc0/0xfc) from [<c063f5a8>] (driver_probe_device+0x90/0x1ac)
- <4>[ 3.605059] [<c063f5a8>] (driver_probe_device+0x90/0x1ac) from [<c063e384>] (bus_for_each_drv+0x118/0x278)
- <4>[ 3.605108] [<c063e384>] (bus_for_each_drv+0x118/0x278) from [<c063f4d8>] (device_attach+0x94/0xb8)
- <4>[ 3.605153] [<c063f4d8>] (device_attach+0x94/0xb8) from [<c063ed28>] (bus_probe_device+0x24/0x40)
- <4>[ 3.605198] [<c063ed28>] (bus_probe_device+0x24/0x40) from [<c063d2ec>] (device_add+0x4bc/0x588)
- <4>[ 3.605244] [<c063d2ec>] (device_add+0x4bc/0x588) from [<c06dea0c>] (i2c_new_device+0x104/0x1e4)
- <4>[ 3.605294] [<c06dea0c>] (i2c_new_device+0x104/0x1e4) from [<c06f4ae0>] (v4l2_i2c_new_subdev_board+0x22c/0x268)
- <4>[ 3.605348] [<c06f4ae0>] (v4l2_i2c_new_subdev_board+0x22c/0x268) from [<c0700f5c>] (soc_camera_probe+0x2cc/0x8d4)
- <4>[ 3.605401] [<c0700f5c>] (soc_camera_probe+0x2cc/0x8d4) from [<c063f5a8>] (driver_probe_device+0x90/0x1ac)
- <4>[ 3.605449] [<c063f5a8>] (driver_probe_device+0x90/0x1ac) from [<c063e384>] (bus_for_each_drv+0x118/0x278)
- <4>[ 3.605497] [<c063e384>] (bus_for_each_drv+0x118/0x278) from [<c063f4d8>] (device_attach+0x94/0xb8)
- <4>[ 3.605543] [<c063f4d8>] (device_attach+0x94/0xb8) from [<c063ed28>] (bus_probe_device+0x24/0x40)
- <4>[ 3.605587] [<c063ed28>] (bus_probe_device+0x24/0x40) from [<c063d2ec>] (device_add+0x4bc/0x588)
- <4>[ 3.605631] [<c063d2ec>] (device_add+0x4bc/0x588) from [<c070082c>] (soc_camera_host_register+0x268/0x2d0)
- <4>[ 3.605682] [<c070082c>] (soc_camera_host_register+0x268/0x2d0) from [<c0705588>] (rk_camera_probe+0x488/0x7ec)
- <4>[ 3.605733] [<c0705588>] (rk_camera_probe+0x488/0x7ec) from [<c06407c0>] (platform_drv_probe+0x18/0x1c)
- <4>[ 3.605781] [<c06407c0>] (platform_drv_probe+0x18/0x1c) from [<c063f5a8>] (driver_probe_device+0x90/0x1ac)
- <4>[ 3.605829] [<c063f5a8>] (driver_probe_device+0x90/0x1ac) from [<c063f750>] (__driver_attach+0x8c/0x90)
- <4>[ 3.605876] [<c063f750>] (__driver_attach+0x8c/0x90) from [<c063e7b4>] (bus_for_each_dev+0x5c/0x88)
- <4>[ 3.605921] [<c063e7b4>] (bus_for_each_dev+0x5c/0x88) from [<c063ef80>] (bus_add_driver+0x180/0x254)
- <4>[ 3.605967] [<c063ef80>] (bus_add_driver+0x180/0x254) from [<c063fbfc>] (driver_register+0x78/0x13c)
- <4>[ 3.606014] [<c063fbfc>] (driver_register+0x78/0x13c) from [<c0701b98>] (rk_camera_init_async+0x10/0x18)
- <4>[ 3.606061] [<c0701b98>] (rk_camera_init_async+0x10/0x18) from [<c048c8c4>] (kthread+0x80/0x88)
2.2.1 I2C camera 的定义
在arch/arm/plat-rk/rk_camera.c中
- static struct rk29camera_platform_data rk_camera_platform_data = {
- .io_init = rk_sensor_io_init,
- .io_deinit = rk_sensor_io_deinit,
- .iomux = rk_sensor_iomux,
- .sensor_ioctrl = rk_sensor_ioctrl,
- .sensor_register = rk_sensor_register,
- .register_dev = {
- {
- .i2c_cam_info = {
- I2C_BOARD_INFO(SENSOR_NAME_1, CONFIG_SENSOR_IIC_ADDR_1>>1) //sp0a19,
- },
- .device_info = {
- .name = "soc-camera-pdrv",
- .dev = {
- .init_name = SENSOR_DEVICE_NAME_1,
- }
- }
- },
- },
- .register_dev_new = new_camera,
- };
那动态注册时的i2c_register_board_info是如何实现的呢?
在drivers/media/video/soc_camera.c中
- static int soc_camera_probe(struct device *dev)
- {
- if (icl->board_info) {
- soc_camera_init_i2c(icd, icl);
- }
- }
在drivers/media/video/soc_camera.c中
- static int soc_camera_init_i2c(struct soc_camera_device *icd,
- struct soc_camera_link *icl)
- {
- struct i2c_client *client;
- struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
- struct i2c_adapter *adap = i2c_get_adapter(icl->i2c_adapter_id);
- struct v4l2_subdev *subdev;
- icl->board_info->platform_data = icd;
- subdev = v4l2_i2c_new_subdev_board(&ici->v4l2_dev, adap, icl->board_info, NULL);
- client = v4l2_get_subdevdata(subdev);
- dev_set_drvdata(&icd->dev, &client->dev);
- return 0;
- }
soc_camera_probe
--> soc_camera_init_i2c(icd, icl);
--> v4l2_i2c_new_subdev_board
在drivers/media/video/v4l2-common.c中
- struct v4l2_subdev *v4l2_i2c_new_subdev_board(struct v4l2_device *v4l2_dev,
- struct i2c_adapter *adapter, struct i2c_board_info *info,
- const unsigned short *probe_addrs)
- {
- struct v4l2_subdev *sd = NULL;
- struct i2c_client *client;
- request_module(I2C_MODULE_PREFIX "%s", info->type);
- if (info->addr == 0 && probe_addrs)
- client = i2c_new_probed_device(adapter, info, probe_addrs, NULL);
- else
- client = i2c_new_device(adapter, info); //终于又出来i2c_new_device了
-
- sd = i2c_get_clientdata(client);
- v4l2_device_register_subdev(v4l2_dev, sd))
- module_put(client->driver->driver.owner);
- return sd;
- }
- EXPORT_SYMBOL_GPL(v4l2_i2c_new_subdev_board);
三. 传输过程
ret = sensor_read(client, 0x02, &pid);
- static int sensor_read(struct i2c_client *client, u8 reg, u8 *val)
- {