• 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设备驱动,都可以在如下路径看到:

    
    
  • 相关阅读:
    centos7最小化安装无法tab补全
    rhcsa备战笔记
    idea springboot应用启动
    idea 导入Mapper错误报错设置
    idea 忽略显示文件
    maven 插件jetty/tomcat启动 web 应用
    maven jstl、jsp、servlet依赖
    maven 项目目录图与web 应用结构图对比
    maven 远程仓库、私服及镜像配置
    maven windows环境nexus3.0私服搭建
  • 原文地址:https://www.cnblogs.com/DF11G/p/16008437.html
Copyright © 2020-2023  润新知