ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

u-boot dm_init_and_scan函数

2022-02-24 12:35:06  阅读:215  来源: 互联网

标签:node dm boot 地址 pos member init type


 

dm_init_and_scan

 dm_init_and_scan(false)中主要有两点:一个是初始化驱动模型的根节点,另一个就是扫描设备书创建udevice、uclass绑定、申请空间等

static int initr_dm(void) {     int ret;
    /* Save the pre-reloc driver model and start a new one */     gd->dm_root_f = gd->dm_root;     gd->dm_root = NULL; #ifdef CONFIG_TIMER     gd->timer = NULL; #endif     bootstage_start(BOOTSTAGE_ID_ACCUM_DM_R, "dm_r");     ret = dm_init_and_scan(false);     bootstage_accum(BOOTSTAGE_ID_ACCUM_DM_R);     if (ret)         return ret;
    return 0; }

 

 

 

u-boot device_bind_by_name函数 

 

list_for_each_entry解析

双向链表及链表头:

建立一个双向链表通常有一个独立的用于管理链表的链表头,链表头一般是不含有实体数据的,必须用INIT_LIST_HEAD()进行初始化,表头建立以后,就可以将带有数据结构的实体链表成员加入到链。

INIT_LIST_HEAD (&nphy_dev_list);

 

定义:

#define list_for_each_entry(pos, head, member)                \
    for (pos = list_entry((head)->next, typeof(*pos), member);    \
         &pos->member != (head);     \
         pos = list_entry(pos->member.next, typeof(*pos), member))

它实际上是一个 for 循环,利用传入的 pos 作为循环变量,从表头 head 开始,逐项向后(next 方向)移动 pos,直至又回head.

我们将for循环分解为一下三点:

1. for循环初始化      pos = list_entry((head)->next, typeof(*pos), member);

2. for循环执行条件  &pos->member != (head);

3. 每循环一次执行   pos = list_entry(pos->member.next, typeof(*pos), member))

typeof()是取变量的类型,这里是取指针pos所指向数据的类型。

先看第一个:

#define list_entry(ptr, type, member) \
    container_of(ptr, type, member)
#define container_of(ptr, type, member) ({                      \
    const typeof( ((type *)0)->member ) *__mptr = (ptr);    \
    (type *)( (char *)__mptr - offsetof(type,member) );})

讲讲container_of:作用:根据一个结构体变量中的一个域成员变量的指针来获取指向整个结构体变量的指针。

所以(type *)0)就是将0强转为一个地址,这个地址(0x0000)指向的是类型type的数据。当然,这里是一个技巧,并不是真的在地址0x0000存放了我们的数据。

((type *)0)->member的作用,这里的‘->’很显然是通过指针指取结构体成员的操作。指针就是刚才通过0强转的地址。所以也就是相当于地址0x0000 是结构体类型type的首地址,通过->’取其中的成员变量member。

typeof( ((type *)0)->member ) *__mptr = (ptr):知道member成员的类型,定义一个指针变量__mptr,指向的类型是member的类型,其初始化为ptr的值

offsetof(type,member):求出member在结构体中的偏移量

#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)

根据优先级的顺序,最里面的小括号优先级最高,TYPE *将整型常量0强制转换为TYPE型的指针,且这个指针指向的地址为0,也就是将地址0开始的一块存储空间映射为TYPE型的对象,接下来再对结构体中MEMBER成员进行取址,而整个TYPE结构体的首地址是0,这里获得的地址就是MEMBER成员在TYPE中的相对偏移量。再将这个偏移量强制转换成size_t型数据(无符号整型)。                                                             

 

(char *)__mptr - offsetof(type,member)就是__mptr - offset,即member类型的指针减去member在结构体中的偏移量。

小结:通过上面的分析,和定义出的注释,container_of的作用很明显了 -- 获得结构体的地址。那么我们需要给他提供三个参数,分别是:ptr:member成员的指针   type:结构体类型   member:成员member的名字。

这样我们就能通过container_of(ptr, type, member)的返回值,得到结构体的地址。

 dm_scan_fdt

int dm_scan_fdt(bool pre_reloc_only) │

 { 
  return dm_scan_fdt_node(gd->dm_root, ofnode_root(), pre_reloc_only); 
 }

 

 

// parent=gd->dm_root,表示以root设备作为父设备开始解析
// blob=gd->fdt_blob,对应dtb入口地址
// offset=0,从偏移0的节点开始扫描
static int dm_scan_fdt_node(struct udevice *parent, ofnode parent_node,
                bool pre_reloc_only)
{
    int ret = 0, err = 0;
    ofnode node;

    if (!ofnode_valid(parent_node))
        return 0;

    for (node = ofnode_first_subnode(parent_node);
         ofnode_valid(node);
         node = ofnode_next_subnode(node)) {
        const char *node_name = ofnode_get_name(node);

        if (!ofnode_is_enabled(node)) {
            pr_debug("   - ignoring disabled device\n");
            continue;
        }
        err = lists_bind_fdt(parent, node, NULL, pre_reloc_only);
        if (err && !ret) {
            ret = err;
            debug("%s: ret=%d\n", node_name, ret);
        }
    }

    if (ret)
        dm_warn("Some drivers failed to bind\n");

    return ret;
}

 

 

2.2.1.7 ranges 属性

ranges属性值可以为空或者按照 (child-bus-address,parent-bus-address,length) 格式编写的数字矩阵, ranges 是一个地址映射/转换表, ranges 属性每个项目由子地址、父地址和地址空间长度这三部分组成:

  • child-bus-address:子总线地址空间的物理地址,由父节点的 #address-cells 确定此物理地址所占用的字长。
  • parent-bus-address:父总线地址空间的物理地址,同样由父节点的 #address-cells 确定此物理地址所占用的字长。
  • length:子地址空间的长度,由父节点的 #size-cells 确定此地址长度所占用的字长。
soc {
    compatible = "simple-bus";
    #address-cells = <1>;
    #size-cells = <1>;
    ranges = <0x0 0xe0000000 0x00100000>;
    serial {
        device_type = "serial";
        compatible = "ns16550";
        reg = <0x4600 0x100>;
        clock-frequency = <0>;
        interrupts = <0xA 0x8>;
        interrupt-parent = <&ipic>;
        };
};

节点 soc 定义的 ranges 属性,值为 <0x0 0xe0000000 0x00100000>,此属性值指定了一个 1024KB(0x00100000) 的地址范围,子地址空间的物理起始地址为 0x0,父地址空间的物理起始地址为 0xe0000000。

serial 是串口设备节点,

reg 属性定义了 serial 设备寄存器的起始地址为 0x4600,寄存器长度为 0x100。

经过地址转换, serial 设备可以从 0xe0004600 开始进行读写操作,0xe0004600=0x4600+0xe0000000。

 

 

标签:node,dm,boot,地址,pos,member,init,type
来源: https://www.cnblogs.com/liujunhuasd/p/15860279.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有