【跟着韦东山学习linux设备树】内核中device_node转换为platform_device的函数调用分析

内核中device_node转换为platform_device的函数调用分析

    • of_platform_default_populate_init 函数调用过程
      • 1. start_kernel() 函数定义在init/main.c
      • 2. rest_init()函数定义在init/main.c
      • 3. kernel_init()
      • 4. kernel_init_freeable()
      • 5. do_basic_setup()
      • 6. do_initcalls()
    • of_platform_default_populate_init函数生成platform_device的过程
      • 1. of_platform_default_populate()
      • 2. of_platform_populate()
      • 3. of_platform_bus_create()
      • 4. of_platform_device_create_pdata()
      • 5. of_device_alloc()
    • I2C总线节点的处理过程:
      • I2C驱动的probe函数
      • i2c_add_numbered_adapter()
      • i2c_add_adapter()
      • __i2c_add_numbered_adapter()
      • i2c_register_adapter()
      • of_i2c_register_devices()
      • of_i2c_register_device()

of_platform_default_populate_init()函数虽然没有被明显的调用到,但是会被执行到的,作用是遍历整个device_node树,创建出对应的platform_device

of_platform_default_populate_init 函数调用过程

1. start_kernel() 函数定义在init/main.c

asmlinkage __visible void __init start_kernel(void)................
/* Do the rest non-__init'ed, we're now alive */
	arch_call_rest_init();
void __init __weak arch_call_rest_init(void)
{
	rest_init();
}

2. rest_init()函数定义在init/main.c

启动一个内核线程,然后在线程里面继续调用。


noinline void __ref rest_init(void)
{
	struct task_struct *tsk;
	int pid;
	rcu_scheduler_starting();
	/* We need to spawn init first so that it obtains pid 1, however
the init task will end up wanting to create kthreads, which, if we schedule it before we create kthreadd, will OOPS. 
我们需要首先衍生init,以便它获得pid1,然而,init任务最终会希望创建kthreads,如果我们在创建kthreadd之前对其进行调度,则会发生意外*/
	pid = kernel_thread(kernel_init, NULL, CLONE_FS);
	.............................
}

3. kernel_init()

static int __ref kernel_init(void *unused)
{
............................
	kernel_init_freeable();
.........................
}

4. kernel_init_freeable()


static noinline void __init kernel_init_freeable(void)
{
................
do_basic_setup();
................
}

5. do_basic_setup()

/*
 * Ok, the machine is now initialized. None of the devices
 * have been touched yet, but the CPU subsystem is up and
 * running, and memory and process management works.
 *
 * Now we can finally start doing some real work..
 */
static void __init do_basic_setup(void)
{
	cpuset_init_smp();
	shmem_init();
	driver_init();
	init_irq_proc();
	do_ctors();
	usermodehelper_enable();
	do_initcalls();
}

6. do_initcalls()

static void __init do_initcalls(void)
{
	int level;

	for (level = 0; level < ARRAY_SIZE(initcall_levels) - 1; level++)
		do_initcall_level(level);
}

// 比如 do_initcall_level(3)
for (fn = initcall_levels[3]; fn < initcall_levels[3+1]; fn++)
do_one_initcall(initcall_from_entry(fn));
// 就是调用"arch_initcall_sync(fn)"中定义的fn函数
以上就是of_platform_default_populate_init (drivers/of/platform.c) 被调用到过程
of_platform_default_populate_init()函数虽然没有被明显的调用到,但是会被执行到的,作用是遍历整个device_node树,创建出对应的platform_device

of_platform_default_populate_init函数生成platform_device的过程

