Chinaunix首页 | 论坛 | 博客
  • 博客访问: 459477
  • 博文数量: 40
  • 博客积分: 1410
  • 博客等级: 军士长
  • 技术积分: 1396
  • 用 户 组: 普通用户
  • 注册时间: 2011-03-22 19:26
个人简介

嵌入式系统工程师,从事视频、图像、网络、虚拟化等方面的底层软件开发与优化。

文章存档

2014年(4)

2013年(10)

2012年(14)

2011年(12)

分类: LINUX

2012-03-30 10:04:53

一、基于s3c6410的I2C总线控制器驱动程序分析及其在BSP中的添加
1. 适配器驱动分析
1.1 平台总线驱动结构体及其注册与注销

点击(此处)折叠或打开

  1. //1. s3c2410 I2C总线的平台驱动结构体
  2. static struct platform_driver s3c24xx_i2c_driver = {
  3.     .probe        = s3c24xx_i2c_probe,
  4.     .remove        = s3c24xx_i2c_remove,
  5.     .id_table    = s3c24xx_driver_ids,
  6.     .driver        = {
  7.         .owner    = THIS_MODULE,
  8.         .name    = "s3c-i2c",
  9.         .pm    = S3C24XX_DEV_PM_OPS,
  10.         .of_match_table = s3c24xx_i2c_match,
  11.     },
  12. };
  13. //2. 注册I2C平台总线驱动
  14. static int __init i2c_adap_s3c_init(void)
  15. {
  16.     return platform_driver_register(&s3c24xx_i2c_driver);
  17. }
  18. subsys_initcall(i2c_adap_s3c_init); //子系统初始化,在系统初始化时在do_initcall中调用
  19. //3. 注销I2C平台总线驱动
  20. static void __exit i2c_adap_s3c_exit(void)
  21. {
  22.     platform_driver_unregister(&s3c24xx_i2c_driver);
  23. }
  24. module_exit(i2c_adap_s3c_exit);
1.2 s3c24xx_i2c_probe函数分析
s3c24xx_i2c_probe函数总的来说是:给s3c24xx_i2c结构体及其私有数据分配内存,并且设置该结构体内的需要设置的成员。

点击(此处)折叠或打开

  1. /* s3c24xx_i2c_probe
  2.  *
  3.  * called by the bus driver when a suitable device is found
  4. */

  5. static int s3c24xx_i2c_probe(struct platform_device *pdev)
  6. {
  7.     struct s3c24xx_i2c *i2c;
  8.     struct s3c2410_platform_i2c *pdata = NULL;
  9.     struct resource *res;
  10.     int ret;
  11.     //1. 判断是否已创建设备节点
  12.     if (!pdev->dev.of_node) {
  13.         pdata = pdev->dev.platform_data; //如果没有,把平台数据赋值给内部指针
  14.         if (!pdata) {
  15.             dev_err(&pdev->dev, "no platform data\n");
  16.             return -EINVAL;
  17.         }
  18.     }
  19.     //2. 分配s3c24xx_i2c结构体内存
  20.     i2c = kzalloc(sizeof(struct s3c24xx_i2c), GFP_KERNEL);
  21.     if (!i2c) {
  22.         dev_err(&pdev->dev, "no memory for state\n");
  23.         return -ENOMEM;
  24.     }
  25.     //3. 分配s3c24xx_i2c结构体私有数据的内存
  26.     i2c->pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
  27.     if (!i2c->pdata) {
  28.         ret = -ENOMEM;
  29.         goto err_noclk;
  30.     }
  31.     //4. 如果私有数据指针不为NULL,复制设备数据
  32.     if (pdata)
  33.         memcpy(i2c->pdata, pdata, sizeof(*pdata));
  34.     else
  35.         s3c24xx_i2c_parse_dt(pdev->dev.of_node, i2c);
  36.     //5. 设置i2c->adap
  37.     strlcpy(i2c->adap.name, "s3c2410-i2c", sizeof(i2c->adap.name));
  38.     i2c->adap.owner = THIS_MODULE;
  39.     i2c->adap.algo = &s3c24xx_i2c_algorithm;
  40.     i2c->adap.retries = 2;
  41.     i2c->adap.class = I2C_CLASS_HWMON | I2C_CLASS_SPD;
  42.     i2c->tx_setup = 50;
  43.     //6. 初始化I2C的自旋锁和等待队列
  44.     spin_lock_init(&i2c->lock);
  45.     init_waitqueue_head(&i2c->wait);
  46.     //7. 获取并使能I2C时钟
  47.     /* find the clock and enable it */

  48.     i2c->dev = &pdev->dev;
  49.     i2c->clk = clk_get(&pdev->dev, "i2c");
  50.     if (IS_ERR(i2c->clk)) {
  51.         dev_err(&pdev->dev, "cannot get clock\n");
  52.         ret = -ENOENT;
  53.         goto err_noclk;
  54.     }

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

  56.     clk_enable(i2c->clk);
  57.     //8. 寄存器映射
  58.     /* map the registers */

  59.     res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
  60.     if (res == NULL) {
  61.         dev_err(&pdev->dev, "cannot find IO resource\n");
  62.         ret = -ENOENT;
  63.         goto err_clk;
  64.     }

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

  67.     if (i2c->ioarea == NULL) {
  68.         dev_err(&pdev->dev, "cannot request IO\n");
  69.         ret = -ENXIO;
  70.         goto err_clk;
  71.     }

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

  73.     if (i2c->regs == NULL) {
  74.         dev_err(&pdev->dev, "cannot map IO\n");
  75.         ret = -ENXIO;
  76.         goto err_ioarea;
  77.     }

  78.     dev_dbg(&pdev->dev, "registers %p (%p, %p)\n",
  79.         i2c->regs, i2c->ioarea, res);
  80.     //9. 为i2c核心设置部分信息
  81.     /* setup info block for the i2c core */

  82.     i2c->adap.algo_data = i2c;
  83.     i2c->adap.dev.parent = &pdev->dev;
  84.     //10. 初始化i2c控制器
  85.     /* initialise the i2c controller */

  86.     ret = s3c24xx_i2c_init(i2c);
  87.     if (ret != 0)
  88.         goto err_iomap;
  89.     //11. 获取并注册I2C IRQ
  90.     /* find the IRQ for this unit (note, this relies on the init call to
  91.      * ensure no current IRQs pending
  92.      */

  93.     i2c->irq = ret = platform_get_irq(pdev, 0);
  94.     if (ret <= 0) {
  95.         dev_err(&pdev->dev, "cannot find IRQ\n");
  96.         goto err_iomap;
  97.     }

  98.     ret = request_irq(i2c->irq, s3c24xx_i2c_irq, 0,
  99.              dev_name(&pdev->dev), i2c);

  100.     if (ret != 0) {
  101.         dev_err(&pdev->dev, "cannot claim IRQ %d\n", i2c->irq);
  102.         goto err_iomap;
  103.     }
  104.     //12. 以CPU的频率注册I2C结构体
  105.     ret = s3c24xx_i2c_register_cpufreq(i2c);
  106.     if (ret < 0) {
  107.         dev_err(&pdev->dev, "failed to register cpufreq notifier\n");
  108.         goto err_irq;
  109.     }

  110.     /* Note, previous versions of the driver used i2c_add_adapter()
  111.      * to add the bus at any number. We now pass the bus number via
  112.      * the platform data, so if unset it will now default to always
  113.      * being bus 0.
  114.      */
  115.     
  116.     i2c->adap.nr = i2c->pdata->bus_num; //当处理器包含多个I2C控制器时,用bus_num区分
  117.     i2c->adap.dev.of_node = pdev->dev.of_node;
  118.     //13. 添加适配器
  119.     ret = i2c_add_numbered_adapter(&i2c->adap);
  120.     if (ret < 0) {
  121.         dev_err(&pdev->dev, "failed to add bus to i2c core\n");
  122.         goto err_cpufreq;
  123.     }

  124.     of_i2c_register_devices(&i2c->adap);
  125.     platform_set_drvdata(pdev, i2c);

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

  129.  err_cpufreq:
  130.     s3c24xx_i2c_deregister_cpufreq(i2c);
  131.  
  132.  err_irq:
  133.     free_irq(i2c->irq, i2c);

  134.  err_iomap:
  135.     iounmap(i2c->regs);

  136.  err_ioarea:
  137.     release_resource(i2c->ioarea);
  138.     kfree(i2c->ioarea);

  139.  err_clk:
  140.     clk_disable(i2c->clk);
  141.     clk_put(i2c->clk);

  142.  err_noclk:
  143.     kfree(i2c);
  144.     return ret;
  145. }

  146. /* s3c24xx_i2c_remove
  147.  *
  148.  * called when device is removed from the bus
  149. */

  150. static int s3c24xx_i2c_remove(struct platform_device *pdev)
  151. {
  152.     struct s3c24xx_i2c *i2c = platform_get_drvdata(pdev);

  153.     s3c24xx_i2c_deregister_cpufreq(i2c);

  154.     i2c_del_adapter(&i2c->adap);
  155.     free_irq(i2c->irq, i2c);

  156.     clk_disable(i2c->clk);
  157.     clk_put(i2c->clk);

  158.     iounmap(i2c->regs);

  159.     release_resource(i2c->ioarea);
  160.     s3c24xx_i2c_dt_gpio_free(i2c);
  161.     kfree(i2c->ioarea);
  162.     kfree(i2c);

  163.     return 0;
  164. }
