NUC972触摸屏驱动移植过程分析(二)

https://blog.csdn.net/b7376811/article/details/86607529

今天继续分析NUC972的触摸屏驱动移植过程,上一节主要分析了触摸屏需要数据,今天来分析一下驱动部分,上一节我们已经了解了触摸屏一般有I2C接口和gpio接口,所以触摸屏既是一个I2C设备,也是一个input设备,linux中把触摸屏整体归为input设备,在input驱动中又包含了I2C驱动和gpio按键驱动,今天首先分析一下I2C驱动部分。

I2C驱动在linux内核中整的还是挺复杂的,按我的理解,作为一般的驱动开发者,需要抓住三部分:第一、I2C核心部分驱动;第二、具体的硬件平台的I2C适配器部分(其实一般都是集成到SOC上的),每个适配器驱动对应一个I2C硬件接口;第三、I2C客户端设备驱动,也就是具体和I2C接口连接的硬件设备的驱动。I2C核心部分驱动类似一个领导,负责协调I2C适配器驱动与I2C客户端驱动之间的数据交互,提供相应的接口;I2C适配器驱动就是SOC上的I2C接口的驱动;I2C客户端驱动就是连接的具体的I2C设备的具体操作方式。I2C核心部分驱动属于linux内核的框架制定者所写,作为一般的驱动开发人员是不需要修改的(当然,框架设计者除外,不过那么高级的开发人员,肯定是不屑于看我这个小白写的东西的,哈哈哈,开个玩笑),这一部分的驱动代码在kernel/i2c这个文件夹里面,主要包括i2c-core.c和i2c-dev.c。其实适配器驱动代码,一般也不需要板级驱动开发者开发的,因为一般的方案提供商或者是芯片生产商,在原版的linux内核的基础上,根据自己的平台开发好了,就比如我用的nuc972这个片子,上面自带两个I2C硬件接口,在nuc970系列的BSP中已经做好了两个硬件I2C适配器的驱动,在kernel/drivers/i2c/busses这个文件夹里面,i2c-nuc970-p0.c和i2c-nuc970-p0.c分别是I2C0与I2C1适配器的驱动代码。另外,在内核中包含了使用SOC的gpio接口模拟I2C时序的模拟I2C适配器的驱动代码,同样是在这个文件夹里面,i2c-gpio.c这个文件就是通用的使用gpio模拟I2C通信的适配器驱动代码。而第三部分的I2C客户端的驱动一般是需要板级驱动移植开发者着重分析和开发的。

        通过以上的分析,可以看得出来,I2C驱动框架中其实是包含了两套driver+device的结构,分别是I2C适配器设备+I2C适配器驱动、I2C客户端设备+I2C客户端驱动。I2C适配器在内核中是被虚拟成platform_device,相对应的驱动是platform_driver,I2C客户端设备是一个标准的I2C设备。内核启动时,注册platform总线,从kernel/driver/base/init.c这个文件中可以看出来:

 
  1. void __init driver_init(void)

  2. {

  3. /* These are the core pieces */

  4. devtmpfs_init();

  5. devices_init();

  6. buses_init();

  7. classes_init();

  8. firmware_init();

  9. hypervisor_init();

  10.  
  11. /* These are also core pieces, but must come after the

  12. * core core pieces.

  13. */

  14. platform_bus_init();

  15. cpu_dev_init();

  16. memory_dev_init();

  17. }

内核使用platform_bus_init()这个函数对platform总线进行了初始化。将这个函数追进去,可以看到对platform总线进行了注册,声明了总线的match方法,这段代码在kernel/drivers/base/platform.c中:

 
  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. };

  8. EXPORT_SYMBOL_GPL(platform_bus_type);

  9.  
  10. int __init platform_bus_init(void)

  11. {

  12. int error;

  13.  
  14. early_platform_cleanup();

  15.  
  16. error = device_register(&platform_bus);

  17. if (error)

  18. return error;

  19. error = bus_register(&platform_bus_type);

  20. if (error)

  21. device_unregister(&platform_bus);

  22. return error;

  23. }

