device tree usage-model.txt

Linux and the Device Tree

 Linuxand the Device Tree

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

TheLinux usage model for device tree data

 

Author:Grant Likely <grant.likely@secretlab.ca>

 

Thisarticle describes how Linux uses the device tree.  An overview of

thedevice tree data format can be found on the device tree usage page

atdevicetree.org[1].

 

[1]http://devicetree.org/Device_Tree_Usage

 

The"Open Firmware Device Tree", or simply Device Tree (DT), is a data

structureand language for describing hardware. More specifically, it

isa description of hardware that is readable by an operating system

sothat the operating system doesn't need to hard code details of the

machine.

 

Structurally,the DT is a tree, or acyclic graph with named nodes, and

nodesmay have an arbitrary number of named properties encapsulating

arbitrarydata.  A mechanism also exists to createarbitrary

linksfrom one node to another outside of the natural tree structure.

 

Conceptually,a common set of usage conventions, called 'bindings',

isdefined for how data should appear in the tree to describe typical

hardwarecharacteristics including data busses, interrupt lines, GPIO

connections,and peripheral devices.

 

Asmuch as possible, hardware is described using existing bindings to

maximizeuse of existing support code, but since property and node

namesare simply text strings, it is easy to extend existing bindings

orcreate new ones by defining new nodes and properties.  Be wary,

however,of creating a new binding without first doing some homework

aboutwhat already exists.  There are currentlytwo different,

incompatible,bindings for i2c busses that came about because the new

bindingwas created without first investigating how i2c devices were

alreadybeing enumerated in existing systems.

 

1.History

----------

TheDT was originally created by Open Firmware as part of the

communicationmethod for passing data from Open Firmware to a client

program(like to an operating system).  Anoperating system used the

DeviceTree to discover the topology of the hardware at runtime, and

therebysupport a majority of available hardware without hard coded

information(assuming drivers were available for all devices).

 

SinceOpen Firmware is commonly used on PowerPC and SPARC platforms,

theLinux support for those architectures has for a long time used the

DeviceTree.

 

In2005, when PowerPC Linux began a major cleanup and to merge 32-bit

and64-bit support, the decision was made to require DT support on all

powerpcplatforms, regardless of whether or not they used Open

Firmware.  To do this, a DT representation called theFlattened Device

Tree(FDT) was created which could be passed to the kernel as a binary

blobwithout requiring a real Open Firmware implementation.  U-Boot,

kexec,and other bootloaders were modified to support both passing a

DeviceTree Binary (dtb) and to modify a dtb at boot time.  DT was

