I2C驱动分析,好文!!



 

Linux I2C驱动完全分析(二)

标签: clinuxstructalgorithmtable
  14323人阅读  评论(25)  收藏  举报
  分类:

博主按:大热的天,刚刚负重从五道口走到石板房,大约4公里吧。终于让我找了一个咖啡屋休息一下,继续写这篇驱动分析。单身的生活就是这样无聊啊。 不发牢骚了,活出个样儿来给自己看!千难万险脚下踩,啥也难不倒咱!继续整!~

 

先说一下,本文中有个疑惑,一直没有搞懂,写在这里,望高人指点一二,不胜感激!

#define I2C_M_NOSTART  0x4000 /* if I2C_FUNC_PROTOCOL_MANGLING */
#define I2C_M_REV_DIR_ADDR 0x2000 /* if I2C_FUNC_PROTOCOL_MANGLING */
#define I2C_M_IGNORE_NAK 0x1000 /* if I2C_FUNC_PROTOCOL_MANGLING */
#define I2C_M_NO_RD_ACK  0x0800 /* if I2C_FUNC_PROTOCOL_MANGLING */

这里I2C_FUNC_PROTOCOL_MANGLING 是什么意思?为什么定义这些东东?看了注释也不太理解。求解释!

 

3. I2C总线驱动代码分析

   s3c2440的总线驱动代码在i2c-s3c2410.c中。照例先从init看起。

[c-sharp]  view plain copy
  1. static int __init i2c_adap_s3c_init(void)  
  2. {  
  3.     return platform_driver_register(&s3c24xx_i2c_driver);  
  4. }  

在init中只是调用了平台驱动注册函数注册了一个i2c的平台驱动s3c24xx_i2c_driver。这个驱动是一个platform_driver的结构体变量。注意这里不是i2c_driver结构体,因为i2c_driver是对设备的驱动,而这里对控制器的驱动要使用platform_driver

[c-sharp]  view plain copy
  1. static struct platform_driver s3c24xx_i2c_driver = {  
  2.     .probe      = s3c24xx_i2c_probe,  
  3.     .remove     = s3c24xx_i2c_remove,  
  4.     .suspend_late   = s3c24xx_i2c_suspend_late,  
  5.     .resume     = s3c24xx_i2c_resume,  
  6.     .id_table   = s3c24xx_driver_ids,  
  7.     .driver     = {  
  8.         .owner  = THIS_MODULE,  
  9.         .name   = "s3c-i2c",  
  10.     },  
  11. };  

同样的,重要的函数还是那几个:probe,remove,suspend_late,resume。再加上一个id_table和device_driver结构体变量。

下面逐个分析:

* probe函数

当调用platform_driver_register函数注册platform_driver结构体时,probe指针指向的s3c24xx_i2c_probe函数将会被调用。这部分详细解释参考本博客另一篇文章《S3C2410看门狗驱动分析》。细心的朋友可能会发现,在s3c24xx_i2c_driver中,驱动的名字是"s3c-i2c",而在板文件中可以看到,设备的名字是"s3c2410-i2c",这两个名字不一样,那驱动和设备是如何match的呢?答案就在于id_table。这个id_table包含了驱动所支持的设备ID表。在match的时候,判断这个表中的名字是不是和设备一致,一致则match成功。这也是为什么一个驱动可以同时match成功多个设备的原因。如果只是靠platform_driver-->driver中的名字来匹配的话,那么驱动和设备只能是一对一的关系了。

[c-sharp]  view plain copy
  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. };  

扯远了,还是看看probe的代码吧~

