build_embed_linux_system

I.MX6ULL设备树分析

在理解设备树前,可以参考设备树说明章节进行设备树框架的了解,不过这里进行一些简化的说明。

  1. 设备树是描述系统配置的结构化语言,可以看作定义了一堆变量,以”=”的形式进行赋值,按照树形的结构描述。
  2. 设备树具有覆盖机制,即相同作用域名称的变量,后面赋值会覆盖掉前面的赋值,这是支持嵌套修改的基本规则。
  3. 设备树中的赋值语句格式如下所示。

[key] = [value];

key的叫做键值属性,相当于变量名称,value就是赋值。这里有个隐含的知识点,也是让人迷惑的地方,那就是键值和变量是怎么定义的,如果是自定义节点又是怎么设计的?其实源头上,就是键值属性从使用方法上是分为两大类的,内核相关键值和自定义键值,这里分别进行说明。

内核相关键值,是指和Linux内核中访问设备树的API相关的属性和接口,这部分的实现是必须按照规则定义,也可以称为通用属性,常见的键值如下所示。

clocks : 用于指定管理模块时钟对应的硬件结构(来源手册)
clock-names : 管理模块时钟对应硬件结构的别名(驱动访问)
compatible: 描述设备节点名称的属性,用于驱动匹配
interrupts :中断属性,定义所属的中断控制器,中断线号,中断类型(来源手册)
pinctrl : 用于指定管理模块相关I/O的硬件结构(来源手册)
pinctrl-names : 用于指定管理模块相关I/O硬件结构的别名(驱动访问)
[dev]-gpios : 定义引脚线号和处理方法(来源手册)
reg : 寄存器属性,描述设备对应的寄存器范围(来源手册)
#address_cell : 描述子节点中寄存器属性中,地址位的数量
#size-cell : 描述子节点中寄存器属性中,长度位的数量
ranges :用于地址跨域转换,子节点可以在父节点的基础上访问(对应处理子节点的寄存器属性)
vref-supply : 用于regulator电压管理的节点
status : 设备树中模块的状态。
dmas : 定义模块支持的dma通道和优先级控制
dma-names : 定义模块支持dma的别名 
#pwm-cells : 定义引用pwm节点的pwms属性中,不包含引入pwm的剩余数据数量
pwms : 引用pwm节点,格式pwms=<&pwm1, .....>

上面就是常见的通用属性,也是大部分驱动都具有的属性,通用属性特征为名称和功能固定,需要按照规则定义,访问也有固定的接口。理解了通用属性,其它属性大部分都是驱动的自定义属性,自定义属性的好处的就是定义只要符合设备树的基础规则即可,主要如下。

  1. 键值属性不与通用属性相同,值属性属于设备树定义类型,如空类型,文本字符串,字符串表strlist,无符号整型u32/u64
  2. 键值属性需要驱动中自行解析,常见的解析函数有of_property_read_u32,of_property_read_u32_index,of_property_read_u32_array等接口访问

有了这些基础,下面去理解分析mx6ull的设备树。

章节目录:

adc

//adc接口设备树
regulators {
    compatible = "simple-bus";                          //标签,"simple-bus"表示节点由platform管理
    #address-cells = <1>;                               //定义子节点寄存器占用个数,占用一个
    #size-cells = <0>;                                  //定义子节点寄存器长度占用个数,不存在

    regulator_vref_adc: regulator@1 {
        compatible = "regulator-fixed";                 //标签,用于platform驱动匹配
        regulator-name = "VREF_3V";                     //regulator定义的别名
        regulator-min-microvolt = <3300000>;            //最小电压,fix类型,要求最大最小电压一致
        regulator-max-microvolt = <3300000>;
    };
};

adc1: adc@2198000 {
    compatible = "fsl,imx6ul-adc", "fsl,vf610-adc";     //标签,用于platform驱动匹配
    reg = <0x02198000 0x4000>;                          //寄存器列表,地址0x02198000,长度0x4000
    interrupts = <GIC_SPI 100 IRQ_TYPE_LEVEL_HIGH>;     //定义adc对应的:[中断控制器,中断线号和中断类型]
    clocks = <&clks IMX6UL_CLK_ADC1>;                   //模块的时钟,驱动用于模块时钟使能/失能
    clock-names = "adc";                                //时钟的别名,驱动中访问clocks使用
    fsl,adck-max-frequency = <30000000>, <40000000>,    //定义adc的最大时钟,驱动访问
                    <20000000>;
    status = "disabled";                                //模块状态,关闭
};

&adc1 {
    #io-channel-cells = <1>;                            //子节点访问时,允许输出的iio channel数目,0表示一路,1表示多路
    io-channel-rangs;                                   //继承了当前节点的子节点可以引用当前节点的 IIO channel
    num-channels = <2>;                                 //adc允许的通道数量,驱动访问                           
    pinctrl-0 = <&pinctrl_adc1>;                        //定义ADC对应引脚的pinctrl配置
    pinctrl-name = "default";                           //pinctrl配置的别名,default为内核初始化类型
    vref-supply = <&regulator_vref_adc>;                //基准电压定义,基于regulator获取基准电压
    status = "okay";                                    //模块状态,正常
};

joystick: adc-joystick {
    compatible = "adc-joystick";                        //标签,移动遥感驱动匹配的字符串
    io-channels = <&adc1 0>,                            //遥感对应的adc接口,<对应adc模块 adc模块中注册通道的顺序>
                <&adc1 1>;
    poll-interval = 1000;                               //定义由底层轮询上传,时间间隔1000ms
    #address-cells = <1>;                               //定义字节点reg属性中寄存器位个数
    #size-cells = <0>;                                  //定义字节点reg属性中长度位个数

    axis@0 {
            reg = <0>;                                  //对应上面adc通道编号0
            linux,code = <ABS_X>;
            abs-range = <3300 0>;                       //ABS相对地址的范围
            abs-fuzz = <4>;                             //用于过滤噪声的fuzz值
            abs-flat = <200>;                           //在此值内的值将被事件流丢弃, 然后当作0报告
    };
    axis@1 {
            reg = <1>;                                  //对应上面adc通道编号1
            linux,code = <ABS_Y>;
            abs-range = <0 3300>;                       //ABS相对地址的范围
            abs-fuzz = <4>;                             //用于过滤噪声的fuzz值
            abs-flat = <200>;                           //在此值内的值将被事件流丢弃, 然后当作0报告
    };
};

驱动路径:
adc驱动: drivers/iio/adc/vf610_adc.c
joystick驱动: drivers/inupt/joystick/adc-joystick.c

驱动中应用:
//reg = <0x02198000 0x4000>; 
//获取寄存器,读取实际值
info->regs = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(info->regs))
    return PTR_ERR(info->regs);
hc_cfg = readl(info->regs + VF610_REG_ADC_HC0);

//interrupts = <GIC_SPI 100 IRQ_TYPE_LEVEL_HIGH>;
//读取irq线号,申请irq
irq = platform_get_irq(pdev, 0);
if (irq < 0)
    return irq;
ret = devm_request_irq(info->dev, irq,
            vf610_adc_isr, 0,
            dev_name(&pdev->dev), indio_dev);
if (ret < 0) {
    dev_err(&pdev->dev, "failed requesting irq, irq = %d\n", irq);
    return ret;
}

//clocks = <&clks IMX6UL_CLK_ADC1>;
//clock-names = "adc";
//获取对应时钟,使能模块时钟
info->clk = devm_clk_get(&pdev->dev, "adc");
if (IS_ERR(info->clk)) {
    dev_err(&pdev->dev, "failed getting clock, err = %ld\n",
                    PTR_ERR(info->clk));
    return PTR_ERR(info->clk);
}
ret = clk_prepare_enable(info->clk);

//fsl,adck-max-frequency用法
//fsl,adck-max-frequency = <30000000>, <40000000>, <20000000>;
device_property_read_u32_array(dev, "fsl,adck-max-frequency", info->max_adck_rate, 3);

//num-channels用法
ret  = of_property_read_u32(pdev->dev.of_node, "num-channels", &channels);

//vref-supply用法
//vref-supply = <&regulator_vref_adc>, 使用vref访问vref-supply
info->vref = devm_regulator_get(&pdev->dev, "vref");
if (IS_ERR(info->vref))
    return PTR_ERR(info->vref);
