设备树中的i2c设备以及内核对i2c节点的处理过程

dtb文件中的i2c节点

&i2c2 {
	clock-frequency = <100000>;  //时钟频率
	pinctrl-names = "default";
	pinctrl-0 = <&pinctrl_i2c2>; //i2c使用的引脚
	status = "okay";             //默认状态为使能状态
	
	codec: wm8904@1a {
		compatible = "wlf,wm8904";            
		reg = <0x1a>;                         //高7位设备地址
		clocks = <&clk IMX8MQ_CLK_SAI2_SRC>;  //使用的时钟
		clock-names = "mclk";
	};
 ....
 }

dtsi文件中的i2c节点

i2c2: i2c@30a30000 {
        #address-cells = <1>; //表示用一个32位的数来描述地址
        #size-cells = <0>; //表示用0个32位的数来描述该地址的大小
        compatible = "fsl,imx21-i2c"; //匹配的platform_driver

        //起始地址0x30a30000 长度0x10000
        reg = <0x0 0x30a30000 0x0 0x10000>;

        //使用的中断 以及触发方式
        interrupts = <GIC_SPI 36 IRQ_TYPE_LEVEL_HIGH>;
        clocks = <&clk IMX8MQ_CLK_I2C2_ROOT>;
        status = "disabled";
	};

/i2c节点一般表示i2c控制器, 它会被转换为platform_device, 在内核中有对应的platform_driver;一般为厂商所配套的platform_driver文件(freescale的处理文件为i2c-imx.c)。platform_driver的probe函数中会调用i2c_add_numbered_adapter():

 

/* 将设备树转换成platform_device后i2c_imx_probe函数被调用 */
i2c_imx_probe
    i2c_add_numbered_adapter /* 添加I2C控制器 */
        __i2c_add_numbered_adapter
            i2c_register_adapter /* 注册I2C控制器 */
                device_register /* I2C控制器设备注册 */
                of_i2c_register_devices /* 查找设备树控制器下面的从设备 */
                    of_i2c_register_device /*解析设备树属性*/
                        i2c_new_device
                            client->dev.bus = &i2c_bus_type;
                            device_register /* 添加设备I2C从设备 */
                i2c_scan_static_board_info /* 查找静态表,有些I2C设备是在代码中写死的,不是通过设备树的形式 */
                    i2c_new_device
                        client->dev.bus = &i2c_bus_type;
                        device_register /* 添加设备I2C从设备 */

下面我们来看一下of_i2c_register_device函数,该函数主要用于解析i2c节点设备树内容。

static struct i2c_client *of_i2c_register_device(struct i2c_adapter *adap,
						 struct device_node *node)
{
	struct i2c_client *result;
	struct i2c_board_info info = {};
	struct dev_archdata dev_ad = {};
	const __be32 *addr_be;
	u32 addr;
	int len;

	dev_dbg(&adap->dev, "of_i2c: register %s\n", node->full_name);

    //获取i2c节点中的compatible属性 拷贝到info.type中
	if (of_modalias_node(node, info.type, sizeof(info.type)) < 0) 
	{
		dev_err(&adap->dev, "of_i2c: modalias failure on %s\n",
			node->full_name);
		return ERR_PTR(-EINVAL);
	}
    
	//获取i2c节点中的reg属性(设备地址) 
	addr_be = of_get_property(node, "reg", &len);
	if (!addr_be || (len < sizeof(*addr_be))) {
		dev_err(&adap->dev, "of_i2c: invalid reg on %s\n",
			node->full_name);
		return ERR_PTR(-EINVAL);
	}

    //解析设备地址
	addr = be32_to_cpup(addr_be);
	if (addr & I2C_TEN_BIT_ADDRESS) {
		addr &= ~I2C_TEN_BIT_ADDRESS;
		info.flags |= I2C_CLIENT_TEN;
	}

	if (addr & I2C_OWN_SLAVE_ADDRESS) {
		addr &= ~I2C_OWN_SLAVE_ADDRESS;
		info.flags |= I2C_CLIENT_SLAVE;
	}

    //检查设备地址
	if (i2c_check_addr_validity(addr, info.flags)) {
		dev_err(&adap->dev, "of_i2c: invalid addr=%x on %s\n",
			addr, node->full_name);
		return ERR_PTR(-EINVAL);
	}

	info.addr = addr;
	info.of_node = of_node_get(node); //将设备树节点存入info.of_node
	info.archdata = &dev_ad;

	if (of_get_property(node, "wakeup-source", NULL))
		info.flags |= I2C_CLIENT_WAKE;

    //注册i2c 设备
	result = i2c_new_device(adap, &info);
	if (result == NULL) {
		dev_err(&adap->dev, "of_i2c: Failure registering %s\n",
			node->full_name);
		of_node_put(node);
		return ERR_PTR(-EINVAL);
	}
	return result;
}

在完成i2c设备注册后,将通过i2c_bus_type.i2c_device_match匹配驱动程序。

i2c设备匹配的过程中会调用到i2c_device_probe(),位于driver/i2c/i2c-core.c。此函数中也有设备树相关的处理。


static int i2c_device_probe(struct device *dev)
{
	struct i2c_client	*client = i2c_verify_client(dev);
	struct i2c_driver	*driver;
	int status;

	if (!client)
		return 0;

	if (!client->irq && dev->of_node) {
		int irq = of_irq_get(dev->of_node, 0); //得到节点中的中断号

		if (irq == -EPROBE_DEFER)
			return irq;
		if (irq < 0)
			irq = 0;

		client->irq = irq;
	}

	driver = to_i2c_driver(dev->driver);
	if (!driver->probe || !driver->id_table)
		return -ENODEV;

	if (!device_can_wakeup(&client->dev))
		device_init_wakeup(&client->dev,
					client->flags & I2C_CLIENT_WAKE);
	dev_dbg(dev, "probe\n");

	status = of_clk_set_defaults(dev->of_node, false);
	if (status < 0)
		return status;

	status = dev_pm_domain_attach(&client->dev, true);
	if (status != -EPROBE_DEFER) {
		status = driver->probe(client, i2c_match_id(driver->id_table,
					client));
		if (status)
			dev_pm_domain_detach(&client->dev, true);
	}

	return status;
}

 

 

  • 3
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值