alsoadded to the PowerPC boot wrapper (arch/powerpc/boot/*) so that

adtb could be wrapped up with the kernel image to support booting

existingnon-DT aware firmware.

 

Sometime later, FDT infrastructure was generalized to be usable by

allarchitectures.  At the time of thiswriting, 6 mainlined

architectures(arm, microblaze, mips, powerpc, sparc, and x86) and 1

outof mainline (nios) have some level of DT support.

 

2.Data Model

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

Ifyou haven't already read the Device Tree Usage[1] page,

thengo read it now.  It's okay, I'll wait....

 

2.1High Level View

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

Themost important thing to understand is that the DT is simply a data

structurethat describes the hardware.  There isnothing magical about

it,and it doesn't magically make all hardware configuration problems

goaway.  What it does do is provide alanguage for decoupling the

hardwareconfiguration from the board and device driver support in the

Linuxkernel (or any other operating system for that matter).  Using

itallows board and device support to become data driven; to make

setupdecisions based on data passed into the kernel instead of on

per-machinehard coded selections.

 

Ideally,data driven platform setup should result in less code

duplicationand make it easier to support a wide range of hardware

witha single kernel image.

 

Linuxuses DT data for three major purposes:

1)platform identification,

2)runtime configuration, and

3)device population.

 

2.2Platform Identification

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

Firstand foremost, the kernel will use data in the DT to identify the

specificmachine.  In a perfect world, thespecific platform shouldn't

matterto the kernel because all platform details would be described

perfectlyby the device tree in a consistent and reliable manner.

Hardwareis not perfect though, and so the kernel must identify the

machineduring early boot so that it has the opportunity to run

machine-specificfixups.

 

Inthe majority of cases, the machine identity is irrelevant, and the

kernelwill instead select setup code based on the machine's core

CPUor SoC.  On ARM for example, setup_arch()in

arch/arm/kernel/setup.cwill call setup_machine_fdt() in

arch/arm/kernel/devtree.cwhich searches through the machine_desc

tableand selects the machine_desc which best matches the device tree

data.  It determines the best match by looking atthe 'compatible'

propertyin the root device tree node, and comparing it with the

dt_compatlist in struct machine_desc (which is defined in

arch/arm/include/asm/mach/arch.hif you're curious).

 

The'compatible' property contains a sorted list of strings starting

withthe exact name of the machine, followed by an optional list of

boardsit is compatible with sorted from most compatible to least.  For

example,the root compatible properties for the TI BeagleBoard and its

successor,the BeagleBoard xM board might look like, respectively:

 

         compatible ="ti,omap3-beagleboard", "ti,omap3450","ti,omap3";

         compatible ="ti,omap3-beagleboard-xm", "ti,omap3450","ti,omap3";

 

Where"ti,omap3-beagleboard-xm" specifies the exact model, it also

claimsthat it compatible with the OMAP 3450 SoC, and the omap3 family

ofSoCs in general.  You'll notice that thelist is sorted from most

specific(exact board) to least specific (SoC family).

 

Astutereaders might point out that the Beagle xM could also claim

compatibilitywith the original Beagle board.  However,one should be

cautionedabout doing so at the board level since there is typically a

highlevel of change from one board to another, even within the same

productline, and it is hard to nail down exactly what is meant when one

boardclaims to be compatible with another. For the top level, it is

betterto err on the side of caution and not claim one board is

compatiblewith another.  The notable exceptionwould be when one

boardis a carrier for another, such as a CPU module attached to a

carrierboard.

 

Onemore note on compatible values.  Anystring used in a compatible

propertymust be documented as to what it indicates. Add

documentationfor compatible strings in Documentation/devicetree/bindings.

 

Againon ARM, for each machine_desc, the kernel looks to see if

anyof the dt_compat list entries appear in the compatible property.

Ifone does, then that machine_desc is a candidate for driving the

machine.  After searching the entire table ofmachine_descs,

setup_machine_fdt()returns the 'most compatible' machine_desc based

onwhich entry in the compatible property each machine_desc matches

against.  If no matching machine_desc is found, then itreturns NULL.

 

Thereasoning behind this scheme is the observation that in the majority

ofcases, a single machine_desc can support a large number of boards

ifthey all use the same SoC, or same family of SoCs.  However,

invariablythere will be some exceptions where a specific board will

requirespecial setup code that is not useful in the generic case.

Specialcases could be handled by explicitly checking for the

troublesomeboard(s) in generic setup code, but doing so very quickly

becomesugly and/or unmaintainable if it is more than just a couple of

cases.

 

Instead,the compatible list allows a generic machine_desc to provide

supportfor a wide common set of boards by specifying "less

compatible"values in the dt_compat list.  In theexample above,

genericboard support can claim compatibility with "ti,omap3" or

"ti,omap3450".  If a bug was discovered on the originalbeagleboard

thatrequired special workaround code during early boot, then a new

machine_desccould be added which implements the workarounds and only

matcheson "ti,omap3-beagleboard".

 

PowerPCuses a slightly different scheme where it calls the .probe()

hookfrom each machine_desc, and the first one returning TRUE is used.

However,this approach does not take into account the priority of the

compatiblelist, and probably should be avoided for new architecture

support.

 

2.3Runtime configuration

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

Inmost cases, a DT will be the sole method of communicating data from

firmwareto the kernel, so also gets used to pass in runtime and

configurationdata like the kernel parameters string and the location

ofan initrd image.

 

Mostof this data is contained in the /chosen node, and when booting

Linuxit will look something like this:

 

         chosen {

                   bootargs ="console=ttyS0,115200 loglevel=8";

                   initrd-start =<0xc8000000>;

                   initrd-end =<0xc8200000>;

         };

 

Thebootargs property contains the kernel arguments, and the initrd-*

propertiesdefine the address and size of an initrd blob. Note that

initrd-endis the first address after the initrd image, so this doesn't

matchthe usual semantic of struct resource. The chosen node may also

optionallycontain an arbitrary number of additional properties for

platform-specificconfiguration data.

 

Duringearly boot, the architecture setup code calls of_scan_flat_dt()

severaltimes with different helper callbacks to parse device tree

databefore paging is setup.  Theof_scan_flat_dt() code scans through

thedevice tree and uses the helpers to extract information required

duringearly boot.  Typically theearly_init_dt_scan_chosen() helper

isused to parse the chosen node including kernel parameters,

early_init_dt_scan_root()to initialize the DT address space model,

andearly_init_dt_scan_memory() to determine the size and

locationof usable RAM.

 

OnARM, the function setup_machine_fdt() is responsible for early

scanningof the device tree after selecting the correct machine_desc

thatsupports the board.

 

2.4Device population

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

Afterthe board has been identified, and after the early configuration data

hasbeen parsed, then kernel initialization can proceed in the normal

way.  At some point in this process, unflatten_device_tree()is called

toconvert the data into a more efficient runtime representation.

Thisis also when machine-specific setup hooks will get called, like

themachine_desc .init_early(), .init_irq() and .init_machine() hooks

onARM.  The remainder of this section usesexamples from the ARM

implementation,but all architectures will do pretty much the same

thingwhen using a DT.

 

Ascan be guessed by the names, .init_early() is used for any machine-

specificsetup that needs to be executed early in the boot process,

and.init_irq() is used to set up interrupt handling.  Using a DT

doesn'tmaterially change the behaviour of either of these functions.

Ifa DT is provided, then both .init_early() and .init_irq() are able

tocall any of the DT query functions (of_* in include/linux/of*.h) to

getadditional data about the platform.

 

Themost interesting hook in the DT context is .init_machine() which

isprimarily responsible for populating the Linux device model with

dataabout the platform.  Historically thishas been implemented on

embeddedplatforms by defining a set of static clock structures,

platform_devices,and other data in the board support .c file, and

registeringit en-masse in .init_machine().  When DTis used, then

insteadof hard coding static devices for each platform, the list of

devicescan be obtained by parsing the DT, and allocating device

structuresdynamically.

 

Thesimplest case is when .init_machine() is only responsible for

registeringa block of platform_devices.  Aplatform_device is a concept

usedby Linux for memory or I/O mapped devices which cannot be detected

byhardware, and for 'composite' or 'virtual' devices (more on those

later).  While there is no 'platform device'terminology for the DT,

platformdevices roughly correspond to device nodes at the root of the

treeand children of simple memory mapped bus nodes.

 

Aboutnow is a good time to lay out an example. Here is part of the

devicetree for the NVIDIA Tegra board.

 

/{

         compatible ="nvidia,harmony", "nvidia,tegra20";

         #address-cells = <1>;

         #size-cells = <1>;

         interrupt-parent = <&intc>;

 

         chosen { };

         aliases { };

 

         memory {

                   device_type ="memory";

                   reg = <0x000000000x40000000>;

         };

 

         soc {

                   compatible ="nvidia,tegra20-soc", "simple-bus";

                   #address-cells = <1>;

                   #size-cells = <1>;

                   ranges;

 

                   intc:interrupt-controller@50041000 {

                            compatible ="nvidia,tegra20-gic";

                            interrupt-controller;

                            #interrupt-cells =<1>;

                            reg = <0x500410000x1000>, < 0x50040100 0x0100 >;

                   };

 

                   serial@70006300 {

                            compatible ="nvidia,tegra20-uart";

                            reg = <0x700063000x100>;

                            interrupts =<122>;

                   };

 

                   i2s1: i2s@70002800 {

                            compatible ="nvidia,tegra20-i2s";

                            reg = <0x700028000x100>;

                            interrupts =<77>;

                            codec =<&wm8903>;

                   };

 

                   i2c@7000c000 {

                            compatible ="nvidia,tegra20-i2c";

                            #address-cells =<1>;

                            #size-cells =<0>;

                            reg = <0x7000c0000x100>;

                            interrupts =<70>;

 

                            wm8903: codec@1a {

                                     compatible= "wlf,wm8903";

                                     reg =<0x1a>;

                                     interrupts= <347>;

                            };

                   };

         };

 

         sound {

                   compatible = "nvidia,harmony-sound";

                   i2s-controller =<&i2s1>;

                   i2s-codec =<&wm8903>;

         };

};

 

At.init_machine() time, Tegra board support code will need to look at

thisDT and decide which nodes to create platform_devices for.

However,looking at the tree, it is not immediately obvious what kind

ofdevice each node represents, or even if a node represents a device

atall.  The /chosen, /aliases, and /memorynodes are informational

nodesthat don't describe devices (although arguably memory could be

considereda device).  The children of the /soc nodeare memory mapped

devices,but the codec@1a is an i2c device, and the sound node

representsnot a device, but rather how other devices are connected

togetherto create the audio subsystem.  I knowwhat each device is

becauseI'm familiar with the board design, but how does the kernel

knowwhat to do with each node?

 

Thetrick is that the kernel starts at the root of the tree and looks

fornodes that have a 'compatible' property. First, it is generally

assumedthat any node with a 'compatible' property represents a device

ofsome kind, and second, it can be assumed that any node at the root

ofthe tree is either directly attached to the processor bus, or is a

miscellaneoussystem device that cannot be described any other way.

Foreach of these nodes, Linux allocates and registers a

platform_device,which in turn may get bound to a platform_driver.

 

Whyis using a platform_device for these nodes a safe assumption?

Well,for the way that Linux models devices, just about all bus_types

assumethat its devices are children of a bus controller.  For

example,each i2c_client is a child of an i2c_master. Each spi_device

isa child of an SPI bus.  Similarly forUSB, PCI, MDIO, etc.  The

samehierarchy is also found in the DT, where I2C device nodes only

everappear as children of an I2C bus node. Ditto for SPI, MDIO, USB,

etc.  The only devices which do not require aspecific type of parent

deviceare platform_devices (and amba_devices, but more on that

later),which will happily live at the base of the Linux /sys/devices

tree.  Therefore, if a DT node is at the root of thetree, then it

reallyprobably is best registered as a platform_device.

 

Linuxboard support code calls of_platform_populate(NULL, NULL, NULL, NULL)

tokick off discovery of devices at the root of the tree.  The

parametersare all NULL because when starting from the root of the

tree,there is no need to provide a starting node (the first NULL), a

parentstruct device (the last NULL), and we're not using a match

table(yet).  For a board that only needs toregister devices,

.init_machine()can be completely empty except for the

of_platform_populate()call.

 

Inthe Tegra example, this accounts for the /soc and /sound nodes, but

whatabout the children of the SoC node? Shouldn't they be registered

asplatform devices too?  For Linux DTsupport, the generic behaviour

isfor child devices to be registered by the parent's device driver at

driver.probe() time.  So, an i2c bus devicedriver will register a

i2c_clientfor each child node, an SPI bus driver will register

itsspi_device children, and similarly for other bus_types.

Accordingto that model, a driver could be written that binds to the

SoCnode and simply registers platform_devices for each of its

children.  The board support code would allocate andregister an SoC

device,a (theoretical) SoC device driver could bind to the SoC device,

andregister platform_devices for /soc/interrupt-controller, /soc/serial,

/soc/i2s,and /soc/i2c in its .probe() hook.  Easy,right?

 

Actually,it turns out that registering children of some

platform_devicesas more platform_devices is a common pattern, and the

devicetree support code reflects that and makes the above example

simpler.  The second argument to of_platform_populate()is an

of_device_idtable, and any node that matches an entry in that table

willalso get its child nodes registered.  Inthe Tegra case, the code

canlook something like this:

 

staticvoid __init harmony_init_machine(void)

{

         /* ... */

         of_platform_populate(NULL, of_default_bus_match_table,NULL, NULL);

}

 