ret = regulator_enable(info->vref);
if (ret)
    return ret;

asrc

// ASRC设备节点
asrc: asrc@2034000 {
    compatible = "fsl,imx6ul-asrc", "fsl,imx53-asrc";           // 兼容属性,表明该设备可以与fsl,imx6ul-asrc和fsl,imx53-asrc驱动兼容
    reg = <0x2034000 0x4000>;                                   // 设备的寄存器地址和大小
    interrupts = <GIC_SPI 50 IRQ_TYPE_LEVEL_HIGH>;              // 中断号和类型
    clocks = <&clks IMX6UL_CLK_ASRC_IPG>,                       // 时钟源,包括ASRC的IPG时钟、内存时钟等
             <&clks IMX6UL_CLK_ASRC_MEM>, <&clks 0>,
             <&clks 0>, <&clks 0>, <&clks 0>, <&clks 0>,
             <&clks 0>, <&clks 0>, <&clks 0>, <&clks 0>,
             <&clks 0>, <&clks 0>, <&clks 0>, <&clks 0>,
             <&clks IMX6UL_CLK_SPDIF>, <&clks 0>, <&clks 0>,
             <&clks IMX6UL_CLK_SPBA>;
    clock-names = "mem", "ipg", "asrck_0",                      // 时钟名称,与clocks对应
                  "asrck_1", "asrck_2", "asrck_3", "asrck_4",
                  "asrck_5", "asrck_6", "asrck_7", "asrck_8",
                  "asrck_9", "asrck_a", "asrck_b", "asrck_c",
                  "asrck_d", "asrck_e", "asrck_f", "spba";
    dmas = <&sdma 17 23 1>, <&sdma 18 23 1>, <&sdma 19 23 1>,   // DMA通道,包括接收和发送通道
           <&sdma 20 23 1>, <&sdma 21 23 1>, <&sdma 22 23 1>;
    dma-names = "rxa", "rxb", "rxc",                            // DMA通道名称,与dmas对应
                "txa", "txb", "txc"; 
    fsl,asrc-rate  = <48000>;                                   // ASRC的采样率,默认为48000Hz                
    fsl,asrc-width = <16>;                                      // ASRC的数据宽度,默认为16位
    status = "okay";                                            // 设备状态,表明设备正常工作
};

驱动路径:
drivers/sound/soc/fsl/fsl_asrc.c

驱动中应用:
//获取时钟资源
asrc->mem_clk = devm_clk_get(&pdev->dev, "mem");
if (IS_ERR(asrc->mem_clk)) {
    dev_err(&pdev->dev, "failed to get mem clock\n");
    return PTR_ERR(asrc->mem_clk);
}

asrc->ipg_clk = devm_clk_get(&pdev->dev, "ipg");
if (IS_ERR(asrc->ipg_clk)) {
    dev_err(&pdev->dev, "failed to get ipg clock\n");
    return PTR_ERR(asrc->ipg_clk);
}

asrc->spba_clk = devm_clk_get(&pdev->dev, "spba");
if (IS_ERR(asrc->spba_clk))
    dev_warn(&pdev->dev, "failed to get spba clock\n");

for (i = 0; i < ASRC_CLK_MAX_NUM; i++) {
    sprintf(tmp, "asrck_%x", i);
    asrc_priv->asrck_clk[i] = devm_clk_get(&pdev->dev, tmp);
    if (IS_ERR(asrc_priv->asrck_clk[i])) {
        dev_err(&pdev->dev, "failed to get %s clock\n", tmp);
        return PTR_ERR(asrc_priv->asrck_clk[i]);
    }
}

//fsl,asrc-rate属性
ret = of_property_read_u32(np, "fsl,asrc-rate",
                &asrc->asrc_rate);
if (ret) {
    dev_err(&pdev->dev, "failed to get output rate\n");
    return ret;
}

ret = of_property_read_u32(np, "fsl,asrc-format", &asrc_fmt);
asrc->asrc_format = (__force snd_pcm_format_t)asrc_fmt;

can

//can接口设备树
gpr: iomuxc-gpr@20e4000 {
    compatible = "fsl,imx6ul-iomuxc-gpr",                       //compatible: 标签,用于platform驱动匹配
                "fsl,imx6q-iomuxc-gpr", "syscon";
    reg = <0x020e4000 0x4000>;                                  //can gpr寄存器列表,起始地址0x020e4000, 长度0x4000
};

can1: can@2090000 {
    compatible = "fsl,imx6ul-flexcan", "fsl,imx6q-flexcan";     //compatible: 标签,用于platform驱动匹配
    reg = <0x02090000 0x4000>;                                  //can1寄存器列表: [起始地址0x02090000, 长度0x4000]
    interrupts = <GIC_SPI 110 IRQ_TYPE_LEVEL_HIGH>;             //can1对应中断,中断控制器,中断线号,中断类型
    clocks = <&clks IMX6UL_CLK_CAN1_IPG>,                       //can1的管理时钟,用于模块电源管理
            <&clks IMX6UL_CLK_CAN1_SERIAL>;                     
    clock-names = "ipg", "per";                                 //can1的时钟别名,用于驱动访问对应时钟
    fsl,stop-mode = <&gpr 0x10 1>;                              //fsl自定义属性,用于停止模式下的管理
    status = "okay";                                            //模块工作状态
};

驱动路径:
can驱动: drivers/net/can/flexcan/flexcan-core.c

驱动中应用:
//reg用法
regs = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(regs))
    return PTR_ERR(regs);

//interrupts
irq = platform_get_irq(pdev, 0);
err = request_irq(dev->irq, flexcan_irq, IRQF_SHARED, dev->name, dev);
if (err)
    goto out_can_rx_offload_disable;

//clock用法
clk_ipg = devm_clk_get(&pdev->dev, "ipg");
if (IS_ERR(clk_ipg)) {
    dev_err(&pdev->dev, "no ipg clock defined\n");
    return PTR_ERR(clk_ipg);
}

clk_per = devm_clk_get(&pdev->dev, "per");
if (IS_ERR(clk_per)) {
    dev_err(&pdev->dev, "no per clock defined\n");
    return PTR_ERR(clk_per);
}
clock_freq = clk_get_rate(clk_per);

//fsl,stop-mode用法
ret = of_property_read_u32_array(np, "fsl,stop-mode", out_val,
                    ARRAY_SIZE(out_val));
phandle = *out_val;
gpr_np = of_find_node_by_phandle(phandle);
priv->stm.gpr = syscon_node_to_regmap(gpr_np);
priv->stm.req_gpr = out_val[1];
priv->stm.req_bit = out_val[2];
regmap_update_bits(priv->stm.gpr, priv->stm.req_gpr,
            1 << priv->stm.req_bit, 0);

crypto

crypto: crypto@2140000 {
    compatible = "fsl,imx6ul-caam", "fsl,sec-v4.0";     //标签,用于设备树匹配的字符串
    #address-cells = <1>;                               //定义子节点寄存器位数
    #size-cells = <1>;                                  //定义子节点长度位数
    reg = <0x2140000 0x3c000>;                          //crypto寄存器范围,<起始地址 地址长度>
    ranges = <0 0x2140000 0x3c000>;                     //定义子节点的转换公式,0表示起始地址0x2140000
    interrupts = <GIC_SPI 48 IRQ_TYPE_LEVEL_HIGH>;      //crypto所属中断,中断线号和中断电平
    clocks = <&clks IMX6UL_CLK_CAAM_IPG>, <&clks IMX6UL_CLK_CAAM_ACLK>, //crypto相关的模块时钟
            <&clks IMX6UL_CLK_CAAM_MEM>;
    clock-names = "ipg", "aclk", "mem";                 //模块时钟别名,用于驱动中访问对应的clocks选项

    sec_jr0: jr@1000 {
        compatible = "fsl,sec-v4.0-job-ring";           //标签,用于设备树匹配的字符串
        reg = <0x1000 0x1000>;                          //sec_jr0寄存器范围,<起始地址 地址长度>
        interrupts = <GIC_SPI 105 IRQ_TYPE_LEVEL_HIGH>; //sec_jr0所属中断,中断线号和中断电平
    };

    sec_jr1: jr@2000 {
        compatible = "fsl,sec-v4.0-job-ring";           //同上
        reg = <0x2000 0x1000>;
        interrupts = <GIC_SPI 106 IRQ_TYPE_LEVEL_HIGH>;
    };

    sec_jr2: jr@3000 {
        compatible = "fsl,sec-v4.0-job-ring";           //同上
        reg = <0x3000 0x1000>;
        interrupts = <GIC_SPI 46 IRQ_TYPE_LEVEL_HIGH>;
    };
};