1.3 s3c24xx_i2c_init函数分析

点击(此处)折叠或打开

  1. /* s3c24xx_i2c_init
  2.  *
  3.  * initialise the controller, set the IO lines and frequency
  4. */

  5. static int s3c24xx_i2c_init(struct s3c24xx_i2c *i2c)
  6. {
  7.     unsigned long iicon = S3C2410_IICCON_IRQEN | S3C2410_IICCON_ACKEN;
  8.     struct s3c2410_platform_i2c *pdata;
  9.     unsigned int freq;

  10.     /* get the plafrom data */

  11.     pdata = i2c->pdata;

  12.     /* inititalise the gpio */

  13.     if (pdata->cfg_gpio)
  14.         pdata->cfg_gpio(to_platform_device(i2c->dev));
  15.     else
  16.         if (s3c24xx_i2c_parse_dt_gpio(i2c))
  17.             return -EINVAL;

  18.     /* write slave address */

  19.     writeb(pdata->slave_addr, i2c->regs + S3C2410_IICADD);

  20.     dev_info(i2c->dev, "slave address 0x%02x\n", pdata->slave_addr);

  21.     writel(iicon, i2c->regs + S3C2410_IICCON);

  22.     /* we need to work out the divisors for the clock... */

  23.     if (s3c24xx_i2c_clockrate(i2c, &freq) != 0) {
  24.         writel(0, i2c->regs + S3C2410_IICCON);
  25.         dev_err(i2c->dev, "cannot meet bus frequency required\n");
  26.         return -EINVAL;
  27.     }

  28.     /* todo - check that the i2c lines aren't being dragged anywhere */

  29.     dev_info(i2c->dev, "bus frequency set to %d KHz\n", freq);
  30.     dev_dbg(i2c->dev, "S3C2410_IICCON=0x%02lx\n", iicon);

  31.     return 0;
  32. }
2.适配器设备驱动

