Chinaunix首页 | 论坛 | 博客
  • 博客访问: 53099
  • 博文数量: 17
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 70
  • 用 户 组: 普通用户
  • 注册时间: 2014-11-09 22:49
文章分类
文章存档

2015年(9)

2014年(8)

我的朋友

分类: 嵌入式

2014-12-13 14:36:45

转载自http://blog.csdn.net/mayaoyao11/article/details/6645990

    看linux  i2c驱动也有一段时间了,由于中间暑假回家一趟搁浅了,回家之前看的忘的一干二净。还是把自己理解的内容写下来,一是下次用的时候便于快速捡起来,二梳理IIC驱动复杂框架,三欢迎大家给我指正,驱动复杂,希望大家互相帮助。
    Linux中I2C体系结构如下图所示(图片来源于网络)。图中用分割线分成了三个层次:用户空间(也就是应用程序),内核(也就是驱动部分)和硬件(也就是实际物理设备,这里就是6410中的i2c控制器和at24xx)。这个够清晰了吧?我们主要研究的是中间那一层。

    中间一层又分为i2c设备驱动、i2c-core层、i2c控制器驱动三部分。其中i2c-core提供了i2c设备驱动和控制器驱动的注册、注销方法。其上三部分Driver、Client、i2c-dev用来描述i2c设备(如at24xx)及其驱动,下面Algorithm、Adapter、及Adapter specific code 用来描述i2c控制器驱动。

    以s3c6410 linux 3.0.1下iic器件 at24xx驱动为例进行分析,主要包含以下文件。
    1、i2c-core.c      实现了I2C的核心功能
    2、i2c-dev.c       实现了I2C控制器设备文件的功能
    3、At24.c          实现了at24xx系列IIC接口设备驱动
    4、i2c-s3c2410.c    实现了6410 IIC控制器驱动
    5、Algos               实现了一些IIC控制器的algorithm
    6、mach-mini6410.c    定义并注册了I2C平台设备

    我们根据系统加载有关I2C模块的顺序进行分析。

    1、文件mach-smdk64106410.c中 MACHINE_START .init_machine 在 arch/arm/kernel/setup.c 中被 customize_machine 调用,放在arch_initcall(customize_machine)中

点击(此处)折叠或打开

  1. MACHINE_START(OK6410, "OK6410")
  2.     /* Maintainer: Ben Dooks <ben-linux@fluff.org> */
  3.     //.phys_io    = S3C_PA_UART & 0xfff00000,
  4.     //.io_pg_offst    = (((u32)S3C_VA_UART) >> 18) & 0xfffc,
  5.     .boot_params    = S3C64XX_PA_SDRAM + 0x100,
  6.     .init_irq    = s3c6410_init_irq,
  7.     .map_io        = smdk6410_map_io,
  8.     .init_machine    = smdk6410_machine_init,
  9.     .timer        = &s3c24xx_timer,
  10. MACHINE_END
    再看看 smdk6410_machine_init(void) 中和 i2c 有关的部分:

点击(此处)折叠或打开

  1. static void __init smdk6410_machine_init(void)
  2. {
  3.     u32 cs1;

  4.     s3c_i2c0_set_platdata(NULL);
  5. #ifdef CONFIG_S3C_DEV_I2C1
  6.     s3c_i2c1_set_platdata(NULL);
  7. #endif
  8.     ...

  9.     i2c_register_board_info(0, i2c_devs0, ARRAY_SIZE(i2c_devs0));
  10.     i2c_register_board_info(1, i2c_devs1, ARRAY_SIZE(i2c_devs1));

  11.     ...
  12.     platform_add_devices(smdk6410_devices, ARRAY_SIZE(smdk6410_devices))
  13. }
    platform_add_devices  添加平台设备,并向系统注册平台设备,包括s3c_device_i2c0。

点击(此处)折叠或打开

  1. static struct platform_device *smdk6410_devices[] __initdata = {
  2.     ...
  3.     &s3c_device_i2c0,
  4. #ifdef CONFIG_S3C_DEV_I2C1
  5.     &s3c_device_i2c1,
  6. #endif
  7.     &s3c_device_fb,
  8.     ...
  9. };
    其中 s3c_device_i2c0定义在/arch/arm/plat-samsung/dev-i2c0.c 中:

