设备树节点和struct device的关系及示例

概述

设备树节点-> device_node-> device-> i2c_client;一次呈包含关系

device_node

struct device_node描述设备树节点,初期初始化设备树的时候,按照树状结构,将设备树节点们都初始化成一个个device_node结构,并建立父亲,儿子,兄弟的关系

struct device_node {
	const char *name;
	const char *type;
	phandle phandle;
	const char *full_name;
	struct fwnode_handle fwnode;

	struct	property *properties;
	struct	property *deadprops;	/* removed properties */
	struct	device_node *parent;
	struct	device_node *child;
	struct	device_node *sibling;
	struct	kobject kobj;
	unsigned long _flags;
	void	*data;
#if defined(CONFIG_SPARC)
	const char *path_component_name;
	unsigned int unique_id;
	struct of_irq_controller *irq_trans;
#endif
};

struct device

struct device里面包含struct device_node结构体来指定设备树节点信息,bus_type指定还有位于总线,device_driver指定匹配的驱动,attribute_group导出属性文件到sysfs,dev_t导到/sys/dev/char或者/sys/dev/block

struct device {
	struct device		*parent;

	struct device_private	*p;

	struct kobject kobj;
	const char		*init_name; /* initial name of the device */
	const struct device_type *type;

	struct mutex		mutex;	/* mutex to synchronize calls to
					 * its driver.
					 */

	struct bus_type	*bus;		/* type of bus device is on */
	struct device_driver *driver;	/* which driver has allocated this
					   device */
	void		*platform_data;	/* Platform specific data, device
					   core doesn't touch it */
	void		*driver_data;	/* Driver data, set and get with
					   dev_set/get_drvdata */
	struct dev_links_info	links;
	struct dev_pm_info	power;
	struct dev_pm_domain	*pm_domain;

#ifdef CONFIG_GENERIC_MSI_IRQ_DOMAIN
	struct irq_domain	*msi_domain;
#endif
#ifdef CONFIG_PINCTRL
	struct dev_pin_info	*pins;
#endif
#ifdef CONFIG_GENERIC_MSI_IRQ
	struct list_head	msi_list;
#endif

#ifdef CONFIG_NUMA
	int		numa_node;	/* NUMA node this device is close to */
#endif
	const struct dma_map_ops *dma_ops;
	u64		*dma_mask;	/* dma mask (if dma'able device) */
	u64		coherent_dma_mask;/* Like dma_mask, but for
					     alloc_coherent mappings as
					     not all hardware supports
					     64 bit addresses for consistent
					     allocations such descriptors. */
	unsigned long	dma_pfn_offset;

	struct device_dma_parameters *dma_parms;

	struct list_head	dma_pools;	/* dma pools (if dma'ble) */

	struct dma_coherent_mem	*dma_mem; /* internal for coherent mem
					     override */
#ifdef CONFIG_DMA_CMA
	struct cma *cma_area;		/* contiguous memory area for dma
					   allocations */
#endif
	/* arch specific additions */
	struct dev_archdata	archdata;

	struct device_node	*of_node; /* associated device tree node */
	struct fwnode_handle	*fwnode; /* firmware device node */

	dev_t			devt;	/* dev_t, creates the sysfs "dev" */
	u32			id;	/* device instance */

	spinlock_t		devres_lock;
	struct list_head	devres_head;

	struct klist_node	knode_class;
	struct class		*class;
	const struct attribute_group **groups;	/* optional groups */

	void	(*release)(struct device *dev);
	struct iommu_group	*iommu_group;
	struct iommu_fwspec	*iommu_fwspec;

	bool			offline_disabled:1;
	bool			offline:1;
	bool			of_node_reused:1;
};

struct i2c_client

struct i2c_client包含struct device;以及设备地址,名字,从属于那个适配器等信息

struct i2c_client {
	unsigned short flags;		/* div., see below		*/
	unsigned short addr;		/* chip address - NOTE: 7bit	*/
					/* addresses are stored in the	*/
					/* _LOWER_ 7 bits		*/
	char name[I2C_NAME_SIZE];
	struct i2c_adapter *adapter;	/* the adapter we sit on	*/
	struct device dev;		/* the device structure		*/
	int irq;			/* irq issued by device		*/
	struct list_head detected;
#if IS_ENABLED(CONFIG_I2C_SLAVE)
	i2c_slave_cb_t slave_cb;	/* callback for slave mode	*/
#endif
};