[c-sharp]  view plain copy
  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.   
  8.     pdata = pdev->dev.platform_data;  
  9.     if (!pdata) {  
  10.         dev_err(&pdev->dev, "no platform data/n");  
  11.         return -EINVAL;  
  12.     }  
  13.         //给s3c24xx_i2c结构体申请空间  
  14.     i2c = kzalloc(sizeof(struct s3c24xx_i2c), GFP_KERNEL);  
  15.     if (!i2c) {  
  16.         dev_err(&pdev->dev, "no memory for state/n");  
  17.         return -ENOMEM;  
  18.     }  
  19.         //填充s3c24xx_i2c结构体中各项,包括名称、所有者、算法、所属class等等  
  20.     strlcpy(i2c->adap.name, "s3c2410-i2c"sizeof(i2c->adap.name));  
  21.     i2c->adap.owner   = THIS_MODULE;  
  22.     i2c->adap.algo    = &s3c24xx_i2c_algorithm; //这个下面会重点介绍  
  23.     i2c->adap.retries = 2;  
  24.     i2c->adap.class   = I2C_CLASS_HWMON | I2C_CLASS_SPD;  
  25.     i2c->tx_setup     = 50;  
  26.   
  27.     spin_lock_init(&i2c->lock);  
  28.     init_waitqueue_head(&i2c->wait);  
  29.   
  30.     /* find the clock and enable it */  
  31.         // 找到i2c始终并且使能它  
  32.     i2c->dev = &pdev->dev;  
  33.     i2c->clk = clk_get(&pdev->dev, "i2c");  
  34.     if (IS_ERR(i2c->clk)) {  
  35.         dev_err(&pdev->dev, "cannot get clock/n");  
  36.         ret = -ENOENT;  
  37.         goto err_noclk;  
  38.     }  
  39.   
  40.     dev_dbg(&pdev->dev, "clock source %p/n", i2c->clk);  
  41.   
  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.     /* initialise the i2c controller */  
  79.         /*s3c24xx_i2c结构体变量i2c的必要的信息都填充完了以后,开始进行初始化*/  
  80.     ret = s3c24xx_i2c_init(i2c);  
  81.     if (ret != 0)  
  82.         goto err_iomap;  
  83.   
  84.     /* find the IRQ for this unit (note, this relies on the init call to 
  85.      * ensure no current IRQs pending 
  86.      */  
  87.         //接下来申请中断  
  88.     i2c->irq = ret = platform_get_irq(pdev, 0);  
  89.     if (ret <= 0) {  
  90.         dev_err(&pdev->dev, "cannot find IRQ/n");  
  91.         goto err_iomap;  
  92.     }  
  93.   
  94.     ret = request_irq(i2c->irq, s3c24xx_i2c_irq, IRQF_DISABLED,  
  95.               dev_name(&pdev->dev), i2c);  
  96.   
  97.     if (ret != 0) {  
  98.         dev_err(&pdev->dev, "cannot claim IRQ %d/n", i2c->irq);  
  99.         goto err_iomap;  
  100.     }  
  101.           
  102.     ret = s3c24xx_i2c_register_cpufreq(i2c);  
  103.     if (ret < 0) {  
  104.         dev_err(&pdev->dev, "failed to register cpufreq notifier/n");  
  105.         goto err_irq;  
  106.     }  
  107.   
  108.     /* Note, previous versions of the driver used i2c_add_adapter() 
  109.      * to add the bus at any number. We now pass the bus number via 
  110.      * the platform data, so if unset it will now default to always 
  111.      * being bus 0. 
  112.      */  
  113.   
  114.     i2c->adap.nr = pdata->bus_num;  
  115.   
  116.         //看到了吧?下面调用了i2c-core中的i2c_add_adapter函数来添加一个i2c控制器  
  117.           //i2c_add_numbered_adapter和i2c_add_adapter的区别在于前者用来添加一个在CPU内  
  118.           //部集成的适配器,而后者用来添加一个CPU外部的适配器。显然这里应该用前者。  
  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.   
  125.     platform_set_drvdata(pdev, i2c);  
  126.   
  127.     dev_info(&pdev->dev, "%s: S3C I2C adapter/n", dev_name(&i2c->adap.dev));  
  128.     return 0;  
  129.   
  130.  err_cpufreq:  
  131.     s3c24xx_i2c_deregister_cpufreq(i2c);  
  132.   
  133.  err_irq:  
  134.     free_irq(i2c->irq, i2c);  
  135.   
  136.  err_iomap:  
  137.     iounmap(i2c->regs);  
  138.   
  139.  err_ioarea:  
  140.     release_resource(i2c->ioarea);  
  141.     kfree(i2c->ioarea);  
  142.   
  143.  err_clk:  
  144.     clk_disable(i2c->clk);  
  145.     clk_put(i2c->clk);  
  146.   
  147.  err_noclk:  
  148.     kfree(i2c);  
  149.     return ret;  
  150. }  

 

*remove函数

这是和probe相反的一个函数,在i2c_adap_s3c_exit时调用。主要功能是注销适配器,释放中断,释放内存区域,禁止始终等等。看到上边代码中的err_的各个部分了吧? remove是它们的汇总。

 

*suspend函数和resume函数

把这两个放一起说吧,挂起和恢复函数。挂起时保存状态并置标志位,恢复时重新初始化i2c适配器并置标志位。

 

Algorithm

 哎呀我去,终于到这了。憋得我难受啊。这里要重点介绍一下,不仅要知其然,还要知其所以然,这样我们以后自己写驱动的时候就有把握了。

[c-sharp]  view plain copy
  1. static const struct i2c_algorithm s3c24xx_i2c_algorithm = {  
  2.     .master_xfer        = s3c24xx_i2c_xfer,  
  3.     .functionality      = s3c24xx_i2c_func,  
  4. };  