点击(此处)折叠或打开

  1. static struct resource s3c_i2c0_resource[] = {
  2.     [0] = DEFINE_RES_MEM(S3C_PA_IIC, SZ_4K),
  3.     [1] = DEFINE_RES_IRQ(IRQ_IIC),
  4. };

  5. struct platform_device s3c_device_i2c0 = {
  6.     .name        = "s3c2410-i2c",
  7. #ifdef CONFIG_S3C_DEV_I2C1
  8.     .id        = 0,
  9. #else
  10.     .id        = -1,
  11. #endif
  12.     .num_resources    = ARRAY_SIZE(s3c_i2c0_resource),
  13.     .resource    = s3c_i2c0_resource,
  14. };

  15. struct s3c2410_platform_i2c default_i2c_data __initdata = {
  16.     .flags        = 0,
  17.     .slave_addr    = 0x10,
  18.     .frequency    = 100*1000,
  19.     .sda_delay    = 100,
  20. };

  21. void __init s3c_i2c0_set_platdata(struct s3c2410_platform_i2c *pd)
  22. {
  23.     struct s3c2410_platform_i2c *npd;

  24.     if (!pd)
  25.         pd = &default_i2c_data;

  26.     npd = s3c_set_platdata(pd, sizeof(struct s3c2410_platform_i2c),
  27.              &s3c_device_i2c0);

  28.     if (!npd->cfg_gpio)
  29.         npd->cfg_gpio = s3c_i2c0_cfg_gpio;
  30. }
3.BSP中添加I2C适配器

点击(此处)折叠或打开

  1. /* linux/arch/arm/mach-s3c64xx/mach-jason6410.c
  2.  *
  3.  * Copyright 2012 Jason Lu <gfvvz@yahoo.com.cn>
  4.  *     http://jason2012.blog.chinaunix.net
  5.  *
  6.  * This program is free software; you can redistribute it and/or modify
  7.  * it under the terms of the GNU General Public License version 2 as
  8.  * published by the Free Software Foundation.
  9.  *
  10. */

  11. #include <linux/init.h>
  12. #include <linux/interrupt.h>
  13. #include <linux/fb.h>
  14. #include <linux/gpio.h>
  15. #include <linux/kernel.h>
  16. #include <linux/list.h>
  17. #include <linux/dm9000.h>
  18. #include <linux/mtd/mtd.h>
  19. #include <linux/mtd/partitions.h>
  20. #include <linux/serial_core.h>
  21. #include <linux/types.h>

  22. #include <asm/mach-types.h>
  23. #include <asm/mach/arch.h>
  24. #include <asm/mach/map.h>

  25. #include <mach/map.h>
  26. #include <mach/regs-gpio.h>
  27. #include <mach/regs-modem.h>
  28. #include <mach/regs-srom.h>

  29. #include <plat/s3c6410.h>
  30. #include <plat/adc.h>
  31. #include <plat/cpu.h>
  32. #include <plat/devs.h>
  33. #include <plat/fb.h>
  34. #include <plat/nand.h>
  35. #include <plat/regs-serial.h>
  36. #include <plat/ts.h>
  37. #include <plat/regs-fb-v4.h>
  38. #include

  39. #include <video/platform_lcd.h>

  40. #define UCON S3C2410_UCON_DEFAULT
  41. #define ULCON (S3C2410_LCON_CS8 | S3C2410_LCON_PNONE | S3C2410_LCON_STOPB)
  42. #define UFCON (S3C2410_UFCON_RXTRIG8 | S3C2410_UFCON_FIFOMODE)

  43. static struct s3c2410_uartcfg jason6410_uartcfgs[] __initdata = {
  44.     [0] = {
  45.         .hwport    = 0,
  46.         .flags    = 0,
  47.         .ucon    = UCON,
  48.         .ulcon    = ULCON,
  49.         .ufcon    = UFCON,
  50.     },
  51.     [1] = {
  52.         .hwport    = 1,
  53.         .flags    = 0,
  54.         .ucon    = UCON,
  55.         .ulcon    = ULCON,
  56.         .ufcon    = UFCON,
  57.     },
  58.     [2] = {
  59.         .hwport    = 2,
  60.         .flags    = 0,
  61.         .ucon    = UCON,
  62.         .ulcon    = ULCON,
  63.         .ufcon    = UFCON,
  64.     },
  65.     [3] = {
  66.         .hwport    = 3,
  67.         .flags    = 0,
  68.         .ucon    = UCON,
  69.         .ulcon    = ULCON,
  70.         .ufcon    = UFCON,
  71.     },
  72. };

  73. /* Nand flash */
  74. static struct mtd_partition jason6410_nand_part[] = {
  75.     {
  76.         .name        = "u-boot-2011.06",
  77.         .offset        = 0,
  78.         .size        = (4 * 128 *SZ_1K),
  79.         .mask_flags    = MTD_CAP_NANDFLASH,
  80.     },
  81.     {
  82.         .name        = "Linux Kernel 3.2.8",
  83.         .offset        = MTDPART_OFS_APPEND,
  84.         .size        = (5*SZ_1M) ,
  85.         .mask_flags    = MTD_CAP_NANDFLASH,
  86.     },
  87.     {
  88.         .name        = "UBI File System",
  89.         .offset        = MTDPART_OFS_APPEND,
  90.         .size        = MTDPART_SIZ_FULL,
  91.     }
  92. };

  93. static struct s3c2410_nand_set jason6410_nand_sets[] = {
  94.     [0] = {
  95.         .name        = "nand",
  96.         .nr_chips    = 1,
  97.         .nr_partitions    = ARRAY_SIZE(jason6410_nand_part),
  98.         .partitions    = jason6410_nand_part,
  99.     },
  100. };

  101. static struct s3c2410_platform_nand jason6410_nand_info = {
  102.     .tacls        = 25,
  103.     .twrph0        = 55,
  104.     .twrph1        = 40,
  105.     .nr_sets    = ARRAY_SIZE(jason6410_nand_sets),
  106.     .sets        = jason6410_nand_sets,
  107. };

  108. static struct platform_device *jason6410_devices[] __initdata = {
  109.     &s3c_device_nand,
  110.     &s3c_device_i2c0,
  111. };

  112. static void __init jason6410_map_io(void)
  113. {
  114.     s3c64xx_init_io(NULL, 0);
  115.     s3c24xx_init_clocks(12000000);
  116.     s3c24xx_init_uarts(jason6410_uartcfgs, ARRAY_SIZE(jason6410_uartcfgs));
  117. }

  118. static void __init jason6410_machine_init(void)
  119. {
  120.     s3c_device_nand.name = "s3c6410-nand";
  121.     s3c_nand_set_platdata(&jason6410_nand_info);

  122.     s3c_i2c0_set_platdata(NULL);

  123.     platform_add_devices(jason6410_devices, ARRAY_SIZE(jason6410_devices));
  124. }

  125. MACHINE_START(JASON6410, "JASON6410")
  126.     /* Maintainer: Darius Augulis <augulis.darius@gmail.com> */
  127.     .atag_offset    = 0x100,
  128.     .init_irq    = s3c6410_init_irq,
  129.     .map_io        = jason6410_map_io,
  130.     .init_machine    = jason6410_machine_init,
  131.     .timer        = &s3c24xx_timer,
  132. MACHINE_END