从这段代码中可以看出来,在platform_bus_init这个函数中,注册了平台总线的match方法和uevent方法等,再往上追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.  
  6. /* Attempt an OF style match first */

  7. if (of_driver_match_device(dev, drv))

  8. return 1;

  9.  
  10. /* Then try ACPI style match */

  11. if (acpi_driver_match_device(dev, drv))

  12. return 1;

  13.  
  14. /* Then try to match against the id table */

  15. if (pdrv->id_table)

  16. return platform_match_id(pdrv->id_table, pdev) != NULL;

  17.  
  18. /* fall-back to driver name match */

  19. return (strcmp(pdev->name, drv->name) == 0);

  20. }

从这段代码中可以看出来platform总线驱动和设备的具体的match方法,id_table的匹配规则优先级高于name的匹配规则。可能说到这里,各位看官迷糊了,现在是在说I2C,说这么多平台总线的事儿干啥,不要急,下面就来看I2C总线的相关内容,基于ncu970的BSP,nuc972的两个硬件I2C适配器驱动的代码包含在kernel/drivers/i2c/busses文件夹中,上面有提到,分别是i2c-nuc970-p0.c和i2c-nuc970-p1.c,这两个文件几乎相同,咱们这里就只看第一个文件吧,这个文件有几百行,具体的可以自己去看看,咱们这里只是了解一下框架,只贴最后几行吧:

 
  1. static int nuc970_i2c0_probe(struct platform_device *pdev)

  2. {

  3. struct nuc970_i2c *i2c;

  4. struct nuc970_platform_i2c *pdata;

  5. struct resource *res;

  6. int ret;

  7.  
  8. pdata = pdev->dev.platform_data;

  9. if (!pdata) {

  10. dev_err(&pdev->dev, "no platform data\n");

  11. return -EINVAL;

  12. }

  13.  
  14. i2c = kzalloc(sizeof(struct nuc970_i2c), GFP_KERNEL);

  15. if (!i2c) {

  16. dev_err(&pdev->dev, "no memory for state\n");

  17. return -ENOMEM;

  18. }

  19.  
  20. strlcpy(i2c->adap.name, "nuc970-i2c0", sizeof(i2c->adap.name));

  21. i2c->adap.owner = THIS_MODULE;

  22. i2c->adap.algo = &nuc970_i2c0_algorithm;

  23. i2c->adap.retries = 2;

  24. i2c->adap.class = I2C_CLASS_HWMON | I2C_CLASS_SPD;

  25.  
  26. spin_lock_init(&i2c->lock);

  27. init_waitqueue_head(&i2c->wait);

  28.  
  29. /* find the clock and enable it */

  30.  
  31. i2c->dev = &pdev->dev;

  32. i2c->clk = clk_get(NULL, "i2c0");

  33. if (IS_ERR(i2c->clk)) {

  34. dev_err(&pdev->dev, "cannot get clock\n");

  35. ret = -ENOENT;

  36. goto err_noclk;

  37. }

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

  40.  
  41. clk_prepare(i2c->clk);

  42. clk_enable(i2c->clk);

  43.  
  44. /* map the registers */

  45.  
  46. res = platform_get_resource(pdev, IORESOURCE_MEM, 0);

  47. if (res == NULL) {

  48. dev_err(&pdev->dev, "cannot find IO resource\n");

  49. ret = -ENOENT;

  50. goto err_clk;

  51. }

  52.  
  53. i2c->ioarea = request_mem_region(res->start, resource_size(res),

  54. pdev->name);

  55.  
  56. if (i2c->ioarea == NULL) {

  57. dev_err(&pdev->dev, "cannot request IO\n");

  58. ret = -ENXIO;

  59. goto err_clk;

  60. }

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

  63.  
  64. if (i2c->regs == NULL) {

  65. dev_err(&pdev->dev, "cannot map IO\n");

  66. ret = -ENXIO;

  67. goto err_ioarea;

  68. }

  69.  
  70. dev_dbg(&pdev->dev, "registers %p (%p, %p)\n",

  71. i2c->regs, i2c->ioarea, res);

  72.  
  73. /* setup info block for the i2c core */

  74.  
  75. i2c->adap.algo_data = i2c;

  76. i2c->adap.dev.parent = &pdev->dev;

  77.  
  78. ret = clk_get_rate(i2c->clk)/(pdata->bus_freq * 5) - 1;

  79. writel(ret & 0xffff, i2c->regs + DIVIDER);

  80.  
  81. /* find the IRQ for this unit (note, this relies on the init call to

  82. * ensure no current IRQs pending

  83. */

  84.  
  85. i2c->irq = ret = platform_get_irq(pdev, 0);

  86. if (ret <= 0) {

  87. dev_err(&pdev->dev, "cannot find IRQ\n");

  88. goto err_iomap;

  89. }

  90.  
  91. ret = request_irq(i2c->irq, nuc970_i2c_irq, IRQF_SHARED,

  92. dev_name(&pdev->dev), i2c);

  93.  
  94. if (ret != 0) {

  95. dev_err(&pdev->dev, "cannot claim IRQ %d\n", i2c->irq);

  96. goto err_iomap;

  97. }

  98.  
  99. /* Note, previous versions of the driver used i2c_add_adapter()

  100. * to add the bus at any number. We now pass the bus number via

  101. * the platform data, so if unset it will now default to always

  102. * being bus 0.

  103. */

  104.  
  105. i2c->adap.nr = pdata->bus_num;

  106.  
  107. ret = i2c_add_numbered_adapter(&i2c->adap);

  108. if (ret < 0) {

  109. dev_err(&pdev->dev, "failed to add bus to i2c core\n");

  110. goto err_irq;

  111. }

  112.  
  113. platform_set_drvdata(pdev, i2c);

  114.  
  115. dev_info(&pdev->dev, "%s: nuc970 I2C adapter\n",

  116. dev_name(&i2c->adap.dev));

  117. return 0;

  118.  
  119. err_irq:

  120. free_irq(i2c->irq, i2c);

  121.  
  122. err_iomap:

  123. iounmap(i2c->regs);

  124.  
  125. err_ioarea:

  126. release_resource(i2c->ioarea);

  127. kfree(i2c->ioarea);

  128.  
  129. err_clk:

  130. clk_disable(i2c->clk);

  131. clk_put(i2c->clk);

  132.  
  133. err_noclk:

  134. kfree(i2c);

  135. return ret;

  136. }

  137.  
  138. /* nuc970_i2c0_remove

  139. *

  140. * called when device is removed from the bus

  141. */

  142.  
  143. static int nuc970_i2c0_remove(struct platform_device *pdev)

  144. {

  145. struct nuc970_i2c *i2c = platform_get_drvdata(pdev);

  146.  
  147. i2c_del_adapter(&i2c->adap);

  148. free_irq(i2c->irq, i2c);

  149.  
  150. clk_disable(i2c->clk);

  151. clk_put(i2c->clk);

  152.  
  153. iounmap(i2c->regs);

  154.  
  155. release_resource(i2c->ioarea);

  156. kfree(i2c->ioarea);

  157. kfree(i2c);

  158.  
  159. return 0;

  160. }

  161.  
  162. static struct platform_driver nuc970_i2c0_driver = {

  163. .probe = nuc970_i2c0_probe,

  164. .remove = nuc970_i2c0_remove,

  165. .driver = {

  166. .name = "nuc970-i2c0",

  167. .owner = THIS_MODULE,

  168. },

  169. };

  170. module_platform_driver(nuc970_i2c0_driver);

  171.  
  172. MODULE_DESCRIPTION("nuc970 I2C Bus driver");

  173. MODULE_AUTHOR("Wan ZongShun, ");

  174. MODULE_LICENSE("GPL");

  175. MODULE_ALIAS("platform:nuc970-i2c0");

