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