驱动代码路径:
drivers/crypto/caam/ctrl.c

驱动代码说明:
//获取寄存器属性
ret = of_address_to_resource(np, 0, &res_regs);
if (ret) {
    dev_err(dev, "failed to retrieve registers base from device tree\n");
    of_node_put(np);
    return -ENODEV;
}

ctrlpriv->sm_phy = res_regs.start;
ctrlpriv->sm_base = devm_ioremap_resource(dev, &res_regs);
if (IS_ERR(ctrlpriv->sm_base)) {
    of_node_put(np);
    return PTR_ERR(ctrlpriv->sm_base);
}

csi

&iomuxc {
    //配置复用为gpio功能
    pinctrl_camera_pwrt: camera_pwrtgrp {
        fsl,pins = <
            MX6UL_PAD_GPIO1_IO02__GPIO1_IO02    0x10b0
            MX6UL_PAD_GPIO1_IO04__GPIO1_IO04    0x10b0
        >;
    };

    //配置复用为CSI引脚功能
    pinctrl_csi1: csi1grp {
        fsl,pins = <
            MX6UL_PAD_CSI_MCLK__CSI_MCLK        0x1b088
            MX6UL_PAD_CSI_PIXCLK__CSI_PIXCLK    0x1b088
            MX6UL_PAD_CSI_VSYNC__CSI_VSYNC      0x1b088
            MX6UL_PAD_CSI_HSYNC__CSI_HSYNC      0x1b088
            MX6UL_PAD_CSI_DATA00__CSI_DATA02    0x1b088
            MX6UL_PAD_CSI_DATA01__CSI_DATA03    0x1b088
            MX6UL_PAD_CSI_DATA02__CSI_DATA04    0x1b088
            MX6UL_PAD_CSI_DATA03__CSI_DATA05    0x1b088
            MX6UL_PAD_CSI_DATA04__CSI_DATA06    0x1b088
            MX6UL_PAD_CSI_DATA05__CSI_DATA07    0x1b088
            MX6UL_PAD_CSI_DATA06__CSI_DATA08    0x1b088
            MX6UL_PAD_CSI_DATA07__CSI_DATA09    0x1b088
        >;
    };
    //...
};

csi: csi@21c4000 {
    compatible = "fsl,imx6ul-csi", "fsl,imx7-csi", "fsl,imx6s-csi"; //标签,用于驱动匹配字符串
    reg = <0x021c4000 0x4000>;                                      //csi寄存器:[寄存器地址 寄存器范围]
    interrupts = <GIC_SPI 7 IRQ_TYPE_LEVEL_HIGH>;                   //csi中断:[中断控制器 中断线号 中断状态]
    clocks = <&clks IMX6UL_CLK_DUMMY>,                              //csi时钟说明
        <&clks IMX6UL_CLK_CSI>,
        <&clks IMX6UL_CLK_DUMMY>;
    clock-names = "disp-axi", "csi_mclk", "disp_dcic";              //csi时钟别名,驱动中读取
    status = "disabled";                                            //设备树状态,默认关闭
};

&csi {
    status = "okay";                                                //设备树状态,覆盖开启

    port {                                                          
        csi1_ep: endpoint {
            remote-endpoint = <&ov5640_ep>;                         //连接到其他设备的endpoint(ov5640)
        };
    };
};

&i2c2 {
    //...
    status = "okay";

    ov5640: ov5640@3c {
        compatible = "ovti,ov5640";                                  //标签,用于驱动匹配字符串      
        reg = <0x3c>;                                                //ov5640设备地址,用于i2c访问
        pinctrl-names = "default";                                   //引脚复用别名,默认模式
        pinctrl-0 = <&pinctrl_csi1                                   //引脚复用定义
                    &pinctrl_camera_pwrt>;                          
        clocks = <&clks IMX6UL_CLK_CSI>;                             //CSI模块时钟      
        clock-names = "csi_mclk";                                    //模块时钟别名,用于驱动中访问
        pwn-gpios = <&gpio1 4 1>;                                    //power down引脚线号,驱动中访问
        rst-gpios = <&gpio1 2 0>;                                    //rst引脚线号,驱动中访问
        csi_id = <0>;                                                //csi编号,多csi接口时有效
        mclk = <24000000>;                                           //csi_mclk时钟设置频率
        mclk_source = <0>;                                           //csi_mclk时钟设置来源
        status = "okay";                                             //模块状态,使能

        port {
            ov5640_ep: endpoint {
                remote-endpoint = <&csi1_ep>;                       //连接到其他设备的endpoint(csi)
            };
        };
    };
};

驱动代码路径:
drivers/media/platform/mxc/capture/mx6s_capture.c
drivers/media/platform/mxc/capture/ov5640_v2.c

驱动应用:
//clk访问方法
ov5640_data.sensor_clk = devm_clk_get(dev, "csi_mclk");
if (IS_ERR(ov5640_data.sensor_clk)) {
    dev_err(dev, "get mclk failed\n");
    return PTR_ERR(ov5640_data.sensor_clk);
}

//mclk值读取
retval = of_property_read_u32(dev->of_node, "mclk",
                &ov5640_data.mclk);
if (retval) {
    dev_err(dev, "mclk frequency is invalid\n");
    return retval;
}

//mclk_source值读取
retval = of_property_read_u32(dev->of_node, "mclk_source",
                (u32 *) &(ov5640_data.mclk_source));
if (retval) {
    dev_err(dev, "mclk_source invalid\n");
    return retval;
}

ecspi

//spi接口设备树
ecspi3: spi@2010000 {   
    #address-cells = <1>;                                   //定义"子节点"寄存器个数,占用1个
    #size-cells = <0>;                                      //定义"子节点"寄存器长度占用,表示不存在
    compatible = "fsl,imx6ul-ecspi", "fsl,imx51-ecspi";     //compatible: 标签,用于platform驱动匹配
    reg = <0x02010000 0x4000>;                              //SPI配置寄存器列表
    interrupts = <GIC_SPI 33 IRQ_TYPE_LEVEL_HIGH>;          //定义ecspi对应的中断控制器,中断线号和中断类型
    clocks = <&clks IMX6UL_CLK_ECSPI3>,                     //模块的时钟(主要用于模块电源管理)
            <&clks IMX6UL_CLK_ECSPI3>;
    clock-names = "ipg", "per";                             //时钟的别名,驱动中管理访问
    dmas = <&sdma 7 7 1>, <&sdma 8 7 2>;                    //定义spi对应的dma配置
    dma-names = "rx", "tx";                                 //spi-dma的别名,驱动中管理访问
    status = "disabled";                                    //模块状态,关闭
};

&ecspi3 {
    fsl,spi-num-chipselects = <1>;                          //自定义类型,用于定义spi已选择的器件数量
    pinctrl-0 = <&pinctrl_ecspi3>;                          //定义对应引脚的pinctrl配置
    pinctrl-names = "default";                              //定义引脚配置的别名,驱动访问时需要
    cs-gpios = <&gpio1 20 GPIO_ACTIVE_LOW>;                 //cs-gpios片选引脚,第一个对应后面reg=<0>,依次递增
    status = "okay";                                        //模块状态,关闭

    spidev0:icm20608@0 {
        compatible = "rmk,icm20608";                         //compatible: 标签,用于spi总线匹配
        spi-max-frequency = <8000000>;                       //定义spi的最大工作时钟
        reg = <0>;                                           //定义spi设备的寄存器地址
    };
};

spi驱动路径:
drivers/spi/spi-imx.c(spi本身属于platform总线)