i2c_register_adapter

一般的soc的i2c适配器驱动匹配到设备树节点后,都会调用i2c_register_adapter来注册适配器

1. dev_set_name(&adap->dev, "i2c-%d", adap->nr);设置适配器名字

2. device_register(&adap->dev);注册适配器设备

3. of_i2c_register_devices(adap);从设备获取并注册子节点,也即i2c从设备

static int i2c_register_adapter(struct i2c_adapter *adap)
{
	int res = -EINVAL;

	/* Can't register until after driver model init */
	if (WARN_ON(!is_registered)) {
		res = -EAGAIN;
		goto out_list;
	}

	/* Sanity checks */
	if (WARN(!adap->name[0], "i2c adapter has no name"))
		goto out_list;

	if (!adap->algo) {
		pr_err("adapter '%s': no algo supplied!\n", adap->name);
		goto out_list;
	}

	if (!adap->lock_ops)
		adap->lock_ops = &i2c_adapter_lock_ops;

	rt_mutex_init(&adap->bus_lock);
	rt_mutex_init(&adap->mux_lock);
	mutex_init(&adap->userspace_clients_lock);
	INIT_LIST_HEAD(&adap->userspace_clients);

	/* Set default timeout to 1 second if not already set */
	if (adap->timeout == 0)
		adap->timeout = HZ;

	/* register soft irqs for Host Notify */
	res = i2c_setup_host_notify_irq_domain(adap);
	if (res) {
		pr_err("adapter '%s': can't create Host Notify IRQs (%d)\n",
		       adap->name, res);
		goto out_list;
	}

	dev_set_name(&adap->dev, "i2c-%d", adap->nr);
	adap->dev.bus = &i2c_bus_type;
	adap->dev.type = &i2c_adapter_type;
	res = device_register(&adap->dev);
	if (res) {
		pr_err("adapter '%s': can't register device (%d)\n", adap->name, res);
		goto out_list;
	}

	dev_dbg(&adap->dev, "adapter [%s] registered\n", adap->name);

	pm_runtime_no_callbacks(&adap->dev);
	pm_suspend_ignore_children(&adap->dev, true);
	pm_runtime_enable(&adap->dev);

#ifdef CONFIG_I2C_COMPAT
	res = class_compat_create_link(i2c_adapter_compat_class, &adap->dev,
				       adap->dev.parent);
	if (res)
		dev_warn(&adap->dev,
			 "Failed to create compatibility class link\n");
#endif

	i2c_init_recovery(adap);

	/* create pre-declared device nodes */
	of_i2c_register_devices(adap);
	i2c_acpi_register_devices(adap);
	i2c_acpi_install_space_handler(adap);

	if (adap->nr < __i2c_first_dynamic_bus_num)
		i2c_scan_static_board_info(adap);

	/* Notify drivers */
	mutex_lock(&core_lock);
	bus_for_each_drv(&i2c_bus_type, NULL, adap, __process_new_adapter);
	mutex_unlock(&core_lock);

	return 0;

out_list:
	mutex_lock(&core_lock);
	idr_remove(&i2c_adapter_idr, adap->nr);
	mutex_unlock(&core_lock);
	return res;
}

of_i2c_register_devices

of_i2c_register_devices遍历子节点,调用of_i2c_register_device注册子设备

void of_i2c_register_devices(struct i2c_adapter *adap)
{
	struct device_node *bus, *node;
	struct i2c_client *client;

	/* Only register child devices if the adapter has a node pointer set */
	if (!adap->dev.of_node)
		return;

	dev_dbg(&adap->dev, "of_i2c: walking child nodes\n");

	bus = of_get_child_by_name(adap->dev.of_node, "i2c-bus");
	if (!bus)
		bus = of_node_get(adap->dev.of_node);

	for_each_available_child_of_node(bus, node) {
		if (of_node_test_and_set_flag(node, OF_POPULATED))
			continue;

		client = of_i2c_register_device(adap, node);
		if (IS_ERR(client)) {
			dev_warn(&adap->dev,
				 "Failed to create I2C device for %pOF\n",
				 node);
			of_node_clear_flag(node, OF_POPULATED);
		}
	}

	of_node_put(bus);
}

