您的位置:首页 > 运维架构 > Linux

powerpc linux下dts解析过程

2013-01-18 19:02 351 查看

一. 在linux中,对dtb文件解析的整个过程序如下:

1)首先将从u-boot 传递过来的映像基地址和dtb 文件映像基地址保存通用寄存器r30,r31;

2)通过调用machine_init()、early_init_devtree()函数来获取内核前期初始化所需的bootargs,cmd_line等系统引导参数;

3)调用start_kernel()、setup_arch()、unflatten_device_tree()函数来解析dtb 文件,构建一个由device_node 结构连接而成的单项链表,并使用全局变量allnodes 指针来保存这个链表的头指针;如下在此函数执行过后,在内存中会存在一个如下的链表,后面所有的函数,如果需要从of
tree结构上读取设备资料的,都将从这个链表中遍历并读取。



4)内核调用OF 提供的API 函数获取allnodes链表信息来初始化内核其他子系统、设备等。(of_flatform_device)

二、dts文件首地址解析

在进行DTS文件解析之前先要从bootm启动命令中获取dtb文件所在的地址。而这一步的开始是从arch/powerpc/kernel/head_32.s文件开始中的。

1、arch/powerpc/kernel/head_32.s

* This is where the main kernel code starts.

*/

start_here:

/* ptr to current */

lis r2,init_task@h

ori r2,r2,init_task@l

/* Set up for using our exception vectors */

/* ptr to phys current thread */

tophys(r4,r2)

addi r4,r4,THREAD
/* init task's THREAD */

CLR_TOP32(r4)

mtspr SPRN_SPRG_THREAD,r4

li r3,0

mtspr SPRN_SPRG_RTAS,r3
/* 0 => not in RTAS */

/* stack */

lis
r1,init_thread_union@ha

addi
r1,r1,init_thread_union@l

li
r0,0

stwu
r0,THREAD_SIZE-STACK_FRAME_OVERHEAD(r1)

/*

* Do early platform-specific initialization,

* and set up the MMU.

*/

mr
r3,r31

mr
r4,r30

bl
machine_init //在这个函数中


bl
__save_cpu_setup

bl
MMU_init

notrace void __init machine_init(unsigned long dt_ptr)

{

lockdep_init();

/* Enable early debugging if any specified (see udbg.h) */

udbg_early_init();

/* Do some early initialization based on the flat device tree */

early_init_devtree(__va(dt_ptr));


probe_machine();

setup_kdump_trampoline();

#ifdef CONFIG_6xx

if (cpu_has_feature(CPU_FTR_CAN_DOZE) ||

cpu_has_feature(CPU_FTR_CAN_NAP))

ppc_md.power_save = ppc6xx_idle;

#endif

#ifdef CONFIG_E500

if (cpu_has_feature(CPU_FTR_CAN_DOZE) ||

cpu_has_feature(CPU_FTR_CAN_NAP))

ppc_md.power_save = e500_idle;

#endif

if (ppc_md.progress)

ppc_md.progress("id mach(): done", 0x200);

}

void __init early_init_devtree(void *params)

{

phys_addr_t limit;

DBG(" -> early_init_devtree(%p)\n", params);

/* Setup flat device-tree pointer */

initial_boot_params = params;

#ifdef CONFIG_PPC_RTAS

/* Some machines might need RTAS info for debugging, grab it now. */

of_scan_flat_dt(early_init_dt_scan_rtas, NULL);

#endif

#ifdef CONFIG_PHYP_DUMP

/* scan tree to see if dump occured during last boot */

of_scan_flat_dt(early_init_dt_scan_phyp_dump, NULL);

#endif

/* Retrieve various informations from the /chosen node of the

* device-tree, including the platform type, initrd location and

* size, TCE reserve, and more ...

*/

of_scan_flat_dt(early_init_dt_scan_chosen, NULL);

/* Scan memory nodes and rebuild LMBs */

lmb_init();

of_scan_flat_dt(early_init_dt_scan_root, NULL);

of_scan_flat_dt(early_init_dt_scan_memory, NULL);

/* Save command line for /proc/cmdline and then parse parameters */

strlcpy(boot_command_line, cmd_line, COMMAND_LINE_SIZE);

parse_early_param();

/* Reserve LMB regions used by kernel, initrd, dt, etc... */

lmb_reserve(PHYSICAL_START, __pa(klimit) - PHYSICAL_START);

/* If relocatable, reserve first 32k for interrupt vectors etc. */

if (PHYSICAL_START > MEMORY_START)

lmb_reserve(MEMORY_START, 0x8000);

reserve_kdump_trampoline();

reserve_crashkernel();

early_reserve_mem();

phyp_dump_reserve_mem();

limit = memory_limit;

if (! limit) {

phys_addr_t memsize;

/* Ensure that total memory size is page-aligned, because

* otherwise mark_bootmem() gets upset. */

lmb_analyze();

memsize = lmb_phys_mem_size();

if ((memsize & PAGE_MASK) != memsize)

limit = memsize & PAGE_MASK;

}

lmb_enforce_memory_limit(limit);

lmb_analyze();

lmb_dump_all();

DBG("Phys. mem: %llx\n", lmb_phys_mem_size());

/* We may need to relocate the flat tree, do it now.

* FIXME .. and the initrd too? */

move_device_tree();

DBG("Scanning CPUs ...\n");

/* Retreive CPU related informations from the flat tree

* (altivec support, boot CPU ID, ...)

*/

of_scan_flat_dt(early_init_dt_scan_cpus, NULL);

DBG(" <- early_init_devtree()\n");

}