这里实现的就是这个s3c24xx_i2c_xfer。这个是控制器能不能动作的关键,缺了这个,控制器就是废铜烂铁。

[c-sharp]  view plain copy
  1. static int s3c24xx_i2c_xfer(struct i2c_adapter *adap,  
  2.             struct i2c_msg *msgs, int num)  
  3. {  
  4.     struct s3c24xx_i2c *i2c = (struct s3c24xx_i2c *)adap->algo_data;  
  5.     int retry;  
  6.     int ret;  
  7.   
  8.     for (retry = 0; retry < adap->retries; retry++) {  
  9.   
  10.         ret = s3c24xx_i2c_doxfer(i2c, msgs, num);  
  11.   
  12.         if (ret != -EAGAIN)  
  13.             return ret;  
  14.   
  15.         dev_dbg(i2c->dev, "Retrying transmission (%d)/n", retry);  
  16.   
  17.         udelay(100);  
  18.     }  
  19.   
  20.     return -EREMOTEIO;  
  21. }  

 

完成任务的函数是s3c24xx_i2c_doxfer(),源码清单如下,

[c-sharp]  view plain copy
  1. static int s3c24xx_i2c_doxfer(struct s3c24xx_i2c *i2c,  
  2.                   struct i2c_msg *msgs, int num)  
  3. {  
  4.     unsigned long timeout;  
  5.     int ret;  
  6.   
  7.     if (i2c->suspended)  
  8.         return -EIO;  
  9.   
  10.     ret = s3c24xx_i2c_set_master(i2c);  
  11.     if (ret != 0) {  
  12.         dev_err(i2c->dev, "cannot get bus (error %d)/n", ret);  
  13.         ret = -EAGAIN;  
  14.         goto out;  
  15.     }  
  16.   
  17.     spin_lock_irq(&i2c->lock);  
  18.   
  19.     i2c->msg     = msgs;  
  20.     i2c->msg_num = num;  
  21.     i2c->msg_ptr = 0;  
  22.     i2c->msg_idx = 0;  
  23.     i2c->state   = STATE_START;  
  24.   
  25.     s3c24xx_i2c_enable_irq(i2c);  
  26.     s3c24xx_i2c_message_start(i2c, msgs);  
  27.     spin_unlock_irq(&i2c->lock);  
  28.   
  29.     timeout = wait_event_timeout(i2c->wait, i2c->msg_num == 0, HZ * 5);  
  30.   
  31.     ret = i2c->msg_idx;  
  32.   
  33.     /* having these next two as dev_err() makes life very 
  34.      * noisy when doing an i2cdetect */  
  35.   
  36.     if (timeout == 0)  
  37.         dev_dbg(i2c->dev, "timeout/n");  
  38.     else if (ret != num)  
  39.         dev_dbg(i2c->dev, "incomplete xfer (%d)/n", ret);  
  40.   
  41.     /* ensure the stop has been through the bus */  
  42.   
  43.     msleep(1);  
  44.   
  45.  out:  
  46.     return ret;  
  47. }  

上面代码可以分成几个部分来看:

  * s3c24xx_i2c_set_master() 这个函数每隔1ms查看一次i2c总线状态,timeout是400ms,如果在这期间总线状态不忙,则返回零。否则返回-ETIMEDOUT

  * 将要发送的消息和其他信息付给i2c->msg和其他变量,并将状态设置为STATE_START

  * s3c24xx_i2c_enable_irq() 使能中断

  * s3c24xx_i2c_message_start() 重中之重啊。在看代码之前先来看看2440的datasheet上是怎么说的吧。

代码清单如下:

[c-sharp]  view plain copy
  1. static void s3c24xx_i2c_message_start(struct s3c24xx_i2c *i2c,  
  2.                       struct i2c_msg *msg)  
  3. {  
  4.     unsigned int addr = (msg->addr & 0x7f) << 1;  
  5.     unsigned long stat;  
  6.     unsigned long iiccon;  
  7.   
  8.     stat = 0;  
  9.     stat |=  S3C2410_IICSTAT_TXRXEN;  
  10.   
  11.     if (msg->flags & I2C_M_RD) {//如果是read data, from slave to master     stat |= S3C2410_IICSTAT_MASTER_RX;  
  12.         addr |= 1;  
  13.     } else  
  14.         stat |= S3C2410_IICSTAT_MASTER_TX;  
  15.   
  16.     if (msg->flags & I2C_M_REV_DIR_ADDR)  
  17.         addr ^= 1;  
  18.   
  19.     /* todo - check for wether ack wanted or not */  
  20.     s3c24xx_i2c_enable_ack(i2c);  
  21.   
  22.     iiccon = readl(i2c->regs + S3C2410_IICCON);  
  23.     writel(stat, i2c->regs + S3C2410_IICSTAT);  
  24.   
  25.     dev_dbg(i2c->dev, "START: %08lx to IICSTAT, %02x to DS/n", stat, addr);  
  26.     writeb(addr, i2c->regs + S3C2410_IICDS);  
  27.   
  28.     /* delay here to ensure the data byte has gotten onto the bus 
  29.      * before the transaction is started */  
  30.   
  31.     ndelay(i2c->tx_setup);  
  32.   
  33.     dev_dbg(i2c->dev, "iiccon, %08lx/n", iiccon);  
  34.     writel(iiccon, i2c->regs + S3C2410_IICCON);  
  35.   
  36.     stat |= S3C2410_IICSTAT_START;  
  37.     writel(stat, i2c->regs + S3C2410_IICSTAT);  
  38. }  

 