static int __init of_platform_default_populate_init(void)
{
	struct device_node *node;

	if (!of_have_populated_dt())
		return -ENODEV;

	/*
	 * Handle certain compatibles explicitly, since we don't want to create
	 * platform_devices for every node in /reserved-memory with a
	 * "compatible",
	 */
	 // 处理一些保留的节点
	for_each_matching_node(node, reserved_mem_matches) 
		of_platform_device_create(node, NULL, NULL);
	// 处理一些特殊节点下面的子节点
	node = of_find_node_by_path("/firmware");
	if (node) {
		of_platform_populate(node, NULL, NULL, NULL);
		of_node_put(node);
	}
	//这个才是我们关心的
	/* Populate everything else. */
	of_platform_default_populate(NULL, NULL, NULL);
	return 0;
}
arch_initcall_sync(of_platform_default_populate_init);
#endif

1. of_platform_default_populate()

int of_platform_default_populate(struct device_node *root,
				 const struct of_dev_auxdata *lookup,
				 struct device *parent)
{
	return of_platform_populate(root, of_default_bus_match_table, lookup,
				    parent);
}
EXPORT_SYMBOL_GPL(of_platform_default_populate);

传入一个特殊的参数:of_default_bus_match_table

const struct of_device_id of_default_bus_match_table[] = {
	{ .compatible = "simple-bus", },
	{ .compatible = "simple-mfd", },
	{ .compatible = "isa", },
#ifdef CONFIG_ARM_AMBA
	{ .compatible = "arm,amba-bus", },
#endif /* CONFIG_ARM_AMBA */
	{} /* Empty terminated list */
};

根据我们之前的分析,只要是子节点中的属性含有"simple-bus",“simple-mfd”,“isa”,“arm,amba-bus”,这些子节点就可以转化为platform_device

2. of_platform_populate()

/**
 * of_platform_populate() - Populate platform_devices from device tree data
 * @root: parent of the first level to probe or NULL for the root of the tree
 * @matches: match table, NULL to use the default
 * @lookup: auxdata table for matching id and platform_data with device nodes
 * @parent: parent to hook devices from, NULL for toplevel
 * Similar to of_platform_bus_probe(), this function walks the device tree
 * and creates devices from nodes.  It differs in that it follows the modern
 * convention of requiring all device nodes to have a 'compatible' property,
 * and it is suitable for creating devices which are children of the root
 * node (of_platform_bus_probe will only create children of the root which
 * are selected by the @matches argument).
 * New board support should be using this function instead of
 * of_platform_bus_probe().
 * Returns 0 on success, < 0 on failure.
 */
int of_platform_populate(struct device_node *root,
			const struct of_device_id *matches,
			const struct of_dev_auxdata *lookup,
			struct device *parent)
{
	struct device_node *child;
	int rc = 0;
	// 首先得到根节点
	root = root ? of_node_get(root) : of_find_node_by_path("/");
	if (!root)
		return -EINVAL;

	pr_debug("%s()\n", __func__);
	pr_debug(" starting at: %pOF\n", root);
	//然后遍历根节点下面的每一个子节点,调用of_platform_bus_create函	//数把这些子节点当做总线来创建
	for_each_child_of_node(root, child) {
		rc = of_platform_bus_create(child, matches, lookup, parent, true);
		if (rc) {
			of_node_put(child);
			break;
		}
	}
	of_node_set_flag(root, OF_POPULATED_BUS);

	of_node_put(root);
	return rc;
}

3. of_platform_bus_create()

/**
 * of_platform_bus_create() - Create a device for a node and its children.
 * @bus: device node of the bus to instantiate
 * @matches: match table for bus nodes
 * @lookup: auxdata table for matching id and platform_data with device nodes
 * @parent: parent for new device, or NULL for top level.
 * @strict: require compatible property
 * Creates a platform_device for the provided device_node, and optionally
 * recursively create devices for all the child nodes.
 */
static int of_platform_bus_create(struct device_node *bus,
				  const struct of_device_id *matches,
				  const struct of_dev_auxdata *lookup,
				  struct device *parent, bool strict)
{
	const struct of_dev_auxdata *auxdata;
	struct device_node *child;
	struct platform_device *dev;
	const char *bus_id = NULL;
	void *platform_data = NULL;
	int rc = 0;