"simple-bus"is defined in the ePAPR 1.0 specification as a property

meaninga simple memory mapped bus, so the of_platform_populate() code

couldbe written to just assume simple-bus compatible nodes will

alwaysbe traversed.  However, we pass it in asan argument so that

boardsupport code can always override the default behaviour.

 

[Needto add discussion of adding i2c/spi/etc child devices]

 

AppendixA: AMBA devices

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

 

ARMPrimecells are a certain kind of device attached to the ARM AMBA

buswhich include some support for hardware detection and power

management.  In Linux, struct amba_device and theamba_bus_type is

usedto represent Primecell devices.  However,the fiddly bit is that

notall devices on an AMBA bus are Primecells, and for Linux it is

typicalfor both amba_device and platform_device instances to be

siblingsof the same bus segment.

 

Whenusing the DT, this creates problems for of_platform_populate()

becauseit must decide whether to register each node as either a

platform_deviceor an amba_device.  This unfortunatelycomplicates the

devicecreation model a little bit, but the solution turns out not to

betoo invasive.  If a node is compatiblewith "arm,amba-primecell", then

of_platform_populate()will register it as an amba_device instead of a

platform_device.

翻译为转载,仅供参考。

Linux内核设备树数据使用模型。

 

OpenFirmware Device Tree (DT) 是一个数据结构,也是一种描述硬件的语言。准确地说,它是一种能被操作系统解析的描述硬件的语言,这样操作系统就不需要把硬件平台的细节在代码中写死。

 