(今天没写完啊,明天继续~)

0
0
我的同类文章
主题推荐
c语言 linux 生活
猜你在找
查看评论
25楼  terry_wang2003 2015-09-20 18:54发表 [回复]
非常感谢,让我耳目一新, 后续三 还有么?
24楼  Healthy_蓝 2014-11-25 09:38发表 [回复]
楼主啊,你倒是继续写撒...这葵花宝典看着难受啊...走火入魔中.....
23楼  guaiguaixiangai 2014-11-24 22:51发表 [回复]
里面提到timeout = 400ms 是在哪里赋值的,在哪里看到的呀?
22楼  sdkhy0808 2014-07-05 13:21发表 [回复]
我也期待着继续写下去呢
21楼  sdkhy0808 2014-07-05 13:20发表 [回复]
转了哈
20楼  sdkhy0808 2014-07-05 13:20发表 [回复]
赞!
19楼  云在青天水在瓶-_- 2013-08-10 00:33发表 [回复]
先说一下,本文中有个疑惑,一直没有搞懂
这几个宏定义是GPIO模拟i2c总线算法实现用的
18楼  yuelengloulan 2013-06-17 14:36发表 [回复]
楼主文章很精简清晰,怎么不继续写了啊
17楼  tian1112yong 2013-05-27 17:27发表 [回复]
后续呢,楼主。
16楼  TC_CT 2013-04-09 11:57发表 [回复]
博主居然没有坚持写完,唉,失望了
15楼  刘二傻 2013-04-01 17:36发表 [回复]
怎么没有下面了吗?
14楼  kcchao 2012-11-05 17:40发表 [回复]
For I2C_FUNC_PROTOCOL_MANGLING, you can see the explanation in http://www.kernel.org/doc/Documentation/i2c/i2c-protocol
13楼  way 2012-08-02 23:44发表 [回复]
写得很好,把总线驱动分析得不错,要是把设备驱动的几种写法都分析一下,那i2c驱动就算很完美了,不过现在的方法,不管是写总线驱动,还是设备驱动,都够了,不过三星公司的总线驱动太复杂
12楼  bbs598598 2012-07-31 15:56发表 [回复]
顶一个,翘首等待
11楼  Ugly_Jorney 2012-07-24 14:13发表 [回复]
顶起!
10楼  zgwstar 2012-07-20 20:06发表 [回复]
太佩服楼主了 向楼主学习
9楼  jackylongchen 2012-06-15 16:19发表 [回复]
来学习
8楼  maybe2101 2012-04-21 15:27发表 [回复]
难道不会出后续了么?%>_<%
7楼  yhl2007kaka 2011-11-23 11:15发表 [回复]
支持楼主!写的确实非常好!
6楼  junziyou1324325 2011-10-28 10:39发表 [回复]
太期盼了!!楼主说的再详细一点吧!!
5楼  烂笔头 2011-10-11 09:54发表 [回复]
楼主,请问一下如果从机是单片机而不是一个芯片应该怎么写驱动啊?
4楼  laobenzhuang1 2011-09-02 23:23发表 [回复]
等待三、四的出现,楼主写的太好了
3楼  lwc92008 2011-08-10 12:30发表 [回复]
关于I2C_M_NOSTART之类的困惑可以在
Document/i2c/i2c-protocol
文件找到说明。
2楼  yanyang_031 2011-07-27 15:19发表 [回复]
翘首等待三、四的出现,楼主写的太好了~~
1楼  Quietly 2011-06-30 17:53发表 [回复]
[e03]
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
  • 个人资料
  •  
    • 访问:227557次
    • 积分:2684
    • 等级: 
    • 排名:第7731名
    • 原创:44篇
    • 转载:46篇
    • 译文:1篇
    • 评论:146条
  • 文章存档
  • 最新评论

 

