linux设备树语法-程序员宅基地

技术标签: 数据结构与算法  嵌入式  

设备树语法及绑定

概述

Device Tree是一种用来描述硬件的数据结构,类似板级描述语言,起源于OpenFirmware(OF)。

就ARM平台来说,设备树文件存放在arch/arm/boot/dts下,绑定文档存在Documentation/devicetree/bindings下。

设备树由一系列被命名的节点(node)和属性(property)组成,而节点本身可包含子节点。所谓属性,就是成对出现的名称和值。

在设备树中可描述的信息包括(原来这些信息大多被编码在内核中):

》CPU数量和类别

》内存基地址和大小

》总线和桥

》外设连接

》中断控制器和中断使用情况

》GPIO控制器和GPIO使用情况

》时钟控制器和时钟使用情况

它基本上就是画一棵电路板上CPU、总线、设备组成的树,Bootloader会将这棵树传递给内核,然后内核可以识别这棵树,并根据它展开出Linux内核中的platform_device、i2c_client、spi_device等设备,而这些设备用到的内存、IRQ等资源,也被传递给了内核,内核会将这些资源绑定给展开的相应的设备。

对于设备树上的节点和属性具体是如何来描述设备的硬件细节的,一般需要绑定文件(.txt)说明:

rtc/rtc-cmos.txt

Motorola mc146818 compatible RTC
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Required properties:
  - compatible : "motorola,mc146818"
  - reg : should contain registers location and length.

Optional properties:
  - interrupts : should contain interrupt.
  - interrupt-parent : interrupt source phandle.
  - ctrl-reg : Contains the initial value of the control register also
    called "Register B". 
  - freq-reg : Contains the initial value of the frequency register also
    called "Regsiter A". 

"Register A" and "B" are usually initialized by the firmware (BIOS for 
instance). If this is not done, it can be performed by the driver.

ISA Example:

    rtc@70 {
             compatible = "motorola,mc146818";
             interrupts = <8 3>; 
             interrupt-parent = <&ioapic1>;
             ctrl-reg = <2>;
             freq-reg = <0x26>;
             reg = <1 0x70 2>;
     };

基本可以看出,设备树绑定文档的主要内容包括:

》关于该模块的最基本的描述。

》必要属性的描述

》可选属性的描述

》一个示例

又如常见的GPIO控制LED,属性绑定示例,gpio/led.txt

LEDs connected to GPIO lines

Required properties:
- compatible : should be "gpio-leds".

Each LED is represented as a sub-node of the gpio-leds device.  Each
node's name represents the name of the corresponding LED.

LED sub-node properties:
- gpios :  Should specify the LED's GPIO, see "Specifying GPIO information
  for devices" in Documentation/devicetree/booting-without-of.txt.  Active
  low LEDs should be indicated using flags in the GPIO specifier.
- label :  (optional) The label for this LED.  If omitted, the label is
  taken from the node name (excluding the unit address).
- linux,default-trigger :  (optional) This parameter, if present, is a
  string defining the trigger assigned to the LED.  Current triggers are:
    "backlight" - LED will act as a back-light, controlled by the framebuffer
          system
    "default-on" - LED will turn on, but see "default-state" below
    "heartbeat" - LED "double" flashes at a load average based rate
    "ide-disk" - LED indicates disk activity
    "timer" - LED flashes at a fixed, configurable rate
- default-state:  (optional) The initial state of the LED.  Valid
  values are "on", "off", and "keep".  If the LED is already on or off
  and the default-state property is set the to same value, then no
  glitch should be produced where the LED momentarily turns off (or
  on).  The "keep" setting will keep the LED at whatever its current
  state is, without producing a glitch.  The default is off if this
  property is not present.

Examples:

leds {
    compatible = "gpio-leds";
    hdd {
        label = "IDE Activity";
        gpios = <&mcu_pio 0 1>; /* Active low */
        linux,default-trigger = "ide-disk";
    };

    fault {
        gpios = <&mcu_pio 1 0>;
        /* Keep LED on if BIOS detected hardware fault */
        default-state = "keep";
    };
};

run-control {
    compatible = "gpio-leds";
    red {
        gpios = <&mpc8572 6 0>;
        default-state = "off";
    };
    green {
        gpios = <&mpc8572 7 0>;
        default-state = "on";
    };
}