二、AT24 EEPROM I2C设备驱动程序分析及其在BSP中的添加
1. AT24设备驱动分析
1.1 平台总线驱动结构体及其注册与注销

点击(此处)折叠或打开

  1. static struct i2c_driver at24_driver = { //该结构体较新内核才有的,具体变化详见附录
  2.     .driver = {
  3.         .name = "at24",
  4.         .owner = THIS_MODULE,
  5.     },
  6.     .probe = at24_probe,
  7.     .remove = __devexit_p(at24_remove),
  8.     .id_table = at24_ids,
  9. };

  10. static int __init at24_init(void)
  11. {
  12.     if (!io_limit) {
  13.         pr_err("at24: io_limit must not be 0!\n");
  14.         return -EINVAL;
  15.     }

  16.     io_limit = rounddown_pow_of_two(io_limit);
  17.     return i2c_add_driver(&at24_driver);
  18. }
  19. module_init(at24_init);

  20. static void __exit at24_exit(void)
  21. {
  22.     i2c_del_driver(&at24_driver);
  23. }
  24. module_exit(at24_exit);
1.2 i2c_device_id结构体

点击(此处)折叠或打开

  1. /* create non-zero magic value for given eeprom parameters */
  2. #define AT24_DEVICE_MAGIC(_len, _flags)         \
  3.     ((1 << AT24_SIZE_FLAGS | (_flags))         \
  4.      << AT24_SIZE_BYTELEN | ilog2(_len))

  5. static const struct i2c_device_id at24_ids[] = {
  6.     /* needs 8 addresses as A0-A2 are ignored */
  7.     { "24c00", AT24_DEVICE_MAGIC(128 / 8, AT24_FLAG_TAKE8ADDR) },
  8.     /* old variants can't be handled with this generic */
  9.     { "24c01", AT24_DEVICE_MAGIC(1024 / 8, 0) },
  10.     { "24c02", AT24_DEVICE_MAGIC(2048 / 8, 0) },
  11.     /* spd is a 24c02 in memory DIMMs */
  12.     { "spd", AT24_DEVICE_MAGIC(2048 / 8,
  13.         AT24_FLAG_READONLY | AT24_FLAG_IRUGO) },
  14.     { "24c04", AT24_DEVICE_MAGIC(4096 / 8, 0) },
  15.     /* 24rf08 quirk is handled at i2c-core */
  16.     { "24c08", AT24_DEVICE_MAGIC(8192 / 8, 0) },
  17.     { "24c16", AT24_DEVICE_MAGIC(16384 / 8, 0) },
  18.     { "24c32", AT24_DEVICE_MAGIC(32768 / 8, AT24_FLAG_ADDR16) },
  19.     { "24c64", AT24_DEVICE_MAGIC(65536 / 8, AT24_FLAG_ADDR16) },
  20.     { "24c128", AT24_DEVICE_MAGIC(131072 / 8, AT24_FLAG_ADDR16) },
  21.     { "24c256", AT24_DEVICE_MAGIC(262144 / 8, AT24_FLAG_ADDR16) },
  22.     { "24c512", AT24_DEVICE_MAGIC(524288 / 8, AT24_FLAG_ADDR16) },
  23.     { "24c1024", AT24_DEVICE_MAGIC(1048576 / 8, AT24_FLAG_ADDR16) },
  24.     { "at24", 0 },
  25.     { /* END OF LIST */ }
  26. };
  27. MODULE_DEVICE_TABLE(i2c, at24_ids);
1.3 at24_probe函数分析