点击(此处)折叠或打开

  1. struct platform_device s3c_device_i2c0 = {
  2.     .name         = "s3c2410-i2c",        /* 此处的设备名称 s3c2410-i2c */
  3. #ifdef CONFIG_S3C_DEV_I2C1
  4.     .id         = 0,
  5. #else
  6.     .id         = -1,
  7. #endif
  8.     .num_resources     = ARRAY_SIZE(s3c_i2c_resource),
  9.     .resource     = s3c_i2c_resource,
  10. };

    2.到系统注册了platform_device 我们会想到 platform_driver 在何处注册,只有设备没有驱动也是不行滴。
    在 /drivers/i2c/busses/i2c-s3c2410.c 中我们看到了 i2c 平台驱动注册  platform_driver_register(&s3c24xx_i2c_driver)。由 subsys_initcall(i2c_adap_s3c_init)我们可以看出此模块是系统启动时自己加载的。

点击(此处)折叠或打开

  1. static struct platform_driver s3c24xx_i2c_driver = {
  2.     .probe        = s3c24xx_i2c_probe,
  3.     .remove        = s3c24xx_i2c_remove,
  4.     .id_table    = s3c24xx_driver_ids,    /* 此处要注意 */
  5.     .driver        = {
  6.         .owner    = THIS_MODULE,
  7.         .name    = "s3c-i2c",             /* 此处的驱动名称 s3c-i2c 与上面的设备名称 s3c2410-i2c 不一致 */
  8.         .pm    = S3C24XX_DEV_PM_OPS,
  9.     },
  10. };

  11. static int __init i2c_adap_s3c_init(void)
  12. {
  13.     return platform_driver_register(&s3c24xx_i2c_driver);
  14. }

  15. subsys_initcall(i2c_adap_s3c_init);
    在这里发现s3c_device_i2c0.name 和 s3c24xx_i2c_driver.driver.name 不一致,设备和驱动如何绑定的呢?

    3.接着上次分析s3c24xx_i2c_driver 中有一个成员 .id_table = s3c24xx_driver_ids 这个好像以前没见到过,孤陋寡闻了哈。先来看看是如何定义的 

点击(此处)折叠或打开

  1. static struct platform_device_id s3c24xx_driver_ids[] = {
  2.     {
  3.         .name        = "s3c2410-i2c",
  4.         .driver_data    = TYPE_S3C2410,
  5.     }, {
  6.         .name        = "s3c2440-i2c",
  7.         .driver_data    = TYPE_S3C2440,
  8.     }, { },
  9. };
    细心会发现这里的 .name 和 s3c_device_i2c0 中的成员 .name 一致。
    我们来具体追踪 平台驱动注册时是如何查找总线上的设备并与其进行匹配并绑定的。 
    platform_driver_register(&s3c24xx_i2c_driver)
        --> driver_register(&drv->driver)
            --> bus_add_driver(drv)
                --> driver_attach(drv)
                    --> bus_for_each_dev(drv->bus, NULL, drv, __driver_attach)
                        --> driver_match_device(drv, dev)