linux内核下scripts/checkpatch.pl会运行一个检查,如果有人在设备树中新添加了compatible字符串,而没有添加相应的文档进行解释,checkpatch程序会报出警告:UNDOCUMENTED_DT_STRINGDT compatible string xxx appears un-documented。

设备树语法

设备树文件包括两种:dts和dtsi。dtsi相当于头文件,其可能包含一些板卡设备的共用部分(被提炼出来,如CPU信息等,多种类板卡共用同一CPU),dts可通过include包含dtsi。

#include "xxx.dtsi"
/include/ "xxx.dtsi"

 下面看个示例

/*
 * Allwinner Technology CO., Ltd. sun50iw1p1 platform
 * modify base on juno.dts
 */

#include <dt-bindings/interrupt-controller/arm-gic.h>
#include <dt-bindings/gpio/gpio.h>
 #include "sun50iw1p1-clk.dtsi"
 #include "sun50iw1p1-pinctrl.dtsi"
 / {
     model = "sun50iw1p1";
     compatible = "arm,sun50iw1p1", "arm,sun50iw1p1";
     interrupt-parent = <&gic>;
     #address-cells = <2>;
     #size-cells = <2>;
 
     aliases {
         serial0 = &uart0;
         ......
         ......
         ......
         boot_disp = &boot_disp;
     };
 
     chosen {
         bootargs = "earlyprintk=sunxi-uart,0x01c28000 loglevel=8 initcall_debug=1 console=ttyS0 init=/init";
         linux,initrd-start = <0x0 0x0>;
         linux,initrd-end = <0x0 0x0>;
     };
 
     cpus {
         #address-cells = <2>;
         #size-cells = <0>;
 
         cpu@0 {
             device_type = "cpu";
             compatible = "arm,cortex-a53","arm,armv8";
             reg = <0x0 0x0>;
             enable-method = "psci";
             cpufreq_tbl = < 480000 600000 720000 816000 1008000 
                     1104000 1152000 1200000 1344000>;
             clock-latency = <2000000>;
             clock-frequency = <1008000000>;
             cpu-idle-states = <&CPU_SLEEP_0 &CLUSTER_SLEEP_0 &SYS_SLEEP_0>;
         };
         cpu@1 {
             device_type = "cpu";
             compatible = "arm,cortex-a53","arm,armv8";
             reg = <0x0 0x1>;
             enable-method = "psci";
             clock-frequency = <1008000000>;
             cpu-idle-states = <&CPU_SLEEP_0 &CLUSTER_SLEEP_0 &SYS_SLEEP_0>;
         };
         cpu@2 {
             ......
             ......
             ......
         };
         cpu@3 {
             ......
             ......
             ......
         };
     };
 
     memory@40000000 {
         device_type = "memory";
         reg = <0x00000000 0x40000000 0x00000000 0x40000000>;
     };
 
     gic: interrupt-controller@1c81000 {
         compatible = "arm,cortex-a15-gic", "arm,cortex-a9-gic";
         #interrupt-cells = <3>;
         #address-cells = <0>;
         device_type = "gic"; 
         interrupt-controller;
         reg = <0x0 0x01c81000 0 0x1000>, /* GIC Dist */
               <0x0 0x01c82000 0 0x2000>, /* GIC CPU */
               <0x0 0x01c84000 0 0x2000>, /* GIC VCPU Control */
               <0x0 0x01c86000 0 0x2000>; /* GIC VCPU */
         interrupts = <GIC_PPI 9 0xf04>; /* GIC Maintenence IRQ */
     };
 
     soc: soc@01c00000 {
         compatible = "simple-bus";
         #address-cells = <2>;
         #size-cells = <2>;
         ranges;
         device_type = "soc";
 
         uart0: uart@01c28000 {
             compatible = "allwinner,sun50i-uart";
             device_type = "uart0";
             reg = <0x0 0x01c28000 0x0 0x400>;
             interrupts = <GIC_SPI 0 IRQ_TYPE_LEVEL_HIGH>;
             clocks = <&clk_uart0>;
             pinctrl-names = "default", "sleep";
             pinctrl-0 = <&uart0_pins_a>;
             pinctrl-1 = <&uart0_pins_b>;
             uart0_port = <0>;
             uart0_type = <2>;
             status = "disabled";
             ......
             ......
             ......            
         };
         ......
         ......
         ......
     };
 };

 *note:<name>[@<unit-address>]是节点的格式,其中unit-address是单位偏移地址,[]可省略。