点击(此处)折叠或打开

  1. //该函数的参数及实现是较新内核才有的,具体变化详见附录
  2. static int at24_probe(struct i2c_client *client, const struct i2c_device_id *id)
  3. {
  4.     struct at24_platform_data chip;
  5.     bool writable;
  6.     int use_smbus = 0;
  7.     struct at24_data *at24;
  8.     int err;
  9.     unsigned i, num_addresses;
  10.     kernel_ulong_t magic;

  11.     if (client->dev.platform_data) {
  12.         chip = *(struct at24_platform_data *)client->dev.platform_data;
  13.     } else {
  14.         if (!id->driver_data) {
  15.             err = -ENODEV;
  16.             goto err_out;
  17.         }
  18.         magic = id->driver_data;
  19.         chip.byte_len = BIT(magic & AT24_BITMASK(AT24_SIZE_BYTELEN));
  20.         magic >>= AT24_SIZE_BYTELEN;
  21.         chip.flags = magic & AT24_BITMASK(AT24_SIZE_FLAGS);
  22.         /*
  23.          * This is slow, but we can't know all eeproms, so we better
  24.          * play safe. Specifying custom eeprom-types via platform_data
  25.          * is recommended anyhow.
  26.          */
  27.         chip.page_size = 1;

  28.         /* update chipdata if OF is present */
  29.         at24_get_ofdata(client, &chip);

  30.         chip.setup = NULL;
  31.         chip.context = NULL;
  32.     }

  33.     if (!is_power_of_2(chip.byte_len))
  34.         dev_warn(&client->dev,
  35.             "byte_len looks suspicious (no power of 2)!\n");
  36.     if (!chip.page_size) {
  37.         dev_err(&client->dev, "page_size must not be 0!\n");
  38.         err = -EINVAL;
  39.         goto err_out;
  40.     }
  41.     if (!is_power_of_2(chip.page_size))
  42.         dev_warn(&client->dev,
  43.             "page_size looks suspicious (no power of 2)!\n");

  44.     /* Use I2C operations unless we're stuck with SMBus extensions. */
  45.     if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
  46.         if (chip.flags & AT24_FLAG_ADDR16) {
  47.             err = -EPFNOSUPPORT;
  48.             goto err_out;
  49.         }
  50.         if (i2c_check_functionality(client->adapter,
  51.                 I2C_FUNC_SMBUS_READ_I2C_BLOCK)) {
  52.             use_smbus = I2C_SMBUS_I2C_BLOCK_DATA;
  53.         } else if (i2c_check_functionality(client->adapter,
  54.                 I2C_FUNC_SMBUS_READ_WORD_DATA)) {
  55.             use_smbus = I2C_SMBUS_WORD_DATA;
  56.         } else if (i2c_check_functionality(client->adapter,
  57.                 I2C_FUNC_SMBUS_READ_BYTE_DATA)) {
  58.             use_smbus = I2C_SMBUS_BYTE_DATA;
  59.         } else {
  60.             err = -EPFNOSUPPORT;
  61.             goto err_out;
  62.         }
  63.     }

  64.     if (chip.flags & AT24_FLAG_TAKE8ADDR)
  65.         num_addresses = 8;
  66.     else
  67.         num_addresses =    DIV_ROUND_UP(chip.byte_len,
  68.             (chip.flags & AT24_FLAG_ADDR16) ? 65536 : 256);

  69.     at24 = kzalloc(sizeof(struct at24_data) +
  70.         num_addresses * sizeof(struct i2c_client *), GFP_KERNEL);
  71.     if (!at24) {
  72.         err = -ENOMEM;
  73.         goto err_out;
  74.     }

  75.     mutex_init(&at24->lock);
  76.     at24->use_smbus = use_smbus;
  77.     at24->chip = chip;
  78.     at24->num_addresses = num_addresses;

  79.     /*
  80.      * Export the EEPROM bytes through sysfs, since that's convenient.
  81.      * By default, only root should see the data (maybe passwords etc)
  82.      */
  83.     sysfs_bin_attr_init(&at24->bin);
  84.     at24->bin.attr.name = "eeprom";
  85.     at24->bin.attr.mode = chip.flags & AT24_FLAG_IRUGO ? S_IRUGO : S_IRUSR;
  86.     at24->bin.read = at24_bin_read;
  87.     at24->bin.size = chip.byte_len;

  88.     at24->macc.read = at24_macc_read;

  89.     writable = !(chip.flags & AT24_FLAG_READONLY);
  90.     if (writable) {
  91.         if (!use_smbus || i2c_check_functionality(client->adapter,
  92.                 I2C_FUNC_SMBUS_WRITE_I2C_BLOCK)) {

  93.             unsigned write_max = chip.page_size;

  94.             at24->macc.write = at24_macc_write;

  95.             at24->bin.write = at24_bin_write;
  96.             at24->bin.attr.mode |= S_IWUSR;

  97.             if (write_max > io_limit)
  98.                 write_max = io_limit;
  99.             if (use_smbus && write_max > I2C_SMBUS_BLOCK_MAX)
  100.                 write_max = I2C_SMBUS_BLOCK_MAX;
  101.             at24->write_max = write_max;

  102.             /* buffer (data + address at the beginning) */
  103.             at24->writebuf = kmalloc(write_max + 2, GFP_KERNEL);
  104.             if (!at24->writebuf) {
  105.                 err = -ENOMEM;
  106.                 goto err_struct;
  107.             }
  108.         } else {
  109.             dev_warn(&client->dev,
  110.                 "cannot write due to controller restrictions.");
  111.         }
  112.     }

  113.     at24->client[0] = client;

  114.     /* use dummy devices for multiple-address chips */
  115.     for (i = 1; i < num_addresses; i++) {
  116.         at24->client[i] = i2c_new_dummy(client->adapter,
  117.                     client->addr + i);
  118.         if (!at24->client[i]) {
  119.             dev_err(&client->dev, "address 0x%02x unavailable\n",
  120.                     client->addr + i);
  121.             err = -EADDRINUSE;
  122.             goto err_clients;
  123.         }
  124.     }

  125.     err = sysfs_create_bin_file(&client->dev.kobj, &at24->bin);
  126.     if (err)
  127.         goto err_clients;

  128.     i2c_set_clientdata(client, at24);

  129.     dev_info(&client->dev, "%zu byte %s EEPROM, %s, %u bytes/write\n",
  130.         at24->bin.size, client->name,
  131.         writable ? "writable" : "read-only", at24->write_max);
  132.     if (use_smbus == I2C_SMBUS_WORD_DATA ||
  133.      use_smbus == I2C_SMBUS_BYTE_DATA) {
  134.         dev_notice(&client->dev, "Falling back to %s reads, "
  135.              "performance will suffer\n", use_smbus ==
  136.              I2C_SMBUS_WORD_DATA ? "word" : "byte");
  137.     }

  138.     /* export data to kernel code */
  139.     if (chip.setup)
  140.         chip.setup(&at24->macc, chip.context);

  141.     return 0;

  142. err_clients:
  143.     for (i = 1; i < num_addresses; i++)
  144.         if (at24->client[i])
  145.             i2c_unregister_device(at24->client[i]);

  146.     kfree(at24->writebuf);
  147. err_struct:
  148.     kfree(at24);
  149. err_out:
  150.     dev_dbg(&client->dev, "probe error %d\n", err);
  151.     return err;
  152. }

  153. static int __devexit at24_remove(struct i2c_client *client)
  154. {
  155.     struct at24_data *at24;
  156.     int i;

  157.     at24 = i2c_get_clientdata(client);
  158.     sysfs_remove_bin_file(&client->dev.kobj, &at24->bin);

  159.     for (i = 1; i < at24->num_addresses; i++)
  160.         i2c_unregister_device(at24->client[i]);

  161.     kfree(at24->writebuf);
  162.     kfree(at24);
  163.     return 0;
  164. }
