I2C驱动分析,好文!!



 

Linux I2C驱动完全分析(二)

标签: clinuxstructalgorithmtable
  14323人阅读  评论(25)  收藏  举报
  分类:
Linux设备驱动程序第三版学习笔记(17) 

博主按:大热的天,刚刚负重从五道口走到石板房,大约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

我的同类文章

Linux设备驱动程序第三版学习笔记(17)
主题推荐
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网站的观点或立场
核心技术类目
全部主题  Hadoop  AWS  移动游戏  Java  Android  iOS  Swift  智能硬件  Docker  OpenStack VPN  Spark  ERP  IE10  Eclipse  CRM  JavaScript  数据库  Ubuntu  NFC  WAP  jQuery  BI HTML5  Spring  Apache  .NET  API  HTML  SDK  IIS  Fedora  XML  LBS  Unity  Splashtop UML  components  Windows Mobile  Rails  QEMU  KDE  Cassandra  CloudStack  FTC  coremail OPhone  CouchBase  云计算  iOS6  Rackspace  Web App  SpringSide  Maemo  Compuware 大数据  aptech  Perl  Tornado  Ruby  Hibernate  ThinkPHP  HBase  Pure  Solr  Angular Cloud Foundry  Redis  Scala  Django  Bootstrap
    个人资料
    I2C驱动分析,好文!!_第1张图片 
    小雷总
     
    • 访问:227557次
    • 积分:2684
    • 等级: 
    • 排名:第7731名
    • 原创:44篇
    • 转载:46篇
    • 译文:1篇
    • 评论:146条
    文章分类
  • 和小雷一起学开发(1)
  • C++/C#(9)
  • CPLD(0)
  • DSP(0)
  • linux学习(19)
  • Linux设备驱动程序第三版学习笔记(18)
  • qpeGPS(0)
  • Qt学习(10)
  • TX-2440A开发板(2)
  • 各种算法研究(1)
  • 嵌入式系统移植(0)
  • 市场营销(3)
  • 心情文章(5)
  • 成长中的研发经理(10)
  • 源代码(1)
  • 电路设计(7)
  • 视频技术(1)
  • 项目管理(0)
  • 企业管理(1)
    文章存档
  • 2015年08月(1)
  • 2015年01月(1)
  • 2014年07月(1)
  • 2014年05月(1)
  • 2014年04月(1)
    展开
    阅读排行
  • Linux I2C驱动完全分析(一)(27925)
  • Linux DM9000网卡驱动程序完全分析(27116)
  • 安装tslib中遇到的错误:./autogen.sh: 4: autoreconf: not found(23994)
  • Linux I2C驱动完全分析(二)(14318)
  • Qt函数之QPainter::drawImage(7520)
  • 不懂技术 如何管理好研发部门?(6097)
  • Linux驱动开发环境配置(内核源码树构造)(5082)
  • 加了醋的啤酒(4937)
  • C# winform DataGridView 操作大全(4751)
  • 基于Qt的GPS导航系统软件源代码(4551)
    评论排行
  • Linux DM9000网卡驱动程序完全分析(53)
  • Linux I2C驱动完全分析(二)(25)
  • 基于Qt的GPS导航系统软件源代码(10)
  • Linux I2C驱动完全分析(一)(10)
  • 安装tslib中遇到的错误:./autogen.sh: 4: autoreconf: not found(8)
  • 周立功:我的成功可以复制(5)
  • 基于Qt的GPS导航系统(4)
  • v4l2驱动编写篇(4)
  • Linux内核sk_buff的结构分析(3)
  • S3C2410看门狗驱动分析(3)
    最新评论
  • Linux I2C驱动完全分析(二)

    terry_wang2003: 非常感谢,让我耳目一新, 后续三 还有么?

  • Linux DM9000网卡驱动程序完全分析

    No威_: 发错了,不好意思

  • Linux DM9000网卡驱动程序完全分析

    No威_: 都是内核里面的。。。

  • C# winform DataGridView 操作大全

    youshuai168: Thanks 这个比较有用

  • 关于SetupDiEnumDeviceInfo枚举设备返回false问题的解决办法

    KiteRunner1992: 您好,请问cbsize是怎么计算的,为什么32位系统就是28,64位系统就是32?多谢指教。。

  • 关于SetupDiEnumDeviceInfo枚举设备返回false问题的解决办法

    KiteRunner1992: 您好,请问cbsize是怎么计算的?为啥32位系统就是28,64位系统就是32?多谢指教。。

  • 周立功:我的成功可以复制

    小雷总: @phker:你是做什么行业的?

  • 周立功:我的成功可以复制

    小雷总: @phker:我在技术上是挺顽固的,“顽固”我当做褒义词来听。哈哈哈

  • 周立功:我的成功可以复制

    走错路的程序员: 说真的,你老不要生气.我看到你照片的第一印象认为你是那种专注技术方向的技术顽固.不好沟通的人.看过你...

  • 周立功:我的成功可以复制

    走错路的程序员: 它山之石可以攻玉,减少“阶段0”的开发 注重核心技术,其余的外包这两点我深有体会啊.


 