从代码的最后几行可以看出来,nuc970系列的I2C0接口确实是被虚拟成了一个platform设备,具体的设备数据在kernel/arch/arm/mach-nuc970/dev.c这个文件里面,如下代码:

 
  1. static struct resource nuc970_i2c0_resource[] = {

  2. [0] = {

  3. .start = NUC970_PA_I2C0,

  4. .end = NUC970_PA_I2C0 + NUC970_SZ_I2C0 - 1,

  5. .flags = IORESOURCE_MEM,

  6. },

  7. [1] = {

  8. .start = IRQ_I2C0,

  9. .end = IRQ_I2C0,

  10. .flags = IORESOURCE_IRQ,

  11. }

  12. };

  13.  
  14. static struct nuc970_platform_i2c nuc970_i2c0_data = {

  15. .bus_num = 0,

  16. .bus_freq = 100000,

  17. };

  18.  
  19. struct platform_device nuc970_device_i2c0 = {

  20. .name = "nuc970-i2c0",

  21. .id = -1,

  22. .num_resources = ARRAY_SIZE(nuc970_i2c0_resource),

  23. .resource = nuc970_i2c0_resource,

  24. .dev = {

  25. .platform_data = &nuc970_i2c0_data,

  26. }

  27. };

在这里定义了nuc970系列的i2c0接口必须的硬件资源,可以明显的看出来是一个platform设备。而platform总线在匹配device与driver时,是根据name = "nuc970-i2c0"这个名字字段来匹配的。在总线上,只要driver与device同时出现这个名字,platform总线的match方法就能让他们匹配起来,干柴烈火,就燃烧起来了,正常情况下,后果就是启动了platform_driver中的probe方法,根据上面对应的probe方法对应的函数,可以很清楚的看出来,使用ret = i2c_add_numbered_adapter(&i2c->adap)这个函数向内核添加了一个I2C适配器。至此,I2C0这个硬件适配器已经被成功添加到内核中,并添加了一个名字为nuc970-i2c0的I2C总线。

