Device Tree(一):背景介绍

作者:linuxer 发布于:2014-5-22 16:46 分类:统一设备模型

一、前言

作为一个多年耕耘在linux 2.6.23内核的开发者,各个不同项目中各种不同周边外设驱动的开发以及各种琐碎的、扯皮的俗务占据了大部分的时间。当有机会下载3.14的内核并准备学习的时候,突然发现linux kernel对于我似乎变得非常的陌生了,各种新的机制,各种framework、各种新的概念让我感到阅读内核代码变得举步维艰。 还好,剖析内核的热情还在,剩下的就交给时间的。首先进入视线的是Device Tree机制,这是和porting内核非常相关的机制,如果想让将我们的硬件平台迁移到高版本的内核上,Device Tree是一个必须要扫清的障碍。

我想从下面三个方面来了解Device Tree:

1、为何要引入Device Tree,这个机制是用来解决什么问题的?(这是本文的主题)

2、Device Tree的基础概念(请参考DT基础概念

3、ARM linux中和Device Tree相关的代码分析(请参考DT代码分析

阅读linux内核代码就像欣赏冰山,有看得到的美景(各种内核机制及其代码),也有埋在水面之下看不到的基础(机制背后的源由和目的)。沉醉于各种内核机制的代码固然有无限乐趣,但更重要的是注入更多的思考,思考其背后的机理,真正理解软件抽象。这样才能举一反三,并应用在具体的工作和生活中。

本文主要从下面几个方面阐述为何ARM linux会引入Device Tree:

1、没有Device Tree的ARM linux是如何运转的?

2、混乱的ARM architecture代码和存在的问题

3、新内核的解决之道

 

二、没有Device Tree的ARM linux是如何运转的?

我曾经porting内核到两个ARM-based的平台上。一个是小的芯片公司的应用处理器,公司自己购买了CPU core,该CPU core使用ARM兼容的指令集(但不是ARM)加上各种公司自行设计的多媒体外设整合成公司的产品进行销售。而我的任务就是porting 2.4.18内核到该平台上。在黑白屏幕的手机时代,那颗AP(application process)支持了彩屏、camera、JPEG硬件加速、2D/3D加速、MMC/SD卡、各种音频加速(内置DSP)等等特性,功能强大到无法直视。另外一次移植经历是让2.6.23内核跑在一个大公司的冷门BP(baseband processor)上。具体porting的方法是很简单的:

1、自己撰写一个bootloader并传递适当的参数给kernel。除了传统的command line以及tag list之类的,最重要的是申请一个machine type,当拿到属于自己项目的machine type ID的时候,当时心情雀跃,似乎自己已经是开源社区的一份子了(其实当时是有意愿,或者说有目标是想将大家的代码并入到linux kernel main line的)。

2、在内核的arch/arm目录下建立mach-xxx目录,这个目录下,放入该SOC的相关代码,例如中断controller的代码,时间相关的代码,内存映射,睡眠相关的代码等等。此外,最重要的是建立一个board specific文件,定义一个machine的宏:

MACHINE_START(project name, "xxx公司的xxx硬件平台")
    .phys_io    = 0x40000000,
    .boot_params    = 0xa0000100,  
    .io_pg_offst    = (io_p2v(0x40000000) >> 18) & 0xfffc,
    .map_io        = xxx_map_io,
    .init_irq    = xxx_init_irq,
    .timer        = &xxx_timer,
    .init_machine    = xxx_init,
MACHINE_END

在xxx_init函数中,一般会加入很多的platform device。因此,伴随这个board specific文件中是大量的静态table,描述了各种硬件设备信息。

3、调通了system level的driver(timer,中断处理,clock等)以及串口terminal之后,linux kernel基本是可以起来了,后续各种driver不断的添加,直到系统软件支持所有的硬件。

综上所述,在linux kernel中支持一个SOC平台其实是非常简单的,让linux kernel在一个特定的平台上“跑”起来也是非常简单的,问题的重点是如何优雅的”跑”。

 

三、混乱的ARM architecture代码和存在的问题

每次正式的linux kernel release之后都会有两周的merge window,在这个窗口期间,kernel各个部分的维护者都会提交各自的patch,将自己测试稳定的代码请求并入kernel main line。每到这个时候,Linus就会比较繁忙,他需要从各个内核维护者的分支上取得最新代码并merge到自己的kernel source tree中。Tony Lindgren,内核OMAP development tree的维护者,发送了一个邮件给Linus,请求提交OMAP平台代码修改,并给出了一些细节描述:

1、简单介绍本次改动

2、关于如何解决merge conficts。有些git mergetool就可以处理,不能处理的,给出了详细介绍和解决方案

一切都很平常,也给出了足够的信息,然而,正是这个pull request引发了一场针对ARM linux的内核代码的争论。我相信Linus一定是对ARM相关的代码早就不爽了,ARM的merge工作量较大倒在其次,主要是他认为ARM很多的代码都是垃圾,代码里面有若干愚蠢的table,而多个人在维护这个table,从而导致了冲突。因此,在处理完OMAP的pull request之后(Linus并非针对OMAP平台,只是Tony Lindgren撞在枪口上了),他发出了怒吼:

Gaah. Guys, this whole ARM thing is a f*cking pain in the ass.

负责ARM linux开发的Russell King脸上挂不住,进行了反驳:事情没有那么严重,这次的merge conficts就是OMAP和IMX/MXC之间一点协调的问题,不能抹杀整个ARM linux团队的努力。其他的各个ARM平台维护者也加入讨论:ARM平台如何复杂,如何庞大,对于arm linux code我们已经有一些思考,正在进行中……一时间,讨论的气氛有些尖锐,但总体是坦诚和友好的。

对于一件事情,不同层次的人有不同层次的思考。这次争论涉及的人包括:

1、内核维护者(CPU体系结构无关的代码)

2、维护ARM系统结构代码的人

3、维护ARM sub architecture的人(来自各个ARM SOC vendor)

维护ARM sub architecture的人并没有强烈的使命感,作为公司的一员,他们最大的目标是以最快的速度支持自己公司的SOC,尽快的占领市场。这些人的软件功力未必强,对linux kernel的理解未必深入(有些人可能很强,但是人在江湖身不由己)。在这样的情况下,很多SOC specific的代码都是通过copy and paste,然后稍加修改代码就提交了。此外,各个ARM vendor的SOC family是一长串的CPU list,每个CPU多多少少有些不同,这时候#ifdef就充斥了各个源代码中,让ARM mach-和plat-目录下的代码有些不忍直视。

作为维护ARM体系结构的人,其能力不容置疑。以Russell King为首的team很好的维护了ARM体系结构的代码。基本上,除了mach-和plat-目录,其他的目录中的代码和目录组织是很好的。作为ARM linux的维护者,维护一个不断有新的SOC加入的CPU architecture code的确是一个挑战。在Intel X86的架构一统天下的时候,任何想正面攻击Intel的对手都败下阵来。想要击倒巨人(或者说想要和巨人并存)必须另辟蹊径。ARM的策略有两个,一个是focus在嵌入式应用上,也就意味着要求低功耗,同时也避免了和Intel的正面对抗。另外一个就是博采众家之长,采用license IP的方式,让更多的厂商加入ARM建立的生态系统。毫无疑问,ARM公司是成功的,但是这种模式也给ARM linux的维护者带来了噩梦。越来越多的芯片厂商加入ARM阵营,越来越多的ARM platform相关的代码被加入到内核,不同厂商的周边HW block设计又各不相同……

内核维护者是真正对操作系统内核软件有深入理解的人,他们往往能站在更高的层次上去观察问题,发现问题。Linus注意到每次merge window中,ARM的代码变化大约占整个ARCH目录的60%,他认为这是一个很明显的符号,意味着ARM linux的代码可能存在问题。其实,60%这个比率的确很夸张,因为unicore32是在2.6.39 merge window中第一次全新提交,它的代码是全新的,但是其代码变化大约占整个ARCH目录的9.6%(需要提及的是unicore32是一个中国芯)。有些维护ARM linux的人认为这是CPU市场占用率的体现,不是问题,直到内核维护者贴出实际的代码并指出问题所在。内核维护者当然想linux kernel支持更多的硬件平台,但是他们更愿意为linux kernel制定更长远的规划。例如:对于各种繁杂的ARM平台,用一个kernel image来支持。

经过争论,确定的问题如下:

1、ARM linux缺少platform(各个ARM sub architecture,或者说各个SOC)之间的协调,导致arm linux的代码有重复。值得一提的是在本次争论之前,ARM维护者已经进行了不少相关的工作(例如PM和clock tree)来抽象相同的功能模块。

2、ARM linux中大量的board specific的源代码应该踢出kernel,否则这些垃圾代码和table会影响linux kernel的长期目标。

3、各个sub architecture的维护者直接提交给Linux并入主线的机制缺乏层次。

 

四、新内核的解决之道

针对ARM linux的现状,最需要解决的是人员问题,也就是如何整合ARM sub architecture(各个ARM Vendor)的资源。因此,内核社区成立了一个ARM sub architecture的team,该team主要负责协调各个ARM厂商的代码(not ARM core part),Russell King继续负责ARM core part的代码。此外,建立一个ARM platform consolidation tree。ARM sub architecture team负责review各个sub architecture维护者提交的代码,并在ARM platform consolidation tree上维护。在下一个merge window到来的时候,将patch发送给Linus。

针对重复的代码问题,如果不同的SOC使用了相同的IP block(例如I2C controller),那么这个driver的code要从各个arch/arm/mach-xxx中独立出来,变成一个通用的模块供各个SOC specific的模块使用。移动到哪个目录呢?对于I2C或者USB OTG而言,这些HW block的驱动当然应该移动到kernel/drivers目录。因为,对于这些外设,可能是in-chip,也可能是off-chip的,但是对于软件而言,它们是没有差别的(或者说好的软件抽象应该掩盖底层硬件的不同)。对于那些system level的code呢?例如clock control、interrupt control。其实这些也不是ARM-specific,应该属于linux kernel的核心代码,应该放到linux/kernel目录下,属于core-Linux-kernel frameworks。当然对于ARM平台,也需要保存一些和framework交互的code,这些code叫做ARM SoC core architecture code。OK,总结一下:

1、ARM的核心代码仍然保存在arch/arm目录下

2、ARM SoC core architecture code保存在arch/arm目录下

3、ARM SOC的周边外设模块的驱动保存在drivers目录下

4、ARM SOC的特定代码在arch/arm/mach-xxx目录下

5、ARM SOC board specific的代码被移除,由Device Tree机制来负责传递硬件拓扑和硬件资源信息。

OK,终于来到了Device Tree了。本质上,Device Tree改变了原来用hardcode方式将HW 配置信息嵌入到内核代码的方法,改用bootloader传递一个DB的形式。对于基于ARM CPU的嵌入式系统,我们习惯于针对每一个platform进行内核的编译。但是随着ARM在消费类电子上的广泛应用(甚至桌面系统、服务器系统),我们期望ARM能够象X86那样用一个kernel image来支持多个platform。在这种情况下,如果我们认为kernel是一个black box,那么其输入参数应该包括:

1、识别platform的信息

2、runtime的配置参数

3、设备的拓扑结构以及特性

对于嵌入式系统,在系统启动阶段,bootloader会加载内核并将控制权转交给内核,此外,还需要把上述的三个参数信息传递给kernel,以便kernel可以有较大的灵活性。在linux kernel中,Device Tree的设计目标就是如此。


原创文章,转发请注明出处。蜗窝科技www.wowotech.net

标签: Device tree

评论:

光阴的事故
2017-02-08 10:21
感谢,我是新手,有空就过来学习哈
edison.jiang
2017-01-24 17:34
博总弄过unicore?
wowo
2017-01-25 09:13
@edison.jiang:没有哦。
iamhuskar
2017-01-18 11:27
新的板子上,关于上层文件系统构建。如果要构建一个定制的rootfs,。一般采用什么样的形式做呢?自己一个个包编译安装构建太麻烦了。linuxfromscratch或者是busybox。不知道博主用什么形式
wowo
2017-01-18 12:27
@iamhuskar:嵌入式的小系统可以用busybox,具体可参考http://www.wowotech.net/x_project/simple_busybox.html。
dianek
2016-07-28 06:49
非常感謝蝸窩的文章,請問arm64 cpu不用device tree,只用acpi能啟動麼,好像arm64 server architecture指定使用ACPI+UEFI,而不是device-tree + uboot,請問群主有何建議,謝謝
wowo
2016-07-28 18:25
@dianek:抱歉,没有server方面的经验,暂时不能给您建议~
hello
2016-07-28 19:24
@dianek:对于ARM64而言,嵌入式平台往往使用device tree,如果是server平台,由于各种原因,ACPI+UEFI是主流。在启动过程中,device tree或者ACPI,只要支持其中之一就可以启动了,具体有什么建议?估计只能是硬肯协议和代码了,呵呵~~~
dianek
2016-07-28 23:47
@hello:最近在用http://open-estuary.org的D02 server開發板,很奇怪的是kernel 4.4.15先用DT加載,後用ACPI識別個別硬件,是不是DT和ACPI可以共用呢?只用ACPI(acpi=force)會core在GICv3上,後來用4.4.0,就沒事了。。。

只是好奇,是不是arm64 ACPI不成熟,還是得部分依靠DT呢?

[    1.477656] Detected PIPT I-cache on CPU2
[    1.477662] CPU2: mpidr 20002 has no re-distributor!
[    1.477670] ------------[ cut here ]------------
[    1.477671] WARNING: at drivers/irqchip/irq-gic-v3.c:496
[    1.477672] Modules linked in:
[    1.477673]
[    1.477675] CPU: 2 PID: 0 Comm: swapper/2 Tainted: G      D W       4.4.11 #7
[    1.477676] Hardware name: Hisilicon Hip05 D02 Development Board (DT)
[    1.477677] task: fffffe17c0649000 ti: fffffe17c0730000 task.ti: fffffe17c0730000
[    1.477680] PC is at gic_populate_rdist+0xe8/0x188
[    1.477681] LR is at gic_populate_rdist+0xe8/0x188
[    1.477683] pc : [<fffffe000058fda0>] lr : [<fffffe000058fda0>] pstate: 600003c5
[    1.477683] sp : fffffe17c0733ed0
[    1.477686] x29: fffffe17c0733ed0 x28: 0000000000000000
[    1.477688] x27: fffffe00000907b0 x26: 0000000001670000
[    1.477690] x25: 0000000001640000 x24: 0000000000000000
[    1.477692] x23: 000000000000000a x22: 0000000000000002
[    1.477694] x21: 0000000000000000 x20: 00000000fffffffd
[    1.477695] x19: 0000000000020002 x18: fffffe0001351b10
[    1.477697] x17: 0000000000000002 x16: fffffe0001356000
[    1.477699] x15: fffffe00014fbdf0 x14: fffffe00014fba21
[    1.477701] x13: fffffe17c0024580 x12: 0000000000000000
[    1.477703] x11: fffffe0001374000 x10: fffffe00014f5000
[    1.477705] x9 : 0000000000000000 x8 : fffffe17fffaa810
[    1.477707] x7 : 0000000000000000 x6 : 0000000000000015
[    1.477708] x5 : 0000000000798b64 x4 : 0000000000000004
[    1.477710] x3 : 0000000000000000 x2 : fffffe17c0730000
[    1.477712] x1 : 0000000000000001 x0 : 0000000000000028
[    1.477712]
[    1.477713] ---[ end trace a4d35ecca081834f ]---
[    1.477714] Call trace:
[    1.477716] [<fffffe000058fda0>] gic_populate_rdist+0xe8/0x188
[    1.477718] [<fffffe0000590634>] gic_secondary_init+0x3c/0x58
[    1.477720] [<fffffe00000f0fc4>] notifier_call_chain+0x5c/0xa0
[    1.477722] [<fffffe00000f11c0>] __raw_notifier_call_chain+0x48/0x60
[    1.477724] [<fffffe00000d1b84>] cpu_notify+0x3c/0x70
[    1.477725] [<fffffe00000d23f4>] notify_cpu_starting+0x24/0x30
[    1.477727] [<fffffe000009d050>] secondary_start_kernel+0xf0/0x160
[    1.477728] [<000000000009079c>] 0x9079c
[    2.458005] CPU2: failed to come online
NOTICE:  [psci_smc_handler]:[408L] PSCI_CPU_ON_AARCH64 CALL
NOTICE:  [psci_smc_handler]:[409L] x1=0x20003 x2=0x90780 x3=0x0
NOTICE:  [scpi_set_css_power_state]:[85L] domain_cluster=0x1
NOTICE:  [scpi_set_css_power_state]:[93L] domain_cluster=0x1
schspa
2015-07-19 18:36
看了您的文章受益良多,在下请教一个问题。
对于device tree,有两种不同的硬件(有部分外设不同,芯片组等大部分都是相同的)。使用了不同的dts文件。问题:
两者是否可以使用同一个编译出来的zImage,但是使用编译出的不同的dtb文件?
wowo
2015-07-19 21:52
@schspa:可以的,DTS的目的就是这。
schspa
2015-07-19 22:31
@wowo:Thanks,不过这样是不是会在内核中将好多无用的驱动代码都编译进去?比如24XX的内核中也会编译进去好多6410或者4412相关的代码?
wowo
2015-07-20 14:07
@schspa:是的,除非使用module的方法。
edaplayer
2015-09-29 01:45
@schspa:编译进内核的大部分都是通用性非常强的代码,当你menuconfig时已经将那些专用代码剔除了
lover
2015-05-28 15:08
灰常长知识,多谢
zlf
2015-04-30 14:25
我也刚接触dt,
我现在知道什么是dt了,dt是什么作用也大体清楚了。

但是我不知道dts应该怎么写,哪些参数应该要,每个参数是什么意思。

我手头上有个板子关于mmc的,加上ti,removable就可以启动,不加就启动不了,我很迷惑。
楼主要是能帮我指点一下我会感激不尽。
&mmc1 {
    status = "okay";
    pinctrl-names = "default";
    pinctrl-0 = <&mmc1_pins>;
    vmmc-supply = <&evm_3v3_sd>;
    bus-width = <4>;
    //ti,non-removable; /* sd card for mmcblk0 eMMC for mmcblk1 */
    /*
     * SDCD signal is not being used here - using the fact that GPIO mode
     * is always hardwired.
     */
    cd-gpios = <&gpio6 27 0>;
};
zlf
2015-04-30 14:27
@zlf:加上之后kernel可以正常挂载/dev/mmcblk0p2
不加ti,removable就无法挂载/dev/mmcblk0p2,报panic
VFS:can not mount /dev/mmcbkl0p2
RobinHsiang
2015-04-30 17:21
@zlf:注释写的很清楚啊,不能移除这个mmc slot。
估计在mmc驱动里面用of API来读取后做了判断------有或者没有“ti,removable”,代码加载的分支是不一样的。
osmond
2015-04-16 22:29
我就是在找为什么要引入dtb看到窝窝的,受益良多,非常感谢楼主。
问个困惑好久的问题:dtb是和硬件相关的,那不同的核在同一款硬件上面运行,dtb是不是就不用换了。
wowo
2015-04-16 22:40
@osmond:DTB中的硬件是广义的硬件,包括传统硬件、CPU、SOC中的控制器等等。如果您这里“不同的核”可以保证以上都不变,dtb就不用换。不过我觉得这不可能存在。
其实DTB的目的就是让你换的,因为换它很方便,何必绞尽脑汁想着不换呢?岂不是辜负好意?
osmond
2015-04-16 23:02
@wowo:我的意思是dtb应该就是衔接硬件和内核的,如果是同样的硬件平台,传统硬件、CPU、SOC中的控制器等应该就是一致的吧,那用哪个核应该没有关系吧。
换个问法,在同样的硬件平台上,跑不同版本的内核需要配套的改用不同的dtb文件吗?
wowo
2015-04-16 23:39
@osmond:这个可以不用。
哈哈,好像您第一条回复少了一个字,怪不得理解差那么大:)
osmond
2015-04-17 08:45
@wowo:哈哈,是的……抱歉抱歉
非常感谢~
ayeu0425
2015-04-09 10:46
再次来读这篇文章,感觉就像看小说一样精彩,佩服博主。
linuxer_j
2015-03-04 14:48
非常感谢!看了你的号几篇文章,收货不小,而且都是比较新的内容。

发表评论:

Copyright @ 2013-2015 蜗窝科技 All rights reserved. Powered by emlog