Linux I2C驱动完全分析(二)

标签: clinuxstructalgorithmtable
  14323人阅读  评论(25)  收藏  举报
  分类:
Linux设备驱动程序第三版学习笔记(17) 

博主按:大热的天,刚刚负重从五道口走到石板房,大约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

我的同类文章

Linux设备驱动程序第三版学习笔记(17)
主题推荐
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网站的观点或立场
核心技术类目
全部主题  Hadoop  AWS  移动游戏  Java  Android  iOS  Swift  智能硬件  Docker  OpenStack VPN  Spark  ERP  IE10  Eclipse  CRM  JavaScript  数据库  Ubuntu  NFC  WAP  jQuery  BI HTML5  Spring  Apache  .NET  API  HTML  SDK  IIS  Fedora  XML  LBS  Unity  Splashtop UML  components  Windows Mobile  Rails  QEMU  KDE  Cassandra  CloudStack  FTC  coremail OPhone  CouchBase  云计算  iOS6  Rackspace  Web App  SpringSide  Maemo  Compuware 大数据  aptech  Perl  Tornado  Ruby  Hibernate  ThinkPHP  HBase  Pure  Solr  Angular Cloud Foundry  Redis  Scala  Django  Bootstrap
    个人资料
    I2C驱动分析,好文!!_第2张图片 
    小雷总
     
    • 访问:227557次
    • 积分:2684
    • 等级: 
    • 排名:第7731名
    • 原创:44篇
    • 转载:46篇
    • 译文:1篇
    • 评论:146条
    文章分类
  • 和小雷一起学开发(1)
  • C++/C#(9)
  • CPLD(0)
  • DSP(0)
  • linux学习(19)
  • Linux设备驱动程序第三版学习笔记(18)
  • qpeGPS(0)
  • Qt学习(10)
  • TX-2440A开发板(2)
  • 各种算法研究(1)
  • 嵌入式系统移植(0)
  • 市场营销(3)
  • 心情文章(5)
  • 成长中的研发经理(10)
  • 源代码(1)
  • 电路设计(7)
  • 视频技术(1)
  • 项目管理(0)
  • 企业管理(1)
    文章存档
  • 2015年08月(1)
  • 2015年01月(1)
  • 2014年07月(1)
  • 2014年05月(1)
  • 2014年04月(1)
    展开
    阅读排行
  • Linux I2C驱动完全分析(一)(27925)
  • Linux DM9000网卡驱动程序完全分析(27116)
  • 安装tslib中遇到的错误:./autogen.sh: 4: autoreconf: not found(23994)
  • Linux I2C驱动完全分析(二)(14318)
  • Qt函数之QPainter::drawImage(7520)
  • 不懂技术 如何管理好研发部门?(6097)
  • Linux驱动开发环境配置(内核源码树构造)(5082)
  • 加了醋的啤酒(4937)
  • C# winform DataGridView 操作大全(4751)
  • 基于Qt的GPS导航系统软件源代码(4551)
    评论排行
  • Linux DM9000网卡驱动程序完全分析(53)
  • Linux I2C驱动完全分析(二)(25)
  • 基于Qt的GPS导航系统软件源代码(10)
  • Linux I2C驱动完全分析(一)(10)
  • 安装tslib中遇到的错误:./autogen.sh: 4: autoreconf: not found(8)
  • 周立功:我的成功可以复制(5)
  • 基于Qt的GPS导航系统(4)
  • v4l2驱动编写篇(4)
  • Linux内核sk_buff的结构分析(3)
  • S3C2410看门狗驱动分析(3)
    最新评论
  • Linux I2C驱动完全分析(二)

    terry_wang2003: 非常感谢,让我耳目一新, 后续三 还有么?

  • Linux DM9000网卡驱动程序完全分析

    No威_: 发错了,不好意思

  • Linux DM9000网卡驱动程序完全分析

    No威_: 都是内核里面的。。。

  • C# winform DataGridView 操作大全

    youshuai168: Thanks 这个比较有用

  • 关于SetupDiEnumDeviceInfo枚举设备返回false问题的解决办法

    KiteRunner1992: 您好,请问cbsize是怎么计算的,为什么32位系统就是28,64位系统就是32?多谢指教。。

  • 关于SetupDiEnumDeviceInfo枚举设备返回false问题的解决办法

    KiteRunner1992: 您好,请问cbsize是怎么计算的?为啥32位系统就是28,64位系统就是32?多谢指教。。

  • 周立功:我的成功可以复制

    小雷总: @phker:你是做什么行业的?

  • 周立功:我的成功可以复制

    小雷总: @phker:我在技术上是挺顽固的,“顽固”我当做褒义词来听。哈哈哈

  • 周立功:我的成功可以复制

    走错路的程序员: 说真的,你老不要生气.我看到你照片的第一印象认为你是那种专注技术方向的技术顽固.不好沟通的人.看过你...

  • 周立功:我的成功可以复制

    走错路的程序员: 它山之石可以攻玉,减少“阶段0”的开发 注重核心技术,其余的外包这两点我深有体会啊.

你可能感兴趣的:(I2C驱动分析,好文!!)