驱动中应用:
//reg访问方法
#define MXC_CSPITXDATA  0x04
spi_imx->base = devm_ioremap_resource(&pdev->dev, res);
unsigned int val = readl(spi_imx->base + MXC_CSPIRXDATA);
writel(val, spi_imx->base + MXC_CSPITXDATA);

//interrupts使用方法
irq = platform_get_irq(pdev, 0);
ret = devm_request_irq(&pdev->dev, irq, spi_imx_isr, 0,
                dev_name(&pdev->dev), spi_imx);

//clock使用方法
spi_imx->clk_ipg = devm_clk_get(&pdev->dev, "ipg");
spi_imx->clk_per = devm_clk_get(&pdev->dev, "per");
ret = clk_prepare_enable(spi_imx->clk_per);
ret = clk_prepare_enable(spi_imx->clk_ipg);

//dma
controller->dma_tx = dma_request_chan(dev, "tx");
controller->dma_rx = dma_request_chan(dev, "rx");
init_completion(&spi_imx->dma_rx_completion);
init_completion(&spi_imx->dma_tx_completion);
controller->can_dma = spi_imx_can_dma;
controller->max_dma_len = MAX_SDMA_BD_BYTES;
spi_imx->controller->flags = SPI_CONTROLLER_MUST_RX |
                    SPI_CONTROLLER_MUST_TX;

//spi-max_frequency最大时钟频率
if (!of_property_read_u32(nc, "spi-max-frequency", &value))
    spi->max_speed_hz = value;

//reg设备地址
rc = of_property_read_u32(nc, "reg", &value);
if (rc) {
    dev_err(&ctlr->dev, "%pOF has no valid 'reg' property (%d)\n",
        nc, rc);
    return rc;
}
spi->chip_select = value;

epadc

epdc: epdc@228c000 {
    compatible = "fsl,imx7d-epdc";                  // 标签,定义驱动兼容性,用于匹配驱动程序
    interrupts = <GIC_SPI 112 IRQ_TYPE_LEVEL_HIGH>; // 定义中断号和中断类型
    reg = <0x228c000 0x4000>;                       // 定义寄存器基地址和范围
    clocks = <&clks IMX6ULL_CLK_EPDC_ACLK>,         // 定义时钟源
            <&clks IMX6ULL_CLK_EPDC_PIX>,
            <&clks IMX6UL_CLK_DUMMY>;
    clock-names = "epdc_axi", "epdc_pix", "epdc_ahb";   // 定义时钟名称
    /* Need to fix epdc-ram */
    /* epdc-ram = <&gpr 0x4 30>; */
    status = "disabled";                            //定义设备状态
};

驱动路径:
drivers/video/fbdev/mxc/mxc_epdc_v2_fb.c

驱动中应用:
//获取设备树中的寄存器资源
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (res == NULL) {
    ret = -ENODEV;
    goto out_cmap;
}

//获取设备树中时钟资源
fb_data->epdc_clk_axi = clk_get(fb_data->dev, "epdc_axi");
if (IS_ERR(fb_data->epdc_clk_axi)) {
    ret = PTR_ERR(fb_data->epdc_clk_axi);
    dev_err(&pdev->dev, "Unable to get EPDC AXI clk."
        "err = %d\n", ret);
    goto out_dma_fb;
}
fb_data->epdc_clk_ahb = clk_get(fb_data->dev, "epdc_ahb");
if (IS_ERR(fb_data->epdc_clk_ahb)) {
    ret = PTR_ERR(fb_data->epdc_clk_ahb);
    dev_err(&pdev->dev, "Unable to get EPDC ahb clk."
        "err = %d\n", ret);
    goto out_dma_fb;
}
fb_data->epdc_clk_pix = clk_get(fb_data->dev, "epdc_pix");
if (IS_ERR(fb_data->epdc_clk_pix)) {
    ret = PTR_ERR(fb_data->epdc_clk_pix);
    dev_err(&pdev->dev, "Unable to get EPDC pix clk."
        "err = %d\n", ret);
    goto out_dma_fb;
}

fec

fec2: ethernet@20b4000 {
    compatible = "fsl,imx6ul-fec", "fsl,imx6q-fec";     //标签,用于设备树匹配的字符串
    reg = <0x020b4000 0x4000>;                          //fec寄存器范围,<起始地址 地址长度>
    interrupt-names = "int0", "pps";                    //fec中断别名,驱动中调用
    interrupts = <GIC_SPI 120 IRQ_TYPE_LEVEL_HIGH>,     //fec所属中断,中断线号和中断电平
                <GIC_SPI 121 IRQ_TYPE_LEVEL_HIGH>;
    clocks = <&clks IMX6UL_CLK_ENET>,                   //fec相关的模块时钟
            <&clks IMX6UL_CLK_ENET_AHB>,
            <&clks IMX6UL_CLK_ENET_PTP>,
            <&clks IMX6UL_CLK_ENET2_REF_125M>,
            <&clks IMX6UL_CLK_ENET2_REF_125M>;
    clock-names = "ipg", "ahb", "ptp",                  //模块时钟别名,用于驱动中访问对应的clocks选项
                "enet_clk_ref", "enet_out";
    fsl,num-tx-queues = <1>;                            //tx发送队列数,不能超过3
    fsl,num-rx-queues = <1>;                            //rx接收队列数,不能超过3
    fsl,stop-mode = <&gpr 0x10 4>;                      //指定休眠模式下的处理
    fsl,magic-packet;                                   //支持远程唤醒数据包
    fsl,wakeup_irq = <0>;                               //唤醒中断
    status = "disabled";                                //模块状态,关闭
};

&fec2 {
    pinctrl-names = "default";                          //引脚复用声明,指定默认状态
    pinctrl-0 = <&pinctrl_enet2                         //引脚复用功能定义,网口和复位引脚
                &pinctrl_fec2_reset>;
    phy-mode = "rmii";                                  //phy工作模式,rmii
    phy-handle = <&ethphy1>;                            //phy指定引用的物理配置
    phy-reset-gpios = <&gpio5 8 GPIO_ACTIVE_LOW>;       //phy复位引脚声明
    phy-reset-duration = <26>;                          //phy复位延时
    status = "okay";                                    //模块状态,开启

    mdio {
        #address-cells = <1>;                           //定义子节点寄存器位数
        #size-cells = <0>;                              //定义子节点长度位数

        ethphy0: ethernet-phy@0 {
            compatible = "ethernet-phy-id0022.1560";    //外部phy型号声明
            reg = <0>;                                  //phy地址声明,根据硬件确定地址
            micrel,led-mode = <1>;                      //phy的led工作模式
            clocks = <&clks IMX6UL_CLK_ENET_REF>;       //phy工作时钟
            clock-names = "rmii-ref";                   //phy工作时钟别名,应用中访问

        };

        ethphy1: ethernet-phy@1 {
            compatible = "ethernet-phy-id0022.1560";    //同上
            reg = <1>;
            micrel,led-mode = <1>;
            clocks = <&clks IMX6UL_CLK_ENET2_REF>;
            clock-names = "rmii-ref";
        };
    };
};

驱动代码路径:
drivers/net/ethernet/freescale/fec_main.c
drivers/net/phy/micrel.c

驱动中实现:
//获取tx, rx队列的值
of_property_read_u32(np, "fsl,num-tx-queues", num_tx);
of_property_read_u32(np, "fsl,num-rx-queues", num_rx);

gpmi

gpmi: nand-controller@1806000 {
    compatible = "fsl,imx6q-gpmi-nand";             // 兼容字符串,用于匹配驱动程序
    #address-cells = <1>;                           // 地址单元数量
    #size-cells = <1>;                              // 大小单元数量
    reg = <0x01806000 0x2000>, <0x01808000 0x2000>; // NAND控制器寄存器范围
    reg-names = "gpmi-nand", "bch";                 // 寄存器名称
    interrupts = <0 15 IRQ_TYPE_LEVEL_HIGH>;        // 中断信息
    interrupt-names = "bch";                        // 中断名称
    clocks = <&clks IMX6UL_CLK_GPMI_IO>,            // 时钟信息
            <&clks IMX6UL_CLK_GPMI_IO_APB>,
            <&clks IMX6UL_CLK_GPMI_APB>,
            <&clks IMX6UL_CLK_GPMI_BCH>,
            <&clks IMX6UL_CLK_GPMI_BCH_APB>,
            <&clks IMX6UL_CLK_PER_BCH>;
    clock-names = "gpmi_io", "gpmi_apb", "gpmi_bch",// 时钟名称
                "gpmi_bch_apb", "per1_bch";
    dmas = <&dma_apbh 0>;                           // DMA通道
    dma-names = "rx-tx";                            // DMA通道名称  
    status = "disabled";                            // 模块状态,关闭
};