Linux I2C驱动完全分析(二)

标签: clinuxstructalgorithmtable
  14323人阅读  评论(25)  收藏  举报
  分类:

博主按:大热的天,刚刚负重从五道口走到石板房,大约4公里吧。终于让我找了一个咖啡屋休息一下,继续写这篇驱动分析。单身的生活就是这样无聊啊。 不发牢骚了,活出个样儿来给自己看!千难万险脚下踩,啥也难不倒咱!继续整!~

 

先说一下,本文中有个疑惑,一直没有搞懂,写在这里,望高人指点一二,不胜感激!

#define I2C_M_NOSTART  0x4000 /* if I2C_FUNC_PROTOCOL_MANGLING */
#define I2C_M_REV_DIR_ADDR 0x2000 /* if I2C_FUNC_PROTOCOL_MANGLING */
#define I2C_M_IGNORE_NAK 0x1000 /* if I2C_FUNC_PROTOCOL_MANGLING */
#define I2C_M_NO_RD_ACK  0x0800 /* if I2C_FUNC_PROTOCOL_MANGLING */

这里I2C_FUNC_PROTOCOL_MANGLING 是什么意思?为什么定义这些东东?看了注释也不太理解。求解释!

 

3. I2C总线驱动代码分析

   s3c2440的总线驱动代码在i2c-s3c2410.c中。照例先从init看起。

[c-sharp]  view plain copy
  1. static int __init i2c_adap_s3c_init(void)  
  2. {  
  3.     return platform_driver_register(&s3c24xx_i2c_driver);  
  4. }  

在init中只是调用了平台驱动注册函数注册了一个i2c的平台驱动s3c24xx_i2c_driver。这个驱动是一个platform_driver的结构体变量。注意这里不是i2c_driver结构体,因为i2c_driver是对设备的驱动,而这里对控制器的驱动要使用platform_driver

[c-sharp]  view plain copy
  1. static struct platform_driver s3c24xx_i2c_driver = {  
  2.     .probe      = s3c24xx_i2c_probe,  
  3.     .remove     = s3c24xx_i2c_remove,  
  4.     .suspend_late   = s3c24xx_i2c_suspend_late,  
  5.     .resume     = s3c24xx_i2c_resume,  
  6.     .id_table   = s3c24xx_driver_ids,  
  7.     .driver     = {  
  8.         .owner  = THIS_MODULE,  
  9.         .name   = "s3c-i2c",  
  10.     },  
  11. };  

同样的,重要的函数还是那几个:probe,remove,suspend_late,resume。再加上一个id_table和device_driver结构体变量。

下面逐个分析:

* probe函数

当调用platform_driver_register函数注册platform_driver结构体时,probe指针指向的s3c24xx_i2c_probe函数将会被调用。这部分详细解释参考本博客另一篇文章《S3C2410看门狗驱动分析》。细心的朋友可能会发现,在s3c24xx_i2c_driver中,驱动的名字是"s3c-i2c",而在板文件中可以看到,设备的名字是"s3c2410-i2c",这两个名字不一样,那驱动和设备是如何match的呢?答案就在于id_table。这个id_table包含了驱动所支持的设备ID表。在match的时候,判断这个表中的名字是不是和设备一致,一致则match成功。这也是为什么一个驱动可以同时match成功多个设备的原因。如果只是靠platform_driver-->driver中的名字来匹配的话,那么驱动和设备只能是一对一的关系了。

[c-sharp]  view plain copy
  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. };  

扯远了,还是看看probe的代码吧~

[c-sharp]  view plain copy
  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.   
  8.     pdata = pdev->dev.platform_data;  
  9.     if (!pdata) {  
  10.         dev_err(&pdev->dev, "no platform data/n");  
  11.         return -EINVAL;  
  12.     }  
  13.         //给s3c24xx_i2c结构体申请空间  
  14.     i2c = kzalloc(sizeof(struct s3c24xx_i2c), GFP_KERNEL);  
  15.     if (!i2c) {  
  16.         dev_err(&pdev->dev, "no memory for state/n");  
  17.         return -ENOMEM;  
  18.     }  
  19.         //填充s3c24xx_i2c结构体中各项,包括名称、所有者、算法、所属class等等  
  20.     strlcpy(i2c->adap.name, "s3c2410-i2c"sizeof(i2c->adap.name));  
  21.     i2c->adap.owner   = THIS_MODULE;  
  22.     i2c->adap.algo    = &s3c24xx_i2c_algorithm; //这个下面会重点介绍  
  23.     i2c->adap.retries = 2;  
  24.     i2c->adap.class   = I2C_CLASS_HWMON | I2C_CLASS_SPD;  
  25.     i2c->tx_setup     = 50;  
  26.   
  27.     spin_lock_init(&i2c->lock);  
  28.     init_waitqueue_head(&i2c->wait);  
  29.   
  30.     /* find the clock and enable it */  
  31.         // 找到i2c始终并且使能它  
  32.     i2c->dev = &pdev->dev;  
  33.     i2c->clk = clk_get(&pdev->dev, "i2c");  
  34.     if (IS_ERR(i2c->clk)) {  
  35.         dev_err(&pdev->dev, "cannot get clock/n");  
  36.         ret = -ENOENT;  
  37.         goto err_noclk;  
  38.     }  
  39.   
  40.     dev_dbg(&pdev->dev, "clock source %p/n", i2c->clk);  
  41.   
  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.     /* initialise the i2c controller */  
  79.         /*s3c24xx_i2c结构体变量i2c的必要的信息都填充完了以后,开始进行初始化*/  
  80.     ret = s3c24xx_i2c_init(i2c);  
  81.     if (ret != 0)  
  82.         goto err_iomap;  
  83.   
  84.     /* find the IRQ for this unit (note, this relies on the init call to 
  85.      * ensure no current IRQs pending 
  86.      */  
  87.         //接下来申请中断  
  88.     i2c->irq = ret = platform_get_irq(pdev, 0);  
  89.     if (ret <= 0) {  
  90.         dev_err(&pdev->dev, "cannot find IRQ/n");  
  91.         goto err_iomap;  
  92.     }  
  93.   
  94.     ret = request_irq(i2c->irq, s3c24xx_i2c_irq, IRQF_DISABLED,  
  95.               dev_name(&pdev->dev), i2c);  
  96.   
  97.     if (ret != 0) {  
  98.         dev_err(&pdev->dev, "cannot claim IRQ %d/n", i2c->irq);  
  99.         goto err_iomap;  
  100.     }  
  101.           
  102.     ret = s3c24xx_i2c_register_cpufreq(i2c);  
  103.     if (ret < 0) {  
  104.         dev_err(&pdev->dev, "failed to register cpufreq notifier/n");  
  105.         goto err_irq;  
  106.     }  
  107.   
  108.     /* Note, previous versions of the driver used i2c_add_adapter() 
  109.      * to add the bus at any number. We now pass the bus number via 
  110.      * the platform data, so if unset it will now default to always 
  111.      * being bus 0. 
  112.      */  
  113.   
  114.     i2c->adap.nr = pdata->bus_num;  
  115.   
  116.         //看到了吧?下面调用了i2c-core中的i2c_add_adapter函数来添加一个i2c控制器  
  117.           //i2c_add_numbered_adapter和i2c_add_adapter的区别在于前者用来添加一个在CPU内  
  118.           //部集成的适配器,而后者用来添加一个CPU外部的适配器。显然这里应该用前者。  
  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.   
  125.     platform_set_drvdata(pdev, i2c);  
  126.   
  127.     dev_info(&pdev->dev, "%s: S3C I2C adapter/n", dev_name(&i2c->adap.dev));  
  128.     return 0;  
  129.   
  130.  err_cpufreq:  
  131.     s3c24xx_i2c_deregister_cpufreq(i2c);  
  132.   
  133.  err_irq:  
  134.     free_irq(i2c->irq, i2c);  
  135.   
  136.  err_iomap:  
  137.     iounmap(i2c->regs);  
  138.   
  139.  err_ioarea:  
  140.     release_resource(i2c->ioarea);  
  141.     kfree(i2c->ioarea);  
  142.   
  143.  err_clk:  
  144.     clk_disable(i2c->clk);  
  145.     clk_put(i2c->clk);  
  146.   
  147.  err_noclk:  
  148.     kfree(i2c);  
  149.     return ret;  
  150. }  

 

*remove函数

这是和probe相反的一个函数,在i2c_adap_s3c_exit时调用。主要功能是注销适配器,释放中断,释放内存区域,禁止始终等等。看到上边代码中的err_的各个部分了吧? remove是它们的汇总。

 

*suspend函数和resume函数

把这两个放一起说吧,挂起和恢复函数。挂起时保存状态并置标志位,恢复时重新初始化i2c适配器并置标志位。

 

Algorithm

 哎呀我去,终于到这了。憋得我难受啊。这里要重点介绍一下,不仅要知其然,还要知其所以然,这样我们以后自己写驱动的时候就有把握了。

[c-sharp]  view plain copy
  1. static const struct i2c_algorithm s3c24xx_i2c_algorithm = {  
  2.     .master_xfer        = s3c24xx_i2c_xfer,  
  3.     .functionality      = s3c24xx_i2c_func,  
  4. };  