对于GPIO模拟的I2C适配器驱动,同样是这个道理,驱动内具体的实现方法不太一样,这里就不详细分析了。

下面再看一下I2C总线的注册过程,在kernel/drivers/i2c/i2c-core.c这个文件中,包含了I2C总线初始化的代码:

 
  1. static int __init i2c_init(void)

  2. {

  3. int retval;

  4.  
  5. retval = bus_register(&i2c_bus_type);

  6. if (retval)

  7. return retval;

  8. #ifdef CONFIG_I2C_COMPAT

  9. i2c_adapter_compat_class = class_compat_register("i2c-adapter");

  10. if (!i2c_adapter_compat_class) {

  11. retval = -ENOMEM;

  12. goto bus_err;

  13. }

  14. #endif

  15. retval = i2c_add_driver(&dummy_driver);

  16. if (retval)

  17. goto class_err;

  18. return 0;

  19.  
  20. class_err:

  21. #ifdef CONFIG_I2C_COMPAT

  22. class_compat_unregister(i2c_adapter_compat_class);

  23. bus_err:

  24. #endif

  25. bus_unregister(&i2c_bus_type);

  26. return retval;

  27. }

  28.  
  29. static void __exit i2c_exit(void)

  30. {

  31. i2c_del_driver(&dummy_driver);

  32. #ifdef CONFIG_I2C_COMPAT

  33. class_compat_unregister(i2c_adapter_compat_class);

  34. #endif

  35. bus_unregister(&i2c_bus_type);

  36. }

  37.  
  38. /* We must initialize early, because some subsystems register i2c drivers

  39. * in subsys_initcall() code, but are linked (and initialized) before i2c.

  40. */

  41. postcore_initcall(i2c_init);

  42. module_exit(i2c_exit);

这段代码可以很清楚的看出来在内核中注册了i2c_bus,并注册了一个名字为dummy的I2C总线。根据注册的i2c_bus_type,网上追代码:

 
  1. struct bus_type i2c_bus_type = {

  2. .name = "i2c",

  3. .match = i2c_device_match,

  4. .probe = i2c_device_probe,

  5. .remove = i2c_device_remove,

  6. .shutdown = i2c_device_shutdown,

  7. .pm = &i2c_device_pm_ops,

  8. };