	/* Make sure it has a compatible property */
	//如果没有compatible属性,就会返回空,不创建platform_device
	if (strict && (!of_get_property(bus, "compatible", NULL))) {
		pr_debug("%s() - skipping %pOF, no compatible prop\n",
			 __func__, bus);
		return 0;
	}
......................................................
//针对根节点下能转化出platform_device的一级子节点,创建出platform_device
	dev = of_platform_device_create_pdata(bus, bus_id, platform_data, parent);
	// 处理一级子节点(比如I2C)下的子节点
	//先检查先节点能否转换为platform_device
	if (!dev || !of_match_node(matches, bus)) 
		return 0;

	for_each_child_of_node(bus, child) {
		pr_debug("   create child: %pOF\n", child);
		rc = of_platform_bus_create(child, matches, lookup, &dev->dev, strict);// 处理它的子节点, of_platform_bus_create是一个递归调用
		if (rc) {
			of_node_put(child);
			break;
		}
	}
	of_node_set_flag(bus, OF_POPULATED_BUS);
	return rc;
}

【跟着韦东山学习linux设备树】内核中device_node转换为platform_device的函数调用分析_第1张图片

4. of_platform_device_create_pdata()

/**
 * of_platform_device_create_pdata - Alloc, initialize and register an of_device
 * @np: pointer to node to create device for
 * @bus_id: name to assign device
 * @platform_data: pointer to populate platform_data pointer with
 * @parent: Linux device model parent device.
 * Returns pointer to created platform device, or NULL if a device was not registered.  Unavailable devices will not get registered.
 */
static struct platform_device *of_platform_device_create_pdata(
					struct device_node *np,
					const char *bus_id,
					void *platform_data,
					struct device *parent)
{
	struct platform_device *dev;

	if (!of_device_is_available(np) ||
	    of_node_test_and_set_flag(np, OF_POPULATED))
		return NULL;

	dev = of_device_alloc(np, bus_id, parent);
	if (!dev)
		goto err_clear_flag;

	dev->dev.coherent_dma_mask = DMA_BIT_MASK(32);
	if (!dev->dev.dma_mask)
		dev->dev.dma_mask = &dev->dev.coherent_dma_mask;
	dev->dev.bus = &platform_bus_type;
	dev->dev.platform_data = platform_data;
	of_msi_configure(&dev->dev, dev->dev.of_node);

	if (of_device_add(dev) != 0) {
		platform_device_put(dev);
		goto err_clear_flag;
	}

	return dev;

err_clear_flag:
	of_node_clear_flag(np, OF_POPULATED);
	return NULL;
}

5. of_device_alloc()

/**
 * of_device_alloc - Allocate and initialize an of_device
 * @np: device node to assign to device
 * @bus_id: Name to assign to the device.  May be null to use default name.
 * @parent: Parent device.
 */
struct platform_device *of_device_alloc(struct device_node *np,
				  const char *bus_id,
				  struct device *parent)
{
	struct platform_device *dev;
	int rc, i, num_reg = 0, num_irq;
	struct resource *res, temp_res;

	dev = platform_device_alloc("", PLATFORM_DEVID_NONE);
	if (!dev)
		return NULL;

	/* count the io and irq resources */
	//首先计算IO,内存,还有中断资源
	while (of_address_to_resource(np, num_reg, &temp_res) == 0)
		num_reg++;
	num_irq = of_irq_count(np);
	// 计算出来之后,再来分配resource数组
	/* Populate the resource table */
	if (num_irq || num_reg) {
		res = kcalloc(num_irq + num_reg, sizeof(*res), GFP_KERNEL);
		if (!res) {
			platform_device_put(dev);
			return NULL;
		}
		dev->num_resources = num_reg + num_irq;
		dev->resource = res;
		for (i = 0; i < num_reg; i++, res++) {
		// 然后把这些地址转化成resource结构体
			rc = of_address_to_resource(np, i, res);
			WARN_ON(rc);
		}
		// 把这些中断转化成resource结构体,这里比较复杂,后面再学习
		if (of_irq_to_resource_table(np, res, num_irq) != num_irq)
			pr_debug("not all legacy IRQ resources mapped for %pOFn\n", np);
	}

