Linux内核根据DTS创建设备过程分析(Android 5.1)

 我们从MACHINE_START开始分析。搜索有关宏定义MACHINE_START的所有文件发现:该宏定义是在"kernel\arch\arm\include\asm\mach"目录的"arch.h",除此之外,其他文件都为这个宏定义的使用文件,形如在目录"kernel\arch\arm\mach-sc"下,名为"board-sp7731gea_hd.c"的文件。

static const char *sprd_boards_compat[] __initdata = {
	"sprd,sp8835eb",
	NULL,
};
MACHINE_START(SCPHONE, "sc8830")
	.smp		= smp_ops(sprd_smp_ops),
	.reserve	= sci_reserve,
	.map_io		= sci_map_io,
	.init_irq	= sci_init_irq,
	.init_time	= sprd_init_time,
	.init_machine	= sc8830_init_machine,
	.init_late	= sc8830_init_late,
	.dt_compat	= sprd_boards_compat,
MACHINE_END

 其中dt_compat这个的值要跟dts里面的compatible一致。创建设备的是sc8830_init_machine这个函数,我们看这个函数里调用的of_platform_populate函数。

static void __init sc8830_init_machine(void)
{
	printk("sci get chip id = 0x%x\n",__sci_get_chip_id());

	//sci_adc_init((void __iomem *)ADC_BASE);
	sci_adc_init();
	sci_regulator_init();
	of_sprd_default_bus_lookup[0].phys_addr = 0x20300000;
	of_sprd_default_bus_lookup[1].phys_addr = 0x20400000;
	of_sprd_default_bus_lookup[2].phys_addr = 0x20500000;
	of_sprd_default_bus_lookup[3].phys_addr = 0x20600000;
	of_platform_populate(NULL, of_sprd_default_bus_match_table, of_sprd_default_bus_lookup, NULL);
	sprd_sr2351_vddwpa_ctrl_power_register();
}

 核心工作是用for_each_child_of_node函数遍历根设备节点下的所有的子节点,并执行of_platform_bus_create函数对每一个子节点进行平台总线设备创建

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;

	for_each_child_of_node(root, child) {
		rc = of_platform_bus_create(child, matches, lookup, parent, true);
		if (rc)
			break;
	}

	of_node_put(root);
	return rc;
}
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 */
	if (strict && (!of_get_property(bus, "compatible", NULL))) {
		pr_debug("%s() - skipping %s, no compatible prop\n",
			 __func__, bus->full_name);
		return 0;
	}

	auxdata = of_dev_lookup(lookup, bus);
	if (auxdata) {
		bus_id = auxdata->name;
		platform_data = auxdata->platform_data;
	}

	if (of_device_is_compatible(bus, "arm,primecell")) {
		of_amba_device_create(bus, bus_id, platform_data, parent);
		return 0;
	}

	dev = of_platform_device_create_pdata(bus, bus_id, platform_data, parent);
	if (!dev || !of_match_node(matches, bus))
		return 0;

	for_each_child_of_node(bus, child) {
		pr_debug("   create child: %s\n", child->full_name);
		rc = of_platform_bus_create(child, matches, lookup, &dev->dev, strict);
		if (rc) {
			of_node_put(child);
			break;
		}
	}
	return rc;
}

平台总线设备创建的主要工作流程是,先通过of_get_property函数检查该平台总线设备节点否具有compatible属性,通过后开始执行of_platform_device_create_pdata函数真正的创建平台设备内容,到这里其实就已经算是完成了一个设备节点从匹配识别到最终创建出设备内容的完整流程了,但是了解DTC文件的朋友知道,这个设备节点下往往还会有其他的设备的子节点,然后子节点的子节点还有设备节点......等等,以此类推组成的一个庞大的树形分支的设备树结构,那怎么办得呢?往下看就知道了,其实很简单,管它有多少个设备的节点,都调用到我们这里把设备内容真正的创建出来不就解决了吗?是的,的确如此,接下来再使用for_each_child_of_node函数遍历当前平台总线设备节点的所有子节点,递归式的执行of_platform_bus_create函数进行每一个平台总线设备的创建,其中每完成一个创建,调用of_node_put函数释放当前节点的内存.分析到这里,我对递归的用法算是理解的更深刻一点了

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))
		return NULL;

	dev = of_device_alloc(np, bus_id, parent);
	if (!dev)
		return NULL;

#if defined(CONFIG_MICROBLAZE)
	dev->archdata.dma_mask = 0xffffffffUL;
#endif
	dev->dev.coherent_dma_mask = DMA_BIT_MASK(32);
	dev->dev.bus = &platform_bus_type;
	dev->dev.platform_data = platform_data;

	/* We do not fill the DMA ops for platform devices by default.
	 * This is currently the responsibility of the platform code
	 * to do such, possibly using a device notifier
	 */

	if (of_device_add(dev) != 0) {
		platform_device_put(dev);
		return NULL;
	}

	return dev;
}

其中主要是,通过of_device_is_available函数检查设备节点是否有效,然后执行of_device_alloc函数分配设备内存,有了设备后,紧接着对设备的一部分内容进行初始化巴拉巴拉,最终调用of_device_add函数往内核中添加设备.

再看看是如何分配设备内容的of_device_alloc函数:

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("", -1);
	if (!dev)
		return NULL;

	/* count the io and irq resources */
	if (of_can_translate_address(np))
		while (of_address_to_resource(np, num_reg, &temp_res) == 0)
			num_reg++;
	num_irq = of_irq_count(np);

	/* Populate the resource table */
	if (num_irq || num_reg) {
		res = kzalloc(sizeof(*res) * (num_irq + num_reg), 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++) {
			rc = of_address_to_resource(np, i, res);
			WARN_ON(rc);
		}
		WARN_ON(of_irq_to_resource_table(np, res, num_irq) != num_irq);
	}

	dev->dev.of_node = of_node_get(np);
#if defined(CONFIG_MICROBLAZE)
	dev->dev.dma_mask = &dev->archdata.dma_mask;
#endif
	dev->dev.parent = parent;

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

	return dev;
}

首先调用platform_device_alloc函数为定义的platform_device结构体类型的设备的分配内存,接下来分别调用of_address_to_resource和of_irq_count两个函数统计该设备使用的地址和中断资源个数,如果有上述资源的存在则分别调用of_address_to_resource和of_irq_to_resource_table两个函数对设备进行地址和中断资源分配,完成上述设备初始化工作后紧接着更进一步初始化,比如调用of_node_get函数初始化设备所属设备树节点,最终执行dev_set_name或者of_device_make_bus_id函数来设置设备的名字或者设置设备总线ID。
 

你可能感兴趣的:(linux)