可以看出来注册I2C总线时,分别配置了match、probe、remove等方法,网上追match方法实现的代码:

 
  1. static int i2c_device_match(struct device *dev, struct device_driver *drv)

  2. {

  3. struct i2c_client *client = i2c_verify_client(dev);

  4. struct i2c_driver *driver;

  5.  
  6. if (!client)

  7. return 0;

  8.  
  9. /* Attempt an OF style match */

  10. if (of_driver_match_device(dev, drv))

  11. return 1;

  12.  
  13. /* Then ACPI style match */

  14. if (acpi_driver_match_device(dev, drv))

  15. return 1;

  16.  
  17. driver = to_i2c_driver(drv);

  18. /* match on an id table if there is one */

  19. if (driver->id_table)

  20. return i2c_match_id(driver->id_table, client) != NULL;

  21.  
  22. return 0;

  23. }

从上面的代码可以很清楚的看出来,i2c的device与driver之间的匹配是根据id_table进行匹配的,其实将i2c_match_id这个函数追进去,可以看出来,也是根据名字进行匹配的。

说到这里,I2C适配器端的驱动与设备基本上已经分析完成了,下面继续分析I2C客户端的驱动与设备数据,即触摸屏芯片TSC2007的驱动(终于说到触摸屏芯片了)。首先看一下TSC2007驱动代码,在kernel/drivers/input/touchscreen文件夹里面,tsc2007.c这个文件,同样的,这里只看框架,所以只看一下最后几行代码:

 
  1. static int tsc2007_probe(struct i2c_client *client,

  2. const struct i2c_device_id *id)

  3. {

  4. struct tsc2007 *ts;

  5. struct tsc2007_platform_data *pdata = client->dev.platform_data;

  6. struct input_dev *input_dev;

  7. int err;

  8.  
  9. if (!pdata) {

  10. dev_err(&client->dev, "platform data is required!\n");

  11. return -EINVAL;

  12. }

  13.  
  14. if (!i2c_check_functionality(client->adapter,

  15. I2C_FUNC_SMBUS_READ_WORD_DATA))

  16. return -EIO;

  17.  
  18. ts = kzalloc(sizeof(struct tsc2007), GFP_KERNEL);

  19. input_dev = input_allocate_device();

  20. if (!ts || !input_dev) {

  21. err = -ENOMEM;

  22. goto err_free_mem;

  23. }

  24.  
  25. /***********************************************************************/

  26. #if 1

  27. #warning hack for MDK972

  28. if (gpio_is_valid(NUC970_PE15)) {

  29.  
  30. err = gpio_request_one(NUC970_PE15, GPIOF_IN, "nuc970-ts2007");

  31. if (err < 0) {

  32. dev_err(&client->dev, "Failed to request GPIO %d, error %d\n",

  33. NUC970_PE15, err);

  34. return err;

  35. }

  36. #if 1

  37. if (1) {

  38. err = gpio_set_debounce(NUC970_PE15,

  39. 1 * 1000);

  40. /* use timer if gpiolib doesn't provide debounce */

  41. if (err < 0)

  42. dev_err(&client->dev, "Failed to set_debounce for GPIO %d, error %d\n",

  43. NUC970_PE15, err);

  44. }

  45. #endif

  46.  
  47. client->irq = gpio_to_irq(NUC970_PE15);

  48. if (client->irq < 0) {

  49. err = client->irq;

  50. dev_err(&client->dev,

  51. "Unable to get irq number for GPIO %d, error %d\n",

  52. NUC970_PE15, err);

  53. goto err_free_mem;

  54. }

  55.  
  56. }

  57. #endif

  58. /***********************************************************************/

  59.  
  60. ts->client = client;

  61. ts->irq = client->irq;

  62. ts->input = input_dev;

  63. init_waitqueue_head(&ts->wait);

  64.  
  65. ts->model = pdata->model;

  66. ts->x_plate_ohms = pdata->x_plate_ohms;

  67. ts->max_rt = pdata->max_rt ? : MAX_12BIT;

  68. ts->poll_delay = pdata->poll_delay ? : 1;

  69. ts->poll_period = pdata->poll_period ? : 1;

  70. ts->get_pendown_state = pdata->get_pendown_state;

  71. ts->clear_penirq = pdata->clear_penirq;

  72.  
  73. if (pdata->x_plate_ohms == 0) {

  74. dev_err(&client->dev, "x_plate_ohms is not set up in platform data");

  75. err = -EINVAL;

  76. goto err_free_mem;

  77. }

  78.  
  79. snprintf(ts->phys, sizeof(ts->phys),

  80. "%s/input0", dev_name(&client->dev));

  81.  
  82. input_dev->name = "TSC2007 Touchscreen";

  83. input_dev->phys = ts->phys;

  84. input_dev->id.bustype = BUS_I2C;

  85.  
  86. input_dev->open = tsc2007_open;

  87. input_dev->close = tsc2007_close;

  88.  
  89. input_set_drvdata(input_dev, ts);

  90.  
  91. input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);

  92. input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);

  93.  
  94. input_set_abs_params(input_dev, ABS_X, 0, MAX_12BIT, pdata->fuzzx, 0);

  95. input_set_abs_params(input_dev, ABS_Y, 0, MAX_12BIT, pdata->fuzzy, 0);

  96. input_set_abs_params(input_dev, ABS_PRESSURE, 0, MAX_12BIT,

  97. pdata->fuzzz, 0);

  98.  
  99. if (pdata->init_platform_hw)

  100. pdata->init_platform_hw();

  101.  
  102. err = request_threaded_irq(ts->irq, tsc2007_hard_irq, tsc2007_soft_irq,

  103. IRQF_ONESHOT, client->dev.driver->name, ts);

  104. if (err < 0) {

  105. dev_err(&client->dev, "irq %d busy?\n", ts->irq);

  106. goto err_free_mem;

  107. }

  108.  
  109. tsc2007_stop(ts);

  110.  
  111. err = input_register_device(input_dev);

  112. if (err)

  113. goto err_free_irq;

  114.  
  115. i2c_set_clientdata(client, ts);

  116.  
  117. return 0;

  118.  
  119. err_free_irq:

  120. free_irq(ts->irq, ts);

  121. if (pdata->exit_platform_hw)

  122. pdata->exit_platform_hw();

  123. err_free_mem:

  124. input_free_device(input_dev);

  125. kfree(ts);

  126. return err;

  127. }

  128.  
  129. static int tsc2007_remove(struct i2c_client *client)

  130. {

  131. struct tsc2007 *ts = i2c_get_clientdata(client);

  132. struct tsc2007_platform_data *pdata = client->dev.platform_data;

  133.  
  134. free_irq(ts->irq, ts);

  135.  
  136. if (pdata->exit_platform_hw)

  137. pdata->exit_platform_hw();

  138.  
  139. input_unregister_device(ts->input);

  140. kfree(ts);

  141.  
  142. return 0;

  143. }

  144.  
  145. static const struct i2c_device_id tsc2007_idtable[] = {

  146. { "tsc2007", 0 },

  147. { }

  148. };

  149.  
  150. MODULE_DEVICE_TABLE(i2c, tsc2007_idtable);

  151.  
  152. static struct i2c_driver tsc2007_driver = {

  153. .driver = {

  154. .owner = THIS_MODULE,

  155. .name = "tsc2007"

  156. },

  157. .id_table = tsc2007_idtable,

  158. .probe = tsc2007_probe,

  159. .remove = tsc2007_remove,

  160. };

  161.  
  162. module_i2c_driver(tsc2007_driver);

  163.  
  164. MODULE_AUTHOR("Kwangwoo Lee ");

  165. MODULE_DESCRIPTION("TSC2007 TouchScreen Driver");

  166. MODULE_LICENSE("GPL");

