.. SPDX-License-Identifier: GPL-2.0
.. include:: ../disclaimer-zh_CN.rst

:Original: Documentation/devicetree/overlay-notes.rst

:翻译:

 司延腾 Yanteng Si <siyanteng@loongson.cn>

:校译:

==============
设备树覆盖说明
==============

本文档描述了drivers/of/overlay.c中的内核内设备树覆盖功能的实现,是
Documentation/devicetree/dynamic-resolution-notes.rst[1]的配套文档。

覆盖如何工作
------------

设备树覆盖的目的是修改内核的实时树,并使修改以反映变化的方式影响内核的状态。
由于内核主要处理的是设备,任何新的设备节点如果导致一个活动的设备,就应该创建它,
而如果设备节点被禁用或被全部删除,受影响的设备应该被取消注册。

让我们举个例子,我们有一个foo板,它的基本树形图如下::

    ---- foo.dts ---------------------------------------------------------------
	/* FOO平台 */
	/dts-v1/;
	/ {
		compatible = "corp,foo";

		/* 共享的资源 */
		res: res {
		};

		/* 芯片上的外围设备 */
		ocp: ocp {
			/* 总是被实例化的外围设备 */
			peripheral1 { ... };
		};
	};
    ---- foo.dts ---------------------------------------------------------------

覆盖bar.dts,
::

    ---- bar.dts - 按标签覆盖目标位置 ----------------------------
	/dts-v1/;
	/插件/;
	&ocp {
		/* bar外围 */
		bar {
			compatible = "corp,bar";
			... /* 各种属性和子节点 */
		};
	};
    ---- bar.dts ---------------------------------------------------------------

当加载(并按照[1]中描述的方式解决)时,应该产生foo+bar.dts::

    ---- foo+bar.dts -----------------------------------------------------------
	/* FOO平台 + bar外围 */
	/ {
		compatible = "corp,foo";

		/* 共享资源 */
		res: res {
		};

		/* 芯片上的外围设备 */
		ocp: ocp {
			/* 总是被实例化的外围设备 */
			peripheral1 { ... };

			/* bar外围 */
			bar {
				compatible = "corp,bar";
				... /* 各种属性和子节点 */
			};
		};
	};
    ---- foo+bar.dts -----------------------------------------------------------

作为覆盖的结果,已经创建了一个新的设备节点(bar),因此将注册一个bar平台设备,
如果加载了匹配的设备驱动程序,将按预期创建设备。

如果基础DT不是用-@选项编译的,那么“&ocp”标签将不能用于将覆盖节点解析到基础
DT中的适当位置。在这种情况下,可以提供目标路径。通过标签的目标位置的语法是比
较好的,因为不管标签在DT中出现在哪里,覆盖都可以被应用到任何包含标签的基础DT上。

上面的bar.dts例子被修改为使用目标路径语法,即为::

    ---- bar.dts - 通过明确的路径覆盖目标位置 --------------------
	/dts-v1/;
	/插件/;
	&{/ocp} {
		/* bar外围 */
		bar {
			compatible = "corp,bar";
			... /* 各种外围设备和子节点 */
		}
	};
    ---- bar.dts ---------------------------------------------------------------


内核中关于覆盖的API
-------------------

该API相当容易使用。

1) 调用of_overlay_fdt_apply()来创建和应用一个覆盖的变更集。返回值是一个
   错误或一个识别这个覆盖的cookie。

2) 调用of_overlay_remove()来删除和清理先前通过调用of_overlay_fdt_apply()
   而创建的覆盖变更集。不允许删除一个被另一个覆盖的覆盖变化集。

最后,如果你需要一次性删除所有的覆盖,只需调用of_overlay_remove_all(),
它将以正确的顺序删除每一个覆盖。

你可以选择注册在覆盖操作中被调用的通知器。详见
of_overlay_notifier_register/unregister和enum of_overlay_notify_action。

OF_OVERLAY_PRE_APPLY、OF_OVERLAY_POST_APPLY或OF_OVERLAY_PRE_REMOVE
的通知器回调可以存储指向覆盖层中的设备树节点或其内容的指针,但这些指针不能持
续到OF_OVERLAY_POST_REMOVE的通知器回调。在OF_OVERLAY_POST_REMOVE通
知器被调用后,包含覆盖层的内存将被kfree()ed。请注意,即使OF_OVERLAY_POST_REMOVE
的通知器返回错误,内存也会被kfree()ed。

drivers/of/dynamic.c中的变更集通知器是第二种类型的通知器,可以通过应用或移除
覆盖层来触发。这些通知器不允许在覆盖层或其内容中存储指向设备树节点的指针。当包含
覆盖层的内存因移除覆盖层而被释放时,覆盖层代码并不能防止这类指针仍然有效。

任何其他保留指向覆盖层节点或数据的指针的代码都被认为是一个错误,因为在移除覆盖层
后,该指针将指向已释放的内存。

覆盖层的用户必须特别注意系统上发生的整体操作,以确保其他内核代码不保留任何指向覆
盖层节点或数据的指针。任何无意中使用这种指针的例子是,如果一个驱动或子系统模块在
应用了覆盖后被加载,并且该驱动或子系统扫描了整个设备树或其大部分,包括覆盖节点。