	dev->dev.of_node = of_node_get(np);
	dev->dev.fwnode = &np->fwnode;
	dev->dev.parent = parent ? : &platform_bus;

	if (bus_id)
		dev_set_name(&dev->dev, "%s", bus_id);
	else
		of_device_make_bus_id(&dev->dev);

	return dev;
}
EXPORT_SYMBOL(of_device_alloc);

会从device_node中的reg,interrupts属性获取参数来构造资源。

I2C总线节点的处理过程:

//i2c节点一般表示i2c控制器, 它会被转换为platform_device, 在内核中有对应的platform_driver; platform_driver的probe函数中会调用

当我们在设备树里面,构造了一个 i2c节点,如果节点中的compatible属性(compatible = “goodix,gt1x”;)和驱动中的i2c_driver.id_table.name匹配成功的话,则probe函数就会被调用。 我们以drivers/i2c/busses/i2c-s3c24410.c中的probe函数为例,学习一下。

I2C驱动的probe函数

static int s3c24xx_i2c_probe(struct platform_device *pdev)
{
	................................
	// 会注册一个I2C总线控制器
	ret = i2c_add_numbered_adapter(&i2c->adap);
	if (ret < 0) {
		pm_runtime_disable(&pdev->dev);
		s3c24xx_i2c_deregister_cpufreq(i2c);
		clk_unprepare(i2c->clk);
		return ret;
	}

	dev_info(&pdev->dev, "%s: S3C I2C adapter\n", dev_name(&i2c->adap.dev));
	return 0;
}

i2c_add_numbered_adapter()

/**
 * i2c_add_numbered_adapter - declare i2c adapter, use static bus number
 * @adap: the adapter to register (with adap->nr initialized)
 * Context: can sleep
 *
 * This routine is used to declare an I2C adapter when its bus number
 matters.  For example, use it for I2C adapters from system-on-chip CPUs, or otherwise built in to the system's mainboard, and where i2c_board_info is used to properly configure I2C devices.
 这个例程用于声明一个I2C适配器,当它的总线号码很重要。例如,将它用于来自SOC的I2C适配器,或者内置到系统主板,其中i2c_board_info用于正确配置I2C设备。
 * If the requested bus number is set to -1, then this function will behave identically to i2c_add_adapter, and will dynamically assign a bus number.
 *如果请求的总线号设置为-1,那么这个函数的行为将与i2c_add_adapter相同,并将动态分配一个总线号。
 * If no devices have pre-been declared for this bus, then be sure to
 * register the adapter before any dynamically allocated ones.  Otherwise the required bus ID may not be available.
 如果这个总线没有预先声明设备,那么请确保在动态分配适配器之前注册适配器。否则,所需的总线ID可能不可用。
 * When this returns zero, the specified adapter became available for
clients using the bus number provided in adap->nr.  Also, the table of I2C devices pre-declared using i2c_register_board_info() is scanned,and the appropriate driver model device nodes are created.  Otherwise, a negative errno value is returned.
当返回0时,指定的适配器可用使用adap->nr中提供的总线号的客户。另外,扫描使用i2c_register_board_info()预先声明的I2C设备表,并创建适当的驱动模型设备节点。否则,将返回负的errno值。
 */
int i2c_add_numbered_adapter(struct i2c_adapter *adap)
{
	if (adap->nr == -1) /* -1 means dynamically assign bus id */
		return i2c_add_adapter(adap);

	return __i2c_add_numbered_adapter(adap);
}
EXPORT_SYMBOL_GPL(i2c_add_numbered_adapter);

i2c_add_adapter()

/**
 * i2c_add_adapter - declare i2c adapter, use dynamic bus number
 * @adapter: the adapter to add
 * Context: can sleep
 * This routine is used to declare an I2C adapter when its bus number
 * doesn't matter or when its bus number is specified by an dt alias.
 * Examples of bases when the bus number doesn't matter: I2C adapters
 * dynamically added by USB links or PCI plugin cards.
 *这个例程用于声明一个I2C适配器,当它的总线号码由dt别名指定时,或者并不重要。当总线号码无关紧要时的示例:由USB链接或PCI插件卡动态添加I2C适配器
 * When this returns zero, a new bus number was allocated and stored
 * in adap->nr, and the specified adapter became available for clients. Otherwise, a negative errno value is returned.
 当它返回0时,就分配并存储了一个新的总线号存储在adap->nr中,指定的适配器对客户端可用。否则,将返回负的errno值。
 */
int i2c_add_adapter(struct i2c_adapter *adapter)
{
	struct device *dev = &adapter->dev;
	int id;

	if (dev->of_node) {
		id = of_alias_get_id(dev->of_node, "i2c");
		if (id >= 0) {
			adapter->nr = id;
			return __i2c_add_numbered_adapter(adapter);
		}
	}

	mutex_lock(&core_lock);
	id = idr_alloc(&i2c_adapter_idr, adapter,
		       __i2c_first_dynamic_bus_num, 0, GFP_KERNEL);
	mutex_unlock(&core_lock);
	if (WARN(id < 0, "couldn't get idr"))
		return id;
	adapter->nr = id;
	return i2c_register_adapter(adapter);
}
EXPORT_SYMBOL(i2c_add_adapter);

__i2c_add_numbered_adapter()

/**
 * __i2c_add_numbered_adapter - i2c_add_numbered_adapter where nr is never -1
 * @adap: the adapter to register (with adap->nr initialized)
 * Context: can sleep
 *
 * See i2c_add_numbered_adapter() for details.
 */
static int __i2c_add_numbered_adapter(struct i2c_adapter *adap)
{
	int id;

	mutex_lock(&core_lock);
	id = idr_alloc(&i2c_adapter_idr, adap, adap->nr, adap->nr + 1, GFP_KERNEL);
	mutex_unlock(&core_lock);
	if (WARN(id < 0, "couldn't get idr"))
		return id == -ENOSPC ? -EBUSY : id;

	return i2c_register_adapter(adap);
}

i2c_register_adapter()

static int i2c_register_adapter(struct i2c_adapter *adap)
{
	......................................
	/* create pre-declared device nodes */
	of_i2c_register_devices(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_reg:
	init_completion(&adap->dev_released);
	device_unregister(&adap->dev);
	wait_for_completion(&adap->dev_released);
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_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);
//对这个总线上的每一个子节点,都会调用of_i2c_register_device()函数
	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_err(&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()

该函数创建一个i2c_client

static struct i2c_client *of_i2c_register_device(struct i2c_adapter *adap,
						 struct device_node *node)
{
	struct i2c_client *client;
	struct i2c_board_info info;
	int ret;

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

	ret = of_i2c_get_board_info(&adap->dev, node, &info);
	if (ret)
		return ERR_PTR(ret);

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

以上过程就是i2c节点下的子节点的处理过程:子节点交给i2c驱动的probe函数来转换为i2c_client。设备树中的spi的子节点的处理过程与之类似,就不再赘述,下面简要罗列设备树中的spi的子节点的处理过程.

SPI总线节点的处理过程:
   /spi节点一般表示spi控制器, 它会被转换为platform_device, 在内核中有对应的platform_driver;
   platform_driver的probe函数中会调用spi_register_master, 即spi_register_controller:
   
   spi_register_controller        // drivers/spi/spi.c
        of_register_spi_devices   // drivers/spi/spi.c
            for_each_available_child_of_node(ctlr->dev.of_node, nc) {
                spi = of_register_spi_device(ctlr, nc);  // 设备树中的spi子节点被转换为spi_device
                                spi = spi_alloc_device(ctlr);
                                rc = of_spi_parse_dt(ctlr, spi, nc);
                                rc = spi_add_device(spi);
            }
                    

你可能感兴趣的:(学习笔记,个人技术成长记录,linux驱动)