“/"代表根节点;

“model”是板的ID;

"compatible"是平台兼容,一般格式是"manufacturer,model"。内核或者uboot依靠这个属性找到相对应driver,若"compatible"出现多个属性,按序匹配driver;

“#address-cells”是address的单位(32bit);

“#size-cells”是length的单位(32bit);

"reg"是寄存器,格式是"<address,length>",作为平台内存资源;

"aliase" 是别名,必须节点全称,可以通过地址引用获取;

”chosen“是板级启动参数;

"cpus"是SOC的CPU信息,可以改变运行频率或者开关CPU;

"memory"是板级内存的信息。

"interrupts"是中断控制器,根据SOC自定义格式,这里是<输入类型 中断号 触发方式>,作为平台中断资源;

“interrupt-controller”指示这个节点是中断控制节点;

"[label:]"如gic: interrupt-controller@1c81000,这个标签可以作为地址赋值到其他节点的属性;

“device_type":设备类型,寻找节点可以依据这个属性;

"status"是开关节点设备的状态,取值"okay"或者"ok"表示使能,"disabled"表示失能。

根节点兼容性

 一个最简单的设备树必须包含根节点,cpus节点,memory节点。根节点的名字及全路径都是“/”,至少需要包含model和compatible两个属性。

model属性我是用来描述产品型号的,类型为字符串,推荐的格式为“manufacturer,model-number”(非强制的)。根节点的model属性描述的是板子的型号或者芯片平台的型号,如:

model = "Atmel AT91SAM9G20 family SoC"
model = "Samsung SMDK5420 board based on EXYNOS5420"
从软件的层面讲model属性仅仅表示一个名字而已,没有更多的作用。

compatible属性则不同,该属性决定软件如何匹配硬件对硬件进行初始化。compatible属性的类型是字符串数组,按照范围从小到大的顺序排列,每个字符串表示一种匹配类型。根节点的compatible属性表示平台如何匹配,比如

compatible = "samsung,smdk5420", "samsung,exynos5420", "samsung,exynos5"

表示软件应该首先匹配'samsung,smdk5420',这个是一款开发板。

如果无法匹配,再试着匹配"samsung,exynos5420",这个是一款芯片平台。

如果还是无法匹配,还可以试着匹配 "samsung,exynos5",这是一个系列的芯片平台。

这里说的匹配是指软件根据该信息找到对应的代码,如对应的初始化函数。

根节点表示的是整个板子或者芯片平台,所以在系统初始化比较早的时候就需要确认是什么平台,怎样初始化。对于Linux,是通过在start_kernel函数调用setup_arch函数实现的。不同的架构,setup_arch函数的实现不同,对于arm架构,setup_arch函数源代码位于arch/arm/kernel/setup.c中。

void __init setup_arch(char **cmdline_p)
{
    struct machine_desc *mdesc;

    setup_processor();
    mdesc = setup_machine_fdt(__atags_pointer);
    if (!mdesc)
        mdesc = setup_machine_tags(machine_arch_type);
    machine_desc = mdesc;
    machine_name = mdesc->name;
...
}

过去,ARM Linux针对不同的电路板会建立由MACHINE_START和MACHINE_END包围起来的针对这个machine的一系列callback,譬如:

MACHINE_START(AM335XEVM, "am335xevm")
    /* Maintainer: Texas Instruments */
    .atag_offset    = 0x100,
    .map_io     = am335x_evm_map_io,
    .init_early = am33xx_init_early,
    .init_irq   = ti81xx_init_irq,
    .handle_irq     = omap3_intc_handle_irq,
    .timer      = &omap3_am33xx_timer,
    .init_machine   = am335x_evm_init,
MACHINE_END

这些不同的machine会有不同的MACHINE ID,Uboot在启动Linux内核时会将MACHINE ID存放在r1寄存器,Linux启动时会匹配Bootloader传递的MACHINE ID和MACHINE_START声明的MACHINE ID,然后执行相应machine的一系列初始化函数。

