Chinaunix首页 | 论坛 | 博客
  • 博客访问: 205888
  • 博文数量: 91
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 15
  • 用 户 组: 普通用户
  • 注册时间: 2015-12-09 10:37
文章分类
文章存档

2016年(87)

2015年(4)

我的朋友

分类: LINUX

2016-03-29 13:43:22

原文地址: 作者:


1820人阅读 评论(8) 举报

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

  1. static int __init i2c_adap_s3c_init(void
  2.     return platform_driver_register(&s3c24xx_i2c_driver); 
static int __init i2c_adap_s3c_init(void) { return platform_driver_register(&s3c24xx_i2c_driver); }


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

  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. }; 
static struct platform_driver s3c24xx_i2c_driver = { .probe = s3c24xx_i2c_probe, .remove = s3c24xx_i2c_remove, .suspend_late = s3c24xx_i2c_suspend_late, .resume = s3c24xx_i2c_resume, .id_table = s3c24xx_driver_ids, .driver = { .owner = THIS_MODULE, .name = "s3c-i2c", }, };


同样的,重要的函数还是那几个: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中的名字来匹配的话,那么驱动和设备只能是一对一的关系了。

  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. }; 
static struct platform_device_id s3c24xx_driver_ids[] = { { .name = "s3c2410-i2c", .driver_data = TYPE_S3C2410, }, { .name = "s3c2440-i2c", .driver_data = TYPE_S3C2440, }, { }, };


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

  1. static int s3c24xx_i2c_probe(struct platform_device *pdev) 
  2.     struct s3c24xx_i2c *i2c; 
  3.     struct s3c2410_platform_i2c *pdata; 
  4.     struct resource *res; 
  5.     int ret; 
  6.  
  7.     pdata = pdev->dev.platform_data; 
  8.     if (!pdata) { 
  9.         dev_err(&pdev->dev, "no platform data/n"); 
  10.         return -EINVAL; 
  11.     } 
  12.         //给s3c24xx_i2c结构体申请空间  
  13.     i2c = kzalloc(sizeof(struct s3c24xx_i2c), GFP_KERNEL); 
  14.     if (!i2c) { 
  15.         dev_err(&pdev->dev, "no memory for state/n"); 
  16.         return -ENOMEM; 
  17.     } 
  18.         //填充s3c24xx_i2c结构体中各项,包括名称、所有者、算法、所属class等等  
  19.     strlcpy(i2c->adap.name, "s3c2410-i2c", sizeof(i2c->adap.name)); 
  20.     i2c->adap.owner   = THIS_MODULE; 
  21.     i2c->adap.algo    = &s3c24xx_i2c_algorithm; //这个下面会重点介绍  
  22.     i2c->adap.retries = 2; 
  23.     i2c->adap.class   = I2C_CLASS_HWMON | I2C_CLASS_SPD; 
  24.     i2c->tx_setup     = 50; 
  25.  
  26.     spin_lock_init(&i2c->lock); 
  27.     init_waitqueue_head(&i2c->wait); 
  28.  
  29.     /* find the clock and enable it */ 
  30.         // 找到i2c始终并且使能它  
  31.     i2c->dev = &pdev->dev; 
  32.     i2c->clk = clk_get(&pdev->dev, "i2c"); 
  33.     if (IS_ERR(i2c->clk)) { 
  34.         dev_err(&pdev->dev, "cannot get clock/n"); 
  35.         ret = -ENOENT; 
  36.         goto err_noclk; 
  37.     } 
  38.  
  39.     dev_dbg(&pdev->dev, "clock source %p/n", i2c->clk); 
  40.  
  41.     clk_enable(i2c->clk); 
  42.  
  43.     /* map the registers */ 
  44.         /*映射寄存器*/ 
  45.     res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 
  46.     if (res == NULL) { 
  47.         dev_err(&pdev->dev, "cannot find IO resource/n"); 
  48.         ret = -ENOENT; 
  49.         goto err_clk; 
  50.     } 
  51.  
  52.     i2c->ioarea = request_mem_region(res->start, resource_size(res), 
  53.                      pdev->name); 
  54.  
  55.     if (i2c->ioarea == NULL) { 
  56.         dev_err(&pdev->dev, "cannot request IO/n"); 
  57.         ret = -ENXIO; 
  58.         goto err_clk; 
  59.     } 
  60.  
  61.     i2c->regs = ioremap(res->start, resource_size(res)); 
  62.  
  63.     if (i2c->regs == NULL) { 
  64.         dev_err(&pdev->dev, "cannot map IO/n"); 
  65.         ret = -ENXIO; 
  66.         goto err_ioarea; 
  67.     } 
  68.  
  69.     dev_dbg(&pdev->dev, "registers %p (%p, %p)/n"
  70.         i2c->regs, i2c->ioarea, res); 
  71.  
  72.     /* setup info block for the i2c core */ 
  73.  
  74.     i2c->adap.algo_data = i2c; 
  75.     i2c->adap.dev.parent = &pdev->dev; 
  76.  
  77.     /* initialise the i2c controller */ 
  78.         /*s3c24xx_i2c结构体变量i2c的必要的信息都填充完了以后,开始进行初始化*/ 
  79.     ret = s3c24xx_i2c_init(i2c); 
  80.     if (ret != 0) 
  81.         goto err_iomap; 
  82.  
  83.     /* find the IRQ for this unit (note, this relies on the init call to
  84.      * ensure no current IRQs pending
  85.      */ 
  86.         //接下来申请中断  
  87.     i2c->irq = ret = platform_get_irq(pdev, 0); 
  88.     if (ret <= 0) { 
  89.         dev_err(&pdev->dev, "cannot find IRQ/n"); 
  90.         goto err_iomap; 
  91.     } 
  92.  
  93.     ret = request_irq(i2c->irq, s3c24xx_i2c_irq, IRQF_DISABLED, 
  94.               dev_name(&pdev->dev), i2c); 
  95.  
  96.     if (ret != 0) { 
  97.         dev_err(&pdev->dev, "cannot claim IRQ %d/n", i2c->irq); 
  98.         goto err_iomap; 
  99.     } 
  100.          
  101.     ret = s3c24xx_i2c_register_cpufreq(i2c); 
  102.     if (ret < 0) { 
  103.         dev_err(&pdev->dev, "failed to register cpufreq notifier/n"); 
  104.         goto err_irq; 
  105.     } 
  106.  
  107.     /* Note, previous versions of the driver used i2c_add_adapter()
  108.      * to add the bus at any number. We now pass the bus number via
  109.      * the platform data, so if unset it will now default to always
  110.      * being bus 0.
  111.      */ 
  112.  
  113.     i2c->adap.nr = pdata->bus_num; 
  114.  
  115.         //看到了吧?下面调用了i2c-core中的i2c_add_adapter函数来添加一个i2c控制器  
  116.           //i2c_add_numbered_adapter和i2c_add_adapter的区别在于前者用来添加一个在CPU内  
  117.           //部集成的适配器,而后者用来添加一个CPU外部的适配器。显然这里应该用前者。  
  118.     ret = i2c_add_numbered_adapter(&i2c->adap); 
  119.     if (ret < 0) { 
  120.         dev_err(&pdev->dev, "failed to add bus to i2c core/n"); 
  121.         goto err_cpufreq; 
  122.     } 
  123.  
  124.     platform_set_drvdata(pdev, i2c); 
  125.  
  126.     dev_info(&pdev->dev, "%s: S3C I2C adapter/n", dev_name(&i2c->adap.dev)); 
  127.     return 0; 
  128.  
  129. err_cpufreq: 
  130.     s3c24xx_i2c_deregister_cpufreq(i2c); 
  131.  
  132. err_irq: 
  133.     free_irq(i2c->irq, i2c); 
  134.  
  135. err_iomap: 
  136.     iounmap(i2c->regs); 
  137.  
  138. err_ioarea: 
  139.     release_resource(i2c->ioarea); 
  140.     kfree(i2c->ioarea); 
  141.  
  142. err_clk: 
  143.     clk_disable(i2c->clk); 
  144.     clk_put(i2c->clk); 
  145.  
  146. err_noclk: 
  147.     kfree(i2c); 
  148.     return ret; 
static int s3c24xx_i2c_probe(struct platform_device *pdev) { struct s3c24xx_i2c *i2c; struct s3c2410_platform_i2c *pdata; struct resource *res; int ret; pdata = pdev->dev.platform_data; if (!pdata) { dev_err(&pdev->dev, "no platform data/n"); return -EINVAL; }        //给s3c24xx_i2c结构体申请空间 i2c = kzalloc(sizeof(struct s3c24xx_i2c), GFP_KERNEL); if (!i2c) { dev_err(&pdev->dev, "no memory for state/n"); return -ENOMEM; }        //填充s3c24xx_i2c结构体中各项,包括名称、所有者、算法、所属class等等 strlcpy(i2c->adap.name, "s3c2410-i2c", sizeof(i2c->adap.name)); i2c->adap.owner   = THIS_MODULE; i2c->adap.algo    = &s3c24xx_i2c_algorithm; //这个下面会重点介绍 i2c->adap.retries = 2; i2c->adap.class   = I2C_CLASS_HWMON | I2C_CLASS_SPD; i2c->tx_setup     = 50; spin_lock_init(&i2c->lock); init_waitqueue_head(&i2c->wait); /* find the clock and enable it */        // 找到i2c始终并且使能它 i2c->dev = &pdev->dev; i2c->clk = clk_get(&pdev->dev, "i2c"); if (IS_ERR(i2c->clk)) { dev_err(&pdev->dev, "cannot get clock/n"); ret = -ENOENT; goto err_noclk; } dev_dbg(&pdev->dev, "clock source %p/n", i2c->clk); clk_enable(i2c->clk); /* map the registers */        /*映射寄存器*/ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (res == NULL) { dev_err(&pdev->dev, "cannot find IO resource/n"); ret = -ENOENT; goto err_clk; } i2c->ioarea = request_mem_region(res->start, resource_size(res), pdev->name); if (i2c->ioarea == NULL) { dev_err(&pdev->dev, "cannot request IO/n"); ret = -ENXIO; goto err_clk; } i2c->regs = ioremap(res->start, resource_size(res)); if (i2c->regs == NULL) { dev_err(&pdev->dev, "cannot map IO/n"); ret = -ENXIO; goto err_ioarea; } dev_dbg(&pdev->dev, "registers %p (%p, %p)/n", i2c->regs, i2c->ioarea, res); /* setup info block for the i2c core */ i2c->adap.algo_data = i2c; i2c->adap.dev.parent = &pdev->dev; /* initialise the i2c controller */        /*s3c24xx_i2c结构体变量i2c的必要的信息都填充完了以后,开始进行初始化*/ ret = s3c24xx_i2c_init(i2c); if (ret != 0) goto err_iomap; /* find the IRQ for this unit (note, this relies on the init call to * ensure no current IRQs pending */        //接下来申请中断 i2c->irq = ret = platform_get_irq(pdev, 0); if (ret <= 0) { dev_err(&pdev->dev, "cannot find IRQ/n"); goto err_iomap; } ret = request_irq(i2c->irq, s3c24xx_i2c_irq, IRQF_DISABLED,   dev_name(&pdev->dev), i2c); if (ret != 0) { dev_err(&pdev->dev, "cannot claim IRQ %d/n", i2c->irq); goto err_iomap; }        ret = s3c24xx_i2c_register_cpufreq(i2c); if (ret < 0) { dev_err(&pdev->dev, "failed to register cpufreq notifier/n"); goto err_irq; } /* Note, previous versions of the driver used i2c_add_adapter() * to add the bus at any number. We now pass the bus number via * the platform data, so if unset it will now default to always * being bus 0. */ i2c->adap.nr = pdata->bus_num;        //看到了吧?下面调用了i2c-core中的i2c_add_adapter函数来添加一个i2c控制器          //i2c_add_numbered_adapter和i2c_add_adapter的区别在于前者用来添加一个在CPU内          //部集成的适配器,而后者用来添加一个CPU外部的适配器。显然这里应该用前者。 ret = i2c_add_numbered_adapter(&i2c->adap); if (ret < 0) { dev_err(&pdev->dev, "failed to add bus to i2c core/n"); goto err_cpufreq; } platform_set_drvdata(pdev, i2c); dev_info(&pdev->dev, "%s: S3C I2C adapter/n", dev_name(&i2c->adap.dev)); return 0; err_cpufreq: s3c24xx_i2c_deregister_cpufreq(i2c); err_irq: free_irq(i2c->irq, i2c); err_iomap: iounmap(i2c->regs); err_ioarea: release_resource(i2c->ioarea); kfree(i2c->ioarea); err_clk: clk_disable(i2c->clk); clk_put(i2c->clk); err_noclk: kfree(i2c); return ret; }



*remove函数

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


*suspend函数和resume函数

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


Algorithm

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

  1. static const struct i2c_algorithm s3c24xx_i2c_algorithm = { 
  2.     .master_xfer        = s3c24xx_i2c_xfer, 
  3.     .functionality      = s3c24xx_i2c_func, 
  4. }; 
static const struct i2c_algorithm s3c24xx_i2c_algorithm = { .master_xfer = s3c24xx_i2c_xfer, .functionality = s3c24xx_i2c_func, };


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

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



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

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


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

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

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

  * s3c24xx_i2c_enable_irq() 使能中断

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

代码清单如下:

  1. static void s3c24xx_i2c_message_start(struct s3c24xx_i2c *i2c, 
  2.                       struct i2c_msg *msg) 
  3.     unsigned int addr = (msg->addr & 0x7f) << 1; 
  4.     unsigned long stat; 
  5.     unsigned long iiccon; 
  6.  
  7.     stat = 0; 
  8.     stat |=  S3C2410_IICSTAT_TXRXEN; 
  9.  
  10.     if (msg->flags & I2C_M_RD) {//如果是read data, from slave to master     stat |= S3C2410_IICSTAT_MASTER_RX;  
  11.         addr |= 1; 
  12.     } else 
  13.         stat |= S3C2410_IICSTAT_MASTER_TX; 
  14.  
  15.     if (msg->flags & I2C_M_REV_DIR_ADDR) 
  16.         addr ^= 1; 
  17.  
  18.     /* todo - check for wether ack wanted or not */ 
  19.     s3c24xx_i2c_enable_ack(i2c); 
  20.  
  21.     iiccon = readl(i2c->regs + S3C2410_IICCON); 
  22.     writel(stat, i2c->regs + S3C2410_IICSTAT); 
  23.  
  24.     dev_dbg(i2c->dev, "START: %08lx to IICSTAT, %02x to DS/n", stat, addr); 
  25.     writeb(addr, i2c->regs + S3C2410_IICDS); 
  26.  
  27.     /* delay here to ensure the data byte has gotten onto the bus
  28.      * before the transaction is started */ 
  29.  
  30.     ndelay(i2c->tx_setup); 
  31.  
  32.     dev_dbg(i2c->dev, "iiccon, %08lx/n", iiccon); 
  33.     writel(iiccon, i2c->regs + S3C2410_IICCON); 
  34.  
  35.     stat |= S3C2410_IICSTAT_START; 
  36.     writel(stat, i2c->regs + S3C2410_IICSTAT); 
http://blog.csdn.net/ypoflyer/article/details/6382097
阅读(1172) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~