从结构上来说,DT是一个树形结构,或者有名结点组成的非循环图,结点可能包含任意数量的有名属性,有名属性又可以包含任意数量的数据。同样存在一种机制,可以创建从一个结点到正常树形结构之外的链接。

 

从概念上讲,一套通用的使用方法,即bindings。Bindings定义了数据如何呈现在设备树中,怎样描述典型的硬件特性,包括数据总线,中断线,GPIO连接以及外设等。

 

尽可能多的硬件被描述从而使得已经存在的bindings最大化地使用源代码,但是由于属性名和结点名是简单字符串, 可以通过定义新结点和属性的方式很方便地扩展已经存在的bindings或者创建一个新的binding。在没有认真了解过已经存在的bindings的情况下,创建一个新的binding要慎之又慎。对于I2C总线,通常有两种不同的,互不相容的bindings出现,就是因为新的binding创建时没有研究I2C设备是如何在当前系统中被枚举的。

 

1.       历史

2.       数据模型

请参考Device Tree Usage章节

2.1   High Level View

必须要认识到的是,DT是一个描述硬件的数据结构。它并没有什么神奇的地方,也不能把所有硬件配置的问题都解决掉。它只是提供了一种语言,将硬件配置从Linux Kernel支持的board and device driver中提取出来。DT使得board和device变成数据驱动的,它们必须基于传递给内核的数据进行初始化,而不是像以前一样采用hard coded的方式。

 