这里实现的就是这个s3c24xx_i2c_xfer。这个是控制器能不能动作的关键,缺了这个,控制器就是废铜烂铁。

[c-sharp]  view plain copy
  1. static int s3c24xx_i2c_xfer(struct i2c_adapter *adap,  
  2.             struct i2c_msg *msgs, int num)  
  3. {  
  4.     struct s3c24xx_i2c *i2c = (struct s3c24xx_i2c *)adap->algo_data;  
  5.     int retry;  
  6.     int ret;  
  7.   
  8.     for (retry = 0; retry < adap->retries; retry++) {  
  9.   
  10.         ret = s3c24xx_i2c_doxfer(i2c, msgs, num);  
  11.   
  12.         if (ret != -EAGAIN)  
  13.             return ret;  
  14.   
  15.         dev_dbg(i2c->dev, "Retrying transmission (%d)/n", retry);  
  16.   
  17.         udelay(100);  
  18.     }  
  19.   
  20.     return -EREMOTEIO;  
  21. }  

 

完成任务的函数是s3c24xx_i2c_doxfer(),源码清单如下,

[c-sharp]  view plain copy
  1. static int s3c24xx_i2c_doxfer(struct s3c24xx_i2c *i2c,  
  2.                   struct i2c_msg *msgs, int num)  
  3. {  
  4.     unsigned long timeout;  
  5.     int ret;  
  6.   
  7.     if (i2c->suspended)  
  8.         return -EIO;  
  9.   
  10.     ret = s3c24xx_i2c_set_master(i2c);  
  11.     if (ret != 0) {  
  12.         dev_err(i2c->dev, "cannot get bus (error %d)/n", ret);  
  13.         ret = -EAGAIN;  
  14.         goto out;  
  15.     }  
  16.   
  17.     spin_lock_irq(&i2c->lock);  
  18.   
  19.     i2c->msg     = msgs;  
  20.     i2c->msg_num = num;  
  21.     i2c->msg_ptr = 0;  
  22.     i2c->msg_idx = 0;  
  23.     i2c->state   = STATE_START;  
  24.   
  25.     s3c24xx_i2c_enable_irq(i2c);  
  26.     s3c24xx_i2c_message_start(i2c, msgs);  
  27.     spin_unlock_irq(&i2c->lock);  
  28.   
  29.     timeout = wait_event_timeout(i2c->wait, i2c->msg_num == 0, HZ * 5);  
  30.   
  31.     ret = i2c->msg_idx;  
  32.   
  33.     /* having these next two as dev_err() makes life very 
  34.      * noisy when doing an i2cdetect */  
  35.   
  36.     if (timeout == 0)  
  37.         dev_dbg(i2c->dev, "timeout/n");  
  38.     else if (ret != num)  
  39.         dev_dbg(i2c->dev, "incomplete xfer (%d)/n", ret);  
  40.   
  41.     /* ensure the stop has been through the bus */  
  42.   
  43.     msleep(1);  
  44.   
  45.  out:  
  46.     return ret;  
  47. }  

上面代码可以分成几个部分来看:

  * s3c24xx_i2c_set_master() 这个函数每隔1ms查看一次i2c总线状态,timeout是400ms,如果在这期间总线状态不忙,则返回零。否则返回-ETIMEDOUT

  * 将要发送的消息和其他信息付给i2c->msg和其他变量,并将状态设置为STATE_START

  * s3c24xx_i2c_enable_irq() 使能中断

  * s3c24xx_i2c_message_start() 重中之重啊。在看代码之前先来看看2440的datasheet上是怎么说的吧。

代码清单如下:

[c-sharp]  view plain copy
  1. static void s3c24xx_i2c_message_start(struct s3c24xx_i2c *i2c,  
  2.                       struct i2c_msg *msg)  
  3. {  
  4.     unsigned int addr = (msg->addr & 0x7f) << 1;  
  5.     unsigned long stat;  
  6.     unsigned long iiccon;  
  7.   
  8.     stat = 0;  
  9.     stat |=  S3C2410_IICSTAT_TXRXEN;  
  10.   
  11.     if (msg->flags & I2C_M_RD) {//如果是read data, from slave to master     stat |= S3C2410_IICSTAT_MASTER_RX;  
  12.         addr |= 1;  
  13.     } else  
  14.         stat |= S3C2410_IICSTAT_MASTER_TX;  
  15.   
  16.     if (msg->flags & I2C_M_REV_DIR_ADDR)  
  17.         addr ^= 1;  
  18.   
  19.     /* todo - check for wether ack wanted or not */  
  20.     s3c24xx_i2c_enable_ack(i2c);  
  21.   
  22.     iiccon = readl(i2c->regs + S3C2410_IICCON);  
  23.     writel(stat, i2c->regs + S3C2410_IICSTAT);  
  24.   
  25.     dev_dbg(i2c->dev, "START: %08lx to IICSTAT, %02x to DS/n", stat, addr);  
  26.     writeb(addr, i2c->regs + S3C2410_IICDS);  
  27.   
  28.     /* delay here to ensure the data byte has gotten onto the bus 
  29.      * before the transaction is started */  
  30.   
  31.     ndelay(i2c->tx_setup);  
  32.   
  33.     dev_dbg(i2c->dev, "iiccon, %08lx/n", iiccon);  
  34.     writel(iiccon, i2c->regs + S3C2410_IICCON);  
  35.   
  36.     stat |= S3C2410_IICSTAT_START;  
  37.     writel(stat, i2c->regs + S3C2410_IICSTAT);  
  38. }  

 