tsc2007的驱动是向内核的添加了i2c的id_table,作为总线match时使用,并注册了一个名字为tsc2007的i2c_driver,里面包含probe方法、remove方法,这是驱动部分。下面再看一下i2c设备的device部分,这部分的数据在kernel/arch/arm/mach-nuc970文件夹里面的dev.c中:

 
  1. static struct i2c_board_info __initdata nuc970_i2c_clients2[] =

  2. {

  3. {

  4. I2C_BOARD_INFO("tsc2007", 0x48),

  5. .platform_data = &tsc2007_info,

  6. /* irq number is run-time assigned */

  7. },

  8. #ifdef CONFIG_SENSOR_OV7725

  9. {I2C_BOARD_INFO("ov7725", 0x21),},

  10. #endif

  11. #ifdef CONFIG_SENSOR_OV5640

  12. {I2C_BOARD_INFO("ov5640", 0x3c),},

  13. #endif

  14. #ifdef CONFIG_SENSOR_NT99141

  15. {I2C_BOARD_INFO("nt99141", 0x2a),},

  16. #endif

  17. #ifdef CONFIG_SENSOR_NT99050

  18. {I2C_BOARD_INFO("nt99050", 0x21),},

  19. #endif

  20. {I2C_BOARD_INFO("lm75a", 0x4e),},

  21.  
  22. {I2C_BOARD_INFO("ds1307", 0x68),},

  23. };