引入Device Tree之后,MACHINE_START变更为DT_MACHINE_START,其中含有一个.dt_compat成员,用于表明相关的machine与.dts中root结点的compatible属性兼容关系。如果Bootloader传递给内核的Device Tree中root结点的compatible属性出现在某machine的.dt_compat表中,相关的machine就与对应的Device Tree匹配,从而引发这一machine的一系列初始化函数被执行。

static const char *omap3_boards_compat[] __initdata = { 
    "ti,omap3",
    NULL,
};

DT_MACHINE_START(OMAP3_DT, "Generic OMAP3 (Flattened Device Tree)")
    .atag_offset    = 0x100,
    .reserve    = omap_reserve,
    .map_io     = omap3_map_io,
    .init_early = omap3430_init_early,
    .init_irq   = omap3_init_irq,
    .handle_irq = omap3_intc_handle_irq,
    .init_machine   = omap3_init,
    .timer      = &omap3_timer,
    .dt_compat  = omap3_boards_compat,
MACHINE_END

Linux倡导针对多个SoC、多个电路板的通用DT machine,即一个DT machine的.dt_compat表含多个电路板.dts文件的root结点compatible属性字符串。之后,如果的电路板的初始化序列不一样,可以透过

int of_machine_is_compatible(const char *compat)

API判断具体的电路板是什么。

158 static char const *exynos5_dt_compat[] __initdata = {
159         "samsung,exynos5250",
160         "samsung,exynos5440",
161         NULL
162 };
163
177 DT_MACHINE_START(EXYNOS5_DT, "SAMSUNG EXYNOS5 (Flattened Device Tree)")
178         /* Maintainer: Kukjin Kim <[email protected]> */
179         .init_irq       = exynos5_init_irq,
180         .smp            = smp_ops(exynos_smp_ops),
181         .map_io         = exynos5_dt_map_io,
182         .handle_irq     = gic_handle_irq,
183         .init_machine   = exynos5_dt_machine_init,
184         .init_late      = exynos_init_late,
185         .timer          = &exynos4_timer,
186         .dt_compat      = exynos5_dt_compat,
187         .restart        = exynos5_restart,
188         .reserve        = exynos5_reserve,
189 MACHINE_END

它的.init_machine成员函数就针对不同的machine进行了不同的分支处理:

126 static void __init exynos5_dt_machine_init(void)
127 {
128149
150         if (of_machine_is_compatible("samsung,exynos5250"))
151                 of_platform_populate(NULL, of_default_bus_match_table,
152                                      exynos5250_auxdata_lookup, NULL);
153         else if (of_machine_is_compatible("samsung,exynos5440"))
154                 of_platform_populate(NULL, of_default_bus_match_table,
155                                      exynos5440_auxdata_lookup, NULL);
156 }

 

根节点还可能包含的属性为#address-cells和#size-cells,规范中说明这两个属性是必须的,实际应用时是可选的,还记得属性那一节说这两个属性如果没有都是有默认值的,#address-cells默认值为2,#size-cells默认值为1。根节点下必须包含的子节点为cpus和memory,后边会说明cpus下边还有每个cpu的子节点,memory节点下边定义的就是memory的起始地址及大小,所以根节点的#address-cells和#size-cells属性实际上说明的就是从cpu角度看系统总线的地址长度和大小。

设备节点兼容性

如何查找节点属性

每个节点都有一个兼容属性(compatible),兼容属性用于驱动和设备的绑定。

兼容属性值都应在绑定文档中描述,即Documentation/devicetree/bindings下文档,通过搜索兼容属性值可查找到该节点应包含的属性,如上述绑定文档。

如需了解通过GPIO连接的LED的属性,可这样查询:

#cd Documentation/devicetree/bindings
#grep 'gpio' ./* -rn
#grep 'led' ./* -rn

主要文件为gpio/led.txt,compatible = “gpio-leds”,每个子节点的属性包括gpios,label,default-status等。 

驱动中绑定

使用设备树后,驱动需要与dts中描述的设备节点进行匹配,从而使驱动的probe()函数执行。新的驱动、设备的匹配变成了设备树节点的兼容属性和设备驱动中of_match_table的匹配。