(今天没写完啊,明天继续~)

0
0
我的同类文章
主题推荐
c语言 linux 生活
猜你在找
查看评论
25楼  terry_wang2003 2015-09-20 18:54发表 [回复]
非常感谢,让我耳目一新, 后续三 还有么?
24楼  Healthy_蓝 2014-11-25 09:38发表 [回复]
楼主啊,你倒是继续写撒...这葵花宝典看着难受啊...走火入魔中.....
23楼  guaiguaixiangai 2014-11-24 22:51发表 [回复]
里面提到timeout = 400ms 是在哪里赋值的,在哪里看到的呀?
22楼  sdkhy0808 2014-07-05 13:21发表 [回复]
我也期待着继续写下去呢
21楼  sdkhy0808 2014-07-05 13:20发表 [回复]
转了哈
20楼  sdkhy0808 2014-07-05 13:20发表 [回复]
赞!
19楼  云在青天水在瓶-_- 2013-08-10 00:33发表 [回复]
先说一下,本文中有个疑惑,一直没有搞懂
这几个宏定义是GPIO模拟i2c总线算法实现用的
18楼  yuelengloulan 2013-06-17 14:36发表 [回复]
楼主文章很精简清晰,怎么不继续写了啊
17楼  tian1112yong 2013-05-27 17:27发表 [回复]
后续呢,楼主。
16楼  TC_CT 2013-04-09 11:57发表 [回复]
博主居然没有坚持写完,唉,失望了
15楼  刘二傻 2013-04-01 17:36发表 [回复]
怎么没有下面了吗?
14楼  kcchao 2012-11-05 17:40发表 [回复]
For I2C_FUNC_PROTOCOL_MANGLING, you can see the explanation in http://www.kernel.org/doc/Documentation/i2c/i2c-protocol
13楼  way 2012-08-02 23:44发表 [回复]
写得很好,把总线驱动分析得不错,要是把设备驱动的几种写法都分析一下,那i2c驱动就算很完美了,不过现在的方法,不管是写总线驱动,还是设备驱动,都够了,不过三星公司的总线驱动太复杂
12楼  bbs598598 2012-07-31 15:56发表 [回复]
顶一个,翘首等待
11楼  Ugly_Jorney 2012-07-24 14:13发表 [回复]
顶起!
10楼  zgwstar 2012-07-20 20:06发表 [回复]
太佩服楼主了 向楼主学习
9楼  jackylongchen 2012-06-15 16:19发表 [回复]
来学习
8楼  maybe2101 2012-04-21 15:27发表 [回复]
难道不会出后续了么?%>_<%
7楼  yhl2007kaka 2011-11-23 11:15发表 [回复]
支持楼主!写的确实非常好!
6楼  junziyou1324325 2011-10-28 10:39发表 [回复]
太期盼了!!楼主说的再详细一点吧!!
5楼  烂笔头 2011-10-11 09:54发表 [回复]
楼主,请问一下如果从机是单片机而不是一个芯片应该怎么写驱动啊?
4楼  laobenzhuang1 2011-09-02 23:23发表 [回复]
等待三、四的出现,楼主写的太好了
3楼  lwc92008 2011-08-10 12:30发表 [回复]
关于I2C_M_NOSTART之类的困惑可以在
Document/i2c/i2c-protocol
文件找到说明。
2楼  yanyang_031 2011-07-27 15:19发表 [回复]
翘首等待三、四的出现,楼主写的太好了~~
1楼  Quietly 2011-06-30 17:53发表 [回复]
[e03]
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
  • 个人资料
  •  
    • 访问:227557次
    • 积分:2684
    • 等级: 
    • 排名:第7731名
    • 原创:44篇
    • 转载:46篇
    • 译文:1篇
    • 评论:146条
  • 文章存档
  • 最新评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值