从上面的代码中可以看出来,这个总线上包含的不仅仅是触摸屏一个I2C设备,I2C_BOARD_INFO就是一个宏定义,填充结构体i2c_board_info的名字和地址字段。这个是就是tsc2007需要传入驱动的数据,下面看一下i2c device的注册:

i2c_register_board_info(2, nuc970_i2c_clients2, ARRAY_SIZE(nuc970_i2c_clients2));

其实就是这个函数,意思就是想I2C适配器号码为2的总线注册这些I2C客户端设备,将这个函数追进去,其实就是向相应i2c总线的id_table对应的链表中增加元素:

 
  1. int __init

  2. i2c_register_board_info(int busnum,

  3. struct i2c_board_info const *info, unsigned len)

  4. {

  5. int status;

  6.  
  7. down_write(&__i2c_board_lock);

  8.  
  9. /* dynamic bus numbers will be assigned after the last static one */

  10. if (busnum >= __i2c_first_dynamic_bus_num)

  11. __i2c_first_dynamic_bus_num = busnum + 1;

  12.  
  13. for (status = 0; len; len--, info++) {

  14. struct i2c_devinfo *devinfo;

  15.  
  16. devinfo = kzalloc(sizeof(*devinfo), GFP_KERNEL);

  17. if (!devinfo) {

  18. pr_debug("i2c-core: can't register boardinfo!\n");

  19. status = -ENOMEM;

  20. break;

  21. }

  22.  
  23. devinfo->busnum = busnum;

  24. devinfo->board_info = *info;

  25. list_add_tail(&devinfo->list, &__i2c_board_list);

  26. }

  27.  
  28. up_write(&__i2c_board_lock);

  29.  
  30. return status;

  31. }

到此为止,i2c客户端的driver与device都已经注册完毕,和平台总线一样的流程,i2c总线通过match绑定的匹配方法对driver与device进行匹配,当driver与device中都出现“tsc2007”这个字符时,同样的情况,同样的干柴烈火,同样的启动驱动中的probe方法对应的函数,就可是使用i2c_device设备中的数据对i2c driver中的相应字段进行填充。

好了,到这里,触摸屏驱动中和I2C相关的内容基本上就分析完了,在这里总结一下,I2C的驱动分为两部分,第一部分是I2C适配器的驱动,也就是soc上的I2C接口的驱动,在内核中是一个platform设备,对应的也是platform驱动,当I2C适配器的设备与驱动匹配成功后,会向内核添加相对应的I2C总线,这个是实实在在的i2c总线,这样I2C适配器的设备与驱动就匹配完成了。第二部分是I2C客户端的设备与驱动,I2c客户端的驱动是i2c总线驱动,设备是i2c设备,通过设备数据向相应的设备总线注册i2c device,驱动端向内核中注册相应的驱动,i2c总线的match方法根据名字进行匹配,一旦匹配成功,I2C部分的初始化基本上就完成了。

好了,今天就说这么多吧。

你可能感兴趣的:(NUC977,Linux)