• 基于tiny4412的Linux内核移植 --- aliases节点解析


    作者信息

    作者: 彭东林

    邮箱:pengdonglin137@163.com

    QQ:405728433

    平台简介

    开发板:tiny4412ADK + S700 + 4GB Flash

    要移植的内核版本:Linux-4.4.0 (支持device tree)

    u-boot版本:友善之臂自带的 U-Boot 2010.12 (为支持uImage启动,做了少许改动)

    busybox版本:busybox 1.25

    交叉编译工具链: arm-none-linux-gnueabi-gcc

          (gcc version 4.8.3 20140320 (prerelease) (Sourcery CodeBench Lite 2014.05-29))

    正文

        在设备树中有一个叫做aliases的节点:

       1: / {
       2:     ... ...
       3:  
       4:     chosen {
       5:         stdout-path = "/serial@13800000";
       6:         bootargs = "root=/dev/ram0 rw rootfstype=ext4 console=ttySAC0,115200 ethmac=1C:6F:65:34:51:7E init=/linuxrc";
       7:     };
       8:  
       9:     aliases {
      10:         spi0 = "/spi@13920000";
      11:         spi1 = "/spi@13930000";
      12:         spi2 = "/spi@13940000";
      13:         i2c0 = "/i2c@13860000";
      14:         i2c1 = "/i2c@13870000";
      15:         i2c2 = "/i2c@13880000";
      16:         i2c3 = "/i2c@13890000";
      17:         ... ...
      18:     };
      19: ... ...
      20: };

    在Linux内核启动的时候会解析这个节点:

    start_kernel
        ---> setup_arch
                ---> unflatten_device_tree
                        ---> of_alias_scan

    在of_alias_scan中会扫描这个节点:

    of_alias_scan:

       1: void of_alias_scan(void * (*dt_alloc)(u64 size, u64 align))
       2: {
       3:     struct property *pp;
       4:  
       5:     of_aliases = of_find_node_by_path("/aliases"); // 找到/aliases节点对应的device_node
       6:     of_chosen = of_find_node_by_path("/chosen"); // 找到/chosen节点对应的device_node
       7:     if (of_chosen == NULL) // 如果没有/chosen的话,就找/chosen@0节点
       8:         of_chosen = of_find_node_by_path("/chosen@0");
       9:  
      10:     if (of_chosen) {
      11:         /* linux,stdout-path and /aliases/stdout are for legacy compatibility */
      12:         const char *name = of_get_property(of_chosen, "stdout-path", NULL);
      13:         if (!name)
      14:             name = of_get_property(of_chosen, "linux,stdout-path", NULL);
      15:         if (IS_ENABLED(CONFIG_PPC) && !name)
      16:             name = of_get_property(of_aliases, "stdout", NULL);
      17:         if (name)
      18:             of_stdout = of_find_node_opts_by_path(name, &of_stdout_options);
      19:     }
      20:  
      21:     if (!of_aliases)
      22:         return;
      23:  
      24:     for_each_property_of_node(of_aliases, pp) { // 遍历/aliases节点的属性,以属性i2c2 = "/i2c@13880000";为例
      25:         const char *start = pp->name; // 属性的名字,如"i2c2"
      26:         const char *end = start + strlen(start); // 名字的结尾,*end是''
      27:         struct device_node *np;
      28:         struct alias_prop *ap;
      29:         int id, len;
      30:  
      31:         /* 不处理名字是name、phandle、linux,phandle的属性 */
      32:         if (!strcmp(pp->name, "name") || 
      33:             !strcmp(pp->name, "phandle") ||
      34:             !strcmp(pp->name, "linux,phandle"))
      35:             continue;
      36:  
      37:         np = of_find_node_by_path(pp->value); 
      38:         /*
      39:             根据属性的值(如"/i2c@13880000")获得这个值对应的节点
      40:             i2c@13880000 {
      41:                 #address-cells = <0x1>;
      42:                 #size-cells = <0x0>;
      43:                 compatible = "samsung,s3c2440-i2c";
      44:                 reg = <0x13880000 0x100>;
      45:                 interrupts = <0x0 0x3c 0x0>;
      46:                 clocks = <0x7 0x13f>;
      47:                 clock-names = "i2c";
      48:                 pinctrl-names = "default";
      49:                 pinctrl-0 = <0x22>;
      50:                 status = "disabled";
      51:             };        
      52:         */
      53:         if (!np)
      54:             continue;
      55:  
      56:         /* walk the alias backwards to extract the id and work out
      57:          * the 'stem' string */
      58:         while (isdigit(*(end-1)) && end > start) //对于"i2c2",end最终会指向字符'2'的地址
      59:             end--;
      60:         len = end - start; // 获得"i2c"的长度(不包含结尾的数字2),就是3
      61:  
      62:         if (kstrtoint(end, 10, &id) < 0) // 将end指向的字符'2'转化为数字2,赋值给id
      63:             continue;
      64:  
      65:         /* Allocate an alias_prop with enough space for the stem */
      66:         ap = dt_alloc(sizeof(*ap) + len + 1, 4); // 分配内存,多分配的"len+1"用于存放stem的名字
      67:         if (!ap)
      68:             continue;
      69:         memset(ap, 0, sizeof(*ap) + len + 1);
      70:         ap->alias = start; // ap->alias指向字符串"i2c2"
      71:         of_alias_add(ap, np, id, start, len);
      72:     }
      73: }

    of_alias_add:

       1: static void of_alias_add(struct alias_prop *ap, struct device_node *np,
       2:              int id, const char *stem, int stem_len)
       3: {
       4:     ap->np = np; // np是"/i2c@13880000"对应的节点device_node
       5:     ap->id = id; // id的值是2
       6:     strncpy(ap->stem, stem, stem_len); // 由于stem_len是3,所以ap->stem被赋值为"i2c"
       7:     ap->stem[stem_len] = 0;
       8:     list_add_tail(&ap->link, &aliases_lookup); // 将这个ap加入到全局aliases_lookup链表中
       9:     pr_debug("adding DT alias:%s: stem=%s id=%i node=%s
    ",
      10:          ap->alias, ap->stem, ap->id, of_node_full_name(np));
      11: }

    使用:

    在drivers/i2c/i2c-core.c中:

       1: int i2c_add_adapter(struct i2c_adapter *adapter)
       2: {
       3:     struct device *dev = &adapter->dev;
       4:     int id;
       5:  
       6:     if (dev->of_node) {
       7:         id = of_alias_get_id(dev->of_node, "i2c");
       8:         if (id >= 0) {
       9:             adapter->nr = id;
      10:             return __i2c_add_numbered_adapter(adapter);
      11:         }
      12:     }
      13:     ... ...
      14: }

    第7行调用of_alias_get_id获得与这个device_node(即/i2c@13880000节点)对应的alias_prop的id,如果以/i2c@13880000节点为例,这里得到的id就是2。

    of_alias_get_id:

       1: int of_alias_get_id(struct device_node *np, const char *stem)
       2: {
       3:     struct alias_prop *app;
       4:     int id = -ENODEV;
       5:  
       6:     mutex_lock(&of_mutex);
       7:     list_for_each_entry(app, &aliases_lookup, link) { // 遍历全局链表aliases_lookup
       8:         if (strcmp(app->stem, stem) != 0) // 找到 stem 是 "i2c" 的alias_prop
       9:             continue;
      10:  
      11:         if (np == app->np) { // 判断这个alias_prop指向的device_node是不是跟传入的匹配
      12:             id = app->id;  // 获得 id,2
      13:             break;
      14:         }
      15:     }
      16:     mutex_unlock(&of_mutex);
      17:  
      18:     return id;
      19: }

    從上面的分析就可以知道alias節點的作用了:

    比如SoC上有如果多個i2c控制器,alias的相當於給每個i2c控制器分配一個唯一的編號,如上面的i2c@13880000對應的alias是i2c2,那麼這個編號就是2,將來就可以在/dev下看到名爲i2c-2的設備節點。

    在內核中可以看到很多地方都會調用of_alias_get_id,他的作用就是根據傳入的device node,在alias中找到對應的唯一編號,如:

    of_alias_get_id(pdev->dev.of_node, "spi")

    of_alias_get_id(node, "fimc")

    of_alias_get_id(pdev->dev.of_node, "serial")

    of_alias_get_id(pdev->dev.of_node, "uart")

    of_alias_get_id(dev->of_node, "gpio")

    ... ...

    完。

  • 相关阅读:
    perl next和last
    用 Flask 来写个轻博客 (26) — 使用 Flask-Celery-Helper 实现异步任务
    mysql 更新唯一主键列 被堵塞
    perl + 匹配前导模式一次或者多次
    跨域
    日志处理
    FineBI:一个简单易用的自助BI工具
    FineBI:一个简单易用的自助BI工具
    bootstrap-treeview
    Bootstrap树形菜单插件TreeView.js使用方法详解
  • 原文地址:https://www.cnblogs.com/pengdonglin137/p/5252348.html
Copyright © 2020-2023  润新知