Devicetree changes for v3.19
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. There are a few trivial merge conflicts. They shouldn't give you any trouble. 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 -----BEGIN PGP SIGNATURE----- Version: GnuPG v1 iQIcBAABAgAGBQJUiaTJAAoJEMWQL496c2LNdKkP/1rk20JXzJc948Z3VFZPXkzf TUKXC+Qn0FmVjQhESkx6LxLDrMDTQlQLlWBmFuWRB87Fk5E32FEf5zzW7I9oQPS4 msIqJoYf5T7EPlmJ/85156xjK5ezc0OyoKEizn23mcKrJE4bmXQEbVw99UUFhq4R Oz1a1ZPQQSSaMteKftOoRBiE3bJut3tJ3dfufNjwOuXi5rALJ0DVxuOeU/Hba13d t05qlImwocKXGBDd/B4psBI5fZl4Tf4AmGOD9aU7YHxrLg4jOCbvqies3DQQ0q3D o9YZBnuBw7A3tzJJ3F5KajRnFLazJBOV5BKGo7eYuTzT56mpZW/HF6eS9b1DbP9x 4q71Vd5qhIuU9JsQAStfZ6pdx3FBXRNGpIXXfwzbCSdaePIuOKS17zvA/Iy5bWeA 2TyqgMuKZwnXOXxQesMZJYIw2IEnIyobzh0A1wAnvReyos/nHF/tha/SA/Jutq1s +0gOkMlPW2EdpADmlfLPRSHgSqO8bfCPeNPihn672MS2dAv9H+XRLcoKuSNErhdl 1gYtnR7IK+Sl0KmMC5YoMvXPchkV5YS2qEp1f3p+ZmgcMSWyHHKMtf8VwjNTaSBU e1AshH6HvmYEPt0cnntSMAxbw+N596QjkVp4RbHsLpyj7qeUVVY56/K/aiM7M69P BvJkuewrhsAxyM2X2OsD =ak0A -----END PGP SIGNATURE----- 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 ...
This commit is contained in:
Коммит
7ef58b32f5
|
@ -0,0 +1,46 @@
|
||||||
|
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:
|
||||||
- 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";
|
||||||
|
};
|
||||||
|
|
|
@ -0,0 +1,14 @@
|
||||||
|
* 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 {
|
||||||
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
|
||||||
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 ('/')
|
||||||
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
|
||||||
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 :)
|
|
||||||
|
|
|
@ -0,0 +1,133 @@
|
||||||
|
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:
|
||||||
|
|
||||||
=== 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)
|
||||||
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)
|
||||||
}
|
}
|
||||||
|
|
||||||
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)
|
||||||
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)
|
||||||
}
|
}
|
||||||
|
|
||||||
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 = {
|
||||||
.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)
|
||||||
" 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)
|
||||||
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 @@ error_out:
|
||||||
* 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 @@
|
||||||
#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,9 +1370,59 @@ 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 struct i2c_client *of_i2c_register_device(struct i2c_adapter *adap,
|
||||||
|
struct device_node *node)
|
||||||
|
{
|
||||||
|
struct i2c_client *result;
|
||||||
|
struct i2c_board_info info = {};
|
||||||
|
struct dev_archdata dev_ad = {};
|
||||||
|
const __be32 *addr;
|
||||||
|
int len;
|
||||||
|
|
||||||
|
dev_dbg(&adap->dev, "of_i2c: register %s\n", node->full_name);
|
||||||
|
|
||||||
|
if (of_modalias_node(node, info.type, sizeof(info.type)) < 0) {
|
||||||
|
dev_err(&adap->dev, "of_i2c: modalias failure on %s\n",
|
||||||
|
node->full_name);
|
||||||
|
return ERR_PTR(-EINVAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
addr = of_get_property(node, "reg", &len);
|
||||||
|
if (!addr || (len < sizeof(int))) {
|
||||||
|
dev_err(&adap->dev, "of_i2c: invalid reg on %s\n",
|
||||||
|
node->full_name);
|
||||||
|
return ERR_PTR(-EINVAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
info.addr = be32_to_cpup(addr);
|
||||||
|
if (info.addr > (1 << 10) - 1) {
|
||||||
|
dev_err(&adap->dev, "of_i2c: invalid addr=%x on %s\n",
|
||||||
|
info.addr, node->full_name);
|
||||||
|
return ERR_PTR(-EINVAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
info.irq = irq_of_parse_and_map(node, 0);
|
||||||
|
info.of_node = of_node_get(node);
|
||||||
|
info.archdata = &dev_ad;
|
||||||
|
|
||||||
|
if (of_get_property(node, "wakeup-source", NULL))
|
||||||
|
info.flags |= I2C_CLIENT_WAKE;
|
||||||
|
|
||||||
|
request_module("%s%s", I2C_MODULE_PREFIX, info.type);
|
||||||
|
|
||||||
|
result = i2c_new_device(adap, &info);
|
||||||
|
if (result == NULL) {
|
||||||
|
dev_err(&adap->dev, "of_i2c: Failure registering %s\n",
|
||||||
|
node->full_name);
|
||||||
|
of_node_put(node);
|
||||||
|
irq_dispose_mapping(info.irq);
|
||||||
|
return ERR_PTR(-EINVAL);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
static void of_i2c_register_devices(struct i2c_adapter *adap)
|
static void of_i2c_register_devices(struct i2c_adapter *adap)
|
||||||
{
|
{
|
||||||
void *result;
|
|
||||||
struct device_node *node;
|
struct device_node *node;
|
||||||
|
|
||||||
/* Only register child devices if the adapter has a node pointer set */
|
/* Only register child devices if the adapter has a node pointer set */
|
||||||
|
@ -1380,52 +1431,8 @@ static void of_i2c_register_devices(struct i2c_adapter *adap)
|
||||||
|
|
||||||
dev_dbg(&adap->dev, "of_i2c: walking child nodes\n");
|
dev_dbg(&adap->dev, "of_i2c: walking child nodes\n");
|
||||||
|
|
||||||
for_each_available_child_of_node(adap->dev.of_node, node) {
|
for_each_available_child_of_node(adap->dev.of_node, node)
|
||||||
struct i2c_board_info info = {};
|
of_i2c_register_device(adap, node);
|
||||||
struct dev_archdata dev_ad = {};
|
|
||||||
const __be32 *addr;
|
|
||||||
int len;
|
|
||||||
|
|
||||||
dev_dbg(&adap->dev, "of_i2c: register %s\n", node->full_name);
|
|
||||||
|
|
||||||
if (of_modalias_node(node, info.type, sizeof(info.type)) < 0) {
|
|
||||||
dev_err(&adap->dev, "of_i2c: modalias failure on %s\n",
|
|
||||||
node->full_name);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
addr = of_get_property(node, "reg", &len);
|
|
||||||
if (!addr || (len < sizeof(int))) {
|
|
||||||
dev_err(&adap->dev, "of_i2c: invalid reg on %s\n",
|
|
||||||
node->full_name);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
info.addr = be32_to_cpup(addr);
|
|
||||||
if (info.addr > (1 << 10) - 1) {
|
|
||||||
dev_err(&adap->dev, "of_i2c: invalid addr=%x on %s\n",
|
|
||||||
info.addr, node->full_name);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
info.irq = irq_of_parse_and_map(node, 0);
|
|
||||||
info.of_node = of_node_get(node);
|
|
||||||
info.archdata = &dev_ad;
|
|
||||||
|
|
||||||
if (of_get_property(node, "wakeup-source", NULL))
|
|
||||||
info.flags |= I2C_CLIENT_WAKE;
|
|
||||||
|
|
||||||
request_module("%s%s", I2C_MODULE_PREFIX, info.type);
|
|
||||||
|
|
||||||
result = i2c_new_device(adap, &info);
|
|
||||||
if (result == NULL) {
|
|
||||||
dev_err(&adap->dev, "of_i2c: Failure registering %s\n",
|
|
||||||
node->full_name);
|
|
||||||
of_node_put(node);
|
|
||||||
irq_dispose_mapping(info.irq);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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)
|
||||||
}
|
}
|
||||||
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)
|
||||||
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 @@ bus_err:
|
||||||
|
|
||||||
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)
|
||||||
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
|
||||||
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
|
||||||
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
|
||||||
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
|
||||||
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,
|
||||||
*/
|
*/
|
||||||
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);
|
||||||
* 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 @@
|
||||||
|
|
||||||
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;
|
||||||
*/
|
*/
|
||||||
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)
|
||||||
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,
|
||||||
}
|
}
|
||||||
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)
|
||||||
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);
|
||||||
* 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);
|
||||||
*
|
*
|
||||||
* @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)
|
||||||
*
|
*
|
||||||
* @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);
|
||||||
|
@ -577,9 +593,9 @@ EXPORT_SYMBOL(of_get_parent);
|
||||||
* of_get_next_parent - Iterate to a node's parent
|
* of_get_next_parent - Iterate to a node's parent
|
||||||
* @node: Node to get parent of
|
* @node: Node to get parent of
|
||||||
*
|
*
|
||||||
* This is like of_get_parent() except that it drops the
|
* This is like of_get_parent() except that it drops the
|
||||||
* refcount on the passed node, making it suitable for iterating
|
* refcount on the passed node, making it suitable for iterating
|
||||||
* through a node's parents.
|
* through a node's parents.
|
||||||
*
|
*
|
||||||
* 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.
|
||||||
|
@ -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,
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 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,
|
||||||
* 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)
|
||||||
/* 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)
|
||||||
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,
|
||||||
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,
|
||||||
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,
|
||||||
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,
|
||||||
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,
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 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,
|
||||||
*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)
|
||||||
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,
|
||||||
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,21 +1571,21 @@ 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:
|
||||||
*
|
*
|
||||||
* phandle1: node1 {
|
* phandle1: node1 {
|
||||||
* #list-cells = <2>;
|
* #list-cells = <2>;
|
||||||
* }
|
* }
|
||||||
*
|
*
|
||||||
* phandle2: node2 {
|
* phandle2: node2 {
|
||||||
* #list-cells = <1>;
|
* #list-cells = <1>;
|
||||||
* }
|
* }
|
||||||
*
|
*
|
||||||
* node3 {
|
* node3 {
|
||||||
* list = <&phandle1 1 2 &phandle2 3>;
|
* list = <&phandle1 1 2 &phandle2 3>;
|
||||||
* }
|
* }
|
||||||
*
|
*
|
||||||
* To get a device_node of the `node2' node you may call this:
|
* To get a device_node of the `node2' node you may call this:
|
||||||
|
@ -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:
|
||||||
|
@ -1604,7 +1626,7 @@ EXPORT_SYMBOL(of_parse_phandle_with_args);
|
||||||
* }
|
* }
|
||||||
*
|
*
|
||||||
* node3 {
|
* node3 {
|
||||||
* list = <&phandle1 0 2 &phandle2 2 3>;
|
* list = <&phandle1 0 2 &phandle2 2 3>;
|
||||||
* }
|
* }
|
||||||
*
|
*
|
||||||
* To get a device_node of the `node2' node you may call this:
|
* To get a device_node of the `node2' node you may call this:
|
||||||
|
@ -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))
|
||||||
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)
|
||||||
{
|
{
|
||||||
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)
|
||||||
}
|
}
|
||||||
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)
|
||||||
|
|
||||||
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)
|
||||||
*/
|
*/
|
||||||
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)
|
||||||
__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)
|
||||||
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)
|
||||||
*/
|
*/
|
||||||
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)
|
||||||
__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)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* __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)
|
||||||
{
|
{
|
||||||
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,
|
||||||
|
|
||||||
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
|
||||||
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:
|
||||||
|
|
119
drivers/of/fdt.c
119
drivers/of/fdt.c
|
@ -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 @@
|
||||||
#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,
|
||||||
* @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,
|
||||||
|
|
||||||
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,
|
||||||
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,
|
||||||
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,
|
||||||
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,
|
||||||
(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,
|
||||||
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,
|
||||||
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,
|
||||||
|
|
||||||
/* 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,
|
||||||
|
|
||||||
/* 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;
|
||||||
|
|
||||||
#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)
|
||||||
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)
|
||||||
if (!params)
|
if (!params)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
/* check device tree validity */
|
||||||
|
if (fdt_check_header(params))
|
||||||
|
return false;
|
||||||
|
|
||||||
/* Setup flat device-tree pointer */
|
/* Setup flat device-tree pointer */
|
||||||
initial_boot_params = params;
|
initial_boot_params = params;
|
||||||
|
of_fdt_crc32 = crc32_be(~0, initial_boot_params,
|
||||||
/* check device tree validity */
|
fdt_totalsize(initial_boot_params));
|
||||||
if (fdt_check_header(params)) {
|
|
||||||
initial_boot_params = NULL;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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)
|
||||||
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;
|
|
||||||
flat_dt_blob.size = fdt_totalsize(initial_boot_params);
|
|
||||||
|
|
||||||
d = debugfs_create_blob("flat-device-tree", S_IFREG | S_IRUSR,
|
|
||||||
d, &flat_dt_blob);
|
|
||||||
if (!d)
|
|
||||||
return -ENOENT;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
module_init(of_flat_dt_debugfs_export_fdt);
|
|
||||||
|
static int __init of_fdt_raw_init(void)
|
||||||
|
{
|
||||||
|
static struct bin_attribute of_fdt_raw_attr =
|
||||||
|
__BIN_ATTR(fdt, S_IRUSR, of_fdt_raw_read, NULL, 0);
|
||||||
|
|
||||||
|
if (!initial_boot_params)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
of_fdt_raw_attr.size = fdt_totalsize(initial_boot_params);
|
||||||
|
return sysfs_create_bin_file(firmware_kobj, &of_fdt_raw_attr);
|
||||||
|
}
|
||||||
|
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,
|
||||||
* 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);
|
||||||
|
|
|
@ -0,0 +1,562 @@
|
||||||
|
/*
|
||||||
|
* 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 @@
|
||||||
|
|
||||||
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,
|
||||||
}
|
}
|
||||||
|
|
||||||
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,
|
||||||
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)
|
||||||
|
|
||||||
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,
|
||||||
}
|
}
|
||||||
|
|
||||||
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,
|
||||||
|
|
||||||
/* 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,
|
||||||
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)
|
||||||
*/
|
*/
|
||||||
void of_platform_depopulate(struct device *parent)
|
void of_platform_depopulate(struct device *parent)
|
||||||
{
|
{
|
||||||
device_for_each_child(parent, NULL, of_platform_device_destroy);
|
if (parent->of_node && of_node_check_flag(parent->of_node, OF_POPULATED_BUS)) {
|
||||||
|
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,
|
||||||
__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
|
||||||
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 @@ err_fail:
|
||||||
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;
|
||||||
|
phandle phandle;
|
||||||
|
|
||||||
/* locate the symbols & fixups nodes on resolve */
|
if (node == NULL)
|
||||||
for_each_child_of_node(node, child)
|
|
||||||
if (of_node_cmp(child->name, "__local_fixups__") == 0)
|
|
||||||
break;
|
|
||||||
|
|
||||||
/* no local fixups */
|
|
||||||
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,
|
||||||
*/
|
*/
|
||||||
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,9 +317,23 @@ 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);
|
|
||||||
if (err != 0)
|
/* locate the local fixups */
|
||||||
return err;
|
childroot = NULL;
|
||||||
|
for_each_child_of_node(resolve, childroot)
|
||||||
|
if (of_node_cmp(childroot->name, "__local_fixups__") == 0)
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (childroot != NULL) {
|
||||||
|
/* resolve root is guaranteed to be the '/' */
|
||||||
|
err = __of_adjust_tree_phandle_references(childroot,
|
||||||
|
resolve, 0);
|
||||||
|
if (err != 0)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
BUG_ON(__of_adjust_tree_phandle_references(childroot,
|
||||||
|
resolve, phandle_delta));
|
||||||
|
}
|
||||||
|
|
||||||
root_sym = NULL;
|
root_sym = NULL;
|
||||||
resolve_sym = NULL;
|
resolve_sym = NULL;
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,50 +0,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"
|
|
||||||
|
|
||||||
/*
|
|
||||||
* 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";
|
|
||||||
}; };
|
|
|
@ -0,0 +1,79 @@
|
||||||
|
/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>;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}; };
|
|
@ -0,0 +1,180 @@
|
||||||
|
|
||||||
|
/ {
|
||||||
|
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 @@
|
||||||
#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];
|
||||||
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)
|
||||||
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)
|
||||||
|
|
||||||
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)
|
||||||
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)
|
||||||
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)
|
||||||
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)
|
||||||
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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
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,
|
||||||
*/
|
*/
|
||||||
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;
|
np->child = NULL;
|
||||||
dup = of_find_node_by_path(np->full_name);
|
np->sibling = NULL;
|
||||||
if (dup)
|
of_attach_node(np);
|
||||||
update_node_properties(np, dup);
|
while (child) {
|
||||||
else {
|
next = child->sibling;
|
||||||
np->child = NULL;
|
attach_node_and_children(child);
|
||||||
if (np->parent == root)
|
child = next;
|
||||||
np->parent = of_allnodes;
|
|
||||||
of_attach_node(np);
|
|
||||||
}
|
|
||||||
np = next;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 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)
|
||||||
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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#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)
|
||||||
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,6 +1220,121 @@ err_init_queue:
|
||||||
/*-------------------------------------------------------------------------*/
|
/*-------------------------------------------------------------------------*/
|
||||||
|
|
||||||
#if defined(CONFIG_OF)
|
#if defined(CONFIG_OF)
|
||||||
|
static struct spi_device *
|
||||||
|
of_register_spi_device(struct spi_master *master, struct device_node *nc)
|
||||||
|
{
|
||||||
|
struct spi_device *spi;
|
||||||
|
int rc;
|
||||||
|
u32 value;
|
||||||
|
|
||||||
|
/* Alloc an spi_device */
|
||||||
|
spi = spi_alloc_device(master);
|
||||||
|
if (!spi) {
|
||||||
|
dev_err(&master->dev, "spi_device alloc error for %s\n",
|
||||||
|
nc->full_name);
|
||||||
|
rc = -ENOMEM;
|
||||||
|
goto err_out;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Select device driver */
|
||||||
|
rc = of_modalias_node(nc, spi->modalias,
|
||||||
|
sizeof(spi->modalias));
|
||||||
|
if (rc < 0) {
|
||||||
|
dev_err(&master->dev, "cannot find modalias for %s\n",
|
||||||
|
nc->full_name);
|
||||||
|
goto err_out;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Device address */
|
||||||
|
rc = of_property_read_u32(nc, "reg", &value);
|
||||||
|
if (rc) {
|
||||||
|
dev_err(&master->dev, "%s has no valid 'reg' property (%d)\n",
|
||||||
|
nc->full_name, rc);
|
||||||
|
goto err_out;
|
||||||
|
}
|
||||||
|
spi->chip_select = value;
|
||||||
|
|
||||||
|
/* Mode (clock phase/polarity/etc.) */
|
||||||
|
if (of_find_property(nc, "spi-cpha", NULL))
|
||||||
|
spi->mode |= SPI_CPHA;
|
||||||
|
if (of_find_property(nc, "spi-cpol", NULL))
|
||||||
|
spi->mode |= SPI_CPOL;
|
||||||
|
if (of_find_property(nc, "spi-cs-high", NULL))
|
||||||
|
spi->mode |= SPI_CS_HIGH;
|
||||||
|
if (of_find_property(nc, "spi-3wire", NULL))
|
||||||
|
spi->mode |= SPI_3WIRE;
|
||||||
|
if (of_find_property(nc, "spi-lsb-first", NULL))
|
||||||
|
spi->mode |= SPI_LSB_FIRST;
|
||||||
|
|
||||||
|
/* Device DUAL/QUAD mode */
|
||||||
|
if (!of_property_read_u32(nc, "spi-tx-bus-width", &value)) {
|
||||||
|
switch (value) {
|
||||||
|
case 1:
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
spi->mode |= SPI_TX_DUAL;
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
spi->mode |= SPI_TX_QUAD;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
dev_warn(&master->dev,
|
||||||
|
"spi-tx-bus-width %d not supported\n",
|
||||||
|
value);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!of_property_read_u32(nc, "spi-rx-bus-width", &value)) {
|
||||||
|
switch (value) {
|
||||||
|
case 1:
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
spi->mode |= SPI_RX_DUAL;
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
spi->mode |= SPI_RX_QUAD;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
dev_warn(&master->dev,
|
||||||
|
"spi-rx-bus-width %d not supported\n",
|
||||||
|
value);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Device speed */
|
||||||
|
rc = of_property_read_u32(nc, "spi-max-frequency", &value);
|
||||||
|
if (rc) {
|
||||||
|
dev_err(&master->dev, "%s has no valid 'spi-max-frequency' property (%d)\n",
|
||||||
|
nc->full_name, rc);
|
||||||
|
goto err_out;
|
||||||
|
}
|
||||||
|
spi->max_speed_hz = value;
|
||||||
|
|
||||||
|
/* IRQ */
|
||||||
|
spi->irq = irq_of_parse_and_map(nc, 0);
|
||||||
|
|
||||||
|
/* Store a pointer to the node in the device structure */
|
||||||
|
of_node_get(nc);
|
||||||
|
spi->dev.of_node = nc;
|
||||||
|
|
||||||
|
/* Register the new device */
|
||||||
|
request_module("%s%s", SPI_MODULE_PREFIX, spi->modalias);
|
||||||
|
rc = spi_add_device(spi);
|
||||||
|
if (rc) {
|
||||||
|
dev_err(&master->dev, "spi_device register error %s\n",
|
||||||
|
nc->full_name);
|
||||||
|
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
|
* of_register_spi_devices() - Register child devices onto the SPI bus
|
||||||
* @master: Pointer to spi_master device
|
* @master: Pointer to spi_master device
|
||||||
|
@ -1231,116 +1346,15 @@ static void of_register_spi_devices(struct spi_master *master)
|
||||||
{
|
{
|
||||||
struct spi_device *spi;
|
struct spi_device *spi;
|
||||||
struct device_node *nc;
|
struct device_node *nc;
|
||||||
int rc;
|
|
||||||
u32 value;
|
|
||||||
|
|
||||||
if (!master->dev.of_node)
|
if (!master->dev.of_node)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
for_each_available_child_of_node(master->dev.of_node, nc) {
|
for_each_available_child_of_node(master->dev.of_node, nc) {
|
||||||
/* Alloc an spi_device */
|
spi = of_register_spi_device(master, nc);
|
||||||
spi = spi_alloc_device(master);
|
if (IS_ERR(spi))
|
||||||
if (!spi) {
|
dev_warn(&master->dev, "Failed to create SPI device for %s\n",
|
||||||
dev_err(&master->dev, "spi_device alloc error for %s\n",
|
|
||||||
nc->full_name);
|
nc->full_name);
|
||||||
spi_dev_put(spi);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Select device driver */
|
|
||||||
if (of_modalias_node(nc, spi->modalias,
|
|
||||||
sizeof(spi->modalias)) < 0) {
|
|
||||||
dev_err(&master->dev, "cannot find modalias for %s\n",
|
|
||||||
nc->full_name);
|
|
||||||
spi_dev_put(spi);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Device address */
|
|
||||||
rc = of_property_read_u32(nc, "reg", &value);
|
|
||||||
if (rc) {
|
|
||||||
dev_err(&master->dev, "%s has no valid 'reg' property (%d)\n",
|
|
||||||
nc->full_name, rc);
|
|
||||||
spi_dev_put(spi);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
spi->chip_select = value;
|
|
||||||
|
|
||||||
/* Mode (clock phase/polarity/etc.) */
|
|
||||||
if (of_find_property(nc, "spi-cpha", NULL))
|
|
||||||
spi->mode |= SPI_CPHA;
|
|
||||||
if (of_find_property(nc, "spi-cpol", NULL))
|
|
||||||
spi->mode |= SPI_CPOL;
|
|
||||||
if (of_find_property(nc, "spi-cs-high", NULL))
|
|
||||||
spi->mode |= SPI_CS_HIGH;
|
|
||||||
if (of_find_property(nc, "spi-3wire", NULL))
|
|
||||||
spi->mode |= SPI_3WIRE;
|
|
||||||
if (of_find_property(nc, "spi-lsb-first", NULL))
|
|
||||||
spi->mode |= SPI_LSB_FIRST;
|
|
||||||
|
|
||||||
/* Device DUAL/QUAD mode */
|
|
||||||
if (!of_property_read_u32(nc, "spi-tx-bus-width", &value)) {
|
|
||||||
switch (value) {
|
|
||||||
case 1:
|
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
spi->mode |= SPI_TX_DUAL;
|
|
||||||
break;
|
|
||||||
case 4:
|
|
||||||
spi->mode |= SPI_TX_QUAD;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
dev_warn(&master->dev,
|
|
||||||
"spi-tx-bus-width %d not supported\n",
|
|
||||||
value);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!of_property_read_u32(nc, "spi-rx-bus-width", &value)) {
|
|
||||||
switch (value) {
|
|
||||||
case 1:
|
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
spi->mode |= SPI_RX_DUAL;
|
|
||||||
break;
|
|
||||||
case 4:
|
|
||||||
spi->mode |= SPI_RX_QUAD;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
dev_warn(&master->dev,
|
|
||||||
"spi-rx-bus-width %d not supported\n",
|
|
||||||
value);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Device speed */
|
|
||||||
rc = of_property_read_u32(nc, "spi-max-frequency", &value);
|
|
||||||
if (rc) {
|
|
||||||
dev_err(&master->dev, "%s has no valid 'spi-max-frequency' property (%d)\n",
|
|
||||||
nc->full_name, rc);
|
|
||||||
spi_dev_put(spi);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
spi->max_speed_hz = value;
|
|
||||||
|
|
||||||
/* IRQ */
|
|
||||||
spi->irq = irq_of_parse_and_map(nc, 0);
|
|
||||||
|
|
||||||
/* Store a pointer to the node in the device structure */
|
|
||||||
of_node_get(nc);
|
|
||||||
spi->dev.of_node = nc;
|
|
||||||
|
|
||||||
/* Register the new device */
|
|
||||||
request_module("%s%s", SPI_MODULE_PREFIX, spi->modalias);
|
|
||||||
rc = spi_add_device(spi);
|
|
||||||
if (rc) {
|
|
||||||
dev_err(&master->dev, "spi_device register error %s\n",
|
|
||||||
nc->full_name);
|
|
||||||
spi_dev_put(spi);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
|
@ -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)
|
||||||
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 @@
|
||||||
#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 {
|
||||||
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 {
|
||||||
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) { }
|
||||||
#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)
|
||||||
|
|
||||||
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
|
||||||
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)
|
||||||
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(
|
||||||
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,
|
||||||
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);
|
||||||
#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)
|
||||||
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,
|
||||||
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,
|
||||||
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); \
|
||||||
|
@ -861,7 +877,7 @@ static inline int of_get_available_child_count(const struct device_node *np)
|
||||||
= { .compatible = compat, \
|
= { .compatible = compat, \
|
||||||
.data = (fn == (fn_type)NULL) ? fn : fn }
|
.data = (fn == (fn_type)NULL) ? fn : fn }
|
||||||
#else
|
#else
|
||||||
#define _OF_DECLARE(table, name, compat, fn, fn_type) \
|
#define _OF_DECLARE(table, name, compat, fn, fn_type) \
|
||||||
static const struct of_device_id __of_table_##name \
|
static const struct of_device_id __of_table_##name \
|
||||||
__attribute__((unused)) \
|
__attribute__((unused)) \
|
||||||
= { .compatible = compat, \
|
= { .compatible = compat, \
|
||||||
|
@ -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,
|
||||||
{
|
{
|
||||||
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
|
||||||
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,
|
||||||
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)
|
||||||
}
|
}
|
||||||
|
|
||||||
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);
|
||||||
/* 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,
|
||||||
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 */
|
||||||
|
|
Загрузка…
Ссылка в новой задаче