2. BSP中添加at24c08 I2C 信息

点击(此处)折叠或打开

  1. /* linux/arch/arm/mach-s3c64xx/mach-jason6410.c
  2.  *
  3.  * Copyright 2012 Jason Lu <gfvvz@yahoo.com.cn>
  4.  *     http://jason2012.blog.chinaunix.net
  5.  *
  6.  * This program is free software; you can redistribute it and/or modify
  7.  * it under the terms of the GNU General Public License version 2 as
  8.  * published by the Free Software Foundation.
  9.  *
  10. */

  11. #include <linux/init.h>
  12. #include <linux/interrupt.h>
  13. #include <linux/fb.h>
  14. #include <linux/gpio.h>
  15. #include <linux/kernel.h>
  16. #include <linux/list.h>
  17. #include <linux/dm9000.h>
  18. #include <linux/mtd/mtd.h>
  19. #include <linux/mtd/partitions.h>
  20. #include <linux/serial_core.h>
  21. #include <linux/types.h>

  22. #include <asm/mach-types.h>
  23. #include <asm/mach/arch.h>
  24. #include <asm/mach/map.h>

  25. #include <mach/map.h>
  26. #include <mach/regs-gpio.h>
  27. #include <mach/regs-modem.h>
  28. #include <mach/regs-srom.h>

  29. #include <plat/s3c6410.h>
  30. #include <plat/adc.h>
  31. #include <plat/cpu.h>
  32. #include <plat/devs.h>
  33. #include <plat/fb.h>
  34. #include <plat/nand.h>
  35. #include <plat/regs-serial.h>
  36. #include <plat/ts.h>
  37. #include <plat/regs-fb-v4.h>
  38. #include <plat/iic.h>

  39. #include <video/platform_lcd.h>

  40. #define UCON S3C2410_UCON_DEFAULT
  41. #define ULCON (S3C2410_LCON_CS8 | S3C2410_LCON_PNONE | S3C2410_LCON_STOPB)
  42. #define UFCON (S3C2410_UFCON_RXTRIG8 | S3C2410_UFCON_FIFOMODE)

  43. static struct s3c2410_uartcfg jason6410_uartcfgs[] __initdata = {
  44.     [0] = {
  45.         .hwport    = 0,
  46.         .flags    = 0,
  47.         .ucon    = UCON,
  48.         .ulcon    = ULCON,
  49.         .ufcon    = UFCON,
  50.     },
  51.     [1] = {
  52.         .hwport    = 1,
  53.         .flags    = 0,
  54.         .ucon    = UCON,
  55.         .ulcon    = ULCON,
  56.         .ufcon    = UFCON,
  57.     },
  58.     [2] = {
  59.         .hwport    = 2,
  60.         .flags    = 0,
  61.         .ucon    = UCON,
  62.         .ulcon    = ULCON,
  63.         .ufcon    = UFCON,
  64.     },
  65.     [3] = {
  66.         .hwport    = 3,
  67.         .flags    = 0,
  68.         .ucon    = UCON,
  69.         .ulcon    = ULCON,
  70.         .ufcon    = UFCON,
  71.     },
  72. };

  73. /* Nand flash */
  74. static struct mtd_partition jason6410_nand_part[] = {
  75.     {
  76.         .name        = "u-boot-2011.06",
  77.         .offset        = 0,
  78.         .size        = (4 * 128 *SZ_1K),
  79.         .mask_flags    = MTD_CAP_NANDFLASH,
  80.     },
  81.     {
  82.         .name        = "Linux Kernel 3.2.8",
  83.         .offset        = MTDPART_OFS_APPEND,
  84.         .size        = (5*SZ_1M) ,
  85.         .mask_flags    = MTD_CAP_NANDFLASH,
  86.     },
  87.     {
  88.         .name        = "UBI File System",
  89.         .offset        = MTDPART_OFS_APPEND,
  90.         .size        = MTDPART_SIZ_FULL,
  91.     }
  92. };

  93. static struct s3c2410_nand_set jason6410_nand_sets[] = {
  94.     [0] = {
  95.         .name        = "nand",
  96.         .nr_chips    = 1,
  97.         .nr_partitions    = ARRAY_SIZE(jason6410_nand_part),
  98.         .partitions    = jason6410_nand_part,
  99.     },
  100. };

  101. static struct s3c2410_platform_nand jason6410_nand_info = {
  102.     .tacls        = 25,
  103.     .twrph0        = 55,
  104.     .twrph1        = 40,
  105.     .nr_sets    = ARRAY_SIZE(jason6410_nand_sets),
  106.     .sets        = jason6410_nand_sets,
  107. };

  108. static struct platform_device *jason6410_devices[] __initdata = {
  109.     &s3c_device_nand,
  110.     &s3c_device_i2c0,
  111. };

  112. static struct i2c_board_info i2c_devs0[] __initdata = { //i2c: add @2012.4.1
  113.     { I2C_BOARD_INFO("24c08", 0x50), },
  114. };

  115. static struct i2c_board_info i2c_devs1[] __initdata = { //i2c: add @2012.4.1
  116.     /* Add your i2c device here */
  117. };

  118. static void __init jason6410_map_io(void)
  119. {
  120.     s3c64xx_init_io(NULL, 0);
  121.     s3c24xx_init_clocks(12000000);
  122.     s3c24xx_init_uarts(jason6410_uartcfgs, ARRAY_SIZE(jason6410_uartcfgs));
  123. }

  124. static void __init jason6410_machine_init(void)
  125. {
  126.     s3c_device_nand.name = "s3c6410-nand";
  127.     s3c_nand_set_platdata(&jason6410_nand_info);

  128.     s3c_i2c0_set_platdata(NULL);
  129.     
  130.     /*
  131.      * 在添加如下加粗代码后,内核报错,如下图,必须用:
  132.      * make KALLSYMS_EXTRA_PASS=1 uImage -j4
  133.      * 添加红色部分才能顺利通过编译,具体原因尚未清楚
  134.      */
  135.     if (ARRAY_SIZE(i2c_devs0)) { //i2c: add @2012.4.1
  136.         i2c_register_board_info(0, i2c_devs0, ARRAY_SIZE(i2c_devs0));
  137.     }
  138.     if (ARRAY_SIZE(i2c_devs1)) { //i2c: add @2012.4.1
  139.         i2c_register_board_info(1, i2c_devs1, ARRAY_SIZE(i2c_devs1));
  140.     }

  141.     platform_add_devices(jason6410_devices, ARRAY_SIZE(jason6410_devices));
  142. }

  143. MACHINE_START(JASON6410, "JASON6410")
  144.     /* Maintainer: Darius Augulis <augulis.darius@gmail.com> */
  145.     .atag_offset    = 0x100,
  146.     .init_irq    = s3c6410_init_irq,
  147.     .map_io        = jason6410_map_io,
  148.     .init_machine    = jason6410_machine_init,
  149.     .timer        = &s3c24xx_timer,
  150. MACHINE_END