观念上说,数据驱动平台初始化可以带来较少的代码重复率,使得单个内核映像能够支持很多硬件平台。

 

Linux使用DT的三个主要原因:

1)  平台识别 (Platform Identification)

2)  实时配置 (Runtime Configuration)

3)  设备植入 (Device Population)

 

2.2   平台识别

第一且最重要的是,内核使用DT中的数据去识别特定机器。最完美的情况是,内核应该与特定硬件平台无关,因为所有硬件平台的细节都由设备树来描述。然而,硬件平台并不是完美的,所以内核必须在早期初始化阶段识别机器,这样内核才有机会运行特定机器相关的初始化序列。

 

大多数情况下,机器识别是与设备树无关的,内核通过机器的核心CPU或者SOC来选择初始化代码。以ARM平台为例,setup_arch()会调用setup_machine_fdt(),后者遍历machine_desc链表,选择最匹配设备树数据的machine_desc结构体。它是通过查找设备树根结点的compatible属性并与machine_desc->dt_compat进行比较来决定哪一个machine_desc结构体是最适合的。

 

Compatible属性包含一个有序的字符串列表,它以确切的机器名开始,紧跟着一个可选的board列表,从最匹配到其他匹配类型。以TI BeagleBoard的compatible属性为例,BeagleBoard xM Board可能描述如下:

compatible= "ti,omap3-beagleboard", "ti,omap3450","ti,omap3";

compatible= "ti,omap3-beagleboard-xm", "ti,omap3450","ti,omap3";

在这里,”ti, omap3-beagleboard-xm”是最匹配的模型,"ti,omap3450"次之,"ti,omap3"再次之。

 

机敏的读者可能指出,Beagle xM也可以声明匹配"ti,omap3-beagleboard",但是要注意的是,板级层次上,两个机器之间的变化比较大,很难确定是否兼容。从顶层上来看,宁可小心也不要去声明一个board兼容另外一个。值得注意的情况是,当一个board承载另外一个,例如一个CPU附加在一个board上。(两种CPU支持同一个board的情况)

 

Compatible属性的另一个值得注意的地方是,它所用到的字符串最好归档在Documnetation/devicetree/bindings中。

 

在ARM平台上,对于每一个machine_desc,内核会留意任何di_compat列表入口是否出现在compatible属性中,如果有,那么该machine_desc就成为一个候选值。遍历完machine_desc链表之后,setup_machine_fdt()返回最匹配的machine_desc based on whichentry in the compatible property each machine_desc matches against. 如果没有匹配的machine_desc被找到,那么返回NULL。

 

这种方案的原因是,通过观察,大多数情况下,单个machine_desc支持很多用同样SOC或者一个系列SOC的boards。然而,总有一种情况就是某个特定的板子需要特殊的启动代码。特殊情况下,需要在通用的启动代码中明确地甄别某个板子的初始化,这样的操作是非常麻烦且不利于维护的。

 

反之,compatible列表允许一个通用的machine_desc通过在dt_compat字段添加less compatible值从而支持多个板子。在上述实例中,通用板子可以通过声明兼容"ti,omap3450"和 "ti,omap3"。

 

PowerPC采用一种稍微不同的方案实现板子的识别,它通过调用每个machine_desc的machine_desc->probe()Hook函数来实现,第一个返回TRUE的machine_desc结构体被返回。然而,这种情况并没有纳入compatible list的优先级别进行考量, 在新的架构中应该予以避免。

 

2.3   实时配置

大多数情况下,DT是firmware与Kernel之间进行数据通信的唯一方式,所以也用于传递实时的或者配置数据给内核,比如内核参数,initrd映像的地址等。

 

大多数这种数据被包含在DT的/chosen结点,形如:

chosen{ bootargs = "console=ttyS0,115200 loglevel=8";

initrd-start= <0xc8000000>;

           initrd-end =<0xc8200000>;

};