对于platform_driver而言,需要添加一个OF匹配表,比如gpio-leds的驱动程序位于drivers/leds/leds-gpio.c。

static const struct of_device_id of_gpio_leds_match[] = {
    { .compatible = "gpio-leds", },
    {},
};
static struct platform_driver gpio_led_driver = {
    .probe      = gpio_led_probe,
    .remove     = gpio_led_remove,
    .driver     = {
        .name   = "leds-gpio",
        .owner  = THIS_MODULE,
        .of_match_table = of_match_ptr(of_gpio_leds_match),
    },
};

module_platform_driver(gpio_led_driver);

of_match_table添加匹配的dts中相关节点的兼容属性。

驱动中设备树解析

设备树的解析一般放在probe()中,当兼容性匹配时驱动执行probe()函数,此时解析设备树。为了兼容,若设备采用以前方式注册则不必解析设备树。

通过宏定义CONFIG_OF_GPIO实现编译选择。

还以drivers/leds/leds-gpio.c为例示意:

/* Code to create from OpenFirmware platform devices */
#ifdef CONFIG_OF_GPIO
static struct gpio_leds_priv *gpio_leds_create_of(struct platform_device *pdev)
{
    struct device_node *np = pdev->dev.of_node, *child;
    struct gpio_leds_priv *priv;
    int count, ret;

    /* count LEDs in this device, so we know how much to allocate */
    count = of_get_available_child_count(np);
    if (!count)
        return ERR_PTR(-ENODEV);

 for_each_available_child_of_node(np, child)
        if (of_get_gpio(child, 0) == -EPROBE_DEFER)
            return ERR_PTR(-EPROBE_DEFER);

    priv = devm_kzalloc(&pdev->dev, sizeof_gpio_leds_priv(count),
            GFP_KERNEL);
    if (!priv)
        return ERR_PTR(-ENOMEM);
    for_each_available_child_of_node(np, child) {
        struct gpio_led led = {};
        enum of_gpio_flags flags;
        const char *state;

        led.gpio = of_get_gpio_flags(child, 0, &flags);
        led.active_low = flags & OF_GPIO_ACTIVE_LOW;
        led.name = of_get_property(child, "label", NULL) ? : child->name;
        led.default_trigger =
            of_get_property(child, "linux,default-trigger", NULL);
        state = of_get_property(child, "default-state", NULL);
        if (state) {
            if (!strcmp(state, "keep"))
                led.default_state = LEDS_GPIO_DEFSTATE_KEEP;
            else if (!strcmp(state, "on"))
                led.default_state = LEDS_GPIO_DEFSTATE_ON;
            else
                led.default_state = LEDS_GPIO_DEFSTATE_OFF;
        }
        ret = create_gpio_led(&led, &priv->leds[priv->num_leds++],
                      &pdev->dev, NULL);
        if (ret < 0) {
            of_node_put(child);
            goto err;
        }
    }

    return priv;

err:
    for (count = priv->num_leds - 2; count >= 0; count--)
        delete_gpio_led(&priv->leds[count]);
    return ERR_PTR(-ENODEV);
}

static const struct of_device_id of_gpio_leds_match[] = {
    { .compatible = "gpio-leds", },
    {},
};
#else /* CONFIG_OF_GPIO */
static struct gpio_leds_priv *gpio_leds_create_of(struct platform_device *pdev)
{
    return ERR_PTR(-ENODEV);
}
#endif /* CONFIG_OF_GPIO */

再看下probe()函数的实现:

static int gpio_led_probe(struct platform_device *pdev)
{
    struct gpio_led_platform_data *pdata = dev_get_platdata(&pdev->dev);
    struct gpio_leds_priv *priv;
    int i, ret = 0;


    if (pdata && pdata->num_leds) {
        priv = devm_kzalloc(&pdev->dev,
                sizeof_gpio_leds_priv(pdata->num_leds),
                    GFP_KERNEL);
        if (!priv)
            return -ENOMEM;

        priv->num_leds = pdata->num_leds;
        for (i = 0; i < priv->num_leds; i++) {
            ret = create_gpio_led(&pdata->leds[i],
                          &priv->leds[i],
                          &pdev->dev, pdata->gpio_blink_set);

            if (ret < 0) {
                /* On failure: unwind the led creations */
                for (i = i - 1; i >= 0; i--)
                    delete_gpio_led(&priv->leds[i]);
                return ret;
            }
        }
    } else {
        priv = gpio_leds_create_of(pdev);
        if (IS_ERR(priv))
            return PTR_ERR(priv);
    }
    platform_set_drvdata(pdev, priv);

    return 0;
}