点击(此处)折叠或打开

  1. static int __driver_attach(struct device *dev, void *data)
  2. {
  3.     struct device_driver *drv = data;

  4.     if (!driver_match_device(drv, dev)) /* 对添加的驱动进行匹配 */
  5.         return 0;

  6.     if (dev->parent)    /* Needed for USB */
  7.         device_lock(dev->parent);
  8.     device_lock(dev);
  9.     if (!dev->driver)
  10.         driver_probe_device(drv, dev);
  11.     device_unlock(dev);
  12.     if (dev->parent)
  13.         device_unlock(dev->parent);

  14.     return 0;

点击(此处)折叠或打开

  1. static inline int driver_match_device(struct device_driver *drvstruct device *dev)
  2. {
  3.     return drv->bus->match ? drv->bus->match(dev, drv) : 1;
  4. }
    继续分析的话就要知道 drv->bus->match 何时进行的赋值。通过以下两段代码我们知道 drv->bus->match 调用了platform_bus_type中的 platform_match。

点击(此处)折叠或打开

  1. int platform_driver_register(struct platform_driver *drv)
  2. {
  3.     drv->driver.bus = &platform_bus_type;
  4.     if (drv->probe)
  5.         drv->driver.probe = platform_drv_probe;
  6.     if (drv->remove)
  7.         drv->driver.remove = platform_drv_remove;
  8.     if (drv->shutdown)
  9.         drv->driver.shutdown = platform_drv_shutdown;

  10.     return driver_register(&drv->driver);
  11. }

点击(此处)折叠或打开

  1. struct bus_type platform_bus_type = {
  2.     .name        = "platform",
  3.     .dev_attrs    = platform_dev_attrs,
  4.     .match        = platform_match,
  5.     .uevent        = platform_uevent,
  6.     .pm        = &platform_dev_pm_ops,
  7. };
    我们看platform_bus_type中的 platform_match代码:

点击(此处)折叠或打开

  1. static int platform_match(struct device *dev, struct device_driver *drv)
  2. {
  3.     struct platform_device *pdev = to_platform_device(dev);
  4.     struct platform_driver *pdrv = to_platform_driver(drv);

  5.     /* Attempt an OF style match first */
  6.     if (of_driver_match_device(dev, drv))
  7.         return 1;

  8.     /* Then try to match against the id table */
  9.     if (pdrv->id_table)
  10.         return platform_match_id(pdrv->id_table, pdev) != NULL;

  11.     /* fall-back to driver name match */
  12.     return (strcmp(pdev->name, drv->name) == 0);
  13. }
    继续

点击(此处)折叠或打开

  1. static const struct platform_device_id *platform_match_id(const struct platform_device_id *idstruct platform_device *pdev)
  2. {
  3.     while (id->name[0]) {
  4.         if (strcmp(pdev->name, id->name) == 0) {
  5.             pdev->id_entry = id;
  6.             return id;
  7.         }
  8.         id++;
  9.     }
  10.     return NULL;
  11. }
    这里的strcmp(pdev->name, id->name) 就完成了 pdev->name和id->name 的匹配。

    4.上一此说到了驱动和设备匹配,中间隔了几天读了下 LDD3   ch14: The Linux Device Model,受益匪浅。
    平台驱动中添加  .id_table 成员好处是使驱动和设备可以一对多,即一个驱动可以支持多个设备。如果我们只使用设备里的  name 和驱动里的name进行匹配匹配那么设备和驱动只能一对一了。
    关于设备和驱动匹配后如何绑定的我们来继续分析代码。

点击(此处)折叠或打开

  1. static int __driver_attach(struct device *dev, void *data)
  2. {
  3.     struct device_driver *drv = data;

  4.     if (!driver_match_device(drv, dev))    /* 对添加的驱动进行匹配 */
  5.         return 0;

  6.     if (dev->parent)    /* Needed for USB */
  7.         device_lock(dev->parent);
  8.     device_lock(dev);
  9.     if (!dev->driver)
  10.         driver_probe_device(drv, dev);     /* 绑定 */
  11.     device_unlock(dev);
  12.     if (dev->parent)
  13.         device_unlock(dev->parent);

  14.     return 0;
    上节分析到在driver_match_device(drv, dev) 中完成了驱动和设备的匹配,  向下看 driver_probe_device(drv, dev) 函数  注释上写了试图把设备和驱动绑定到一起。

点击(此处)折叠或打开

  1. int driver_probe_device(struct device_driver *drv, struct device *dev)
  2. {
  3.     int ret = 0;

  4.     if (!device_is_registered(dev))
  5.         return -ENODEV;

  6.     pr_debug("bus: '%s': %s: matched device %s with driver %s\n",
  7.          drv->bus->name, __func__, dev_name(dev), drv->name);

  8.     pm_runtime_get_noresume(dev);
  9.     pm_runtime_barrier(dev);
  10.     ret = really_probe(dev, drv);
  11.     pm_runtime_put_sync(dev);

  12.     return ret;
  13. }
    继续分析里面有个 real_probe(dev, drv) 函数

点击(此处)折叠或打开

  1. static int really_probe(struct device *dev, struct device_driver *drv)
  2. {
  3.     int ret = 0;

  4.     atomic_inc(&probe_count);
  5.     pr_debug("bus: '%s': %s: probing driver %s with device %s\n",
  6.          drv->bus->name, __func__, drv->name, dev_name(dev));
  7.     WARN_ON(!list_empty(&dev->devres_head));

  8.     dev->driver = drv;        /* 终于看到了 */
  9.     if (driver_sysfs_add(dev)) {
  10.         printk(KERN_ERR "%s: driver_sysfs_add(%s) failed\n",
  11.             __func__, dev_name(dev));
  12.         goto probe_failed;
  13.     }

  14.     if (dev->bus->probe) {
  15.         ret = dev->bus->probe(dev);
  16.         if (ret)
  17.             goto probe_failed;
  18.     } else if (drv->probe) {
  19.         ret = drv->probe(dev);
  20.         if (ret)
  21.             goto probe_failed;
  22.     }

  23.     driver_bound(dev);
  24.     ret = 1;
  25.     pr_debug("bus: '%s': %s: bound device %s to driver %s\n",
  26.          drv->bus->name, __func__, dev_name(dev), drv->name);
  27.     goto done;

  28. probe_failed:
  29.     devres_release_all(dev);
  30.     driver_sysfs_remove(dev);
  31.     dev->driver = NULL;

  32.     if (ret != -ENODEV && ret != -ENXIO) {
  33.         /* driver matched but the probe failed */
  34.         printk(KERN_WARNING
  35.          "%s: probe of %s failed with error %d\n",
  36.          drv->name, dev_name(dev), ret);
  37.     }
  38.     /*
  39.      * Ignore errors returned by ->probe so that the next driver can try
  40.      * its luck.
  41.      */
  42.     ret = 0;
  43. done:
  44.     atomic_dec(&probe_count);
  45.     wake_up(&probe_waitqueue);
  46.     return ret;
  47. }
    看到一行 dev->driver = drv, 这里应该就是把设备结构里的驱动指向了我们注册的 driver。那下面为什么还有一行driver_bound(dev),看样还要继续分析。

点击(此处)折叠或打开

  1. static void driver_bound(struct device *dev)
  2. {
  3.     if (klist_node_attached(&dev->p->knode_driver)) {
  4.         printk(KERN_WARNING "%s: device %s already bound\n",
  5.             __func__, kobject_name(&dev->kobj));
  6.         return;
  7.     }

  8.     pr_debug("driver: '%s': %s: bound to device '%s'\n", dev_name(dev),
  9.          __func__, dev->driver->name);

  10.     klist_add_tail(&dev->p->knode_driver, &dev->driver->p->klist_devices);/* 加入到驱动的的设备链表 */

  11.     if (dev->bus)
  12.         blocking_notifier_call_chain(&dev->bus->p->bus_notifier,
  13.                      BUS_NOTIFY_BOUND_DRIVER, dev);
  14. }
    把函数设备加入了驱动的的设备链表。
    中间还落下了一部分 就是 real_probe(dev, drv) 函数的

点击(此处)折叠或打开

  1. if (dev->bus->probe) {
  2.         ret = dev->bus->probe(dev);
  3.         if (ret)
  4.             goto probe_failed;
  5.     } else if (drv->probe) {
  6.         ret = drv->probe(dev);
  7.         if (ret)
  8.             goto probe_failed;
  9.     }
    platform_bus_type中没有probe函数,dev->bus->probe为空,因此该处调用了drv->probe

点击(此处)折叠或打开

  1. static struct platform_driver s3c24xx_i2c_driver = {
  2.     .probe        = s3c24xx_i2c_probe,
  3.     .remove        = s3c24xx_i2c_remove,
  4.     .id_table    = s3c24xx_driver_ids,
  5.     .driver        = {
  6.         .owner    = THIS_MODULE,
  7.         .name    = "s3c-i2c",
  8.         .pm    = S3C24XX_DEV_PM_OPS,
  9.     },
  10. };
    中的 s3c_i2c_probe 函数。此处要明确下,处传给s3c24xx_i2c_probe 的参数是我们注册的i2c 平台设备s3c_device_i2c0。

    5.s3c24xx_i2c_probe分析:

点击(此处)折叠或打开

  1. static int s3c24xx_i2c_probe(struct platform_device *pdev)
  2. {
  3.     struct s3c24xx_i2c *i2c;
  4.     struct s3c2410_platform_i2c *pdata;
  5.     struct resource *res;
  6.     int ret;

  7.     pdata = pdev->dev.platform_data;            /* 获得s3c_device_i2c0.dev.platform_data*/
  8.     if (!pdata) {
  9.         dev_err(&pdev->dev, "no platform data\n");
  10.         return -EINVAL;
  11.     }

  12.     i2c = kzalloc(sizeof(struct s3c24xx_i2c), GFP_KERNEL);
  13.     if (!i2c) {
  14.         dev_err(&pdev->dev, "no memory for state\n");
  15.         return -ENOMEM;
  16.     }

  17.     /* 初始化i2c适配器adap */
  18.     strlcpy(i2c->adap.name, "s3c2410-i2c", sizeof(i2c->adap.name));
  19.     i2c->adap.owner = THIS_MODULE;
  20.     i2c->adap.algo = &s3c24xx_i2c_algorithm;    /* 总线通信方法 */
  21.     i2c->adap.retries = 2;
  22.     i2c->adap.class = I2C_CLASS_HWMON | I2C_CLASS_SPD;
  23.     i2c->tx_setup = 50;

  24.     spin_lock_init(&i2c->lock);
  25.     init_waitqueue_head(&i2c->wait);

  26.     /* find the clock and enable it */

  27.     /* 使能i2c时钟 */
  28.     i2c->dev = &pdev->dev;
  29.     i2c->clk = clk_get(&pdev->dev, "i2c");
  30.     if (IS_ERR(i2c->clk)) {
  31.         dev_err(&pdev->dev, "cannot get clock\n");
  32.         ret = -ENOENT;
  33.         goto err_noclk;
  34.     }

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

  36.     clk_enable(i2c->clk);
  37.     
  38.     /* map the registers */
  39.     res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
  40.     if (res == NULL) {
  41.         dev_err(&pdev->dev, "cannot find IO resource\n");
  42.         ret = -ENOENT;
  43.         goto err_clk;
  44.     }

  45.     i2c->ioarea = request_mem_region(res->start, resource_size(res),
  46.                      pdev->name);

  47.     if (i2c->ioarea == NULL) {
  48.         dev_err(&pdev->dev, "cannot request IO\n");
  49.         ret = -ENXIO;
  50.         goto err_clk;
  51.     }

  52.     i2c->regs = ioremap(res->start, resource_size(res));

  53.     if (i2c->regs == NULL) {
  54.         dev_err(&pdev->dev, "cannot map IO\n");
  55.         ret = -ENXIO;
  56.         goto err_ioarea;
  57.     }

  58.     dev_dbg(&pdev->dev, "registers %p (%p, %p)\n",
  59.         i2c->regs, i2c->ioarea, res);

  60.     /* setup info block for the i2c core */
  61.     i2c->adap.algo_data = i2c;
  62.     i2c->adap.dev.parent = &pdev->dev;

  63.     /* initialise the i2c controller */
  64.     ret = s3c24xx_i2c_init(i2c);                /* 初始化IO,  s3c_device_i2c0从器件地址, i2c时钟分频信息等*/
  65.     if (ret != 0)
  66.         goto err_iomap;

  67.     /* find the IRQ for this unit (note, this relies on the init call to
  68.      * ensure no current IRQs pending
  69.      */
  70.     i2c->irq = ret = platform_get_irq(pdev, 0);
  71.     if (ret <= 0) {
  72.         dev_err(&pdev->dev, "cannot find IRQ\n");
  73.         goto err_iomap;
  74.     }

  75.     ret = request_irq(i2c->irq, s3c24xx_i2c_irq, IRQF_DISABLED,
  76.              dev_name(&pdev->dev), i2c);

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

  81.     ret = s3c24xx_i2c_register_cpufreq(i2c);
  82.     if (ret < 0) {
  83.         dev_err(&pdev->dev, "failed to register cpufreq notifier\n");
  84.         goto err_irq;
  85.     }

  86.     /* Note, previous versions of the driver used i2c_add_adapter()
  87.      * to add the bus at any number. We now pass the bus number via
  88.      * the platform data, so if unset it will now default to always
  89.      * being bus 0.
  90.      */

  91.     i2c->adap.nr = pdata->bus_num;

  92.     ret = i2c_add_numbered_adapter(&i2c->adap); /* 将i2c->adap按照i2c->adap.nr注册进i2c_bus_type, 注册该设备device_register(&adap->dev)并将i2c->adap->dev添加至i2c_bus_type->klist_devices中, 完成板文件中的设备client初始化 */
  93.     if (ret < 0) {
  94.         dev_err(&pdev->dev, "failed to add bus to i2c core\n");
  95.         goto err_cpufreq;
  96.     }

  97.     platform_set_drvdata(pdev, i2c);

  98.     dev_info(&pdev->dev, "%s: S3C I2C adapter\n", dev_name(&i2c->adap.dev));
  99.     clk_disable(i2c->clk);
  100.     return 0;

  101.  err_cpufreq:
  102.     s3c24xx_i2c_deregister_cpufreq(i2c);

  103.  err_irq:
  104.     free_irq(i2c->irq, i2c);

  105.  err_iomap:
  106.     iounmap(i2c->regs);

  107.  err_ioarea:
  108.     release_resource(i2c->ioarea);
  109.     kfree(i2c->ioarea);

  110.  err_clk:
  111.     clk_disable(i2c->clk);
  112.     clk_put(i2c->clk);

  113.  err_noclk:
  114.     kfree(i2c);
  115.     return ret;
  116. }
    i2c_add_numbered_adapter将adapter注册进具有总线号的i2c:

点击(此处)折叠或打开

  1. int i2c_add_numbered_adapter(struct i2c_adapter *adap)
  2. {
  3.     int    id;
  4.     int    status;

  5.     if (adap->nr & ~MAX_ID_MASK)
  6.         return -EINVAL;

  7. retry:
  8.     if (idr_pre_get(&i2c_adapter_idr, GFP_KERNEL) == 0)
  9.         return -ENOMEM;

  10.     mutex_lock(&core_lock);
  11.     /* "above" here means "above or equal to", sigh;
  12.      * we need the "equal to" result to force the result
  13.      */
  14.     status = idr_get_new_above(&i2c_adapter_idr, adap, adap->nr, &id);
  15.     if (status == 0 && id != adap->nr) {
  16.         status = -EBUSY;
  17.         idr_remove(&i2c_adapter_idr, id);
  18.     }
  19.     mutex_unlock(&core_lock);
  20.     if (status == -EAGAIN)
  21.         goto retry;

  22.     if (status == 0)
  23.         status = i2c_register_adapter(adap);/* 注册adapter */
  24.     return status;
  25. }
    i2c_register_adapter(adap)函数:

点击(此处)折叠或打开

  1. static int i2c_register_adapter(struct i2c_adapter *adap)
  2. {
  3.     int res = 0;

  4.     /* Can't register until after driver model init */
  5.     if (unlikely(WARN_ON(!i2c_bus_type.p))) {
  6.         res = -EAGAIN;
  7.         goto out_list;
  8.     }

  9.     /* Sanity checks */
  10.     if (unlikely(adap->name[0] == '\0')) {
  11.         pr_err("i2c-core: Attempt to register an adapter with "
  12.          "no name!\n");
  13.         return -EINVAL;
  14.     }
  15.     if (unlikely(!adap->algo)) {
  16.         pr_err("i2c-core: Attempt to register adapter '%s' with "
  17.          "no algo!\n", adap->name);
  18.         return -EINVAL;
  19.     }

  20.     rt_mutex_init(&adap->bus_lock);
  21.     mutex_init(&adap->userspace_clients_lock);
  22.     INIT_LIST_HEAD(&adap->userspace_clients);

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

  26.     dev_set_name(&adap->dev, "i2c-%d", adap->nr);
  27.     adap->dev.bus = &i2c_bus_type;        /* 关联i2c_bus_type */
  28.     adap->dev.type = &i2c_adapter_type;
  29.     res = device_register(&adap->dev);    /* 注册adapter->dev, 将i2c->adap->dev添加至i2c_bus_type->klist_devices */
  30.     if (res)
  31.         goto out_list;

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

  33. #ifdef CONFIG_I2C_COMPAT
  34.     res = class_compat_create_link(i2c_adapter_compat_class, &adap->dev,
  35.                  adap->dev.parent);
  36.     if (res)
  37.         dev_warn(&adap->dev,
  38.              "Failed to create compatibility class link\n");
  39. #endif

  40.     /* create pre-declared device nodes */
  41.     if (adap->nr < __i2c_first_dynamic_bus_num)
  42.         i2c_scan_static_board_info(adap);  /* 完成板文件中的设备client初始化, 将板文件中的__i2c_board_list添加进i2c_bus_type,并关联adapter */

  43.     /* Notify drivers */
  44.     mutex_lock(&core_lock);
  45.     bus_for_each_drv(&i2c_bus_type, NULL, adap, __process_new_adapter);  /* 列举bus->p->klist_drivers, 此时i2c-core.c中的dummy_driver出现在该驱动列表中, 调用__process_new_adapter */
  46.     mutex_unlock(&core_lock);

  47.     return 0;

  48. out_list:
  49.     mutex_lock(&core_lock);
  50.     idr_remove(&i2c_adapter_idr, adap->nr);
  51.     mutex_unlock(&core_lock);
  52.     return res;
  53. }
    可以看出,该函数获取板文件中的相关硬件信息并完成注册,完成s3c6410的i2c控制器初始化,并添加进i2c-core。

    6.驱动前面做的工作:1、注册了 i2c控制器设备及驱动,2、对s3c6410的控制器进行了初始化。
    呵呵,如果整过单片机知道如何用单片机读取 AT24XX存储器里的内容。 应该是一开始初始化I2C控制器,然后就可以按照I2C时序进行读取存储器里的数据。
    但是linux下就不一样了,linux系统把设备当成文件对待。那如果想读取数据应该打开哪个设备文件,还有和设备文件惺惺相惜的 file_operations 具体如何实现。
    带着这些问题继续分析代码。看/drivers/i2c/i2c-dev.c,此文件实现了I2C 字符设备文件操作接口。
    先看设备驱动模块加载函数module_init(i2c_dev_init)。

点击(此处)折叠或打开

  1. static int __init i2c_dev_init(void)
  2. {
  3.     int res;

  4.     printk(KERN_INFO "i2c /dev entries driver\n");

  5.     res = register_chrdev(I2C_MAJOR, "i2c", &i2cdev_fops);   /* 注册字符设备 */
  6.     if (res)
  7.         goto out;

  8.     i2c_dev_class = class_create(THIS_MODULE, "i2c-dev");    /* 创建i2v-dev类 */
  9.     if (IS_ERR(i2c_dev_class)) {
  10.         res = PTR_ERR(i2c_dev_class);
  11.         goto out_unreg_chrdev;
  12.     }

  13.     /* Keep track of adapters which will be added or removed later */
  14.     res = bus_register_notifier(&i2c_bus_type, &i2cdev_notifier);
  15.     if (res)
  16.         goto out_unreg_class;

  17.     /* Bind to already existing adapters right away */
  18.     i2c_for_each_dev(NULL, i2cdev_attach_adapter);

  19.     return 0;

  20. out_unreg_class:
  21.     class_destroy(i2c_dev_class);
  22. out_unreg_chrdev:
  23.     unregister_chrdev(I2C_MAJOR, "i2c");
  24. out:
  25.     printk(KERN_ERR "%s: Driver Initialisation failed\n", __FILE__);
  26.     return res;
  27. }
    i2c_for_each_dev(NULL, i2cdev_attach_adapter)
        --> bus_for_each_dev(&i2c_bus_type, NULL, data, i2cdev_attach_adapter)

点击(此处)折叠或打开

  1. int bus_for_each_dev(struct bus_type *bus, struct device *start,
  2.          void *data, int (*fn)(struct device *, void *))
  3. {
  4.     struct klist_iter i;
  5.     struct device *dev;
  6.     int error = 0;

  7.     if (!bus)
  8.         return -EINVAL;

  9.     klist_iter_init_node(&bus->p->klist_devices, &i,
  10.              (start ? &start->p->knode_bus : NULL));
  11.     while ((dev = next_device(&i)) && !error)
  12.         error = fn(dev, data);
  13.     klist_iter_exit(&i);
  14.     return error;
  15. }
    遍历i2c_bus_type->p->klist_devices并调用 i2cdev_attach_adapter(dev,data)

点击(此处)折叠或打开

  1. static int i2cdev_attach_adapter(struct device *dev, void *dummy)
  2. {
  3.     struct i2c_adapter *adap;
  4.     struct i2c_dev *i2c_dev;
  5.     int res;

  6.     if (dev->type != &i2c_adapter_type)
  7.         return 0;
  8.     adap = to_i2c_adapter(dev);

  9.     i2c_dev = get_free_i2c_dev(adap);
  10.     if (IS_ERR(i2c_dev))
  11.         return PTR_ERR(i2c_dev);

  12.     /* register this i2c device with the driver core */
  13.     i2c_dev->dev = device_create(i2c_dev_class, &adap->dev,
  14.                  MKDEV(I2C_MAJOR, adap->nr), NULL,
  15.                  "i2c-%d", adap->nr);
  16.     if (IS_ERR(i2c_dev->dev)) {
  17.         res = PTR_ERR(i2c_dev->dev);
  18.         goto error;
  19.     }
  20.     res = device_create_file(i2c_dev->dev, &dev_attr_name);
  21.     if (res)
  22.         goto error_destroy;

  23.     pr_debug("i2c-dev: adapter [%s] registered as minor %d\n",
  24.          adap->name, adap->nr);
  25.     return 0;
  26. error_destroy:
  27.     device_destroy(i2c_dev_class, MKDEV(I2C_MAJOR, adap->nr));
  28. error:
  29.     return_i2c_dev(i2c_dev);
  30.     return res;
  31. }
    将设备dev与s3c6410的i2c适配器关联,并注册进i2c_dev_class。
    后面的dev设备操作就可以通过适配器完成了。
    对于该方法下操作EEPROM给出一段示例代码,EEPROM的从机地址为0x50
    (1)向0x10中写入数据0x58

    (2)从0x10中读出存储的数据

点击(此处)折叠或打开
  1. /*i2c_test.c
  2. * hongtao_liu <lht@farsight.com.cn>
  3. */
  4. #include <stdio.h>
  5. #include <linux/types.h>
  6. #include <stdlib.h>
  7. #include <fcntl.h>
  8. #include <unistd.h>
  9. #include <sys/types.h>
  10. #include <errno.h>
  11. #define I2C_RETRIES 0x0701
  12. #define I2C_TIMEOUT 0x0702
  13. #define I2C_RDWR 0x0707

  14. /*********定义struct i2c_rdwr_ioctl_data和struct i2c_msg,要和内核一致*******/
  15. struct i2c_msg {
  16.         unsigned short addr;
  17.         unsigned short flags;
  18. #define I2C_M_TEN 0x0010
  19. #define I2C_M_RD 0x0001
  20.         unsigned short len;
  21.         unsigned char *buf;
  22. };

  23. struct i2c_rdwr_ioctl_data {
  24.         struct i2c_msg *msgs;
  25.         int nmsgs;
  26.         /* nmsgs这个数量决定了有多少开始信号,对于“单开始时序”,取1*/
  27. };

  28. /***********主程序***********/
  29. int main()
  30. {
  31.         int fd,ret;
  32.         struct i2c_rdwr_ioctl_data e2prom_data;
  33.         fd open("/dev/i2c-0",O_RDWR);
  34.         /*
  35.          /dev/i2c-0是在注册i2c-dev.c后产生的,代表一个可操作的适配器。如果不使用i2c-dev.c
  36.          的方式,就没有,也不需要这个节点。
  37.          */
  38.         if(fd<0{
  39.                 perror("open error");
  40.         }
  41.         e2prom_data.nmsg2;
  42.         /*
  43.          因为操作时序中,最多是用到2个开始信号(字节读操作中),所以此将
  44.          e2prom_data.nmsgs配置为2
  45.          */
  46.         e2prom_data.msgs=(struct i2c_msg*)malloc(e2prom_data.nmsgs*sizeof(struct i2c_msg));
  47.         if(!e2prom_data.msgs{
  48.                 perror("malloc error");
  49.                 exit(1);
  50.         }

  51.         ioctl(fd,I2C_TIMEOUT,1);/*超时时间*/
  52.         ioctl(fd,I2C_RETRIES,2);/*重复次数*/

  53.         /***write data to e2prom**/
  54.         e2prom_data.nmsgs=1;
  55.         (e2prom_data.msgs[0]).len=2; //1个 e2prom 写入目标的地址和1个数据
  56.         (e2prom_data.msgs[0]).addr=0x50;//e2prom 设备地址
  57.         (e2prom_data.msgs[0]).flags=0; //write
  58.         (e2prom_data.msgs[0]).buf=(unsigned char*)malloc(2);
  59.         (e2prom_data.msgs[0]).buf[0]=0x10;// e2prom 写入目标的地址
  60.         (e2prom_data.msgs[0]).buf[1]=0x58;//the data to write

  61.         ret ioctl(fd,I2C_RDWR,(unsigned long)&e2prom_data);
  62.         if(ret<0{
  63.                 perror("ioctl error1");
  64.         }
  65.         
  66.         sleep(1);

  67.         /******read data from e2prom*******/
  68.         e2prom_data.nmsgs=2;
  69.         (e2prom_data.msgs[0]).len=1; //e2prom 目标数据的地址
  70.         (e2prom_data.msgs[0]).addr=0x50; // e2prom 设备地址
  71.         (e2prom_data.msgs[0]).flags=0;//write
  72.         (e2prom_data.msgs[0]).buf[0]=0x10;//e2prom数据地址
  73.         (e2prom_data.msgs[1]).len=1;//读出的数据
  74.         (e2prom_data.msgs[1]).addr=0x50;// e2prom 设备地址
  75.         (e2prom_data.msgs[1]).flags=I2C_M_RD;//read
  76.         (e2prom_data.msgs[1]).buf=(unsigned char*)malloc(1);//存放返回值的地址。
  77.         (e2prom_data.msgs[1]).buf[0]=0;//初始化读缓冲

  78.         ret ioctl(fd,I2C_RDWR,(unsigned long)&e2prom_data);
  79.         if(ret<0{
  80.                 perror("ioctl error2");
  81.         }
  82.         printf("buff[0]=%x\n",(e2prom_data.msgs[1]).buf[0]);
  83.         /***打印读出的值,没错的话,就应该是前面写的0x58了***/
  84.         free(e2prom_data.msgs);
  85.         close(fd);
  86.         return 0;
  87. }


    7.i2c-dev.c文件定的主设备号为89的设备可以方便地给应用程序提供读写i2c设备寄存器的能力,使得工程师大多数时候并不需要为具体的i2c设备驱动定义文件操作接口。
    对于某些设备驱动,比如/drivers/misc/eeprom/at24.c,它调用了i2c核心提供的api,不依赖于cpu的类型和i2c适配器的硬件特性,通用型很强。
    at24设备驱动的注册过程如下:
    at24_init(void)
        -->i2c_add_driver(&at24_driver)
            -->i2c_register_driver(THIS_MODULE, driver)
                -->driver_register(&driver->driver)
                    -->bus_add_driver(drv)
                        -->driver_attach(drv)
                            -->bus_for_each_dev(drv->bus, NULL, drv, __driver_attach)
                                -->__driver_attach(dev, drv)
                                    -->driver_match_device(drv, dev)
                                    -->driver_probe_device(drv, dev)
                                        -->really_probe(dev, drv)
                                            -->i2c_device_probe(dev)
                                                -->driver->probe(client, i2c_match_id(driver->id_table, client))
                                                    -->at24_probe(struct i2c_client *client, const struct i2c_device_id *id)
    注册过程虽然比较复杂,通过i2c-core将__i2c_board_list中的client(在适配器注册过程中i2c_add_numbered_adapter()-->i2c_register_adapter(adap)-->i2c_scan_static_board_info(adap)-->i2c_new_device(adapter, &devinfo->board_info) 中完成板文件 client->adapter = adap的关联)与at24驱动关联,在对at24读写时调用i2c-core.c中的i2c_transfer,从而调用adatper完成操作,具体过程就不详细分析了。













阅读(1327) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~