Bootargs属性包含内核参数,initrd-*属性定义和initrd文件的首地址和大小。Chosen结点可能包含任意数量的描述平台特殊配置的属性。

 

在早期初始化阶段,体系结构初始化相关的代码会多次调用of_scan_flat_dt() withdifferent helper callbacks去解析设备树数据before paging is setup。Of_scan_flat_dt()遍历设备树,利用helper提取需要的信息。通常,early_init_dt_scan_chosen()helper用于解析Chosen结点,包括内核参数;early_init_dt_scan_root()用于初始化DT地址空间模型;early_init_dt_scan_memory()用于决定可用内存的大小和地址。

 

在ARM平台,setup_machine_fdt()负责在选取到正确的machine_desc结构体之后进行早期的DT遍历。

 

2.4   设备植入 (Device Population)

经过板子识别和早期配置数据解析之后,内核进一步进行初始化。期间,unflatten_device_tree()被调用,将DT的数据转换成一种更有效的实时的形式。同时,机器特殊的启动hooks也会被调用,例如machine_desc->init_early(),machine_desc->init_irq(),machine_desc->init_machine()等钩子函数。

 

通过名称我们可以猜想到,init_early()会在早期初始化时被执行,init_irq()用于初始化中断处理。利用DT并没有实质上改变这些函数的行为和功能。如果DT被提供,那么不管是init_early()还是init_irq()都可以调用任何DT查找函数(of_* ininclude/linux/of*.h)去获取额外的平台信息。

 

在DT上下文中最有趣的钩子函数是machine_desc->init_machine()函数,该函数主要负责populating Linux设备模型。在早期内核中,是通过在board support .c 文件中定义一组static clock structures,platform_devices以及其他数据,并全部注册在machine_desc->init_machine()中。使用DT之后,不再是为每个特定平台写死静态设备,而是通过解析DT生成设备链表,并动态地分配设备结构体。

 

最简单的machine_desc->init_machine()仅仅负责注册platform_devices。一个platform_device被Linux用来定义那些不能被硬件检测的需要做内存映射或者I/O映射的, 以及组合或者虚拟设备等。对于DT而言,没有platform_device这个概念,platform_device可以被简单地认为是DT根结点下的设备结点以及简单内存映射总线结点的子设备结点。

到此,可以做一个DT的简单的lay out。以NVDIA Tegra board的设备树部分内容为例:

/{compatible = "nvidia,harmony", "nvidia,tegra20";

#address-cells= <1>;

#size-cells= <1>;

interrupt-parent= <&intc>;

chosen{ };

aliases{ };

memory{ device_type = "memory";

           reg =<0x00000000 0x40000000>; };

soc{ compatible = "nvidia,tegra20-soc", "simple-bus";

           #address-cells= <1>;

           #size-cells =<1>;

           ranges;

           intc: interrupt-controller@50041000{ compatible = "nvidia,tegra20-gic";

                    interrupt-controller;

                    #interrupt-cells= <1>;

                    reg =<0x50041000 0x1000>, < 0x50040100 0x0100 >; };

           serial@70006300{ compatible = "nvidia,tegra20-uart";

                    reg =<0x70006300 0x100>;

                    interrupts =<122>; };

           i2s1:i2s@70002800 { compatible = "nvidia,tegra20-i2s";

                    reg =<0x70002800 0x100>;

                    interrupts =<77>;

                    codec =<&wm8903>; };

           i2c@7000c000 {compatible = "nvidia,tegra20-i2c";

                    #address-cells= <1>;

                    #size-cells =<0>;

                    reg =<0x7000c000 0x100>;

                    interrupts =<70>;

                    wm8903:codec@1a { compatible = "wlf,wm8903";

                             reg =<0x1a>;

                             interrupts =<347>; }; }; };

sound{ compatible = "nvidia,harmony-sound";

           i2s-controller= <&i2s1>;

           i2s-codec =<&wm8903>; }; };

 

在machine_desc->machine_init()被调用时,Tegra board会查找DT去决定哪些结点用来创建platform_devices。然而,我们没法通过DT立刻看出一个结点代表何种类型的设备,或者一个结点是否代表了一个设备。

 

/chosen,/aliases, /memory结点是信息结点,他们不代表设备。

 

/soc结点的子结点是内存映射设备,但是codec@1a是一个I2C设备,另外,sound结点也不代表一个设备,而是描述了设备如何连接从而构成audio子系统。我之所以知道每个设备是因为我数序硬件设计,但是内核是怎样知道每个结点代表了什么的呢?

 