of_i2c_register_device

主要功能如下:

1. addr_be = of_get_property(node, "reg", &len);i2c_board_info的地址为reg属性

2. i2c_new_device(adap, &info);新建i2c_client

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 %pOF\n", node);

	if (of_modalias_node(node, info.type, sizeof(info.type)) < 0) {
		dev_err(&adap->dev, "of_i2c: modalias failure on %pOF\n",
			node);
		return ERR_PTR(-EINVAL);
	}

	addr_be = of_get_property(node, "reg", &len);
	if (!addr_be || (len < sizeof(*addr_be))) {
		dev_err(&adap->dev, "of_i2c: invalid reg on %pOF\n", node);
		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 %pOF\n",
			addr, node);
		return ERR_PTR(-EINVAL);
	}

	info.addr = addr;
	info.of_node = of_node_get(node);
	info.archdata = &dev_ad;

	if (of_property_read_bool(node, "host-notify"))
		info.flags |= I2C_CLIENT_HOST_NOTIFY;

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

	result = i2c_new_device(adap, &info);
	if (result == NULL) {
		dev_err(&adap->dev, "of_i2c: Failure registering %pOF\n", node);
		of_node_put(node);
		return ERR_PTR(-EINVAL);
	}
	return result;
}

i2c_new_device

主要功能如下:

1. client = kzalloc(sizeof *client, GFP_KERNEL);分配i2c_client

2. client->addr = info->addr;设置地址

3. i2c_dev_set_name(adap, client);设置名字

4. client->dev.bus = &i2c_bus_type;client->dev.type = &i2c_client_type;client->dev.of_node = info->of_node;初始化struct device的成员

5. device_register(&client->dev);注册设备

struct i2c_client *
i2c_new_device(struct i2c_adapter *adap, struct i2c_board_info const *info)
{
	struct i2c_client	*client;
	int			status;

	client = kzalloc(sizeof *client, GFP_KERNEL);
	if (!client)
		return NULL;

	client->adapter = adap;

	client->dev.platform_data = info->platform_data;

	if (info->archdata)
		client->dev.archdata = *info->archdata;

	client->flags = info->flags;
	client->addr = info->addr;

	client->irq = info->irq;
	if (!client->irq)
		client->irq = i2c_dev_irq_from_resources(info->resources,
							 info->num_resources);

	strlcpy(client->name, info->type, sizeof(client->name));

	status = i2c_check_addr_validity(client->addr, client->flags);
	if (status) {
		dev_err(&adap->dev, "Invalid %d-bit I2C address 0x%02hx\n",
			client->flags & I2C_CLIENT_TEN ? 10 : 7, client->addr);
		goto out_err_silent;
	}

	/* Check for address business */
	status = i2c_check_addr_busy(adap, i2c_encode_flags_to_addr(client));
	if (status)
		goto out_err;

	client->dev.parent = &client->adapter->dev;
	client->dev.bus = &i2c_bus_type;
	client->dev.type = &i2c_client_type;
	client->dev.of_node = info->of_node;
	client->dev.fwnode = info->fwnode;

	i2c_dev_set_name(adap, client);

	if (info->properties) {
		status = device_add_properties(&client->dev, info->properties);
		if (status) {
			dev_err(&adap->dev,
				"Failed to add properties to client %s: %d\n",
				client->name, status);
			goto out_err;
		}
	}

	status = device_register(&client->dev);
	if (status)
		goto out_free_props;

	dev_dbg(&adap->dev, "client [%s] registered with bus id %s\n",
		client->name, dev_name(&client->dev));

	return client;

out_free_props:
	if (info->properties)
		device_remove_properties(&client->dev);
out_err:
	dev_err(&adap->dev,
		"Failed to register i2c client %s at 0x%02x (%d)\n",
		client->name, client->addr, status);
out_err_silent:
	kfree(client);
	return NULL;
}
EXPORT_SYMBOL_GPL(i2c_new_device);

示例

驱动及设备树

从某个音频设备树节点通过of_parse_phandle获取phandle指向编解码的i2c设备树节点;并通过of_find_i2c_device_by_node获取对应的i2c_client

static int fsl_asoc_card_probe(struct platform_device *pdev)
{
    ......
    struct i2c_client *codec_dev;
    codec_np = of_parse_phandle(np, "audio-codec", 0);
	if (codec_np)
		codec_dev = of_find_i2c_device_by_node(codec_np);
	else
		codec_dev = NULL;

	asrc_np = of_parse_phandle(np, "audio-asrc", 0);
	if (asrc_np)
		asrc_pdev = of_find_device_by_node(asrc_np);
    ......
}
sound-cs42888 {
                compatible = "fsl,imx6-sabreauto-cs42888",
                        "fsl,imx-audio-cs42888";
                model = "imx-cs42888";
                audio-cpu = <&esai>;
                audio-asrc = <&asrc>; //设备树节点
                audio-codec = <&sgtl5000>;
        };

i2c4: i2c@0 {
                        #address-cells = <1>;
                        #size-cells = <0>;
                        reg = <0>;

                        sgtl5000: codec@a {
                                compatible = "fsl,sgtl5000";
                                reg = <0xa>;
                                #sound-dai-cells = <0>;
                                VDDA-supply = <&reg_sgtl5k>;
                                VDDIO-supply = <&reg_sgtl5k>;
                                clocks = <&cko2_11M>;
                                status = "okay";
                        };
                };

of_find_i2c_device_by_node

1. 通过struct device_node遍历对应总线上的device,device匹配成功后转换成i2c_client;

2. 为什么能用container_of?因为device_register(&client->dev)注册到总线的struct device的地址是i2c_new_device里kzalloc分配的地址 + device的offset

static int of_dev_node_match(struct device *dev, void *data)
{
	return dev->of_node == data;
}

/* must call put_device() when done with returned i2c_client device */
struct i2c_client *of_find_i2c_device_by_node(struct device_node *node)
{
	struct device *dev;
	struct i2c_client *client;

	dev = bus_find_device(&i2c_bus_type, NULL, node, of_dev_node_match);
	if (!dev)
		return NULL;

	client = i2c_verify_client(dev);
	if (!client)
		put_device(dev);

	return client;
}
EXPORT_SYMBOL(of_find_i2c_device_by_node);

bus_find_device

1. 从总线上找到该struct device_node对应的struct device;

2. 为什么不能用container_of?要用of_dev_node_match里的(dev->of_node == data)来匹配;因为device_node的地址是早期解析设备树分配的,不是i2c_new_device里kzalloc分配的地址 + device的offset + device_node的offset

struct device *bus_find_device(struct bus_type *bus,
			       struct device *start, void *data,
			       int (*match)(struct device *dev, void *data))
{
	struct klist_iter i;
	struct device *dev;

	if (!bus || !bus->p)
		return NULL;

	klist_iter_init_node(&bus->p->klist_devices, &i,
			     (start ? &start->p->knode_bus : NULL));
	while ((dev = next_device(&i)))
		if (match(dev, data) && get_device(dev))
			break;
	klist_iter_exit(&i);
	return dev;
}
EXPORT_SYMBOL_GPL(bus_find_device);

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-07-18 21:48:03       66 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-07-18 21:48:03       70 阅读
  3. 在Django里面运行非项目文件

    2024-07-18 21:48:03       57 阅读
  4. Python语言-面向对象

    2024-07-18 21:48:03       68 阅读

热门阅读

  1. Html_Css问答集(8)

    2024-07-18 21:48:03       18 阅读
  2. APP开发者选择苹果企业签名的理由是什么?

    2024-07-18 21:48:03       21 阅读
  3. 负载均衡轮询逻辑

    2024-07-18 21:48:03       19 阅读
  4. swift小知识点(二)

    2024-07-18 21:48:03       18 阅读
  5. Redis常见阻塞原因

    2024-07-18 21:48:03       22 阅读
  6. Pandas库学习之DataFrame.replace()函数

    2024-07-18 21:48:03       21 阅读
  7. ros2--插件

    2024-07-18 21:48:03       27 阅读
  8. 探索 Flask:从入门到精通的完整学习指南

    2024-07-18 21:48:03       22 阅读
  9. antd使用踩坑记录

    2024-07-18 21:48:03       19 阅读
  10. 数组 59.螺旋矩阵Ⅱ

    2024-07-18 21:48:03       23 阅读