&iomuxc {
    pinctrl_gpmi_nand_1: gpmi-nand-1 {
        fsl,pins = <
            MX6UL_PAD_NAND_CLE__RAWNAND_CLE         0xb0b1
            //...
            MX6UL_PAD_NAND_DATA07__RAWNAND_DATA07   0xb0b1
        >;
    };
};

&gpmi {
    pinctrl-names = "default";                      // 引脚复用声明,指定默认状态
    pinctrl-0 = <&pinctrl_gpmi_nand_1>;             // 引脚复用功能定义
    status = "okay";                                // 模块状态,开启
    nand-on-flash-bbt;                              // 定义nand-on-flash-bbt属性
};

驱动代码路径:
drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c

驱动中应用:
//获取中断资源,申请中断
err = platform_get_irq_byname(pdev, res_name);
if (err < 0)
    return err;

err = devm_request_irq(this->dev, err, irq_h, 0, res_name, this);
if (err)
    dev_err(this->dev, "error requesting BCH IRQ\n");

//申请dma资源
dma_chan = dma_request_chan(&pdev->dev, "rx-tx");
if (IS_ERR(dma_chan)) {
    ret = dev_err_probe(this->dev, PTR_ERR(dma_chan),
                "DMA channel request failed\n");
    release_dma_channels(this);
} else {
    this->dma_chans[0] = dma_chan;
}

gpio

//gpio接口设备树
&iomuxc {
    pinctrl_gpio_led: gpio-led {
        fsl,pins = <
            MX6UL_PAD_GPIO1_IO03__GPIO1_IO03    0x17059             //定义引脚配置0x17059
        >;
    };

    pinctrl_led_improve: led-improve {
        fsl,pins = <
            MX6UL_PAD_GPIO1_IO03__GPIO1_IO03    0x40017059          //定义引脚配置0x40017059
        >;
    };
}

gpiosgrp {
    compatible = "simple-bus";                  //定义为"simple-bus", "simple-mfd", "isa", "arm,amba-bus"的对象,其子节点也属于platform管理
    #address-cells = <1>;                       //定义"子节点"寄存器占用,1字节
    #size-cells = <1>;                          //定义子节点寄存器长度占用,1字节
    ranges;                                     //地址域转换,表示子节点继承父节点地址域

    usr_led {
        compatible = "rmk,usr-led";             //标签,用于驱动匹配
        pinctrl-0 = <&pinctrl_gpio_led>;        //定义对应gpio引脚的pinctrl配置第0组
        pinctrl-1 = <&pinctrl_led_improve>;     //定义对应gpio引脚的pinctrl配置第1组
        pinctrl-names = "default", "improve";   //定义对应gpio引脚的pinctrl配置别名,用于驱动选择
        led-gpios = <&gpio1 3 GPIO_ACTIVE_LOW>; //定义I/O引脚对应的GPIO
        reg = <0x020c406c 0x04>,                //定义I/O引脚对应的寄存器
            <0x020e0068 0x04>,  
            <0x020e02f4 0x04>,
            <0x0209c000 0x04>,
            <0x0209c004 0x04>;
        status = "okay";                        //定义模块状态
    };
};

驱动对应:自定义驱动,gpio访问

驱动应用:
//pinctrl子系统支持修改
chip->led_pinctrl = devm_pinctrl_get(&pdev->dev);
chip->pinctrl_state[0] = pinctrl_lookup_state(chip->led_pinctrl, "default");
chip->pinctrl_state[1] = pinctrl_lookup_state(chip->led_pinctrl, "improve");
pinctrl_select_state(chip->led_pinctrl, chip->pinctrl_state[0]);

//led-gpios读取和访问方法
chip->led_desc = devm_gpiod_get(&pdev->dev, "led", GPIOD_OUT_LOW);
gpiod_direction_output(chip->led_desc, chip->status);
gpiod_set_value(chip->led_desc, 0);

//reg读取和访问方法
chip->io_reg = of_iomap(led_nd, 2); //读取寄存器列表中的第三组寄存器
u32 regval = readl(chip->io_reg);

i2c

&iomuxc {
    pinctrl_ap3216_tsc: gpio-ap3216 {
        fsl,pins = <
            MX6UL_PAD_GPIO1_IO01__GPIO1_IO01    0x40017059      //定义引脚配置
        >;
    };
}

i2c1: i2c@21a0000 {
    #address-cells = <1>;                               //定义"子节点"寄存器占用,1位
    #size-cells = <0>;                                  //定义"子节点"寄存器长度占用,表示不存在
    compatible = "fsl,imx6ul-i2c", "fsl,imx21-i2c";     //compatible: 标签,用于platform驱动匹配
    reg = <0x021a0000 0x4000>;                          //寄存器列表,用于访问配置<基地址 长度范围>
    interrupts = <GIC_SPI 36 IRQ_TYPE_LEVEL_HIGH>;      //定义i2c1对应的中断控制器,中断线号和中断类型
    clocks = <&clks IMX6UL_CLK_I2C1>;                   //定义i2c1的时钟管理模块
    status = "disabled";                                //模块状态,会被后续覆盖
};

&i2c1 {
    clock-frequency = <100000>;                         //定义i2c的工作时钟
    pinctrl-0 = <&pinctrl_i2c1>;                        //定义对应i2c通讯引脚的pinctrl配置
    pinctrl-names = "default";                          //定义i2c通讯引脚配置的别名,驱动访问时需要
    status = "okay";                                    //模块状态

    ap3216@1e {
        compatible = "rmk,ap3216";                      //compatible: 标签,用于spi总线匹配
        reg = <0x1e>;                                   //reg: i2c的寄存器地址, 总线加载器件时获取
        rmk,sysconf = <0x03>;                           //rmk,sysconf: 自定义属性,用于定义配置寄存器的初始值
        pinctrl-0 = <&pinctrl_ap3216_tsc>;              //定义器件的中断I/O引脚配置
        pinctrl-names = "default";                      //定义器件的中断I/O引脚配置别名
        interrupt-parent = <&gpio1>;                    //定义器件的中断I/O对应得中断控制器
        interrupts = <1 IRQ_TYPE_EDGE_FALLING>;         //定义I/O对应的中断线号和中断类型
        int-gpios = <&gpio1 1 GPIO_ACTIVE_LOW>;         //定义I/O引脚对应的GPIO
    };
};

驱动路径:
drivers/i2c/busses/i2c-imx.c(spi本身属于platform总线)

驱动中应用:
//i2c1
//reg
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
base = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(base))
    return PTR_ERR(base);

//interrupt
irq = platform_get_irq(pdev, 0);
if (irq < 0) {
    dev_err(&pdev->dev, "can't get irq number\n");
    return irq;
}

//clock
i2c_imx->clk = devm_clk_get(&pdev->dev, NULL);
if (IS_ERR(i2c_imx->clk)) {
    if (PTR_ERR(i2c_imx->clk) != -EPROBE_DEFER)
        dev_err(&pdev->dev, "can't get I2C clock\n");
    return PTR_ERR(i2c_imx->clk);
}
ret = clk_prepare_enable(i2c_imx->clk);
if (ret) {
    dev_err(&pdev->dev, "can't enable I2C clock, ret=%d\n", ret);
    return ret;
}

//clock-frequency
ret = of_property_read_u32(pdev->dev.of_node,
                "clock-frequency", &i2c_imx->bitrate);
if (ret < 0 && pdata && pdata->bitrate)
    i2c_imx->bitrate = pdata->bitrate;

//ap3216
//rmk,sysconf
result = of_property_read_u32(np, "rmk,sysconf", &chip->sysconf);
if(result)
    chip->sysconf = 0x03;

