OpenRisc-20-or1200下linux的i2c(二)

来源:互联网 发布:ubuntu下安装exe 编辑:程序博客网 时间:2024/05/21 11:29
这段时间开始有点小忙了,但是还是坚持学习一下linux,至于上次记得是基于linux系统自带的i2c-ocores.c文件接口写了简单的测试程序,这次就基于opencores社区上大虾写好的i2c controller总线驱动来做一下分析,这个总线驱动在openrisc-3.1\drivers\i2c\busses目录下~这个总线驱动是基于Richard Herveille编写的i2c-masteripcore,在opencores社区上可以下到这份RTL的代码,然后打开源码里面的i2c_specs说明文档看看这个ipcores的相关操作。

        openrisc-3.1\drivers\i2c\busses\i2c-ocores.c,好~Open之,

        然后回想一下之前blog里面写过的关于gpio字符驱动的内容,最后一步我们是把gpio的字符驱动封装成platform机制的,所以,照板煮碗,把代码拉到最后面就可以看到i2c-ocres.c也是同样的道理。


        First定义一个platform_driver结构

        Second初始化这个结构,指定其proberemove等函数,并初始化其中的driver变量

        Third实现其proberemove等函数

        以上是platform平台驱动的步骤。


        然后说明一下i2c总线驱动负责的工作是:

        First,申请i2c总线控制器所用到的硬件资源(IO、中断号、内存等等)。

        Second,向内核增加一个i2c_adapter数据结构表示这个总线驱动,并且实现i2c_adapter数据结构中的algo成员(即控制i2c控制器的代码)。


        所以总线驱动代码的综合效果就是将上面驱动负责的工作写入platform平台驱动,并注册完成i2c总线驱动。


        下面来分析一下~

        首先,例化关于i2c的平台驱动,这里编写的是对应i2c的总线驱动,所以在例化i2c总线驱动时的of_match_table列表要对应在DTS设备树文件中i2c的总线控制器的compatible,在module加载入kernel时才会按照名称搜索加载i2c的总线驱动。


        DTS设备树i2c描述:

[cpp] view plaincopyprint?
  1. i2c0: ocores@93000000 {  
  2.              compatible = "opencores,i2c-ocores";  
  3.              reg = <0x93000000 0x8>;  
  4.              interrupts = <5>;  
  5.                
  6.              regstep = <1>;  
  7.              clock-frequency = <40000000>;  
  8.                
  9.              #address-cells = <1>;  
  10.              #size-cells = <0>;  
  11.              eeprom@54 {  
  12.                     compatible = "at24";  
  13.                     reg = <0x54>;  
  14.     };  
  15. };  

        总线驱动注册代码:

[cpp] view plaincopyprint?
  1. static struct of_device_id ocores_i2c_match[] = {  
  2.     { .compatible = "opencores,i2c-ocores", },  
  3.     {},  
  4. };  
  5. MODULE_DEVICE_TABLE(of, ocores_i2c_match);  
  6.   
  7. /* work with hotplug and coldplug */  
  8. MODULE_ALIAS("platform:ocores-i2c");  
  9.   
  10. static struct platform_driver ocores_i2c_driver = {  
  11.     .probe   = ocores_i2c_probe,  
  12.     .remove  = __devexit_p(ocores_i2c_remove),  
  13.     .suspend = ocores_i2c_suspend,  
  14.     .resume  = ocores_i2c_resume,  
  15.     .driver  = {  
  16.         .owner = THIS_MODULE,  
  17.         .name = "ocores-i2c",  
  18.         .of_match_table = ocores_i2c_match,  
  19.     },  
  20. };  
  21.   
  22. static int __init ocores_i2c_init(void)  
  23. {  
  24.     return platform_driver_register(&ocores_i2c_driver);  
  25. }  
  26.   
  27. static void __exit ocores_i2c_exit(void)  
  28. {  
  29.     platform_driver_unregister(&ocores_i2c_driver);  
  30. }  
  31.   
  32. module_init(ocores_i2c_init);  
  33. module_exit(ocores_i2c_exit);  


        然后,要实现proberemove等函数

[cpp] view plaincopyprint?
  1. static int __devinit ocores_i2c_probe(struct platform_device *pdev)  
  2. {  
  3.     struct ocores_i2c *i2c;  
  4.     struct ocores_i2c_platform_data *pdata;  
  5.     struct resource *res, *res2;  
  6.     int ret;  
  7.     int i;  
  8.   
  9.     //获取DTS文件中的IO资源(地址范围)  
  10.     res = platform_get_resource(pdev, IORESOURCE_MEM, 0);  
  11.     if (!res)  
  12.         return -ENODEV;  
  13.   
  14.     //获取DTS文件中的中断号资源  
  15.     res2 = platform_get_resource(pdev, IORESOURCE_IRQ, 0);  
  16.     if (!res2)  
  17.         return -ENODEV;  
  18.   
  19.     //申请内存  
  20.     i2c = devm_kzalloc(&pdev->dev, sizeof(*i2c), GFP_KERNEL);  
  21.     if (!i2c)  
  22.         return -ENOMEM;  
  23.   
  24.     //检查是否有足够内存  
  25.     if (!devm_request_mem_region(&pdev->dev, res->start,  
  26.                      resource_size(res), pdev->name)) {  
  27.         dev_err(&pdev->dev, "Memory region busy\n");  
  28.         return -EBUSY;  
  29.     }  
  30.   
  31.     //i2c控制器wishbone总线物理地址映射到内核地址空间  
  32.     i2c->base = devm_ioremap_nocache(&pdev->dev, res->start,  
  33.                      resource_size(res));  
  34.     if (!i2c->base) {  
  35.         dev_err(&pdev->dev, "Unable to map registers\n");  
  36.         return -EIO;  
  37.     }  
  38.   
  39.     //例化一个指针指向platform_device的设备的私有数据域,这个数据从编译DTS文件时获取  
  40.     pdata = pdev->dev.platform_data;  
  41.     if (pdata) {  
  42.         i2c->regstep = pdata->regstep;  
  43.         i2c->clock_khz = pdata->clock_khz;  
  44.     } else {  
  45.         ret = ocores_i2c_of_probe(pdev, i2c);  
  46.         if (ret)  
  47.             return ret;  
  48.     }  
  49.   
  50.     //初始化i2c控制器,具体可参考i2c_specs  
  51.     ocores_init(i2c);  
  52.   
  53.     //初始化等待队列,目前我还没明白这个,水平有限  
  54.     init_waitqueue_head(&i2c->wait);  
  55.   
  56.     //申请设备使用的中断号,并注册中断函数  
  57.     ret = devm_request_irq(&pdev->dev, res2->start, ocores_isr, 0,  
  58.                    pdev->name, i2c);  
  59.     if (ret) {  
  60.         dev_err(&pdev->dev, "Cannot claim IRQ\n");  
  61.         return ret;  
  62.     }  
  63.   
  64.     /* hook up driver to tree */  
  65.     //将i2c保存到platform_device中dev成员的p成员的driver_data数据域中  
  66.     platform_set_drvdata(pdev, i2c);  
  67.     i2c->adap = ocores_adapter;  
  68.   
  69.     //将i2c保存到i2c_adapter中dev成员的p成员的driver_data数据域中  
  70.     i2c_set_adapdata(&i2c->adap, i2c);  
  71.     i2c->adap.dev.parent = &pdev->dev;  
  72.     i2c->adap.dev.of_node = pdev->dev.of_node;  
  73.   
  74.     /* add i2c adapter to i2c tree */  
  75.     //增加i2c adapter的数据结构  
  76.     ret = i2c_add_adapter(&i2c->adap);  
  77.     if (ret) {  
  78.         dev_err(&pdev->dev, "Failed to add adapter\n");  
  79.         return ret;  
  80.     }  
  81.   
  82.     /* add in known devices to the bus */  
  83.     //如果在DTS文件中有多个共享总线驱动的从设备便会枚举出来  
  84.     if (pdata) {  
  85.         for (i = 0; i < pdata->num_devices; i++)  
  86.             i2c_new_device(&i2c->adap, pdata->devices + i);  
  87.     }  
  88.   
  89.     return 0;  
  90. }  


[cpp] view plaincopyprint?
  1. static int __devexit ocores_i2c_remove(struct platform_device* pdev)  
  2. {  
  3.     //将之前保存在platform_device中dev成员的p成员的driver_data数据域中的数据取出  
  4.     struct ocores_i2c *i2c = platform_get_drvdata(pdev);  
  5.   
  6.     /* disable i2c logic */  
  7.     //无效i2c总线控制器  
  8.     oc_setreg(i2c, OCI2C_CONTROL, oc_getreg(i2c, OCI2C_CONTROL)  
  9.           & ~(OCI2C_CTRL_EN|OCI2C_CTRL_IEN));  
  10.   
  11.     /* remove adapter & data */  
  12.     //删除完成注册的i2c_adapter  
  13.     i2c_del_adapter(&i2c->adap);  
  14.       
  15.     //删除platform_device中dev成员的p成员的driver_data  
  16.     platform_set_drvdata(pdev, NULL);  
  17.   
  18.     return 0;  
  19. }  

        至于有关电源管理的suspendresume暂且忽略,内核配置中暂时不去实现。


        这样就注册好平台设备驱动。

        接下去就在源码中去结合注释和i2c-specs的文档好好看看通信算法处理函数,这也是我们从新开发总线驱动的时候必须去编写的代码~

        首先是定义i2c_adapter和其成员变量的algo算法

[cpp] view plaincopyprint?
  1. static u32 ocores_func(struct i2c_adapter *adap)  
  2. {  
  3.     return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;  
  4. }  
  5.   
  6. //定义一个i2c_algorithm的数据结构,其中实现控制器通信算法master_xfer  
  7. //和functionality(master_xfer通信算法支持的协议)  
  8. static const struct i2c_algorithm ocores_algorithm = {  
  9.     .master_xfer    = ocores_xfer,  
  10.     .functionality  = ocores_func,  
  11. };  
  12.   
  13. //定义一个i2c_adapter,完成总线控制器的描述,并在probe中向内核注册  
  14. static struct i2c_adapter ocores_adapter = {  
  15.     .owner      = THIS_MODULE,  
  16.     .name       = "i2c-ocores",  
  17.     .class      = I2C_CLASS_HWMON | I2C_CLASS_SPD,  
  18.     .algo       = &ocores_algorithm,  
  19. };  


        其中由于这个驱动是基于中断架构的收发,结合源码继续往下看。

        首先看看在probe中调用的ocores_init(),完成的是总线控制器的初始化

[cpp] view plaincopyprint?
  1. //根据i2c-specs文档初始化总线控制器  
  2. //对比文档中的寄存器进行操作  
  3. static void ocores_init(struct ocores_i2c *i2c)  
  4. {  
  5.     int prescale;  
  6.   
  7.     //取出Control register寄存器的值,在i2c-specs中有说明~  
  8.     u8 ctrl = oc_getreg(i2c, OCI2C_CONTROL);  
  9.   
  10.     /* make sure the device is disabled */  
  11.     //i2c cores禁止中断和无效总线控制器  
  12.     oc_setreg(i2c, OCI2C_CONTROL, ctrl & ~(OCI2C_CTRL_EN|OCI2C_CTRL_IEN));  
  13.   
  14.     //设置i2c总线控制器的通信速率,在i2c-specs文档有计算公式  
  15.     prescale = (i2c->clock_khz / (5*100)) - 1;  
  16.   
  17.     //将速率写入PRERlo和PRERhi寄存器  
  18.     oc_setreg(i2c, OCI2C_PRELOW, prescale & 0xff);  
  19.     oc_setreg(i2c, OCI2C_PREHIGH, prescale >> 8);  
  20.   
  21.     /* Init the device */  
  22.     //清除中断标志位,使能总线控制器的中断和使能总线控制器  
  23.     oc_setreg(i2c, OCI2C_CMD, OCI2C_CMD_IACK);  
  24.     oc_setreg(i2c, OCI2C_CONTROL, ctrl | OCI2C_CTRL_IEN | OCI2C_CTRL_EN);  
  25. }  

        然后程序调用通信算法时会hold住,在中断中处理数据,然后等待事件集到来,完成通信。

[cpp] view plaincopyprint?
  1. //发送和接收调用的总线通信算法函数  
  2. static int ocores_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)  
  3. {  
  4.     //在ocores_i2c中有i2c_adapter成员,而i2c_adapter成员在probe进行时  
  5.     //已经由i2c_set_adapdata()保存到i2c_adapter中,所以这里取出保存在  
  6.     //i2c_adapter中dev成员的p成员的driver_data数据域  
  7.     struct ocores_i2c *i2c = i2c_get_adapdata(adap);  
  8.   
  9.     //获取由应用层传入的i2c message  
  10.     i2c->msg = msgs;  
  11.     i2c->pos = 0;  
  12.     i2c->nmsgs = num;  
  13.     i2c->state = STATE_START;  
  14.   
  15.     //根据i2c message类型是读还是写进行器件地址选择和设置LSB的读写位  
  16.     oc_setreg(i2c, OCI2C_DATA,  
  17.             (i2c->msg->addr << 1) |  
  18.             ((i2c->msg->flags & I2C_M_RD) ? 1:0));  
  19.   
  20.     //根据i2c协议第一次是写器件地址和读写位的组合  
  21.     oc_setreg(i2c, OCI2C_CMD, OCI2C_CMD_START);  
  22.   
  23.     //等待事件集到来,???操作系统没学过,现在恶补  
  24.     if (wait_event_timeout(i2c->wait, (i2c->state == STATE_ERROR) ||  
  25.                    (i2c->state == STATE_DONE), HZ))  
  26.         return (i2c->state == STATE_DONE) ? num : -EIO;  
  27.     else  
  28.         return -ETIMEDOUT;  
  29. }  