设备节点语法

1. 如果一个节点描述的设备有地址,则应该给出@unit-address。多个相同类型设备节点的name可以一样,只要unit-address不同即可。

2. 对于挂在内存空间的设备而言,@字符后面跟的一般就是该设备在内存空间的基地址。

3. 可以给一个设备节点添加label,之后可以通过&label的形式访问这个label,这种引用是通过phandle(pointer handle)进行的。

4. 可寻址的设备使用如下信息在设备树中编码地址信息:

reg   
    #address-cells
    #size-cells

其中reg的组织形式为reg = <address1 length1 [address2 length2] [address3 length3] ... >,其中的每一组address length表明了设备使用的一个地址范围。address为1个或多个32位的整型(即cell),而length的意义则意味着从address到address+length-1的地址范围都是属于该节点。若#size-cells = 0,则length字段为空。

address和length字段是可变长的,父节点的#address-cells和#size-cells分别决定了子节点reg属性的address和length字段的长度。

#address-cells决定了该reg的前address-cells个cells(整型数)为address,代表地址;

#size-cells决定了该reg的代表长度的cells个数,#size-cells代表没有字段代表地址的length。

举例如下:

external-bus结点的#address-cells = <2>和#size-cells = <1>;决定了其下的ethernet、i2c、flash的reg字段形如reg = <0 0 0x1000>;、reg = <1 0 0x1000>;和reg = <2 0 0x4000000>;。其中,address字段长度为0,开始的第一个cell(0、1、2)是对应的片选,第2个cell(0,0,0)是相对该片选的基地址,即reg的前两个cells代表地址;第3个cell(0x1000、0x1000、0x4000000)为length,为一个cells,即reg的第3个cells代表长度

ranges = <0 0  0x10100000   0x10000     // Chipselect 1, Ethernet
    1 0  0x10160000   0x10000     // Chipselect 2, i2c controller  
    2 0  0x30000000   0x1000000>; // Chipselect 3, NOR Flash      

ranges 是地址转换表,其中的每个项目是一个子地址、父地址以及在子地址空间的大小的映射。映射表中的子地址、父地址分别采用子地址空间的#address- cells和父地址空间的#address-cells大小。对于本例而言,子地址空间的#address-cells为2,父地址空间 的#address-cells值为1,因此0 0  0x10100000   0x10000的前2个cell为external-bus后片选0上偏移0,即子地址#adress-cells(为2),第3个cell表示external-bus后片选0上偏移0的地址空间被映射到CPU的0x10100000位置,即父地址#address-cells(为1)第4个cell表示映射的大小为0x10000(为1)

特别要留意的是i2c结点中定义的 #address-cells = <1>;和#size-cells = <0>;作用到了I2C总线上连接的RTC,它的address字段为0x58,是设备的I2C地址,长度字段空。

5. 中断连接。终端相关绑定信息定义在Documentation/devicetree/bindings/interrupt-controller/interrupts.txt。

interrupt-parent: 设备节点通过它来指定它所依附的中断控制器的phandle,当节点没有interrupt-parent时,则从父节点继承。

interrupts: 用到了中断的设备节点,通过它指定中断号/触发方法等,这个属性具体有多少个cell,由它依附的中断控制器节点的#interrupt-cells属性指定。而每个cell具体含义一般由驱动的实现决定,而且也会在设备树的绑定文档中说明。

        i2c@7000c000 {
        gpioext: gpio-adnp@41 {
            compatible = "ad,gpio-adnp";
            reg = <0x41>;

            interrupt-parent = <&gpio>;
            interrupts = <160 1>;

            gpio-controller;
            #gpio-cells = <1>;

            interrupt-controller;
            #interrupt-cells = <2>;

            nr-gpios = <64>;
        };


        sx8634@2b {
            compatible = "smtc,sx8634";
            reg = <0x2b>;

            interrupt-parent = <&gpioext>;
            interrupts = <3 0x8>;

            #address-cells = <1>;
            #size-cells = <0>;

            threshold = <0x40>;
            sensitivity = <7>;
        };
    }; 

 

