LINUX的IIC驱动从这开始(四)

转载地址:http://blog.youkuaiyun.com/xie0812/article/details/24291153

首先这篇文章主要介绍IIC总线的实现过程,怎么说了,本人也是一个小菜鸟,可能有许多错误在里面,如有大神发现,请指出来,多谢多谢!

注意:平台还是和前面的一样,所以分析三星的iic总线实现,当然这部分,可能不需要咱们驱动工程师实现,但本人认为好好研究这部分内容有助于提高水平,也能更好的理解linux的设备模型,不过今天仅仅讨论三星iic总线驱动具体是怎么操作s5pv210上的iic模块的,至于涉及到的platform总线还有设备模型会在接下来的文章中讨论,希望能给像我一样的菜鸟一点帮助!

1、首先说一下主要的讨论内容,主要是通过解释下面几个问题为指引展开的

(1)怎么把s5pv210上的iic模块注册到linux中的?

(2)iic模块是怎么获得时钟的,以及怎么样才能调节时钟的快慢?

(3)iic的总线驱动是怎么注册到iic-core当中的?

(4)具体怎么把一个字符通过片上iic模块发送出去?

好吧,咱们就按问题来吧。三星的iic总线驱动的位置在:linux-3.0.8/drivers/i2c/busses/i2c-s3c2410.c就是这个文件了,整个程序也就是1000行吧。那先从init和exit看起吧,这是看驱动的出发点嘛,为方便起见,我就直接附上代码了

  1. static int __init i2c_adap_s3c_init(void)  
  2. {  
  3.     return platform_driver_register(&s3c24xx_i2c_driver);  
  4. }  
  5. subsys_initcall(i2c_adap_s3c_init);  
  6.   
  7. static void __exit i2c_adap_s3c_exit(void)  
  8. {  
  9.     platform_driver_unregister(&s3c24xx_i2c_driver);  
  10. }  
  11. module_exit(i2c_adap_s3c_exit);  
可以很清楚的看到,它使用platform总线以驱动的形式注册到内核里面的。这里注意一下,总线也是设备,当然可能你早已经知道了。是设备嘛,肯定就需要驱动了,要不然linux的应用层就没法用这个iic模块了,那这是驱动,那对应的设备的信息在什么地方了?说明一下,我用的是友善的开发板,所以就直接用友善提供的linux源码包了,你可以在linux-3.0.8/arch/arm/mach-s5pv210/mach-mini210.c这个文件中看到如下的函数:

  1. static struct platform_device *mini210_devices[] __initdata = {  
  2.     ...  
  3.     <span style="color:#ff0000;">&s3c_device_i2c0,  
  4.     &s3c_device_i2c1,  
  5.     &s3c_device_i2c2,</span>}  
看到了吧,用platform_device结构体定义的这个指针数组中,有上面的三项,这是三项就是s5pv210上面的iic模块的信息,你可以点进去看看。当然,这仅仅是信息,还没有注册到platform总线上了,还是在这个文件中,下面的代码就是注册设备了。

static void __init mini210_machine_init(void)
{
......
platform_add_devices(mini210_devices, ARRAY_SIZE(mini210_devices));
......
}
上面的这个函数就是把刚才那个指针数组中定义的设备信息注册到platform总线上了。至于当给platform总线上注册一个驱动后内核如何进行操作,这里就不讨论了,等那天再写一篇吧。当注册到platform总线上的驱动的名字和已经注册到platform上的设备的名字匹配后,就会调用probe函数,其实许多事都是在probe函数做的,我下面直接把代码附上,直接在代码里做解释吧。

  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;  //通过这里获得了,iic模块的一些附加信息,包括总线设备号、iic的时钟频率等  
  9.     if (!pdata) {  
  10.         dev_err(&pdev->dev, "no platform data\n");  
  11.         return -EINVAL;  
  12.     }  
  13.   
  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.         //下面的这几句很关键,你应该还记得iic中的adapter这个结构体吧,它就代表一个iic适配器  
  20.     strlcpy(i2c->adap.name, "s3c2410-i2c"sizeof(i2c->adap.name));//给总线取名字  
  21.     i2c->adap.owner   = THIS_MODULE;  
  22.     i2c->adap.algo    = <span style="color:#ff0000;">&s3c24xx_i2c_algorithm</span>;//这个是最重要的,是iic适配器对应的通信方法,也就是具体怎么把数据通过iic模块传输出去的方法,这也是我们的主要问题之一,所以会在后面好好讨论一下  
  23.     i2c->adap.retries = 2;  
  24.     i2c->adap.class   = I2C_CLASS_HWMON | I2C_CLASS_SPD;  
  25.     i2c->tx_setup     = 50;//这个在通信方法里面可以看到,就是每次发送数据后的要延时的ns数  
  26.   
  27.     spin_lock_init(&i2c->lock);  
  28.     init_waitqueue_head(&i2c->wait);  
  29.   
  30.     /* find the clock and enable it */  
  31. <span style="white-space:pre">  </span>  
  32.     i2c->dev = &pdev->dev;  
  33.     i2c->clk = clk_get(&pdev->dev, "i2c");//获取iic适配器的时钟,至于具体通过怎么用名字匹配获取等,这个这里就不说了  
  34.   
  35.     if (IS_ERR(i2c->clk)) {  
  36.         dev_err(&pdev->dev, "cannot get clock\n");  
  37.         ret = -ENOENT;  
  38.         goto err_noclk;  
  39.     }  
  40.   
  41.     dev_dbg(&pdev->dev, "clock source %p\n", i2c->clk);  
  42.   
  43.     clk_enable(i2c->clk);//这里就把iic设备的时钟给使能了,仅仅是使能,通信的频率不是这里调节的啊,具体怎么调节通信的频率高低我会详细介绍  
  44.   
  45.     /* map the registers */  
  46. <span style="white-space:pre">  </span>//下面就是通过调用相应的函数获取我们在mach-mini210.c文件里注册到platform总线上的iic适配器的信息了,下面是获取内存资源  
  47.     res = platform_get_resource(pdev, IORESOURCE_MEM, 0);  
  48.     if (res == NULL) {  
  49.         dev_err(&pdev->dev, "cannot find IO resource\n");  
  50.         ret = -ENOENT;  
  51.         goto err_clk;  
  52.     }  
  53. <span style="white-space:pre">  </span>//这里根据获取到的地址和大小,在内存中申请这样一块大小的内存区  
  54.     i2c->ioarea = request_mem_region(res->start, resource_size(res),  
  55.                      pdev->name);  
  56.   
  57.     if (i2c->ioarea == NULL) {  
  58.         dev_err(&pdev->dev, "cannot request IO\n");  
  59.         ret = -ENXIO;  
  60.         goto err_clk;  
  61.     }  
  62. <span style="white-space:pre">  </span>//这里就是用申请到的内存做实际的映射了  
  63.     i2c->regs = ioremap(res->start, resource_size(res));  
  64.   
  65.     if (i2c->regs == NULL) {  
  66.         dev_err(&pdev->dev, "cannot map IO\n");  
  67.         ret = -ENXIO;  
  68.         goto err_ioarea;  
  69.     }  
  70.   
  71.     dev_dbg(&pdev->dev, "registers %p (%p, %p)\n",  
  72.         i2c->regs, i2c->ioarea, res);  
  73.   
  74.     /* setup info block for the i2c core */  
  75.   
  76.     i2c->adap.algo_data = i2c;  
  77.     i2c->adap.dev.parent = &pdev->dev;  
  78.   
  79.     /* initialise the i2c controller */  
  80. <span style="white-space:pre">  </span>//这个函数里,主要就是通过配置iic适配器的几个寄存器,来完成实际的适配器的初始化,上面的过程主要是获得适配器的资源,比如说地址了,寄存器基地址什么的  
  81.     ret = s3c24xx_i2c_init(i2c);  
  82.     if (ret != 0)  
  83.         goto err_iomap;  
  84.   
  85.     /* find the IRQ for this unit (note, this relies on the init call to 
  86.      * ensure no current IRQs pending 
  87.      */  
  88. <span style="white-space:pre">  </span>//下面这句话就是获取iic适配器的中断资源,其实iic适配器实际传输数据时用中断的方式完成的,所以作用是可想而知了  
  89.     i2c->irq = ret = platform_get_irq(pdev, 0);  
  90.     if (ret <= 0) {  
  91.         dev_err(&pdev->dev, "cannot find IRQ\n");  
  92.         goto err_iomap;  
  93.     }  
  94. <span style="white-space:pre">  </span>//这就是把中断注册到内核中的函数了,注意这里的s3c24xx_i2c_irq这个中断服务函数,其实在这里真真完成数据传输  
  95.     ret = request_irq(i2c->irq, s3c24xx_i2c_irq, IRQF_DISABLED,  
  96.               dev_name(&pdev->dev), i2c);  
  97.   
  98.     if (ret != 0) {  
  99.         dev_err(&pdev->dev, "cannot claim IRQ %d\n", i2c->irq);  
  100.         goto err_iomap;  
  101.     }  
  102.   
  103.     ret = s3c24xx_i2c_register_cpufreq(i2c);  
  104.     if (ret < 0) {  
  105.         dev_err(&pdev->dev, "failed to register cpufreq notifier\n");  
  106.         goto err_irq;  
  107.     }  
  108.   
  109.     /* Note, previous versions of the driver used i2c_add_adapter() 
  110.      * to add the bus at any number. We now pass the bus number via 
  111.      * the platform data, so if unset it will now default to always 
  112.      * being bus 0. 
  113.      */  
  114.   
  115.     i2c->adap.nr = pdata->bus_num;  
  116. <span style="white-space:pre">  </span>//下面这个函数式iic-core里面的函数,这个函数的作用就是把上面获取的iic适配器的注册为iic总线设备,具体的实现可以看看iic-core  
  117.     ret = i2c_add_numbered_adapter(&i2c->adap);  
  118.     if (ret < 0) {  
  119.         dev_err(&pdev->dev, "failed to add bus to i2c core\n");  
  120.         goto err_cpufreq;  
  121.     }  
  122.   
  123.     platform_set_drvdata(pdev, i2c);  
  124.   
  125.     dev_info(&pdev->dev, "%s: S3C I2C adapter\n", dev_name(&i2c->adap.dev));  
  126.     clk_disable(i2c->clk);  
  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;  
  149. }  
