rockchip rk30系列的i2c驱动分析——注意和三星写法有较大不同
来源:互联网 发布:网络不好 英文 编辑:程序博客网 时间:2024/05/21 08:03
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驱动
- rockchip rk30系列的i2c驱动分析——注意和三星写法有较大不同
- I2C驱动情景分析——怎样控制I2C时序
- I2C驱动情景分析——怎样增加I2C设备
- I2C驱动情景分析——怎样增加I2C设备
- I2C驱动情景分析——怎样增加I2C设备
- I2C驱动情景分析——怎样增加I2C设备
- I2C驱动情景分析——怎样增加I2C设备
- I2C驱动情景分析——怎样增加I2C设备
- I2C驱动情景分析——怎样控制I2C时序
- Linux的i2c驱动分析
- Linux I2C驱动分析(一)----I2C架构和总线驱动
- Linux I2C驱动分析(一)----I2C架构和总线驱动
- Linux I2C驱动分析(一)----I2C架构和总线驱动
- Linux I2C驱动分析(一)----I2C架构和总线驱动
- Linux I2C驱动分析(一)----I2C架构和总线驱动
- linux的i2c驱动中的函数和数据结构的分析
- 使用Beaglebone Black的I2C (二)——使用C语言和i2c-dev驱动
- linux驱动基础系列--Linux I2c驱动分析
- 对字符串输出流的认识
- HttpCache in android
- cocos2d-x手机游戏内存优化
- leetcode-Binary Tree Maximum Path Sum
- 输入输出的进制问题
- rockchip rk30系列的i2c驱动分析——注意和三星写法有较大不同
- android--UI开发之--PagerSlidingTabStrip介绍及使用,让ViewPager更绚丽
- 杭电-1102Constructing Roads(最小生成树)
- 工作文档撰写——产品体验报告
- 对输入流 的一些认识
- 【Android】 今日问题记录2015/11/9
- 1031 Hello World for u
- Tiny6410学习之-烧写裸机程序失败问题
- IOS获取图片的方式以及图片的压缩