参考:

http://www.eefocus.com/marianna/blog/cate_18142_0.html?p=2

http://www.eefocus.com/marianna/blog/14-10/306247_821be.html

ARM Linux 3.x的设备树(Device Tree)

设备树概述

设备树实例(一)

设备树实例(二)

内核移植之设备树

我眼中的Linux设备树(五 根节点)

转载于:https://www.cnblogs.com/embedded-linux/p/6654109.html

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/weixin_30920853/article/details/97608201

智能推荐

Cognos11中关于CJAP第三方认证的相关配置-程序员宅基地

文章浏览阅读253次。cognos11同样适用于自定义java程序的第三方认证,而且在测试方面给了直观的测试接口,如下图所示当用户配置好了自定义java程序的认证之后,程序会提示用户输入我们自己的认证库用户信息例如admin 123456,通过认证以后,测显示如下启动服务可以在启动详情里面看到启动的过程中已经创建了新的命名空间说明第三方认证功能第一步已经配置OK了,是否成功还要接下来去cogno..._cjap

女朋友生气了吗?算法比直男更懂她-程序员宅基地

文章浏览阅读973次。By 超神经场景描述:利用 AI 技术判断一个人的情绪通常有两种途径,一种是通过面部表情,另一种是通过语音。前者已经比较成熟,而语音识别情感方面的研究,正在快速发展。近期...

强烈推荐的机器学习,深度学习课程以及python库_机器学习深度学习谁讲得好-程序员宅基地

文章浏览阅读744次。强烈推荐的机器学习,深度学习课程以及python库张冰洋9 个月前本着两条原则推荐一波机器学习,深度学习的课程以及常用的库:1.不建议报辅导班。不是因为我们不应该为学习知识付费, 而是因为有更好的资源,而这些资源恰好免费。报辅导班学习浪费钱倒是次要的,主要是时间有限,所以我们要把最好的时间集中在最高效的事情上。2.视频资源种类繁多,但我只选最有价值的。Are y_机器学习深度学习谁讲得好

python基础代码大全-python基础语法,python 代码命令大全-程序员宅基地