三、解析

main.c-------------->arch/pwerpc/kernel/setup_32.c-------------->arch/powerpc/kernel/prom.c

start_kernel()------>setup_arch()---------->unflatten_device_tree()

/**

* unflattens the device-tree passed by the firmware, creating the

* tree of struct device_node. It also fills the "name" and "type"

* pointers of the nodes so the normal device-tree walking functions

* can be used (this used to be done by finish_device_tree)

*/

void __init unflatten_device_tree(void)

{

unsigned long start, mem, size;

struct device_node **allnextp = &allnodes;

DBG(" -> unflatten_device_tree()\n");

/* First pass, scan for size */

start = ((unsigned long)initial_boot_params) +

initial_boot_params->off_dt_struct;

size = unflatten_dt_node(0, &start, NULL, NULL, 0);

size = (size | 3) + 1;

DBG(" size is %lx, allocating...\n", size);

/* Allocate memory for the expanded device tree */

mem = lmb_alloc(size + 4, __alignof__(struct device_node));

mem = (unsigned long) __va(mem);

((u32 *)mem)[size / 4] = 0xdeadbeef;

DBG(" unflattening %lx...\n", mem);

/* Second pass, do actual unflattening */

start = ((unsigned long)initial_boot_params) +

initial_boot_params->off_dt_struct;

unflatten_dt_node(mem, &start, NULL, &allnextp, 0);

if (*((u32 *)start) != OF_DT_END)

printk(KERN_WARNING "Weird tag at end of tree: %08x\n", *((u32 *)start));

if (((u32 *)mem)[size / 4] != 0xdeadbeef)

printk(KERN_WARNING "End of tree marker overwritten: %08x\n",

((u32 *)mem)[size / 4] );

*allnextp = NULL;

/* Get pointer to OF "/chosen" node for use everywhere */

of_chosen = of_find_node_by_path("/chosen");

if (of_chosen == NULL)

of_chosen = of_find_node_by_path("/chosen@0");

DBG(" <- unflatten_device_tree()\n");

}

四. 将allnodes显示在/proc/device-tree目录下

start_kernel() -->

#ifdef CONFIG_PROC_FS

proc_root_init(); -->

#endif

#ifdef CONFIG_PROC_DEVICETREE

proc_device_tree_init(); -->

#endif

proc_device_tree_add_node(root, proc_device_tree);

此时将在/proc/device-tree目录下生成设备节点

# ls -al /proc/device-tree

五. of_platform_bus_type的注册/初始化

-------------------------------------------------------------------

arch/powerpc/kernel/of_platform.c

struct bus_type of_platform_bus_type =
{

.uevent = of_device_uevent,

};

EXPORT_SYMBOL(of_platform_bus_type);

static int __init of_bus_driver_init(void)

{

return of_bus_type_init(&of_platform_bus_type, "of_platform");

}

postcore_initcall(of_bus_driver_init);

of_platform_bus_type总线注册完毕。

此时/sys/bus/目录下将会有of_platform



六. 将allnodes设备节点添加到总线of_platform_bus_type

-------------------------------------------------------------------

arch/powerpc/platform/85xx/mpc85Xx_ads.c

static int __init declare_of_platform_devices(void)

{

of_platform_bus_probe(NULL, of_bus_ids, NULL);

return 0;

}

