Commit 7ef58b32 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'devicetree-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/glikely/linux

Pull devicetree changes from Grant Likely:
 "Lots of activity in the devicetree code for v3.18.  Most of it is
  related to getting all of the overlay support code in place, but there
  are other important things in there.

  Highlights:

   - OF_RECONFIG notifiers for SPI, I2C and Platform devices.  Those
     subsystems can now respond to live changes to the device tree.

   - CONFIG_OF_OVERLAY method for applying live changes to the device
     tree

   - Removal of the of_allnodes list.  This used to be used to iterate
     over all the nodes in the device tree, but it is unnecessary
     because the same thing can be done by iterating over the list of
     child pointers.  Getting rid of of_allnodes saves some memory and
     avoids the possibility of of_allnodes being sorted differently from
     the child lists.

   - Support for retrieving original DTB blob via sysfs.  Needed by
     kexec.

   - More unittests

   - Documentation and minor bug fixes"

* tag 'devicetree-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/glikely/linux: (42 commits)
  of: Delete unnecessary check before calling "of_node_put()"
  of: Drop ->next pointer from struct device_node
  spi: Check for spi_of_notifier when CONFIG_OF_DYNAMIC=y
  of: support passing console options with stdout-path
  of: add optional options parameter to of_find_node_by_path()
  of: Add bindings for chosen node, stdout-path
  of: Remove unneeded and incorrect MODULE_DEVICE_TABLE
  ARM: dt: fix up PL011 device tree bindings
  of: base, fix of_property_read_string_helper kernel-doc
  of: remove select of non-existant OF_DEVICE config symbol
  spi/of: Add OF notifier handler
  spi/of: Create new device registration method and accessors
  i2c/of: Add OF_RECONFIG notifier handler
  i2c/of: Factor out Devicetree registration code
  of/overlay: Add overlay unittests
  of/overlay: Introduce DT overlay support
  of/reconfig: Add OF_DYNAMIC notifier for platform_bus_type
  of/reconfig: Always use the same structure for notifiers
  of/reconfig: Add debug output for OF_RECONFIG notifiers
  of/reconfig: Add empty stubs for the of_reconfig methods
  ...