三、内核配置
Device Drivers  --->
<*> I2C support --->
I2C Hardware Bus support --->
<*> S3C2410 I2C Driver //默认配置里是已选的

Device Drivers  --->
[*] Misc devices  --->  
EEPROM support --->
<*> I2C EEPROMs from most vendors //输入"Y",选择编译进内核

最后,在启动信息里,多出如下红色方框里几行。上面一个红色方框里的是s3c6410适配器的device和driver匹配成功后,在调用probe函数时,probe函数及probe函数里s3c24xx_i2c_init函数的的打印信息。
下面一个方框里的打印信息是BSP中i2c_board_info结构体中的I2C_BOARD_INFO成员里的.name成员24c08与at24.c里的at24_ids结构体里的某一成员名匹配成功后,调用probe函数,该函数打印出如此信息。

四、简单测试

附录:upgrading-clients: Upgrading I2C Drivers to the new 2.6 Driver Model

点击(此处)折叠或打开

  1. Upgrading I2C Drivers to the new 2.6 Driver Model
  2. =================================================

  3. Ben Dooks <ben-linux@fluff.org>

  4. Introduction
  5. ------------

  6. This guide outlines how to alter existing Linux 2.6 client drivers from
  7. the old to the new new binding methods.


  8. Example old-style driver
  9. ------------------------


  10. struct example_state {
  11.     struct i2c_client    client;
  12.     ....
  13. };

  14. static struct i2c_driver example_driver;

  15. static unsigned short ignore[] = { I2C_CLIENT_END };
  16. static unsigned short normal_addr[] = { OUR_ADDR, I2C_CLIENT_END };

  17. I2C_CLIENT_INSMOD;

  18. static int example_attach(struct i2c_adapter *adap, int addr, int kind)
  19. {
  20.     struct example_state *state;
  21.     struct device *dev = &adap->dev; /* to use for dev_ reports */
  22.     int ret;

  23.     state = kzalloc(sizeof(struct example_state), GFP_KERNEL);
  24.     if (state == NULL) {
  25.         dev_err(dev, "failed to create our state\n");
  26.         return -ENOMEM;
  27.     }

  28.     example->client.addr = addr;
  29.     example->client.flags = 0;
  30.     example->client.adapter = adap;

  31.     i2c_set_clientdata(&state->i2c_client, state);
  32.     strlcpy(client->i2c_client.name, "example", I2C_NAME_SIZE);

  33.     ret = i2c_attach_client(&state->i2c_client);
  34.     if (ret < 0) {
  35.         dev_err(dev, "failed to attach client\n");
  36.         kfree(state);
  37.         return ret;
  38.     }

  39.     dev = &state->i2c_client.dev;

  40.     /* rest of the initialisation goes here. */

  41.     dev_info(dev, "example client created\n");

  42.     return 0;
  43. }

  44. static int example_detach(struct i2c_client *client)
  45. {
  46.     struct example_state *state = i2c_get_clientdata(client);

  47.     i2c_detach_client(client);
  48.     kfree(state);
  49.     return 0;
  50. }

  51. static int example_attach_adapter(struct i2c_adapter *adap)
  52. {
  53.     return i2c_probe(adap, &addr_data, example_attach);
  54. }

  55. static struct i2c_driver example_driver = {
  56.      .driver        = {
  57.         .owner        = THIS_MODULE,
  58.         .name        = "example",
  59.     },
  60.     .attach_adapter = example_attach_adapter,
  61.     .detach_client    = example_detach,
  62.     .suspend    = example_suspend,
  63.     .resume        = example_resume,
  64. };


  65. Updating the client
  66. -------------------

  67. The new style binding model will check against a list of supported
  68. devices and their associated address supplied by the code registering
  69. the busses. This means that the driver .attach_adapter and
  70. .detach_client methods can be removed, along with the addr_data,
  71. as follows:

  72. - static struct i2c_driver example_driver;

  73. - static unsigned short ignore[] = { I2C_CLIENT_END };
  74. - static unsigned short normal_addr[] = { OUR_ADDR, I2C_CLIENT_END };

  75. - I2C_CLIENT_INSMOD;

  76. - static int example_attach_adapter(struct i2c_adapter *adap)
  77. - {
  78. -     return i2c_probe(adap, &addr_data, example_attach);
  79. - }

  80.  static struct i2c_driver example_driver = {
  81. -    .attach_adapter = example_attach_adapter,
  82. -    .detach_client    = example_detach,
  83.  }

  84. Add the probe and remove methods to the i2c_driver, as so:

  85.  static struct i2c_driver example_driver = {
  86. +    .probe        = example_probe,
  87. +    .remove        = example_remove,
  88.  }

  89. Change the example_attach method to accept the new parameters
  90. which include the i2c_client that it will be working with:

  91. - static int example_attach(struct i2c_adapter *adap, int addr, int kind)
  92. + static int example_probe(struct i2c_client *client,
  93. +             const struct i2c_device_id *id)

  94. Change the name of example_attach to example_probe to align it with the
  95. i2c_driver entry names. The rest of the probe routine will now need to be
  96. changed as the i2c_client has already been setup for use.

  97. The necessary client fields have already been setup before
  98. the probe function is called, so the following client setup
  99. can be removed:

  100. -    example->client.addr = addr;
  101. -    example->client.flags = 0;
  102. -    example->client.adapter = adap;
  103. -
  104. -    strlcpy(client->i2c_client.name, "example", I2C_NAME_SIZE);

  105. The i2c_set_clientdata is now:

  106. -    i2c_set_clientdata(&state->client, state);
  107. +    i2c_set_clientdata(client, state);

  108. The call to i2c_attach_client is no longer needed, if the probe
  109. routine exits successfully, then the driver will be automatically
  110. attached by the core. Change the probe routine as so:

  111. -    ret = i2c_attach_client(&state->i2c_client);
  112. -    if (ret < 0) {
  113. -        dev_err(dev, "failed to attach client\n");
  114. -        kfree(state);
  115. -        return ret;
  116. -    }


  117. Remove the storage of 'struct i2c_client' from the 'struct example_state'
  118. as we are provided with the i2c_client in our example_probe. Instead we
  119. store a pointer to it for when it is needed.

  120. struct example_state {
  121. -    struct i2c_client    client;
  122. +    struct i2c_client    *client;

  123. the new i2c client as so:

  124. -    struct device *dev = &adap->dev; /* to use for dev_ reports */
  125. +     struct device *dev = &i2c_client->dev; /* to use for dev_ reports */

  126. And remove the change after our client is attached, as the driver no
  127. longer needs to register a new client structure with the core:

  128. -    dev = &state->i2c_client.dev;

  129. In the probe routine, ensure that the new state has the client stored
  130. in it:

  131. static int example_probe(struct i2c_client *i2c_client,
  132.              const struct i2c_device_id *id)
  133. {
  134.     struct example_state *state;
  135.      struct device *dev = &i2c_client->dev;
  136.     int ret;

  137.     state = kzalloc(sizeof(struct example_state), GFP_KERNEL);
  138.     if (state == NULL) {
  139.         dev_err(dev, "failed to create our state\n");
  140.         return -ENOMEM;
  141.     }

  142. +    state->client = i2c_client;

  143. Update the detach method, by changing the name to _remove and
  144. to delete the i2c_detach_client call. It is possible that you
  145. can also remove the ret variable as it is not not needed for
  146. any of the core functions.

  147. - static int example_detach(struct i2c_client *client)
  148. + static int example_remove(struct i2c_client *client)
  149. {
  150.     struct example_state *state = i2c_get_clientdata(client);

  151. -    i2c_detach_client(client);

  152. And finally ensure that we have the correct ID table for the i2c-core
  153. and other utilities:

  154. + struct i2c_device_id example_idtable[] = {
  155. + { "example", 0 },
  156. + { }
  157. +};
  158. +
  159. +MODULE_DEVICE_TABLE(i2c, example_idtable);

  160. static struct i2c_driver example_driver = {
  161.      .driver        = {
  162.         .owner        = THIS_MODULE,
  163.         .name        = "example",
  164.     },
  165. +    .id_table    = example_ids,


  166. Our driver should now look like this:

  167. struct example_state {
  168.     struct i2c_client    *client;
  169.     ....
  170. };

  171. static int example_probe(struct i2c_client *client,
  172.               const struct i2c_device_id *id)
  173. {
  174.     struct example_state *state;
  175.     struct device *dev = &client->dev;

  176.     state = kzalloc(sizeof(struct example_state), GFP_KERNEL);
  177.     if (state == NULL) {
  178.         dev_err(dev, "failed to create our state\n");
  179.         return -ENOMEM;
  180.     }

  181.     state->client = client;
  182.     i2c_set_clientdata(client, state);

  183.     /* rest of the initialisation goes here. */

  184.     dev_info(dev, "example client created\n");

  185.     return 0;
  186. }

  187. static int example_remove(struct i2c_client *client)
  188. {
  189.     struct example_state *state = i2c_get_clientdata(client);

  190.     kfree(state);
  191.     return 0;
  192. }

  193. static struct i2c_device_id example_idtable[] = {
  194.     { "example", 0 },
  195.     { }
  196. };

  197. MODULE_DEVICE_TABLE(i2c, example_idtable);

  198. static struct i2c_driver example_driver = {
  199.      .driver        = {
  200.         .owner        = THIS_MODULE,
  201.         .name        = "example",
  202.     },
  203.     .id_table    = example_idtable,
  204.     .probe        = example_probe,
  205.     .remove        = example_remove,
  206.     .suspend    = example_suspend,
  207.     .resume        = example_resume,
  208. };






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