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

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

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

  Highlights:

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

   - CONFIG_OF_OVERLAY method for applying live changes to the device
     tree

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

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

   - More unittests

   - Documentation and minor bug fixes"

* tag 'devicetree-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/glikely/linux: (42 commits)
  of: Delete unnecessary check before calling "of_node_put()"
  of: Drop ->next pointer from struct device_node
  spi: Check for spi_of_notifier when CONFIG_OF_DYNAMIC=y
  of: support passing console options with stdout-path
  of: add optional options parameter to of_find_node_by_path()
  of: Add bindings for chosen node, stdout-path
  of: Remove unneeded and incorrect MODULE_DEVICE_TABLE
  ARM: dt: fix up PL011 device tree bindings
  of: base, fix of_property_read_string_helper kernel-doc
  of: remove select of non-existant OF_DEVICE config symbol
  spi/of: Add OF notifier handler
  spi/of: Create new device registration method and accessors
  i2c/of: Add OF_RECONFIG notifier handler
  i2c/of: Factor out Devicetree registration code
  of/overlay: Add overlay unittests
  of/overlay: Introduce DT overlay support
  of/reconfig: Add OF_DYNAMIC notifier for platform_bus_type
  of/reconfig: Always use the same structure for notifiers
  of/reconfig: Add debug output for OF_RECONFIG notifiers
  of/reconfig: Add empty stubs for the of_reconfig methods
  ...
