mirror of
https://github.com/torvalds/linux.git
synced 2024-11-11 06:31:49 +00:00
DeviceTree updates for 4.10:
- Add various vendor prefixes. - Fix NUMA node handling when "numa=off" is passed on kernel command line. - Coding style Clean-up of overlay handling code. - DocBook fixes in DT platform driver code - Altera SoCFPGA binding addtions for freeze bridge, arria10 FPGA manager and FPGA bridges. - Couple of printk message fixes. -----BEGIN PGP SIGNATURE----- iQItBAABCAAXBQJYUqo6EBxyb2JoQGtlcm5lbC5vcmcACgkQ+vtdtY28YcMHiw// WIJH6XF8PkAEkITxChwb3kJ1y7PCtR7YmncMvnMRh9kIcyPwFjiDRqUz/0MiTHgD UJsFtX6057GWDd0mlUrQ8iFFlzdGLl2Xw4u6sr4bC7bGVK0Nj9QX+xrFmXCLtGj0 wYkXIJXDXmLQJPKhG5np5jBYfJIaYr5FcNCwdOEljDDauHAg6f2hvgOby1X8FCOS oC3LCqvx4SWp9EcQAvOkKAJazsIlSAynb7EFwTaKo0RHJPzX/Uhp7RmyvAgeVZVE a2GS+YwboD3PMa7cjC3R7FwBXhuM/YQLLWfgSGwvr8nADP4a/iZNzJt1H0rU1LKP ukgSP5m58bt713H7Gr6OJ1ygAFmvFAzye+mXwHVxwqg3BFp141LCKoD0A2RO+zH8 5cma2OTl9bQSWxtKJyLm4y9/QegaThyM/eZyk5j6XOjIEtlTS0h8H3ZHbU55k1HC 7MJrnoAb3z5vGhxPplF1MOeGswz5vLME0e74Po0Nt8X7sdpj7zVWBL8AZh+Xyw2O d6oz9xYQVgJ5mqnnXXTWSqNfTM82+aUZV6SREaOJ1GjSy3pfd2s85N1oB8oIOb+o gRNYibbv/EvaOBqADwQoRWRTIW+0Vtf/vY1xVFB6Wd8XO2a/dmHZJ95jaPlAfwf3 l5EqgsHP1kirkNYgCAUSpCkPf61gA+ZAWxjlfrARmCg= =1Sex -----END PGP SIGNATURE----- Merge tag 'devicetree-for-4.10' of git://git.kernel.org/pub/scm/linux/kernel/git/robh/linux Pull DeviceTree updates from Rob Herring: - add various vendor prefixes. - fix NUMA node handling when "numa=off" is passed on kernel command line. - coding style Clean-up of overlay handling code. - DocBook fixes in DT platform driver code - Altera SoCFPGA binding addtions for freeze bridge, arria10 FPGA manager and FPGA bridges. - a couple of printk message fixes. * tag 'devicetree-for-4.10' of git://git.kernel.org/pub/scm/linux/kernel/git/robh/linux: (33 commits) dt: pwm: bcm2835: fix typo in clocks property name devicetree: add vendor prefix for National Instruments Revert "of: base: add support to get machine model name" of: Fix issue where code would fall through to error case. drivers/of: fix missing pr_cont()s in of_print_phandle_args devicetree: bindings: Add vendor prefix for Oki devicetree: bindings: Add vendor prefix for Andes Technology Corporation dt-bindings: add MYIR Tech hardware vendor prefix add bindings document for altera freeze bridge ARM: socfpga: add bindings doc for arria10 fpga manager ARM: socfpga: add bindings document for fpga bridge drivers of: base: add support to get machine model name of/platform: clarify of_find_device_by_node refcounting of/platform: fix of_platform_device_destroy comment of: Remove unused variable overlay_symbols of: Move setting of pointer to beside test for non-null of: Add back an error message, restructured of: Update comments to reflect changes and increase clarity of: Remove redundant size check of: Update structure of code to be clearer, also remove BUG_ON() ...
This commit is contained in:
commit
6df8b74b17
@ -148,11 +148,12 @@ Example:
|
||||
|
||||
/dts-v1/;
|
||||
#include <dt-bindings/interrupt-controller/irq.h>
|
||||
#include "skeleton.dtsi"
|
||||
|
||||
/ {
|
||||
model = "ARM RealView PB1176 with device tree";
|
||||
compatible = "arm,realview-pb1176";
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
|
||||
soc {
|
||||
#address-cells = <1>;
|
||||
|
@ -0,0 +1,16 @@
|
||||
Altera FPGA To SDRAM Bridge Driver
|
||||
|
||||
Required properties:
|
||||
- compatible : Should contain "altr,socfpga-fpga2sdram-bridge"
|
||||
|
||||
Optional properties:
|
||||
- bridge-enable : 0 if driver should disable bridge at startup
|
||||
1 if driver should enable bridge at startup
|
||||
Default is to leave bridge in current state.
|
||||
|
||||
Example:
|
||||
fpga_bridge3: fpga-bridge@ffc25080 {
|
||||
compatible = "altr,socfpga-fpga2sdram-bridge";
|
||||
reg = <0xffc25080 0x4>;
|
||||
bridge-enable = <0>;
|
||||
};
|
@ -0,0 +1,23 @@
|
||||
Altera Freeze Bridge Controller Driver
|
||||
|
||||
The Altera Freeze Bridge Controller manages one or more freeze bridges.
|
||||
The controller can freeze/disable the bridges which prevents signal
|
||||
changes from passing through the bridge. The controller can also
|
||||
unfreeze/enable the bridges which allows traffic to pass through the
|
||||
bridge normally.
|
||||
|
||||
Required properties:
|
||||
- compatible : Should contain "altr,freeze-bridge-controller"
|
||||
- regs : base address and size for freeze bridge module
|
||||
|
||||
Optional properties:
|
||||
- bridge-enable : 0 if driver should disable bridge at startup
|
||||
1 if driver should enable bridge at startup
|
||||
Default is to leave bridge in current state.
|
||||
|
||||
Example:
|
||||
freeze-controller@100000450 {
|
||||
compatible = "altr,freeze-bridge-controller";
|
||||
regs = <0x1000 0x10>;
|
||||
bridge-enable = <0>;
|
||||
};
|
@ -0,0 +1,39 @@
|
||||
Altera FPGA/HPS Bridge Driver
|
||||
|
||||
Required properties:
|
||||
- regs : base address and size for AXI bridge module
|
||||
- compatible : Should contain one of:
|
||||
"altr,socfpga-lwhps2fpga-bridge",
|
||||
"altr,socfpga-hps2fpga-bridge", or
|
||||
"altr,socfpga-fpga2hps-bridge"
|
||||
- resets : Phandle and reset specifier for this bridge's reset
|
||||
- clocks : Clocks used by this module.
|
||||
|
||||
Optional properties:
|
||||
- bridge-enable : 0 if driver should disable bridge at startup.
|
||||
1 if driver should enable bridge at startup.
|
||||
Default is to leave bridge in its current state.
|
||||
|
||||
Example:
|
||||
fpga_bridge0: fpga-bridge@ff400000 {
|
||||
compatible = "altr,socfpga-lwhps2fpga-bridge";
|
||||
reg = <0xff400000 0x100000>;
|
||||
resets = <&rst LWHPS2FPGA_RESET>;
|
||||
clocks = <&l4_main_clk>;
|
||||
bridge-enable = <0>;
|
||||
};
|
||||
|
||||
fpga_bridge1: fpga-bridge@ff500000 {
|
||||
compatible = "altr,socfpga-hps2fpga-bridge";
|
||||
reg = <0xff500000 0x10000>;
|
||||
resets = <&rst HPS2FPGA_RESET>;
|
||||
clocks = <&l4_main_clk>;
|
||||
bridge-enable = <1>;
|
||||
};
|
||||
|
||||
fpga_bridge2: fpga-bridge@ff600000 {
|
||||
compatible = "altr,socfpga-fpga2hps-bridge";
|
||||
reg = <0xff600000 0x100000>;
|
||||
resets = <&rst FPGA2HPS_RESET>;
|
||||
clocks = <&l4_main_clk>;
|
||||
};
|
@ -0,0 +1,19 @@
|
||||
Altera SOCFPGA Arria10 FPGA Manager
|
||||
|
||||
Required properties:
|
||||
- compatible : should contain "altr,socfpga-a10-fpga-mgr"
|
||||
- reg : base address and size for memory mapped io.
|
||||
- The first index is for FPGA manager register access.
|
||||
- The second index is for writing FPGA configuration data.
|
||||
- resets : Phandle and reset specifier for the device's reset.
|
||||
- clocks : Clocks used by the device.
|
||||
|
||||
Example:
|
||||
|
||||
fpga_mgr: fpga-mgr@ffd03000 {
|
||||
compatible = "altr,socfpga-a10-fpga-mgr";
|
||||
reg = <0xffd03000 0x100
|
||||
0xffcfe400 0x20>;
|
||||
clocks = <&l4_mp_clk>;
|
||||
resets = <&rst FPGAMGR_RESET>;
|
||||
};
|
@ -3,7 +3,7 @@ BCM2835 PWM controller (Raspberry Pi controller)
|
||||
Required properties:
|
||||
- compatible: should be "brcm,bcm2835-pwm"
|
||||
- reg: physical base address and length of the controller's registers
|
||||
- clock: This clock defines the base clock frequency of the PWM hardware
|
||||
- clocks: This clock defines the base clock frequency of the PWM hardware
|
||||
system, the period and the duty_cycle of the PWM signal is a multiple of
|
||||
the base period.
|
||||
- #pwm-cells: Should be 2. See pwm.txt in this directory for a description of
|
||||
|
@ -24,9 +24,11 @@ ampire Ampire Co., Ltd.
|
||||
ams AMS AG
|
||||
amstaos AMS-Taos Inc.
|
||||
analogix Analogix Semiconductor, Inc.
|
||||
andestech Andes Technology Corporation
|
||||
apm Applied Micro Circuits Corporation (APM)
|
||||
aptina Aptina Imaging
|
||||
arasan Arasan Chip Systems
|
||||
aries Aries Embedded GmbH
|
||||
arm ARM Ltd.
|
||||
armadeus ARMadeus Systems SARL
|
||||
arrow Arrow Electronics
|
||||
@ -161,6 +163,7 @@ lg LG Corporation
|
||||
linux Linux-specific binding
|
||||
lltc Linear Technology Corporation
|
||||
lsi LSI Corp. (LSI Logic)
|
||||
macnica Macnica Americas
|
||||
marvell Marvell Technology Group Ltd.
|
||||
maxim Maxim Integrated Products
|
||||
mcube mCube
|
||||
@ -186,6 +189,7 @@ mti Imagination Technologies Ltd. (formerly MIPS Technologies Inc.)
|
||||
mundoreader Mundo Reader S.L.
|
||||
murata Murata Manufacturing Co., Ltd.
|
||||
mxicy Macronix International Co., Ltd.
|
||||
myir MYIR Tech Limited
|
||||
national National Semiconductor
|
||||
nec NEC LCD Technologies, Ltd.
|
||||
neonode Neonode Inc.
|
||||
@ -193,13 +197,15 @@ netgear NETGEAR
|
||||
netlogic Broadcom Corporation (formerly NetLogic Microsystems)
|
||||
netxeon Shenzhen Netxeon Technology CO., LTD
|
||||
newhaven Newhaven Display International
|
||||
nvd New Vision Display
|
||||
ni National Instruments
|
||||
nintendo Nintendo
|
||||
nokia Nokia
|
||||
nuvoton Nuvoton Technology Corporation
|
||||
nvd New Vision Display
|
||||
nvidia NVIDIA
|
||||
nxp NXP Semiconductors
|
||||
okaya Okaya Electric America, Inc.
|
||||
oki Oki Electric Industry Co., Ltd.
|
||||
olimex OLIMEX Ltd.
|
||||
onion Onion Corporation
|
||||
onnn ON Semiconductor Corp.
|
||||
@ -238,6 +244,7 @@ richtek Richtek Technology Corporation
|
||||
ricoh Ricoh Co. Ltd.
|
||||
rockchip Fuzhou Rockchip Electronics Co., Ltd
|
||||
samsung Samsung Semiconductor
|
||||
samtec Samtec/Softing company
|
||||
sandisk Sandisk Corporation
|
||||
sbs Smart Battery System
|
||||
schindler Schindler
|
||||
@ -282,6 +289,7 @@ tcg Trusted Computing Group
|
||||
tcl Toby Churchill Ltd.
|
||||
technexion TechNexion
|
||||
technologic Technologic Systems
|
||||
terasic Terasic Inc.
|
||||
thine THine Electronics, Inc.
|
||||
ti Texas Instruments
|
||||
tlm Trusted Logic Mobility
|
||||
@ -296,6 +304,7 @@ tronfy Tronfy
|
||||
tronsmart Tronsmart
|
||||
truly Truly Semiconductors Limited
|
||||
tyan Tyan Computer Corporation
|
||||
udoo Udoo
|
||||
uniwest United Western Technologies Corp (UniWest)
|
||||
upisemi uPI Semiconductor Corp.
|
||||
urt United Radiant Technology Corporation
|
||||
|
@ -1534,9 +1534,12 @@ void of_print_phandle_args(const char *msg, const struct of_phandle_args *args)
|
||||
{
|
||||
int i;
|
||||
printk("%s %s", msg, of_node_full_name(args->np));
|
||||
for (i = 0; i < args->args_count; i++)
|
||||
printk(i ? ",%08x" : ":%08x", args->args[i]);
|
||||
printk("\n");
|
||||
for (i = 0; i < args->args_count; i++) {
|
||||
const char delim = i ? ',' : ':';
|
||||
|
||||
pr_cont("%c%08x", delim, args->args[i]);
|
||||
}
|
||||
pr_cont("\n");
|
||||
}
|
||||
|
||||
int of_phandle_iterator_init(struct of_phandle_iterator *it,
|
||||
|
@ -176,7 +176,12 @@ int of_node_to_nid(struct device_node *device)
|
||||
np->name);
|
||||
of_node_put(np);
|
||||
|
||||
if (!r)
|
||||
/*
|
||||
* If numa=off passed on command line, or with a defective
|
||||
* device tree, the nid may not be in the set of possible
|
||||
* nodes. Check for this case and return NUMA_NO_NODE.
|
||||
*/
|
||||
if (!r && nid < MAX_NUMNODES && node_possible(nid))
|
||||
return nid;
|
||||
|
||||
return NUMA_NO_NODE;
|
||||
|
@ -45,6 +45,9 @@ static int of_dev_node_match(struct device *dev, void *data)
|
||||
* of_find_device_by_node - Find the platform_device associated with a node
|
||||
* @np: Pointer to device tree node
|
||||
*
|
||||
* Takes a reference to the embedded struct device which needs to be dropped
|
||||
* after use.
|
||||
*
|
||||
* Returns platform_device pointer, or NULL if not found
|
||||
*/
|
||||
struct platform_device *of_find_device_by_node(struct device_node *np)
|
||||
@ -558,9 +561,6 @@ static int of_platform_device_destroy(struct device *dev, void *data)
|
||||
* of the given device (and, recurrently, their children) that have been
|
||||
* created from their respective device tree nodes (and only those,
|
||||
* leaving others - eg. manually created - unharmed).
|
||||
*
|
||||
* Returns 0 when all children devices have been removed or
|
||||
* -EBUSY when some children remained.
|
||||
*/
|
||||
void of_platform_depopulate(struct device *parent)
|
||||
{
|
||||
|
@ -28,20 +28,19 @@
|
||||
* Find a node with the give full name by recursively following any of
|
||||
* the child node links.
|
||||
*/
|
||||
static struct device_node *__of_find_node_by_full_name(struct device_node *node,
|
||||
static struct device_node *find_node_by_full_name(struct device_node *node,
|
||||
const char *full_name)
|
||||
{
|
||||
struct device_node *child, *found;
|
||||
|
||||
if (node == NULL)
|
||||
if (!node)
|
||||
return NULL;
|
||||
|
||||
/* check */
|
||||
if (of_node_cmp(node->full_name, full_name) == 0)
|
||||
if (!of_node_cmp(node->full_name, full_name))
|
||||
return of_node_get(node);
|
||||
|
||||
for_each_child_of_node(node, child) {
|
||||
found = __of_find_node_by_full_name(child, full_name);
|
||||
found = find_node_by_full_name(child, full_name);
|
||||
if (found != NULL) {
|
||||
of_node_put(child);
|
||||
return found;
|
||||
@ -51,16 +50,12 @@ static struct device_node *__of_find_node_by_full_name(struct device_node *node,
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Find live tree's maximum phandle value.
|
||||
*/
|
||||
static phandle of_get_tree_max_phandle(void)
|
||||
static phandle live_tree_max_phandle(void)
|
||||
{
|
||||
struct device_node *node;
|
||||
phandle phandle;
|
||||
unsigned long flags;
|
||||
|
||||
/* now search recursively */
|
||||
raw_spin_lock_irqsave(&devtree_lock, flags);
|
||||
phandle = 0;
|
||||
for_each_of_allnodes(node) {
|
||||
@ -73,131 +68,102 @@ static phandle of_get_tree_max_phandle(void)
|
||||
return phandle;
|
||||
}
|
||||
|
||||
/*
|
||||
* Adjust a subtree's phandle values by a given delta.
|
||||
* Makes sure not to just adjust the device node's phandle value,
|
||||
* but modify the phandle properties values as well.
|
||||
*/
|
||||
static void __of_adjust_tree_phandles(struct device_node *node,
|
||||
static void adjust_overlay_phandles(struct device_node *overlay,
|
||||
int phandle_delta)
|
||||
{
|
||||
struct device_node *child;
|
||||
struct property *prop;
|
||||
phandle phandle;
|
||||
|
||||
/* first adjust the node's phandle direct value */
|
||||
if (node->phandle != 0 && node->phandle != OF_PHANDLE_ILLEGAL)
|
||||
node->phandle += phandle_delta;
|
||||
/* adjust node's phandle in node */
|
||||
if (overlay->phandle != 0 && overlay->phandle != OF_PHANDLE_ILLEGAL)
|
||||
overlay->phandle += phandle_delta;
|
||||
|
||||
/* now adjust phandle & linux,phandle values */
|
||||
for_each_property_of_node(node, prop) {
|
||||
/* copy adjusted phandle into *phandle properties */
|
||||
for_each_property_of_node(overlay, prop) {
|
||||
|
||||
/* only look for these two */
|
||||
if (of_prop_cmp(prop->name, "phandle") != 0 &&
|
||||
of_prop_cmp(prop->name, "linux,phandle") != 0)
|
||||
if (of_prop_cmp(prop->name, "phandle") &&
|
||||
of_prop_cmp(prop->name, "linux,phandle"))
|
||||
continue;
|
||||
|
||||
/* must be big enough */
|
||||
if (prop->length < 4)
|
||||
continue;
|
||||
|
||||
/* read phandle value */
|
||||
phandle = be32_to_cpup(prop->value);
|
||||
if (phandle == OF_PHANDLE_ILLEGAL) /* unresolved */
|
||||
if (phandle == OF_PHANDLE_ILLEGAL)
|
||||
continue;
|
||||
|
||||
/* adjust */
|
||||
*(uint32_t *)prop->value = cpu_to_be32(node->phandle);
|
||||
*(uint32_t *)prop->value = cpu_to_be32(overlay->phandle);
|
||||
}
|
||||
|
||||
/* now do the children recursively */
|
||||
for_each_child_of_node(node, child)
|
||||
__of_adjust_tree_phandles(child, phandle_delta);
|
||||
for_each_child_of_node(overlay, child)
|
||||
adjust_overlay_phandles(child, phandle_delta);
|
||||
}
|
||||
|
||||
static int __of_adjust_phandle_ref(struct device_node *node,
|
||||
struct property *rprop, int value)
|
||||
static int update_usages_of_a_phandle_reference(struct device_node *overlay,
|
||||
struct property *prop_fixup, phandle phandle)
|
||||
{
|
||||
phandle phandle;
|
||||
struct device_node *refnode;
|
||||
struct property *sprop;
|
||||
char *propval, *propcur, *propend, *nodestr, *propstr, *s;
|
||||
int offset, propcurlen;
|
||||
struct property *prop;
|
||||
char *value, *cur, *end, *node_path, *prop_name, *s;
|
||||
int offset, len;
|
||||
int err = 0;
|
||||
|
||||
/* make a copy */
|
||||
propval = kmalloc(rprop->length, GFP_KERNEL);
|
||||
if (!propval) {
|
||||
pr_err("%s: Could not copy value of '%s'\n",
|
||||
__func__, rprop->name);
|
||||
value = kmalloc(prop_fixup->length, GFP_KERNEL);
|
||||
if (!value)
|
||||
return -ENOMEM;
|
||||
}
|
||||
memcpy(propval, rprop->value, rprop->length);
|
||||
memcpy(value, prop_fixup->value, prop_fixup->length);
|
||||
|
||||
propend = propval + rprop->length;
|
||||
for (propcur = propval; propcur < propend; propcur += propcurlen + 1) {
|
||||
propcurlen = strlen(propcur);
|
||||
/* prop_fixup contains a list of tuples of path:property_name:offset */
|
||||
end = value + prop_fixup->length;
|
||||
for (cur = value; cur < end; cur += len + 1) {
|
||||
len = strlen(cur);
|
||||
|
||||
nodestr = propcur;
|
||||
s = strchr(propcur, ':');
|
||||
node_path = cur;
|
||||
s = strchr(cur, ':');
|
||||
if (!s) {
|
||||
pr_err("%s: Illegal symbol entry '%s' (1)\n",
|
||||
__func__, propcur);
|
||||
err = -EINVAL;
|
||||
goto err_fail;
|
||||
}
|
||||
*s++ = '\0';
|
||||
|
||||
propstr = s;
|
||||
prop_name = s;
|
||||
s = strchr(s, ':');
|
||||
if (!s) {
|
||||
pr_err("%s: Illegal symbol entry '%s' (2)\n",
|
||||
__func__, (char *)rprop->value);
|
||||
err = -EINVAL;
|
||||
goto err_fail;
|
||||
}
|
||||
|
||||
*s++ = '\0';
|
||||
|
||||
err = kstrtoint(s, 10, &offset);
|
||||
if (err != 0) {
|
||||
pr_err("%s: Could get offset '%s'\n",
|
||||
__func__, (char *)rprop->value);
|
||||
if (err)
|
||||
goto err_fail;
|
||||
}
|
||||
|
||||
/* look into the resolve node for the full path */
|
||||
refnode = __of_find_node_by_full_name(node, nodestr);
|
||||
if (!refnode) {
|
||||
pr_warn("%s: Could not find refnode '%s'\n",
|
||||
__func__, (char *)rprop->value);
|
||||
refnode = find_node_by_full_name(overlay, node_path);
|
||||
if (!refnode)
|
||||
continue;
|
||||
}
|
||||
|
||||
/* now find the property */
|
||||
for_each_property_of_node(refnode, sprop) {
|
||||
if (of_prop_cmp(sprop->name, propstr) == 0)
|
||||
for_each_property_of_node(refnode, prop) {
|
||||
if (!of_prop_cmp(prop->name, prop_name))
|
||||
break;
|
||||
}
|
||||
of_node_put(refnode);
|
||||
|
||||
if (!sprop) {
|
||||
pr_err("%s: Could not find property '%s'\n",
|
||||
__func__, (char *)rprop->value);
|
||||
if (!prop) {
|
||||
err = -ENOENT;
|
||||
goto err_fail;
|
||||
}
|
||||
|
||||
phandle = value;
|
||||
*(__be32 *)(sprop->value + offset) = cpu_to_be32(phandle);
|
||||
*(__be32 *)(prop->value + offset) = cpu_to_be32(phandle);
|
||||
}
|
||||
|
||||
err_fail:
|
||||
kfree(propval);
|
||||
kfree(value);
|
||||
return err;
|
||||
}
|
||||
|
||||
/* compare nodes taking into account that 'name' strips out the @ part */
|
||||
static int __of_node_name_cmp(const struct device_node *dn1,
|
||||
static int node_name_cmp(const struct device_node *dn1,
|
||||
const struct device_node *dn2)
|
||||
{
|
||||
const char *n1 = strrchr(dn1->full_name, '/') ? : "/";
|
||||
@ -208,85 +174,77 @@ static int __of_node_name_cmp(const struct device_node *dn1,
|
||||
|
||||
/*
|
||||
* Adjust the local phandle references by the given phandle delta.
|
||||
* Assumes the existances of a __local_fixups__ node at the root.
|
||||
* Assumes that __of_verify_tree_phandle_references has been called.
|
||||
* Does not take any devtree locks so make sure you call this on a tree
|
||||
* which is at the detached state.
|
||||
*
|
||||
* Subtree @local_fixups, which is overlay node __local_fixups__,
|
||||
* mirrors the fragment node structure at the root of the overlay.
|
||||
*
|
||||
* For each property in the fragments that contains a phandle reference,
|
||||
* @local_fixups has a property of the same name that contains a list
|
||||
* of offsets of the phandle reference(s) within the respective property
|
||||
* value(s). The values at these offsets will be fixed up.
|
||||
*/
|
||||
static int __of_adjust_tree_phandle_references(struct device_node *node,
|
||||
struct device_node *target, int phandle_delta)
|
||||
static int adjust_local_phandle_references(struct device_node *local_fixups,
|
||||
struct device_node *overlay, int phandle_delta)
|
||||
{
|
||||
struct device_node *child, *childtarget;
|
||||
struct property *rprop, *sprop;
|
||||
struct device_node *child, *overlay_child;
|
||||
struct property *prop_fix, *prop;
|
||||
int err, i, count;
|
||||
unsigned int off;
|
||||
phandle phandle;
|
||||
|
||||
if (node == NULL)
|
||||
if (!local_fixups)
|
||||
return 0;
|
||||
|
||||
for_each_property_of_node(node, rprop) {
|
||||
for_each_property_of_node(local_fixups, prop_fix) {
|
||||
|
||||
/* skip properties added automatically */
|
||||
if (of_prop_cmp(rprop->name, "name") == 0 ||
|
||||
of_prop_cmp(rprop->name, "phandle") == 0 ||
|
||||
of_prop_cmp(rprop->name, "linux,phandle") == 0)
|
||||
if (!of_prop_cmp(prop_fix->name, "name") ||
|
||||
!of_prop_cmp(prop_fix->name, "phandle") ||
|
||||
!of_prop_cmp(prop_fix->name, "linux,phandle"))
|
||||
continue;
|
||||
|
||||
if ((rprop->length % 4) != 0 || rprop->length == 0) {
|
||||
pr_err("%s: Illegal property (size) '%s' @%s\n",
|
||||
__func__, rprop->name, node->full_name);
|
||||
if ((prop_fix->length % 4) != 0 || prop_fix->length == 0)
|
||||
return -EINVAL;
|
||||
}
|
||||
count = rprop->length / sizeof(__be32);
|
||||
count = prop_fix->length / sizeof(__be32);
|
||||
|
||||
/* now find the target property */
|
||||
for_each_property_of_node(target, sprop) {
|
||||
if (of_prop_cmp(sprop->name, rprop->name) == 0)
|
||||
for_each_property_of_node(overlay, prop) {
|
||||
if (!of_prop_cmp(prop->name, prop_fix->name))
|
||||
break;
|
||||
}
|
||||
|
||||
if (sprop == NULL) {
|
||||
pr_err("%s: Could not find target property '%s' @%s\n",
|
||||
__func__, rprop->name, node->full_name);
|
||||
if (!prop)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
for (i = 0; i < count; i++) {
|
||||
off = be32_to_cpu(((__be32 *)rprop->value)[i]);
|
||||
/* make sure the offset doesn't overstep (even wrap) */
|
||||
if (off >= sprop->length ||
|
||||
(off + 4) > sprop->length) {
|
||||
pr_err("%s: Illegal property '%s' @%s\n",
|
||||
__func__, rprop->name,
|
||||
node->full_name);
|
||||
off = be32_to_cpu(((__be32 *)prop_fix->value)[i]);
|
||||
if ((off + 4) > prop->length)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (phandle_delta) {
|
||||
/* adjust */
|
||||
phandle = be32_to_cpu(*(__be32 *)(sprop->value + off));
|
||||
phandle += phandle_delta;
|
||||
*(__be32 *)(sprop->value + off) = cpu_to_be32(phandle);
|
||||
}
|
||||
phandle = be32_to_cpu(*(__be32 *)(prop->value + off));
|
||||
phandle += phandle_delta;
|
||||
*(__be32 *)(prop->value + off) = cpu_to_be32(phandle);
|
||||
}
|
||||
}
|
||||
|
||||
for_each_child_of_node(node, child) {
|
||||
/*
|
||||
* These nested loops recurse down two subtrees in parallel, where the
|
||||
* node names in the two subtrees match.
|
||||
*
|
||||
* The roots of the subtrees are the overlay's __local_fixups__ node
|
||||
* and the overlay's root node.
|
||||
*/
|
||||
for_each_child_of_node(local_fixups, child) {
|
||||
|
||||
for_each_child_of_node(target, childtarget)
|
||||
if (__of_node_name_cmp(child, childtarget) == 0)
|
||||
for_each_child_of_node(overlay, overlay_child)
|
||||
if (!node_name_cmp(child, overlay_child))
|
||||
break;
|
||||
|
||||
if (!childtarget) {
|
||||
pr_err("%s: Could not find target child '%s' @%s\n",
|
||||
__func__, child->name, node->full_name);
|
||||
if (!overlay_child)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
err = __of_adjust_tree_phandle_references(child, childtarget,
|
||||
err = adjust_local_phandle_references(child, overlay_child,
|
||||
phandle_delta);
|
||||
if (err != 0)
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
|
||||
@ -294,111 +252,103 @@ static int __of_adjust_tree_phandle_references(struct device_node *node,
|
||||
}
|
||||
|
||||
/**
|
||||
* of_resolve - Resolve the given node against the live tree.
|
||||
* of_resolve_phandles - Relocate and resolve overlay against live tree
|
||||
*
|
||||
* @resolve: Node to resolve
|
||||
* @overlay: Pointer to devicetree overlay to relocate and resolve
|
||||
*
|
||||
* Perform dynamic Device Tree resolution against the live tree
|
||||
* to the given node to resolve. This depends on the live tree
|
||||
* having a __symbols__ node, and the resolve node the __fixups__ &
|
||||
* __local_fixups__ nodes (if needed).
|
||||
* The result of the operation is a resolve node that it's contents
|
||||
* are fit to be inserted or operate upon the live tree.
|
||||
* Returns 0 on success or a negative error value on error.
|
||||
* Modify (relocate) values of local phandles in @overlay to a range that
|
||||
* does not conflict with the live expanded devicetree. Update references
|
||||
* to the local phandles in @overlay. Update (resolve) phandle references
|
||||
* in @overlay that refer to the live expanded devicetree.
|
||||
*
|
||||
* Phandle values in the live tree are in the range of
|
||||
* 1 .. live_tree_max_phandle(). The range of phandle values in the overlay
|
||||
* also begin with at 1. Adjust the phandle values in the overlay to begin
|
||||
* at live_tree_max_phandle() + 1. Update references to the phandles to
|
||||
* the adjusted phandle values.
|
||||
*
|
||||
* The name of each property in the "__fixups__" node in the overlay matches
|
||||
* the name of a symbol (a label) in the live tree. The values of each
|
||||
* property in the "__fixups__" node is a list of the property values in the
|
||||
* overlay that need to be updated to contain the phandle reference
|
||||
* corresponding to that symbol in the live tree. Update the references in
|
||||
* the overlay with the phandle values in the live tree.
|
||||
*
|
||||
* @overlay must be detached.
|
||||
*
|
||||
* Resolving and applying @overlay to the live expanded devicetree must be
|
||||
* protected by a mechanism to ensure that multiple overlays are processed
|
||||
* in a single threaded manner so that multiple overlays will not relocate
|
||||
* phandles to overlapping ranges. The mechanism to enforce this is not
|
||||
* yet implemented.
|
||||
*
|
||||
* Return: %0 on success or a negative error value on error.
|
||||
*/
|
||||
int of_resolve_phandles(struct device_node *resolve)
|
||||
int of_resolve_phandles(struct device_node *overlay)
|
||||
{
|
||||
struct device_node *child, *childroot, *refnode;
|
||||
struct device_node *root_sym, *resolve_sym, *resolve_fix;
|
||||
struct property *rprop;
|
||||
struct device_node *child, *local_fixups, *refnode;
|
||||
struct device_node *tree_symbols, *overlay_fixups;
|
||||
struct property *prop;
|
||||
const char *refpath;
|
||||
phandle phandle, phandle_delta;
|
||||
int err;
|
||||
|
||||
if (!resolve)
|
||||
pr_err("%s: null node\n", __func__);
|
||||
if (resolve && !of_node_check_flag(resolve, OF_DETACHED))
|
||||
pr_err("%s: node %s not detached\n", __func__,
|
||||
resolve->full_name);
|
||||
/* the resolve node must exist, and be detached */
|
||||
if (!resolve || !of_node_check_flag(resolve, OF_DETACHED))
|
||||
return -EINVAL;
|
||||
tree_symbols = NULL;
|
||||
|
||||
/* first we need to adjust the phandles */
|
||||
phandle_delta = of_get_tree_max_phandle() + 1;
|
||||
__of_adjust_tree_phandles(resolve, phandle_delta);
|
||||
|
||||
/* locate the local fixups */
|
||||
childroot = NULL;
|
||||
for_each_child_of_node(resolve, childroot)
|
||||
if (of_node_cmp(childroot->name, "__local_fixups__") == 0)
|
||||
break;
|
||||
|
||||
if (childroot != NULL) {
|
||||
/* resolve root is guaranteed to be the '/' */
|
||||
err = __of_adjust_tree_phandle_references(childroot,
|
||||
resolve, 0);
|
||||
if (err != 0)
|
||||
return err;
|
||||
|
||||
BUG_ON(__of_adjust_tree_phandle_references(childroot,
|
||||
resolve, phandle_delta));
|
||||
}
|
||||
|
||||
root_sym = NULL;
|
||||
resolve_sym = NULL;
|
||||
resolve_fix = NULL;
|
||||
|
||||
/* this may fail (if no fixups are required) */
|
||||
root_sym = of_find_node_by_path("/__symbols__");
|
||||
|
||||
/* locate the symbols & fixups nodes on resolve */
|
||||
for_each_child_of_node(resolve, child) {
|
||||
|
||||
if (!resolve_sym &&
|
||||
of_node_cmp(child->name, "__symbols__") == 0)
|
||||
resolve_sym = child;
|
||||
|
||||
if (!resolve_fix &&
|
||||
of_node_cmp(child->name, "__fixups__") == 0)
|
||||
resolve_fix = child;
|
||||
|
||||
/* both found, don't bother anymore */
|
||||
if (resolve_sym && resolve_fix)
|
||||
break;
|
||||
}
|
||||
|
||||
/* we do allow for the case where no fixups are needed */
|
||||
if (!resolve_fix) {
|
||||
err = 0; /* no error */
|
||||
if (!overlay) {
|
||||
pr_err("null overlay\n");
|
||||
err = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* we need to fixup, but no root symbols... */
|
||||
if (!root_sym) {
|
||||
pr_err("%s: no symbols in root of device tree.\n", __func__);
|
||||
if (!of_node_check_flag(overlay, OF_DETACHED)) {
|
||||
pr_err("overlay not detached\n");
|
||||
err = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
for_each_property_of_node(resolve_fix, rprop) {
|
||||
phandle_delta = live_tree_max_phandle() + 1;
|
||||
adjust_overlay_phandles(overlay, phandle_delta);
|
||||
|
||||
for_each_child_of_node(overlay, local_fixups)
|
||||
if (!of_node_cmp(local_fixups->name, "__local_fixups__"))
|
||||
break;
|
||||
|
||||
err = adjust_local_phandle_references(local_fixups, overlay, phandle_delta);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
overlay_fixups = NULL;
|
||||
|
||||
for_each_child_of_node(overlay, child) {
|
||||
if (!of_node_cmp(child->name, "__fixups__"))
|
||||
overlay_fixups = child;
|
||||
}
|
||||
|
||||
if (!overlay_fixups) {
|
||||
err = 0;
|
||||
goto out;
|
||||
}
|
||||
|
||||
tree_symbols = of_find_node_by_path("/__symbols__");
|
||||
if (!tree_symbols) {
|
||||
pr_err("no symbols in root of device tree.\n");
|
||||
err = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
for_each_property_of_node(overlay_fixups, prop) {
|
||||
|
||||
/* skip properties added automatically */
|
||||
if (of_prop_cmp(rprop->name, "name") == 0)
|
||||
if (!of_prop_cmp(prop->name, "name"))
|
||||
continue;
|
||||
|
||||
err = of_property_read_string(root_sym,
|
||||
rprop->name, &refpath);
|
||||
if (err != 0) {
|
||||
pr_err("%s: Could not find symbol '%s'\n",
|
||||
__func__, rprop->name);
|
||||
err = of_property_read_string(tree_symbols,
|
||||
prop->name, &refpath);
|
||||
if (err)
|
||||
goto out;
|
||||
}
|
||||
|
||||
refnode = of_find_node_by_path(refpath);
|
||||
if (!refnode) {
|
||||
pr_err("%s: Could not find node by path '%s'\n",
|
||||
__func__, refpath);
|
||||
err = -ENOENT;
|
||||
goto out;
|
||||
}
|
||||
@ -406,17 +356,15 @@ int of_resolve_phandles(struct device_node *resolve)
|
||||
phandle = refnode->phandle;
|
||||
of_node_put(refnode);
|
||||
|
||||
pr_debug("%s: %s phandle is 0x%08x\n",
|
||||
__func__, rprop->name, phandle);
|
||||
|
||||
err = __of_adjust_phandle_ref(resolve, rprop, phandle);
|
||||
err = update_usages_of_a_phandle_reference(overlay, prop, phandle);
|
||||
if (err)
|
||||
break;
|
||||
}
|
||||
|
||||
out:
|
||||
/* NULL is handled by of_node_put as NOP */
|
||||
of_node_put(root_sym);
|
||||
if (err)
|
||||
pr_err("overlay phandle fixup failed: %d\n", err);
|
||||
of_node_put(tree_symbols);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user