文章浏览阅读2.2k次。python:1.语法强制缩进2.区分大小写:iLoop与iloop是两个变量3.变量无需申明,但是变量赋值前无法使用:a=3合法,b=a+3合法,b=a+c不合法,因为c未赋值前不能使用4.变量类型:数字,字符串等等5.基本语句为赋值语句x=y,数学运算x=y+/-/*//z,比较运算:==,=,等,条件语句ifelse,循环语句for,循环语句while6.内置函数7.类与类方法按这个顺序每步..._python基本代码大全

十字路口交通信号灯PLC(三菱 FX2N-128MT-001)控制系统设计【论文+CAD图纸,梯形总图A0、IO端口分配、PLC外部接线图A1、灯流程图A2、东西时序图A2、南北时序图A2、……】_智能交通灯三菱plc接线图解-程序员宅基地

文章浏览阅读197次。本文总结了交通灯控制技术的发展,讨论了基于PLC的十字路口交通信号灯控制系统的设计可行性。根据PLC的工作原理并结合城市交通的实际状况,本文提出了以三菱公司生产的FX2N-128MT-001型PLC作为基本控制核心,安排了四个方向的直行、左转红黄绿灯,人行道红绿灯以及倒计时数码管的具体配置;随着社会的发展和进步以及人民生活水平的提高,上路的车辆越来越多,但相应的公路设施却没有相应的改善,这就导致了城市交通拥堵问题突出,而且拥堵的地方多是十字路口等车辆汇集处。信号灯无线遥控系统的设计。信号灯控制系统的设计。_智能交通灯三菱plc接线图解

进程间通信之共享内存_进程间的通信共享内存-程序员宅基地

文章浏览阅读1.6k次,点赞29次,收藏28次。1.进程间通信的分类:(1)管道:1、匿名管道pipe;2、命名管道mkfifo(2)System V IPC:1、System V 消息队列;2、System V 共享内存;3、System V 信号量。(3)POSIX IPC:1、消息队列;2、共享内存;3、信号量;4、互斥量;5、条件变量;6、读写锁。前面已经了解了进程间管道通信,那么共享内存又是什么原理?_进程间的通信共享内存

随便推点

fatal: Not a git repository_fatal: not a git repository (or any parent up to m-程序员宅基地

文章浏览阅读8k次。很多git的初学者会在关联一个远程库这里出现错误,当然, 我也是,如果你也遇到了 这个问题,那么看了这篇,你就会解决该问题了。运行命令:git remote add origin [email protected]:yourgithubusername/learngit.git出现下列错误:fatal: Not a git repository (or any of t..._fatal: not a git repository (or any parent up to mount point /home)

?idea离谱的注解问题,Cause: java.sql.SQLSyntaxErrorException: You have an error in your SQL syntax; 的解决方案-程序员宅基地

文章浏览阅读208次。如图如果在编写灵活sql语句时,在中间想要注解的话,不能使用自动注解ctrl+?,系统会告诉你用的符号或字符有错误,我找了一下午愣是没找出哪里写错了,去看前台的控制台,说是我total是空,这个我是赋值为0的怎么可能为空的呢。前端控制台一个错误,后端一个错误,不知道信谁(虽然很多时候前台的错误是在发癫),我也只能去看看前台的问题,结果一脸懵,又重头到尾看了一下午,最后重新打了一遍成功了。原因是后台sql的灵活书写,中间不能使用ctrl+?

c++ 书籍(zz)_effettive c++ more effectivec++ 系列-程序员宅基地

文章浏览阅读1.2k次,点赞34次,收藏5次。From:http://blog.csdn.net/is2120/article/details/44317241下面是转载的在博客上看到的一个C++书籍阅读清单,可以参考下: 阶段 1 《Essential C++》 这是一本内容不多但很实用的C++入门书籍,强调快速上手与理解C++编程。本书主要围绕一系列逐渐复杂的程序问题,以及用以解决这些问题的语言特性展开讲解。你不只学到C++的函数和结构,也会学习到它们的设计目的和基本原理。 《C++ Primer》 本书对C++基本概念、技术、以及现_effettive c++ more effectivec++ 系列

银河麒麟高级服务器操作系统 V10 安装手册_银河麒麟服务器操作系统 v10-程序员宅基地

文章浏览阅读2k次,点赞10次,收藏16次。本手册面向系统管理员及相关技术人员,实现在服务器整机上使用图形化界面快速完成银河麒麟高级服务器操作系统 V10(以下简称“银河麒麟服务器操作系统”)的安装。重要:针对不同 CPU 平台可能有少许差异,本手册尽量加以说明;如未提到之处,以各硬件平台实际安装情况及输出信息为准。在【安装信息摘要】页面配置所有与安装相关的信息,如下图所示:图 5-1 安装信息摘要您可以点击相应配置项的图标进入配置页面,需要注意的是【软件】和【系统】两类配置。安装程序会自动检测各个配置项;_银河麒麟服务器操作系统 v10

maven-基础-程序员宅基地

文章浏览阅读88次。mvn -v查看maven的版本-compile 编译-test 测试clean 删除targetinstall 安装jar包到本地仓库mvn archetype generate 自动创建maven目录groupid 公司名+项目名artifactid 项目名+模块名仓库分为本地仓库和远程仓库&lt;build&gt; &lt;plugins&g...

python编程从入门到实践(第2版)第二章练习题解答_python编程:从入门到实践(第2版)习题答案-程序员宅基地

文章浏览阅读2.9k次。文章目录一、第二章练习题解答练习2-1练习2-2练习2-3练习2-4练习2-5练习2-6练习2-7练习2-8练习2-9练习2-10练习2-11小结一、第二章练习题解答练习2-1**简单消息 将一条消息赋给变量, 并将其打印出来。**msg = "I love learning to use Pythoy"print(msg)练习2-2**多条简单消息 将一条消息赋给变量, 并将其打印出来; 再将变量的值修改为一条新消息, 并将其打印出来。**msg = "I love learning_python编程:从入门到实践(第2版)习题答案

推荐文章

热门文章

相关标签