//int-gpios
chip->int_desc = devm_gpiod_get(&client->dev, "int", GPIOD_IN);
if (chip->int_desc == NULL)
{
    dev_err(&client->dev, "gpio get failed!\n");
    return -EIO;
}

//interrupt
chip->irq = gpiod_to_irq(chip->int_desc);
ret = devm_request_threaded_irq(&client->dev, client->irq, 
                        NULL, irq_handler, 
                        IRQF_SHARED | IRQF_ONESHOT | IRQF_TRIGGER_FALLING, 
                        "ap3216_int", 
                        (void *)chip);

lcdif

lcdif: lcdif@21c8000 {
    compatible = "fsl,imx6ul-lcdif", "fsl,imx28-lcdif"; //标签,用于设备树匹配
    reg = <0x021c8000 0x4000>;                          //lcdif寄存器范围,<起始地址 地址长度>
    interrupts = <GIC_SPI 5 IRQ_TYPE_LEVEL_HIGH>;       //lcd所属中断,中断线号和中断电平
    clocks = <&clks IMX6UL_CLK_LCDIF_PIX>,              //lcd相关的模块时钟
            <&clks IMX6UL_CLK_LCDIF_APB>,
            <&clks IMX6UL_CLK_DUMMY>;
    clock-names = "pix", "axi", "disp_axi";             //模块时钟别名,用于驱动中访问对应的clocks选项
    status = "disabled";                                //模块状态,关闭
};

&lcdif {
    assigned-clocks = <&clks IMX6UL_CLK_LCDIF_PRE_SEL>;
    assigned-clock-parents = <&clks IMX6UL_CLK_PLL5_VIDEO_DIV>;
    pinctrl-names = "default";                          //pinctrl别名,default表示默认的复用
    pinctrl-0 = <&pinctrl_lcdif_dat                     //pinctrl复用定义,指定数据引脚和功能引脚
                &pinctrl_lcdif_ctrl>;
    display = <&display0>;                              //图形界面信息              
    status = "okay";                                    //模块状态,开启

    display0: display@0 {                               //界面显示信息
        bits-per-pixel = <24>;                          //每个像素点的bit数目,24bit
        bus-width = <24>;                               //总线宽度,24bit(RGB888模式)

        display-timings {                               //显示的时序
            native-mode = <&timing0>;
            
            timing0: timing0 {
                clock-frequency = <33000000>;           //时钟频率
                hactive = <800>;                        //水平分辨率,也就是屏分辨率中的宽
                vactive = <480>;                        //垂直分辨率,也就是屏分辨率中的高
                hfront-porch = <40>;                    //水平前沿周期,在行显示范围后的clk脉冲,对应屏幕的HFP
                hback-porch = <88>;                     //水平后沿周期,在行显示范围前的clk脉冲,对应屏幕的HBP
                hsync-len = <48>;                       //水平同步宽度,用于行同步的clk脉冲,对应屏幕的HSYNC
                vback-porch = <32>;                     //垂直后沿周期,在帧范围前的clk脉冲,对应屏幕的HBP
                vfront-porch = <13>;                    //垂直前沿周期,在帧显示范围后的clk脉冲,对应屏幕的HFP
                vsync-len = <3>;                        //水平同步宽度,用于帧同步的clk脉冲,对应屏幕的VSYNC

                hsync-active = <0>;                     //hsync行同步极性,0表示低电平有效,1表示高电平有效
                vsync-active = <0>;                     //vsync帧同步极性,0表示低电平有效,1表示高电平有效
                de-active = <1>;                        //数据使能极性,0表示低电平有效,1表示高电平有效
                pixelclk-active = <0>;                  //时钟有效极性,0表示低电平有效,1表示高电平有效
            };
        };
    };
};

驱动代码路径:
drivers/video/fbdev/mxsfb.c

驱动应用:
//获取寄存器reg属性
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res) {
    dev_err(&pdev->dev, "Cannot get memory IO resource\n");
    return -ENODEV;
}

//获取pix时钟资源
host->clk_pix = devm_clk_get(&host->pdev->dev, "pix");

//获取axi资源
host->clk_axi = devm_clk_get(&host->pdev->dev, "axi");

//请求中断,绑定中断回调
int irq = platform_get_irq(pdev, 0);
ret = devm_request_irq(&pdev->dev, irq, mxsfb_irq_handler, 0,
            dev_name(&pdev->dev), host);

关于屏幕配置显示如下所示。

image

ocotp

ocotp: efuse@21bc000 {
    #address-cells = <1>;                       //定义子节点中寄存器的位数
    #size-cells = <1>;                          //定义字节点中寄存器长度的位数
    compatible = "fsl,imx6ul-ocotp", "syscon";  //标签,用于设备树匹配的字符串
    reg = <0x021bc000 0x4000>;                  //ocotp寄存器范围,<起始地址 地址长度>
    clocks = <&clks IMX6UL_CLK_OCOTP>;          //ocotp相关的时钟

    tempmon_calib: calib@38 {                   //校准数据
        reg = <0x38 4>;
    };

    tempmon_temp_grade: temp-grade@20 {         //温度监控分级数据
        reg = <0x20 4>;
    };

    cpu_speed_grade: speed-grade@10 {            //cpu频率数据
        reg = <0x10 4>;
    };
};

驱动代码路径:
drivers/nvmem/imx-ocotp.c

生成文件目录:
/sys/bus/platform/devices/21bc000.efuse/imx-ocotp0

驱动应用:
//读取reg属性地址
priv->base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(priv->base))
    return PTR_ERR(priv->base);

//读取时钟资源
priv->clk = devm_clk_get(dev, NULL);
if (IS_ERR(priv->clk))
    return PTR_ERR(priv->clk);

pwm

//pwm接口设备树
pwm1: pwm@2080000 {
    compatible = "fsl,imx6ul-pwm", "fsl,imx27-pwm";     //标签,用于platform驱动匹配
    reg = <0x02080000 0x4000>;                          //寄存器列表,用于访问配置
    interrupts = <GIC_SPI 83 IRQ_TYPE_LEVEL_HIGH>;      //定义pwm对应的中断控制器,中断线号和中断类型
    clocks = <&clks IMX6UL_CLK_PWM1>,                   //模块的时钟(主要用于模块电源管理)
            <&clks IMX6UL_CLK_PWM1>;
    clock-names = "ipg", "per";                         //时钟的别名,驱动中管理访问
    #pwm-cells = <3>;                                   //描述引用pwms单元的子节点后,数据的数量(不包含引用设备本身) pwms=<&pwm1, 0, 255> => 2
    status = "disabled";                                //模块状态
};

&pwm1 {
    #pwm-cells = <2>;                                  //描述引用pwm1单元的子节点后,内部数据的数量,参考backlight-display中的pwm属性
    pinctrl-0 = <&pinctrl_pwm1>;                       //定义对应引脚的pinctrl配置
    pinctrl-names = "default";                         //pinctrl配置的别名,驱动中管理访问
    status = "okay";                                   //模块状态
};

backlight-display {
    compatible = "pwm-backlight";                       //标签,用于platform驱动匹配
    pwms = <&pwm1 0 5000000>;                           //表示使用pwm1接口,表示pwm1的通道0索引,500000表示周期
    brightness-levels = <0 4 8 16 32 64 128 255>;       //自定义属性,表示PWM支持的占空比
    default-brightness-level = <6>;                     //自定义属性,表示默认选择PWM支持的占空比(列表见brightness-levels)
    status = "okay";                                    //模块状态
};

驱动代码路径:
drivers/pwm/pwm-imx27.c(pwm属于platform总线)
drivers/video/backlight/pwm_bl.c

//reg使用方法
imx->mmio_base = devm_platform_ioremap_resource(pdev, 0);
pwmcr = readl(imx->mmio_base + MX3_PWMCR)

//#pwm-cells使用方法
if (of_property_read_u32(chip->dev->of_node, "#pwm-cells",
            &pwm_cells))
    pwm_cells = 2;

//clocks/clock-names使用方法
imx->clk_ipg = devm_clk_get(&pdev->dev, "ipg");
if (IS_ERR(imx->clk_ipg))
    return dev_err_probe(&pdev->dev, PTR_ERR(imx->clk_ipg),
                    "getting ipg clock failed\n");