parents 413fd0e3 c46ca3c8
The chosen node
---------------
The chosen node does not represent a real device, but serves as a place
for passing data between firmware and the operating system, like boot
arguments. Data in the chosen node does not represent the hardware.
stdout-path property
--------------------
Device trees may specify the device to be used for boot console output
with a stdout-path property under /chosen, as described in ePAPR, e.g.
/ {
chosen {
stdout-path = "/serial@f00:115200";
};
serial@f00 {
compatible = "vendor,some-uart";
reg = <0xf00 0x10>;
};
};
If the character ":" is present in the value, this terminates the path.
The meaning of any characters following the ":" is device-specific, and
must be specified in the relevant binding documentation.
For UART devices, the preferred binding is a string in the form:
<baud>{<parity>{<bits>{<flow>}}}
where
baud - baud rate in decimal
parity - 'n' (none), 'o', (odd) or 'e' (even)
bits - number of data bits
flow - 'r' (rts)
For example: 115200n8r
Implementation note: Linux will look for the property "linux,stdout-path" or
on PowerPC "stdout" if "stdout-path" is not found. However, the
"linux,stdout-path" and "stdout" properties are deprecated. New platforms
should only use the "stdout-path" property.
...@@ -6,12 +6,34 @@ Required properties: ...@@ -6,12 +6,34 @@ Required properties:
- interrupts: exactly one interrupt specifier - interrupts: exactly one interrupt specifier
Optional properties: Optional properties:
- pinctrl: When present, must have one state named "sleep" - pinctrl: When present, must have one state named "default",
and one state named "default" and may contain a second name named "sleep". The former
- clocks: When present, must refer to exactly one clock named state sets up pins for ordinary operation whereas
the latter state will put the associated pins to sleep
when the UART is unused
- clocks: When present, the first clock listed must correspond to
the clock named UARTCLK on the IP block, i.e. the clock
to the external serial line, whereas the second clock
must correspond to the PCLK clocking the internal logic
of the block. Just listing one clock (the first one) is
deprecated.
- clocks-names: When present, the first clock listed must be named
"uartclk" and the second clock listed must be named
"apb_pclk" "apb_pclk"
- dmas: When present, may have one or two dma channels. - dmas: When present, may have one or two dma channels.
The first one must be named "rx", the second one The first one must be named "rx", the second one
must be named "tx". must be named "tx".
See also bindings/arm/primecell.txt See also bindings/arm/primecell.txt
Example:
uart@80120000 {
compatible = "arm,pl011", "arm,primecell";
reg = <0x80120000 0x1000>;
interrupts = <0 11 IRQ_TYPE_LEVEL_HIGH>;
dmas = <&dma 13 0 0x2>, <&dma 13 0 0x0>;
dma-names = "rx", "tx";
clocks = <&foo_clk>, <&bar_clk>;
clock-names = "uartclk", "apb_pclk";
};
* OF selftest platform device
** selftest
Required properties:
- compatible: must be "selftest"
All other properties are optional.
Example:
selftest {
compatible = "selftest";
status = "okay";
};
...@@ -63,7 +63,6 @@ struct device_node { ...@@ -63,7 +63,6 @@ struct device_node {
struct device_node *parent; struct device_node *parent;
struct device_node *child; struct device_node *child;
struct device_node *sibling; struct device_node *sibling;
struct device_node *allnext; /* next in list of all nodes */
... ...
}; };
...@@ -99,12 +98,6 @@ child11 -> sibling12 -> sibling13 -> sibling14 -> null ...@@ -99,12 +98,6 @@ child11 -> sibling12 -> sibling13 -> sibling14 -> null
Figure 1: Generic structure of un-flattened device tree Figure 1: Generic structure of un-flattened device tree
*allnext: it is used to link all the nodes of DT into a list. So, for the
above tree the list would be as follows:
root->child1->child11->sibling12->sibling13->child131->sibling14->sibling2->
child21->sibling22->sibling23->sibling3->child31->sibling32->sibling4->null
Before executing OF selftest, it is required to attach the test data to Before executing OF selftest, it is required to attach the test data to
machine's device tree (if present). So, when selftest_data_add() is called, machine's device tree (if present). So, when selftest_data_add() is called,
at first it reads the flattened device tree data linked into the kernel image at first it reads the flattened device tree data linked into the kernel image
...@@ -131,11 +124,6 @@ root ('/') ...@@ -131,11 +124,6 @@ root ('/')
test-child01 null null null test-child01 null null null
allnext list:
root->testcase-data->test-child0->test-child01->test-sibling1->test-sibling2
->test-sibling3->null
Figure 2: Example test data tree to be attached to live tree. Figure 2: Example test data tree to be attached to live tree.
According to the scenario above, the live tree is already present so it isn't According to the scenario above, the live tree is already present so it isn't
...@@ -204,8 +192,6 @@ detached and then moving up the parent nodes are removed, and eventually the ...@@ -204,8 +192,6 @@ detached and then moving up the parent nodes are removed, and eventually the
whole tree). selftest_data_remove() calls detach_node_and_children() that uses whole tree). selftest_data_remove() calls detach_node_and_children() that uses
of_detach_node() to detach the nodes from the live device tree. of_detach_node() to detach the nodes from the live device tree.
To detach a node, of_detach_node() first updates all_next linked list, by To detach a node, of_detach_node() either updates the child pointer of given
attaching the previous node's allnext to current node's allnext pointer. And node's parent to its sibling or attaches the previous sibling to the given
then, it either updates the child pointer of given node's parent to its node's sibling, as appropriate. That is it :)
sibling or attaches the previous sibling to the given node's sibling, as
appropriate. That is it :)
Device Tree Overlay Notes
-------------------------
This document describes the implementation of the in-kernel
device tree overlay functionality residing in drivers/of/overlay.c and is a
companion document to Documentation/devicetree/dt-object-internal.txt[1] &
Documentation/devicetree/dynamic-resolution-notes.txt[2]
How overlays work
-----------------
A Device Tree's overlay purpose is to modify the kernel's live tree, and
have the modification affecting the state of the the kernel in a way that
is reflecting the changes.
Since the kernel mainly deals with devices, any new device node that result
in an active device should have it created while if the device node is either
disabled or removed all together, the affected device should be deregistered.
Lets take an example where we have a foo board with the following base tree
which is taken from [1].
---- foo.dts -----------------------------------------------------------------
/* FOO platform */
/ {
compatible = "corp,foo";
/* shared resources */
res: res {
};
/* On chip peripherals */
ocp: ocp {
/* peripherals that are always instantiated */
peripheral1 { ... };
}
};
---- foo.dts -----------------------------------------------------------------
The overlay bar.dts, when loaded (and resolved as described in [2]) should
---- bar.dts -----------------------------------------------------------------
/plugin/; /* allow undefined label references and record them */
/ {
.... /* various properties for loader use; i.e. part id etc. */
fragment@0 {
target = <&ocp>;
__overlay__ {
/* bar peripheral */
bar {
compatible = "corp,bar";
... /* various properties and child nodes */
}
};
};
};
---- bar.dts -----------------------------------------------------------------
result in foo+bar.dts
---- foo+bar.dts -------------------------------------------------------------
/* FOO platform + bar peripheral */
/ {
compatible = "corp,foo";
/* shared resources */
res: res {
};
/* On chip peripherals */
ocp: ocp {
/* peripherals that are always instantiated */
peripheral1 { ... };
/* bar peripheral */
bar {
compatible = "corp,bar";
... /* various properties and child nodes */
}
}
};
---- foo+bar.dts -------------------------------------------------------------
As a result of the the overlay, a new device node (bar) has been created
so a bar platform device will be registered and if a matching device driver
is loaded the device will be created as expected.
Overlay in-kernel API
--------------------------------
The API is quite easy to use.
1. Call of_overlay_create() to create and apply an overlay. The return value
is a cookie identifying this overlay.
2. Call of_overlay_destroy() to remove and cleanup the overlay previously
created via the call to of_overlay_create(). Removal of an overlay that
is stacked by another will not be permitted.
Finally, if you need to remove all overlays in one-go, just call
of_overlay_destroy_all() which will remove every single one in the correct
order.
Overlay DTS Format
------------------
The DTS of an overlay should have the following format:
{
/* ignored properties by the overlay */
fragment@0 { /* first child node */
target=<phandle>; /* phandle target of the overlay */
or
target-path="/path"; /* target path of the overlay */
__overlay__ {
property-a; /* add property-a to the target */
node-a { /* add to an existing, or create a node-a */
...
};
};
}
fragment@1 { /* second child node */
...
};
/* more fragments follow */
}
Using the non-phandle based target method allows one to use a base DT which does
not contain a __symbols__ node, i.e. it was not compiled with the -@ option.
The __symbols__ node is only required for the target=<phandle> method, since it
contains the information required to map from a phandle to a tree location.
...@@ -2,7 +2,6 @@ Todo list for devicetree: ...@@ -2,7 +2,6 @@ Todo list for devicetree:
=== General structure === === General structure ===
- Switch from custom lists to (h)list_head for nodes and properties structure - Switch from custom lists to (h)list_head for nodes and properties structure
- Remove of_allnodes list and iterate using list of child nodes alone
=== CONFIG_OF_DYNAMIC === === CONFIG_OF_DYNAMIC ===
- Switch to RCU for tree updates and get rid of global spinlock - Switch to RCU for tree updates and get rid of global spinlock
......
...@@ -1711,12 +1711,11 @@ static void stage_topology_update(int core_id) ...@@ -1711,12 +1711,11 @@ static void stage_topology_update(int core_id)
static int dt_update_callback(struct notifier_block *nb, static int dt_update_callback(struct notifier_block *nb,
unsigned long action, void *data) unsigned long action, void *data)
{ {
struct of_prop_reconfig *update; struct of_reconfig_data *update = data;
int rc = NOTIFY_DONE; int rc = NOTIFY_DONE;
switch (action) { switch (action) {
case OF_RECONFIG_UPDATE_PROPERTY: case OF_RECONFIG_UPDATE_PROPERTY:
update = (struct of_prop_reconfig *)data;
if (!of_prop_cmp(update->dn->type, "cpu") && if (!of_prop_cmp(update->dn->type, "cpu") &&
!of_prop_cmp(update->prop->name, "ibm,associativity")) { !of_prop_cmp(update->prop->name, "ibm,associativity")) {
u32 core_id; u32 core_id;
......
...@@ -340,16 +340,17 @@ static void pseries_remove_processor(struct device_node *np) ...@@ -340,16 +340,17 @@ static void pseries_remove_processor(struct device_node *np)
} }
static int pseries_smp_notifier(struct notifier_block *nb, static int pseries_smp_notifier(struct notifier_block *nb,
unsigned long action, void *node) unsigned long action, void *data)
{ {
struct of_reconfig_data *rd = data;
int err = 0; int err = 0;
switch (action) { switch (action) {
case OF_RECONFIG_ATTACH_NODE: case OF_RECONFIG_ATTACH_NODE:
err = pseries_add_processor(node); err = pseries_add_processor(rd->dn);
break; break;
case OF_RECONFIG_DETACH_NODE: case OF_RECONFIG_DETACH_NODE:
pseries_remove_processor(node); pseries_remove_processor(rd->dn);
break; break;
} }
return notifier_from_errno(err); return notifier_from_errno(err);
......
...@@ -183,7 +183,7 @@ static int pseries_add_mem_node(struct device_node *np) ...@@ -183,7 +183,7 @@ static int pseries_add_mem_node(struct device_node *np)
return (ret < 0) ? -EINVAL : 0; return (ret < 0) ? -EINVAL : 0;
} }
static int pseries_update_drconf_memory(struct of_prop_reconfig *pr) static int pseries_update_drconf_memory(struct of_reconfig_data *pr)
{ {
struct of_drconf_cell *new_drmem, *old_drmem; struct of_drconf_cell *new_drmem, *old_drmem;
unsigned long memblock_size; unsigned long memblock_size;
...@@ -232,22 +232,21 @@ static int pseries_update_drconf_memory(struct of_prop_reconfig *pr) ...@@ -232,22 +232,21 @@ static int pseries_update_drconf_memory(struct of_prop_reconfig *pr)
} }
static int pseries_memory_notifier(struct notifier_block *nb, static int pseries_memory_notifier(struct notifier_block *nb,
unsigned long action, void *node) unsigned long action, void *data)
{ {
struct of_prop_reconfig *pr; struct of_reconfig_data *rd = data;
int err = 0; int err = 0;
switch (action) { switch (action) {
case OF_RECONFIG_ATTACH_NODE: case OF_RECONFIG_ATTACH_NODE:
err = pseries_add_mem_node(node); err = pseries_add_mem_node(rd->dn);
break; break;
case OF_RECONFIG_DETACH_NODE: case OF_RECONFIG_DETACH_NODE:
err = pseries_remove_mem_node(node); err = pseries_remove_mem_node(rd->dn);
break; break;
case OF_RECONFIG_UPDATE_PROPERTY: case OF_RECONFIG_UPDATE_PROPERTY:
pr = (struct of_prop_reconfig *)node; if (!strcmp(rd->prop->name, "ibm,dynamic-memory"))
if (!strcmp(pr->prop->name, "ibm,dynamic-memory")) err = pseries_update_drconf_memory(rd);
err = pseries_update_drconf_memory(pr);
break; break;
} }
return notifier_from_errno(err); return notifier_from_errno(err);
......
...@@ -1251,10 +1251,11 @@ static struct notifier_block iommu_mem_nb = { ...@@ -1251,10 +1251,11 @@ static struct notifier_block iommu_mem_nb = {
.notifier_call = iommu_mem_notifier, .notifier_call = iommu_mem_notifier,
}; };
static int iommu_reconfig_notifier(struct notifier_block *nb, unsigned long action, void *node) static int iommu_reconfig_notifier(struct notifier_block *nb, unsigned long action, void *data)
{ {
int err = NOTIFY_OK; int err = NOTIFY_OK;
struct device_node *np = node; struct of_reconfig_data *rd = data;
struct device_node *np = rd->dn;
struct pci_dn *pci = PCI_DN(np); struct pci_dn *pci = PCI_DN(np);
struct direct_window *window; struct direct_window *window;
......
...@@ -251,9 +251,10 @@ static void __init pseries_discover_pic(void) ...@@ -251,9 +251,10 @@ static void __init pseries_discover_pic(void)
" interrupt-controller\n"); " interrupt-controller\n");
} }
static int pci_dn_reconfig_notifier(struct notifier_block *nb, unsigned long action, void *node) static int pci_dn_reconfig_notifier(struct notifier_block *nb, unsigned long action, void *data)
{ {
struct device_node *np = node; struct of_reconfig_data *rd = data;
struct device_node *np = rd->dn;
struct pci_dn *pci = NULL; struct pci_dn *pci = NULL;
int err = NOTIFY_OK; int err = NOTIFY_OK;
......
...@@ -1006,6 +1006,7 @@ int __init platform_bus_init(void) ...@@ -1006,6 +1006,7 @@ int __init platform_bus_init(void)
error = bus_register(&platform_bus_type); error = bus_register(&platform_bus_type);
if (error) if (error)
device_unregister(&platform_bus); device_unregister(&platform_bus);
of_platform_register_reconfig_notifier();
return error; return error;
} }
......
...@@ -1009,9 +1009,9 @@ static int nx842_OF_upd(struct property *new_prop) ...@@ -1009,9 +1009,9 @@ static int nx842_OF_upd(struct property *new_prop)
* notifier_to_errno() to decode this value * notifier_to_errno() to decode this value
*/ */
static int nx842_OF_notifier(struct notifier_block *np, unsigned long action, static int nx842_OF_notifier(struct notifier_block *np, unsigned long action,
void *update) void *data)
{ {
struct of_prop_reconfig *upd = update; struct of_reconfig_data *upd = data;
struct nx842_devdata *local_devdata; struct nx842_devdata *local_devdata;
struct device_node *node = NULL; struct device_node *node = NULL;
......
...@@ -49,6 +49,7 @@ ...@@ -49,6 +49,7 @@
#include <linux/acpi.h> #include <linux/acpi.h>
#include <linux/jump_label.h> #include <linux/jump_label.h>
#include <asm/uaccess.h> #include <asm/uaccess.h>
#include <linux/err.h>
#include "i2c-core.h" #include "i2c-core.h"
...@@ -1369,18 +1370,10 @@ static void i2c_scan_static_board_info(struct i2c_adapter *adapter) ...@@ -1369,18 +1370,10 @@ static void i2c_scan_static_board_info(struct i2c_adapter *adapter)
/* OF support code */ /* OF support code */
#if IS_ENABLED(CONFIG_OF) #if IS_ENABLED(CONFIG_OF)
static void of_i2c_register_devices(struct i2c_adapter *adap) static struct i2c_client *of_i2c_register_device(struct i2c_adapter *adap,
struct device_node *node)
{ {
void *result; struct i2c_client *result;
struct device_node *node;
/* 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");
for_each_available_child_of_node(adap->dev.of_node, node) {
struct i2c_board_info info = {}; struct i2c_board_info info = {};
struct dev_archdata dev_ad = {}; struct dev_archdata dev_ad = {};
const __be32 *addr; const __be32 *addr;
...@@ -1391,21 +1384,21 @@ static void of_i2c_register_devices(struct i2c_adapter *adap) ...@@ -1391,21 +1384,21 @@ static void of_i2c_register_devices(struct i2c_adapter *adap)
if (of_modalias_node(node, info.type, sizeof(info.type)) < 0) { if (of_modalias_node(node, info.type, sizeof(info.type)) < 0) {
dev_err(&adap->dev, "of_i2c: modalias failure on %s\n", dev_err(&adap->dev, "of_i2c: modalias failure on %s\n",
node->full_name); node->full_name);
continue; return ERR_PTR(-EINVAL);
} }
addr = of_get_property(node, "reg", &len); addr = of_get_property(node, "reg", &len);
if (!addr || (len < sizeof(int))) { if (!addr || (len < sizeof(int))) {
dev_err(&adap->dev, "of_i2c: invalid reg on %s\n", dev_err(&adap->dev, "of_i2c: invalid reg on %s\n",
node->full_name); node->full_name);
continue; return ERR_PTR(-EINVAL);
} }
info.addr = be32_to_cpup(addr); info.addr = be32_to_cpup(addr);
if (info.addr > (1 << 10) - 1) { if (info.addr > (1 << 10) - 1) {
dev_err(&adap->dev, "of_i2c: invalid addr=%x on %s\n", dev_err(&adap->dev, "of_i2c: invalid addr=%x on %s\n",
info.addr, node->full_name); info.addr, node->full_name);
continue; return ERR_PTR(-EINVAL);
} }
info.irq = irq_of_parse_and_map(node, 0); info.irq = irq_of_parse_and_map(node, 0);
...@@ -1423,9 +1416,23 @@ static void of_i2c_register_devices(struct i2c_adapter *adap) ...@@ -1423,9 +1416,23 @@ static void of_i2c_register_devices(struct i2c_adapter *adap)
node->full_name); node->full_name);
of_node_put(node); of_node_put(node);
irq_dispose_mapping(info.irq); irq_dispose_mapping(info.irq);
continue; return ERR_PTR(-EINVAL);
}
} }
return result;
}
static void of_i2c_register_devices(struct i2c_adapter *adap)
{
struct device_node *node;
/* 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");
for_each_available_child_of_node(adap->dev.of_node, node)
of_i2c_register_device(adap, node);
} }
static int of_dev_node_match(struct device *dev, void *data) static int of_dev_node_match(struct device *dev, void *data)
...@@ -1945,6 +1952,52 @@ void i2c_clients_command(struct i2c_adapter *adap, unsigned int cmd, void *arg) ...@@ -1945,6 +1952,52 @@ void i2c_clients_command(struct i2c_adapter *adap, unsigned int cmd, void *arg)
} }
EXPORT_SYMBOL(i2c_clients_command); EXPORT_SYMBOL(i2c_clients_command);
#if IS_ENABLED(CONFIG_OF_DYNAMIC)
static int of_i2c_notify(struct notifier_block *nb, unsigned long action,
void *arg)
{
struct of_reconfig_data *rd = arg;
struct i2c_adapter *adap;
struct i2c_client *client;
switch (of_reconfig_get_state_change(action, rd)) {
case OF_RECONFIG_CHANGE_ADD:
adap = of_find_i2c_adapter_by_node(rd->dn->parent);
if (adap == NULL)
return NOTIFY_OK; /* not for us */
client = of_i2c_register_device(adap, rd->dn);
put_device(&adap->dev);
if (IS_ERR(client)) {
pr_err("%s: failed to create for '%s'\n",
__func__, rd->dn->full_name);
return notifier_from_errno(PTR_ERR(client));
}
break;
case OF_RECONFIG_CHANGE_REMOVE:
/* find our device by node */
client = of_find_i2c_device_by_node(rd->dn);
if (client == NULL)
return NOTIFY_OK; /* no? not meant for us */
/* unregister takes one ref away */
i2c_unregister_device(client);
/* and put the reference of the find */
put_device(&client->dev);
break;
}
return NOTIFY_OK;
}
static struct notifier_block i2c_of_notifier = {
.notifier_call = of_i2c_notify,
};
#else
extern struct notifier_block i2c_of_notifier;
#endif /* CONFIG_OF_DYNAMIC */
static int __init i2c_init(void) static int __init i2c_init(void)
{ {
int retval; int retval;
...@@ -1962,6 +2015,10 @@ static int __init i2c_init(void) ...@@ -1962,6 +2015,10 @@ static int __init i2c_init(void)
retval = i2c_add_driver(&dummy_driver); retval = i2c_add_driver(&dummy_driver);
if (retval) if (retval)
goto class_err; goto class_err;
if (IS_ENABLED(CONFIG_OF_DYNAMIC))
WARN_ON(of_reconfig_notifier_register(&i2c_of_notifier));
return 0; return 0;
class_err: class_err:
...@@ -1975,6 +2032,8 @@ static int __init i2c_init(void) ...@@ -1975,6 +2032,8 @@ static int __init i2c_init(void)
static void __exit i2c_exit(void) static void __exit i2c_exit(void)
{ {
if (IS_ENABLED(CONFIG_OF_DYNAMIC))
WARN_ON(of_reconfig_notifier_unregister(&i2c_of_notifier));
i2c_del_driver(&dummy_driver); i2c_del_driver(&dummy_driver);
#ifdef CONFIG_I2C_COMPAT #ifdef CONFIG_I2C_COMPAT
class_compat_unregister(i2c_adapter_compat_class); class_compat_unregister(i2c_adapter_compat_class);
......
...@@ -223,7 +223,7 @@ static int vexpress_sysreg_probe(struct platform_device *pdev) ...@@ -223,7 +223,7 @@ static int vexpress_sysreg_probe(struct platform_device *pdev)
vexpress_config_set_master(vexpress_sysreg_get_master()); vexpress_config_set_master(vexpress_sysreg_get_master());
/* Confirm board type against DT property, if available */ /* Confirm board type against DT property, if available */
if (of_property_read_u32(of_allnodes, "arm,hbi", &dt_hbi) == 0) { if (of_property_read_u32(of_root, "arm,hbi", &dt_hbi) == 0) {
u32 id = vexpress_get_procid(VEXPRESS_SITE_MASTER); u32 id = vexpress_get_procid(VEXPRESS_SITE_MASTER);
u32 hbi = (id >> SYS_PROCIDx_HBI_SHIFT) & SYS_HBI_MASK; u32 hbi = (id >> SYS_PROCIDx_HBI_SHIFT) & SYS_HBI_MASK;
......
...@@ -7,8 +7,8 @@ config OF ...@@ -7,8 +7,8 @@ config OF
menu "Device Tree and Open Firmware support" menu "Device Tree and Open Firmware support"
depends on OF depends on OF
config OF_SELFTEST config OF_UNITTEST
bool "Device Tree Runtime self tests" bool "Device Tree runtime unit tests"
depends on OF_IRQ && OF_EARLY_FLATTREE depends on OF_IRQ && OF_EARLY_FLATTREE
select OF_DYNAMIC select OF_DYNAMIC
select OF_RESOLVE select OF_RESOLVE
...@@ -23,6 +23,7 @@ config OF_FLATTREE ...@@ -23,6 +23,7 @@ config OF_FLATTREE
bool bool
select DTC select DTC
select LIBFDT select LIBFDT
select CRC32
config OF_EARLY_FLATTREE config OF_EARLY_FLATTREE
bool bool
...@@ -83,4 +84,10 @@ config OF_RESERVED_MEM ...@@ -83,4 +84,10 @@ config OF_RESERVED_MEM
config OF_RESOLVE config OF_RESOLVE
bool bool
config OF_OVERLAY
bool
depends on OF
select OF_DYNAMIC
select OF_RESOLVE
endmenu # OF endmenu # OF
...@@ -6,14 +6,15 @@ obj-$(CONFIG_OF_PROMTREE) += pdt.o ...@@ -6,14 +6,15 @@ obj-$(CONFIG_OF_PROMTREE) += pdt.o
obj-$(CONFIG_OF_ADDRESS) += address.o obj-$(CONFIG_OF_ADDRESS) += address.o
obj-$(CONFIG_OF_IRQ) += irq.o obj-$(CONFIG_OF_IRQ) += irq.o
obj-$(CONFIG_OF_NET) += of_net.o obj-$(CONFIG_OF_NET) += of_net.o
obj-$(CONFIG_OF_SELFTEST) += of_selftest.o obj-$(CONFIG_OF_UNITTEST) += of_unittest.o
of_selftest-objs := selftest.o testcase-data/testcases.dtb.o of_unittest-objs := unittest.o unittest-data/testcases.dtb.o
obj-$(CONFIG_OF_MDIO) += of_mdio.o obj-$(CONFIG_OF_MDIO) += of_mdio.o
obj-$(CONFIG_OF_PCI) += of_pci.o obj-$(CONFIG_OF_PCI) += of_pci.o
obj-$(CONFIG_OF_PCI_IRQ) += of_pci_irq.o obj-$(CONFIG_OF_PCI_IRQ) += of_pci_irq.o
obj-$(CONFIG_OF_MTD) += of_mtd.o obj-$(CONFIG_OF_MTD) += of_mtd.o
obj-$(CONFIG_OF_RESERVED_MEM) += of_reserved_mem.o obj-$(CONFIG_OF_RESERVED_MEM) += of_reserved_mem.o
obj-$(CONFIG_OF_RESOLVE) += resolver.o obj-$(CONFIG_OF_RESOLVE) += resolver.o
obj-$(CONFIG_OF_OVERLAY) += overlay.o
CFLAGS_fdt.o = -I$(src)/../../scripts/dtc/libfdt CFLAGS_fdt.o = -I$(src)/../../scripts/dtc/libfdt
CFLAGS_fdt_address.o = -I$(src)/../../scripts/dtc/libfdt CFLAGS_fdt_address.o = -I$(src)/../../scripts/dtc/libfdt
...@@ -491,7 +491,7 @@ static int of_translate_one(struct device_node *parent, struct of_bus *bus, ...@@ -491,7 +491,7 @@ static int of_translate_one(struct device_node *parent, struct of_bus *bus,
*/ */
ranges = of_get_property(parent, rprop, &rlen); ranges = of_get_property(parent, rprop, &rlen);
if (ranges == NULL && !of_empty_ranges_quirk()) { if (ranges == NULL && !of_empty_ranges_quirk()) {
pr_err("OF: no ranges; cannot translate\n"); pr_debug("OF: no ranges; cannot translate\n");
return 1; return 1;
} }
if (ranges == NULL || rlen == 0) { if (ranges == NULL || rlen == 0) {
...@@ -884,7 +884,7 @@ EXPORT_SYMBOL(of_iomap); ...@@ -884,7 +884,7 @@ EXPORT_SYMBOL(of_iomap);
* return PTR_ERR(base); * return PTR_ERR(base);
*/ */
void __iomem *of_io_request_and_map(struct device_node *np, int index, void __iomem *of_io_request_and_map(struct device_node *np, int index,
char *name) const char *name)
{ {
struct resource res; struct resource res;
void __iomem *mem; void __iomem *mem;
......
...@@ -32,11 +32,12 @@ ...@@ -32,11 +32,12 @@
LIST_HEAD(aliases_lookup); LIST_HEAD(aliases_lookup);
struct device_node *of_allnodes; struct device_node *of_root;
EXPORT_SYMBOL(of_allnodes); EXPORT_SYMBOL(of_root);
struct device_node *of_chosen; struct device_node *of_chosen;
struct device_node *of_aliases; struct device_node *of_aliases;
struct device_node *of_stdout; struct device_node *of_stdout;
static const char *of_stdout_options;
struct kset *of_kset; struct kset *of_kset;
...@@ -48,7 +49,7 @@ struct kset *of_kset; ...@@ -48,7 +49,7 @@ struct kset *of_kset;
*/ */
DEFINE_MUTEX(of_mutex); DEFINE_MUTEX(of_mutex);
/* use when traversing tree through the allnext, child, sibling, /* use when traversing tree through the child, sibling,
* or parent members of struct device_node. * or parent members of struct device_node.
*/ */
DEFINE_RAW_SPINLOCK(devtree_lock); DEFINE_RAW_SPINLOCK(devtree_lock);
...@@ -204,7 +205,7 @@ static int __init of_init(void) ...@@ -204,7 +205,7 @@ static int __init of_init(void)
mutex_unlock(&of_mutex); mutex_unlock(&of_mutex);
/* Symlink in /proc as required by userspace ABI */ /* Symlink in /proc as required by userspace ABI */
if (of_allnodes) if (of_root)
proc_symlink("device-tree", NULL, "/sys/firmware/devicetree/base"); proc_symlink("device-tree", NULL, "/sys/firmware/devicetree/base");
return 0; return 0;
...@@ -245,6 +246,23 @@ struct property *of_find_property(const struct device_node *np, ...@@ -245,6 +246,23 @@ struct property *of_find_property(const struct device_node *np,
} }
EXPORT_SYMBOL(of_find_property); EXPORT_SYMBOL(of_find_property);
struct device_node *__of_find_all_nodes(struct device_node *prev)
{
struct device_node *np;
if (!prev) {
np = of_root;
} else if (prev->child) {
np = prev->child;
} else {
/* Walk back up looking for a sibling, or the end of the structure */
np = prev;
while (np->parent && !np->sibling)
np = np->parent;
np = np->sibling; /* Might be null at the end of the tree */
}
return np;
}
/** /**
* of_find_all_nodes - Get next node in global list * of_find_all_nodes - Get next node in global list
* @prev: Previous node or NULL to start iteration * @prev: Previous node or NULL to start iteration
...@@ -259,10 +277,8 @@ struct device_node *of_find_all_nodes(struct device_node *prev) ...@@ -259,10 +277,8 @@ struct device_node *of_find_all_nodes(struct device_node *prev)
unsigned long flags; unsigned long flags;
raw_spin_lock_irqsave(&devtree_lock, flags); raw_spin_lock_irqsave(&devtree_lock, flags);
np = prev ? prev->allnext : of_allnodes; np = __of_find_all_nodes(prev);
for (; np != NULL; np = np->allnext) of_node_get(np);
if (of_node_get(np))
break;
of_node_put(prev); of_node_put(prev);
raw_spin_unlock_irqrestore(&devtree_lock, flags); raw_spin_unlock_irqrestore(&devtree_lock, flags);
return np; return np;
...@@ -485,7 +501,7 @@ EXPORT_SYMBOL(of_device_is_compatible); ...@@ -485,7 +501,7 @@ EXPORT_SYMBOL(of_device_is_compatible);
* of_machine_is_compatible - Test root of device tree for a given compatible value * of_machine_is_compatible - Test root of device tree for a given compatible value
* @compat: compatible string to look for in root node's compatible property. * @compat: compatible string to look for in root node's compatible property.
* *
* Returns true if the root node has the given value in its * Returns a positive integer if the root node has the given value in its
* compatible property. * compatible property.
*/ */
int of_machine_is_compatible(const char *compat) int of_machine_is_compatible(const char *compat)
...@@ -507,27 +523,27 @@ EXPORT_SYMBOL(of_machine_is_compatible); ...@@ -507,27 +523,27 @@ EXPORT_SYMBOL(of_machine_is_compatible);
* *
* @device: Node to check for availability, with locks already held * @device: Node to check for availability, with locks already held
* *
* Returns 1 if the status property is absent or set to "okay" or "ok", * Returns true if the status property is absent or set to "okay" or "ok",
* 0 otherwise * false otherwise
*/ */
static int __of_device_is_available(const struct device_node *device) static bool __of_device_is_available(const struct device_node *device)
{ {
const char *status; const char *status;
int statlen; int statlen;
if (!device) if (!device)
return 0; return false;
status = __of_get_property(device, "status", &statlen); status = __of_get_property(device, "status", &statlen);
if (status == NULL) if (status == NULL)
return 1; return true;
if (statlen > 0) { if (statlen > 0) {
if (!strcmp(status, "okay") || !strcmp(status, "ok")) if (!strcmp(status, "okay") || !strcmp(status, "ok"))
return 1; return true;
} }
return 0; return false;
} }
/** /**
...@@ -535,13 +551,13 @@ static int __of_device_is_available(const struct device_node *device) ...@@ -535,13 +551,13 @@ static int __of_device_is_available(const struct device_node *device)
* *
* @device: Node to check for availability * @device: Node to check for availability
* *
* Returns 1 if the status property is absent or set to "okay" or "ok", * Returns true if the status property is absent or set to "okay" or "ok",
* 0 otherwise * false otherwise
*/ */
int of_device_is_available(const struct device_node *device) bool of_device_is_available(const struct device_node *device)
{ {
unsigned long flags; unsigned long flags;
int res; bool res;
raw_spin_lock_irqsave(&devtree_lock, flags); raw_spin_lock_irqsave(&devtree_lock, flags);
res = __of_device_is_available(device); res = __of_device_is_available(device);
...@@ -699,10 +715,15 @@ static struct device_node *__of_find_node_by_path(struct device_node *parent, ...@@ -699,10 +715,15 @@ static struct device_node *__of_find_node_by_path(struct device_node *parent,
{ {
struct device_node *child; struct device_node *child;
int len = strchrnul(path, '/') - path; int len = strchrnul(path, '/') - path;
int term;
if (!len) if (!len)
return NULL; return NULL;
term = strchrnul(path, ':') - path;
if (term < len)
len = term;
__for_each_child_of_node(parent, child) { __for_each_child_of_node(parent, child) {
const char *name = strrchr(child->full_name, '/'); const char *name = strrchr(child->full_name, '/');
if (WARN(!name, "malformed device_node %s\n", child->full_name)) if (WARN(!name, "malformed device_node %s\n", child->full_name))
...@@ -715,11 +736,14 @@ static struct device_node *__of_find_node_by_path(struct device_node *parent, ...@@ -715,11 +736,14 @@ static struct device_node *__of_find_node_by_path(struct device_node *parent,
} }
/** /**
* of_find_node_by_path - Find a node matching a full OF path * of_find_node_opts_by_path - Find a node matching a full OF path
* @path: Either the full path to match, or if the path does not * @path: Either the full path to match, or if the path does not
* start with '/', the name of a property of the /aliases * start with '/', the name of a property of the /aliases
* node (an alias). In the case of an alias, the node * node (an alias). In the case of an alias, the node
* matching the alias' value will be returned. * matching the alias' value will be returned.
* @opts: Address of a pointer into which to store the start of
* an options string appended to the end of the path with
* a ':' separator.
* *
* Valid paths: * Valid paths:
* /foo/bar Full path * /foo/bar Full path
...@@ -729,19 +753,23 @@ static struct device_node *__of_find_node_by_path(struct device_node *parent, ...@@ -729,19 +753,23 @@ static struct device_node *__of_find_node_by_path(struct device_node *parent,
* Returns a node pointer with refcount incremented, use * Returns a node pointer with refcount incremented, use
* of_node_put() on it when done. * of_node_put() on it when done.
*/ */
struct device_node *of_find_node_by_path(const char *path) struct device_node *of_find_node_opts_by_path(const char *path, const char **opts)
{ {
struct device_node *np = NULL; struct device_node *np = NULL;
struct property *pp; struct property *pp;
unsigned long flags; unsigned long flags;
const char *separator = strchr(path, ':');
if (opts)
*opts = separator ? separator + 1 : NULL;
if (strcmp(path, "/") == 0) if (strcmp(path, "/") == 0)
return of_node_get(of_allnodes); return of_node_get(of_root);
/* The path could begin with an alias */ /* The path could begin with an alias */
if (*path != '/') { if (*path != '/') {
char *p = strchrnul(path, '/'); char *p = strchrnul(path, '/');
int len = p - path; int len = separator ? separator - path : p - path;
/* of_aliases must not be NULL */ /* of_aliases must not be NULL */
if (!of_aliases) if (!of_aliases)
...@@ -761,7 +789,7 @@ struct device_node *of_find_node_by_path(const char *path) ...@@ -761,7 +789,7 @@ struct device_node *of_find_node_by_path(const char *path)
/* Step down the tree matching path components */ /* Step down the tree matching path components */
raw_spin_lock_irqsave(&devtree_lock, flags); raw_spin_lock_irqsave(&devtree_lock, flags);
if (!np) if (!np)
np = of_node_get(of_allnodes); np = of_node_get(of_root);
while (np && *path == '/') { while (np && *path == '/') {
path++; /* Increment past '/' delimiter */ path++; /* Increment past '/' delimiter */
np = __of_find_node_by_path(np, path); np = __of_find_node_by_path(np, path);
...@@ -770,7 +798,7 @@ struct device_node *of_find_node_by_path(const char *path) ...@@ -770,7 +798,7 @@ struct device_node *of_find_node_by_path(const char *path)
raw_spin_unlock_irqrestore(&devtree_lock, flags); raw_spin_unlock_irqrestore(&devtree_lock, flags);
return np; return np;
} }
EXPORT_SYMBOL(of_find_node_by_path); EXPORT_SYMBOL(of_find_node_opts_by_path);
/** /**
* of_find_node_by_name - Find a node by its "name" property * of_find_node_by_name - Find a node by its "name" property
...@@ -790,8 +818,7 @@ struct device_node *of_find_node_by_name(struct device_node *from, ...@@ -790,8 +818,7 @@ struct device_node *of_find_node_by_name(struct device_node *from,
unsigned long flags; unsigned long flags;
raw_spin_lock_irqsave(&devtree_lock, flags); raw_spin_lock_irqsave(&devtree_lock, flags);
np = from ? from->allnext : of_allnodes; for_each_of_allnodes_from(from, np)
for (; np; np = np->allnext)
if (np->name && (of_node_cmp(np->name, name) == 0) if (np->name && (of_node_cmp(np->name, name) == 0)
&& of_node_get(np)) && of_node_get(np))
break; break;
...@@ -820,8 +847,7 @@ struct device_node *of_find_node_by_type(struct device_node *from, ...@@ -820,8 +847,7 @@ struct device_node *of_find_node_by_type(struct device_node *from,
unsigned long flags; unsigned long flags;
raw_spin_lock_irqsave(&devtree_lock, flags); raw_spin_lock_irqsave(&devtree_lock, flags);
np = from ? from->allnext : of_allnodes; for_each_of_allnodes_from(from, np)
for (; np; np = np->allnext)
if (np->type && (of_node_cmp(np->type, type) == 0) if (np->type && (of_node_cmp(np->type, type) == 0)
&& of_node_get(np)) && of_node_get(np))
break; break;
...@@ -852,12 +878,10 @@ struct device_node *of_find_compatible_node(struct device_node *from, ...@@ -852,12 +878,10 @@ struct device_node *of_find_compatible_node(struct device_node *from,
unsigned long flags; unsigned long flags;
raw_spin_lock_irqsave(&devtree_lock, flags); raw_spin_lock_irqsave(&devtree_lock, flags);
np = from ? from->allnext : of_allnodes; for_each_of_allnodes_from(from, np)
for (; np; np = np->allnext) {
if (__of_device_is_compatible(np, compatible, type, NULL) && if (__of_device_is_compatible(np, compatible, type, NULL) &&
of_node_get(np)) of_node_get(np))
break; break;
}
of_node_put(from); of_node_put(from);
raw_spin_unlock_irqrestore(&devtree_lock, flags); raw_spin_unlock_irqrestore(&devtree_lock, flags);
return np; return np;
...@@ -884,8 +908,7 @@ struct device_node *of_find_node_with_property(struct device_node *from, ...@@ -884,8 +908,7 @@ struct device_node *of_find_node_with_property(struct device_node *from,
unsigned long flags; unsigned long flags;
raw_spin_lock_irqsave(&devtree_lock, flags); raw_spin_lock_irqsave(&devtree_lock, flags);
np = from ? from->allnext : of_allnodes; for_each_of_allnodes_from(from, np) {
for (; np; np = np->allnext) {
for (pp = np->properties; pp; pp = pp->next) { for (pp = np->properties; pp; pp = pp->next) {
if (of_prop_cmp(pp->name, prop_name) == 0) { if (of_prop_cmp(pp->name, prop_name) == 0) {
of_node_get(np); of_node_get(np);
...@@ -923,7 +946,7 @@ const struct of_device_id *__of_match_node(const struct of_device_id *matches, ...@@ -923,7 +946,7 @@ const struct of_device_id *__of_match_node(const struct of_device_id *matches,
} }
/** /**
* of_match_node - Tell if an device_node has a matching of_match structure * of_match_node - Tell if a device_node has a matching of_match structure
* @matches: array of of device match structures to search in * @matches: array of of device match structures to search in
* @node: the of device structure to match against * @node: the of device structure to match against
* *
...@@ -967,8 +990,7 @@ struct device_node *of_find_matching_node_and_match(struct device_node *from, ...@@ -967,8 +990,7 @@ struct device_node *of_find_matching_node_and_match(struct device_node *from,
*match = NULL; *match = NULL;
raw_spin_lock_irqsave(&devtree_lock, flags); raw_spin_lock_irqsave(&devtree_lock, flags);
np = from ? from->allnext : of_allnodes; for_each_of_allnodes_from(from, np) {
for (; np; np = np->allnext) {
m = __of_match_node(matches, np); m = __of_match_node(matches, np);
if (m && of_node_get(np)) { if (m && of_node_get(np)) {
if (match) if (match)
...@@ -1025,7 +1047,7 @@ struct device_node *of_find_node_by_phandle(phandle handle) ...@@ -1025,7 +1047,7 @@ struct device_node *of_find_node_by_phandle(phandle handle)
return NULL; return NULL;
raw_spin_lock_irqsave(&devtree_lock, flags); raw_spin_lock_irqsave(&devtree_lock, flags);
for (np = of_allnodes; np; np = np->allnext) for_each_of_allnodes(np)
if (np->phandle == handle) if (np->phandle == handle)
break; break;
of_node_get(np); of_node_get(np);
...@@ -1350,7 +1372,7 @@ int of_property_match_string(struct device_node *np, const char *propname, ...@@ -1350,7 +1372,7 @@ int of_property_match_string(struct device_node *np, const char *propname,
EXPORT_SYMBOL_GPL(of_property_match_string); EXPORT_SYMBOL_GPL(of_property_match_string);
/** /**
* of_property_read_string_util() - Utility helper for parsing string properties * of_property_read_string_helper() - Utility helper for parsing string properties
* @np: device node from which the property value is to be read. * @np: device node from which the property value is to be read.
* @propname: name of the property to be searched. * @propname: name of the property to be searched.
* @out_strs: output array of string pointers. * @out_strs: output array of string pointers.
...@@ -1549,7 +1571,7 @@ EXPORT_SYMBOL(of_parse_phandle); ...@@ -1549,7 +1571,7 @@ EXPORT_SYMBOL(of_parse_phandle);
* Returns 0 on success and fills out_args, on error returns appropriate * Returns 0 on success and fills out_args, on error returns appropriate
* errno value. * errno value.
* *
* Caller is responsible to call of_node_put() on the returned out_args->node * Caller is responsible to call of_node_put() on the returned out_args->np
* pointer. * pointer.
* *
* Example: * Example:
...@@ -1592,7 +1614,7 @@ EXPORT_SYMBOL(of_parse_phandle_with_args); ...@@ -1592,7 +1614,7 @@ EXPORT_SYMBOL(of_parse_phandle_with_args);
* Returns 0 on success and fills out_args, on error returns appropriate * Returns 0 on success and fills out_args, on error returns appropriate
* errno value. * errno value.
* *
* Caller is responsible to call of_node_put() on the returned out_args->node * Caller is responsible to call of_node_put() on the returned out_args->np
* pointer. * pointer.
* *
* Example: * Example:
...@@ -1838,14 +1860,14 @@ static void of_alias_add(struct alias_prop *ap, struct device_node *np, ...@@ -1838,14 +1860,14 @@ static void of_alias_add(struct alias_prop *ap, struct device_node *np,
} }
/** /**
* of_alias_scan - Scan all properties of 'aliases' node * of_alias_scan - Scan all properties of the 'aliases' node
* *
* The function scans all the properties of 'aliases' node and populate * The function scans all the properties of the 'aliases' node and populates
* the the global lookup table with the properties. It returns the * the global lookup table with the properties. It returns the
* number of alias_prop found, or error code in error case. * number of alias properties found, or an error code in case of failure.
* *
* @dt_alloc: An allocator that provides a virtual address to memory * @dt_alloc: An allocator that provides a virtual address to memory
* for the resulting tree * for storing the resulting tree
*/ */
void of_alias_scan(void * (*dt_alloc)(u64 size, u64 align)) void of_alias_scan(void * (*dt_alloc)(u64 size, u64 align))
{ {
...@@ -1864,7 +1886,7 @@ void of_alias_scan(void * (*dt_alloc)(u64 size, u64 align)) ...@@ -1864,7 +1886,7 @@ void of_alias_scan(void * (*dt_alloc)(u64 size, u64 align))
if (IS_ENABLED(CONFIG_PPC) && !name) if (IS_ENABLED(CONFIG_PPC) && !name)
name = of_get_property(of_aliases, "stdout", NULL); name = of_get_property(of_aliases, "stdout", NULL);
if (name) if (name)
of_stdout = of_find_node_by_path(name); of_stdout = of_find_node_opts_by_path(name, &of_stdout_options);
} }
if (!of_aliases) if (!of_aliases)
...@@ -1990,7 +2012,8 @@ bool of_console_check(struct device_node *dn, char *name, int index) ...@@ -1990,7 +2012,8 @@ bool of_console_check(struct device_node *dn, char *name, int index)
{ {
if (!dn || dn != of_stdout || console_set_on_cmdline) if (!dn || dn != of_stdout || console_set_on_cmdline)
return false; return false;
return !add_preferred_console(name, index, NULL); return !add_preferred_console(name, index,
kstrdup(of_stdout_options, GFP_KERNEL));
} }
EXPORT_SYMBOL_GPL(of_console_check); EXPORT_SYMBOL_GPL(of_console_check);
......
...@@ -77,18 +77,132 @@ int of_reconfig_notifier_unregister(struct notifier_block *nb) ...@@ -77,18 +77,132 @@ int of_reconfig_notifier_unregister(struct notifier_block *nb)
} }
EXPORT_SYMBOL_GPL(of_reconfig_notifier_unregister); EXPORT_SYMBOL_GPL(of_reconfig_notifier_unregister);
int of_reconfig_notify(unsigned long action, void *p) #ifdef DEBUG
const char *action_names[] = {
[OF_RECONFIG_ATTACH_NODE] = "ATTACH_NODE",
[OF_RECONFIG_DETACH_NODE] = "DETACH_NODE",
[OF_RECONFIG_ADD_PROPERTY] = "ADD_PROPERTY",
[OF_RECONFIG_REMOVE_PROPERTY] = "REMOVE_PROPERTY",
[OF_RECONFIG_UPDATE_PROPERTY] = "UPDATE_PROPERTY",
};
#endif
int of_reconfig_notify(unsigned long action, struct of_reconfig_data *p)
{ {
int rc; int rc;
#ifdef DEBUG
struct of_reconfig_data *pr = p;
switch (action) {
case OF_RECONFIG_ATTACH_NODE:
case OF_RECONFIG_DETACH_NODE:
pr_debug("of/notify %-15s %s\n", action_names[action],
pr->dn->full_name);
break;
case OF_RECONFIG_ADD_PROPERTY:
case OF_RECONFIG_REMOVE_PROPERTY:
case OF_RECONFIG_UPDATE_PROPERTY:
pr_debug("of/notify %-15s %s:%s\n", action_names[action],
pr->dn->full_name, pr->prop->name);
break;
}
#endif
rc = blocking_notifier_call_chain(&of_reconfig_chain, action, p); rc = blocking_notifier_call_chain(&of_reconfig_chain, action, p);
return notifier_to_errno(rc); return notifier_to_errno(rc);
} }
/*
* of_reconfig_get_state_change() - Returns new state of device
* @action - action of the of notifier
* @arg - argument of the of notifier
*
* Returns the new state of a device based on the notifier used.
* Returns 0 on device going from enabled to disabled, 1 on device
* going from disabled to enabled and -1 on no change.
*/
int of_reconfig_get_state_change(unsigned long action, struct of_reconfig_data *pr)
{
struct property *prop, *old_prop = NULL;
int is_status, status_state, old_status_state, prev_state, new_state;
/* figure out if a device should be created or destroyed */
switch (action) {
case OF_RECONFIG_ATTACH_NODE:
case OF_RECONFIG_DETACH_NODE:
prop = of_find_property(pr->dn, "status", NULL);
break;
case OF_RECONFIG_ADD_PROPERTY:
case OF_RECONFIG_REMOVE_PROPERTY:
prop = pr->prop;
break;
case OF_RECONFIG_UPDATE_PROPERTY:
prop = pr->prop;
old_prop = pr->old_prop;
break;
default:
return OF_RECONFIG_NO_CHANGE;
}
is_status = 0;
status_state = -1;
old_status_state = -1;
prev_state = -1;
new_state = -1;
if (prop && !strcmp(prop->name, "status")) {
is_status = 1;
status_state = !strcmp(prop->value, "okay") ||
!strcmp(prop->value, "ok");
if (old_prop)
old_status_state = !strcmp(old_prop->value, "okay") ||
!strcmp(old_prop->value, "ok");
}
switch (action) {
case OF_RECONFIG_ATTACH_NODE:
prev_state = 0;
/* -1 & 0 status either missing or okay */
new_state = status_state != 0;
break;
case OF_RECONFIG_DETACH_NODE:
/* -1 & 0 status either missing or okay */
prev_state = status_state != 0;
new_state = 0;
break;
case OF_RECONFIG_ADD_PROPERTY:
if (is_status) {
/* no status property -> enabled (legacy) */
prev_state = 1;
new_state = status_state;
}
break;
case OF_RECONFIG_REMOVE_PROPERTY:
if (is_status) {
prev_state = status_state;
/* no status property -> enabled (legacy) */
new_state = 1;
}
break;
case OF_RECONFIG_UPDATE_PROPERTY:
if (is_status) {
prev_state = old_status_state != 0;
new_state = status_state != 0;
}
break;
}
if (prev_state == new_state)
return OF_RECONFIG_NO_CHANGE;
return new_state ? OF_RECONFIG_CHANGE_ADD : OF_RECONFIG_CHANGE_REMOVE;
}
EXPORT_SYMBOL_GPL(of_reconfig_get_state_change);
int of_property_notify(int action, struct device_node *np, int of_property_notify(int action, struct device_node *np,
struct property *prop, struct property *oldprop) struct property *prop, struct property *oldprop)
{ {
struct of_prop_reconfig pr; struct of_reconfig_data pr;
/* only call notifiers if the node is attached */ /* only call notifiers if the node is attached */
if (!of_node_is_attached(np)) if (!of_node_is_attached(np))
...@@ -117,8 +231,6 @@ void __of_attach_node(struct device_node *np) ...@@ -117,8 +231,6 @@ void __of_attach_node(struct device_node *np)
np->child = NULL; np->child = NULL;
np->sibling = np->parent->child; np->sibling = np->parent->child;
np->allnext = np->parent->allnext;
np->parent->allnext = np;
np->parent->child = np; np->parent->child = np;
of_node_clear_flag(np, OF_DETACHED); of_node_clear_flag(np, OF_DETACHED);
} }
...@@ -128,8 +240,12 @@ void __of_attach_node(struct device_node *np) ...@@ -128,8 +240,12 @@ void __of_attach_node(struct device_node *np)
*/ */
int of_attach_node(struct device_node *np) int of_attach_node(struct device_node *np)
{ {
struct of_reconfig_data rd;
unsigned long flags; unsigned long flags;
memset(&rd, 0, sizeof(rd));
rd.dn = np;
mutex_lock(&of_mutex); mutex_lock(&of_mutex);
raw_spin_lock_irqsave(&devtree_lock, flags); raw_spin_lock_irqsave(&devtree_lock, flags);
__of_attach_node(np); __of_attach_node(np);
...@@ -138,7 +254,7 @@ int of_attach_node(struct device_node *np) ...@@ -138,7 +254,7 @@ int of_attach_node(struct device_node *np)
__of_attach_node_sysfs(np); __of_attach_node_sysfs(np);
mutex_unlock(&of_mutex); mutex_unlock(&of_mutex);
of_reconfig_notify(OF_RECONFIG_ATTACH_NODE, np); of_reconfig_notify(OF_RECONFIG_ATTACH_NODE, &rd);
return 0; return 0;
} }
...@@ -154,17 +270,6 @@ void __of_detach_node(struct device_node *np) ...@@ -154,17 +270,6 @@ void __of_detach_node(struct device_node *np)
if (WARN_ON(!parent)) if (WARN_ON(!parent))
return; return;
if (of_allnodes == np)
of_allnodes = np->allnext;
else {
struct device_node *prev;
for (prev = of_allnodes;
prev->allnext != np;
prev = prev->allnext)
;
prev->allnext = np->allnext;
}
if (parent->child == np) if (parent->child == np)
parent->child = np->sibling; parent->child = np->sibling;
else { else {
...@@ -187,9 +292,13 @@ void __of_detach_node(struct device_node *np) ...@@ -187,9 +292,13 @@ void __of_detach_node(struct device_node *np)
*/ */
int of_detach_node(struct device_node *np) int of_detach_node(struct device_node *np)
{ {
struct of_reconfig_data rd;
unsigned long flags; unsigned long flags;
int rc = 0; int rc = 0;
memset(&rd, 0, sizeof(rd));
rd.dn = np;
mutex_lock(&of_mutex); mutex_lock(&of_mutex);
raw_spin_lock_irqsave(&devtree_lock, flags); raw_spin_lock_irqsave(&devtree_lock, flags);
__of_detach_node(np); __of_detach_node(np);
...@@ -198,7 +307,7 @@ int of_detach_node(struct device_node *np) ...@@ -198,7 +307,7 @@ int of_detach_node(struct device_node *np)
__of_detach_node_sysfs(np); __of_detach_node_sysfs(np);
mutex_unlock(&of_mutex); mutex_unlock(&of_mutex);
of_reconfig_notify(OF_RECONFIG_DETACH_NODE, np); of_reconfig_notify(OF_RECONFIG_DETACH_NODE, &rd);
return rc; return rc;
} }
...@@ -285,36 +394,54 @@ struct property *__of_prop_dup(const struct property *prop, gfp_t allocflags) ...@@ -285,36 +394,54 @@ struct property *__of_prop_dup(const struct property *prop, gfp_t allocflags)
} }
/** /**
* __of_node_alloc() - Create an empty device node dynamically. * __of_node_dup() - Duplicate or create an empty device node dynamically.
* @full_name: Full name of the new device node * @fmt: Format string (plus vargs) for new full name of the device node
* @allocflags: Allocation flags (typically pass GFP_KERNEL)
* *
* Create an empty device tree node, suitable for further modification. * Create an device tree node, either by duplicating an empty node or by allocating
* The node data are dynamically allocated and all the node flags * an empty one suitable for further modification. The node data are
* have the OF_DYNAMIC & OF_DETACHED bits set. * dynamically allocated and all the node flags have the OF_DYNAMIC &
* Returns the newly allocated node or NULL on out of memory error. * OF_DETACHED bits set. Returns the newly allocated node or NULL on out of
* memory error.
*/ */
struct device_node *__of_node_alloc(const char *full_name, gfp_t allocflags) struct device_node *__of_node_dup(const struct device_node *np, const char *fmt, ...)
{ {
va_list vargs;
struct device_node *node; struct device_node *node;
node = kzalloc(sizeof(*node), allocflags); node = kzalloc(sizeof(*node), GFP_KERNEL);
if (!node) if (!node)
return NULL; return NULL;
va_start(vargs, fmt);
node->full_name = kvasprintf(GFP_KERNEL, fmt, vargs);
va_end(vargs);
if (!node->full_name) {
kfree(node);
return NULL;
}
node->full_name = kstrdup(full_name, allocflags);
of_node_set_flag(node, OF_DYNAMIC); of_node_set_flag(node, OF_DYNAMIC);
of_node_set_flag(node, OF_DETACHED); of_node_set_flag(node, OF_DETACHED);
if (!node->full_name)
goto err_free;
of_node_init(node); of_node_init(node);
/* Iterate over and duplicate all properties */
if (np) {
struct property *pp, *new_pp;
for_each_property_of_node(np, pp) {
new_pp = __of_prop_dup(pp, GFP_KERNEL);
if (!new_pp)
goto err_prop;
if (__of_add_property(node, new_pp)) {
kfree(new_pp->name);
kfree(new_pp->value);
kfree(new_pp);
goto err_prop;
}
}
}
return node; return node;
err_free: err_prop:
kfree(node->full_name); of_node_put(node); /* Frees the node and properties */
kfree(node);
return NULL; return NULL;
} }
...@@ -330,27 +457,15 @@ static void __of_changeset_entry_dump(struct of_changeset_entry *ce) ...@@ -330,27 +457,15 @@ static void __of_changeset_entry_dump(struct of_changeset_entry *ce)
{ {
switch (ce->action) { switch (ce->action) {
case OF_RECONFIG_ADD_PROPERTY: case OF_RECONFIG_ADD_PROPERTY:
pr_debug("%p: %s %s/%s\n",
ce, "ADD_PROPERTY ", ce->np->full_name,
ce->prop->name);
break;
case OF_RECONFIG_REMOVE_PROPERTY: case OF_RECONFIG_REMOVE_PROPERTY:
pr_debug("%p: %s %s/%s\n",
ce, "REMOVE_PROPERTY", ce->np->full_name,
ce->prop->name);
break;
case OF_RECONFIG_UPDATE_PROPERTY: case OF_RECONFIG_UPDATE_PROPERTY:
pr_debug("%p: %s %s/%s\n", pr_debug("of/cset<%p> %-15s %s/%s\n", ce, action_names[ce->action],
ce, "UPDATE_PROPERTY", ce->np->full_name, ce->np->full_name, ce->prop->name);
ce->prop->name);
break; break;
case OF_RECONFIG_ATTACH_NODE: case OF_RECONFIG_ATTACH_NODE:
pr_debug("%p: %s %s\n",
ce, "ATTACH_NODE ", ce->np->full_name);
break;
case OF_RECONFIG_DETACH_NODE: case OF_RECONFIG_DETACH_NODE:
pr_debug("%p: %s %s\n", pr_debug("of/cset<%p> %-15s %s\n", ce, action_names[ce->action],
ce, "DETACH_NODE ", ce->np->full_name); ce->np->full_name);
break; break;
} }
} }
...@@ -388,6 +503,7 @@ static void __of_changeset_entry_invert(struct of_changeset_entry *ce, ...@@ -388,6 +503,7 @@ static void __of_changeset_entry_invert(struct of_changeset_entry *ce,
static void __of_changeset_entry_notify(struct of_changeset_entry *ce, bool revert) static void __of_changeset_entry_notify(struct of_changeset_entry *ce, bool revert)
{ {
struct of_reconfig_data rd;
struct of_changeset_entry ce_inverted; struct of_changeset_entry ce_inverted;
int ret; int ret;
...@@ -399,7 +515,9 @@ static void __of_changeset_entry_notify(struct of_changeset_entry *ce, bool reve ...@@ -399,7 +515,9 @@ static void __of_changeset_entry_notify(struct of_changeset_entry *ce, bool reve
switch (ce->action) { switch (ce->action) {
case OF_RECONFIG_ATTACH_NODE: case OF_RECONFIG_ATTACH_NODE:
case OF_RECONFIG_DETACH_NODE: case OF_RECONFIG_DETACH_NODE:
ret = of_reconfig_notify(ce->action, ce->np); memset(&rd, 0, sizeof(rd));
rd.dn = ce->np;
ret = of_reconfig_notify(ce->action, &rd);
break; break;
case OF_RECONFIG_ADD_PROPERTY: case OF_RECONFIG_ADD_PROPERTY:
case OF_RECONFIG_REMOVE_PROPERTY: case OF_RECONFIG_REMOVE_PROPERTY:
......
...@@ -9,6 +9,7 @@ ...@@ -9,6 +9,7 @@
* version 2 as published by the Free Software Foundation. * version 2 as published by the Free Software Foundation.
*/ */
#include <linux/crc32.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/initrd.h> #include <linux/initrd.h>
#include <linux/memblock.h> #include <linux/memblock.h>
...@@ -22,6 +23,7 @@ ...@@ -22,6 +23,7 @@
#include <linux/libfdt.h> #include <linux/libfdt.h>
#include <linux/debugfs.h> #include <linux/debugfs.h>
#include <linux/serial_core.h> #include <linux/serial_core.h>
#include <linux/sysfs.h>
#include <asm/setup.h> /* for COMMAND_LINE_SIZE */ #include <asm/setup.h> /* for COMMAND_LINE_SIZE */
#include <asm/page.h> #include <asm/page.h>
...@@ -145,15 +147,15 @@ static void *unflatten_dt_alloc(void **mem, unsigned long size, ...@@ -145,15 +147,15 @@ static void *unflatten_dt_alloc(void **mem, unsigned long size,
* @mem: Memory chunk to use for allocating device nodes and properties * @mem: Memory chunk to use for allocating device nodes and properties
* @p: pointer to node in flat tree * @p: pointer to node in flat tree
* @dad: Parent struct device_node * @dad: Parent struct device_node
* @allnextpp: pointer to ->allnext from last allocated device_node
* @fpsize: Size of the node path up at the current depth. * @fpsize: Size of the node path up at the current depth.
*/ */
static void * unflatten_dt_node(void *blob, static void * unflatten_dt_node(void *blob,
void *mem, void *mem,
int *poffset, int *poffset,
struct device_node *dad, struct device_node *dad,
struct device_node ***allnextpp, struct device_node **nodepp,
unsigned long fpsize) unsigned long fpsize,
bool dryrun)
{ {
const __be32 *p; const __be32 *p;
struct device_node *np; struct device_node *np;
...@@ -200,7 +202,7 @@ static void * unflatten_dt_node(void *blob, ...@@ -200,7 +202,7 @@ static void * unflatten_dt_node(void *blob,
np = unflatten_dt_alloc(&mem, sizeof(struct device_node) + allocl, np = unflatten_dt_alloc(&mem, sizeof(struct device_node) + allocl,
__alignof__(struct device_node)); __alignof__(struct device_node));
if (allnextpp) { if (!dryrun) {
char *fn; char *fn;
of_node_init(np); of_node_init(np);
np->full_name = fn = ((char *)np) + sizeof(*np); np->full_name = fn = ((char *)np) + sizeof(*np);
...@@ -222,16 +224,10 @@ static void * unflatten_dt_node(void *blob, ...@@ -222,16 +224,10 @@ static void * unflatten_dt_node(void *blob,
memcpy(fn, pathp, l); memcpy(fn, pathp, l);
prev_pp = &np->properties; prev_pp = &np->properties;
**allnextpp = np;
*allnextpp = &np->allnext;
if (dad != NULL) { if (dad != NULL) {
np->parent = dad; np->parent = dad;
/* we temporarily use the next field as `last_child'*/ np->sibling = dad->child;
if (dad->next == NULL)
dad->child = np; dad->child = np;
else
dad->next->sibling = np;
dad->next = np;
} }
} }
/* process properties */ /* process properties */
...@@ -254,7 +250,7 @@ static void * unflatten_dt_node(void *blob, ...@@ -254,7 +250,7 @@ static void * unflatten_dt_node(void *blob,
has_name = 1; has_name = 1;
pp = unflatten_dt_alloc(&mem, sizeof(struct property), pp = unflatten_dt_alloc(&mem, sizeof(struct property),
__alignof__(struct property)); __alignof__(struct property));
if (allnextpp) { if (!dryrun) {
/* We accept flattened tree phandles either in /* We accept flattened tree phandles either in
* ePAPR-style "phandle" properties, or the * ePAPR-style "phandle" properties, or the
* legacy "linux,phandle" properties. If both * legacy "linux,phandle" properties. If both
...@@ -296,7 +292,7 @@ static void * unflatten_dt_node(void *blob, ...@@ -296,7 +292,7 @@ static void * unflatten_dt_node(void *blob,
sz = (pa - ps) + 1; sz = (pa - ps) + 1;
pp = unflatten_dt_alloc(&mem, sizeof(struct property) + sz, pp = unflatten_dt_alloc(&mem, sizeof(struct property) + sz,
__alignof__(struct property)); __alignof__(struct property));
if (allnextpp) { if (!dryrun) {
pp->name = "name"; pp->name = "name";
pp->length = sz; pp->length = sz;
pp->value = pp + 1; pp->value = pp + 1;
...@@ -308,7 +304,7 @@ static void * unflatten_dt_node(void *blob, ...@@ -308,7 +304,7 @@ static void * unflatten_dt_node(void *blob,
(char *)pp->value); (char *)pp->value);
} }
} }
if (allnextpp) { if (!dryrun) {
*prev_pp = NULL; *prev_pp = NULL;
np->name = of_get_property(np, "name", NULL); np->name = of_get_property(np, "name", NULL);
np->type = of_get_property(np, "device_type", NULL); np->type = of_get_property(np, "device_type", NULL);
...@@ -324,12 +320,30 @@ static void * unflatten_dt_node(void *blob, ...@@ -324,12 +320,30 @@ static void * unflatten_dt_node(void *blob,
if (depth < 0) if (depth < 0)
depth = 0; depth = 0;
while (*poffset > 0 && depth > old_depth) while (*poffset > 0 && depth > old_depth)
mem = unflatten_dt_node(blob, mem, poffset, np, allnextpp, mem = unflatten_dt_node(blob, mem, poffset, np, NULL,
fpsize); fpsize, dryrun);
if (*poffset < 0 && *poffset != -FDT_ERR_NOTFOUND) if (*poffset < 0 && *poffset != -FDT_ERR_NOTFOUND)
pr_err("unflatten: error %d processing FDT\n", *poffset); pr_err("unflatten: error %d processing FDT\n", *poffset);
/*
* Reverse the child list. Some drivers assumes node order matches .dts
* node order
*/
if (!dryrun && np->child) {
struct device_node *child = np->child;
np->child = NULL;
while (child) {
struct device_node *next = child->sibling;
child->sibling = np->child;
np->child = child;
child = next;
}
}
if (nodepp)
*nodepp = np;
return mem; return mem;
} }
...@@ -352,7 +366,6 @@ static void __unflatten_device_tree(void *blob, ...@@ -352,7 +366,6 @@ static void __unflatten_device_tree(void *blob,
unsigned long size; unsigned long size;
int start; int start;
void *mem; void *mem;
struct device_node **allnextp = mynodes;
pr_debug(" -> unflatten_device_tree()\n"); pr_debug(" -> unflatten_device_tree()\n");
...@@ -373,7 +386,7 @@ static void __unflatten_device_tree(void *blob, ...@@ -373,7 +386,7 @@ static void __unflatten_device_tree(void *blob,
/* First pass, scan for size */ /* First pass, scan for size */
start = 0; start = 0;
size = (unsigned long)unflatten_dt_node(blob, NULL, &start, NULL, NULL, 0); size = (unsigned long)unflatten_dt_node(blob, NULL, &start, NULL, NULL, 0, true);
size = ALIGN(size, 4); size = ALIGN(size, 4);
pr_debug(" size is %lx, allocating...\n", size); pr_debug(" size is %lx, allocating...\n", size);
...@@ -388,11 +401,10 @@ static void __unflatten_device_tree(void *blob, ...@@ -388,11 +401,10 @@ static void __unflatten_device_tree(void *blob,
/* Second pass, do actual unflattening */ /* Second pass, do actual unflattening */
start = 0; start = 0;
unflatten_dt_node(blob, mem, &start, NULL, &allnextp, 0); unflatten_dt_node(blob, mem, &start, NULL, mynodes, 0, false);
if (be32_to_cpup(mem + size) != 0xdeadbeef) if (be32_to_cpup(mem + size) != 0xdeadbeef)
pr_warning("End of tree marker overwritten: %08x\n", pr_warning("End of tree marker overwritten: %08x\n",
be32_to_cpup(mem + size)); be32_to_cpup(mem + size));
*allnextp = NULL;
pr_debug(" <- unflatten_device_tree()\n"); pr_debug(" <- unflatten_device_tree()\n");
} }
...@@ -425,6 +437,8 @@ void *initial_boot_params; ...@@ -425,6 +437,8 @@ void *initial_boot_params;
#ifdef CONFIG_OF_EARLY_FLATTREE #ifdef CONFIG_OF_EARLY_FLATTREE
static u32 of_fdt_crc32;
/** /**
* res_mem_reserve_reg() - reserve all memory described in 'reg' property * res_mem_reserve_reg() - reserve all memory described in 'reg' property
*/ */
...@@ -930,6 +944,11 @@ void __init __weak early_init_dt_add_memory_arch(u64 base, u64 size) ...@@ -930,6 +944,11 @@ void __init __weak early_init_dt_add_memory_arch(u64 base, u64 size)
const u64 phys_offset = __pa(PAGE_OFFSET); const u64 phys_offset = __pa(PAGE_OFFSET);
if (!PAGE_ALIGNED(base)) { if (!PAGE_ALIGNED(base)) {
if (size < PAGE_SIZE - (base & ~PAGE_MASK)) {
pr_warn("Ignoring memory block 0x%llx - 0x%llx\n",
base, base + size);
return;
}
size -= PAGE_SIZE - (base & ~PAGE_MASK); size -= PAGE_SIZE - (base & ~PAGE_MASK);
base = PAGE_ALIGN(base); base = PAGE_ALIGN(base);
} }
...@@ -992,15 +1011,14 @@ bool __init early_init_dt_verify(void *params) ...@@ -992,15 +1011,14 @@ bool __init early_init_dt_verify(void *params)
if (!params) if (!params)
return false; return false;
/* Setup flat device-tree pointer */
initial_boot_params = params;
/* check device tree validity */ /* check device tree validity */
if (fdt_check_header(params)) { if (fdt_check_header(params))
initial_boot_params = NULL;
return false; return false;
}
/* Setup flat device-tree pointer */
initial_boot_params = params;
of_fdt_crc32 = crc32_be(~0, initial_boot_params,
fdt_totalsize(initial_boot_params));
return true; return true;
} }
...@@ -1039,7 +1057,7 @@ bool __init early_init_dt_scan(void *params) ...@@ -1039,7 +1057,7 @@ bool __init early_init_dt_scan(void *params)
*/ */
void __init unflatten_device_tree(void) void __init unflatten_device_tree(void)
{ {
__unflatten_device_tree(initial_boot_params, &of_allnodes, __unflatten_device_tree(initial_boot_params, &of_root,
early_init_dt_alloc_memory_arch); early_init_dt_alloc_memory_arch);
/* Get pointer to "/chosen" and "/aliases" nodes for use everywhere */ /* Get pointer to "/chosen" and "/aliases" nodes for use everywhere */
...@@ -1078,27 +1096,32 @@ void __init unflatten_and_copy_device_tree(void) ...@@ -1078,27 +1096,32 @@ void __init unflatten_and_copy_device_tree(void)
unflatten_device_tree(); unflatten_device_tree();
} }
#if defined(CONFIG_DEBUG_FS) && defined(DEBUG) #ifdef CONFIG_SYSFS
static struct debugfs_blob_wrapper flat_dt_blob; static ssize_t of_fdt_raw_read(struct file *filp, struct kobject *kobj,
struct bin_attribute *bin_attr,
static int __init of_flat_dt_debugfs_export_fdt(void) char *buf, loff_t off, size_t count)
{ {
struct dentry *d = debugfs_create_dir("device-tree", NULL); memcpy(buf, initial_boot_params + off, count);
return count;
if (!d) }
return -ENOENT;
flat_dt_blob.data = initial_boot_params; static int __init of_fdt_raw_init(void)
flat_dt_blob.size = fdt_totalsize(initial_boot_params); {
static struct bin_attribute of_fdt_raw_attr =
__BIN_ATTR(fdt, S_IRUSR, of_fdt_raw_read, NULL, 0);
d = debugfs_create_blob("flat-device-tree", S_IFREG | S_IRUSR, if (!initial_boot_params)
d, &flat_dt_blob); return 0;
if (!d)
return -ENOENT;
if (of_fdt_crc32 != crc32_be(~0, initial_boot_params,
fdt_totalsize(initial_boot_params))) {
pr_warn("fdt: not creating '/sys/firmware/fdt': CRC check failed\n");
return 0; return 0;
}
of_fdt_raw_attr.size = fdt_totalsize(initial_boot_params);
return sysfs_create_bin_file(firmware_kobj, &of_fdt_raw_attr);
} }
module_init(of_flat_dt_debugfs_export_fdt); late_initcall(of_fdt_raw_init);
#endif #endif
#endif /* CONFIG_OF_EARLY_FLATTREE */ #endif /* CONFIG_OF_EARLY_FLATTREE */
...@@ -61,7 +61,7 @@ static inline int of_property_notify(int action, struct device_node *np, ...@@ -61,7 +61,7 @@ static inline int of_property_notify(int action, struct device_node *np,
* own the devtree lock or work on detached trees only. * own the devtree lock or work on detached trees only.
*/ */
struct property *__of_prop_dup(const struct property *prop, gfp_t allocflags); struct property *__of_prop_dup(const struct property *prop, gfp_t allocflags);
struct device_node *__of_node_alloc(const char *full_name, gfp_t allocflags); __printf(2, 3) struct device_node *__of_node_dup(const struct device_node *np, const char *fmt, ...);
extern const void *__of_get_property(const struct device_node *np, extern const void *__of_get_property(const struct device_node *np,
const char *name, int *lenp); const char *name, int *lenp);
......
/*
* Functions for working with device tree overlays
*
* Copyright (C) 2012 Pantelis Antoniou <panto@antoniou-consulting.com>
* Copyright (C) 2012 Texas Instruments Inc.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation.
*/
#undef DEBUG
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/string.h>
#include <linux/ctype.h>
#include <linux/errno.h>
#include <linux/string.h>
#include <linux/slab.h>
#include <linux/err.h>
#include "of_private.h"
/**
* struct of_overlay_info - Holds a single overlay info
* @target: target of the overlay operation
* @overlay: pointer to the overlay contents node
*
* Holds a single overlay state, including all the overlay logs &
* records.
*/
struct of_overlay_info {
struct device_node *target;
struct device_node *overlay;
};
/**
* struct of_overlay - Holds a complete overlay transaction
* @node: List on which we are located
* @count: Count of ovinfo structures
* @ovinfo_tab: Overlay info table (count sized)
* @cset: Changeset to be used
*
* Holds a complete overlay transaction
*/
struct of_overlay {
int id;
struct list_head node;
int count;
struct of_overlay_info *ovinfo_tab;
struct of_changeset cset;
};
static int of_overlay_apply_one(struct of_overlay *ov,
struct device_node *target, const struct device_node *overlay);
static int of_overlay_apply_single_property(struct of_overlay *ov,
struct device_node *target, struct property *prop)
{
struct property *propn, *tprop;
/* NOTE: Multiple changes of single properties not supported */
tprop = of_find_property(target, prop->name, NULL);
/* special properties are not meant to be updated (silent NOP) */
if (of_prop_cmp(prop->name, "name") == 0 ||
of_prop_cmp(prop->name, "phandle") == 0 ||
of_prop_cmp(prop->name, "linux,phandle") == 0)
return 0;
propn = __of_prop_dup(prop, GFP_KERNEL);
if (propn == NULL)
return -ENOMEM;
/* not found? add */
if (tprop == NULL)
return of_changeset_add_property(&ov->cset, target, propn);
/* found? update */
return of_changeset_update_property(&ov->cset, target, propn);
}
static int of_overlay_apply_single_device_node(struct of_overlay *ov,
struct device_node *target, struct device_node *child)
{
const char *cname;
struct device_node *tchild, *grandchild;
int ret = 0;
cname = kbasename(child->full_name);
if (cname == NULL)
return -ENOMEM;
/* NOTE: Multiple mods of created nodes not supported */
tchild = of_get_child_by_name(target, cname);
if (tchild != NULL) {
/* apply overlay recursively */
ret = of_overlay_apply_one(ov, tchild, child);
of_node_put(tchild);
} else {
/* create empty tree as a target */
tchild = __of_node_dup(child, "%s/%s", target->full_name, cname);
if (!tchild)
return -ENOMEM;
/* point to parent */
tchild->parent = target;
ret = of_changeset_attach_node(&ov->cset, tchild);
if (ret)
return ret;
ret = of_overlay_apply_one(ov, tchild, child);
if (ret)
return ret;
/* The properties are already copied, now do the child nodes */
for_each_child_of_node(child, grandchild) {
ret = of_overlay_apply_single_device_node(ov, tchild, grandchild);
if (ret) {
pr_err("%s: Failed to apply single node @%s/%s\n",
__func__, tchild->full_name,
grandchild->name);
return ret;
}
}
}
return ret;
}
/*
* Apply a single overlay node recursively.
*
* Note that the in case of an error the target node is left
* in a inconsistent state. Error recovery should be performed
* by using the changeset.
*/
static int of_overlay_apply_one(struct of_overlay *ov,
struct device_node *target, const struct device_node *overlay)
{
struct device_node *child;
struct property *prop;
int ret;
for_each_property_of_node(overlay, prop) {
ret = of_overlay_apply_single_property(ov, target, prop);
if (ret) {
pr_err("%s: Failed to apply prop @%s/%s\n",
__func__, target->full_name, prop->name);
return ret;
}
}
for_each_child_of_node(overlay, child) {
ret = of_overlay_apply_single_device_node(ov, target, child);
if (ret != 0) {
pr_err("%s: Failed to apply single node @%s/%s\n",
__func__, target->full_name,
child->name);
return ret;
}
}
return 0;
}
/**
* of_overlay_apply() - Apply @count overlays pointed at by @ovinfo_tab
* @ov: Overlay to apply
*
* Applies the overlays given, while handling all error conditions
* appropriately. Either the operation succeeds, or if it fails the
* live tree is reverted to the state before the attempt.
* Returns 0, or an error if the overlay attempt failed.
*/
static int of_overlay_apply(struct of_overlay *ov)
{
int i, err;
/* first we apply the overlays atomically */
for (i = 0; i < ov->count; i++) {
struct of_overlay_info *ovinfo = &ov->ovinfo_tab[i];
err = of_overlay_apply_one(ov, ovinfo->target, ovinfo->overlay);
if (err != 0) {
pr_err("%s: overlay failed '%s'\n",
__func__, ovinfo->target->full_name);
return err;
}
}
return 0;
}
/*
* Find the target node using a number of different strategies
* in order of preference
*
* "target" property containing the phandle of the target
* "target-path" property containing the path of the target
*/
static struct device_node *find_target_node(struct device_node *info_node)
{
const char *path;
u32 val;
int ret;
/* first try to go by using the target as a phandle */
ret = of_property_read_u32(info_node, "target", &val);
if (ret == 0)
return of_find_node_by_phandle(val);
/* now try to locate by path */
ret = of_property_read_string(info_node, "target-path", &path);
if (ret == 0)
return of_find_node_by_path(path);
pr_err("%s: Failed to find target for node %p (%s)\n", __func__,
info_node, info_node->name);
return NULL;
}
/**
* of_fill_overlay_info() - Fill an overlay info structure
* @ov Overlay to fill
* @info_node: Device node containing the overlay
* @ovinfo: Pointer to the overlay info structure to fill
*
* Fills an overlay info structure with the overlay information
* from a device node. This device node must have a target property
* which contains a phandle of the overlay target node, and an
* __overlay__ child node which has the overlay contents.
* Both ovinfo->target & ovinfo->overlay have their references taken.
*
* Returns 0 on success, or a negative error value.
*/
static int of_fill_overlay_info(struct of_overlay *ov,
struct device_node *info_node, struct of_overlay_info *ovinfo)
{
ovinfo->overlay = of_get_child_by_name(info_node, "__overlay__");
if (ovinfo->overlay == NULL)
goto err_fail;
ovinfo->target = find_target_node(info_node);
if (ovinfo->target == NULL)
goto err_fail;
return 0;
err_fail:
of_node_put(ovinfo->target);
of_node_put(ovinfo->overlay);
memset(ovinfo, 0, sizeof(*ovinfo));
return -EINVAL;
}
/**
* of_build_overlay_info() - Build an overlay info array
* @ov Overlay to build
* @tree: Device node containing all the overlays
*
* Helper function that given a tree containing overlay information,
* allocates and builds an overlay info array containing it, ready
* for use using of_overlay_apply.
*
* Returns 0 on success with the @cntp @ovinfop pointers valid,
* while on error a negative error value is returned.
*/
static int of_build_overlay_info(struct of_overlay *ov,
struct device_node *tree)
{
struct device_node *node;
struct of_overlay_info *ovinfo;
int cnt, err;
/* worst case; every child is a node */
cnt = 0;
for_each_child_of_node(tree, node)
cnt++;
ovinfo = kcalloc(cnt, sizeof(*ovinfo), GFP_KERNEL);
if (ovinfo == NULL)
return -ENOMEM;
cnt = 0;
for_each_child_of_node(tree, node) {
memset(&ovinfo[cnt], 0, sizeof(*ovinfo));
err = of_fill_overlay_info(ov, node, &ovinfo[cnt]);
if (err == 0)
cnt++;
}
/* if nothing filled, return error */
if (cnt == 0) {
kfree(ovinfo);
return -ENODEV;
}
ov->count = cnt;
ov->ovinfo_tab = ovinfo;
return 0;
}
/**
* of_free_overlay_info() - Free an overlay info array
* @ov Overlay to free the overlay info from
* @ovinfo_tab: Array of overlay_info's to free
*
* Releases the memory of a previously allocated ovinfo array
* by of_build_overlay_info.
* Returns 0, or an error if the arguments are bogus.
*/
static int of_free_overlay_info(struct of_overlay *ov)
{
struct of_overlay_info *ovinfo;
int i;
/* do it in reverse */
for (i = ov->count - 1; i >= 0; i--) {
ovinfo = &ov->ovinfo_tab[i];
of_node_put(ovinfo->target);
of_node_put(ovinfo->overlay);
}
kfree(ov->ovinfo_tab);
return 0;
}
static LIST_HEAD(ov_list);
static DEFINE_IDR(ov_idr);
/**
* of_overlay_create() - Create and apply an overlay
* @tree: Device node containing all the overlays
*
* Creates and applies an overlay while also keeping track
* of the overlay in a list. This list can be used to prevent
* illegal overlay removals.
*
* Returns the id of the created overlay, or an negative error number
*/
int of_overlay_create(struct device_node *tree)
{
struct of_overlay *ov;
int err, id;
/* allocate the overlay structure */
ov = kzalloc(sizeof(*ov), GFP_KERNEL);
if (ov == NULL)
return -ENOMEM;
ov->id = -1;
INIT_LIST_HEAD(&ov->node);
of_changeset_init(&ov->cset);
mutex_lock(&of_mutex);
id = idr_alloc(&ov_idr, ov, 0, 0, GFP_KERNEL);
if (id < 0) {
pr_err("%s: idr_alloc() failed for tree@%s\n",
__func__, tree->full_name);
err = id;
goto err_destroy_trans;
}
ov->id = id;
/* build the overlay info structures */
err = of_build_overlay_info(ov, tree);
if (err) {
pr_err("%s: of_build_overlay_info() failed for tree@%s\n",
__func__, tree->full_name);
goto err_free_idr;
}
/* apply the overlay */
err = of_overlay_apply(ov);
if (err) {
pr_err("%s: of_overlay_apply() failed for tree@%s\n",
__func__, tree->full_name);
goto err_abort_trans;
}
/* apply the changeset */
err = of_changeset_apply(&ov->cset);
if (err) {
pr_err("%s: of_changeset_apply() failed for tree@%s\n",
__func__, tree->full_name);
goto err_revert_overlay;
}
/* add to the tail of the overlay list */
list_add_tail(&ov->node, &ov_list);
mutex_unlock(&of_mutex);
return id;
err_revert_overlay:
err_abort_trans:
of_free_overlay_info(ov);
err_free_idr:
idr_remove(&ov_idr, ov->id);
err_destroy_trans:
of_changeset_destroy(&ov->cset);
kfree(ov);
mutex_unlock(&of_mutex);
return err;
}
EXPORT_SYMBOL_GPL(of_overlay_create);
/* check whether the given node, lies under the given tree */
static int overlay_subtree_check(struct device_node *tree,
struct device_node *dn)
{
struct device_node *child;
/* match? */
if (tree == dn)
return 1;
for_each_child_of_node(tree, child) {
if (overlay_subtree_check(child, dn))
return 1;
}
return 0;
}
/* check whether this overlay is the topmost */
static int overlay_is_topmost(struct of_overlay *ov, struct device_node *dn)
{
struct of_overlay *ovt;
struct of_changeset_entry *ce;
list_for_each_entry_reverse(ovt, &ov_list, node) {
/* if we hit ourselves, we're done */
if (ovt == ov)
break;
/* check against each subtree affected by this overlay */
list_for_each_entry(ce, &ovt->cset.entries, node) {
if (overlay_subtree_check(ce->np, dn)) {
pr_err("%s: #%d clashes #%d @%s\n",
__func__, ov->id, ovt->id,
dn->full_name);
return 0;
}
}
}
/* overlay is topmost */
return 1;
}
/*
* We can safely remove the overlay only if it's the top-most one.
* Newly applied overlays are inserted at the tail of the overlay list,
* so a top most overlay is the one that is closest to the tail.
*
* The topmost check is done by exploiting this property. For each
* affected device node in the log list we check if this overlay is
* the one closest to the tail. If another overlay has affected this
* device node and is closest to the tail, then removal is not permited.
*/
static int overlay_removal_is_ok(struct of_overlay *ov)
{
struct of_changeset_entry *ce;
list_for_each_entry(ce, &ov->cset.entries, node) {
if (!overlay_is_topmost(ov, ce->np)) {
pr_err("%s: overlay #%d is not topmost\n",
__func__, ov->id);
return 0;
}
}
return 1;
}
/**
* of_overlay_destroy() - Removes an overlay
* @id: Overlay id number returned by a previous call to of_overlay_create
*
* Removes an overlay if it is permissible.
*
* Returns 0 on success, or an negative error number
*/
int of_overlay_destroy(int id)
{
struct of_overlay *ov;
int err;
mutex_lock(&of_mutex);
ov = idr_find(&ov_idr, id);
if (ov == NULL) {
err = -ENODEV;
pr_err("%s: Could not find overlay #%d\n",
__func__, id);
goto out;
}
/* check whether the overlay is safe to remove */
if (!overlay_removal_is_ok(ov)) {
err = -EBUSY;
pr_err("%s: removal check failed for overlay #%d\n",
__func__, id);
goto out;
}
list_del(&ov->node);
of_changeset_revert(&ov->cset);
of_free_overlay_info(ov);
idr_remove(&ov_idr, id);
of_changeset_destroy(&ov->cset);
kfree(ov);
err = 0;
out:
mutex_unlock(&of_mutex);
return err;
}
EXPORT_SYMBOL_GPL(of_overlay_destroy);
/**
* of_overlay_destroy_all() - Removes all overlays from the system
*
* Removes all overlays from the system in the correct order.
*
* Returns 0 on success, or an negative error number
*/
int of_overlay_destroy_all(void)
{
struct of_overlay *ov, *ovn;
mutex_lock(&of_mutex);
/* the tail of list is guaranteed to be safe to remove */
list_for_each_entry_safe_reverse(ov, ovn, &ov_list, node) {
list_del(&ov->node);
of_changeset_revert(&ov->cset);
of_free_overlay_info(ov);
idr_remove(&ov_idr, ov->id);
kfree(ov);
}
mutex_unlock(&of_mutex);
return 0;
}
EXPORT_SYMBOL_GPL(of_overlay_destroy_all);
...@@ -25,8 +25,7 @@ ...@@ -25,8 +25,7 @@
static struct of_pdt_ops *of_pdt_prom_ops __initdata; static struct of_pdt_ops *of_pdt_prom_ops __initdata;
void __initdata (*of_pdt_build_more)(struct device_node *dp, void __initdata (*of_pdt_build_more)(struct device_node *dp);
struct device_node ***nextp);
#if defined(CONFIG_SPARC) #if defined(CONFIG_SPARC)
unsigned int of_pdt_unique_id __initdata; unsigned int of_pdt_unique_id __initdata;
...@@ -192,8 +191,7 @@ static struct device_node * __init of_pdt_create_node(phandle node, ...@@ -192,8 +191,7 @@ static struct device_node * __init of_pdt_create_node(phandle node,
} }
static struct device_node * __init of_pdt_build_tree(struct device_node *parent, static struct device_node * __init of_pdt_build_tree(struct device_node *parent,
phandle node, phandle node)
struct device_node ***nextp)
{ {
struct device_node *ret = NULL, *prev_sibling = NULL; struct device_node *ret = NULL, *prev_sibling = NULL;
struct device_node *dp; struct device_node *dp;
...@@ -210,16 +208,12 @@ static struct device_node * __init of_pdt_build_tree(struct device_node *parent, ...@@ -210,16 +208,12 @@ static struct device_node * __init of_pdt_build_tree(struct device_node *parent,
ret = dp; ret = dp;
prev_sibling = dp; prev_sibling = dp;
*(*nextp) = dp;
*nextp = &dp->allnext;
dp->full_name = of_pdt_build_full_name(dp); dp->full_name = of_pdt_build_full_name(dp);
dp->child = of_pdt_build_tree(dp, dp->child = of_pdt_build_tree(dp, of_pdt_prom_ops->getchild(node));
of_pdt_prom_ops->getchild(node), nextp);
if (of_pdt_build_more) if (of_pdt_build_more)
of_pdt_build_more(dp, nextp); of_pdt_build_more(dp);
node = of_pdt_prom_ops->getsibling(node); node = of_pdt_prom_ops->getsibling(node);
} }
...@@ -234,20 +228,17 @@ static void * __init kernel_tree_alloc(u64 size, u64 align) ...@@ -234,20 +228,17 @@ static void * __init kernel_tree_alloc(u64 size, u64 align)
void __init of_pdt_build_devicetree(phandle root_node, struct of_pdt_ops *ops) void __init of_pdt_build_devicetree(phandle root_node, struct of_pdt_ops *ops)
{ {
struct device_node **nextp;
BUG_ON(!ops); BUG_ON(!ops);
of_pdt_prom_ops = ops; of_pdt_prom_ops = ops;
of_allnodes = of_pdt_create_node(root_node, NULL); of_root = of_pdt_create_node(root_node, NULL);
#if defined(CONFIG_SPARC) #if defined(CONFIG_SPARC)
of_allnodes->path_component_name = ""; of_root->path_component_name = "";
#endif #endif
of_allnodes->full_name = "/"; of_root->full_name = "/";
nextp = &of_allnodes->allnext; of_root->child = of_pdt_build_tree(of_root,
of_allnodes->child = of_pdt_build_tree(of_allnodes, of_pdt_prom_ops->getchild(of_root->phandle));
of_pdt_prom_ops->getchild(of_allnodes->phandle), &nextp);
/* Get pointer to "/chosen" and "/aliases" nodes for use everywhere */ /* Get pointer to "/chosen" and "/aliases" nodes for use everywhere */
of_alias_scan(kernel_tree_alloc); of_alias_scan(kernel_tree_alloc);
......
...@@ -138,7 +138,7 @@ struct platform_device *of_device_alloc(struct device_node *np, ...@@ -138,7 +138,7 @@ struct platform_device *of_device_alloc(struct device_node *np,
} }
dev->dev.of_node = of_node_get(np); dev->dev.of_node = of_node_get(np);
dev->dev.parent = parent; dev->dev.parent = parent ? : &platform_bus;
if (bus_id) if (bus_id)
dev_set_name(&dev->dev, "%s", bus_id); dev_set_name(&dev->dev, "%s", bus_id);
...@@ -291,7 +291,7 @@ static struct amba_device *of_amba_device_create(struct device_node *node, ...@@ -291,7 +291,7 @@ static struct amba_device *of_amba_device_create(struct device_node *node,
/* setup generic device info */ /* setup generic device info */
dev->dev.of_node = of_node_get(node); dev->dev.of_node = of_node_get(node);
dev->dev.parent = parent; dev->dev.parent = parent ? : &platform_bus;
dev->dev.platform_data = platform_data; dev->dev.platform_data = platform_data;
if (bus_id) if (bus_id)
dev_set_name(&dev->dev, "%s", bus_id); dev_set_name(&dev->dev, "%s", bus_id);
...@@ -500,6 +500,7 @@ int of_platform_populate(struct device_node *root, ...@@ -500,6 +500,7 @@ int of_platform_populate(struct device_node *root,
if (rc) if (rc)
break; break;
} }
of_node_set_flag(root, OF_POPULATED_BUS);
of_node_put(root); of_node_put(root);
return rc; return rc;
...@@ -542,8 +543,66 @@ static int of_platform_device_destroy(struct device *dev, void *data) ...@@ -542,8 +543,66 @@ static int of_platform_device_destroy(struct device *dev, void *data)
*/ */
void of_platform_depopulate(struct device *parent) void of_platform_depopulate(struct device *parent)
{ {
if (parent->of_node && of_node_check_flag(parent->of_node, OF_POPULATED_BUS)) {
device_for_each_child(parent, NULL, of_platform_device_destroy); device_for_each_child(parent, NULL, of_platform_device_destroy);
of_node_clear_flag(parent->of_node, OF_POPULATED_BUS);
}
} }
EXPORT_SYMBOL_GPL(of_platform_depopulate); EXPORT_SYMBOL_GPL(of_platform_depopulate);
#ifdef CONFIG_OF_DYNAMIC
static int of_platform_notify(struct notifier_block *nb,
unsigned long action, void *arg)
{
struct of_reconfig_data *rd = arg;
struct platform_device *pdev_parent, *pdev;
bool children_left;
switch (of_reconfig_get_state_change(action, rd)) {
case OF_RECONFIG_CHANGE_ADD:
/* verify that the parent is a bus */
if (!of_node_check_flag(rd->dn->parent, OF_POPULATED_BUS))
return NOTIFY_OK; /* not for us */
/* pdev_parent may be NULL when no bus platform device */
pdev_parent = of_find_device_by_node(rd->dn->parent);
pdev = of_platform_device_create(rd->dn, NULL,
pdev_parent ? &pdev_parent->dev : NULL);
of_dev_put(pdev_parent);
if (pdev == NULL) {
pr_err("%s: failed to create for '%s'\n",
__func__, rd->dn->full_name);
/* of_platform_device_create tosses the error code */
return notifier_from_errno(-EINVAL);
}
break;
case OF_RECONFIG_CHANGE_REMOVE:
/* find our device by node */
pdev = of_find_device_by_node(rd->dn);
if (pdev == NULL)
return NOTIFY_OK; /* no? not meant for us */
/* unregister takes one ref away */
of_platform_device_destroy(&pdev->dev, &children_left);
/* and put the reference of the find */
of_dev_put(pdev);
break;
}
return NOTIFY_OK;
}
static struct notifier_block platform_of_notifier = {
.notifier_call = of_platform_notify,
};
void of_platform_register_reconfig_notifier(void)
{
WARN_ON(of_reconfig_notifier_register(&platform_of_notifier));
}
#endif /* CONFIG_OF_DYNAMIC */
#endif /* CONFIG_OF_ADDRESS */ #endif /* CONFIG_OF_ADDRESS */
...@@ -111,7 +111,8 @@ static void __of_adjust_tree_phandles(struct device_node *node, ...@@ -111,7 +111,8 @@ static void __of_adjust_tree_phandles(struct device_node *node,
__of_adjust_tree_phandles(child, phandle_delta); __of_adjust_tree_phandles(child, phandle_delta);
} }
static int __of_adjust_phandle_ref(struct device_node *node, struct property *rprop, int value, bool is_delta) static int __of_adjust_phandle_ref(struct device_node *node,
struct property *rprop, int value)
{ {
phandle phandle; phandle phandle;
struct device_node *refnode; struct device_node *refnode;
...@@ -181,7 +182,7 @@ static int __of_adjust_phandle_ref(struct device_node *node, struct property *rp ...@@ -181,7 +182,7 @@ static int __of_adjust_phandle_ref(struct device_node *node, struct property *rp
goto err_fail; goto err_fail;
} }
phandle = is_delta ? be32_to_cpup(sprop->value + offset) + value : value; phandle = value;
*(__be32 *)(sprop->value + offset) = cpu_to_be32(phandle); *(__be32 *)(sprop->value + offset) = cpu_to_be32(phandle);
} }
...@@ -190,36 +191,97 @@ static int __of_adjust_phandle_ref(struct device_node *node, struct property *rp ...@@ -190,36 +191,97 @@ static int __of_adjust_phandle_ref(struct device_node *node, struct property *rp
return err; return err;
} }
/* compare nodes taking into account that 'name' strips out the @ part */
static int __of_node_name_cmp(const struct device_node *dn1,
const struct device_node *dn2)
{
const char *n1 = strrchr(dn1->full_name, '/') ? : "/";
const char *n2 = strrchr(dn2->full_name, '/') ? : "/";
return of_node_cmp(n1, n2);
}
/* /*
* Adjust the local phandle references by the given phandle delta. * Adjust the local phandle references by the given phandle delta.
* Assumes the existances of a __local_fixups__ node at the root * Assumes the existances of a __local_fixups__ node at the root.
* of the tree. Does not take any devtree locks so make sure you * Assumes that __of_verify_tree_phandle_references has been called.
* call this on a tree which is at the detached state. * Does not take any devtree locks so make sure you call this on a tree
* which is at the detached state.
*/ */
static int __of_adjust_tree_phandle_references(struct device_node *node, static int __of_adjust_tree_phandle_references(struct device_node *node,
int phandle_delta) struct device_node *target, int phandle_delta)
{ {
struct device_node *child; struct device_node *child, *childtarget;
struct property *rprop; struct property *rprop, *sprop;
int err; int err, i, count;
unsigned int off;
/* locate the symbols & fixups nodes on resolve */ phandle phandle;
for_each_child_of_node(node, child)
if (of_node_cmp(child->name, "__local_fixups__") == 0)
break;
/* no local fixups */ if (node == NULL)
if (!child)
return 0; return 0;
/* find the local fixups property */ for_each_property_of_node(node, rprop) {
for_each_property_of_node(child, rprop) {
/* skip properties added automatically */ /* skip properties added automatically */
if (of_prop_cmp(rprop->name, "name") == 0) if (of_prop_cmp(rprop->name, "name") == 0 ||
of_prop_cmp(rprop->name, "phandle") == 0 ||
of_prop_cmp(rprop->name, "linux,phandle") == 0)
continue; continue;
err = __of_adjust_phandle_ref(node, rprop, phandle_delta, true); if ((rprop->length % 4) != 0 || rprop->length == 0) {
if (err) pr_err("%s: Illegal property (size) '%s' @%s\n",
__func__, rprop->name, node->full_name);
return -EINVAL;
}
count = rprop->length / sizeof(__be32);
/* now find the target property */
for_each_property_of_node(target, sprop) {
if (of_prop_cmp(sprop->name, rprop->name) == 0)
break;
}
if (sprop == NULL) {
pr_err("%s: Could not find target property '%s' @%s\n",
__func__, rprop->name, node->full_name);
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);
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);
}
}
}
for_each_child_of_node(node, child) {
for_each_child_of_node(target, childtarget)
if (__of_node_name_cmp(child, childtarget) == 0)
break;
if (!childtarget) {
pr_err("%s: Could not find target child '%s' @%s\n",
__func__, child->name, node->full_name);
return -EINVAL;
}
err = __of_adjust_tree_phandle_references(child, childtarget,
phandle_delta);
if (err != 0)
return err; return err;
} }
...@@ -241,7 +303,7 @@ static int __of_adjust_tree_phandle_references(struct device_node *node, ...@@ -241,7 +303,7 @@ static int __of_adjust_tree_phandle_references(struct device_node *node,
*/ */
int of_resolve_phandles(struct device_node *resolve) int of_resolve_phandles(struct device_node *resolve)
{ {
struct device_node *child, *refnode; struct device_node *child, *childroot, *refnode;
struct device_node *root_sym, *resolve_sym, *resolve_fix; struct device_node *root_sym, *resolve_sym, *resolve_fix;
struct property *rprop; struct property *rprop;
const char *refpath; const char *refpath;
...@@ -255,10 +317,24 @@ int of_resolve_phandles(struct device_node *resolve) ...@@ -255,10 +317,24 @@ int of_resolve_phandles(struct device_node *resolve)
/* first we need to adjust the phandles */ /* first we need to adjust the phandles */
phandle_delta = of_get_tree_max_phandle() + 1; phandle_delta = of_get_tree_max_phandle() + 1;
__of_adjust_tree_phandles(resolve, phandle_delta); __of_adjust_tree_phandles(resolve, phandle_delta);
err = __of_adjust_tree_phandle_references(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) if (err != 0)
return err; return err;
BUG_ON(__of_adjust_tree_phandle_references(childroot,
resolve, phandle_delta));
}
root_sym = NULL; root_sym = NULL;
resolve_sym = NULL; resolve_sym = NULL;
resolve_fix = NULL; resolve_fix = NULL;
...@@ -322,7 +398,7 @@ int of_resolve_phandles(struct device_node *resolve) ...@@ -322,7 +398,7 @@ int of_resolve_phandles(struct device_node *resolve)
pr_debug("%s: %s phandle is 0x%08x\n", pr_debug("%s: %s phandle is 0x%08x\n",
__func__, rprop->name, phandle); __func__, rprop->name, phandle);
err = __of_adjust_phandle_ref(resolve, rprop, phandle, false); err = __of_adjust_phandle_ref(resolve, rprop, phandle);
if (err) if (err)
break; break;
} }
......
/dts-v1/;
/ {
testcase-data {
changeset {
prop-update = "hello";
prop-remove = "world";
node-remove {
};
};
};
};
#include "tests-phandle.dtsi"
#include "tests-interrupts.dtsi"
#include "tests-match.dtsi"
#include "tests-platform.dtsi"
/*
* phandle fixup data - generated by dtc patches that aren't upstream.
* This data must be regenerated whenever phandle references are modified in
* the testdata tree.
*
* The format of this data may be subject to change. For the time being consider
* this a kernel-internal data format.
*/
/ { __local_fixups__ {
fixup = "/testcase-data/testcase-device2:interrupt-parent:0",
"/testcase-data/testcase-device1:interrupt-parent:0",
"/testcase-data/interrupts/interrupts-extended0:interrupts-extended:60",
"/testcase-data/interrupts/interrupts-extended0:interrupts-extended:52",
"/testcase-data/interrupts/interrupts-extended0:interrupts-extended:44",
"/testcase-data/interrupts/interrupts-extended0:interrupts-extended:36",
"/testcase-data/interrupts/interrupts-extended0:interrupts-extended:24",
"/testcase-data/interrupts/interrupts-extended0:interrupts-extended:8",
"/testcase-data/interrupts/interrupts-extended0:interrupts-extended:0",
"/testcase-data/interrupts/interrupts1:interrupt-parent:0",
"/testcase-data/interrupts/interrupts0:interrupt-parent:0",
"/testcase-data/interrupts/intmap1:interrupt-map:12",
"/testcase-data/interrupts/intmap0:interrupt-map:52",
"/testcase-data/interrupts/intmap0:interrupt-map:36",
"/testcase-data/interrupts/intmap0:interrupt-map:16",
"/testcase-data/interrupts/intmap0:interrupt-map:4",
"/testcase-data/phandle-tests/consumer-a:phandle-list-bad-args:12",
"/testcase-data/phandle-tests/consumer-a:phandle-list-bad-args:0",
"/testcase-data/phandle-tests/consumer-a:phandle-list:56",
"/testcase-data/phandle-tests/consumer-a:phandle-list:52",
"/testcase-data/phandle-tests/consumer-a:phandle-list:40",
"/testcase-data/phandle-tests/consumer-a:phandle-list:24",
"/testcase-data/phandle-tests/consumer-a:phandle-list:8",
"/testcase-data/phandle-tests/consumer-a:phandle-list:0";
}; };
/dts-v1/;
/ {
testcase-data {
changeset {
prop-update = "hello";
prop-remove = "world";
node-remove {
};
};
};
};
#include "tests-phandle.dtsi"
#include "tests-interrupts.dtsi"
#include "tests-match.dtsi"
#include "tests-platform.dtsi"
#include "tests-overlay.dtsi"
/*
* phandle fixup data - generated by dtc patches that aren't upstream.
* This data must be regenerated whenever phandle references are modified in
* the testdata tree.
*
* The format of this data may be subject to change. For the time being consider
* this a kernel-internal data format.
*/
/ { __local_fixups__ {
testcase-data {
phandle-tests {
consumer-a {
phandle-list = <0x00000000 0x00000008
0x00000018 0x00000028
0x00000034 0x00000038>;
phandle-list-bad-args = <0x00000000 0x0000000c>;
};
};
interrupts {
intmap0 {
interrupt-map = <0x00000004 0x00000010
0x00000024 0x00000034>;
};
intmap1 {
interrupt-map = <0x0000000c>;
};
interrupts0 {
interrupt-parent = <0x00000000>;
};
interrupts1 {
interrupt-parent = <0x00000000>;
};
interrupts-extended0 {
interrupts-extended = <0x00000000 0x00000008
0x00000018 0x00000024
0x0000002c 0x00000034
0x0000003c>;
};
};
testcase-device1 {
interrupt-parent = <0x00000000>;
};
testcase-device2 {
interrupt-parent = <0x00000000>;
};
overlay2 {
fragment@0 {
target = <0x00000000>;
};
};
overlay3 {
fragment@0 {
target = <0x00000000>;
};
};
overlay4 {
fragment@0 {
target = <0x00000000>;
};
};
};
}; };
/ {
testcase-data {
overlay-node {
/* test bus */
selftestbus: test-bus {
compatible = "simple-bus";
#address-cells = <1>;
#size-cells = <0>;
selftest100: test-selftest100 {
compatible = "selftest";
status = "okay";
reg = <100>;
};
selftest101: test-selftest101 {
compatible = "selftest";
status = "disabled";
reg = <101>;
};
selftest0: test-selftest0 {
compatible = "selftest";
status = "disabled";
reg = <0>;
};
selftest1: test-selftest1 {
compatible = "selftest";
status = "okay";
reg = <1>;
};
selftest2: test-selftest2 {
compatible = "selftest";
status = "disabled";
reg = <2>;
};
selftest3: test-selftest3 {
compatible = "selftest";
status = "okay";
reg = <3>;
};
selftest5: test-selftest5 {
compatible = "selftest";
status = "disabled";
reg = <5>;
};
selftest6: test-selftest6 {
compatible = "selftest";
status = "disabled";
reg = <6>;
};
selftest7: test-selftest7 {
compatible = "selftest";
status = "disabled";
reg = <7>;
};
selftest8: test-selftest8 {
compatible = "selftest";
status = "disabled";
reg = <8>;
};
};
};
/* test enable using absolute target path */
overlay0 {
fragment@0 {
target-path = "/testcase-data/overlay-node/test-bus/test-selftest0";
__overlay__ {
status = "okay";
};
};
};
/* test disable using absolute target path */
overlay1 {
fragment@0 {
target-path = "/testcase-data/overlay-node/test-bus/test-selftest1";
__overlay__ {
status = "disabled";
};
};
};
/* test enable using label */
overlay2 {
fragment@0 {
target = <&selftest2>;
__overlay__ {
status = "okay";
};
};
};
/* test disable using label */
overlay3 {
fragment@0 {
target = <&selftest3>;
__overlay__ {
status = "disabled";
};
};
};
/* test insertion of a full node */
overlay4 {
fragment@0 {
target = <&selftestbus>;
__overlay__ {
/* suppress DTC warning */
#address-cells = <1>;
#size-cells = <0>;
test-selftest4 {
compatible = "selftest";
status = "okay";
reg = <4>;
};
};
};
};
/* test overlay apply revert */
overlay5 {
fragment@0 {
target-path = "/testcase-data/overlay-node/test-bus/test-selftest5";
__overlay__ {
status = "okay";
};
};
};
/* test overlays application and removal in sequence */
overlay6 {
fragment@0 {
target-path = "/testcase-data/overlay-node/test-bus/test-selftest6";
__overlay__ {
status = "okay";
};
};
};
overlay7 {
fragment@0 {
target-path = "/testcase-data/overlay-node/test-bus/test-selftest7";
__overlay__ {
status = "okay";
};
};
};
/* test overlays application and removal in bad sequence */
overlay8 {
fragment@0 {
target-path = "/testcase-data/overlay-node/test-bus/test-selftest8";
__overlay__ {
status = "okay";
};
};
};
overlay9 {
fragment@0 {
target-path = "/testcase-data/overlay-node/test-bus/test-selftest8";
__overlay__ {
property-foo = "bar";
};
};
};
};
};
...@@ -17,6 +17,8 @@ ...@@ -17,6 +17,8 @@
#include <linux/mutex.h> #include <linux/mutex.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/device.h> #include <linux/device.h>
#include <linux/platform_device.h>
#include <linux/of_platform.h>
#include "of_private.h" #include "of_private.h"
...@@ -30,19 +32,22 @@ static struct device_node *nodes[NO_OF_NODES]; ...@@ -30,19 +32,22 @@ static struct device_node *nodes[NO_OF_NODES];
static int last_node_index; static int last_node_index;
static bool selftest_live_tree; static bool selftest_live_tree;
#define selftest(result, fmt, ...) { \ #define selftest(result, fmt, ...) ({ \
if (!(result)) { \ bool failed = !(result); \
if (failed) { \
selftest_results.failed++; \ selftest_results.failed++; \
pr_err("FAIL %s():%i " fmt, __func__, __LINE__, ##__VA_ARGS__); \ pr_err("FAIL %s():%i " fmt, __func__, __LINE__, ##__VA_ARGS__); \
} else { \ } else { \
selftest_results.passed++; \ selftest_results.passed++; \
pr_debug("pass %s():%i\n", __func__, __LINE__); \ pr_debug("pass %s():%i\n", __func__, __LINE__); \
} \ } \
} failed; \
})
static void __init of_selftest_find_node_by_name(void) static void __init of_selftest_find_node_by_name(void)
{ {
struct device_node *np; struct device_node *np;
const char *options;
np = of_find_node_by_path("/testcase-data"); np = of_find_node_by_path("/testcase-data");
selftest(np && !strcmp("/testcase-data", np->full_name), selftest(np && !strcmp("/testcase-data", np->full_name),
...@@ -83,6 +88,35 @@ static void __init of_selftest_find_node_by_name(void) ...@@ -83,6 +88,35 @@ static void __init of_selftest_find_node_by_name(void)
np = of_find_node_by_path("testcase-alias/missing-path"); np = of_find_node_by_path("testcase-alias/missing-path");
selftest(!np, "non-existent alias with relative path returned node %s\n", np->full_name); selftest(!np, "non-existent alias with relative path returned node %s\n", np->full_name);
of_node_put(np); of_node_put(np);
np = of_find_node_opts_by_path("/testcase-data:testoption", &options);
selftest(np && !strcmp("testoption", options),
"option path test failed\n");
of_node_put(np);
np = of_find_node_opts_by_path("/testcase-data:testoption", NULL);
selftest(np, "NULL option path test failed\n");
of_node_put(np);
np = of_find_node_opts_by_path("testcase-alias:testaliasoption",
&options);
selftest(np && !strcmp("testaliasoption", options),
"option alias path test failed\n");
of_node_put(np);
np = of_find_node_opts_by_path("testcase-alias:testaliasoption", NULL);
selftest(np, "NULL option alias path test failed\n");
of_node_put(np);
options = "testoption";
np = of_find_node_opts_by_path("testcase-alias", &options);
selftest(np && !options, "option clearing test failed\n");
of_node_put(np);
options = "testoption";
np = of_find_node_opts_by_path("/", &options);
selftest(np && !options, "option clearing root node test failed\n");
of_node_put(np);
} }
static void __init of_selftest_dynamic(void) static void __init of_selftest_dynamic(void)
...@@ -148,7 +182,7 @@ static void __init of_selftest_dynamic(void) ...@@ -148,7 +182,7 @@ static void __init of_selftest_dynamic(void)
static int __init of_selftest_check_node_linkage(struct device_node *np) static int __init of_selftest_check_node_linkage(struct device_node *np)
{ {
struct device_node *child, *allnext_index = np; struct device_node *child;
int count = 0, rc; int count = 0, rc;
for_each_child_of_node(np, child) { for_each_child_of_node(np, child) {
...@@ -158,14 +192,6 @@ static int __init of_selftest_check_node_linkage(struct device_node *np) ...@@ -158,14 +192,6 @@ static int __init of_selftest_check_node_linkage(struct device_node *np)
return -EINVAL; return -EINVAL;
} }
while (allnext_index && allnext_index != child)
allnext_index = allnext_index->allnext;
if (allnext_index != child) {
pr_err("Node %s is ordered differently in sibling and allnode lists\n",
child->name);
return -EINVAL;
}
rc = of_selftest_check_node_linkage(child); rc = of_selftest_check_node_linkage(child);
if (rc < 0) if (rc < 0)
return rc; return rc;
...@@ -180,12 +206,12 @@ static void __init of_selftest_check_tree_linkage(void) ...@@ -180,12 +206,12 @@ static void __init of_selftest_check_tree_linkage(void)
struct device_node *np; struct device_node *np;
int allnode_count = 0, child_count; int allnode_count = 0, child_count;
if (!of_allnodes) if (!of_root)
return; return;
for_each_of_allnodes(np) for_each_of_allnodes(np)
allnode_count++; allnode_count++;
child_count = of_selftest_check_node_linkage(of_allnodes); child_count = of_selftest_check_node_linkage(of_root);
selftest(child_count > 0, "Device node data structure is corrupted\n"); selftest(child_count > 0, "Device node data structure is corrupted\n");
selftest(child_count == allnode_count, "allnodes list size (%i) doesn't match" selftest(child_count == allnode_count, "allnodes list size (%i) doesn't match"
...@@ -451,15 +477,15 @@ static void __init of_selftest_changeset(void) ...@@ -451,15 +477,15 @@ static void __init of_selftest_changeset(void)
struct property *ppadd, padd = { .name = "prop-add", .length = 0, .value = "" }; struct property *ppadd, padd = { .name = "prop-add", .length = 0, .value = "" };
struct property *ppupdate, pupdate = { .name = "prop-update", .length = 5, .value = "abcd" }; struct property *ppupdate, pupdate = { .name = "prop-update", .length = 5, .value = "abcd" };
struct property *ppremove; struct property *ppremove;
struct device_node *n1, *n2, *n21, *nremove, *parent; struct device_node *n1, *n2, *n21, *nremove, *parent, *np;
struct of_changeset chgset; struct of_changeset chgset;
of_changeset_init(&chgset); of_changeset_init(&chgset);
n1 = __of_node_alloc("/testcase-data/changeset/n1", GFP_KERNEL); n1 = __of_node_dup(NULL, "/testcase-data/changeset/n1");
selftest(n1, "testcase setup failure\n"); selftest(n1, "testcase setup failure\n");
n2 = __of_node_alloc("/testcase-data/changeset/n2", GFP_KERNEL); n2 = __of_node_dup(NULL, "/testcase-data/changeset/n2");
selftest(n2, "testcase setup failure\n"); selftest(n2, "testcase setup failure\n");
n21 = __of_node_alloc("/testcase-data/changeset/n2/n21", GFP_KERNEL); n21 = __of_node_dup(NULL, "%s/%s", "/testcase-data/changeset/n2", "n21");
selftest(n21, "testcase setup failure %p\n", n21); selftest(n21, "testcase setup failure %p\n", n21);
nremove = of_find_node_by_path("/testcase-data/changeset/node-remove"); nremove = of_find_node_by_path("/testcase-data/changeset/node-remove");
selftest(nremove, "testcase setup failure\n"); selftest(nremove, "testcase setup failure\n");
...@@ -487,6 +513,11 @@ static void __init of_selftest_changeset(void) ...@@ -487,6 +513,11 @@ static void __init of_selftest_changeset(void)
selftest(!of_changeset_apply(&chgset), "apply failed\n"); selftest(!of_changeset_apply(&chgset), "apply failed\n");
mutex_unlock(&of_mutex); mutex_unlock(&of_mutex);
/* Make sure node names are constructed correctly */
selftest((np = of_find_node_by_path("/testcase-data/changeset/n2/n21")),
"'%s' not added\n", n21->full_name);
of_node_put(np);
mutex_lock(&of_mutex); mutex_lock(&of_mutex);
selftest(!of_changeset_revert(&chgset), "revert failed\n"); selftest(!of_changeset_revert(&chgset), "revert failed\n");
mutex_unlock(&of_mutex); mutex_unlock(&of_mutex);
...@@ -702,10 +733,13 @@ static void __init of_selftest_match_node(void) ...@@ -702,10 +733,13 @@ static void __init of_selftest_match_node(void)
} }
} }
struct device test_bus = {
.init_name = "unittest-bus",
};
static void __init of_selftest_platform_populate(void) static void __init of_selftest_platform_populate(void)
{ {
int irq; int irq, rc;
struct device_node *np, *child; struct device_node *np, *child, *grandchild;
struct platform_device *pdev; struct platform_device *pdev;
struct of_device_id match[] = { struct of_device_id match[] = {
{ .compatible = "test-device", }, { .compatible = "test-device", },
...@@ -730,20 +764,32 @@ static void __init of_selftest_platform_populate(void) ...@@ -730,20 +764,32 @@ static void __init of_selftest_platform_populate(void)
irq = platform_get_irq(pdev, 0); irq = platform_get_irq(pdev, 0);
selftest(irq < 0 && irq != -EPROBE_DEFER, "device parsing error failed - %d\n", irq); selftest(irq < 0 && irq != -EPROBE_DEFER, "device parsing error failed - %d\n", irq);
np = of_find_node_by_path("/testcase-data/platform-tests"); if (selftest(np = of_find_node_by_path("/testcase-data/platform-tests"),
if (!np) { "No testcase data in device tree\n"));
pr_err("No testcase data in device tree\n"); return;
if (selftest(!(rc = device_register(&test_bus)),
"testbus registration failed; rc=%i\n", rc));
return; return;
}
for_each_child_of_node(np, child) { for_each_child_of_node(np, child) {
struct device_node *grandchild; of_platform_populate(child, match, NULL, &test_bus);
of_platform_populate(child, match, NULL, NULL);
for_each_child_of_node(child, grandchild) for_each_child_of_node(child, grandchild)
selftest(of_find_device_by_node(grandchild), selftest(of_find_device_by_node(grandchild),
"Could not create device for node '%s'\n", "Could not create device for node '%s'\n",
grandchild->name); grandchild->name);
} }
of_platform_depopulate(&test_bus);
for_each_child_of_node(np, child) {
for_each_child_of_node(child, grandchild)
selftest(!of_find_device_by_node(grandchild),
"device didn't get destroyed '%s'\n",
grandchild->name);
}
device_unregister(&test_bus);
of_node_put(np);
} }
/** /**
...@@ -775,33 +821,29 @@ static void update_node_properties(struct device_node *np, ...@@ -775,33 +821,29 @@ static void update_node_properties(struct device_node *np,
*/ */
static int attach_node_and_children(struct device_node *np) static int attach_node_and_children(struct device_node *np)
{ {
struct device_node *next, *root = np, *dup; struct device_node *next, *dup, *child;
/* skip root node */ dup = of_find_node_by_path(np->full_name);
np = np->child; if (dup) {
/* storing a copy in temporary node */ update_node_properties(np, dup);
dup = np; return 0;
}
while (dup) { /* Children of the root need to be remembered for removal */
if (np->parent == of_root) {
if (WARN_ON(last_node_index >= NO_OF_NODES)) if (WARN_ON(last_node_index >= NO_OF_NODES))
return -EINVAL; return -EINVAL;
nodes[last_node_index++] = dup; nodes[last_node_index++] = np;
dup = dup->sibling;
} }
dup = NULL;
while (np) { child = np->child;
next = np->allnext;
dup = of_find_node_by_path(np->full_name);
if (dup)
update_node_properties(np, dup);
else {
np->child = NULL; np->child = NULL;
if (np->parent == root) np->sibling = NULL;
np->parent = of_allnodes;
of_attach_node(np); of_attach_node(np);
} while (child) {
np = next; next = child->sibling;
attach_node_and_children(child);
child = next;
} }
return 0; return 0;
...@@ -846,10 +888,10 @@ static int __init selftest_data_add(void) ...@@ -846,10 +888,10 @@ static int __init selftest_data_add(void)
return -EINVAL; return -EINVAL;
} }
if (!of_allnodes) { if (!of_root) {
/* enabling flag for removing nodes */ /* enabling flag for removing nodes */
selftest_live_tree = true; selftest_live_tree = true;
of_allnodes = selftest_data_node; of_root = selftest_data_node;
for_each_of_allnodes(np) for_each_of_allnodes(np)
__of_attach_node_sysfs(np); __of_attach_node_sysfs(np);
...@@ -859,7 +901,14 @@ static int __init selftest_data_add(void) ...@@ -859,7 +901,14 @@ static int __init selftest_data_add(void)
} }
/* attach the sub-tree to live tree */ /* attach the sub-tree to live tree */
return attach_node_and_children(selftest_data_node); np = selftest_data_node->child;
while (np) {
struct device_node *next = np->sibling;
np->parent = of_root;
attach_node_and_children(np);
np = next;
}
return 0;
} }
/** /**
...@@ -889,10 +938,10 @@ static void selftest_data_remove(void) ...@@ -889,10 +938,10 @@ static void selftest_data_remove(void)
of_node_put(of_chosen); of_node_put(of_chosen);
of_aliases = NULL; of_aliases = NULL;
of_chosen = NULL; of_chosen = NULL;
for_each_child_of_node(of_allnodes, np) for_each_child_of_node(of_root, np)
detach_node_and_children(np); detach_node_and_children(np);
__of_detach_node_sysfs(of_allnodes); __of_detach_node_sysfs(of_root);
of_allnodes = NULL; of_root = NULL;
return; return;
} }
...@@ -915,6 +964,483 @@ static void selftest_data_remove(void) ...@@ -915,6 +964,483 @@ static void selftest_data_remove(void)
} }
} }
#ifdef CONFIG_OF_OVERLAY
static int selftest_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct device_node *np = dev->of_node;
if (np == NULL) {
dev_err(dev, "No OF data for device\n");
return -EINVAL;
}
dev_dbg(dev, "%s for node @%s\n", __func__, np->full_name);
return 0;
}
static int selftest_remove(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct device_node *np = dev->of_node;
dev_dbg(dev, "%s for node @%s\n", __func__, np->full_name);
return 0;
}
static struct of_device_id selftest_match[] = {
{ .compatible = "selftest", },
{},
};
static struct platform_driver selftest_driver = {
.probe = selftest_probe,
.remove = selftest_remove,
.driver = {
.name = "selftest",
.owner = THIS_MODULE,
.of_match_table = of_match_ptr(selftest_match),
},
};
/* get the platform device instantiated at the path */
static struct platform_device *of_path_to_platform_device(const char *path)
{
struct device_node *np;
struct platform_device *pdev;
np = of_find_node_by_path(path);
if (np == NULL)
return NULL;
pdev = of_find_device_by_node(np);
of_node_put(np);
return pdev;
}
/* find out if a platform device exists at that path */
static int of_path_platform_device_exists(const char *path)
{
struct platform_device *pdev;
pdev = of_path_to_platform_device(path);
platform_device_put(pdev);
return pdev != NULL;
}
static const char *selftest_path(int nr)
{
static char buf[256];
snprintf(buf, sizeof(buf) - 1,
"/testcase-data/overlay-node/test-bus/test-selftest%d", nr);
buf[sizeof(buf) - 1] = '\0';
return buf;
}
static const char *overlay_path(int nr)
{
static char buf[256];
snprintf(buf, sizeof(buf) - 1,
"/testcase-data/overlay%d", nr);
buf[sizeof(buf) - 1] = '\0';
return buf;
}
static const char *bus_path = "/testcase-data/overlay-node/test-bus";
static int of_selftest_apply_overlay(int selftest_nr, int overlay_nr,
int *overlay_id)
{
struct device_node *np = NULL;
int ret, id = -1;
np = of_find_node_by_path(overlay_path(overlay_nr));
if (np == NULL) {
selftest(0, "could not find overlay node @\"%s\"\n",
overlay_path(overlay_nr));
ret = -EINVAL;
goto out;
}
ret = of_overlay_create(np);
if (ret < 0) {
selftest(0, "could not create overlay from \"%s\"\n",
overlay_path(overlay_nr));
goto out;
}
id = ret;
ret = 0;
out:
of_node_put(np);
if (overlay_id)
*overlay_id = id;
return ret;
}
/* apply an overlay while checking before and after states */
static int of_selftest_apply_overlay_check(int overlay_nr, int selftest_nr,
int before, int after)
{
int ret;
/* selftest device must not be in before state */
if (of_path_platform_device_exists(selftest_path(selftest_nr))
!= before) {
selftest(0, "overlay @\"%s\" with device @\"%s\" %s\n",
overlay_path(overlay_nr),
selftest_path(selftest_nr),
!before ? "enabled" : "disabled");
return -EINVAL;
}
ret = of_selftest_apply_overlay(overlay_nr, selftest_nr, NULL);
if (ret != 0) {
/* of_selftest_apply_overlay already called selftest() */
return ret;
}
/* selftest device must be to set to after state */
if (of_path_platform_device_exists(selftest_path(selftest_nr))
!= after) {
selftest(0, "overlay @\"%s\" failed to create @\"%s\" %s\n",
overlay_path(overlay_nr),
selftest_path(selftest_nr),
!after ? "enabled" : "disabled");
return -EINVAL;
}
return 0;
}
/* apply an overlay and then revert it while checking before, after states */
static int of_selftest_apply_revert_overlay_check(int overlay_nr,
int selftest_nr, int before, int after)
{
int ret, ov_id;
/* selftest device must be in before state */
if (of_path_platform_device_exists(selftest_path(selftest_nr))
!= before) {
selftest(0, "overlay @\"%s\" with device @\"%s\" %s\n",
overlay_path(overlay_nr),
selftest_path(selftest_nr),
!before ? "enabled" : "disabled");
return -EINVAL;
}
/* apply the overlay */
ret = of_selftest_apply_overlay(overlay_nr, selftest_nr, &ov_id);
if (ret != 0) {
/* of_selftest_apply_overlay already called selftest() */
return ret;
}
/* selftest device must be in after state */
if (of_path_platform_device_exists(selftest_path(selftest_nr))
!= after) {
selftest(0, "overlay @\"%s\" failed to create @\"%s\" %s\n",
overlay_path(overlay_nr),
selftest_path(selftest_nr),
!after ? "enabled" : "disabled");
return -EINVAL;
}
ret = of_overlay_destroy(ov_id);
if (ret != 0) {
selftest(0, "overlay @\"%s\" failed to be destroyed @\"%s\"\n",
overlay_path(overlay_nr),
selftest_path(selftest_nr));
return ret;
}
/* selftest device must be again in before state */
if (of_path_platform_device_exists(selftest_path(selftest_nr))
!= before) {
selftest(0, "overlay @\"%s\" with device @\"%s\" %s\n",
overlay_path(overlay_nr),
selftest_path(selftest_nr),
!before ? "enabled" : "disabled");
return -EINVAL;
}
return 0;
}
/* test activation of device */
static void of_selftest_overlay_0(void)
{
int ret;
/* device should enable */
ret = of_selftest_apply_overlay_check(0, 0, 0, 1);
if (ret != 0)
return;
selftest(1, "overlay test %d passed\n", 0);
}
/* test deactivation of device */
static void of_selftest_overlay_1(void)
{
int ret;
/* device should disable */
ret = of_selftest_apply_overlay_check(1, 1, 1, 0);
if (ret != 0)
return;
selftest(1, "overlay test %d passed\n", 1);
}
/* test activation of device */
static void of_selftest_overlay_2(void)
{
int ret;
/* device should enable */
ret = of_selftest_apply_overlay_check(2, 2, 0, 1);
if (ret != 0)
return;
selftest(1, "overlay test %d passed\n", 2);
}
/* test deactivation of device */
static void of_selftest_overlay_3(void)
{
int ret;
/* device should disable */
ret = of_selftest_apply_overlay_check(3, 3, 1, 0);
if (ret != 0)
return;
selftest(1, "overlay test %d passed\n", 3);
}
/* test activation of a full device node */
static void of_selftest_overlay_4(void)
{
int ret;
/* device should disable */
ret = of_selftest_apply_overlay_check(4, 4, 0, 1);
if (ret != 0)
return;
selftest(1, "overlay test %d passed\n", 4);
}
/* test overlay apply/revert sequence */
static void of_selftest_overlay_5(void)
{
int ret;
/* device should disable */
ret = of_selftest_apply_revert_overlay_check(5, 5, 0, 1);
if (ret != 0)
return;
selftest(1, "overlay test %d passed\n", 5);
}
/* test overlay application in sequence */
static void of_selftest_overlay_6(void)
{
struct device_node *np;
int ret, i, ov_id[2];
int overlay_nr = 6, selftest_nr = 6;
int before = 0, after = 1;
/* selftest device must be in before state */
for (i = 0; i < 2; i++) {
if (of_path_platform_device_exists(
selftest_path(selftest_nr + i))
!= before) {
selftest(0, "overlay @\"%s\" with device @\"%s\" %s\n",
overlay_path(overlay_nr + i),
selftest_path(selftest_nr + i),
!before ? "enabled" : "disabled");
return;
}
}
/* apply the overlays */
for (i = 0; i < 2; i++) {
np = of_find_node_by_path(overlay_path(overlay_nr + i));
if (np == NULL) {
selftest(0, "could not find overlay node @\"%s\"\n",
overlay_path(overlay_nr + i));
return;
}
ret = of_overlay_create(np);
if (ret < 0) {
selftest(0, "could not create overlay from \"%s\"\n",
overlay_path(overlay_nr + i));
return;
}
ov_id[i] = ret;
}
for (i = 0; i < 2; i++) {
/* selftest device must be in after state */
if (of_path_platform_device_exists(
selftest_path(selftest_nr + i))
!= after) {
selftest(0, "overlay @\"%s\" failed @\"%s\" %s\n",
overlay_path(overlay_nr + i),
selftest_path(selftest_nr + i),
!after ? "enabled" : "disabled");
return;
}
}
for (i = 1; i >= 0; i--) {
ret = of_overlay_destroy(ov_id[i]);
if (ret != 0) {
selftest(0, "overlay @\"%s\" failed destroy @\"%s\"\n",
overlay_path(overlay_nr + i),
selftest_path(selftest_nr + i));
return;
}
}
for (i = 0; i < 2; i++) {
/* selftest device must be again in before state */
if (of_path_platform_device_exists(
selftest_path(selftest_nr + i))
!= before) {
selftest(0, "overlay @\"%s\" with device @\"%s\" %s\n",
overlay_path(overlay_nr + i),
selftest_path(selftest_nr + i),
!before ? "enabled" : "disabled");
return;
}
}
selftest(1, "overlay test %d passed\n", 6);
}
/* test overlay application in sequence */
static void of_selftest_overlay_8(void)
{
struct device_node *np;
int ret, i, ov_id[2];
int overlay_nr = 8, selftest_nr = 8;
/* we don't care about device state in this test */
/* apply the overlays */
for (i = 0; i < 2; i++) {
np = of_find_node_by_path(overlay_path(overlay_nr + i));
if (np == NULL) {
selftest(0, "could not find overlay node @\"%s\"\n",
overlay_path(overlay_nr + i));
return;
}
ret = of_overlay_create(np);
if (ret < 0) {
selftest(0, "could not create overlay from \"%s\"\n",
overlay_path(overlay_nr + i));
return;
}
ov_id[i] = ret;
}
/* now try to remove first overlay (it should fail) */
ret = of_overlay_destroy(ov_id[0]);
if (ret == 0) {
selftest(0, "overlay @\"%s\" was destroyed @\"%s\"\n",
overlay_path(overlay_nr + 0),
selftest_path(selftest_nr));
return;
}
/* removing them in order should work */
for (i = 1; i >= 0; i--) {
ret = of_overlay_destroy(ov_id[i]);
if (ret != 0) {
selftest(0, "overlay @\"%s\" not destroyed @\"%s\"\n",
overlay_path(overlay_nr + i),
selftest_path(selftest_nr));
return;
}
}
selftest(1, "overlay test %d passed\n", 8);
}
static void __init of_selftest_overlay(void)
{
struct device_node *bus_np = NULL;
int ret;
ret = platform_driver_register(&selftest_driver);
if (ret != 0) {
selftest(0, "could not register selftest driver\n");
goto out;
}
bus_np = of_find_node_by_path(bus_path);
if (bus_np == NULL) {
selftest(0, "could not find bus_path \"%s\"\n", bus_path);
goto out;
}
ret = of_platform_populate(bus_np, of_default_bus_match_table,
NULL, NULL);
if (ret != 0) {
selftest(0, "could not populate bus @ \"%s\"\n", bus_path);
goto out;
}
if (!of_path_platform_device_exists(selftest_path(100))) {
selftest(0, "could not find selftest0 @ \"%s\"\n",
selftest_path(100));
goto out;
}
if (of_path_platform_device_exists(selftest_path(101))) {
selftest(0, "selftest1 @ \"%s\" should not exist\n",
selftest_path(101));
goto out;
}
selftest(1, "basic infrastructure of overlays passed");
/* tests in sequence */
of_selftest_overlay_0();
of_selftest_overlay_1();
of_selftest_overlay_2();
of_selftest_overlay_3();
of_selftest_overlay_4();
of_selftest_overlay_5();
of_selftest_overlay_6();
of_selftest_overlay_8();
out:
of_node_put(bus_np);
}
#else
static inline void __init of_selftest_overlay(void) { }
#endif
static int __init of_selftest(void) static int __init of_selftest(void)
{ {
struct device_node *np; struct device_node *np;
...@@ -947,6 +1473,7 @@ static int __init of_selftest(void) ...@@ -947,6 +1473,7 @@ static int __init of_selftest(void)
of_selftest_parse_interrupts_extended(); of_selftest_parse_interrupts_extended();
of_selftest_match_node(); of_selftest_match_node();
of_selftest_platform_populate(); of_selftest_platform_populate();
of_selftest_overlay();
/* removing selftest data from live tree */ /* removing selftest data from live tree */
selftest_data_remove(); selftest_data_remove();
......
...@@ -1220,40 +1220,29 @@ static int spi_master_initialize_queue(struct spi_master *master) ...@@ -1220,40 +1220,29 @@ static int spi_master_initialize_queue(struct spi_master *master)
/*-------------------------------------------------------------------------*/ /*-------------------------------------------------------------------------*/
#if defined(CONFIG_OF) #if defined(CONFIG_OF)
/** static struct spi_device *
* of_register_spi_devices() - Register child devices onto the SPI bus of_register_spi_device(struct spi_master *master, struct device_node *nc)
* @master: Pointer to spi_master device
*
* Registers an spi_device for each child node of master node which has a 'reg'
* property.
*/
static void of_register_spi_devices(struct spi_master *master)
{ {
struct spi_device *spi; struct spi_device *spi;
struct device_node *nc;
int rc; int rc;
u32 value; u32 value;
if (!master->dev.of_node)
return;
for_each_available_child_of_node(master->dev.of_node, nc) {
/* Alloc an spi_device */ /* Alloc an spi_device */
spi = spi_alloc_device(master); spi = spi_alloc_device(master);
if (!spi) { if (!spi) {
dev_err(&master->dev, "spi_device alloc error for %s\n", dev_err(&master->dev, "spi_device alloc error for %s\n",
nc->full_name); nc->full_name);
spi_dev_put(spi); rc = -ENOMEM;
continue; goto err_out;
} }
/* Select device driver */ /* Select device driver */
if (of_modalias_node(nc, spi->modalias, rc = of_modalias_node(nc, spi->modalias,
sizeof(spi->modalias)) < 0) { sizeof(spi->modalias));
if (rc < 0) {
dev_err(&master->dev, "cannot find modalias for %s\n", dev_err(&master->dev, "cannot find modalias for %s\n",
nc->full_name); nc->full_name);
spi_dev_put(spi); goto err_out;
continue;
} }
/* Device address */ /* Device address */
...@@ -1261,8 +1250,7 @@ static void of_register_spi_devices(struct spi_master *master) ...@@ -1261,8 +1250,7 @@ static void of_register_spi_devices(struct spi_master *master)
if (rc) { if (rc) {
dev_err(&master->dev, "%s has no valid 'reg' property (%d)\n", dev_err(&master->dev, "%s has no valid 'reg' property (%d)\n",
nc->full_name, rc); nc->full_name, rc);
spi_dev_put(spi); goto err_out;
continue;
} }
spi->chip_select = value; spi->chip_select = value;
...@@ -1320,8 +1308,7 @@ static void of_register_spi_devices(struct spi_master *master) ...@@ -1320,8 +1308,7 @@ static void of_register_spi_devices(struct spi_master *master)
if (rc) { if (rc) {
dev_err(&master->dev, "%s has no valid 'spi-max-frequency' property (%d)\n", dev_err(&master->dev, "%s has no valid 'spi-max-frequency' property (%d)\n",
nc->full_name, rc); nc->full_name, rc);
spi_dev_put(spi); goto err_out;
continue;
} }
spi->max_speed_hz = value; spi->max_speed_hz = value;
...@@ -1338,9 +1325,36 @@ static void of_register_spi_devices(struct spi_master *master) ...@@ -1338,9 +1325,36 @@ static void of_register_spi_devices(struct spi_master *master)
if (rc) { if (rc) {
dev_err(&master->dev, "spi_device register error %s\n", dev_err(&master->dev, "spi_device register error %s\n",
nc->full_name); nc->full_name);
spi_dev_put(spi); goto err_out;
} }
return spi;
err_out:
spi_dev_put(spi);
return ERR_PTR(rc);
}
/**
* of_register_spi_devices() - Register child devices onto the SPI bus
* @master: Pointer to spi_master device
*
* Registers an spi_device for each child node of master node which has a 'reg'
* property.
*/
static void of_register_spi_devices(struct spi_master *master)
{
struct spi_device *spi;
struct device_node *nc;
if (!master->dev.of_node)
return;
for_each_available_child_of_node(master->dev.of_node, nc) {
spi = of_register_spi_device(master, nc);
if (IS_ERR(spi))
dev_warn(&master->dev, "Failed to create SPI device for %s\n",
nc->full_name);
} }
} }
#else #else
...@@ -2303,6 +2317,86 @@ EXPORT_SYMBOL_GPL(spi_write_then_read); ...@@ -2303,6 +2317,86 @@ EXPORT_SYMBOL_GPL(spi_write_then_read);
/*-------------------------------------------------------------------------*/ /*-------------------------------------------------------------------------*/
#if IS_ENABLED(CONFIG_OF_DYNAMIC)
static int __spi_of_device_match(struct device *dev, void *data)
{
return dev->of_node == data;
}
/* must call put_device() when done with returned spi_device device */
static struct spi_device *of_find_spi_device_by_node(struct device_node *node)
{
struct device *dev = bus_find_device(&spi_bus_type, NULL, node,
__spi_of_device_match);
return dev ? to_spi_device(dev) : NULL;
}
static int __spi_of_master_match(struct device *dev, const void *data)
{
return dev->of_node == data;
}
/* the spi masters are not using spi_bus, so we find it with another way */
static struct spi_master *of_find_spi_master_by_node(struct device_node *node)
{
struct device *dev;
dev = class_find_device(&spi_master_class, NULL, node,
__spi_of_master_match);
if (!dev)
return NULL;
/* reference got in class_find_device */
return container_of(dev, struct spi_master, dev);
}
static int of_spi_notify(struct notifier_block *nb, unsigned long action,
void *arg)
{
struct of_reconfig_data *rd = arg;
struct spi_master *master;
struct spi_device *spi;
switch (of_reconfig_get_state_change(action, arg)) {
case OF_RECONFIG_CHANGE_ADD:
master = of_find_spi_master_by_node(rd->dn->parent);
if (master == NULL)
return NOTIFY_OK; /* not for us */
spi = of_register_spi_device(master, rd->dn);
put_device(&master->dev);
if (IS_ERR(spi)) {
pr_err("%s: failed to create for '%s'\n",
__func__, rd->dn->full_name);
return notifier_from_errno(PTR_ERR(spi));
}
break;
case OF_RECONFIG_CHANGE_REMOVE:
/* find our device by node */
spi = of_find_spi_device_by_node(rd->dn);
if (spi == NULL)
return NOTIFY_OK; /* no? not meant for us */
/* unregister takes one ref away */
spi_unregister_device(spi);
/* and put the reference of the find */
put_device(&spi->dev);
break;
}
return NOTIFY_OK;
}
static struct notifier_block spi_of_notifier = {
.notifier_call = of_spi_notify,
};
#else /* IS_ENABLED(CONFIG_OF_DYNAMIC) */
extern struct notifier_block spi_of_notifier;
#endif /* IS_ENABLED(CONFIG_OF_DYNAMIC) */
static int __init spi_init(void) static int __init spi_init(void)
{ {
int status; int status;
...@@ -2320,6 +2414,10 @@ static int __init spi_init(void) ...@@ -2320,6 +2414,10 @@ static int __init spi_init(void)
status = class_register(&spi_master_class); status = class_register(&spi_master_class);
if (status < 0) if (status < 0)
goto err2; goto err2;
if (IS_ENABLED(CONFIG_OF_DYNAMIC))
WARN_ON(of_reconfig_notifier_register(&spi_of_notifier));
return 0; return 0;
err2: err2:
......
...@@ -24,6 +24,7 @@ ...@@ -24,6 +24,7 @@
#include <linux/topology.h> #include <linux/topology.h>
#include <linux/notifier.h> #include <linux/notifier.h>
#include <linux/property.h> #include <linux/property.h>
#include <linux/list.h>
#include <asm/byteorder.h> #include <asm/byteorder.h>
#include <asm/errno.h> #include <asm/errno.h>
...@@ -57,8 +58,6 @@ struct device_node { ...@@ -57,8 +58,6 @@ struct device_node {
struct device_node *parent; struct device_node *parent;
struct device_node *child; struct device_node *child;
struct device_node *sibling; struct device_node *sibling;
struct device_node *next; /* next device of same type */
struct device_node *allnext; /* next in list of all nodes */
struct kobject kobj; struct kobject kobj;
unsigned long _flags; unsigned long _flags;
void *data; void *data;
...@@ -76,6 +75,12 @@ struct of_phandle_args { ...@@ -76,6 +75,12 @@ struct of_phandle_args {
uint32_t args[MAX_PHANDLE_ARGS]; uint32_t args[MAX_PHANDLE_ARGS];
}; };
struct of_reconfig_data {
struct device_node *dn;
struct property *prop;
struct property *old_prop;
};
/* initialize a node */ /* initialize a node */
extern struct kobj_type of_node_ktype; extern struct kobj_type of_node_ktype;
static inline void of_node_init(struct device_node *node) static inline void of_node_init(struct device_node *node)
...@@ -109,7 +114,7 @@ static inline void of_node_put(struct device_node *node) { } ...@@ -109,7 +114,7 @@ static inline void of_node_put(struct device_node *node) { }
#endif /* !CONFIG_OF_DYNAMIC */ #endif /* !CONFIG_OF_DYNAMIC */
/* Pointer for first entry in chain of all nodes. */ /* Pointer for first entry in chain of all nodes. */
extern struct device_node *of_allnodes; extern struct device_node *of_root;
extern struct device_node *of_chosen; extern struct device_node *of_chosen;
extern struct device_node *of_aliases; extern struct device_node *of_aliases;
extern struct device_node *of_stdout; extern struct device_node *of_stdout;
...@@ -128,7 +133,7 @@ static inline struct device_node *of_node(struct fwnode_handle *fwnode) ...@@ -128,7 +133,7 @@ static inline struct device_node *of_node(struct fwnode_handle *fwnode)
static inline bool of_have_populated_dt(void) static inline bool of_have_populated_dt(void)
{ {
return of_allnodes != NULL; return of_root != NULL;
} }
static inline bool of_node_is_root(const struct device_node *node) static inline bool of_node_is_root(const struct device_node *node)
...@@ -172,6 +177,7 @@ static inline void of_property_clear_flag(struct property *p, unsigned long flag ...@@ -172,6 +177,7 @@ static inline void of_property_clear_flag(struct property *p, unsigned long flag
clear_bit(flag, &p->_flags); clear_bit(flag, &p->_flags);
} }
extern struct device_node *__of_find_all_nodes(struct device_node *prev);
extern struct device_node *of_find_all_nodes(struct device_node *prev); extern struct device_node *of_find_all_nodes(struct device_node *prev);
/* /*
...@@ -227,8 +233,9 @@ static inline const char *of_node_full_name(const struct device_node *np) ...@@ -227,8 +233,9 @@ static inline const char *of_node_full_name(const struct device_node *np)
return np ? np->full_name : "<no-node>"; return np ? np->full_name : "<no-node>";
} }
#define for_each_of_allnodes(dn) \ #define for_each_of_allnodes_from(from, dn) \
for (dn = of_allnodes; dn; dn = dn->allnext) for (dn = __of_find_all_nodes(from); dn; dn = __of_find_all_nodes(dn))
#define for_each_of_allnodes(dn) for_each_of_allnodes_from(NULL, dn)
extern struct device_node *of_find_node_by_name(struct device_node *from, extern struct device_node *of_find_node_by_name(struct device_node *from,
const char *name); const char *name);
extern struct device_node *of_find_node_by_type(struct device_node *from, extern struct device_node *of_find_node_by_type(struct device_node *from,
...@@ -240,7 +247,13 @@ extern struct device_node *of_find_matching_node_and_match( ...@@ -240,7 +247,13 @@ extern struct device_node *of_find_matching_node_and_match(
const struct of_device_id *matches, const struct of_device_id *matches,
const struct of_device_id **match); const struct of_device_id **match);
extern struct device_node *of_find_node_by_path(const char *path); extern struct device_node *of_find_node_opts_by_path(const char *path,
const char **opts);
static inline struct device_node *of_find_node_by_path(const char *path)
{
return of_find_node_opts_by_path(path, NULL);
}
extern struct device_node *of_find_node_by_phandle(phandle handle); extern struct device_node *of_find_node_by_phandle(phandle handle);
extern struct device_node *of_get_parent(const struct device_node *node); extern struct device_node *of_get_parent(const struct device_node *node);
extern struct device_node *of_get_next_parent(struct device_node *node); extern struct device_node *of_get_next_parent(struct device_node *node);
...@@ -291,7 +304,7 @@ extern int of_property_read_string_helper(struct device_node *np, ...@@ -291,7 +304,7 @@ extern int of_property_read_string_helper(struct device_node *np,
const char **out_strs, size_t sz, int index); const char **out_strs, size_t sz, int index);
extern int of_device_is_compatible(const struct device_node *device, extern int of_device_is_compatible(const struct device_node *device,
const char *); const char *);
extern int of_device_is_available(const struct device_node *device); extern bool of_device_is_available(const struct device_node *device);
extern const void *of_get_property(const struct device_node *node, extern const void *of_get_property(const struct device_node *node,
const char *name, const char *name,
int *lenp); int *lenp);
...@@ -333,16 +346,6 @@ extern int of_update_property(struct device_node *np, struct property *newprop); ...@@ -333,16 +346,6 @@ extern int of_update_property(struct device_node *np, struct property *newprop);
#define OF_RECONFIG_REMOVE_PROPERTY 0x0004 #define OF_RECONFIG_REMOVE_PROPERTY 0x0004
#define OF_RECONFIG_UPDATE_PROPERTY 0x0005 #define OF_RECONFIG_UPDATE_PROPERTY 0x0005
struct of_prop_reconfig {
struct device_node *dn;
struct property *prop;
struct property *old_prop;
};
extern int of_reconfig_notifier_register(struct notifier_block *);
extern int of_reconfig_notifier_unregister(struct notifier_block *);
extern int of_reconfig_notify(unsigned long, void *);
extern int of_attach_node(struct device_node *); extern int of_attach_node(struct device_node *);
extern int of_detach_node(struct device_node *); extern int of_detach_node(struct device_node *);
...@@ -411,6 +414,12 @@ static inline struct device_node *of_find_node_by_path(const char *path) ...@@ -411,6 +414,12 @@ static inline struct device_node *of_find_node_by_path(const char *path)
return NULL; return NULL;
} }
static inline struct device_node *of_find_node_opts_by_path(const char *path,
const char **opts)
{
return NULL;
}
static inline struct device_node *of_get_parent(const struct device_node *node) static inline struct device_node *of_get_parent(const struct device_node *node)
{ {
return NULL; return NULL;
...@@ -452,9 +461,9 @@ static inline int of_device_is_compatible(const struct device_node *device, ...@@ -452,9 +461,9 @@ static inline int of_device_is_compatible(const struct device_node *device,
return 0; return 0;
} }
static inline int of_device_is_available(const struct device_node *device) static inline bool of_device_is_available(const struct device_node *device)
{ {
return 0; return false;
} }
static inline struct property *of_find_property(const struct device_node *np, static inline struct property *of_find_property(const struct device_node *np,
...@@ -793,6 +802,13 @@ static inline int of_property_read_u32(const struct device_node *np, ...@@ -793,6 +802,13 @@ static inline int of_property_read_u32(const struct device_node *np,
return of_property_read_u32_array(np, propname, out_value, 1); return of_property_read_u32_array(np, propname, out_value, 1);
} }
static inline int of_property_read_s32(const struct device_node *np,
const char *propname,
s32 *out_value)
{
return of_property_read_u32(np, propname, (u32*) out_value);
}
#define of_property_for_each_u32(np, propname, prop, p, u) \ #define of_property_for_each_u32(np, propname, prop, p, u) \
for (prop = of_find_property(np, propname, NULL), \ for (prop = of_find_property(np, propname, NULL), \
p = of_prop_next_u32(prop, NULL, &u); \ p = of_prop_next_u32(prop, NULL, &u); \
...@@ -912,7 +928,19 @@ struct of_changeset { ...@@ -912,7 +928,19 @@ struct of_changeset {
struct list_head entries; struct list_head entries;
}; };
enum of_reconfig_change {
OF_RECONFIG_NO_CHANGE = 0,
OF_RECONFIG_CHANGE_ADD,
OF_RECONFIG_CHANGE_REMOVE,
};
#ifdef CONFIG_OF_DYNAMIC #ifdef CONFIG_OF_DYNAMIC
extern int of_reconfig_notifier_register(struct notifier_block *);
extern int of_reconfig_notifier_unregister(struct notifier_block *);
extern int of_reconfig_notify(unsigned long, struct of_reconfig_data *rd);
extern int of_reconfig_get_state_change(unsigned long action,
struct of_reconfig_data *arg);
extern void of_changeset_init(struct of_changeset *ocs); extern void of_changeset_init(struct of_changeset *ocs);
extern void of_changeset_destroy(struct of_changeset *ocs); extern void of_changeset_destroy(struct of_changeset *ocs);
extern int of_changeset_apply(struct of_changeset *ocs); extern int of_changeset_apply(struct of_changeset *ocs);
...@@ -950,7 +978,26 @@ static inline int of_changeset_update_property(struct of_changeset *ocs, ...@@ -950,7 +978,26 @@ static inline int of_changeset_update_property(struct of_changeset *ocs,
{ {
return of_changeset_action(ocs, OF_RECONFIG_UPDATE_PROPERTY, np, prop); return of_changeset_action(ocs, OF_RECONFIG_UPDATE_PROPERTY, np, prop);
} }
#endif #else /* CONFIG_OF_DYNAMIC */
static inline int of_reconfig_notifier_register(struct notifier_block *nb)
{
return -EINVAL;
}
static inline int of_reconfig_notifier_unregister(struct notifier_block *nb)
{
return -EINVAL;
}
static inline int of_reconfig_notify(unsigned long action,
struct of_reconfig_data *arg)
{
return -EINVAL;
}
static inline int of_reconfig_get_state_change(unsigned long action,
struct of_reconfig_data *arg)
{
return -EINVAL;
}
#endif /* CONFIG_OF_DYNAMIC */
/* CONFIG_OF_RESOLVE api */ /* CONFIG_OF_RESOLVE api */
extern int of_resolve_phandles(struct device_node *tree); extern int of_resolve_phandles(struct device_node *tree);
...@@ -966,4 +1013,34 @@ static inline bool of_device_is_system_power_controller(const struct device_node ...@@ -966,4 +1013,34 @@ static inline bool of_device_is_system_power_controller(const struct device_node
return of_property_read_bool(np, "system-power-controller"); return of_property_read_bool(np, "system-power-controller");
} }
/**
* Overlay support
*/
#ifdef CONFIG_OF_OVERLAY
/* ID based overlays; the API for external users */
int of_overlay_create(struct device_node *tree);
int of_overlay_destroy(int id);
int of_overlay_destroy_all(void);
#else
static inline int of_overlay_create(struct device_node *tree)
{
return -ENOTSUPP;
}
static inline int of_overlay_destroy(int id)
{
return -ENOTSUPP;
}
static inline int of_overlay_destroy_all(void)
{
return -ENOTSUPP;
}
#endif
#endif /* _LINUX_OF_H */ #endif /* _LINUX_OF_H */
...@@ -106,7 +106,7 @@ extern int of_address_to_resource(struct device_node *dev, int index, ...@@ -106,7 +106,7 @@ extern int of_address_to_resource(struct device_node *dev, int index,
struct resource *r); struct resource *r);
void __iomem *of_iomap(struct device_node *node, int index); void __iomem *of_iomap(struct device_node *node, int index);
void __iomem *of_io_request_and_map(struct device_node *device, void __iomem *of_io_request_and_map(struct device_node *device,
int index, char *name); int index, const char *name);
#else #else
#include <linux/io.h> #include <linux/io.h>
...@@ -123,7 +123,7 @@ static inline void __iomem *of_iomap(struct device_node *device, int index) ...@@ -123,7 +123,7 @@ static inline void __iomem *of_iomap(struct device_node *device, int index)
} }
static inline void __iomem *of_io_request_and_map(struct device_node *device, static inline void __iomem *of_io_request_and_map(struct device_node *device,
int index, char *name) int index, const char *name)
{ {
return IOMEM_ERR_PTR(-EINVAL); return IOMEM_ERR_PTR(-EINVAL);
} }
......
...@@ -39,7 +39,6 @@ extern void *prom_early_alloc(unsigned long size); ...@@ -39,7 +39,6 @@ extern void *prom_early_alloc(unsigned long size);
/* for building the device tree */ /* for building the device tree */
extern void of_pdt_build_devicetree(phandle root_node, struct of_pdt_ops *ops); extern void of_pdt_build_devicetree(phandle root_node, struct of_pdt_ops *ops);
extern void (*of_pdt_build_more)(struct device_node *dp, extern void (*of_pdt_build_more)(struct device_node *dp);
struct device_node ***nextp);
#endif /* _LINUX_OF_PDT_H */ #endif /* _LINUX_OF_PDT_H */
...@@ -84,4 +84,10 @@ static inline int of_platform_populate(struct device_node *root, ...@@ -84,4 +84,10 @@ static inline int of_platform_populate(struct device_node *root,
static inline void of_platform_depopulate(struct device *parent) { } static inline void of_platform_depopulate(struct device *parent) { }
#endif #endif
#ifdef CONFIG_OF_DYNAMIC
extern void of_platform_register_reconfig_notifier(void);
#else
static inline void of_platform_register_reconfig_notifier(void) { }
#endif
#endif /* _LINUX_OF_PLATFORM_H */ #endif /* _LINUX_OF_PLATFORM_H */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment