zoukankan      html  css  js  c++  java
  • Linux i2c子系统(四) _从i2c-s3c24xx.c看i2c控制器驱动的编写

    "./drivers/i2c/busses/i2c-s3c2410.c"是3.14.0内核中三星SoC的i2c控制器驱动程序, 本文试图通过对这个程序的分析, 剥离繁复的细节, 总结一套编写i2c主机控制器驱动的框架以及一个分析内核驱动的流程.

    匹配之前

    1287 static int __init i2c_adap_s3c_init(void)
    1288 {
    1289         return platform_driver_register(&s3c24xx_i2c_driver);
    1290 }
    1291 subsys_initcall(i2c_adap_s3c_init);
    

    --1291-->将主机控制器驱动在系统启动的时候就注册好
    --1289-->这个驱动是基于platform总线的, 设备信息的部分在板级文件i2c_board_info中描述并作为platform_device随内核启动被注册, 所以控制器驱动在系统启动的时候就可以工作了

    1275 static struct platform_driver s3c24xx_i2c_driver = {
    1276         .probe          = s3c24xx_i2c_probe,
    1277         .remove         = s3c24xx_i2c_remove,
    1278         .id_table       = s3c24xx_driver_ids,
    1279         .driver         = {
    1280                 .owner  = THIS_MODULE,
    1281                 .name   = "s3c-i2c",
    1282                 .pm     = S3C24XX_DEV_PM_OPS,
    1283                 .of_match_table = of_match_ptr(s3c24xx_i2c_match),
    1284         },
    1285 };
    

    既然是遵循的platform编写, 那么所有的信息都要在一个platform_driver中描述, 分析也是围绕这个对象展开

    --1276-->probe函数, 最重要的函数
    --1278-->用于匹配的id表, 由于是平台文件编写的设备信息, 所以会使用这个域作为匹配的依据, 如下

     132 static struct platform_device_id s3c24xx_driver_ids[] = {                                          
     133         {
     134                 .name           = "s3c2410-i2c",
     135                 .driver_data    = 0,
     136         }, {
     137                 .name           = "s3c2440-i2c",
     138                 .driver_data    = QUIRK_S3C2440,
     139         }, {
     140                 .name           = "s3c2440-hdmiphy-i2c",
     141                 .driver_data    = QUIRK_S3C2440 | QUIRK_HDMIPHY | QUIRK_NO_GPIO,
     142         }, { }, 
     143 }; 
    

    我们可以在"arch/arm/plat-samsung"中找到相应的设备信息

     485 struct platform_device s3c_device_i2c0 = {
     486         .name           = "s3c2410-i2c",
     487         .id             = 0,
     488         .num_resources  = ARRAY_SIZE(s3c_i2c0_resource),
     489         .resource       = s3c_i2c0_resource,
     490 };
    

    二者一匹配, probe就执行!

    匹配之后

    一旦匹配上, 分析流程就会有点变化, 驱动开发都是基于面向对象的思想的, 内核虽然给我们封装了很多"类", 但当我们开发一个具体的驱动的时候, 还是要对其进行"继承", 进而创建针对具体设备的资源对象, 资源对象管理着驱动中诸多函数的共用资源, 是整个驱动运行过程中资源管理者与桥梁, 主要包括:内核类+资源(io, irq,时钟, 寄存器)+状态表示+其他,所以, 设计驱动的工作中很重要的一个工作就是"设计资源类". 下面就是三星设计的类, 我把次要的部分剔除了

    资源类

    资源对象是整个驱动运作的核心, 所有的方法需要的资源都是对这个对象的操作, 它的设计是迭代的过程, 但当整个框架搭起来之后, 不应该有大的变化

     103 struct s3c24xx_i2c {
     104         wait_queue_head_t       wait;
     108         struct i2c_msg          *msg;
     109         unsigned int            msg_num;
     110         unsigned int            msg_idx;
     111         unsigned int            msg_ptr;
     113         unsigned int            tx_setup;
     114         unsigned int            irq;
     116         enum s3c24xx_i2c_state  state;
     117         unsigned long           clkrate;
     119         void __iomem            *regs;
     120         struct clk              *clk;
     121         struct device           *dev;
     122         struct i2c_adapter      adap;
     124         struct s3c2410_platform_i2c     *pdata;
     125         int                     gpios[2];
     130 };
    

    struct s3c24xx_i2c
    --108-->收到的i2c-core.c发送过来的i2c_msg对象数组首地址
    --109-->i2c_msg数组的元素个数
    --110-->i2c_msg数组元素的索引
    --114-->使用的中断号
    --116-->当前控制器的状态, 用枚举量表示STATE_IDLE, STATE_START,STATE_READ,STATE_WRITE,STATE_STOP
    --117-->时钟频率
    --120-->时钟
    --121-->属于device, 按照device来管理
    --122-->构造并使用的i2c_adapter对象, 和上一篇的框架图对应
    --124-->封装的平台信息, 是一个数组首地址, 每一个元素包括从机地址, 标志位, 总线编号等

    probe

    probe主要负责"申请资源+初始化+提供接口", 通过对probe的分析, 就可以对整个驱动的构建有一个
    提纲挈领的理解

    1072 static int s3c24xx_i2c_probe(struct platform_device *pdev)
    1073 {
    1074         struct s3c24xx_i2c *i2c;
    1075         struct s3c2410_platform_i2c *pdata = NULL;
    1076         struct resource *res;
    1077         int ret;
    1078 
    1079         if (!pdev->dev.of_node) {
    1080                 pdata = dev_get_platdata(&pdev->dev);
    1085         }
    1086 
    1087         i2c = devm_kzalloc(&pdev->dev, sizeof(struct s3c24xx_i2c), GFP_KERNEL);
    1092 
    1093         i2c->pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
    1098 
    1099         i2c->quirks = s3c24xx_get_device_quirks(pdev);
    1100         if (pdata)
    1101                 memcpy(i2c->pdata, pdata, sizeof(*pdata));
    1102         else
    1103                 s3c24xx_i2c_parse_dt(pdev->dev.of_node, i2c);
    1104 
    1105         strlcpy(i2c->adap.name, "s3c2410-i2c", sizeof(i2c->adap.name));
    1106         i2c->adap.owner   = THIS_MODULE;
    1107         i2c->adap.algo    = &s3c24xx_i2c_algorithm;
    1108         i2c->adap.retries = 2;
    1109         i2c->adap.class   = I2C_CLASS_HWMON | I2C_CLASS_SPD;
    1110         i2c->tx_setup     = 50;
    1111 
    1112         init_waitqueue_head(&i2c->wait);
    1113 
    1114         /* find the clock and enable it */
    1116         i2c->dev = &pdev->dev;
    1117         i2c->clk = devm_clk_get(&pdev->dev, "i2c");
    1124 
    1126         /* map the registers */
    1128         res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
    1129         i2c->regs = devm_ioremap_resource(&pdev->dev, res);
    1136 
    1137         /* setup info block for the i2c core */
    1139         i2c->adap.algo_data = i2c;
    1140         i2c->adap.dev.parent = &pdev->dev;
    1141 
    1142         i2c->pctrl = devm_pinctrl_get_select_default(i2c->dev);
    1143 
    1144         /* inititalise the i2c gpio lines */
    1146         if (i2c->pdata->cfg_gpio) {
    1147                 i2c->pdata->cfg_gpio(to_platform_device(i2c->dev));
    1148         } else if (IS_ERR(i2c->pctrl) && s3c24xx_i2c_parse_dt_gpio(i2c)) {
    1149                 return -EINVAL;
    1150         }
    1151 
    1152         /* initialise the i2c controller */
    1154         clk_prepare_enable(i2c->clk);
    1155         ret = s3c24xx_i2c_init(i2c);
    1156         clk_disable_unprepare(i2c->clk);
    1161         /* find the IRQ for this unit (note, this relies on the init call to
    1162          * ensure no current IRQs pending
    1163          */
    1165         if (!(i2c->quirks & QUIRK_POLL)) {
    1166                 i2c->irq = ret = platform_get_irq(pdev, 0);
    1171 
    1172         ret = devm_request_irq(&pdev->dev, i2c->irq, s3c24xx_i2c_irq, 0,
    1173                                 dev_name(&pdev->dev), i2c);
    1179         }
    1180 
    1181         ret = s3c24xx_i2c_register_cpufreq(i2c);
    1192 
    1193         i2c->adap.nr = i2c->pdata->bus_num;
    1194         i2c->adap.dev.of_node = pdev->dev.of_node;
    1196         ret = i2c_add_numbered_adapter(&i2c->adap);
    1202 
    1203         platform_set_drvdata(pdev, i2c);
    1204 
    1205         pm_runtime_enable(&pdev->dev);
    1206         pm_runtime_enable(&i2c->adap.dev);
    1209         return 0;
    1210 }
    

    s3c24xx_i2c_probe()
    --1074-1077-->准备好指针与变量, 准备从传入的对象中提取数据, 虽说这是C89的语法要求, 但这种写法确实比较舒服, 遇到不认识的变量就去函数开头找
    --1079-->如果pdev->dev.of_node为空, 表示设备不是通过设备树获得的, 那么就调用dev_get_platdata获取pdev->dev.oplatform_data中的数据, 显然, 在编写设备文件的时候这里藏的是一个s3c2410_platform_i2c对象, 所以我们用pdata取出来以备使用
    --1087-->pdev->dev是device类型, 以它为的detach为标志分配一个我们自己的对象的空间并将分配的首地址返回给i2c。 这里使用的是devm_kzalloc(), 函数 devm_kzalloc()和kzalloc()一样都是内核内存分配函数,但是devm_kzalloc()是跟设备(device)有关的,当设备(device)被detached或者驱动(driver)卸载(unloaded)时,内存会被自动释放。另外,当内存不在使用时,可以使用函数devm_kfree()释放。而kzalloc()则需要手动释放(使用kfree()),但如果工程师检查不仔细,则有可能造成内存泄漏
    --1100-1103-->如果在--1079--中获得了相应的s3c2410_platform_i2c对象地址,就将其拷贝到资源对象中的相应的域存起来,否则自己去设备树中找
    --1106-1110-->使用赋值的方式直接对一部分资源对象的域进行初始化
    --1112-->初始化资源对象中的等待队列头wait_queue_head_t wait
    --1116-->初始化资源对象中的device *dev
    --1117-->初始化资源对象中的struct *clk
    --1128-->获取pdev中的地址resource, ioremap之后用于初始化资源对象中的regs域, 使用的是devm_ioremap_resource(), 同样是基于device的资源自动回收API
    --1139-->将自定义资源对象指针藏到algo_data中, 和--1203--的作用一样, 给xfer()接口函数用
    --1140-->初始化资源对象中的i2c_adapter对象中的部分成员, 指定其父设备是控制器设备的device域
    --1142-->初始化资源对象中的pctrl域, 使用的是devm_pinctrl_get_select_default()
    --1147-->使用to_platform_device(其实就是container_of)通过i2c->dev找到包含它的platform_device对象, 回调cfg_gpio()函数, 配置GPIO引脚
    --1154-->初始化时钟
    --1166-->获取中断资源
    --1171-->注册中断, devm_request_irq
    --1193-1194-->初始化i2c->adap对象, 总线编号是来自于设备的
    --1196-->将构造的adapter对象注册到内核
    --1203-->设置私有数据, pdev->dev->p->driver_data = i2c; 由于i2c->dev==pdev->dev, 所以其实就是将资源对象的首地址赋值给藏到device->device_private->driver_data中, 因为所有的接口都是使用platform_device作为形参的, 这种方法可以方便的找到自定义资源对象, 所以才叫void * driver_data
    --1205-->设置dev的电源管理
    --1206-->设置adap的电源管理

    s3c24xx_i2c_algorithm

    probe中我们最关心的就是这个--1107--实现的接口了, i2c-core最终就是通过algo->xfer将设备驱动的数据发送出去的, 是一个硬件相关的函数

     787 static const struct i2c_algorithm s3c24xx_i2c_algorithm = {                                        
     788         .master_xfer            = s3c24xx_i2c_xfer,
     789         .functionality          = s3c24xx_i2c_func,
     790 };
    
     748 static int s3c24xx_i2c_xfer(struct i2c_adapter *adap,struct i2c_msg *msgs, int num)
     750 {
     751         struct s3c24xx_i2c *i2c = (struct s3c24xx_i2c *)adap->algo_data;
     758         for (retry = 0; retry < adap->retries; retry++) {
     760                 ret = s3c24xx_i2c_doxfer(i2c, msgs, num);
     770                 udelay(100);
     771         }
     776 }
    

    s3c24xx_i2c_xfer()
    --760-->循环调用发送函数, 函数的实现如下, 可以看到其中对寄存器的读写, 设备驱动中的发送的请求, 就是通过这些readl(), writel()来实现的.
    --770-->时序要求!

     256 static void s3c24xx_i2c_message_start(struct s3c24xx_i2c *i2c,                                     
     257                                       struct i2c_msg *msg)
     258 {
     275         /* todo - check for whether ack wanted or not */
     276         s3c24xx_i2c_enable_ack(i2c);
     277 
     278         iiccon = readl(i2c->regs + S3C2410_IICCON);
     279         writel(stat, i2c->regs + S3C2410_IICSTAT);
     280 
     281         dev_dbg(i2c->dev, "START: %08lx to IICSTAT, %02x to DS
    ", stat, addr);
     282         writeb(addr, i2c->regs + S3C2410_IICDS);
     287         ndelay(i2c->tx_setup);
     288 
     290         writel(iiccon, i2c->regs + S3C2410_IICCON);
     291 
     292         stat |= S3C2410_IICSTAT_START;
     293         writel(stat, i2c->regs + S3C2410_IICSTAT);
     294 
     295         if (i2c->quirks & QUIRK_POLL) {
     296                 while ((i2c->msg_num != 0) && is_ack(i2c)) {
     297                         i2c_s3c_irq_nextbyte(i2c, stat);
     298                         stat = readl(i2c->regs + S3C2410_IICSTAT);
     299 
     300                         if (stat & S3C2410_IICSTAT_ARBITR)
     301                                 dev_err(i2c->dev, "deal with arbitration loss
    ");
     302                 }
     303         }
     304 }
    
  • 相关阅读:
    Java中的pom.xml
    Java中request请求配置
    java,名称工具类。手机号加星。
    Java数据库查询与循环处理
    php接口分页
    java中sql映射机制
    java中的model映射
    Java启动项目
    httpSession.removeAttribute 移除header中的属性
    mybatisplus 查询数据
  • 原文地址:https://www.cnblogs.com/xiaojiang1025/p/6505255.html
Copyright © 2011-2022 走看看