imx->clk_per = devm_clk_get(&pdev->dev, "per");
if (IS_ERR(imx->clk_per))
    return dev_err_probe(&pdev->dev, PTR_ERR(imx->clk_per),
                    "failed to get peripheral clock\n");

//pwms使用方法
pb->pwm = devm_pwm_get(&pdev->dev, NULL);

//brightness-levels, default-brightness-level使用方法
struct property *prop;
int length;
unsigned int num_levels;
prop = of_find_property(node, "brightness-levels", &length);
num_levels = length / sizeof(u32);
if (num_levels > 0) {
    ret = of_property_read_u32_array(node, "brightness-levels",
        data->levels,
        num_levels);

    ret = of_property_read_u32(node, "default-brightness-level",
                &value);
}

rtc

//rtc接口设备树
snvs: snvs@20cc000 {
    compatible = "fsl,sec-v4.0-mon", "syscon", "simple-mfd"; //标签,定义为"simple-bus", "simple-mfd", "isa", "arm,amba-bus"的对象,其子节点也属于platform管理
    reg = <0x020cc000 0x4000>;                               //寄存器列表,用于rtc访问

    snvs_rtc: snvs-rtc-lp {
        compatible = "fsl,sec-v4.0-mon-rtc-lp";             //compatible: 标签,用于platform驱动匹配
        regmap = <&snvs>;                                   //regmap: 定义regmap使用的节点内的reg属性
        offset = <0x34>;                                    //offset: 定义设备节点的起始偏移值
        interrupts = <GIC_SPI 19 IRQ_TYPE_LEVEL_HIGH>,      //定义rtc中断对应的中断控制器,中断线号,状态触发条件
                    <GIC_SPI 20 IRQ_TYPE_LEVEL_HIGH>;
    };
    //...
}

驱动代码路径:drivers/rtc/rtc-snvs.c

属性解析:
//regmap,offset获取方法
#define SNVS_LPPGDR         0x30
#define SNVS_LPPGDR_INIT    0x41736166
data->regmap = syscon_regmap_lookup_by_phandle(pdev->dev.of_node, "regmap");
of_property_read_u32(pdev->dev.of_node, "offset", &data->offset);
regmap_write(data->regmap, data->offset + SNVS_LPPGDR, SNVS_LPPGDR_INIT);

//interrupts
data->irq = platform_get_irq(pdev, 0);
if (data->irq < 0)
    return data->irq;
ret = devm_request_irq(&pdev->dev, data->irq, snvs_rtc_irq_handler, IRQF_SHARED, "rtc alarm", &pdev->dev);

sim

sim卡接口设备树。

//sim设备树
sim2: sim@021b4000 {
    compatible = "fsl,imx6ul-sim";                          //标签,用于platform驱动匹配
    reg = <0x021b4000 0x4000>;                              //寄存器列表,用于驱动访问
    interrupts = <GIC_SPI 113 IRQ_TYPE_LEVEL_HIGH>;         //sim对应的中断控制器,中断线号和中断触发条件
    clocks = <&clks IMX6UL_CLK_SIM2>;                       //sim对应的管理时钟
    clock-names = "sim";                                    //sim管理时钟的别名
    status = "disabled";                                    //sim模块状态
};

&sim2 {
    pinctrl-0 = <&pinctrl_sim2>;                            //定义对应引脚的pinctrl配置
    pinctrl-names = "default";                              //pinctrl配置的别名,驱动中管理访问
    assigned-clocks = <&clks IMX6UL_CLK_SIM_SEL>;           //sim卡初始化时需要处理的时钟
    assigned-clock-parents = <&clks IMX6UL_CLK_SIM_PODF>;   //sim卡初始化时需要处理的时钟父节点
    assigned-clock-rates = <240000000>;                     //sim卡初始化时配置的时钟频率
    /* GPIO_ACTIVE_HIGH/LOW:sim card voltage control
        * NCN8025:Vcc = ACTIVE_HIGH?5V:3V
        * TDA8035:Vcc = ACTIVE_HIGH?5V:1.8V
        */
    pinctrl-assert-gpios = <&gpio4 23 GPIO_ACTIVE_HIGH>;    //sim模块时钟控制引脚
    port = <1>;                                             //自定义属性,定义使用sim的端口(SIM模块支持两个端口)
    sven_low_active;                                        //自定义属性,定义是否支持低功耗模式
    status = "okay";                                        //sim模块状态
};


驱动代码路径:drivers/mxc/sim/imx_sim.c

驱动应用:
//reg属性
sim->res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!sim->res) {
    pr_err("Can't get the MEMORY\n");
    return -ENOMEM;
}
sim->ioaddr = devm_ioremap_resource(&pdev->dev, sim->res);
dev_dbg(&pdev->dev, "mapped base address: 0x%08x\n", (u32)sim->ioaddr);
if (IS_ERR(sim->ioaddr)) {
    dev_err(&pdev->dev,
        "failed to get ioremap base\n");
    ret = PTR_ERR(sim->ioaddr);
    return ret;
}
u32 reg_val;
reg_val = __raw_readl(sim->ioaddr + CNTL);

//interrupts
sim->ipb_irq = platform_get_irq(pdev, 0);
if (sim->ipb_irq < 0) {
    dev_err(&pdev->dev, "No ipb irq line provided\n");
    return -ENOENT;
}
if (devm_request_irq(&pdev->dev, sim->ipb_irq, sim_irq_handler,
            0, "mxc_sim_ipb", sim)) {
    dev_err(&pdev->dev, "can't claim irq %d\n", sim->ipb_irq);
    return -ENOENT;
}

//clocks
/* request the sim clk and sim_serial_clk */
sim->clk = devm_clk_get(&pdev->dev, "sim");
if (IS_ERR(sim->clk)) {
    ret = PTR_ERR(sim->clk);
    pr_err("Get CLK ERROR !\n");
    return ret;
}

//port
ret = of_property_read_u32(of_node, "port", &sim->port_index);
if (ret)
    sim->port_index = 0;
sim->port_ctrl_reg = (sim->port_index == 0) ?
            PORT0_CNTL : PORT1_CNTL;
sim->port_detect_reg = (sim->port_index == 0) ?
            PORT0_DETECT : PORT1_DETECT;

//sven_low_active
sim->sven_low_active = of_property_read_bool(of_node,
                    "sven_low_active");

tsc

tsc: tsc@2040000 {
    compatible = "fsl,imx6ul-tsc";                  // 标签,用于驱动匹配
    reg = <0x02040000 0x4000>, <0x0219c000 0x4000>; // 寄存器列表,用于驱动访问
    interrupts = <GIC_SPI 3 IRQ_TYPE_LEVEL_HIGH>,   // 中断控制器,中断线号和中断触发条件
                <GIC_SPI 101 IRQ_TYPE_LEVEL_HIGH>;
    clocks = <&clks IMX6UL_CLK_IPG>,                // 时钟列表,用于驱动访问
            <&clks IMX6UL_CLK_ADC2>;
    clock-names = "tsc", "adc";                     // 时钟别名,用于驱动访问
    status = "disabled";                            // 设备状态,禁用
};

pinctrl_tsc: tscgrp {
    fsl,pins = <
        MX6UL_PAD_GPIO1_IO01__GPIO1_IO01        0xb0    // 定义了GPIO引脚的复用设置
        MX6UL_PAD_GPIO1_IO02__GPIO1_IO02        0xb0
        MX6UL_PAD_GPIO1_IO03__GPIO1_IO03        0xb0
        MX6UL_PAD_GPIO1_IO04__GPIO1_IO04        0xb0
    >;
};

&tsc {
    pinctrl-names = "default";              // 定义引脚复用的别名
    pinctrl-0 = <&pinctrl_tsc>;             // 指定了默认状态下使用的引脚复用设置
    xnur-gpio = <&gpio1 3 GPIO_ACTIVE_LOW>; // 定义了一个GPIO引脚,用于控制TSC模块的XNUR信号
    measure-delay-time = <0xffff>;          // 定义了测量延迟时间
    pre-charge-time = <0xfff>;              // 定义了预充电时间
    status = "disabled";                    // 设备状态,禁用
};

驱动对应:drivers/input/touchscreen/imx6ul_tsc.c

驱动应用:
// 获取gpio处理引脚
tsc->xnur_gpio = devm_gpiod_get(&pdev->dev, "xnur", GPIOD_IN);
if (IS_ERR(tsc->xnur_gpio)) {
    err = PTR_ERR(tsc->xnur_gpio);
    dev_err(&pdev->dev,
        "failed to request GPIO tsc_X- (xnur): %d\n", err);
    return err;
}

// 获取tsc处理寄存器
tsc->tsc_regs = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(tsc->tsc_regs)) {
    err = PTR_ERR(tsc->tsc_regs);
    dev_err(&pdev->dev, "failed to remap tsc memory: %d\n", err);
    return err;
}

// 获取adc检测寄存器
tsc->adc_regs = devm_platform_ioremap_resource(pdev, 1);
if (IS_ERR(tsc->adc_regs)) {
    err = PTR_ERR(tsc->adc_regs);
    dev_err(&pdev->dev, "failed to remap adc memory: %d\n", err);
    return err;
}

// 获取时钟资源
tsc->tsc_clk = devm_clk_get(&pdev->dev, "tsc");
if (IS_ERR(tsc->tsc_clk)) {
    err = PTR_ERR(tsc->tsc_clk);
    dev_err(&pdev->dev, "failed getting tsc clock: %d\n", err);
    return err;
}

tsc->adc_clk = devm_clk_get(&pdev->dev, "adc");
if (IS_ERR(tsc->adc_clk)) {
    err = PTR_ERR(tsc->adc_clk);
    dev_err(&pdev->dev, "failed getting adc clock: %d\n", err);
    return err;
}

// 获取中断资源
tsc_irq = platform_get_irq(pdev, 0);
if (tsc_irq < 0)
    return tsc_irq;

adc_irq = platform_get_irq(pdev, 1);
if (adc_irq < 0)
    return adc_irq;

uart

uart接口设备树。

//uart设备树接口
uart1: serial@2020000 {
    compatible = "fsl,imx6ul-uart",                 //标签,用于platform驱动匹配
                "fsl,imx6q-uart";
    reg = <0x02020000 0x4000>;                      //用于访问uart硬件配置寄存器
    interrupts = <GIC_SPI 26 IRQ_TYPE_LEVEL_HIGH>;  //uart的中断控制器,中断线号和中断触发条件
    clocks = <&clks IMX6UL_CLK_UART1_IPG>,          //uart的控制时钟
            <&clks IMX6UL_CLK_UART1_SERIAL>;
    clock-names = "ipg", "per";                     //控制时钟别名,用于驱动访问
    status = "disabled";                            //模块状态
};

&uart1 {
    pinctrl-0 = <&pinctrl_uart1>;                   //定义对应引脚的pinctrl配置
    pinctrl-names = "default";                      //pinctrl配置的别名,驱动中管理访问   
    status = "okay";                                //模块状态
};

驱动对应:drivers/tty/serial/imx.c

驱动应用:
//reg处理方法
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
base = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(base))
    return PTR_ERR(base);

//irq处理方法
rxirq = platform_get_irq(pdev, 0);
if (rxirq < 0)
    return rxirq;
txirq = platform_get_irq_optional(pdev, 1);
rtsirq = platform_get_irq_optional(pdev, 2);

//clk处理方法
sport->clk_ipg = devm_clk_get(&pdev->dev, "ipg");
sport->clk_per = devm_clk_get(&pdev->dev, "per");
sport->port.uartclk = clk_get_rate(sport->clk_per);
/* For register access, we only need to enable the ipg clock. */
ret = clk_prepare_enable(sport->clk_ipg);

usdhc

emmc/sdcard接口设备树。

usdhc2: mmc@2194000 {
    compatible = "fsl,imx6ul-usdhc", "fsl,imx6sx-usdhc";    //标签,用于platform驱动匹配
    reg = <0x02194000 0x4000>;                              //用于操作usdhc硬件寄存器:[寄存器地址 寄存器范围]
    interrupts = <GIC_SPI 23 IRQ_TYPE_LEVEL_HIGH>;          //指定模块中断:[中断控制器,中断线号,中断触发条件]
    clocks = <&clks IMX6UL_CLK_USDHC2>,                     //指定管理时钟地址,ipg/ahb/per
            <&clks IMX6UL_CLK_USDHC2>,
            <&clks IMX6UL_CLK_USDHC2>;
    clock-names = "ipg", "ahb", "per";
    bus-width = <4>;                                        //emmc数据总线宽度
    fsl,tuning-step = <2>;                                  //在调优过程中,延迟单元的时间
    fsl,tuning-start-tap = <20>;                            //在调优过程中,起始延时的时间点
    status = "disabled";                                    //状态,关闭
};

&usdhc2 {
    pinctrl-names = "default", "state_100mhz", "state_200mhz";  //对应不同时钟频率配置的引脚复用列表
    pinctrl-0 = <&pinctrl_usdhc2_8bit>;
    pinctrl-1 = <&pinctrl_usdhc2_8bit_100mhz>;
    pinctrl-2 = <&pinctrl_usdhc2_8bit_200mhz>;
    bus-width = <8>;                                        //emmc数据总线宽度,8bit  
    non-removable;                                          //不可拆卸槽,假设长期存在
    no-1-8-v;                                               //系统主体不支持1.8v模式,即使emmc设备支持
    status = "okay";                                        //状态,开启
};


驱动代码路径:
drivers/mmc/host/sdhci-esdhc-imx.c

驱动应用:
//读取ipg时钟
imx_data->clk_ipg = devm_clk_get(&pdev->dev, "ipg");
if (IS_ERR(imx_data->clk_ipg)) {
    err = PTR_ERR(imx_data->clk_ipg);
    goto free_sdhci;
}
//读取ahb时钟
imx_data->clk_ahb = devm_clk_get(&pdev->dev, "ahb");
if (IS_ERR(imx_data->clk_ahb)) {
    err = PTR_ERR(imx_data->clk_ahb);
    goto free_sdhci;
}
//读取per时钟
imx_data->clk_per = devm_clk_get(&pdev->dev, "per");
if (IS_ERR(imx_data->clk_per)) {
    err = PTR_ERR(imx_data->clk_per);
    goto free_sdhci;
}
//检查no-1-8-v状态
if (of_find_property(np, "no-1-8-v", NULL))
    host->quirks2 |= SDHCI_QUIRK2_NO_1_8_V;

wdog

芯片看门狗保护和复位模块。

wdog1: watchdog@20bc000 {
    compatible = "fsl,imx6ul-wdt", "fsl,imx21-wdt";         //标签,用于驱动匹配
    reg = <0x020bc000 0x4000>;                              //寄存器列表,通过regmap访问
    interrupts = <GIC_SPI 80 IRQ_TYPE_LEVEL_HIGH>;          //watchdog的中断控制器,中断线号和中断触发条件
    clocks = <&clks IMX6UL_CLK_WDOG1>;                      //管理模块的时钟使能,一般用于模块的电源管理(shutdown, suspend, resume)
};

驱动代码路径:
drivers/watchdog/imx2_wdt.c

驱动应用:
//reg访问方法
static const struct regmap_config imx2_wdt_regmap_config = {
    .reg_bits = 16,
    .reg_stride = 2,
    .val_bits = 16,
    .max_register = 0x8,
};
void __iomem *base;
u32 val;
base = devm_platform_ioremap_resource(pdev, 0)
wdev->regmap = devm_regmap_init_mmio(dev, NULL, base,
    &imx2_wdt_regmap_config);
regmap_read(wdev->regmap, IMX2_WDT_WRSR, &val);
regmap_write(wdev->regmap, IMX2_WDT_WSR, IMX2_WDT_SEQ1);

//clocks访问方法
int ret;
wdev->clk = devm_clk_get(dev, NULL);
ret = clk_prepare_enable(wdev->clk);

//interrupt访问方法
ret = platform_get_irq(pdev, 0);
if (ret > 0)
{
    if (!devm_request_irq(dev, ret, imx2_wdt_isr, 0, dev_name(dev), wdog))
    {
        wdog->info = &imx2_wdt_pretimeout_info;
    }
}

next_chapter

返回目录

直接开始下一节说明: t113_i设备树分析