这其中的诀窍在于,内核从DT的根结点开始遍历那些有compatible属性的结点。首先,它假设任何包含compatible属性的结点代表了一个设备;其次,它假设根结点下的任何结点是直接挂接在处理器总线上的,或者是一个混杂的没办法描述的系统设备。对于每个这样的结点,Linux都分配并注册一个platform_device结构体,它们反过来会被绑定在一个platform_driver上。

 

为什么要用platform_device结构体来实例化这些结点呢?这要归因于Linux驱动模型,几乎所有bus_types都认为它的设备是bus controller的子设备。例如,每一个I2C_client都是I2C_master的子设备;每一个spi_device都是SPI总线的子设备。对于USB,PCI,MDIO等总线而言,同样如此。这种层级关系同样出现在DT中,I2C设备结点之出现在I2C总线结点下,SPI,MDIO,USB等同样如此。唯一不需要任何特定类型父设备的设备是platform device,它们快乐地生活在 Linux /sys/devices树下。因此,如果一个DT结点在DT的根结点下,那么最好把它注册成一个platform_device。

 

Linux板级支持代码调用of_platform_populate(NULL,NULL, NULL)开始从DT根结点发现设备。之所以所有参数都是NULL,是因为它从DT根结点开始遍历,所以不需要指定起始结点(第一个NULL),parent struct device(最后一个结点),也不使用match table。对于一个只需要注册设备的板子,machine_desc->init_machine()除了调用of_platform_populate()之外,可以不做任何操作。

 

在Tegra的例子中,platform_devices占据了/soc和/sound结点,但是SoC结点的子结点如何呢?它们也应该被注册成platform_devices吗?通常的做法是在父设备驱动的probe()函数被调用时注册子设备。这样,一个I2C总线设备驱动会为每一个子结点注册一个i2c_client设备结构体, SPI总线驱动会为子结点注册spi_device结构体,对于其他总线而言同样如此。依据该模型,一个绑定Soc结点的驱动会为Soc结点的所有子结点注册platform_device结构体。板级支持代码会分配和注册一个Soc设备,理论上,一个Soc设备驱动会绑定在Soc设备上,并在其probe()钩子函数中为/soc/interrupt-controller,/soc/serial, /soc/i2s, /soc/i2c注册platform_devices结构体。Easy, right?

 

事实上,注册paltform_devices的子设备为platform_devices是一个通用模型,设备树支持代码反映了该模型,并使上述实例更易于理解。函数of_platform_populate()的第二个参数是一个of_device_id列表,任何匹配该of_device_id列表任何一个entry的结点都可以使得其子结点被注册,以Tegra为例, 可以如此:

staticvoid __init harmony_init_machine(void) {

 

of_platform_populate(NULL,of_default_bus_match_table, NULL, NULL);

}

 

“simple_bus”是定义在ePARP 1.0 Specification中的,作为一个代表简单内存映射总线的属性,这样of_platform_populate()函数可以写成假设simple-bus兼容的结点都可以被转换成设备结构体。然而,我们把它当作一个参数,这样,板级支持代码可以用它来覆盖默认的行为。

 

 

AppendixA: AMBA devices

 

ARM原始核可以被认为是一个挂载在ARM AMBA总线上的设备,它支持硬件检测和电源管理。在Linux中,amba_device结构体和amba_bus_type用来代表原始核设备。然而,值得注意的是,并非所有AMBA总线上的设备都是原始核,对于Linux而言,amba_device和platform_device实例可以被认为是拥有同样bus字段的兄弟姐妹。

 