[cpp] view plaincopyprint?
  1. //当总线控制器完成一个byte发送和接收时调用的中断函数  
  2. //因为这个驱动采用了中断的方式收发数据  
  3. static irqreturn_t ocores_isr(int irq, void *dev_id)  
  4. {  
  5.     struct ocores_i2c *i2c = dev_id;  
  6.   
  7.     ocores_process(i2c);  
  8.   
  9.     return IRQ_HANDLED;  
  10. }  

[cpp] view plaincopyprint?
  1. //中断处理函数的处理过程,目前没认真整理过,根据i2c通信协议再好好看看  
  2. //然后有时间再总结出来  
  3. static void ocores_process(struct ocores_i2c *i2c)  
  4. {  
  5.     struct i2c_msg *msg = i2c->msg;  
  6.     u8 stat = oc_getreg(i2c, OCI2C_STATUS);  
  7.   
  8.     if ((i2c->state == STATE_DONE) || (i2c->state == STATE_ERROR)) {  
  9.         /* stop has been sent */  
  10.         oc_setreg(i2c, OCI2C_CMD, OCI2C_CMD_IACK);  
  11.         wake_up(&i2c->wait);  
  12.         return;  
  13.     }  
  14.   
  15.     /* error? */  
  16.     if (stat & OCI2C_STAT_ARBLOST) {  
  17.         i2c->state = STATE_ERROR;  
  18.         oc_setreg(i2c, OCI2C_CMD, OCI2C_CMD_STOP);  
  19.         return;  
  20.     }  
  21.   
  22.     if ((i2c->state == STATE_START) || (i2c->state == STATE_WRITE)) {  
  23.         i2c->state =  
  24.             (msg->flags & I2C_M_RD) ? STATE_READ : STATE_WRITE;  
  25.   
  26.         if (stat & OCI2C_STAT_NACK) {  
  27.             i2c->state = STATE_ERROR;  
  28.             oc_setreg(i2c, OCI2C_CMD, OCI2C_CMD_STOP);  
  29.             return;  
  30.         }  
  31.     } else  
  32.         msg->buf[i2c->pos++] = oc_getreg(i2c, OCI2C_DATA);  
  33.   
  34.     /* end of msg? */  
  35.     if (i2c->pos == msg->len) {  
  36.         i2c->nmsgs--;  
  37.         i2c->msg++;  
  38.         i2c->pos = 0;  
  39.         msg = i2c->msg;  
  40.   
  41.         if (i2c->nmsgs) {    /* end? */  
  42.             /* send start? */  
  43.             if (!(msg->flags & I2C_M_NOSTART)) {  
  44.                 u8 addr = (msg->addr << 1);  
  45.   
  46.                 if (msg->flags & I2C_M_RD)  
  47.                     addr |= 1;  
  48.   
  49.                 i2c->state = STATE_START;  
  50.   
  51.                 oc_setreg(i2c, OCI2C_DATA, addr);  
  52.                 oc_setreg(i2c, OCI2C_CMD,  OCI2C_CMD_START);  
  53.                 return;  
  54.             } else  
  55.                 i2c->state = (msg->flags & I2C_M_RD)  
  56.                     ? STATE_READ : STATE_WRITE;  
  57.         } else {  
  58.             i2c->state = STATE_DONE;  
  59.             oc_setreg(i2c, OCI2C_CMD, OCI2C_CMD_STOP);  
  60.             return;  
  61.         }  
  62.     }  
  63.   
  64.     if (i2c->state == STATE_READ) {  
  65.         oc_setreg(i2c, OCI2C_CMD, i2c->pos == (msg->len-1) ?  
  66.               OCI2C_CMD_READ_NACK : OCI2C_CMD_READ_ACK);  
  67.     } else {  
  68.         oc_setreg(i2c, OCI2C_DATA, msg->buf[i2c->pos++]);  
  69.         oc_setreg(i2c, OCI2C_CMD, OCI2C_CMD_WRITE);  
  70.     }  
  71. }  

        另外还有两个读写寄存器的函数,相信聪明你的绝对是看得明白的~不解释了~

[cpp] view plaincopyprint?
  1. //这两个函数没什么好解释的了,大家都能看得懂的了  
  2. //至于regstep在DTS文件中定义(寄存器的步长),对应的i2c-specs中寄存器步长为1  
  3. static inline void oc_setreg(struct ocores_i2c *i2c, int reg, u8 value)  
  4. {  
  5.     iowrite8(value, i2c->base + reg * i2c->regstep);  
  6. }  
  7.   
  8. static inline u8 oc_getreg(struct ocores_i2c *i2c, int reg)  
  9. {  
  10.     return ioread8(i2c->base + reg * i2c->regstep);  
  11. }  

        先到这里了,至于文章中我觉得没说明白的地方在有时间的时候会补齐的~今天要把食梦者第3季给追完啦~谢谢各位捧场啦~

原创粉丝点击