Linux I2C驱动整理(以RK3399Pro+Kernel 4.4为例)

一. Linux I2C驱动架构

Linux内核里,I2C驱动框架可以分为两层,adapter驱动和deivce驱动。Adapter驱动也可以理解为I2C总线驱动,指的是SOC里的I2C控制器驱动。一个SOC可能包含多个I2C控制器,而每个控制器的使用方式是相同的(寄存器参数、收发数据的方法等),因此多个控制器可以共用一套adapter驱动;Deivce驱动,对应的是SOC外围的I2C设备,不同类型I2C设备需要开发不同的设备驱动,同一类型的I2C设备可以使用一种驱动,但是每一个I2C设备都由一个唯一的client来描述。

 

 

二. Adapter配置

DTSI文件(kernel/arch/arm64/boot/dts/rockchip/rk3399.dtsi)描述了RK3399Pro所有的I2C控制器信息:

 

i2c0: i2c@ff3c0000 {
        compatible = "rockchip,rk3399-i2c";
        reg = <0x0 0xff3c0000 0x0 0x1000>;
        clocks =  <&pmucru SCLK_I2C0_PMU>, <&pmucru PCLK_I2C0_PMU>;
        clock-names = "i2c", "pclk";
        interrupts = <GIC_SPI 57 IRQ_TYPE_LEVEL_HIGH 0>;
        pinctrl-names = "default";
        pinctrl-0 = <&i2c0_xfer>;
        #address-cells = <1>;
        #size-cells = <0>;
        status = "disabled";
    };

    i2c1: i2c@ff110000 {
        compatible = "rockchip,rk3399-i2c";
        reg = <0x0 0xff110000 0x0 0x1000>;
        clocks = <&cru SCLK_I2C1>, <&cru PCLK_I2C1>;
        clock-names = "i2c", "pclk";
        interrupts = <GIC_SPI 59 IRQ_TYPE_LEVEL_HIGH 0>;
        pinctrl-names = "default";
        pinctrl-0 = <&i2c1_xfer>;
        #address-cells = <1>;
        #size-cells = <0>;
        status = "disabled";
    };

    i2c2: i2c@ff120000 {
        compatible = "rockchip,rk3399-i2c";
        reg = <0x0 0xff120000 0x0 0x1000>;
        clocks = <&cru SCLK_I2C2>, <&cru PCLK_I2C2>;
        clock-names = "i2c", "pclk";
        interrupts = <GIC_SPI 35 IRQ_TYPE_LEVEL_HIGH 0>;
        pinctrl-names = "default";
        pinctrl-0 = <&i2c2_xfer>;
        #address-cells = <1>;
        #size-cells = <0>;
        status = "disabled";
    };

    i2c3: i2c@ff130000 {
        compatible = "rockchip,rk3399-i2c";
        reg = <0x0 0xff130000 0x0 0x1000>;
        clocks = <&cru SCLK_I2C3>, <&cru PCLK_I2C3>;
        clock-names = "i2c", "pclk";
        interrupts = <GIC_SPI 34 IRQ_TYPE_LEVEL_HIGH 0>;
        pinctrl-names = "default";
        pinctrl-0 = <&i2c3_xfer>;
        #address-cells = <1>;
        #size-cells = <0>;
        status = "disabled";
    };

    i2c5: i2c@ff140000 {
        compatible = "rockchip,rk3399-i2c";
        reg = <0x0 0xff140000 0x0 0x1000>;
        clocks = <&cru SCLK_I2C5>, <&cru PCLK_I2C5>;
        clock-names = "i2c", "pclk";
        interrupts = <GIC_SPI 38 IRQ_TYPE_LEVEL_HIGH 0>;
        pinctrl-names = "default";
        pinctrl-0 = <&i2c5_xfer>;
        #address-cells = <1>;
        #size-cells = <0>;
        status = "disabled";
    };

    i2c6: i2c@ff150000 {
        compatible = "rockchip,rk3399-i2c";
        reg = <0x0 0xff150000 0x0 0x1000>;
        clocks = <&cru SCLK_I2C6>, <&cru PCLK_I2C6>;
        clock-names = "i2c", "pclk";
        interrupts = <GIC_SPI 37 IRQ_TYPE_LEVEL_HIGH 0>;
        pinctrl-names = "default";
        pinctrl-0 = <&i2c6_xfer>;
        #address-cells = <1>;
        #size-cells = <0>;
        status = "disabled";
    };

    i2c7: i2c@ff160000 {
        compatible = "rockchip,rk3399-i2c";
        reg = <0x0 0xff160000 0x0 0x1000>;
        clocks = <&cru SCLK_I2C7>, <&cru PCLK_I2C7>;
        clock-names = "i2c", "pclk";
        interrupts = <GIC_SPI 36 IRQ_TYPE_LEVEL_HIGH 0>;
        pinctrl-names = "default";
        pinctrl-0 = <&i2c7_xfer>;
        #address-cells = <1>;
        #size-cells = <0>;
        status = "disabled";
    };   