当使用DT时,上述情况对of_platform_populate()创建设备造成了困扰,因为它必须决定是把设备注册成platform_device还是amba_device。这给设备创建模型添加了一点复杂性,但是解决方案并不麻烦。如果一个结点兼容”arm, amba-primecell”, 那么of_platform_populate()会把它注册成amba_device,否则注册成platform_device。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Go语言(也称为Golang)是由Google开发的一种静态强类型、编译型的编程语言。它旨在成为一门简单、高效、安全和并发的编程语言,特别适用于构建高性能的服务器和分布式系统。以下是Go语言的一些主要特点和优势: 简洁性:Go语言的语法简单直观,易于学习和使用。它避免了复杂的语法特性,如继承、重载等,转而采用组合和接口来实现代码的复用和扩展。 高性能:Go语言具有出色的性能,可以媲美C和C++。它使用静态类型系统和编译型语言的优势,能够生成高效的机器码。 并发性:Go语言内置了对并发的支持,通过轻量级的goroutine和channel机制,可以轻松实现并发编程。这使得Go语言在构建高性能的服务器和分布式系统时具有天然的优势。 安全性:Go语言具有强大的类型系统和内存管理机制,能够减少运行时错误和内存泄漏等问题。它还支持编译时检查,可以在编译阶段就发现潜在的问题。 标准库:Go语言的标准库非常丰富,包含了大量的实用功能和工具,如网络编程、文件操作、加密解密等。这使得开发者可以更加专注于业务逻辑的实现,而无需花费太多时间在底层功能的实现上。 跨平台:Go语言支持多种操作系统和平台,包括Windows、Linux、macOS等。它使用统一的构建系统(如Go Modules),可以轻松地跨平台编译和运行代码。 开源和社区支持:Go语言是开源的,具有庞大的社区支持和丰富的资源。开发者可以通过社区获取帮助、分享经验和学习资料。 总之,Go语言是一种简单、高效、安全、并发的编程语言,特别适用于构建高性能的服务器和分布式系统。如果你正在寻找一种易于学习和使用的编程语言,并且需要处理大量的并发请求和数据,那么Go语言可能是一个不错的选择。
Go语言(也称为Golang)是由Google开发的一种静态强类型、编译型的编程语言。它旨在成为一门简单、高效、安全和并发的编程语言,特别适用于构建高性能的服务器和分布式系统。以下是Go语言的一些主要特点和优势: 简洁性:Go语言的语法简单直观,易于学习和使用。它避免了复杂的语法特性,如继承、重载等,转而采用组合和接口来实现代码的复用和扩展。 高性能:Go语言具有出色的性能,可以媲美C和C++。它使用静态类型系统和编译型语言的优势,能够生成高效的机器码。 并发性:Go语言内置了对并发的支持,通过轻量级的goroutine和channel机制,可以轻松实现并发编程。这使得Go语言在构建高性能的服务器和分布式系统时具有天然的优势。 安全性:Go语言具有强大的类型系统和内存管理机制,能够减少运行时错误和内存泄漏等问题。它还支持编译时检查,可以在编译阶段就发现潜在的问题。 标准库:Go语言的标准库非常丰富,包含了大量的实用功能和工具,如网络编程、文件操作、加密解密等。这使得开发者可以更加专注于业务逻辑的实现,而无需花费太多时间在底层功能的实现上。 跨平台:Go语言支持多种操作系统和平台,包括Windows、Linux、macOS等。它使用统一的构建系统(如Go Modules),可以轻松地跨平台编译和运行代码。 开源和社区支持:Go语言是开源的,具有庞大的社区支持和丰富的资源。开发者可以通过社区获取帮助、分享经验和学习资料。 总之,Go语言是一种简单、高效、安全、并发的编程语言,特别适用于构建高性能的服务器和分布式系统。如果你正在寻找一种易于学习和使用的编程语言,并且需要处理大量的并发请求和数据,那么Go语言可能是一个不错的选择。
Go语言(也称为Golang)是由Google开发的一种静态强类型、编译型的编程语言。它旨在成为一门简单、高效、安全和并发的编程语言,特别适用于构建高性能的服务器和分布式系统。以下是Go语言的一些主要特点和优势: 简洁性:Go语言的语法简单直观,易于学习和使用。它避免了复杂的语法特性,如继承、重载等,转而采用组合和接口来实现代码的复用和扩展。 高性能:Go语言具有出色的性能,可以媲美C和C++。它使用静态类型系统和编译型语言的优势,能够生成高效的机器码。 并发性:Go语言内置了对并发的支持,通过轻量级的goroutine和channel机制,可以轻松实现并发编程。这使得Go语言在构建高性能的服务器和分布式系统时具有天然的优势。 安全性:Go语言具有强大的类型系统和内存管理机制,能够减少运行时错误和内存泄漏等问题。它还支持编译时检查,可以在编译阶段就发现潜在的问题。 标准库:Go语言的标准库非常丰富,包含了大量的实用功能和工具,如网络编程、文件操作、加密解密等。这使得开发者可以更加专注于业务逻辑的实现,而无需花费太多时间在底层功能的实现上。 跨平台:Go语言支持多种操作系统和平台,包括Windows、Linux、macOS等。它使用统一的构建系统(如Go Modules),可以轻松地跨平台编译和运行代码。 开源和社区支持:Go语言是开源的,具有庞大的社区支持和丰富的资源。开发者可以通过社区获取帮助、分享经验和学习资料。 总之,Go语言是一种简单、高效、安全、并发的编程语言,特别适用于构建高性能的服务器和分布式系统。如果你正在寻找一种易于学习和使用的编程语言,并且需要处理大量的并发请求和数据,那么Go语言可能是一个不错的选择。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值