看到上面的用红颜色标出来的s3c24xx_algorithm这个结构里吧,这个结构体实现了iic适配器具体的通信方法,具体如下:

  1. static const struct i2c_algorithm s3c24xx_i2c_algorithm = {  
  2.     .master_xfer        = s3c24xx_i2c_xfer, //这个函数是具体的通信方法  
  3.     .functionality      = s3c24xx_i2c_func, //这个函数描述的iic适配器的功能特性  
  4. };  
由于s3c24xx_i2c_xfer是真真的通信方法,那咱们就再看看它的具体实现吧,看这神秘的通信方法。

  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.     clk_enable(i2c->clk); //使能时钟嘛  
  9.   
  10.     for (retry = 0; retry < adap->retries; retry++) {  //这个循环来完成实际的数据传输,循环的次数可以自定义了,这是为了保证数据传输的可靠性嘛  
  11.   
  12.         ret = s3c24xx_i2c_doxfer(i2c, msgs, num);//还记得iic里面重要的那个几个数据结构嘛,其中一个就是msg吧,在linux中,把要通过iic传输的具体数据会封装成msg结构的一个包,它里面包含的要传输数据的长度,设备的地址,要传输的数据。看出来吧,还没到到真真传输数据了,内核会调用这个函数  
  13.   
  14.         if (ret != -EAGAIN) { //你看,只要以传输成功,马上就返回了  
  15.             clk_disable(i2c->clk);  
  16.             return ret;  
  17.         }  
  18.   
  19.         dev_dbg(i2c->dev, "Retrying transmission (%d)\n", retry);  
  20.   
  21.         udelay(100);  
  22.     }  
  23.   
  24.     clk_disable(i2c->clk);  
  25.     return -EREMOTEIO;  
  26. }  
好吧,还没到真真传输数据的函数,这黄金一层又一层的,但这样写的确是有道理的,就比如说通过上面的for循环能加强数据传输的可靠性。好吧,咱们就硬着坚硬的头继续吧。

  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); //这个函数就是看iic适配器是否在忙,如果在忙就等待,直到把得到这个iic适配器的使用权  
  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;  //上面这几句就是把msg结构体赋值给在前面全局定义的iic结构体,因为还要包含其他信息嘛  
  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.     udelay(10);  
  44.   
  45.  out:  
  46.     return ret;  
  47. }  
哎,还是没有到真真要传输的函数啊,那为什么要写这个函数啊,因为在有三个iic模块嘛,当然多少模块都行,这样写可以让三个模块或更多的模块公用这个函数,减少代码重复量嘛,膜拜一下这些高手吧,好吧,生活还得继续,那咱们也不能不前进啊,看看s3c24xx_i2c_message_irq这个函数吧

  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) {  //看到了吧,这就是msg结构体里面的flag起的作用,判断是否是接收数据,也就是读数据了  
  12.         stat |= S3C2410_IICSTAT_MASTER_RX;  
  13.         addr |= 1;  
  14.     } else  
  15.         stat |= S3C2410_IICSTAT_MASTER_TX;//这当然就是发送数据了  
  16.   
  17.     if (msg->flags & I2C_M_REV_DIR_ADDR)  
  18.         addr ^= 1;  
  19.   
  20.     /* todo - check for wether ack wanted or not */  
  21.     s3c24xx_i2c_enable_ack(i2c);  
  22.   
  23.     iiccon = readl(i2c->regs + S3C2410_IICCON);  
  24.     writel(stat, i2c->regs + S3C2410_IICSTAT);  
  25.   
  26.     dev_dbg(i2c->dev, "START: %08lx to IICSTAT, %02x to DS\n", stat, addr);  
  27.     writeb(addr, i2c->regs + S3C2410_IICDS);  
  28.   
  29.     /* delay here to ensure the data byte has gotten onto the bus 
  30.      * before the transaction is started */  
  31.   
  32.     ndelay(i2c->tx_setup);  
  33.   
  34.     dev_dbg(i2c->dev, "iiccon, %08lx\n", iiccon);  
  35.     writel(iiccon, i2c->regs + S3C2410_IICCON);  
  36.   
  37.     stat |= S3C2410_IICSTAT_START;  
  38.     writel(stat, i2c->regs + S3C2410_IICSTAT);  //上面这些通过readl和writel来读取和设置了一些iic适配器的寄存器,最后边的这句话,是最关键的,它设置iic适配器为开始状态,这样就能触发中断,来完成实际数据的传输,用中断传输的好处我就不说了,想来大家都会知道啊。  
  39. }  
这家伙终于启动了中断,要完成数据的传输了。中断具体怎么传输的,这个大家可以看看,做过单片机的人肯定还是挺亲切的。发现说了这么多,其实才把刚开始提出的问题1和问题4讨论一下,2和3还没有详细的讨论,那咱们就在下一篇继续讨论吧。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值