i2c4: i2c@ff3d0000 { compatible
= "rockchip,rk3399-i2c"; reg = <0x0 0xff3d0000 0x0 0x1000>; clocks = <&pmucru SCLK_I2C4_PMU>, <&pmucru PCLK_I2C4_PMU>; clock-names = "i2c", "pclk"; interrupts = <GIC_SPI 56 IRQ_TYPE_LEVEL_HIGH 0>; pinctrl-names = "default"; pinctrl-0 = <&i2c4_xfer>; #address-cells = <1>; #size-cells = <0>; status = "disabled"; }; i2c8: i2c@ff3e0000 { compatible = "rockchip,rk3399-i2c"; reg = <0x0 0xff3e0000 0x0 0x1000>; clocks = <&pmucru SCLK_I2C8_PMU>, <&pmucru PCLK_I2C8_PMU>; clock-names = "i2c", "pclk"; interrupts = <GIC_SPI 58 IRQ_TYPE_LEVEL_HIGH 0>; pinctrl-names = "default"; pinctrl-0 = <&i2c8_xfer>; #address-cells = <1>; #size-cells = <0>; status = "disabled"; };

 可以看出,该SOC共有9个I2C控制器,分别为I2C0~I2C8, 每个控制器对应了不同的寄存器基地址(例如I2C0对应0xff3c0000),它们的compatible匹配属性都是"rockchip,rk3399-i2c",也就是对应了同一个adapter驱动。

注意,这9个控制器并不是每一个都挂上了I2C设备,系统总DTS文件只会讲描述挂载了外围设备的I2C控制器,其余闲置控制器将不会注册adapter驱动。

 

再打开驱动文件i2c-rk3x.c(位于kernel/driver/i2c/busses):

static const struct of_device_id rk3x_i2c_match[] = {
    {
        .compatible = "rockchip,rv1108-i2c",
        .data = (void *)&rv1108_soc_data
    },
    {
        .compatible = "rockchip,rk3066-i2c",
        .data = (void *)&rk3066_soc_data
    },
    {
        .compatible = "rockchip,rk3188-i2c",
        .data = (void *)&rk3188_soc_data
    },
    {
        .compatible = "rockchip,rk3228-i2c",
        .data = (void *)&rk3228_soc_data
    },
    {
        .compatible = "rockchip,rk3288-i2c",
        .data = (void *)&rk3288_soc_data
    },
    {
        .compatible = "rockchip,rk3399-i2c",
        .data = (void *)&rk3399_soc_data
    },
    {},
};
MODULE_DEVICE_TABLE(of, rk3x_i2c_match);



static struct platform_driver rk3x_i2c_driver = {
    .probe   = rk3x_i2c_probe,
    .remove  = rk3x_i2c_remove,
    .driver  = {
        .name  = "rk3x-i2c",
        .of_match_table = rk3x_i2c_match,
        .pm = &rk3x_i2c_pm_ops,
    },
};

module_platform_driver(rk3x_i2c_driver);

文件中将rk3x_i2c_driver作为一个platform driver注册到内核,rk3x_i2c_driver的匹配表rk3x_i2c_match是一个数组,其中包含了上文DTS描述的匹配字符串"rockchip,rk3399-i2c"。

系统启动后,如果DTS中的匹配属性与驱动匹配字符串相符,即会进入probe接口即rk3x_i2c_probe:

static const struct i2c_algorithm rk3x_i2c_algorithm = {
    .master_xfer        = rk3x_i2c_xfer,
    .functionality        = rk3x_i2c_func,
};

static int rk3x_i2c_probe(struct platform_device *pdev)
{
    struct device_node *np = pdev->dev.of_node;
    const struct of_device_id *match;
    struct rk3x_i2c *i2c;
    struct resource *mem;
    int ret = 0;
    int bus_nr;
    u32 value;
    int irq;
    unsigned long clk_rate;

    i2c = devm_kzalloc(&pdev->dev, sizeof(struct rk3x_i2c), GFP_KERNEL);
    if (!i2c)
        return -ENOMEM;

    match = of_match_node(rk3x_i2c_match, np);
    i2c->soc_data = (struct rk3x_i2c_soc_data *)match->data;

    /* use common interface to get I2C timing properties */
    i2c_parse_fw_timings(&pdev->dev, &i2c->t, true);

    strlcpy(i2c->adap.name, "rk3x-i2c", sizeof(i2c->adap.name));
    i2c->adap.owner = THIS_MODULE;
    i2c->adap.algo = &rk3x_i2c_algorithm;
    i2c->adap.retries = 3;
    i2c->adap.dev.of_node = np;
    i2c->adap.algo_data = i2c;
    i2c->adap.dev.parent = &pdev->dev;

    i2c->dev = &pdev->dev;

    spin_lock_init(&i2c->lock);
    init_waitqueue_head(&i2c->wait);

    i2c->i2c_restart_nb.notifier_call = rk3x_i2c_restart_notify;
    i2c->i2c_restart_nb.priority = 128;
    ret = register_i2c_restart_handler(&i2c->i2c_restart_nb);
    if (ret) {
        dev_err(&pdev->dev, "failed to setup i2c restart handler.\n");
        return ret;
    }

    mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
    i2c->regs = devm_ioremap_resource(&pdev->dev, mem);
    if (IS_ERR(i2c->regs))
        return PTR_ERR(i2c->regs);

    /* Try to set the I2C adapter number from dt */
    bus_nr = of_alias_get_id(np, "i2c");

    /*
     * Switch to new interface if the SoC also offers the old one.
     * The control bit is located in the GRF register space.
     */
    if (i2c->soc_data->grf_offset >= 0) {
        struct regmap *grf;

        grf = syscon_regmap_lookup_by_phandle(np, "rockchip,grf");
        if (IS_ERR(grf)) {
            dev_err(&pdev->dev,
                "rk3x-i2c needs 'rockchip,grf' property\n");
            return PTR_ERR(grf);
        }

        if (bus_nr < 0) {
            dev_err(&pdev->dev, "rk3x-i2c needs i2cX alias");
            return -EINVAL;
        }

        /* 27+i: write mask, 11+i: value */
        value = BIT(27 + bus_nr) | BIT(11 + bus_nr);

        ret = regmap_write(grf, i2c->soc_data->grf_offset, value);
        if (ret != 0) {
            dev_err(i2c->dev, "Could not write to GRF: %d\n", ret);
            return ret;
        }
    }

    /* IRQ setup */
    irq = platform_get_irq(pdev, 0);
    if (irq < 0) {
        dev_err(&pdev->dev, "cannot find rk3x IRQ\n");
        return irq;
    }

    ret = devm_request_irq(&pdev->dev, irq, rk3x_i2c_irq,
                   0, dev_name(&pdev->dev), i2c);
    if (ret < 0) {
        dev_err(&pdev->dev, "cannot request IRQ\n");
        return ret;
    }

    platform_set_drvdata(pdev, i2c);

    if (i2c->soc_data->calc_timings == rk3x_i2c_v0_calc_timings) {
        /* Only one clock to use for bus clock and peripheral clock */
        i2c->clk = devm_clk_get(&pdev->dev, NULL);
        i2c->pclk = i2c->clk;
    } else {
        i2c->clk = devm_clk_get(&pdev->dev, "i2c");
        i2c->pclk = devm_clk_get(&pdev->dev, "pclk");
    }

    if (IS_ERR(i2c->clk)) {
        ret = PTR_ERR(i2c->clk);
        if (ret != -EPROBE_DEFER)
            dev_err(&pdev->dev, "Can't get bus clk: %d\n", ret);
        return ret;
    }
    if (IS_ERR(i2c->pclk)) {
        ret = PTR_ERR(i2c->pclk);
        if (ret != -EPROBE_DEFER)
            dev_err(&pdev->dev, "Can't get periph clk: %d\n", ret);
        return ret;
    }

    ret = clk_prepare(i2c->clk);
    if (ret < 0) {
        dev_err(&pdev->dev, "Can't prepare bus clk: %d\n", ret);
        return ret;
    }
    ret = clk_prepare(i2c->pclk);
    if (ret < 0) {
        dev_err(&pdev->dev, "Can't prepare periph clock: %d\n", ret);
        goto err_clk;
    }

    i2c->clk_rate_nb.notifier_call = rk3x_i2c_clk_notifier_cb;
    ret = clk_notifier_register(i2c->clk, &i2c->clk_rate_nb);
    if (ret != 0) {
        dev_err(&pdev->dev, "Unable to register clock notifier\n");
        goto err_pclk;
    }

    clk_rate = clk_get_rate(i2c->clk);
    rk3x_i2c_adapt_div(i2c, clk_rate);

    ret = i2c_add_adapter(&i2c->adap);
    if (ret < 0) {
        dev_err(&pdev->dev, "Could not register adapter\n");
        goto err_clk_notifier;
    }

    dev_info(&pdev->dev, "Initialized RK3xxx I2C bus at %p\n", i2c->regs);

    return 0;

err_clk_notifier:
    clk_notifier_unregister(i2c->clk, &i2c->clk_rate_nb);
err_pclk:
    clk_unprepare(i2c->pclk);
err_clk:
    clk_unprepare(i2c->clk);
    return ret;
}

probe函数中有一条打印:

dev_info(&pdev->dev, "Initialized RK3xxx I2C bus at %p\n", i2c->regs);

搜索启动log,可以看到相应的打印输出:

[    1.096191] rk3x-i2c ff3c0000.i2c: Initialized RK3xxx I2C bus at ffffff8009542000
[    1.098189] rk3x-i2c ff110000.i2c: Initialized RK3xxx I2C bus at ffffff8009544000
...
[    1.155006] rk3x-i2c ff3d0000.i2c: Initialized RK3xxx I2C bus at ffffff8009546000
...
[    1.169117] rk3x-i2c ff3e0000.i2c: Initialized RK3xxx I2C bus at ffffff8009556000

DTS文件最终只配置了四个控制器(I2C0,I2C1,I2C4,I2C8),在启动log中可以看到对应的输出。

 

系统启动后可以在/sys/bus/platform/devices目录看到已配置的I2C Adapter信息:

 

 进入其中目录后可以进一步获取该Adapter信息,如ff3c0000.i2c目录属于总线i2c-0。

 

三. Device配置

每个I2C Adapter对应一条I2C总线,总线上可以挂载一个或多个I2C设备,挂载设备信息同样需要配置到DTS文件中:

打开kernel/arch/arm64/boot/dts/rockchip/rk3399pro-evb-v11-linux.dts文件

&i2c0 {
    status = "okay";
    i2c-scl-rising-time-ns = <180>;
    i2c-scl-falling-time-ns = <30>;
    clock-frequency = <400000>;

    rk809: pmic@20 {
        compatible = "rockchip,rk809";
        reg = <0x20>;
        interrupt-parent = <&gpio1>;
        interrupts = <RK_PC2 IRQ_TYPE_LEVEL_LOW>;
        pinctrl-names = "default", "pmic-sleep",
                "pmic-power-off", "pmic-reset";
        pinctrl-0 = <&pmic_int_l>;
        pinctrl-1 = <&soc_slppin_slp>, <&rk809_slppin_slp>;
        pinctrl-2 = <&soc_slppin_gpio>, <&rk809_slppin_pwrdn>;
        pinctrl-3 = <&soc_slppin_gpio>, <&rk809_slppin_null>;
        rockchip,system-power-controller;
        pmic-reset-func = <0>;
        wakeup-source;
        #clock-cells = <1>;
        clock-output-names = "rk808-clkout1", "rk808-clkout2";

        vcc1-supply = <&vcc5v0_sys>;
        vcc2-supply = <&vcc5v0_sys>;
        vcc3-supply = <&vcc5v0_sys>;
        vcc4-supply = <&vcc5v0_sys>;
        vcc5-supply = <&vcc_buck5>;
        vcc6-supply = <&vcc_buck5>;
        vcc7-supply = <&vcc3v3_sys>;
        vcc8-supply = <&vcc3v3_sys>;
        vcc9-supply = <&vcc5v0_sys>;

        pwrkey {
            status = "okay";
        };

        rtc {
            status = "okay";
        };

        pinctrl_rk8xx: pinctrl_rk8xx {
            gpio-controller;
            #gpio-cells = <2>;

            rk809_slppin_null: rk809_slppin_null {
                pins = "gpio_slp";
                function = "pin_fun0";
            };

            rk809_slppin_slp: rk809_slppin_slp {
                pins = "gpio_slp";
                function = "pin_fun1";
            };

            rk809_slppin_pwrdn: rk809_slppin_pwrdn {
                pins = "gpio_slp";
                function = "pin_fun2";
            };

            rk809_slppin_rst: rk809_slppin_rst {
                pins = "gpio_slp";
                function = "pin_fun3";
            };
        };

        regulators {
            vdd_center: DCDC_REG1 {
                regulator-always-on;
                regulator-boot-on;
                regulator-min-microvolt = <750000>;
                regulator-max-microvolt = <1350000>;
                regulator-initial-mode = <0x2>;
                regulator-name = "vdd_center";
                regulator-state-mem {
                    regulator-off-in-suspend;
                    regulator-suspend-microvolt = <900000>;
                };
            };

            vdd_cpu_l: DCDC_REG2 {
                regulator-always-on;
                regulator-boot-on;
                regulator-min-microvolt = <750000>;
                regulator-max-microvolt = <1350000>;
                regulator-ramp-delay = <6001>;
                regulator-initial-mode = <0x2>;
                regulator-name = "vdd_cpu_l";
                regulator-state-mem {
                    regulator-off-in-suspend;
                };
            };

            vcc_ddr: DCDC_REG3 {
                regulator-always-on;
                regulator-boot-on;
                regulator-name = "vcc_ddr";
                regulator-initial-mode = <0x2>;
                regulator-state-mem {
                    regulator-on-in-suspend;
                };
            };

            vcc3v3_sys: DCDC_REG4 {
                regulator-always-on;
                regulator-boot-on;
                regulator-min-microvolt = <3300000>;
                regulator-max-microvolt = <3300000>;
                regulator-initial-mode = <0x2>;
                regulator-name = "vcc3v3_sys";
                regulator-state-mem {
                    regulator-on-in-suspend;
                    regulator-suspend-microvolt = <3300000>;
                };
            };

            vcc_buck5: DCDC_REG5 {
                regulator-always-on;
                regulator-boot-on;
                regulator-min-microvolt = <2200000>;
                regulator-max-microvolt = <2200000>;
                regulator-name = "vcc_buck5";
                regulator-state-mem {
                    regulator-on-in-suspend;
                    regulator-suspend-microvolt = <2200000>;
                };
            };

            vcca_0v9: LDO_REG1 {
                regulator-always-on;
                regulator-boot-on;
                regulator-min-microvolt = <900000>;
                regulator-max-microvolt = <900000>;
                regulator-name = "vcca_0v9";
                regulator-state-mem {
                    regulator-off-in-suspend;
                };
            };

            vcc_1v8: LDO_REG2 {
                regulator-always-on;
                regulator-boot-on;
                regulator-min-microvolt = <1800000>;
                regulator-max-microvolt = <1800000>;

                regulator-name = "vcc_1v8";
                regulator-state-mem {
                    regulator-on-in-suspend;
                    regulator-suspend-microvolt = <1800000>;
                };
            };

            vcc0v9_soc: LDO_REG3 {
                regulator-always-on;
                regulator-boot-on;
                regulator-min-microvolt = <900000>;
                regulator-max-microvolt = <900000>;

                regulator-name = "vcc0v9_soc";
                regulator-state-mem {
                    regulator-on-in-suspend;
                    regulator-suspend-microvolt = <900000>;
                };
            };

            vcca_1v8: LDO_REG4 {
                regulator-always-on;
                regulator-boot-on;
                regulator-min-microvolt = <1800000>;
                regulator-max-microvolt = <1800000>;

                regulator-name = "vcca_1v8";
                regulator-state-mem {
                    regulator-off-in-suspend;
                };
            };

            vdd1v5_dvp: LDO_REG5 {
                regulator-always-on;
                regulator-boot-on;
                regulator-min-microvolt = <1500000>;
                regulator-max-microvolt = <1500000>;

                regulator-name = "vdd1v5_dvp";
                regulator-state-mem {
                    regulator-off-in-suspend;
                };
            };

            vcc_1v5: LDO_REG6 {
                regulator-always-on;
                regulator-boot-on;
                regulator-min-microvolt = <1500000>;
                regulator-max-microvolt = <1500000>;

                regulator-name = "vcc_1v5";
                regulator-state-mem {
                    regulator-off-in-suspend;
                };
            };

            vcc_3v0: LDO_REG7 {
                regulator-always-on;
                regulator-boot-on;
                regulator-min-microvolt = <3000000>;
                regulator-max-microvolt = <3000000>;

                regulator-name = "vcc_3v0";
                regulator-state-mem {
                    regulator-off-in-suspend;
                };
            };

            vccio_sd: LDO_REG8 {
                regulator-always-on;
                regulator-boot-on;
                regulator-min-microvolt = <1800000>;
                regulator-max-microvolt = <3300000>;

                regulator-name = "vccio_sd";
                regulator-state-mem {
                    regulator-off-in-suspend;
                };
            };

            vcc_sd: LDO_REG9 {
                regulator-boot-on;
                regulator-min-microvolt = <3300000>;
                regulator-max-microvolt = <3300000>;

                regulator-name = "vcc_sd";
                regulator-state-mem {
                    regulator-off-in-suspend;
                };
            };

            vcc5v0_usb: SWITCH_REG1 {
                regulator-min-microvolt = <5000000>;
                regulator-max-microvolt = <5000000>;

                regulator-name = "vcc5v0_usb";
                regulator-state-mem {
                    regulator-on-in-suspend;
                    regulator-suspend-microvolt = <5000000>;
                };
            };

            vccio_3v3: SWITCH_REG2 {
                regulator-always-on;
                regulator-boot-on;
                regulator-min-microvolt = <3300000>;
                regulator-max-microvolt = <3300000>;

                regulator-name = "vccio_3v3";
                regulator-state-mem {
                    regulator-off-in-suspend;
                };
            };
        };

        rk809_codec: codec {
            #sound-dai-cells = <0>;
            compatible = "rockchip,rk809-codec", "rockchip,rk817-codec";
            clocks = <&cru SCLK_I2S_8CH_OUT>;
            clock-names = "mclk";
            pinctrl-names = "default";
            pinctrl-0 = <&i2s_8ch_mclk>;
            hp-volume = <20>;
            spk-volume = <3>;
            status = "okay";
        };
    };

    vdd_cpu_b: tcs452x@1c {
        compatible = "tcs,tcs452x";
        reg = <0x1c>;
        vin-supply = <&vcc5v0_sys>;
        regulator-compatible = "fan53555-reg";
        pinctrl-0 = <&vsel1_gpio>;
        vsel-gpios = <&gpio1 RK_PC1 GPIO_ACTIVE_HIGH>;
        regulator-name = "vdd_cpu_b";
        regulator-min-microvolt = <712500>;
        regulator-max-microvolt = <1500000>;
        regulator-ramp-delay = <2300>;
        fcs,suspend-voltage-selector = <1>;
        regulator-always-on;
        regulator-boot-on;
        regulator-initial-state = <3>;
        regulator-state-mem {
            regulator-off-in-suspend;
        };
    };

    vdd_gpu: tcs452x@10 {
        compatible = "tcs,tcs452x";
        reg = <0x10>;
        vin-supply = <&vcc5v0_sys>;
        regulator-compatible = "fan53555-reg";
        pinctrl-0 = <&vsel2_gpio>;
        vsel-gpios = <&gpio1 RK_PB6 GPIO_ACTIVE_HIGH>;
        regulator-name = "vdd_gpu";
        regulator-min-microvolt = <735000>;
        regulator-max-microvolt = <1400000>;
        regulator-ramp-delay = <1000>;
        fcs,suspend-voltage-selector = <1>;
        regulator-always-on;
        regulator-boot-on;
        regulator-initial-state = <3>;
        regulator-state-mem {
            regulator-off-in-suspend;
        };
    };

    bq25700: bq25700@6b {
        compatible = "ti,bq25703";
        reg = <0x6b>;
        extcon = <&fusb0>;
        interrupt-parent = <&gpio1>;
        interrupts = <RK_PA1 IRQ_TYPE_LEVEL_LOW>;
        pinctrl-names = "default";
        pinctrl-0 = <&charger_ok_int>;
        ti,charge-current = <1500000>;
        ti,max-charge-voltage = <8704000>;
        ti,max-input-voltage = <20000000>;
        ti,max-input-current = <6000000>;
        ti,input-current-sdp = <500000>;
        ti,input-current-dcp = <2000000>;
        ti,input-current-cdp = <2000000>;
        ti,input-current-dc = <2000000>;
        ti,minimum-sys-voltage = <6700000>;
        ti,otg-voltage = <5000000>;
        ti,otg-current = <500000>;
        ti,input-current = <500000>;
        pd-charge-only = <0>;
        status = "disabled";
    };
};

&i2c1 {
    status = "okay";
    i2c-scl-rising-time-ns = <140>;
    i2c-scl-falling-time-ns = <30>;

    mpu6500@68 {
        status = "okay";
        compatible = "invensense,mpu6500";
        reg = <0x68>;
        irq-gpio = <&gpio3 RK_PD2 IRQ_TYPE_EDGE_RISING>;
        mpu-int_config = <0x10>;
        mpu-level_shifter = <0>;
        mpu-orientation = <0 1 0 1 0 0 0 0 1>;
        orientation-x= <1>;
        orientation-y= <0>;
        orientation-z= <0>;
        mpu-debug = <1>;
    };

    sensor@d {
        status = "okay";
        compatible = "ak8963";
        reg = <0x0d>;
        type = <SENSOR_TYPE_COMPASS>;
        irq-gpio = <&gpio3 RK_PD7 IRQ_TYPE_EDGE_RISING>;
        irq_enable = <0>;
        poll_delay_ms = <30>;
        layout = <3>;
    };

    vm149c: vm149c@0c {
        compatible = "silicon touch,vm149c";
        status = "okay";
        reg = <0x0c>;
        rockchip,camera-module-index = <0>;
        rockchip,camera-module-facing = "back";
    };

    ov13850: ov13850@10 {
        compatible = "ovti,ov13850";
        status = "okay";
        reg = <0x10>;
        clocks = <&cru SCLK_CIF_OUT>;
        clock-names = "xvclk";

        /* conflict with csi-ctl-gpios */
        reset-gpios = <&gpio4 26 GPIO_ACTIVE_HIGH>;
        pwdn-gpios = <&gpio4 25 GPIO_ACTIVE_HIGH>;
        pinctrl-names = "rockchip,camera_default";
        pinctrl-0 = <&cif_clkout>;
        rockchip,camera-module-index = <0>;
        rockchip,camera-module-facing = "back";
        rockchip,camera-module-name = "CMK-CT0116";
        rockchip,camera-module-lens-name = "Largan-50013A1";
        lens-focus = <&vm149c>;

        port {
            ucam_out0: endpoint {
                remote-endpoint = <&mipi_in_ucam0>;
                data-lanes = <1 2>;
            };
        };
    };

    imx327: imx327@1a {
        compatible = "sony,imx327";
        status = "okay";
        reg = <0x1a>;
        clocks = <&cru SCLK_CIF_OUT>;
        clock-names = "xvclk";
        /* conflict with csi-ctl-gpios */
        reset-gpios = <&gpio4 26 GPIO_ACTIVE_HIGH>;
        pwdn-gpios = <&gpio4 25 GPIO_ACTIVE_HIGH>;
        pinctrl-names = "default";
        pinctrl-0 = <&cif_clkout>;
        rockchip,camera-module-index = <0>;
        rockchip,camera-module-facing = "back";
        rockchip,camera-module-name = "TongJu";
        rockchip,camera-module-lens-name = "CHT842-MD";
        port {
            ucam_out2: endpoint {
                remote-endpoint = <&mipi_in_ucam2>;
                data-lanes = <1 2>;
            };
        };
    };

};

&i2c4 {
    status = "okay";
    i2c-scl-rising-time-ns = <345>;
    i2c-scl-falling-time-ns = <11>;

    gsl3673: gsl3673@40 {
        compatible = "GSL,GSL3673";
        reg = <0x40>;
        screen_max_x = <1536>;
        screen_max_y = <2048>;
        irq_gpio_number = <&gpio4 RK_PC3 IRQ_TYPE_LEVEL_LOW>;
        rst_gpio_number = <&gpio4 RK_PC6 GPIO_ACTIVE_HIGH>;
    };
};

&i2c8 {
    status = "okay";
    i2c-scl-rising-time-ns = <345>;
    i2c-scl-falling-time-ns = <11>;
    clock-frequency = <100000>;

    fusb0: fusb30x@22 {
        compatible = "fairchild,fusb302";
        reg = <0x22>;
        pinctrl-names = "default";
        pinctrl-0 = <&fusb0_int>;
        int-n-gpios = <&gpio1 RK_PA2 GPIO_ACTIVE_HIGH>;
        vbus-5v-gpios = <&gpio0 RK_PA1 GPIO_ACTIVE_LOW>;
        status = "okay";
    };

};

 

 以I2C1为例,该Adapter配置了如下几个设备:

mpu6500@68   "invensense,mpu6500";

sensor@d   "ak8963";

vm149c@0c  "silicon touch,vm149c";

ov13850@10  "ovti,ov13850";

 imx327@1a "sony,imx327";

 

@符号后面的十六进制数字表示该设备的从地址(Slave Address),compatible属性用于和设备驱动进行匹配。

 

 配置的这5个设备,在系统启动后可以在如下路径看到:

 

其中1-000d就代表着sensor@d即ak8963设备:

 

1-0010代表着ov13850@10即"ovti,ov13850"设备:

 

四. Device驱动

为了用户层能方便操作,理论上每个I2C设备都需要实现一个驱动并生成设备节点,我们以Camera Sensor OV13850为例,其驱动文件位于kernel/drivers/media/i2c/ov13850.c:

#if IS_ENABLED(CONFIG_OF)
static const struct of_device_id ov13850_of_match[] = {
    { .compatible = "ovti,ov13850" },
    {},
};
MODULE_DEVICE_TABLE(of, ov13850_of_match);
#endif

static const struct i2c_device_id ov13850_match_id[] = {
    { "ovti,ov13850", 0 },
    { },
};

static struct i2c_driver ov13850_i2c_driver = {
    .driver = {
        .name = OV13850_NAME,
        .pm = &ov13850_pm_ops,
        .of_match_table = of_match_ptr(ov13850_of_match),
    },
    .probe        = &ov13850_probe,
    .remove        = &ov13850_remove,
    .id_table    = ov13850_match_id,
};

static int __init sensor_mod_init(void)
{
    return i2c_add_driver(&ov13850_i2c_driver);
}

static void __exit sensor_mod_exit(void)
{
    i2c_del_driver(&ov13850_i2c_driver);
}

 

如果该驱动编译进内核,那么系统启动就会调用sensor_mod_init进行初始化,该函数调用i2c_add_driverov13850_i2c_driver结构体注册到I2C总线。

根据设备树的匹配规则,compatible = “ovti,ov13850”;of_match_table = of_match_ptr(ov13850_of_match)两者匹配,然后i2c_driver里面的probe函数即ov13850_probe被调用。

static int ov13850_probe(struct i2c_client *client,
             const struct i2c_device_id *id)
{
    struct device *dev = &client->dev;
    struct device_node *node = dev->of_node;
    struct ov13850 *ov13850;
    struct v4l2_subdev *sd;
    char facing[2];
    int ret;

    dev_info(dev, "driver version: %02x.%02x.%02x",
        DRIVER_VERSION >> 16,
        (DRIVER_VERSION & 0xff00) >> 8,
        DRIVER_VERSION & 0x00ff);

    ov13850 = devm_kzalloc(dev, sizeof(*ov13850), GFP_KERNEL);
    if (!ov13850)
        return -ENOMEM;

    ret = of_property_read_u32(node, RKMODULE_CAMERA_MODULE_INDEX,
                   &ov13850->module_index);
    ret |= of_property_read_string(node, RKMODULE_CAMERA_MODULE_FACING,
                       &ov13850->module_facing);
    ret |= of_property_read_string(node, RKMODULE_CAMERA_MODULE_NAME,
                       &ov13850->module_name);
    ret |= of_property_read_string(node, RKMODULE_CAMERA_LENS_NAME,
                       &ov13850->len_name);
    if (ret) {
        dev_err(dev, "could not get module information!\n");
        return -EINVAL;
    }

    ov13850->client = client;
    ov13850->cur_mode = &supported_modes[0];

    ov13850->xvclk = devm_clk_get(dev, "xvclk");
    if (IS_ERR(ov13850->xvclk)) {
        dev_err(dev, "Failed to get xvclk\n");
        return -EINVAL;
    }

    ov13850->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW);
    if (IS_ERR(ov13850->reset_gpio))
        dev_warn(dev, "Failed to get reset-gpios\n");

    ov13850->pwdn_gpio = devm_gpiod_get(dev, "pwdn", GPIOD_OUT_LOW);
    if (IS_ERR(ov13850->pwdn_gpio))
        dev_warn(dev, "Failed to get pwdn-gpios\n");

    ret = ov13850_configure_regulators(ov13850);
    if (ret) {
        dev_err(dev, "Failed to get power regulators\n");
        return ret;
    }

    ov13850->pinctrl = devm_pinctrl_get(dev);
    if (!IS_ERR(ov13850->pinctrl)) {
        ov13850->pins_default =
            pinctrl_lookup_state(ov13850->pinctrl,
                         OF_CAMERA_PINCTRL_STATE_DEFAULT);
        if (IS_ERR(ov13850->pins_default))
            dev_err(dev, "could not get default pinstate\n");

        ov13850->pins_sleep =
            pinctrl_lookup_state(ov13850->pinctrl,
                         OF_CAMERA_PINCTRL_STATE_SLEEP);
        if (IS_ERR(ov13850->pins_sleep))
            dev_err(dev, "could not get sleep pinstate\n");
    }

    mutex_init(&ov13850->mutex);

    sd = &ov13850->subdev;
    v4l2_i2c_subdev_init(sd, client, &ov13850_subdev_ops);
    ret = ov13850_initialize_controls(ov13850);
    if (ret)
        goto err_destroy_mutex;

    ret = __ov13850_power_on(ov13850);
    if (ret)
        goto err_free_handler;

    ret = ov13850_check_sensor_id(ov13850, client);
    if (ret)
        goto err_power_off;

#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API
    sd->internal_ops = &ov13850_internal_ops;
    sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
#endif
#if defined(CONFIG_MEDIA_CONTROLLER)
    ov13850->pad.flags = MEDIA_PAD_FL_SOURCE;
    sd->entity.type = MEDIA_ENT_T_V4L2_SUBDEV_SENSOR;
    ret = media_entity_init(&sd->entity, 1, &ov13850->pad, 0);
    if (ret < 0)
        goto err_power_off;
#endif

    memset(facing, 0, sizeof(facing));
    if (strcmp(ov13850->module_facing, "back") == 0)
        facing[0] = 'b';
    else
        facing[0] = 'f';

    snprintf(sd->name, sizeof(sd->name), "m%02d_%s_%s %s",
         ov13850->module_index, facing,
         OV13850_NAME, dev_name(sd->dev));
    ret = v4l2_async_register_subdev_sensor_common(sd);
    if (ret) {
        dev_err(dev, "v4l2 async register subdev failed\n");
        goto err_clean_entity;
    }

    pm_runtime_set_active(dev);
    pm_runtime_enable(dev);
    pm_runtime_idle(dev);

    return 0;

err_clean_entity:
#if defined(CONFIG_MEDIA_CONTROLLER)
    media_entity_cleanup(&sd->entity);
#endif
err_power_off:
    __ov13850_power_off(ov13850);
err_free_handler:
    v4l2_ctrl_handler_free(&ov13850->ctrl_handler);
err_destroy_mutex:
    mutex_destroy(&ov13850->mutex);

    return ret;
}

该函数主要完成对OV13850初始化上电(__ov13850_power_on)以及将设备注册到V4L2的框架(v4l2_i2c_subdev_init,v4l2_async_register_subdev_sensor_common)中,如果设备没有正确连接,那么在ov13850_check_sensor_id函数中会返回失败,停止后续的注册流程。

[    1.725121] ov13850 1-0010: driver version: 00.01.03
[    1.725741] 1-0010 supply avdd not found, using dummy regulator
[    1.726329] 1-0010 supply dovdd not found, using dummy regulator
[    1.726927] 1-0010 supply dvdd not found, using dummy regulator
[    1.727531] ov13850 1-0010: could not get sleep pinstate
[    1.729928] ov13850 1-0010: Unexpected sensor id(000000), ret(-5)

如果是其他普通I2C设备,可以调用misc_register注册混杂字符设备并生成设备节点。

所有已注册的IC设备驱动,都可以在如下路径看到:

 

 


posted @ 2022-03-15 14:54  DF11G  阅读(3167)  评论(1编辑  收藏  举报