parents 413fd0e3 c46ca3c8
The chosen node
---------------
The chosen node does not represent a real device, but serves as a place
for passing data between firmware and the operating system, like boot
arguments. Data in the chosen node does not represent the hardware.
stdout-path property
--------------------
Device trees may specify the device to be used for boot console output
with a stdout-path property under /chosen, as described in ePAPR, e.g.
/ {
chosen {
stdout-path = "/serial@f00:115200";
};
serial@f00 {
compatible = "vendor,some-uart";
reg = <0xf00 0x10>;
};
};
If the character ":" is present in the value, this terminates the path.
The meaning of any characters following the ":" is device-specific, and
must be specified in the relevant binding documentation.
For UART devices, the preferred binding is a string in the form:
<baud>{<parity>{<bits>{<flow>}}}
where
baud - baud rate in decimal
parity - 'n' (none), 'o', (odd) or 'e' (even)
bits - number of data bits
flow - 'r' (rts)
For example: 115200n8r
Implementation note: Linux will look for the property "linux,stdout-path" or
on PowerPC "stdout" if "stdout-path" is not found. However, the
"linux,stdout-path" and "stdout" properties are deprecated. New platforms
should only use the "stdout-path" property.
......@@ -6,12 +6,34 @@ Required properties:
- interrupts: exactly one interrupt specifier
Optional properties:
- pinctrl: When present, must have one state named "sleep"
and one state named "default"
- clocks: When present, must refer to exactly one clock named
- pinctrl: When present, must have one state named "default",
and may contain a second name named "sleep". The former
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"
- dmas: When present, may have one or two dma channels.
The first one must be named "rx", the second one
must be named "tx".
See also bindings/arm/primecell.txt
Example:
uart@80120000 {
compatible = "arm,pl011", "arm,primecell";
reg = <0x80120000 0x1000>;
interrupts = <0 11 IRQ_TYPE_LEVEL_HIGH>;
dmas = <&dma 13 0 0x2>, <&dma 13 0 0x0>;
dma-names = "rx", "tx";
clocks = <&foo_clk>, <&bar_clk>;
clock-names = "uartclk", "apb_pclk";
};
* OF selftest platform device
** selftest
Required properties:
- compatible: must be "selftest"
All other properties are optional.
Example:
selftest {
compatible = "selftest";
status = "okay";
};
......@@ -63,7 +63,6 @@ struct device_node {
struct device_node *parent;
struct device_node *child;
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
*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
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
......@@ -131,11 +124,6 @@ root ('/')
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.
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
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
attaching the previous node's allnext to current node's allnext pointer. And
then, it either updates the child pointer of given node's parent to its
sibling or attaches the previous sibling to the given node's sibling, as
appropriate. That is it :)
To detach a node, of_detach_node() either updates the child pointer of given
node's parent to its sibling or attaches the previous sibling to the given
node's sibling, as appropriate. That is it :)
Device Tree Overlay Notes
-------------------------
This document describes the implementation of the in-kernel
device tree overlay functionality residing in drivers/of/overlay.c and is a
companion document to Documentation/devicetree/dt-object-internal.txt[1] &
Documentation/devicetree/dynamic-resolution-notes.txt[2]
How overlays work
-----------------
A Device Tree's overlay purpose is to modify the kernel's live tree, and
have the modification affecting the state of the the kernel in a way that
is reflecting the changes.
Since the kernel mainly deals with devices, any new device node that result
in an active device should have it created while if the device node is either
disabled or removed all together, the affected device should be deregistered.
Lets take an example where we have a foo board with the following base tree
which is taken from [1].
---- foo.dts -----------------------------------------------------------------
/* FOO platform */
/ {
compatible = "corp,foo";
/* shared resources */
res: res {
};
/* On chip peripherals */
ocp: ocp {
/* peripherals that are always instantiated */
peripheral1 { ... };
}
};
---- foo.dts -----------------------------------------------------------------
The overlay bar.dts, when loaded (and resolved as described in [2]) should
---- bar.dts -----------------------------------------------------------------
/plugin/; /* allow undefined label references and record them */
/ {
.... /* various properties for loader use; i.e. part id etc. */
fragment@0 {
target = <&ocp>;
__overlay__ {
/* bar peripheral */
bar {
compatible = "corp,bar";
... /* various properties and child nodes */
}
};
};
};
---- bar.dts -----------------------------------------------------------------
result in foo+bar.dts
---- foo+bar.dts -------------------------------------------------------------
/* FOO platform + bar peripheral */
/ {
compatible = "corp,foo";
/* shared resources */
res: res {
};
/* On chip peripherals */
ocp: ocp {
/* peripherals that are always instantiated */
peripheral1 { ... };
/* bar peripheral */
bar {
compatible = "corp,bar";
... /* various properties and child nodes */
}
}
};
---- foo+bar.dts -------------------------------------------------------------
As a result of the the overlay, a new device node (bar) has been created
so a bar platform device will be registered and if a matching device driver
is loaded the device will be created as expected.
Overlay in-kernel API
--------------------------------
The API is quite easy to use.
1. Call of_overlay_create() to create and apply an overlay. The return value
is a cookie identifying this overlay.
2. Call of_overlay_destroy() to remove and cleanup the overlay previously
created via the call to of_overlay_create(). Removal of an overlay that
is stacked by another will not be permitted.
Finally, if you need to remove all overlays in one-go, just call
of_overlay_destroy_all() which will remove every single one in the correct
order.
Overlay DTS Format
------------------
The DTS of an overlay should have the following format:
{
/* ignored properties by the overlay */
fragment@0 { /* first child node */
target=<phandle>; /* phandle target of the overlay */
or
target-path="/path"; /* target path of the overlay */
__overlay__ {
property-a; /* add property-a to the target */
node-a { /* add to an existing, or create a node-a */
...
};
};
}
fragment@1 { /* second child node */
...
};
/* more fragments follow */
}
Using the non-phandle based target method allows one to use a base DT which does
not contain a __symbols__ node, i.e. it was not compiled with the -@ option.
The __symbols__ node is only required for the target=<phandle> method, since it
contains the information required to map from a phandle to a tree location.
......@@ -2,7 +2,6 @@ Todo list for devicetree:
=== General 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 ===
- 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,
unsigned long action, void *data)
{
struct of_prop_reconfig *update;
struct of_reconfig_data *update = data;
int rc = NOTIFY_DONE;
switch (action) {
case OF_RECONFIG_UPDATE_PROPERTY:
update = (struct of_prop_reconfig *)data;
if (!of_prop_cmp(update->dn->type, "cpu") &&
!of_prop_cmp(update->prop->name, "ibm,associativity")) {
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,
unsigned long action, void *node)
unsigned long action, void *data)
{
struct of_reconfig_data *rd = data;
int err = 0;
switch (action) {
case OF_RECONFIG_ATTACH_NODE:
err = pseries_add_processor(node);
err = pseries_add_processor(rd->dn);
break;
case OF_RECONFIG_DETACH_NODE:
pseries_remove_processor(node);
pseries_remove_processor(rd->dn);
break;
}
return notifier_from_errno(err);
......
......@@ -183,7 +183,7 @@ static int pseries_add_mem_node(struct device_node *np)
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;
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,
unsigned long action, void *node)
unsigned long action, void *data)
{
struct of_prop_reconfig *pr;
struct of_reconfig_data *rd = data;
int err = 0;
switch (action) {
case OF_RECONFIG_ATTACH_NODE:
err = pseries_add_mem_node(node);
err = pseries_add_mem_node(rd->dn);
break;
case OF_RECONFIG_DETACH_NODE:
err = pseries_remove_mem_node(node);
err = pseries_remove_mem_node(rd->dn);
break;
case OF_RECONFIG_UPDATE_PROPERTY:
pr = (struct of_prop_reconfig *)node;
if (!strcmp(pr->prop->name, "ibm,dynamic-memory"))
err = pseries_update_drconf_memory(pr);
if (!strcmp(rd->prop->name, "ibm,dynamic-memory"))
err = pseries_update_drconf_memory(rd);
break;
}
return notifier_from_errno(err);
......
......@@ -1251,10 +1251,11 @@ static struct notifier_block iommu_mem_nb = {
.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;
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 direct_window *window;
......
......@@ -251,9 +251,10 @@ static void __init pseries_discover_pic(void)
" 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;
int err = NOTIFY_OK;
......
......@@ -1006,6 +1006,7 @@ int __init platform_bus_init(void)
error = bus_register(&platform_bus_type);
if (error)
device_unregister(&platform_bus);
of_platform_register_reconfig_notifier();
return error;
}
......
......@@ -1009,9 +1009,9 @@ static int nx842_OF_upd(struct property *new_prop)
* notifier_to_errno() to decode this value
*/
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 device_node *node = NULL;
......
......@@ -49,6 +49,7 @@
#include <linux/acpi.h>
#include <linux/jump_label.h>
#include <asm/uaccess.h>
#include <linux/err.h>
#include "i2c-core.h"
......@@ -1369,63 +1370,69 @@ static void i2c_scan_static_board_info(struct i2c_adapter *adapter)
/* OF support code */
#if IS_ENABLED(CONFIG_OF)
static void of_i2c_register_devices(struct i2c_adapter *adap)
static struct i2c_client *of_i2c_register_device(struct i2c_adapter *adap,
struct device_node *node)
{
void *result;
struct device_node *node;
struct i2c_client *result;
struct i2c_board_info info = {};
struct dev_archdata dev_ad = {};
const __be32 *addr;
int len;
/* Only register child devices if the adapter has a node pointer set */
if (!adap->dev.of_node)
return;
dev_dbg(&adap->dev, "of_i2c: register %s\n", node->full_name);
dev_dbg(&adap->dev, "of_i2c: walking child nodes\n");
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);
}
for_each_available_child_of_node(adap->dev.of_node, node) {
struct i2c_board_info info = {};
struct dev_archdata dev_ad = {};
const __be32 *addr;
int len;
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);
}
dev_dbg(&adap->dev, "of_i2c: register %s\n", node->full_name);
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);
}
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;
}
info.irq = irq_of_parse_and_map(node, 0);
info.of_node = of_node_get(node);
info.archdata = &dev_ad;
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;
}
if (of_get_property(node, "wakeup-source", NULL))
info.flags |= I2C_CLIENT_WAKE;
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;
}
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;
}
info.irq = irq_of_parse_and_map(node, 0);
info.of_node = of_node_get(node);
info.archdata = &dev_ad;
static void of_i2c_register_devices(struct i2c_adapter *adap)
{
struct device_node *node;
if (of_get_property(node, "wakeup-source", NULL))
info.flags |= I2C_CLIENT_WAKE;
/* Only register child devices if the adapter has a node pointer set */
if (!adap->dev.of_node)
return;
request_module("%s%s", I2C_MODULE_PREFIX, info.type);
dev_dbg(&adap->dev, "of_i2c: walking child nodes\n");
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;
}
}
for_each_available_child_of_node(adap->dev.of_node, node)
of_i2c_register_device(adap, node);
}
static int of_dev_node_match(struct device *dev, void *data)
......@@ -1945,6 +1952,52 @@ void i2c_clients_command(struct i2c_adapter *adap, unsigned int cmd, void *arg)
}
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)
{
int retval;
......@@ -1962,6 +2015,10 @@ static int __init i2c_init(void)
retval = i2c_add_driver(&dummy_driver);
if (retval)
goto class_err;
if (IS_ENABLED(CONFIG_OF_DYNAMIC))
WARN_ON(of_reconfig_notifier_register(&i2c_of_notifier));
return 0;
class_err:
......@@ -1975,6 +2032,8 @@ static int __init i2c_init(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);
#ifdef CONFIG_I2C_COMPAT
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());
/* 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 hbi = (id >> SYS_PROCIDx_HBI_SHIFT) & SYS_HBI_MASK;
......
......@@ -7,8 +7,8 @@ config OF
menu "Device Tree and Open Firmware support"
depends on OF
config OF_SELFTEST
bool "Device Tree Runtime self tests"
config OF_UNITTEST
bool "Device Tree runtime unit tests"
depends on OF_IRQ && OF_EARLY_FLATTREE
select OF_DYNAMIC
select OF_RESOLVE
......@@ -23,6 +23,7 @@ config OF_FLATTREE
bool
select DTC
select LIBFDT
select CRC32
config OF_EARLY_FLATTREE
bool
......@@ -83,4 +84,10 @@ config OF_RESERVED_MEM
config OF_RESOLVE
bool
config OF_OVERLAY
bool
depends on OF
select OF_DYNAMIC
select OF_RESOLVE
endmenu # OF
......@@ -6,14 +6,15 @@ obj-$(CONFIG_OF_PROMTREE) += pdt.o
obj-$(CONFIG_OF_ADDRESS) += address.o
obj-$(CONFIG_OF_IRQ) += irq.o
obj-$(CONFIG_OF_NET) += of_net.o
obj-$(CONFIG_OF_SELFTEST) += of_selftest.o
of_selftest-objs := selftest.o testcase-data/testcases.dtb.o
obj-$(CONFIG_OF_UNITTEST) += of_unittest.o
of_unittest-objs := unittest.o unittest-data/testcases.dtb.o
obj-$(CONFIG_OF_MDIO) += of_mdio.o
obj-$(CONFIG_OF_PCI) += of_pci.o
obj-$(CONFIG_OF_PCI_IRQ) += of_pci_irq.o
obj-$(CONFIG_OF_MTD) += of_mtd.o
obj-$(CONFIG_OF_RESERVED_MEM) += of_reserved_mem.o
obj-$(CONFIG_OF_RESOLVE) += resolver.o
obj-$(CONFIG_OF_OVERLAY) += overlay.o
CFLAGS_fdt.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);
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;
}
if (ranges == NULL || rlen == 0) {
......@@ -884,7 +884,7 @@ EXPORT_SYMBOL(of_iomap);
* return PTR_ERR(base);
*/
void __iomem *of_io_request_and_map(struct device_node *np, int index,
char *name)
const char *name)
{
struct resource res;
void __iomem *mem;
......
This diff is collapsed.
......@@ -77,18 +77,132 @@ int of_reconfig_notifier_unregister(struct notifier_block *nb)
}
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;
#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);
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,
struct property *prop, struct property *oldprop)
{
struct of_prop_reconfig pr;
struct of_reconfig_data pr;
/* only call notifiers if the node is attached */
if (!of_node_is_attached(np))
......@@ -117,8 +231,6 @@ void __of_attach_node(struct device_node *np)
np->child = NULL;
np->sibling = np->parent->child;
np->allnext = np->parent->allnext;
np->parent->allnext = np;
np->parent->child = np;
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)
{
struct of_reconfig_data rd;
unsigned long flags;
memset(&rd, 0, sizeof(rd));
rd.dn = np;
mutex_lock(&of_mutex);
raw_spin_lock_irqsave(&devtree_lock, flags);
__of_attach_node(np);
......@@ -138,7 +254,7 @@ int of_attach_node(struct device_node *np)
__of_attach_node_sysfs(np);
mutex_unlock(&of_mutex);
of_reconfig_notify(OF_RECONFIG_ATTACH_NODE, np);
of_reconfig_notify(OF_RECONFIG_ATTACH_NODE, &rd);
return 0;
}
......@@ -154,17 +270,6 @@ void __of_detach_node(struct device_node *np)
if (WARN_ON(!parent))
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)
parent->child = np->sibling;
else {
......@@ -187,9 +292,13 @@ void __of_detach_node(struct device_node *np)
*/
int of_detach_node(struct device_node *np)
{
struct of_reconfig_data rd;
unsigned long flags;
int rc = 0;
memset(&rd, 0, sizeof(rd));
rd.dn = np;
mutex_lock(&of_mutex);
raw_spin_lock_irqsave(&devtree_lock, flags);
__of_detach_node(np);
......@@ -198,7 +307,7 @@ int of_detach_node(struct device_node *np)
__of_detach_node_sysfs(np);
mutex_unlock(&of_mutex);
of_reconfig_notify(OF_RECONFIG_DETACH_NODE, np);
of_reconfig_notify(OF_RECONFIG_DETACH_NODE, &rd);
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.
* @full_name: Full name of the new device node
* @allocflags: Allocation flags (typically pass GFP_KERNEL)
* __of_node_dup() - Duplicate or create an empty device node dynamically.
* @fmt: Format string (plus vargs) for new full name of the device node
*
* Create an empty device tree node, suitable for further modification.
* The node data are dynamically allocated and all the node flags
* have the OF_DYNAMIC & OF_DETACHED bits set.
* Returns the newly allocated node or NULL on out of memory error.
* Create an device tree node, either by duplicating an empty node or by allocating
* an empty one suitable for further modification. The node data are
* dynamically allocated and all the node flags have the OF_DYNAMIC &
* 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;
node = kzalloc(sizeof(*node), allocflags);
node = kzalloc(sizeof(*node), GFP_KERNEL);
if (!node)
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_DETACHED);
if (!node->full_name)
goto err_free;
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;
err_free:
kfree(node->full_name);
kfree(node);
err_prop:
of_node_put(node); /* Frees the node and properties */
return NULL;
}
......@@ -330,27 +457,15 @@ static void __of_changeset_entry_dump(struct of_changeset_entry *ce)
{
switch (ce->action) {
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:
pr_debug("%p: %s %s/%s\n",
ce, "REMOVE_PROPERTY", ce->np->full_name,
ce->prop->name);
break;
case OF_RECONFIG_UPDATE_PROPERTY:
pr_debug("%p: %s %s/%s\n",
ce, "UPDATE_PROPERTY", ce->np->full_name,
ce->prop->name);
pr_debug("of/cset<%p> %-15s %s/%s\n", ce, action_names[ce->action],
ce->np->full_name, ce->prop->name);
break;
case OF_RECONFIG_ATTACH_NODE:
pr_debug("%p: %s %s\n",
ce, "ATTACH_NODE ", ce->np->full_name);
break;
case OF_RECONFIG_DETACH_NODE:
pr_debug("%p: %s %s\n",
ce, "DETACH_NODE ", ce->np->full_name);
pr_debug("of/cset<%p> %-15s %s\n", ce, action_names[ce->action],
ce->np->full_name);
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)
{
struct of_reconfig_data rd;
struct of_changeset_entry ce_inverted;
int ret;
......@@ -399,7 +515,9 @@ static void __of_changeset_entry_notify(struct of_changeset_entry *ce, bool reve
switch (ce->action) {
case OF_RECONFIG_ATTACH_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;
case OF_RECONFIG_ADD_PROPERTY:
case OF_RECONFIG_REMOVE_PROPERTY:
......
......@@ -9,6 +9,7 @@
* version 2 as published by the Free Software Foundation.
*/
#include <linux/crc32.h>
#include <linux/kernel.h>
#include <linux/initrd.h>
#include <linux/memblock.h>
......@@ -22,6 +23,7 @@
#include <linux/libfdt.h>
#include <linux/debugfs.h>
#include <linux/serial_core.h>
#include <linux/sysfs.h>
#include <asm/setup.h> /* for COMMAND_LINE_SIZE */
#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
* @p: pointer to node in flat tree
* @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.
*/
static void * unflatten_dt_node(void *blob,
void *mem,
int *poffset,
struct device_node *dad,
struct device_node ***allnextpp,
unsigned long fpsize)
struct device_node **nodepp,
unsigned long fpsize,
bool dryrun)
{
const __be32 *p;
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,
__alignof__(struct device_node));
if (allnextpp) {
if (!dryrun) {
char *fn;
of_node_init(np);
np->full_name = fn = ((char *)np) + sizeof(*np);
......@@ -222,16 +224,10 @@ static void * unflatten_dt_node(void *blob,
memcpy(fn, pathp, l);
prev_pp = &np->properties;
**allnextpp = np;
*allnextpp = &np->allnext;
if (dad != NULL) {
np->parent = dad;
/* we temporarily use the next field as `last_child'*/
if (dad->next == NULL)
dad->child = np;
else
dad->next->sibling = np;
dad->next = np;
np->sibling = dad->child;
dad->child = np;
}
}
/* process properties */
......@@ -254,7 +250,7 @@ static void * unflatten_dt_node(void *blob,
has_name = 1;
pp = unflatten_dt_alloc(&mem, sizeof(struct property),
__alignof__(struct property));
if (allnextpp) {
if (!dryrun) {
/* We accept flattened tree phandles either in
* ePAPR-style "phandle" properties, or the
* legacy "linux,phandle" properties. If both
......@@ -296,7 +292,7 @@ static void * unflatten_dt_node(void *blob,
sz = (pa - ps) + 1;
pp = unflatten_dt_alloc(&mem, sizeof(struct property) + sz,
__alignof__(struct property));
if (allnextpp) {
if (!dryrun) {
pp->name = "name";
pp->length = sz;
pp->value = pp + 1;
......@@ -308,7 +304,7 @@ static void * unflatten_dt_node(void *blob,
(char *)pp->value);
}
}
if (allnextpp) {
if (!dryrun) {
*prev_pp = NULL;
np->name = of_get_property(np, "name", NULL);
np->type = of_get_property(np, "device_type", NULL);
......@@ -324,12 +320,30 @@ static void * unflatten_dt_node(void *blob,
if (depth < 0)
depth = 0;
while (*poffset > 0 && depth > old_depth)
mem = unflatten_dt_node(blob, mem, poffset, np, allnextpp,
fpsize);
mem = unflatten_dt_node(blob, mem, poffset, np, NULL,
fpsize, dryrun);
if (*poffset < 0 && *poffset != -FDT_ERR_NOTFOUND)
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;
}
......@@ -352,7 +366,6 @@ static void __unflatten_device_tree(void *blob,
unsigned long size;
int start;
void *mem;
struct device_node **allnextp = mynodes;
pr_debug(" -> unflatten_device_tree()\n");
......@@ -373,7 +386,7 @@ static void __unflatten_device_tree(void *blob,
/* First pass, scan for size */
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);
pr_debug(" size is %lx, allocating...\n", size);
......@@ -388,11 +401,10 @@ static void __unflatten_device_tree(void *blob,
/* Second pass, do actual unflattening */
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)
pr_warning("End of tree marker overwritten: %08x\n",
be32_to_cpup(mem + size));
*allnextp = NULL;
pr_debug(" <- unflatten_device_tree()\n");
}
......@@ -425,6 +437,8 @@ void *initial_boot_params;
#ifdef CONFIG_OF_EARLY_FLATTREE
static u32 of_fdt_crc32;
/**
* 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);
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);
base = PAGE_ALIGN(base);
}
......@@ -992,15 +1011,14 @@ bool __init early_init_dt_verify(void *params)
if (!params)
return false;
/* Setup flat device-tree pointer */
initial_boot_params = params;
/* check device tree validity */
if (fdt_check_header(params)) {
initial_boot_params = NULL;
if (fdt_check_header(params))
return false;
}
/* Setup flat device-tree pointer */
initial_boot_params = params;
of_fdt_crc32 = crc32_be(~0, initial_boot_params,
fdt_totalsize(initial_boot_params));
return true;
}
......@@ -1039,7 +1057,7 @@ bool __init early_init_dt_scan(void *params)
*/
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);
/* 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();
}
#if defined(CONFIG_DEBUG_FS) && defined(DEBUG)
static struct debugfs_blob_wrapper flat_dt_blob;
static int __init of_flat_dt_debugfs_export_fdt(void)
#ifdef CONFIG_SYSFS
static ssize_t of_fdt_raw_read(struct file *filp, struct kobject *kobj,
struct bin_attribute *bin_attr,
char *buf, loff_t off, size_t count)
{
struct dentry *d = debugfs_create_dir("device-tree", NULL);
if (!d)
return -ENOENT;
memcpy(buf, initial_boot_params + off, count);
return count;
}
flat_dt_blob.data = initial_boot_params;
flat_dt_blob.size = fdt_totalsize(initial_boot_params);
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);
d = debugfs_create_blob("flat-device-tree", S_IFREG | S_IRUSR,
d, &flat_dt_blob);
if (!d)
return -ENOENT;
if (!initial_boot_params)
return 0;
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);
}
module_init(of_flat_dt_debugfs_export_fdt);
late_initcall(of_fdt_raw_init);
#endif
#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.
*/
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,
const char *name, int *lenp);
......
This diff is collapsed.
......@@ -25,8 +25,7 @@
static struct of_pdt_ops *of_pdt_prom_ops __initdata;
void __initdata (*of_pdt_build_more)(struct device_node *dp,
struct device_node ***nextp);
void __initdata (*of_pdt_build_more)(struct device_node *dp);
#if defined(CONFIG_SPARC)
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,
phandle node,
struct device_node ***nextp)
phandle node)
{
struct device_node *ret = NULL, *prev_sibling = NULL;
struct device_node *dp;
......@@ -210,16 +208,12 @@ static struct device_node * __init of_pdt_build_tree(struct device_node *parent,
ret = dp;
prev_sibling = dp;
*(*nextp) = dp;
*nextp = &dp->allnext;
dp->full_name = of_pdt_build_full_name(dp);
dp->child = of_pdt_build_tree(dp,
of_pdt_prom_ops->getchild(node), nextp);
dp->child = of_pdt_build_tree(dp, of_pdt_prom_ops->getchild(node));
if (of_pdt_build_more)
of_pdt_build_more(dp, nextp);
of_pdt_build_more(dp);
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)
{
struct device_node **nextp;
BUG_ON(!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)
of_allnodes->path_component_name = "";
of_root->path_component_name = "";
#endif
of_allnodes->full_name = "/";
of_root->full_name = "/";
nextp = &of_allnodes->allnext;
of_allnodes->child = of_pdt_build_tree(of_allnodes,
of_pdt_prom_ops->getchild(of_allnodes->phandle), &nextp);
of_root->child = of_pdt_build_tree(of_root,
of_pdt_prom_ops->getchild(of_root->phandle));
/* Get pointer to "/chosen" and "/aliases" nodes for use everywhere */
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.parent = parent;
dev->dev.parent = parent ? : &platform_bus;
if (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 */
dev->dev.of_node = of_node_get(node);
dev->dev.parent = parent;
dev->dev.parent = parent ? : &platform_bus;
dev->dev.platform_data = platform_data;
if (bus_id)
dev_set_name(&dev->dev, "%s", bus_id);
......@@ -500,6 +500,7 @@ int of_platform_populate(struct device_node *root,
if (rc)
break;
}
of_node_set_flag(root, OF_POPULATED_BUS);
of_node_put(root);
return rc;
......@@ -542,8 +543,66 @@ static int of_platform_device_destroy(struct device *dev, void *data)
*/
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);
#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 */
......@@ -111,7 +111,8 @@ static void __of_adjust_tree_phandles(struct device_node *node,
__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;
struct device_node *refnode;
......@@ -181,7 +182,7 @@ static int __of_adjust_phandle_ref(struct device_node *node, struct property *rp
goto err_fail;
}
phandle = is_delta ? be32_to_cpup(sprop->value + offset) + value : value;
phandle = value;
*(__be32 *)(sprop->value + offset) = cpu_to_be32(phandle);
}
......@@ -190,36 +191,97 @@ static int __of_adjust_phandle_ref(struct device_node *node, struct property *rp
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.
* Assumes the existances of a __local_fixups__ node at the root
* of the tree. Does not take any devtree locks so make sure you
* call this on a tree which is at the detached state.
* Assumes the existances of a __local_fixups__ node at the root.
* Assumes that __of_verify_tree_phandle_references has been called.
* Does not take any devtree locks so make sure you call this on a tree
* which is at the detached state.
*/
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 property *rprop;
int err;
/* locate the symbols & fixups nodes on resolve */
for_each_child_of_node(node, child)
if (of_node_cmp(child->name, "__local_fixups__") == 0)
break;
struct device_node *child, *childtarget;
struct property *rprop, *sprop;
int err, i, count;
unsigned int off;
phandle phandle;
/* no local fixups */
if (!child)
if (node == NULL)
return 0;
/* find the local fixups property */
for_each_property_of_node(child, rprop) {
for_each_property_of_node(node, rprop) {
/* 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;
err = __of_adjust_phandle_ref(node, rprop, phandle_delta, true);
if (err)
if ((rprop->length % 4) != 0 || rprop->length == 0) {
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;
}
......@@ -241,7 +303,7 @@ static int __of_adjust_tree_phandle_references(struct device_node *node,
*/
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 property *rprop;
const char *refpath;
......@@ -255,9 +317,23 @@ int of_resolve_phandles(struct device_node *resolve)
/* first we need to adjust the phandles */
phandle_delta = of_get_tree_max_phandle() + 1;
__of_adjust_tree_phandles(resolve, phandle_delta);
err = __of_adjust_tree_phandle_references(resolve, phandle_delta);
if (err != 0)
return err;
/* locate the local fixups */
childroot = NULL;
for_each_child_of_node(resolve, childroot)
if (of_node_cmp(childroot->name, "__local_fixups__") == 0)
break;
if (childroot != NULL) {
/* resolve root is guaranteed to be the '/' */
err = __of_adjust_tree_phandle_references(childroot,
resolve, 0);
if (err != 0)
return err;
BUG_ON(__of_adjust_tree_phandle_references(childroot,
resolve, phandle_delta));
}
root_sym = NULL;
resolve_sym = NULL;
......@@ -322,7 +398,7 @@ int of_resolve_phandles(struct device_node *resolve)
pr_debug("%s: %s phandle is 0x%08x\n",
__func__, rprop->name, phandle);
err = __of_adjust_phandle_ref(resolve, rprop, phandle, false);
err = __of_adjust_phandle_ref(resolve, rprop, phandle);
if (err)
break;
}
......
/dts-v1/;
/ {
testcase-data {
changeset {
prop-update = "hello";
prop-remove = "world";
node-remove {
};
};
};
};
#include "tests-phandle.dtsi"
#include "tests-interrupts.dtsi"
#include "tests-match.dtsi"
#include "tests-platform.dtsi"
/*
* phandle fixup data - generated by dtc patches that aren't upstream.
* This data must be regenerated whenever phandle references are modified in
* the testdata tree.
*
* The format of this data may be subject to change. For the time being consider
* this a kernel-internal data format.
*/
/ { __local_fixups__ {
fixup = "/testcase-data/testcase-device2:interrupt-parent:0",
"/testcase-data/testcase-device1:interrupt-parent:0",
"/testcase-data/interrupts/interrupts-extended0:interrupts-extended:60",
"/testcase-data/interrupts/interrupts-extended0:interrupts-extended:52",
"/testcase-data/interrupts/interrupts-extended0:interrupts-extended:44",
"/testcase-data/interrupts/interrupts-extended0:interrupts-extended:36",
"/testcase-data/interrupts/interrupts-extended0:interrupts-extended:24",
"/testcase-data/interrupts/interrupts-extended0:interrupts-extended:8",
"/testcase-data/interrupts/interrupts-extended0:interrupts-extended:0",
"/testcase-data/interrupts/interrupts1:interrupt-parent:0",
"/testcase-data/interrupts/interrupts0:interrupt-parent:0",
"/testcase-data/interrupts/intmap1:interrupt-map:12",
"/testcase-data/interrupts/intmap0:interrupt-map:52",
"/testcase-data/interrupts/intmap0:interrupt-map:36",
"/testcase-data/interrupts/intmap0:interrupt-map:16",
"/testcase-data/interrupts/intmap0:interrupt-map:4",
"/testcase-data/phandle-tests/consumer-a:phandle-list-bad-args:12",
"/testcase-data/phandle-tests/consumer-a:phandle-list-bad-args:0",
"/testcase-data/phandle-tests/consumer-a:phandle-list:56",
"/testcase-data/phandle-tests/consumer-a:phandle-list:52",
"/testcase-data/phandle-tests/consumer-a:phandle-list:40",
"/testcase-data/phandle-tests/consumer-a:phandle-list:24",
"/testcase-data/phandle-tests/consumer-a:phandle-list:8",
"/testcase-data/phandle-tests/consumer-a:phandle-list:0";
}; };
/dts-v1/;
/ {
testcase-data {
changeset {
prop-update = "hello";
prop-remove = "world";
node-remove {
};
};
};
};
#include "tests-phandle.dtsi"
#include "tests-interrupts.dtsi"
#include "tests-match.dtsi"
#include "tests-platform.dtsi"
#include "tests-overlay.dtsi"
/*
* phandle fixup data - generated by dtc patches that aren't upstream.
* This data must be regenerated whenever phandle references are modified in
* the testdata tree.
*
* The format of this data may be subject to change. For the time being consider
* this a kernel-internal data format.
*/
/ { __local_fixups__ {
testcase-data {
phandle-tests {
consumer-a {
phandle-list = <0x00000000 0x00000008
0x00000018 0x00000028
0x00000034 0x00000038>;
phandle-list-bad-args = <0x00000000 0x0000000c>;
};
};
interrupts {
intmap0 {
interrupt-map = <0x00000004 0x00000010
0x00000024 0x00000034>;
};
intmap1 {
interrupt-map = <0x0000000c>;
};
interrupts0 {
interrupt-parent = <0x00000000>;
};
interrupts1 {
interrupt-parent = <0x00000000>;
};
interrupts-extended0 {
interrupts-extended = <0x00000000 0x00000008
0x00000018 0x00000024
0x0000002c 0x00000034
0x0000003c>;
};
};
testcase-device1 {
interrupt-parent = <0x00000000>;
};
testcase-device2 {
interrupt-parent = <0x00000000>;
};
overlay2 {
fragment@0 {
target = <0x00000000>;
};
};
overlay3 {
fragment@0 {
target = <0x00000000>;
};
};
overlay4 {
fragment@0 {
target = <0x00000000>;
};
};
};
}; };
/ {
testcase-data {
overlay-node {
/* test bus */
selftestbus: test-bus {
compatible = "simple-bus";
#address-cells = <1>;
#size-cells = <0>;
selftest100: test-selftest100 {
compatible = "selftest";
status = "okay";
reg = <100>;
};
selftest101: test-selftest101 {
compatible = "selftest";
status = "disabled";
reg = <101>;
};
selftest0: test-selftest0 {
compatible = "selftest";
status = "disabled";
reg = <0>;
};
selftest1: test-selftest1 {
compatible = "selftest";
status = "okay";
reg = <1>;
};
selftest2: test-selftest2 {
compatible = "selftest";
status = "disabled";
reg = <2>;
};
selftest3: test-selftest3 {
compatible = "selftest";
status = "okay";
reg = <3>;
};
selftest5: test-selftest5 {
compatible = "selftest";
status = "disabled";
reg = <5>;
};
selftest6: test-selftest6 {
compatible = "selftest";
status = "disabled";
reg = <6>;
};
selftest7: test-selftest7 {
compatible = "selftest";
status = "disabled";
reg = <7>;
};
selftest8: test-selftest8 {
compatible = "selftest";
status = "disabled";
reg = <8>;
};
};
};
/* test enable using absolute target path */
overlay0 {
fragment@0 {
target-path = "/testcase-data/overlay-node/test-bus/test-selftest0";
__overlay__ {
status = "okay";
};
};
};
/* test disable using absolute target path */
overlay1 {
fragment@0 {
target-path = "/testcase-data/overlay-node/test-bus/test-selftest1";
__overlay__ {
status = "disabled";
};
};
};
/* test enable using label */
overlay2 {
fragment@0 {
target = <&selftest2>;
__overlay__ {
status = "okay";
};
};
};
/* test disable using label */
overlay3 {
fragment@0 {
target = <&selftest3>;
__overlay__ {
status = "disabled";
};
};
};
/* test insertion of a full node */
overlay4 {
fragment@0 {
target = <&selftestbus>;
__overlay__ {
/* suppress DTC warning */
#address-cells = <1>;
#size-cells = <0>;
test-selftest4 {
compatible = "selftest";
status = "okay";
reg = <4>;
};
};
};
};
/* test overlay apply revert */
overlay5 {
fragment@0 {
target-path = "/testcase-data/overlay-node/test-bus/test-selftest5";
__overlay__ {
status = "okay";
};
};
};
/* test overlays application and removal in sequence */
overlay6 {
fragment@0 {
target-path = "/testcase-data/overlay-node/test-bus/test-selftest6";
__overlay__ {
status = "okay";
};
};
};
overlay7 {
fragment@0 {
target-path = "/testcase-data/overlay-node/test-bus/test-selftest7";
__overlay__ {
status = "okay";
};
};
};
/* test overlays application and removal in bad sequence */
overlay8 {
fragment@0 {
target-path = "/testcase-data/overlay-node/test-bus/test-selftest8";
__overlay__ {
status = "okay";
};
};
};
overlay9 {
fragment@0 {
target-path = "/testcase-data/overlay-node/test-bus/test-selftest8";
__overlay__ {
property-foo = "bar";
};
};
};
};
};
......@@ -1220,6 +1220,121 @@ static int spi_master_initialize_queue(struct spi_master *master)
/*-------------------------------------------------------------------------*/
#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
* @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 device_node *nc;
int rc;
u32 value;
if (!master->dev.of_node)
return;
for_each_available_child_of_node(master->dev.of_node, nc) {
/* Alloc an spi_device */
spi = spi_alloc_device(master);
if (!spi) {
dev_err(&master->dev, "spi_device alloc error for %s\n",
spi = of_register_spi_device(master, nc);
if (IS_ERR(spi))
dev_warn(&master->dev, "Failed to create SPI device for %s\n",
nc->full_name);
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
......@@ -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)
{
int status;
......@@ -2320,6 +2414,10 @@ static int __init spi_init(void)
status = class_register(&spi_master_class);
if (status < 0)
goto err2;
if (IS_ENABLED(CONFIG_OF_DYNAMIC))
WARN_ON(of_reconfig_notifier_register(&spi_of_notifier));
return 0;
err2:
......
......@@ -24,6 +24,7 @@
#include <linux/topology.h>
#include <linux/notifier.h>
#include <linux/property.h>
#include <linux/list.h>
#include <asm/byteorder.h>
#include <asm/errno.h>
......@@ -57,8 +58,6 @@ struct device_node {
struct device_node *parent;
struct device_node *child;
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;
unsigned long _flags;
void *data;
......@@ -76,6 +75,12 @@ struct of_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 */
extern struct kobj_type of_node_ktype;
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 */
/* 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_aliases;
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)
{
return of_allnodes != NULL;
return of_root != NULL;
}
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);
}
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>";
}
#define for_each_of_allnodes(dn) \
for (dn = of_allnodes; dn; dn = dn->allnext)
#define for_each_of_allnodes_from(from, dn) \
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,
const char *name);
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 **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_get_parent(const 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);
extern int of_device_is_compatible(const struct device_node *device,
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,
const char *name,
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_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_detach_node(struct device_node *);
......@@ -411,6 +414,12 @@ static inline struct device_node *of_find_node_by_path(const char *path)
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)
{
return NULL;
......@@ -452,9 +461,9 @@ static inline int of_device_is_compatible(const struct device_node *device,
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,
......@@ -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);
}
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) \
for (prop = of_find_property(np, propname, NULL), \
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, \
.data = (fn == (fn_type)NULL) ? fn : fn }
#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 \
__attribute__((unused)) \
= { .compatible = compat, \
......@@ -912,7 +928,19 @@ struct of_changeset {
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
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_destroy(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);
}
#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 */
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");
}
/**
* 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 */
......@@ -106,7 +106,7 @@ extern int of_address_to_resource(struct device_node *dev, int index,
struct resource *r);
void __iomem *of_iomap(struct device_node *node, int index);
void __iomem *of_io_request_and_map(struct device_node *device,
int index, char *name);
int index, const char *name);
#else
#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,
int index, char *name)
int index, const char *name)
{
return IOMEM_ERR_PTR(-EINVAL);
}
......
......@@ -39,7 +39,6 @@ extern void *prom_early_alloc(unsigned long size);
/* for building the device tree */
extern void of_pdt_build_devicetree(phandle root_node, struct of_pdt_ops *ops);
extern void (*of_pdt_build_more)(struct device_node *dp,
struct device_node ***nextp);
extern void (*of_pdt_build_more)(struct device_node *dp);
#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) { }
#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 */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment