一、of_parse_thermal_zones()

       接着上一节的代码流程,of_parse_thermal_zones() 这个函数粉墨登场,可以说读懂了这个函数,Thermal框架就明白了一大半。那这个函数是干什么用的呢?做了些什么事情呢?函数体有点长,我们拆分来解析:

/**
 * of_parse_thermal_zones - parse device tree thermal data
 *
 * Initialization function that can be called by machine initialization
 * code to parse thermal data and populate the thermal framework
 * with hardware thermal zones info. This function only parses thermal zones.
 * Cooling devices and sensor devices nodes are supposed to be parsed
 * by their respective drivers.
 *
 * Return: 0 on success, proper error code otherwise
 *
 */
int __init of_parse_thermal_zones(void)
{
	struct device_node *np, *child;
	struct __thermal_zone *tz;
	struct thermal_zone_device_ops *ops;

	np = of_find_node_by_name(NULL, "thermal-zones");
	if (!np) {
		pr_debug("unable to find thermal zones\n");
		/* 即便dts里没有thermal-zones结点也允许thermal core跑起来 */
		return 0; /* Run successfully on systems without thermal DT */
	}

	......
}

       需要指出的是,这个函数位于 of-thermal.c 里而不是 thermal_core.c。我们知道,关于 dts 解析的一系列 API 都是 of_xxx() 的形式,所以根据代码位置和函数名,也可以猜到这个函数应该是要解析 dts 里面的东西。首先看注释,说是用来解析设备树 thermal 数据。具体来说,这是一个初始化函数,被用来解析 thermal 数据以及根据硬件 thermal zones 信息来填充 thermal 框架。个人认为 thermal zones 是整个 thermal 框架中最不容易理解的一个概念,本人在代码分析阶段也是反复阅读分析才明白。所谓 zone,就是区域的意思,thermal zone 可以认为是一个温控系统的抽象,比如概述里描述的空调制冷的例子。在这个温控系统里,有温感 sensor,降温设备,温控策略等信息,而 Thermal core 就负责解析出这些信息,并使之有机的 work 起来,各司其职。

       还记得概述中最后的 dts 结点吗?我们的分析就是从这个 dts 结点开始。既然要解析 dts 结点,那就得先找到这个结点,这就是 of_parse_thermal_zones() 里做的第一件事,通过 of_find_node_by_name() 找到该结点。注意返回值检查的代码块,打印提示 log 后返回的是 0 而不是 error code,return 0 后面的注释说即便没有 thermal TD 也允许 thermal core 跑起来。没有初始化也能正常 work 起来?根据本人的分析和理解,这是有问题的,因为后面 PM notifier 回调 thermal_pm_notify() 中会去调用 thermal_zone_device_update() 去更新温度及相关信息,而这个函数又进一步会去调用 delayed_work 的回调,但是这个delayed_work 并没有初始化。这个问题先抛出来,感兴趣的读者等代码分析完了可以跟踪代码分析看看。

       接下来我们分析是如何找到 thermal_zones 结点的:

/**
 * of_find_node_by_name - Find a node by its "name" property
 * @from: The node to start searching from or NULL, the node
 *     you pass will not be searched, only the next one
 *     will; typically, you pass what the previous call
 *     returned. of_node_put() will be called on it
 * @name: The name string to match against
 *
 * Returns a node pointer with refcount incremented, use
 * of_node_put() on it when done.
 */
struct device_node *of_find_node_by_name(struct device_node *from,
	const char *name)
{
	struct device_node *np;
	unsigned long flags;

	raw_spin_lock_irqsave(&devtree_lock, flags);
	for_each_of_allnodes_from(from, np)
		if (np->name && (of_node_cmp(np->name, name) == 0)
		    && of_node_get(np))
			break;
	of_node_put(from);
	raw_spin_unlock_irqrestore(&devtree_lock, flags);

	return np;
}
EXPORT_SYMBOL(of_find_node_by_name);

首先对临界区上锁,用的是自旋锁,而且是关掉了中断,这样运行这段代码的 cpu 上就不会被中断打断。自旋锁也属于内核基础设施之一,关于它的更多细节,计划以后开专题来研究。for_each_of_allnodes_from(),根据名字也可以猜个七七八八,遍历所有的 dts 结点,并根据传进来的 name 实施匹配。如果匹配上就 break 跳出循环并返回该结点。

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Logo

更多推荐