machine_device_initcall(mpc85xx_ads, declare_of_platform_devices);

declare_of_platform_devices() --> of_platform_bus_probe(NULL, of_bus_ids, NULL)

arch/powerpc/kernel/of_platform.c

遍历第一步中在内存中生成链表的所有soc的子节点,将所有的soc子节点设备添加到of_platform总线。

int of_platform_bus_probe(struct device_node *root,

const struct of_device_id *matches,

struct device *parent)

{

struct device_node *child;

struct of_device *dev;

int rc = 0;

if (matches == NULL)

matches = of_default_bus_ids;

if (matches == OF_NO_DEEP_PROBE)

return -EINVAL;

if (root == NULL)

root = of_find_node_by_path("/");

else

of_node_get(root);

pr_debug("of_platform_bus_probe()\n");

pr_debug(" starting at: %s\n", root->full_name);

/* Do a self check of bus type, if there's a match, create

* children

*/

if (of_match_node(matches, root)) {

pr_debug(" root match, create all sub devices\n");

dev = of_platform_device_create(root, NULL, parent);

if (dev == NULL) {

rc = -ENOMEM;

goto bail;

}

pr_debug(" create all sub busses\n");

rc = of_platform_bus_create(root, matches, &dev->dev);

goto bail;

}

for_each_child_of_node(root, child) {

if (!of_match_node(matches, child))

continue;

pr_debug(" match: %s\n", child->full_name);

dev = of_platform_device_create(child, NULL, parent);

if (dev == NULL)

rc = -ENOMEM;

else

rc = of_platform_bus_create(child, matches, &dev->dev);

if (rc) {

of_node_put(child);

break;

}

}

bail:

of_node_put(root);

return rc;

}

EXPORT_SYMBOL(of_platform_bus_probe);

--> of_platform_device_create--> of_device_register() --> device_add()

of_platform总线上的所有设备添加完毕,e0024000.ethernet,e0024520.mdio等设备现在都在总线上。至此设备节点将出现在/sys/devices/platform/



七、实例

1、mdio总线的注册

/driver/net/phy_device.c

subsys_initcall(phy_init)

phy_init --> mdio_bus_init --> bus_register(&mdio_bus_type)

总线注册后,在总线上注册了一个默认的phy的驱动 genphy_driver:

.phy_id = 0xffffffff,

.phy_id_mask = 0xffffffff,

.name = "Generic phy",

mdio总线注册完毕。

/sys/bus/mdio

2、 mdio总线上驱动的添加

--------------------------------------------
/driver/net/phy/marvell.c

module_init(marvell_init)

marvell_init() -->

phy_driver_register(&marvell_drivers[i]) -->

driver_register()

前面第三步,注册mdio总线后,已经添加了一个默认的phy的驱动,现在要将所有的phy驱动添加到总线上,这里将所有的marvell的phy都添加。

这步过后,内核的/sys/bus/mdio/driver里面就有了各种phy的驱动,但这时还没有和具体的设备绑定。

3、 of_platform总线上mdio设备驱动(该驱动的目的是在mdio总线上添加phy设备)的添加,并绑定设备:e0024520.mdio和e0025520.mdio
/driver/net/fsl_pq_mdio.c

module_init(fsl_pq_mdio_init)

fsl_pq_mdio_init --> of_register_platform_driver(&fsl_pq_mdio_driver) --> of_register_driver --> driver_register --> bus_add_driver --> driver_attach

遍历整个of_platform总线,寻找与之相匹配的设备,找到e0024520.mdio

driver_attach --> __driver_attach --> driver_match_device

将driver的match_table里的信息和dev_nod中的做比较,若符合就进入driver的probe,也就是fsl_pq_mdio_probe。

现在of_platform总线上的设备e0024520.mdio和e0025520.mdio已经绑定了驱动。

4、 mdio总线上的设备的添加,寻找并绑定相应的驱动。

/driver/net/fsl_pq_mdio.c

fsl_pq_mdio_probe --> of_mdiobus_register --> phy_device_register --> device_register(&phydev->dev) --> device_add --> bus_probe_device --> device_attach
-->bus_for_each_drv

扫描mdio总线上的所有的驱动,若找到匹配的,就绑定,并probe。

__device_attach --> driver_probe_device --> really_probe --> phy_probe

将所有的phy和tbi-phy的设备都添加到mdio总线上,并且两个phy设备和两个tbi-phy设备都会根据其自己的phyID找到各自的驱动
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: