The branch contains the following device tree changes the v3.17 merge
window: Group changes to the device tree. In preparation for adding device tree overlay support, OF_DYNAMIC is reworked so that a set of device tree changes can be prepared and applied to the tree all at once. OF_RECONFIG notifiers see the most significant change here so that users always get a consistent view of the tree. Notifiers generation is moved from before a change to after it, and notifiers for a group of changes are emitted after the entire block of changes have been applied Automatic console selection from DT. Console drivers can now use of_console_check() to see if the device node is specified as a console device. If so then it gets added as a preferred console. UART devices get this support automatically when uart_add_one_port() is called. DT unit tests no longer depend on pre-loaded data in the device tree. Data is loaded dynamically at the start of unit tests, and then unloaded again when the tests have completed. Also contains a few bugfixes for reserved regions and early memory setup. -----BEGIN PGP SIGNATURE----- Version: GnuPG v1 iQIcBAABAgAGBQJT6M7XAAoJEMWQL496c2LNTTgP/2rXyrTTGZpK/qrLKHWKYHvr XL7tcTkhA0OLU64E37fB+xtDXyBYnLsharuwUFSd1LlL1Wnc1cZN5ORRlMJbmjUR Wvwl0A8/mkhGl4tzzKgJ4z4rMJXvlZfJRpnVoRB5FOn90LI7k/jsf5rIwF/6S90B 6D6II0r4RG9ku1m7g70cToxcIFCzp0V+eu2tym9GnhsyGKlunPT9iNiTpwfVhPAj QUvMPKIQXReOv6xDU5q6E07839IMf7SdAvciBTHGnCDD7sGziHvnBIShj/2vTDAF 27sGRKrWUnnuxRUMOoIudiYyeHXIdt1WXp6FsS/ztVI37Ijh9YPShJwWGhQDppnp 4tcoSdefqw9IRUajAVWsB0RUW/tCL4a7tggWofylOA6itDi+HZnw6YafE1G1YzxF q8OFo9uqLcmFQfHDJpk+sdtXoMZzOgrxlEscsGsQ8kd2Uoe8+chgR9EY1sqPkWVF Zw0FJCTB6spBlsnXeboBGrnvpbPkacwhvesIFO0IANy4j4R55xlEeTcs1fe3ubUf UhNyyFdnCAUA7e5CAabcAQYsdbEKG6v0Il3H6xts36c8lXCSFXVgNcw5mdCpFCcQ DZ3/1FGSVzkYNXX8hlzIn1W0dqvwn4x0ZbnpXUJrGUBpSmjt9qkXx0XbdeFwartU 7X4tNGS1jfNYhOdP87jF =8IFz -----END PGP SIGNATURE----- Merge tag 'devicetree-for-linus' of git://git.secretlab.ca/git/linux Pull device tree updates from Grant Likely: "The branch contains the following device tree changes the v3.17 merge window: Group changes to the device tree. In preparation for adding device tree overlay support, OF_DYNAMIC is reworked so that a set of device tree changes can be prepared and applied to the tree all at once. OF_RECONFIG notifiers see the most significant change here so that users always get a consistent view of the tree. Notifiers generation is moved from before a change to after it, and notifiers for a group of changes are emitted after the entire block of changes have been applied Automatic console selection from DT. Console drivers can now use of_console_check() to see if the device node is specified as a console device. If so then it gets added as a preferred console. UART devices get this support automatically when uart_add_one_port() is called. DT unit tests no longer depend on pre-loaded data in the device tree. Data is loaded dynamically at the start of unit tests, and then unloaded again when the tests have completed. Also contains a few bugfixes for reserved regions and early memory setup" * tag 'devicetree-for-linus' of git://git.secretlab.ca/git/linux: (21 commits) of: Fixing OF Selftest build error drivers: of: add automated assignment of reserved regions to client devices of: Use proper types for checking memory overflow of: typo fix in __of_prop_dup() Adding selftest testdata dynamically into live tree of: Add todo tasklist for Devicetree of: Transactional DT support. of: Reorder device tree changes and notifiers of: Move dynamic node fixups out of powerpc and into common code of: Make sure attached nodes don't carry along extra children of: Make devicetree sysfs update functions consistent. of: Create unlocked versions of node and property add/remove functions OF: Utility helper functions for dynamic nodes of: Move CONFIG_OF_DYNAMIC code into a separate file of: rename of_aliases_mutex to just of_mutex of/platform: Fix of_platform_device_destroy iteration of devices of: Migrate of_find_node_by_name() users to for_each_node_by_name() tty: Update hypervisor tty drivers to use core stdout parsing code. arm/versatile: Add the uart as the stdout device. of: Enable console on serial ports specified by /chosen/stdout-path ...
This commit is contained in:
commit
ae36e95cf8
36 changed files with 1385 additions and 584 deletions
40
Documentation/devicetree/changesets.txt
Normal file
40
Documentation/devicetree/changesets.txt
Normal file
|
@ -0,0 +1,40 @@
|
|||
A DT changeset is a method which allows one to apply changes
|
||||
in the live tree in such a way that either the full set of changes
|
||||
will be applied, or none of them will be. If an error occurs partway
|
||||
through applying the changeset, then the tree will be rolled back to the
|
||||
previous state. A changeset can also be removed after it has been
|
||||
applied.
|
||||
|
||||
When a changeset is applied, all of the changes get applied to the tree
|
||||
at once before emitting OF_RECONFIG notifiers. This is so that the
|
||||
receiver sees a complete and consistent state of the tree when it
|
||||
receives the notifier.
|
||||
|
||||
The sequence of a changeset is as follows.
|
||||
|
||||
1. of_changeset_init() - initializes a changeset
|
||||
|
||||
2. A number of DT tree change calls, of_changeset_attach_node(),
|
||||
of_changeset_detach_node(), of_changeset_add_property(),
|
||||
of_changeset_remove_property, of_changeset_update_property() to prepare
|
||||
a set of changes. No changes to the active tree are made at this point.
|
||||
All the change operations are recorded in the of_changeset 'entries'
|
||||
list.
|
||||
|
||||
3. mutex_lock(of_mutex) - starts a changeset; The global of_mutex
|
||||
ensures there can only be one editor at a time.
|
||||
|
||||
4. of_changeset_apply() - Apply the changes to the tree. Either the
|
||||
entire changeset will get applied, or if there is an error the tree will
|
||||
be restored to the previous state
|
||||
|
||||
5. mutex_unlock(of_mutex) - All operations complete, release the mutex
|
||||
|
||||
If a successfully applied changeset needs to be removed, it can be done
|
||||
with the following sequence.
|
||||
|
||||
1. mutex_lock(of_mutex)
|
||||
|
||||
2. of_changeset_revert()
|
||||
|
||||
3. mutex_unlock(of_mutex)
|
11
Documentation/devicetree/todo.txt
Normal file
11
Documentation/devicetree/todo.txt
Normal file
|
@ -0,0 +1,11 @@
|
|||
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
|
||||
- Document node lifecycle for CONFIG_OF_DYNAMIC
|
||||
- Always set ->full_name at of_attach_node() time
|
||||
- pseries: Get rid of open-coded tree modification from arch/powerpc/platforms/pseries/dlpar.c
|
|
@ -15,6 +15,10 @@
|
|||
i2c0 = &i2c0;
|
||||
};
|
||||
|
||||
chosen {
|
||||
stdout-path = &uart0;
|
||||
};
|
||||
|
||||
memory {
|
||||
reg = <0x0 0x08000000>;
|
||||
};
|
||||
|
|
|
@ -56,5 +56,3 @@
|
|||
};
|
||||
};
|
||||
};
|
||||
|
||||
#include <testcases.dtsi>
|
||||
|
|
|
@ -818,76 +818,6 @@ int cpu_to_chip_id(int cpu)
|
|||
}
|
||||
EXPORT_SYMBOL(cpu_to_chip_id);
|
||||
|
||||
#ifdef CONFIG_PPC_PSERIES
|
||||
/*
|
||||
* Fix up the uninitialized fields in a new device node:
|
||||
* name, type and pci-specific fields
|
||||
*/
|
||||
|
||||
static int of_finish_dynamic_node(struct device_node *node)
|
||||
{
|
||||
struct device_node *parent = of_get_parent(node);
|
||||
int err = 0;
|
||||
const phandle *ibm_phandle;
|
||||
|
||||
node->name = of_get_property(node, "name", NULL);
|
||||
node->type = of_get_property(node, "device_type", NULL);
|
||||
|
||||
if (!node->name)
|
||||
node->name = "<NULL>";
|
||||
if (!node->type)
|
||||
node->type = "<NULL>";
|
||||
|
||||
if (!parent) {
|
||||
err = -ENODEV;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* We don't support that function on PowerMac, at least
|
||||
* not yet
|
||||
*/
|
||||
if (machine_is(powermac))
|
||||
return -ENODEV;
|
||||
|
||||
/* fix up new node's phandle field */
|
||||
if ((ibm_phandle = of_get_property(node, "ibm,phandle", NULL)))
|
||||
node->phandle = *ibm_phandle;
|
||||
|
||||
out:
|
||||
of_node_put(parent);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int prom_reconfig_notifier(struct notifier_block *nb,
|
||||
unsigned long action, void *node)
|
||||
{
|
||||
int err;
|
||||
|
||||
switch (action) {
|
||||
case OF_RECONFIG_ATTACH_NODE:
|
||||
err = of_finish_dynamic_node(node);
|
||||
if (err < 0)
|
||||
printk(KERN_ERR "finish_node returned %d\n", err);
|
||||
break;
|
||||
default:
|
||||
err = 0;
|
||||
break;
|
||||
}
|
||||
return notifier_from_errno(err);
|
||||
}
|
||||
|
||||
static struct notifier_block prom_reconfig_nb = {
|
||||
.notifier_call = prom_reconfig_notifier,
|
||||
.priority = 10, /* This one needs to run first */
|
||||
};
|
||||
|
||||
static int __init prom_reconfig_setup(void)
|
||||
{
|
||||
return of_reconfig_notifier_register(&prom_reconfig_nb);
|
||||
}
|
||||
__initcall(prom_reconfig_setup);
|
||||
#endif
|
||||
|
||||
bool arch_match_cpu_phys_id(int cpu, u64 phys_id)
|
||||
{
|
||||
return (int)phys_id == get_hard_smp_processor_id(cpu);
|
||||
|
|
|
@ -2805,25 +2805,20 @@ set_initial_features(void)
|
|||
/* Enable GMAC for now for PCI probing. It will be disabled
|
||||
* later on after PCI probe
|
||||
*/
|
||||
np = of_find_node_by_name(NULL, "ethernet");
|
||||
while(np) {
|
||||
for_each_node_by_name(np, "ethernet")
|
||||
if (of_device_is_compatible(np, "K2-GMAC"))
|
||||
g5_gmac_enable(np, 0, 1);
|
||||
np = of_find_node_by_name(np, "ethernet");
|
||||
}
|
||||
|
||||
/* Enable FW before PCI probe. Will be disabled later on
|
||||
* Note: We should have a batter way to check that we are
|
||||
* dealing with uninorth internal cell and not a PCI cell
|
||||
* on the external PCI. The code below works though.
|
||||
*/
|
||||
np = of_find_node_by_name(NULL, "firewire");
|
||||
while(np) {
|
||||
for_each_node_by_name(np, "firewire") {
|
||||
if (of_device_is_compatible(np, "pci106b,5811")) {
|
||||
macio_chips[0].flags |= MACIO_FLAG_FW_SUPPORTED;
|
||||
g5_fw_enable(np, 0, 1);
|
||||
}
|
||||
np = of_find_node_by_name(np, "firewire");
|
||||
}
|
||||
}
|
||||
#else /* CONFIG_PPC64 */
|
||||
|
@ -2834,13 +2829,11 @@ set_initial_features(void)
|
|||
/* Enable GMAC for now for PCI probing. It will be disabled
|
||||
* later on after PCI probe
|
||||
*/
|
||||
np = of_find_node_by_name(NULL, "ethernet");
|
||||
while(np) {
|
||||
for_each_node_by_name(np, "ethernet") {
|
||||
if (np->parent
|
||||
&& of_device_is_compatible(np->parent, "uni-north")
|
||||
&& of_device_is_compatible(np, "gmac"))
|
||||
core99_gmac_enable(np, 0, 1);
|
||||
np = of_find_node_by_name(np, "ethernet");
|
||||
}
|
||||
|
||||
/* Enable FW before PCI probe. Will be disabled later on
|
||||
|
@ -2848,8 +2841,7 @@ set_initial_features(void)
|
|||
* dealing with uninorth internal cell and not a PCI cell
|
||||
* on the external PCI. The code below works though.
|
||||
*/
|
||||
np = of_find_node_by_name(NULL, "firewire");
|
||||
while(np) {
|
||||
for_each_node_by_name(np, "firewire") {
|
||||
if (np->parent
|
||||
&& of_device_is_compatible(np->parent, "uni-north")
|
||||
&& (of_device_is_compatible(np, "pci106b,18") ||
|
||||
|
@ -2858,18 +2850,16 @@ set_initial_features(void)
|
|||
macio_chips[0].flags |= MACIO_FLAG_FW_SUPPORTED;
|
||||
core99_firewire_enable(np, 0, 1);
|
||||
}
|
||||
np = of_find_node_by_name(np, "firewire");
|
||||
}
|
||||
|
||||
/* Enable ATA-100 before PCI probe. */
|
||||
np = of_find_node_by_name(NULL, "ata-6");
|
||||
while(np) {
|
||||
for_each_node_by_name(np, "ata-6") {
|
||||
if (np->parent
|
||||
&& of_device_is_compatible(np->parent, "uni-north")
|
||||
&& of_device_is_compatible(np, "kauai-ata")) {
|
||||
core99_ata100_enable(np, 1);
|
||||
}
|
||||
np = of_find_node_by_name(np, "ata-6");
|
||||
}
|
||||
|
||||
/* Switch airport off */
|
||||
|
|
|
@ -698,7 +698,7 @@ static void __init fixup_nec_usb2(void)
|
|||
{
|
||||
struct device_node *nec;
|
||||
|
||||
for (nec = NULL; (nec = of_find_node_by_name(nec, "usb")) != NULL;) {
|
||||
for_each_node_by_name(nec, "usb") {
|
||||
struct pci_controller *hose;
|
||||
u32 data;
|
||||
const u32 *prop;
|
||||
|
|
|
@ -577,7 +577,7 @@ static void __init smp_core99_setup_i2c_hwsync(int ncpus)
|
|||
int ok;
|
||||
|
||||
/* Look for the clock chip */
|
||||
while ((cc = of_find_node_by_name(cc, "i2c-hwclock")) != NULL) {
|
||||
for_each_node_by_name(cc, "i2c-hwclock") {
|
||||
p = of_get_parent(cc);
|
||||
ok = p && of_device_is_compatible(p, "uni-n-i2c");
|
||||
of_node_put(p);
|
||||
|
|
|
@ -191,7 +191,7 @@ int __init udbg_adb_init(int force_btext)
|
|||
* of type "adb". If not, we return a failure, but we keep the
|
||||
* bext output set for now
|
||||
*/
|
||||
for (np = NULL; (np = of_find_node_by_name(np, "keyboard")) != NULL;) {
|
||||
for_each_node_by_name(np, "keyboard") {
|
||||
struct device_node *parent = of_get_parent(np);
|
||||
int found = (parent && strcmp(parent->type, "adb") == 0);
|
||||
of_node_put(parent);
|
||||
|
|
|
@ -194,7 +194,7 @@ static int pseries_update_drconf_memory(struct of_prop_reconfig *pr)
|
|||
if (!memblock_size)
|
||||
return -EINVAL;
|
||||
|
||||
p = (u32 *)of_get_property(pr->dn, "ibm,dynamic-memory", NULL);
|
||||
p = (u32 *) pr->old_prop->value;
|
||||
if (!p)
|
||||
return -EINVAL;
|
||||
|
||||
|
|
|
@ -232,8 +232,7 @@ static void __init pseries_discover_pic(void)
|
|||
struct device_node *np;
|
||||
const char *typep;
|
||||
|
||||
for (np = NULL; (np = of_find_node_by_name(np,
|
||||
"interrupt-controller"));) {
|
||||
for_each_node_by_name(np, "interrupt-controller") {
|
||||
typep = of_get_property(np, "compatible", NULL);
|
||||
if (strstr(typep, "open-pic")) {
|
||||
pSeries_mpic_node = of_node_get(np);
|
||||
|
|
|
@ -499,8 +499,7 @@ static int __init g5_pm72_cpufreq_init(struct device_node *cpunode)
|
|||
}
|
||||
|
||||
/* Lookup the i2c hwclock */
|
||||
for (hwclock = NULL;
|
||||
(hwclock = of_find_node_by_name(hwclock, "i2c-hwclock")) != NULL;){
|
||||
for_each_node_by_name(hwclock, "i2c-hwclock") {
|
||||
const char *loc = of_get_property(hwclock,
|
||||
"hwctrl-location", NULL);
|
||||
if (loc == NULL)
|
||||
|
|
|
@ -936,28 +936,14 @@ static int nx842_OF_upd(struct property *new_prop)
|
|||
goto error_out;
|
||||
}
|
||||
|
||||
/* Set ptr to new property if provided */
|
||||
if (new_prop) {
|
||||
/* Single property */
|
||||
if (!strncmp(new_prop->name, "status", new_prop->length)) {
|
||||
status = new_prop;
|
||||
|
||||
} else if (!strncmp(new_prop->name, "ibm,max-sg-len",
|
||||
new_prop->length)) {
|
||||
maxsglen = new_prop;
|
||||
|
||||
} else if (!strncmp(new_prop->name, "ibm,max-sync-cop",
|
||||
new_prop->length)) {
|
||||
maxsyncop = new_prop;
|
||||
|
||||
} else {
|
||||
/*
|
||||
* Skip the update, the property being updated
|
||||
* has no impact.
|
||||
*/
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
/*
|
||||
* If this is a property update, there are only certain properties that
|
||||
* we care about. Bail if it isn't in the below list
|
||||
*/
|
||||
if (new_prop && (strncmp(new_prop->name, "status", new_prop->length) ||
|
||||
strncmp(new_prop->name, "ibm,max-sg-len", new_prop->length) ||
|
||||
strncmp(new_prop->name, "ibm,max-sync-cop", new_prop->length)))
|
||||
goto out;
|
||||
|
||||
/* Perform property updates */
|
||||
ret = nx842_OF_upd_status(new_devdata, status);
|
||||
|
|
|
@ -134,8 +134,7 @@ static void cell_edac_init_csrows(struct mem_ctl_info *mci)
|
|||
int j;
|
||||
u32 nr_pages;
|
||||
|
||||
for (np = NULL;
|
||||
(np = of_find_node_by_name(np, "memory")) != NULL;) {
|
||||
for_each_node_by_name(np, "memory") {
|
||||
struct resource r;
|
||||
|
||||
/* We "know" that the Cell firmware only creates one entry
|
||||
|
|
|
@ -9,7 +9,8 @@ menu "Device Tree and Open Firmware support"
|
|||
|
||||
config OF_SELFTEST
|
||||
bool "Device Tree Runtime self tests"
|
||||
depends on OF_IRQ
|
||||
depends on OF_IRQ && OF_EARLY_FLATTREE
|
||||
select OF_DYNAMIC
|
||||
help
|
||||
This option builds in test cases for the device tree infrastructure
|
||||
that are executed once at boot time, and the results dumped to the
|
||||
|
|
|
@ -1,11 +1,13 @@
|
|||
obj-y = base.o device.o platform.o
|
||||
obj-$(CONFIG_OF_DYNAMIC) += dynamic.o
|
||||
obj-$(CONFIG_OF_FLATTREE) += fdt.o
|
||||
obj-$(CONFIG_OF_EARLY_FLATTREE) += fdt_address.o
|
||||
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) += selftest.o
|
||||
obj-$(CONFIG_OF_SELFTEST) += of_selftest.o
|
||||
of_selftest-objs := selftest.o testcase-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
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
* as published by the Free Software Foundation; either version
|
||||
* 2 of the License, or (at your option) any later version.
|
||||
*/
|
||||
#include <linux/console.h>
|
||||
#include <linux/ctype.h>
|
||||
#include <linux/cpu.h>
|
||||
#include <linux/module.h>
|
||||
|
@ -35,15 +36,17 @@ struct device_node *of_allnodes;
|
|||
EXPORT_SYMBOL(of_allnodes);
|
||||
struct device_node *of_chosen;
|
||||
struct device_node *of_aliases;
|
||||
static struct device_node *of_stdout;
|
||||
struct device_node *of_stdout;
|
||||
|
||||
static struct kset *of_kset;
|
||||
struct kset *of_kset;
|
||||
|
||||
/*
|
||||
* Used to protect the of_aliases; but also overloaded to hold off addition of
|
||||
* nodes to sysfs
|
||||
* Used to protect the of_aliases, to hold off addition of nodes to sysfs.
|
||||
* This mutex must be held whenever modifications are being made to the
|
||||
* device tree. The of_{attach,detach}_node() and
|
||||
* of_{add,remove,update}_property() helpers make sure this happens.
|
||||
*/
|
||||
DEFINE_MUTEX(of_aliases_mutex);
|
||||
DEFINE_MUTEX(of_mutex);
|
||||
|
||||
/* use when traversing tree through the allnext, child, sibling,
|
||||
* or parent members of struct device_node.
|
||||
|
@ -89,79 +92,7 @@ int __weak of_node_to_nid(struct device_node *np)
|
|||
}
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_OF_DYNAMIC)
|
||||
/**
|
||||
* of_node_get - Increment refcount of a node
|
||||
* @node: Node to inc refcount, NULL is supported to
|
||||
* simplify writing of callers
|
||||
*
|
||||
* Returns node.
|
||||
*/
|
||||
struct device_node *of_node_get(struct device_node *node)
|
||||
{
|
||||
if (node)
|
||||
kobject_get(&node->kobj);
|
||||
return node;
|
||||
}
|
||||
EXPORT_SYMBOL(of_node_get);
|
||||
|
||||
static inline struct device_node *kobj_to_device_node(struct kobject *kobj)
|
||||
{
|
||||
return container_of(kobj, struct device_node, kobj);
|
||||
}
|
||||
|
||||
/**
|
||||
* of_node_release - release a dynamically allocated node
|
||||
* @kref: kref element of the node to be released
|
||||
*
|
||||
* In of_node_put() this function is passed to kref_put()
|
||||
* as the destructor.
|
||||
*/
|
||||
static void of_node_release(struct kobject *kobj)
|
||||
{
|
||||
struct device_node *node = kobj_to_device_node(kobj);
|
||||
struct property *prop = node->properties;
|
||||
|
||||
/* We should never be releasing nodes that haven't been detached. */
|
||||
if (!of_node_check_flag(node, OF_DETACHED)) {
|
||||
pr_err("ERROR: Bad of_node_put() on %s\n", node->full_name);
|
||||
dump_stack();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!of_node_check_flag(node, OF_DYNAMIC))
|
||||
return;
|
||||
|
||||
while (prop) {
|
||||
struct property *next = prop->next;
|
||||
kfree(prop->name);
|
||||
kfree(prop->value);
|
||||
kfree(prop);
|
||||
prop = next;
|
||||
|
||||
if (!prop) {
|
||||
prop = node->deadprops;
|
||||
node->deadprops = NULL;
|
||||
}
|
||||
}
|
||||
kfree(node->full_name);
|
||||
kfree(node->data);
|
||||
kfree(node);
|
||||
}
|
||||
|
||||
/**
|
||||
* of_node_put - Decrement refcount of a node
|
||||
* @node: Node to dec refcount, NULL is supported to
|
||||
* simplify writing of callers
|
||||
*
|
||||
*/
|
||||
void of_node_put(struct device_node *node)
|
||||
{
|
||||
if (node)
|
||||
kobject_put(&node->kobj);
|
||||
}
|
||||
EXPORT_SYMBOL(of_node_put);
|
||||
#else
|
||||
#ifndef CONFIG_OF_DYNAMIC
|
||||
static void of_node_release(struct kobject *kobj)
|
||||
{
|
||||
/* Without CONFIG_OF_DYNAMIC, no nodes gets freed */
|
||||
|
@ -200,13 +131,16 @@ static const char *safe_name(struct kobject *kobj, const char *orig_name)
|
|||
return name;
|
||||
}
|
||||
|
||||
static int __of_add_property_sysfs(struct device_node *np, struct property *pp)
|
||||
int __of_add_property_sysfs(struct device_node *np, struct property *pp)
|
||||
{
|
||||
int rc;
|
||||
|
||||
/* Important: Don't leak passwords */
|
||||
bool secure = strncmp(pp->name, "security-", 9) == 0;
|
||||
|
||||
if (!of_kset || !of_node_is_attached(np))
|
||||
return 0;
|
||||
|
||||
sysfs_bin_attr_init(&pp->attr);
|
||||
pp->attr.attr.name = safe_name(&np->kobj, pp->name);
|
||||
pp->attr.attr.mode = secure ? S_IRUSR : S_IRUGO;
|
||||
|
@ -218,12 +152,15 @@ static int __of_add_property_sysfs(struct device_node *np, struct property *pp)
|
|||
return rc;
|
||||
}
|
||||
|
||||
static int __of_node_add(struct device_node *np)
|
||||
int __of_attach_node_sysfs(struct device_node *np)
|
||||
{
|
||||
const char *name;
|
||||
struct property *pp;
|
||||
int rc;
|
||||
|
||||
if (!of_kset)
|
||||
return 0;
|
||||
|
||||
np->kobj.kset = of_kset;
|
||||
if (!np->parent) {
|
||||
/* Nodes without parents are new top level trees */
|
||||
|
@ -245,59 +182,20 @@ static int __of_node_add(struct device_node *np)
|
|||
return 0;
|
||||
}
|
||||
|
||||
int of_node_add(struct device_node *np)
|
||||
{
|
||||
int rc = 0;
|
||||
|
||||
BUG_ON(!of_node_is_initialized(np));
|
||||
|
||||
/*
|
||||
* Grab the mutex here so that in a race condition between of_init() and
|
||||
* of_node_add(), node addition will still be consistent.
|
||||
*/
|
||||
mutex_lock(&of_aliases_mutex);
|
||||
if (of_kset)
|
||||
rc = __of_node_add(np);
|
||||
else
|
||||
/* This scenario may be perfectly valid, but report it anyway */
|
||||
pr_info("of_node_add(%s) before of_init()\n", np->full_name);
|
||||
mutex_unlock(&of_aliases_mutex);
|
||||
return rc;
|
||||
}
|
||||
|
||||
#if defined(CONFIG_OF_DYNAMIC)
|
||||
static void of_node_remove(struct device_node *np)
|
||||
{
|
||||
struct property *pp;
|
||||
|
||||
BUG_ON(!of_node_is_initialized(np));
|
||||
|
||||
/* only remove properties if on sysfs */
|
||||
if (of_node_is_attached(np)) {
|
||||
for_each_property_of_node(np, pp)
|
||||
sysfs_remove_bin_file(&np->kobj, &pp->attr);
|
||||
kobject_del(&np->kobj);
|
||||
}
|
||||
|
||||
/* finally remove the kobj_init ref */
|
||||
of_node_put(np);
|
||||
}
|
||||
#endif
|
||||
|
||||
static int __init of_init(void)
|
||||
{
|
||||
struct device_node *np;
|
||||
|
||||
/* Create the kset, and register existing nodes */
|
||||
mutex_lock(&of_aliases_mutex);
|
||||
mutex_lock(&of_mutex);
|
||||
of_kset = kset_create_and_add("devicetree", NULL, firmware_kobj);
|
||||
if (!of_kset) {
|
||||
mutex_unlock(&of_aliases_mutex);
|
||||
mutex_unlock(&of_mutex);
|
||||
return -ENOMEM;
|
||||
}
|
||||
for_each_of_allnodes(np)
|
||||
__of_node_add(np);
|
||||
mutex_unlock(&of_aliases_mutex);
|
||||
__of_attach_node_sysfs(np);
|
||||
mutex_unlock(&of_mutex);
|
||||
|
||||
/* Symlink in /proc as required by userspace ABI */
|
||||
if (of_allnodes)
|
||||
|
@ -369,8 +267,8 @@ EXPORT_SYMBOL(of_find_all_nodes);
|
|||
* Find a property with a given name for a given node
|
||||
* and return the value.
|
||||
*/
|
||||
static const void *__of_get_property(const struct device_node *np,
|
||||
const char *name, int *lenp)
|
||||
const void *__of_get_property(const struct device_node *np,
|
||||
const char *name, int *lenp)
|
||||
{
|
||||
struct property *pp = __of_find_property(np, name, lenp);
|
||||
|
||||
|
@ -1748,32 +1646,10 @@ int of_count_phandle_with_args(const struct device_node *np, const char *list_na
|
|||
}
|
||||
EXPORT_SYMBOL(of_count_phandle_with_args);
|
||||
|
||||
#if defined(CONFIG_OF_DYNAMIC)
|
||||
static int of_property_notify(int action, struct device_node *np,
|
||||
struct property *prop)
|
||||
{
|
||||
struct of_prop_reconfig pr;
|
||||
|
||||
/* only call notifiers if the node is attached */
|
||||
if (!of_node_is_attached(np))
|
||||
return 0;
|
||||
|
||||
pr.dn = np;
|
||||
pr.prop = prop;
|
||||
return of_reconfig_notify(action, &pr);
|
||||
}
|
||||
#else
|
||||
static int of_property_notify(int action, struct device_node *np,
|
||||
struct property *prop)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* __of_add_property - Add a property to a node without lock operations
|
||||
*/
|
||||
static int __of_add_property(struct device_node *np, struct property *prop)
|
||||
int __of_add_property(struct device_node *np, struct property *prop)
|
||||
{
|
||||
struct property **next;
|
||||
|
||||
|
@ -1799,22 +1675,49 @@ int of_add_property(struct device_node *np, struct property *prop)
|
|||
unsigned long flags;
|
||||
int rc;
|
||||
|
||||
rc = of_property_notify(OF_RECONFIG_ADD_PROPERTY, np, prop);
|
||||
if (rc)
|
||||
return rc;
|
||||
mutex_lock(&of_mutex);
|
||||
|
||||
raw_spin_lock_irqsave(&devtree_lock, flags);
|
||||
rc = __of_add_property(np, prop);
|
||||
raw_spin_unlock_irqrestore(&devtree_lock, flags);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
if (of_node_is_attached(np))
|
||||
if (!rc)
|
||||
__of_add_property_sysfs(np, prop);
|
||||
|
||||
mutex_unlock(&of_mutex);
|
||||
|
||||
if (!rc)
|
||||
of_property_notify(OF_RECONFIG_ADD_PROPERTY, np, prop, NULL);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
int __of_remove_property(struct device_node *np, struct property *prop)
|
||||
{
|
||||
struct property **next;
|
||||
|
||||
for (next = &np->properties; *next; next = &(*next)->next) {
|
||||
if (*next == prop)
|
||||
break;
|
||||
}
|
||||
if (*next == NULL)
|
||||
return -ENODEV;
|
||||
|
||||
/* found the node */
|
||||
*next = prop->next;
|
||||
prop->next = np->deadprops;
|
||||
np->deadprops = prop;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void __of_remove_property_sysfs(struct device_node *np, struct property *prop)
|
||||
{
|
||||
/* at early boot, bail here and defer setup to of_init() */
|
||||
if (of_kset && of_node_is_attached(np))
|
||||
sysfs_remove_bin_file(&np->kobj, &prop->attr);
|
||||
}
|
||||
|
||||
/**
|
||||
* of_remove_property - Remove a property from a node.
|
||||
*
|
||||
|
@ -1825,42 +1728,64 @@ int of_add_property(struct device_node *np, struct property *prop)
|
|||
*/
|
||||
int of_remove_property(struct device_node *np, struct property *prop)
|
||||
{
|
||||
struct property **next;
|
||||
unsigned long flags;
|
||||
int found = 0;
|
||||
int rc;
|
||||
|
||||
rc = of_property_notify(OF_RECONFIG_REMOVE_PROPERTY, np, prop);
|
||||
if (rc)
|
||||
return rc;
|
||||
mutex_lock(&of_mutex);
|
||||
|
||||
raw_spin_lock_irqsave(&devtree_lock, flags);
|
||||
next = &np->properties;
|
||||
while (*next) {
|
||||
if (*next == prop) {
|
||||
/* found the node */
|
||||
*next = prop->next;
|
||||
prop->next = np->deadprops;
|
||||
np->deadprops = prop;
|
||||
found = 1;
|
||||
break;
|
||||
}
|
||||
next = &(*next)->next;
|
||||
}
|
||||
rc = __of_remove_property(np, prop);
|
||||
raw_spin_unlock_irqrestore(&devtree_lock, flags);
|
||||
|
||||
if (!found)
|
||||
return -ENODEV;
|
||||
if (!rc)
|
||||
__of_remove_property_sysfs(np, prop);
|
||||
|
||||
/* at early boot, bail hear and defer setup to of_init() */
|
||||
if (!of_kset)
|
||||
return 0;
|
||||
mutex_unlock(&of_mutex);
|
||||
|
||||
sysfs_remove_bin_file(&np->kobj, &prop->attr);
|
||||
if (!rc)
|
||||
of_property_notify(OF_RECONFIG_REMOVE_PROPERTY, np, prop, NULL);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
int __of_update_property(struct device_node *np, struct property *newprop,
|
||||
struct property **oldpropp)
|
||||
{
|
||||
struct property **next, *oldprop;
|
||||
|
||||
for (next = &np->properties; *next; next = &(*next)->next) {
|
||||
if (of_prop_cmp((*next)->name, newprop->name) == 0)
|
||||
break;
|
||||
}
|
||||
*oldpropp = oldprop = *next;
|
||||
|
||||
if (oldprop) {
|
||||
/* replace the node */
|
||||
newprop->next = oldprop->next;
|
||||
*next = newprop;
|
||||
oldprop->next = np->deadprops;
|
||||
np->deadprops = oldprop;
|
||||
} else {
|
||||
/* new node */
|
||||
newprop->next = NULL;
|
||||
*next = newprop;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void __of_update_property_sysfs(struct device_node *np, struct property *newprop,
|
||||
struct property *oldprop)
|
||||
{
|
||||
/* At early boot, bail out and defer setup to of_init() */
|
||||
if (!of_kset)
|
||||
return;
|
||||
|
||||
if (oldprop)
|
||||
sysfs_remove_bin_file(&np->kobj, &oldprop->attr);
|
||||
__of_add_property_sysfs(np, newprop);
|
||||
}
|
||||
|
||||
/*
|
||||
* of_update_property - Update a property in a node, if the property does
|
||||
* not exist, add it.
|
||||
|
@ -1872,164 +1797,29 @@ int of_remove_property(struct device_node *np, struct property *prop)
|
|||
*/
|
||||
int of_update_property(struct device_node *np, struct property *newprop)
|
||||
{
|
||||
struct property **next, *oldprop;
|
||||
struct property *oldprop;
|
||||
unsigned long flags;
|
||||
int rc;
|
||||
|
||||
rc = of_property_notify(OF_RECONFIG_UPDATE_PROPERTY, np, newprop);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
if (!newprop->name)
|
||||
return -EINVAL;
|
||||
|
||||
raw_spin_lock_irqsave(&devtree_lock, flags);
|
||||
next = &np->properties;
|
||||
oldprop = __of_find_property(np, newprop->name, NULL);
|
||||
if (!oldprop) {
|
||||
/* add the new node */
|
||||
rc = __of_add_property(np, newprop);
|
||||
} else while (*next) {
|
||||
/* replace the node */
|
||||
if (*next == oldprop) {
|
||||
newprop->next = oldprop->next;
|
||||
*next = newprop;
|
||||
oldprop->next = np->deadprops;
|
||||
np->deadprops = oldprop;
|
||||
break;
|
||||
}
|
||||
next = &(*next)->next;
|
||||
}
|
||||
raw_spin_unlock_irqrestore(&devtree_lock, flags);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
/* At early boot, bail out and defer setup to of_init() */
|
||||
if (!of_kset)
|
||||
return 0;
|
||||
|
||||
/* Update the sysfs attribute */
|
||||
if (oldprop)
|
||||
sysfs_remove_bin_file(&np->kobj, &oldprop->attr);
|
||||
__of_add_property_sysfs(np, newprop);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if defined(CONFIG_OF_DYNAMIC)
|
||||
/*
|
||||
* Support for dynamic device trees.
|
||||
*
|
||||
* On some platforms, the device tree can be manipulated at runtime.
|
||||
* The routines in this section support adding, removing and changing
|
||||
* device tree nodes.
|
||||
*/
|
||||
|
||||
static BLOCKING_NOTIFIER_HEAD(of_reconfig_chain);
|
||||
|
||||
int of_reconfig_notifier_register(struct notifier_block *nb)
|
||||
{
|
||||
return blocking_notifier_chain_register(&of_reconfig_chain, nb);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(of_reconfig_notifier_register);
|
||||
|
||||
int of_reconfig_notifier_unregister(struct notifier_block *nb)
|
||||
{
|
||||
return blocking_notifier_chain_unregister(&of_reconfig_chain, nb);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(of_reconfig_notifier_unregister);
|
||||
|
||||
int of_reconfig_notify(unsigned long action, void *p)
|
||||
{
|
||||
int rc;
|
||||
|
||||
rc = blocking_notifier_call_chain(&of_reconfig_chain, action, p);
|
||||
return notifier_to_errno(rc);
|
||||
}
|
||||
|
||||
/**
|
||||
* of_attach_node - Plug a device node into the tree and global list.
|
||||
*/
|
||||
int of_attach_node(struct device_node *np)
|
||||
{
|
||||
unsigned long flags;
|
||||
int rc;
|
||||
|
||||
rc = of_reconfig_notify(OF_RECONFIG_ATTACH_NODE, np);
|
||||
if (rc)
|
||||
return rc;
|
||||
mutex_lock(&of_mutex);
|
||||
|
||||
raw_spin_lock_irqsave(&devtree_lock, flags);
|
||||
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);
|
||||
rc = __of_update_property(np, newprop, &oldprop);
|
||||
raw_spin_unlock_irqrestore(&devtree_lock, flags);
|
||||
|
||||
of_node_add(np);
|
||||
return 0;
|
||||
}
|
||||
if (!rc)
|
||||
__of_update_property_sysfs(np, newprop, oldprop);
|
||||
|
||||
/**
|
||||
* of_detach_node - "Unplug" a node from the device tree.
|
||||
*
|
||||
* The caller must hold a reference to the node. The memory associated with
|
||||
* the node is not freed until its refcount goes to zero.
|
||||
*/
|
||||
int of_detach_node(struct device_node *np)
|
||||
{
|
||||
struct device_node *parent;
|
||||
unsigned long flags;
|
||||
int rc = 0;
|
||||
mutex_unlock(&of_mutex);
|
||||
|
||||
rc = of_reconfig_notify(OF_RECONFIG_DETACH_NODE, np);
|
||||
if (rc)
|
||||
return rc;
|
||||
if (!rc)
|
||||
of_property_notify(OF_RECONFIG_UPDATE_PROPERTY, np, newprop, oldprop);
|
||||
|
||||
raw_spin_lock_irqsave(&devtree_lock, flags);
|
||||
|
||||
if (of_node_check_flag(np, OF_DETACHED)) {
|
||||
/* someone already detached it */
|
||||
raw_spin_unlock_irqrestore(&devtree_lock, flags);
|
||||
return rc;
|
||||
}
|
||||
|
||||
parent = np->parent;
|
||||
if (!parent) {
|
||||
raw_spin_unlock_irqrestore(&devtree_lock, flags);
|
||||
return rc;
|
||||
}
|
||||
|
||||
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 {
|
||||
struct device_node *prevsib;
|
||||
for (prevsib = np->parent->child;
|
||||
prevsib->sibling != np;
|
||||
prevsib = prevsib->sibling)
|
||||
;
|
||||
prevsib->sibling = np->sibling;
|
||||
}
|
||||
|
||||
of_node_set_flag(np, OF_DETACHED);
|
||||
raw_spin_unlock_irqrestore(&devtree_lock, flags);
|
||||
|
||||
of_node_remove(np);
|
||||
return rc;
|
||||
}
|
||||
#endif /* defined(CONFIG_OF_DYNAMIC) */
|
||||
|
||||
static void of_alias_add(struct alias_prop *ap, struct device_node *np,
|
||||
int id, const char *stem, int stem_len)
|
||||
|
@ -2062,9 +1852,12 @@ void of_alias_scan(void * (*dt_alloc)(u64 size, u64 align))
|
|||
of_chosen = of_find_node_by_path("/chosen@0");
|
||||
|
||||
if (of_chosen) {
|
||||
/* linux,stdout-path and /aliases/stdout are for legacy compatibility */
|
||||
const char *name = of_get_property(of_chosen, "stdout-path", NULL);
|
||||
if (!name)
|
||||
name = of_get_property(of_chosen, "linux,stdout-path", NULL);
|
||||
if (IS_ENABLED(CONFIG_PPC) && !name)
|
||||
name = of_get_property(of_aliases, "stdout", NULL);
|
||||
if (name)
|
||||
of_stdout = of_find_node_by_path(name);
|
||||
}
|
||||
|
@ -2122,7 +1915,7 @@ int of_alias_get_id(struct device_node *np, const char *stem)
|
|||
struct alias_prop *app;
|
||||
int id = -ENODEV;
|
||||
|
||||
mutex_lock(&of_aliases_mutex);
|
||||
mutex_lock(&of_mutex);
|
||||
list_for_each_entry(app, &aliases_lookup, link) {
|
||||
if (strcmp(app->stem, stem) != 0)
|
||||
continue;
|
||||
|
@ -2132,7 +1925,7 @@ int of_alias_get_id(struct device_node *np, const char *stem)
|
|||
break;
|
||||
}
|
||||
}
|
||||
mutex_unlock(&of_aliases_mutex);
|
||||
mutex_unlock(&of_mutex);
|
||||
|
||||
return id;
|
||||
}
|
||||
|
@ -2180,20 +1973,22 @@ const char *of_prop_next_string(struct property *prop, const char *cur)
|
|||
EXPORT_SYMBOL_GPL(of_prop_next_string);
|
||||
|
||||
/**
|
||||
* of_device_is_stdout_path - check if a device node matches the
|
||||
* linux,stdout-path property
|
||||
* of_console_check() - Test and setup console for DT setup
|
||||
* @dn - Pointer to device node
|
||||
* @name - Name to use for preferred console without index. ex. "ttyS"
|
||||
* @index - Index to use for preferred console.
|
||||
*
|
||||
* Check if this device node matches the linux,stdout-path property
|
||||
* in the chosen node. return true if yes, false otherwise.
|
||||
* Check if the given device node matches the stdout-path property in the
|
||||
* /chosen node. If it does then register it as the preferred console and return
|
||||
* TRUE. Otherwise return FALSE.
|
||||
*/
|
||||
int of_device_is_stdout_path(struct device_node *dn)
|
||||
bool of_console_check(struct device_node *dn, char *name, int index)
|
||||
{
|
||||
if (!of_stdout)
|
||||
if (!dn || dn != of_stdout || console_set_on_cmdline)
|
||||
return false;
|
||||
|
||||
return of_stdout == dn;
|
||||
return add_preferred_console(name, index, NULL);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(of_device_is_stdout_path);
|
||||
EXPORT_SYMBOL_GPL(of_console_check);
|
||||
|
||||
/**
|
||||
* of_find_next_cache_node - Find a node's subsidiary cache
|
||||
|
|
|
@ -160,7 +160,7 @@ void of_device_uevent(struct device *dev, struct kobj_uevent_env *env)
|
|||
add_uevent_var(env, "OF_COMPATIBLE_N=%d", seen);
|
||||
|
||||
seen = 0;
|
||||
mutex_lock(&of_aliases_mutex);
|
||||
mutex_lock(&of_mutex);
|
||||
list_for_each_entry(app, &aliases_lookup, link) {
|
||||
if (dev->of_node == app->np) {
|
||||
add_uevent_var(env, "OF_ALIAS_%d=%s", seen,
|
||||
|
@ -168,7 +168,7 @@ void of_device_uevent(struct device *dev, struct kobj_uevent_env *env)
|
|||
seen++;
|
||||
}
|
||||
}
|
||||
mutex_unlock(&of_aliases_mutex);
|
||||
mutex_unlock(&of_mutex);
|
||||
}
|
||||
|
||||
int of_device_uevent_modalias(struct device *dev, struct kobj_uevent_env *env)
|
||||
|
|
660
drivers/of/dynamic.c
Normal file
660
drivers/of/dynamic.c
Normal file
|
@ -0,0 +1,660 @@
|
|||
/*
|
||||
* Support for dynamic device trees.
|
||||
*
|
||||
* On some platforms, the device tree can be manipulated at runtime.
|
||||
* The routines in this section support adding, removing and changing
|
||||
* device tree nodes.
|
||||
*/
|
||||
|
||||
#include <linux/of.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/proc_fs.h>
|
||||
|
||||
#include "of_private.h"
|
||||
|
||||
/**
|
||||
* of_node_get() - Increment refcount of a node
|
||||
* @node: Node to inc refcount, NULL is supported to simplify writing of
|
||||
* callers
|
||||
*
|
||||
* Returns node.
|
||||
*/
|
||||
struct device_node *of_node_get(struct device_node *node)
|
||||
{
|
||||
if (node)
|
||||
kobject_get(&node->kobj);
|
||||
return node;
|
||||
}
|
||||
EXPORT_SYMBOL(of_node_get);
|
||||
|
||||
/**
|
||||
* of_node_put() - Decrement refcount of a node
|
||||
* @node: Node to dec refcount, NULL is supported to simplify writing of
|
||||
* callers
|
||||
*/
|
||||
void of_node_put(struct device_node *node)
|
||||
{
|
||||
if (node)
|
||||
kobject_put(&node->kobj);
|
||||
}
|
||||
EXPORT_SYMBOL(of_node_put);
|
||||
|
||||
void __of_detach_node_sysfs(struct device_node *np)
|
||||
{
|
||||
struct property *pp;
|
||||
|
||||
BUG_ON(!of_node_is_initialized(np));
|
||||
if (!of_kset)
|
||||
return;
|
||||
|
||||
/* only remove properties if on sysfs */
|
||||
if (of_node_is_attached(np)) {
|
||||
for_each_property_of_node(np, pp)
|
||||
sysfs_remove_bin_file(&np->kobj, &pp->attr);
|
||||
kobject_del(&np->kobj);
|
||||
}
|
||||
|
||||
/* finally remove the kobj_init ref */
|
||||
of_node_put(np);
|
||||
}
|
||||
|
||||
static BLOCKING_NOTIFIER_HEAD(of_reconfig_chain);
|
||||
|
||||
int of_reconfig_notifier_register(struct notifier_block *nb)
|
||||
{
|
||||
return blocking_notifier_chain_register(&of_reconfig_chain, nb);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(of_reconfig_notifier_register);
|
||||
|
||||
int of_reconfig_notifier_unregister(struct notifier_block *nb)
|
||||
{
|
||||
return blocking_notifier_chain_unregister(&of_reconfig_chain, nb);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(of_reconfig_notifier_unregister);
|
||||
|
||||
int of_reconfig_notify(unsigned long action, void *p)
|
||||
{
|
||||
int rc;
|
||||
|
||||
rc = blocking_notifier_call_chain(&of_reconfig_chain, action, p);
|
||||
return notifier_to_errno(rc);
|
||||
}
|
||||
|
||||
int of_property_notify(int action, struct device_node *np,
|
||||
struct property *prop, struct property *oldprop)
|
||||
{
|
||||
struct of_prop_reconfig pr;
|
||||
|
||||
/* only call notifiers if the node is attached */
|
||||
if (!of_node_is_attached(np))
|
||||
return 0;
|
||||
|
||||
pr.dn = np;
|
||||
pr.prop = prop;
|
||||
pr.old_prop = oldprop;
|
||||
return of_reconfig_notify(action, &pr);
|
||||
}
|
||||
|
||||
void __of_attach_node(struct device_node *np)
|
||||
{
|
||||
const __be32 *phandle;
|
||||
int sz;
|
||||
|
||||
np->name = __of_get_property(np, "name", NULL) ? : "<NULL>";
|
||||
np->type = __of_get_property(np, "device_type", NULL) ? : "<NULL>";
|
||||
|
||||
phandle = __of_get_property(np, "phandle", &sz);
|
||||
if (!phandle)
|
||||
phandle = __of_get_property(np, "linux,phandle", &sz);
|
||||
if (IS_ENABLED(PPC_PSERIES) && !phandle)
|
||||
phandle = __of_get_property(np, "ibm,phandle", &sz);
|
||||
np->phandle = (phandle && (sz >= 4)) ? be32_to_cpup(phandle) : 0;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
* of_attach_node() - Plug a device node into the tree and global list.
|
||||
*/
|
||||
int of_attach_node(struct device_node *np)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
mutex_lock(&of_mutex);
|
||||
raw_spin_lock_irqsave(&devtree_lock, flags);
|
||||
__of_attach_node(np);
|
||||
raw_spin_unlock_irqrestore(&devtree_lock, flags);
|
||||
|
||||
__of_attach_node_sysfs(np);
|
||||
mutex_unlock(&of_mutex);
|
||||
|
||||
of_reconfig_notify(OF_RECONFIG_ATTACH_NODE, np);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void __of_detach_node(struct device_node *np)
|
||||
{
|
||||
struct device_node *parent;
|
||||
|
||||
if (WARN_ON(of_node_check_flag(np, OF_DETACHED)))
|
||||
return;
|
||||
|
||||
parent = np->parent;
|
||||
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 {
|
||||
struct device_node *prevsib;
|
||||
for (prevsib = np->parent->child;
|
||||
prevsib->sibling != np;
|
||||
prevsib = prevsib->sibling)
|
||||
;
|
||||
prevsib->sibling = np->sibling;
|
||||
}
|
||||
|
||||
of_node_set_flag(np, OF_DETACHED);
|
||||
}
|
||||
|
||||
/**
|
||||
* of_detach_node() - "Unplug" a node from the device tree.
|
||||
*
|
||||
* The caller must hold a reference to the node. The memory associated with
|
||||
* the node is not freed until its refcount goes to zero.
|
||||
*/
|
||||
int of_detach_node(struct device_node *np)
|
||||
{
|
||||
unsigned long flags;
|
||||
int rc = 0;
|
||||
|
||||
mutex_lock(&of_mutex);
|
||||
raw_spin_lock_irqsave(&devtree_lock, flags);
|
||||
__of_detach_node(np);
|
||||
raw_spin_unlock_irqrestore(&devtree_lock, flags);
|
||||
|
||||
__of_detach_node_sysfs(np);
|
||||
mutex_unlock(&of_mutex);
|
||||
|
||||
of_reconfig_notify(OF_RECONFIG_DETACH_NODE, np);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* of_node_release() - release a dynamically allocated node
|
||||
* @kref: kref element of the node to be released
|
||||
*
|
||||
* In of_node_put() this function is passed to kref_put() as the destructor.
|
||||
*/
|
||||
void of_node_release(struct kobject *kobj)
|
||||
{
|
||||
struct device_node *node = kobj_to_device_node(kobj);
|
||||
struct property *prop = node->properties;
|
||||
|
||||
/* We should never be releasing nodes that haven't been detached. */
|
||||
if (!of_node_check_flag(node, OF_DETACHED)) {
|
||||
pr_err("ERROR: Bad of_node_put() on %s\n", node->full_name);
|
||||
dump_stack();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!of_node_check_flag(node, OF_DYNAMIC))
|
||||
return;
|
||||
|
||||
while (prop) {
|
||||
struct property *next = prop->next;
|
||||
kfree(prop->name);
|
||||
kfree(prop->value);
|
||||
kfree(prop);
|
||||
prop = next;
|
||||
|
||||
if (!prop) {
|
||||
prop = node->deadprops;
|
||||
node->deadprops = NULL;
|
||||
}
|
||||
}
|
||||
kfree(node->full_name);
|
||||
kfree(node->data);
|
||||
kfree(node);
|
||||
}
|
||||
|
||||
/**
|
||||
* __of_prop_dup - Copy a property dynamically.
|
||||
* @prop: Property to copy
|
||||
* @allocflags: Allocation flags (typically pass GFP_KERNEL)
|
||||
*
|
||||
* Copy a property by dynamically allocating the memory of both the
|
||||
* property stucture and the property name & contents. The property's
|
||||
* flags have the OF_DYNAMIC bit set so that we can differentiate between
|
||||
* dynamically allocated properties and not.
|
||||
* Returns the newly allocated property or NULL on out of memory error.
|
||||
*/
|
||||
struct property *__of_prop_dup(const struct property *prop, gfp_t allocflags)
|
||||
{
|
||||
struct property *new;
|
||||
|
||||
new = kzalloc(sizeof(*new), allocflags);
|
||||
if (!new)
|
||||
return NULL;
|
||||
|
||||
/*
|
||||
* NOTE: There is no check for zero length value.
|
||||
* In case of a boolean property, this will allocate a value
|
||||
* of zero bytes. We do this to work around the use
|
||||
* of of_get_property() calls on boolean values.
|
||||
*/
|
||||
new->name = kstrdup(prop->name, allocflags);
|
||||
new->value = kmemdup(prop->value, prop->length, allocflags);
|
||||
new->length = prop->length;
|
||||
if (!new->name || !new->value)
|
||||
goto err_free;
|
||||
|
||||
/* mark the property as dynamic */
|
||||
of_property_set_flag(new, OF_DYNAMIC);
|
||||
|
||||
return new;
|
||||
|
||||
err_free:
|
||||
kfree(new->name);
|
||||
kfree(new->value);
|
||||
kfree(new);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* __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)
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
struct device_node *__of_node_alloc(const char *full_name, gfp_t allocflags)
|
||||
{
|
||||
struct device_node *node;
|
||||
|
||||
node = kzalloc(sizeof(*node), allocflags);
|
||||
if (!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);
|
||||
|
||||
return node;
|
||||
|
||||
err_free:
|
||||
kfree(node->full_name);
|
||||
kfree(node);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void __of_changeset_entry_destroy(struct of_changeset_entry *ce)
|
||||
{
|
||||
of_node_put(ce->np);
|
||||
list_del(&ce->node);
|
||||
kfree(ce);
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
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);
|
||||
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);
|
||||
break;
|
||||
}
|
||||
}
|
||||
#else
|
||||
static inline void __of_changeset_entry_dump(struct of_changeset_entry *ce)
|
||||
{
|
||||
/* empty */
|
||||
}
|
||||
#endif
|
||||
|
||||
static void __of_changeset_entry_invert(struct of_changeset_entry *ce,
|
||||
struct of_changeset_entry *rce)
|
||||
{
|
||||
memcpy(rce, ce, sizeof(*rce));
|
||||
|
||||
switch (ce->action) {
|
||||
case OF_RECONFIG_ATTACH_NODE:
|
||||
rce->action = OF_RECONFIG_DETACH_NODE;
|
||||
break;
|
||||
case OF_RECONFIG_DETACH_NODE:
|
||||
rce->action = OF_RECONFIG_ATTACH_NODE;
|
||||
break;
|
||||
case OF_RECONFIG_ADD_PROPERTY:
|
||||
rce->action = OF_RECONFIG_REMOVE_PROPERTY;
|
||||
break;
|
||||
case OF_RECONFIG_REMOVE_PROPERTY:
|
||||
rce->action = OF_RECONFIG_ADD_PROPERTY;
|
||||
break;
|
||||
case OF_RECONFIG_UPDATE_PROPERTY:
|
||||
rce->old_prop = ce->prop;
|
||||
rce->prop = ce->old_prop;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void __of_changeset_entry_notify(struct of_changeset_entry *ce, bool revert)
|
||||
{
|
||||
struct of_changeset_entry ce_inverted;
|
||||
int ret;
|
||||
|
||||
if (revert) {
|
||||
__of_changeset_entry_invert(ce, &ce_inverted);
|
||||
ce = &ce_inverted;
|
||||
}
|
||||
|
||||
switch (ce->action) {
|
||||
case OF_RECONFIG_ATTACH_NODE:
|
||||
case OF_RECONFIG_DETACH_NODE:
|
||||
ret = of_reconfig_notify(ce->action, ce->np);
|
||||
break;
|
||||
case OF_RECONFIG_ADD_PROPERTY:
|
||||
case OF_RECONFIG_REMOVE_PROPERTY:
|
||||
case OF_RECONFIG_UPDATE_PROPERTY:
|
||||
ret = of_property_notify(ce->action, ce->np, ce->prop, ce->old_prop);
|
||||
break;
|
||||
default:
|
||||
pr_err("%s: invalid devicetree changeset action: %i\n", __func__,
|
||||
(int)ce->action);
|
||||
return;
|
||||
}
|
||||
|
||||
if (ret)
|
||||
pr_err("%s: notifier error @%s\n", __func__, ce->np->full_name);
|
||||
}
|
||||
|
||||
static int __of_changeset_entry_apply(struct of_changeset_entry *ce)
|
||||
{
|
||||
struct property *old_prop, **propp;
|
||||
unsigned long flags;
|
||||
int ret = 0;
|
||||
|
||||
__of_changeset_entry_dump(ce);
|
||||
|
||||
raw_spin_lock_irqsave(&devtree_lock, flags);
|
||||
switch (ce->action) {
|
||||
case OF_RECONFIG_ATTACH_NODE:
|
||||
__of_attach_node(ce->np);
|
||||
break;
|
||||
case OF_RECONFIG_DETACH_NODE:
|
||||
__of_detach_node(ce->np);
|
||||
break;
|
||||
case OF_RECONFIG_ADD_PROPERTY:
|
||||
/* If the property is in deadprops then it must be removed */
|
||||
for (propp = &ce->np->deadprops; *propp; propp = &(*propp)->next) {
|
||||
if (*propp == ce->prop) {
|
||||
*propp = ce->prop->next;
|
||||
ce->prop->next = NULL;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
ret = __of_add_property(ce->np, ce->prop);
|
||||
if (ret) {
|
||||
pr_err("%s: add_property failed @%s/%s\n",
|
||||
__func__, ce->np->full_name,
|
||||
ce->prop->name);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case OF_RECONFIG_REMOVE_PROPERTY:
|
||||
ret = __of_remove_property(ce->np, ce->prop);
|
||||
if (ret) {
|
||||
pr_err("%s: remove_property failed @%s/%s\n",
|
||||
__func__, ce->np->full_name,
|
||||
ce->prop->name);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case OF_RECONFIG_UPDATE_PROPERTY:
|
||||
/* If the property is in deadprops then it must be removed */
|
||||
for (propp = &ce->np->deadprops; *propp; propp = &(*propp)->next) {
|
||||
if (*propp == ce->prop) {
|
||||
*propp = ce->prop->next;
|
||||
ce->prop->next = NULL;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
ret = __of_update_property(ce->np, ce->prop, &old_prop);
|
||||
if (ret) {
|
||||
pr_err("%s: update_property failed @%s/%s\n",
|
||||
__func__, ce->np->full_name,
|
||||
ce->prop->name);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
}
|
||||
raw_spin_unlock_irqrestore(&devtree_lock, flags);
|
||||
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
switch (ce->action) {
|
||||
case OF_RECONFIG_ATTACH_NODE:
|
||||
__of_attach_node_sysfs(ce->np);
|
||||
break;
|
||||
case OF_RECONFIG_DETACH_NODE:
|
||||
__of_detach_node_sysfs(ce->np);
|
||||
break;
|
||||
case OF_RECONFIG_ADD_PROPERTY:
|
||||
/* ignore duplicate names */
|
||||
__of_add_property_sysfs(ce->np, ce->prop);
|
||||
break;
|
||||
case OF_RECONFIG_REMOVE_PROPERTY:
|
||||
__of_remove_property_sysfs(ce->np, ce->prop);
|
||||
break;
|
||||
case OF_RECONFIG_UPDATE_PROPERTY:
|
||||
__of_update_property_sysfs(ce->np, ce->prop, ce->old_prop);
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int __of_changeset_entry_revert(struct of_changeset_entry *ce)
|
||||
{
|
||||
struct of_changeset_entry ce_inverted;
|
||||
|
||||
__of_changeset_entry_invert(ce, &ce_inverted);
|
||||
return __of_changeset_entry_apply(&ce_inverted);
|
||||
}
|
||||
|
||||
/**
|
||||
* of_changeset_init - Initialize a changeset for use
|
||||
*
|
||||
* @ocs: changeset pointer
|
||||
*
|
||||
* Initialize a changeset structure
|
||||
*/
|
||||
void of_changeset_init(struct of_changeset *ocs)
|
||||
{
|
||||
memset(ocs, 0, sizeof(*ocs));
|
||||
INIT_LIST_HEAD(&ocs->entries);
|
||||
}
|
||||
|
||||
/**
|
||||
* of_changeset_destroy - Destroy a changeset
|
||||
*
|
||||
* @ocs: changeset pointer
|
||||
*
|
||||
* Destroys a changeset. Note that if a changeset is applied,
|
||||
* its changes to the tree cannot be reverted.
|
||||
*/
|
||||
void of_changeset_destroy(struct of_changeset *ocs)
|
||||
{
|
||||
struct of_changeset_entry *ce, *cen;
|
||||
|
||||
list_for_each_entry_safe_reverse(ce, cen, &ocs->entries, node)
|
||||
__of_changeset_entry_destroy(ce);
|
||||
}
|
||||
|
||||
/**
|
||||
* of_changeset_apply - Applies a changeset
|
||||
*
|
||||
* @ocs: changeset pointer
|
||||
*
|
||||
* Applies a changeset to the live tree.
|
||||
* Any side-effects of live tree state changes are applied here on
|
||||
* sucess, like creation/destruction of devices and side-effects
|
||||
* like creation of sysfs properties and directories.
|
||||
* Returns 0 on success, a negative error value in case of an error.
|
||||
* On error the partially applied effects are reverted.
|
||||
*/
|
||||
int of_changeset_apply(struct of_changeset *ocs)
|
||||
{
|
||||
struct of_changeset_entry *ce;
|
||||
int ret;
|
||||
|
||||
/* perform the rest of the work */
|
||||
pr_debug("of_changeset: applying...\n");
|
||||
list_for_each_entry(ce, &ocs->entries, node) {
|
||||
ret = __of_changeset_entry_apply(ce);
|
||||
if (ret) {
|
||||
pr_err("%s: Error applying changeset (%d)\n", __func__, ret);
|
||||
list_for_each_entry_continue_reverse(ce, &ocs->entries, node)
|
||||
__of_changeset_entry_revert(ce);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
pr_debug("of_changeset: applied, emitting notifiers.\n");
|
||||
|
||||
/* drop the global lock while emitting notifiers */
|
||||
mutex_unlock(&of_mutex);
|
||||
list_for_each_entry(ce, &ocs->entries, node)
|
||||
__of_changeset_entry_notify(ce, 0);
|
||||
mutex_lock(&of_mutex);
|
||||
pr_debug("of_changeset: notifiers sent.\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* of_changeset_revert - Reverts an applied changeset
|
||||
*
|
||||
* @ocs: changeset pointer
|
||||
*
|
||||
* Reverts a changeset returning the state of the tree to what it
|
||||
* was before the application.
|
||||
* Any side-effects like creation/destruction of devices and
|
||||
* removal of sysfs properties and directories are applied.
|
||||
* Returns 0 on success, a negative error value in case of an error.
|
||||
*/
|
||||
int of_changeset_revert(struct of_changeset *ocs)
|
||||
{
|
||||
struct of_changeset_entry *ce;
|
||||
int ret;
|
||||
|
||||
pr_debug("of_changeset: reverting...\n");
|
||||
list_for_each_entry_reverse(ce, &ocs->entries, node) {
|
||||
ret = __of_changeset_entry_revert(ce);
|
||||
if (ret) {
|
||||
pr_err("%s: Error reverting changeset (%d)\n", __func__, ret);
|
||||
list_for_each_entry_continue(ce, &ocs->entries, node)
|
||||
__of_changeset_entry_apply(ce);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
pr_debug("of_changeset: reverted, emitting notifiers.\n");
|
||||
|
||||
/* drop the global lock while emitting notifiers */
|
||||
mutex_unlock(&of_mutex);
|
||||
list_for_each_entry_reverse(ce, &ocs->entries, node)
|
||||
__of_changeset_entry_notify(ce, 1);
|
||||
mutex_lock(&of_mutex);
|
||||
pr_debug("of_changeset: notifiers sent.\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* of_changeset_action - Perform a changeset action
|
||||
*
|
||||
* @ocs: changeset pointer
|
||||
* @action: action to perform
|
||||
* @np: Pointer to device node
|
||||
* @prop: Pointer to property
|
||||
*
|
||||
* On action being one of:
|
||||
* + OF_RECONFIG_ATTACH_NODE
|
||||
* + OF_RECONFIG_DETACH_NODE,
|
||||
* + OF_RECONFIG_ADD_PROPERTY
|
||||
* + OF_RECONFIG_REMOVE_PROPERTY,
|
||||
* + OF_RECONFIG_UPDATE_PROPERTY
|
||||
* Returns 0 on success, a negative error value in case of an error.
|
||||
*/
|
||||
int of_changeset_action(struct of_changeset *ocs, unsigned long action,
|
||||
struct device_node *np, struct property *prop)
|
||||
{
|
||||
struct of_changeset_entry *ce;
|
||||
|
||||
ce = kzalloc(sizeof(*ce), GFP_KERNEL);
|
||||
if (!ce) {
|
||||
pr_err("%s: Failed to allocate\n", __func__);
|
||||
return -ENOMEM;
|
||||
}
|
||||
/* get a reference to the node */
|
||||
ce->action = action;
|
||||
ce->np = of_node_get(np);
|
||||
ce->prop = prop;
|
||||
|
||||
if (action == OF_RECONFIG_UPDATE_PROPERTY && prop)
|
||||
ce->old_prop = of_find_property(np, prop->name, NULL);
|
||||
|
||||
/* add it to the list */
|
||||
list_add_tail(&ce->node, &ocs->entries);
|
||||
return 0;
|
||||
}
|
|
@ -923,24 +923,24 @@ int __init early_init_dt_scan_chosen(unsigned long node, const char *uname,
|
|||
}
|
||||
|
||||
#ifdef CONFIG_HAVE_MEMBLOCK
|
||||
#define MAX_PHYS_ADDR ((phys_addr_t)~0)
|
||||
|
||||
void __init __weak early_init_dt_add_memory_arch(u64 base, u64 size)
|
||||
{
|
||||
const u64 phys_offset = __pa(PAGE_OFFSET);
|
||||
base &= PAGE_MASK;
|
||||
size &= PAGE_MASK;
|
||||
|
||||
if (sizeof(phys_addr_t) < sizeof(u64)) {
|
||||
if (base > ULONG_MAX) {
|
||||
pr_warning("Ignoring memory block 0x%llx - 0x%llx\n",
|
||||
base, base + size);
|
||||
return;
|
||||
}
|
||||
if (base > MAX_PHYS_ADDR) {
|
||||
pr_warning("Ignoring memory block 0x%llx - 0x%llx\n",
|
||||
base, base + size);
|
||||
return;
|
||||
}
|
||||
|
||||
if (base + size > ULONG_MAX) {
|
||||
pr_warning("Ignoring memory range 0x%lx - 0x%llx\n",
|
||||
ULONG_MAX, base + size);
|
||||
size = ULONG_MAX - base;
|
||||
}
|
||||
if (base + size > MAX_PHYS_ADDR) {
|
||||
pr_warning("Ignoring memory range 0x%lx - 0x%llx\n",
|
||||
ULONG_MAX, base + size);
|
||||
size = MAX_PHYS_ADDR - base;
|
||||
}
|
||||
|
||||
if (base + size < phys_offset) {
|
||||
|
|
|
@ -31,6 +31,63 @@ struct alias_prop {
|
|||
char stem[0];
|
||||
};
|
||||
|
||||
extern struct mutex of_aliases_mutex;
|
||||
extern struct mutex of_mutex;
|
||||
extern struct list_head aliases_lookup;
|
||||
extern struct kset *of_kset;
|
||||
|
||||
|
||||
static inline struct device_node *kobj_to_device_node(struct kobject *kobj)
|
||||
{
|
||||
return container_of(kobj, struct device_node, kobj);
|
||||
}
|
||||
|
||||
#if defined(CONFIG_OF_DYNAMIC)
|
||||
extern int of_property_notify(int action, struct device_node *np,
|
||||
struct property *prop, struct property *old_prop);
|
||||
extern void of_node_release(struct kobject *kobj);
|
||||
#else /* CONFIG_OF_DYNAMIC */
|
||||
static inline int of_property_notify(int action, struct device_node *np,
|
||||
struct property *prop, struct property *old_prop)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#endif /* CONFIG_OF_DYNAMIC */
|
||||
|
||||
/**
|
||||
* General utilities for working with live trees.
|
||||
*
|
||||
* All functions with two leading underscores operate
|
||||
* without taking node references, so you either have to
|
||||
* 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);
|
||||
|
||||
extern const void *__of_get_property(const struct device_node *np,
|
||||
const char *name, int *lenp);
|
||||
extern int __of_add_property(struct device_node *np, struct property *prop);
|
||||
extern int __of_add_property_sysfs(struct device_node *np,
|
||||
struct property *prop);
|
||||
extern int __of_remove_property(struct device_node *np, struct property *prop);
|
||||
extern void __of_remove_property_sysfs(struct device_node *np,
|
||||
struct property *prop);
|
||||
extern int __of_update_property(struct device_node *np,
|
||||
struct property *newprop, struct property **oldprop);
|
||||
extern void __of_update_property_sysfs(struct device_node *np,
|
||||
struct property *newprop, struct property *oldprop);
|
||||
|
||||
extern void __of_attach_node(struct device_node *np);
|
||||
extern int __of_attach_node_sysfs(struct device_node *np);
|
||||
extern void __of_detach_node(struct device_node *np);
|
||||
extern void __of_detach_node_sysfs(struct device_node *np);
|
||||
|
||||
/* iterators for transactions, used for overlays */
|
||||
/* forward iterator */
|
||||
#define for_each_transaction_entry(_oft, _te) \
|
||||
list_for_each_entry(_te, &(_oft)->te_list, node)
|
||||
|
||||
/* reverse iterator */
|
||||
#define for_each_transaction_entry_reverse(_oft, _te) \
|
||||
list_for_each_entry_reverse(_te, &(_oft)->te_list, node)
|
||||
|
||||
#endif /* _LINUX_OF_PRIVATE_H */
|
||||
|
|
|
@ -206,8 +206,16 @@ void __init fdt_init_reserved_mem(void)
|
|||
for (i = 0; i < reserved_mem_count; i++) {
|
||||
struct reserved_mem *rmem = &reserved_mem[i];
|
||||
unsigned long node = rmem->fdt_node;
|
||||
int len;
|
||||
const __be32 *prop;
|
||||
int err = 0;
|
||||
|
||||
prop = of_get_flat_dt_prop(node, "phandle", &len);
|
||||
if (!prop)
|
||||
prop = of_get_flat_dt_prop(node, "linux,phandle", &len);
|
||||
if (prop)
|
||||
rmem->phandle = of_read_number(prop, len/4);
|
||||
|
||||
if (rmem->size == 0)
|
||||
err = __reserved_mem_alloc_size(node, rmem->name,
|
||||
&rmem->base, &rmem->size);
|
||||
|
@ -215,3 +223,65 @@ void __init fdt_init_reserved_mem(void)
|
|||
__reserved_mem_init_node(rmem);
|
||||
}
|
||||
}
|
||||
|
||||
static inline struct reserved_mem *__find_rmem(struct device_node *node)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
if (!node->phandle)
|
||||
return NULL;
|
||||
|
||||
for (i = 0; i < reserved_mem_count; i++)
|
||||
if (reserved_mem[i].phandle == node->phandle)
|
||||
return &reserved_mem[i];
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* of_reserved_mem_device_init() - assign reserved memory region to given device
|
||||
*
|
||||
* This function assign memory region pointed by "memory-region" device tree
|
||||
* property to the given device.
|
||||
*/
|
||||
void of_reserved_mem_device_init(struct device *dev)
|
||||
{
|
||||
struct reserved_mem *rmem;
|
||||
struct device_node *np;
|
||||
|
||||
np = of_parse_phandle(dev->of_node, "memory-region", 0);
|
||||
if (!np)
|
||||
return;
|
||||
|
||||
rmem = __find_rmem(np);
|
||||
of_node_put(np);
|
||||
|
||||
if (!rmem || !rmem->ops || !rmem->ops->device_init)
|
||||
return;
|
||||
|
||||
rmem->ops->device_init(rmem, dev);
|
||||
dev_info(dev, "assigned reserved memory node %s\n", rmem->name);
|
||||
}
|
||||
|
||||
/**
|
||||
* of_reserved_mem_device_release() - release reserved memory device structures
|
||||
*
|
||||
* This function releases structures allocated for memory region handling for
|
||||
* the given device.
|
||||
*/
|
||||
void of_reserved_mem_device_release(struct device *dev)
|
||||
{
|
||||
struct reserved_mem *rmem;
|
||||
struct device_node *np;
|
||||
|
||||
np = of_parse_phandle(dev->of_node, "memory-region", 0);
|
||||
if (!np)
|
||||
return;
|
||||
|
||||
rmem = __find_rmem(np);
|
||||
of_node_put(np);
|
||||
|
||||
if (!rmem || !rmem->ops || !rmem->ops->device_release)
|
||||
return;
|
||||
|
||||
rmem->ops->device_release(rmem, dev);
|
||||
}
|
||||
|
|
|
@ -422,6 +422,7 @@ static int of_platform_bus_create(struct device_node *bus,
|
|||
break;
|
||||
}
|
||||
}
|
||||
of_node_set_flag(bus, OF_POPULATED_BUS);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
@ -508,19 +509,13 @@ EXPORT_SYMBOL_GPL(of_platform_populate);
|
|||
|
||||
static int of_platform_device_destroy(struct device *dev, void *data)
|
||||
{
|
||||
bool *children_left = data;
|
||||
|
||||
/* Do not touch devices not populated from the device tree */
|
||||
if (!dev->of_node || !of_node_check_flag(dev->of_node, OF_POPULATED)) {
|
||||
*children_left = true;
|
||||
if (!dev->of_node || !of_node_check_flag(dev->of_node, OF_POPULATED))
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Recurse, but don't touch this device if it has any children left */
|
||||
if (of_platform_depopulate(dev) != 0) {
|
||||
*children_left = true;
|
||||
return 0;
|
||||
}
|
||||
/* Recurse for any nodes that were treated as busses */
|
||||
if (of_node_check_flag(dev->of_node, OF_POPULATED_BUS))
|
||||
device_for_each_child(dev, NULL, of_platform_device_destroy);
|
||||
|
||||
if (dev->bus == &platform_bus_type)
|
||||
platform_device_unregister(to_platform_device(dev));
|
||||
|
@ -528,19 +523,15 @@ static int of_platform_device_destroy(struct device *dev, void *data)
|
|||
else if (dev->bus == &amba_bustype)
|
||||
amba_device_unregister(to_amba_device(dev));
|
||||
#endif
|
||||
else {
|
||||
*children_left = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
of_node_clear_flag(dev->of_node, OF_POPULATED);
|
||||
|
||||
of_node_clear_flag(dev->of_node, OF_POPULATED_BUS);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* of_platform_depopulate() - Remove devices populated from device tree
|
||||
* @parent: device which childred will be removed
|
||||
* @parent: device which children will be removed
|
||||
*
|
||||
* Complementary to of_platform_populate(), this function removes children
|
||||
* of the given device (and, recurrently, their children) that have been
|
||||
|
@ -550,14 +541,9 @@ static int of_platform_device_destroy(struct device *dev, void *data)
|
|||
* Returns 0 when all children devices have been removed or
|
||||
* -EBUSY when some children remained.
|
||||
*/
|
||||
int of_platform_depopulate(struct device *parent)
|
||||
void of_platform_depopulate(struct device *parent)
|
||||
{
|
||||
bool children_left = false;
|
||||
|
||||
device_for_each_child(parent, &children_left,
|
||||
of_platform_device_destroy);
|
||||
|
||||
return children_left ? -EBUSY : 0;
|
||||
device_for_each_child(parent, NULL, of_platform_device_destroy);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(of_platform_depopulate);
|
||||
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
#include <linux/errno.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_fdt.h>
|
||||
#include <linux/of_irq.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/list.h>
|
||||
|
@ -16,11 +17,17 @@
|
|||
#include <linux/slab.h>
|
||||
#include <linux/device.h>
|
||||
|
||||
#include "of_private.h"
|
||||
|
||||
static struct selftest_results {
|
||||
int passed;
|
||||
int failed;
|
||||
} selftest_results;
|
||||
|
||||
#define NO_OF_NODES 2
|
||||
static struct device_node *nodes[NO_OF_NODES];
|
||||
static int last_node_index;
|
||||
|
||||
#define selftest(result, fmt, ...) { \
|
||||
if (!(result)) { \
|
||||
selftest_results.failed++; \
|
||||
|
@ -266,6 +273,81 @@ static void __init of_selftest_property_match_string(void)
|
|||
selftest(rc == -EILSEQ, "unterminated string; rc=%i", rc);
|
||||
}
|
||||
|
||||
#define propcmp(p1, p2) (((p1)->length == (p2)->length) && \
|
||||
(p1)->value && (p2)->value && \
|
||||
!memcmp((p1)->value, (p2)->value, (p1)->length) && \
|
||||
!strcmp((p1)->name, (p2)->name))
|
||||
static void __init of_selftest_property_copy(void)
|
||||
{
|
||||
#ifdef CONFIG_OF_DYNAMIC
|
||||
struct property p1 = { .name = "p1", .length = 0, .value = "" };
|
||||
struct property p2 = { .name = "p2", .length = 5, .value = "abcd" };
|
||||
struct property *new;
|
||||
|
||||
new = __of_prop_dup(&p1, GFP_KERNEL);
|
||||
selftest(new && propcmp(&p1, new), "empty property didn't copy correctly\n");
|
||||
kfree(new->value);
|
||||
kfree(new->name);
|
||||
kfree(new);
|
||||
|
||||
new = __of_prop_dup(&p2, GFP_KERNEL);
|
||||
selftest(new && propcmp(&p2, new), "non-empty property didn't copy correctly\n");
|
||||
kfree(new->value);
|
||||
kfree(new->name);
|
||||
kfree(new);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void __init of_selftest_changeset(void)
|
||||
{
|
||||
#ifdef CONFIG_OF_DYNAMIC
|
||||
struct property *ppadd, padd = { .name = "prop-add", .length = 0, .value = "" };
|
||||
struct property *ppupdate, pupdate = { .name = "prop-update", .length = 5, .value = "abcd" };
|
||||
struct property *ppremove;
|
||||
struct device_node *n1, *n2, *n21, *nremove, *parent;
|
||||
struct of_changeset chgset;
|
||||
|
||||
of_changeset_init(&chgset);
|
||||
n1 = __of_node_alloc("/testcase-data/changeset/n1", GFP_KERNEL);
|
||||
selftest(n1, "testcase setup failure\n");
|
||||
n2 = __of_node_alloc("/testcase-data/changeset/n2", GFP_KERNEL);
|
||||
selftest(n2, "testcase setup failure\n");
|
||||
n21 = __of_node_alloc("/testcase-data/changeset/n2/n21", GFP_KERNEL);
|
||||
selftest(n21, "testcase setup failure %p\n", n21);
|
||||
nremove = of_find_node_by_path("/testcase-data/changeset/node-remove");
|
||||
selftest(nremove, "testcase setup failure\n");
|
||||
ppadd = __of_prop_dup(&padd, GFP_KERNEL);
|
||||
selftest(ppadd, "testcase setup failure\n");
|
||||
ppupdate = __of_prop_dup(&pupdate, GFP_KERNEL);
|
||||
selftest(ppupdate, "testcase setup failure\n");
|
||||
parent = nremove->parent;
|
||||
n1->parent = parent;
|
||||
n2->parent = parent;
|
||||
n21->parent = n2;
|
||||
n2->child = n21;
|
||||
ppremove = of_find_property(parent, "prop-remove", NULL);
|
||||
selftest(ppremove, "failed to find removal prop");
|
||||
|
||||
of_changeset_init(&chgset);
|
||||
selftest(!of_changeset_attach_node(&chgset, n1), "fail attach n1\n");
|
||||
selftest(!of_changeset_attach_node(&chgset, n2), "fail attach n2\n");
|
||||
selftest(!of_changeset_detach_node(&chgset, nremove), "fail remove node\n");
|
||||
selftest(!of_changeset_attach_node(&chgset, n21), "fail attach n21\n");
|
||||
selftest(!of_changeset_add_property(&chgset, parent, ppadd), "fail add prop\n");
|
||||
selftest(!of_changeset_update_property(&chgset, parent, ppupdate), "fail update prop\n");
|
||||
selftest(!of_changeset_remove_property(&chgset, parent, ppremove), "fail remove prop\n");
|
||||
mutex_lock(&of_mutex);
|
||||
selftest(!of_changeset_apply(&chgset), "apply failed\n");
|
||||
mutex_unlock(&of_mutex);
|
||||
|
||||
mutex_lock(&of_mutex);
|
||||
selftest(!of_changeset_revert(&chgset), "revert failed\n");
|
||||
mutex_unlock(&of_mutex);
|
||||
|
||||
of_changeset_destroy(&chgset);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void __init of_selftest_parse_interrupts(void)
|
||||
{
|
||||
struct device_node *np;
|
||||
|
@ -517,9 +599,156 @@ static void __init of_selftest_platform_populate(void)
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* update_node_properties - adds the properties
|
||||
* of np into dup node (present in live tree) and
|
||||
* updates parent of children of np to dup.
|
||||
*
|
||||
* @np: node already present in live tree
|
||||
* @dup: node present in live tree to be updated
|
||||
*/
|
||||
static void update_node_properties(struct device_node *np,
|
||||
struct device_node *dup)
|
||||
{
|
||||
struct property *prop;
|
||||
struct device_node *child;
|
||||
|
||||
for_each_property_of_node(np, prop)
|
||||
of_add_property(dup, prop);
|
||||
|
||||
for_each_child_of_node(np, child)
|
||||
child->parent = dup;
|
||||
}
|
||||
|
||||
/**
|
||||
* attach_node_and_children - attaches nodes
|
||||
* and its children to live tree
|
||||
*
|
||||
* @np: Node to attach to live tree
|
||||
*/
|
||||
static int attach_node_and_children(struct device_node *np)
|
||||
{
|
||||
struct device_node *next, *root = np, *dup;
|
||||
|
||||
if (!np) {
|
||||
pr_warn("%s: No tree to attach; not running tests\n",
|
||||
__func__);
|
||||
return -ENODATA;
|
||||
}
|
||||
|
||||
|
||||
/* skip root node */
|
||||
np = np->child;
|
||||
/* storing a copy in temporary node */
|
||||
dup = np;
|
||||
|
||||
while (dup) {
|
||||
nodes[last_node_index++] = dup;
|
||||
dup = dup->sibling;
|
||||
}
|
||||
dup = NULL;
|
||||
|
||||
while (np) {
|
||||
next = np->allnext;
|
||||
dup = of_find_node_by_path(np->full_name);
|
||||
if (dup)
|
||||
update_node_properties(np, dup);
|
||||
else {
|
||||
np->child = NULL;
|
||||
if (np->parent == root)
|
||||
np->parent = of_allnodes;
|
||||
of_attach_node(np);
|
||||
}
|
||||
np = next;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* selftest_data_add - Reads, copies data from
|
||||
* linked tree and attaches it to the live tree
|
||||
*/
|
||||
static int __init selftest_data_add(void)
|
||||
{
|
||||
void *selftest_data;
|
||||
struct device_node *selftest_data_node;
|
||||
extern uint8_t __dtb_testcases_begin[];
|
||||
extern uint8_t __dtb_testcases_end[];
|
||||
const int size = __dtb_testcases_end - __dtb_testcases_begin;
|
||||
|
||||
if (!size || !of_allnodes) {
|
||||
pr_warn("%s: No testcase data to attach; not running tests\n",
|
||||
__func__);
|
||||
return -ENODATA;
|
||||
}
|
||||
|
||||
/* creating copy */
|
||||
selftest_data = kmemdup(__dtb_testcases_begin, size, GFP_KERNEL);
|
||||
|
||||
if (!selftest_data) {
|
||||
pr_warn("%s: Failed to allocate memory for selftest_data; "
|
||||
"not running tests\n", __func__);
|
||||
return -ENOMEM;
|
||||
}
|
||||
of_fdt_unflatten_tree(selftest_data, &selftest_data_node);
|
||||
|
||||
/* attach the sub-tree to live tree */
|
||||
return attach_node_and_children(selftest_data_node);
|
||||
}
|
||||
|
||||
/**
|
||||
* detach_node_and_children - detaches node
|
||||
* and its children from live tree
|
||||
*
|
||||
* @np: Node to detach from live tree
|
||||
*/
|
||||
static void detach_node_and_children(struct device_node *np)
|
||||
{
|
||||
while (np->child)
|
||||
detach_node_and_children(np->child);
|
||||
|
||||
while (np->sibling)
|
||||
detach_node_and_children(np->sibling);
|
||||
|
||||
of_detach_node(np);
|
||||
}
|
||||
|
||||
/**
|
||||
* selftest_data_remove - removes the selftest data
|
||||
* nodes from the live tree
|
||||
*/
|
||||
static void selftest_data_remove(void)
|
||||
{
|
||||
struct device_node *np;
|
||||
struct property *prop;
|
||||
|
||||
while (last_node_index >= 0) {
|
||||
if (nodes[last_node_index]) {
|
||||
np = of_find_node_by_path(nodes[last_node_index]->full_name);
|
||||
if (strcmp(np->full_name, "/aliases") != 0) {
|
||||
detach_node_and_children(np->child);
|
||||
of_detach_node(np);
|
||||
} else {
|
||||
for_each_property_of_node(np, prop) {
|
||||
if (strcmp(prop->name, "testcase-alias") == 0)
|
||||
of_remove_property(np, prop);
|
||||
}
|
||||
}
|
||||
}
|
||||
last_node_index--;
|
||||
}
|
||||
}
|
||||
|
||||
static int __init of_selftest(void)
|
||||
{
|
||||
struct device_node *np;
|
||||
int res;
|
||||
|
||||
/* adding data for selftest */
|
||||
res = selftest_data_add();
|
||||
if (res)
|
||||
return res;
|
||||
|
||||
np = of_find_node_by_path("/testcase-data/phandle-tests/consumer-a");
|
||||
if (!np) {
|
||||
|
@ -533,12 +762,18 @@ static int __init of_selftest(void)
|
|||
of_selftest_dynamic();
|
||||
of_selftest_parse_phandle_with_args();
|
||||
of_selftest_property_match_string();
|
||||
of_selftest_property_copy();
|
||||
of_selftest_changeset();
|
||||
of_selftest_parse_interrupts();
|
||||
of_selftest_parse_interrupts_extended();
|
||||
of_selftest_match_node();
|
||||
of_selftest_platform_populate();
|
||||
pr_info("end of selftest - %i passed, %i failed\n",
|
||||
selftest_results.passed, selftest_results.failed);
|
||||
|
||||
/* removing selftest data from live tree */
|
||||
selftest_data_remove();
|
||||
|
||||
return 0;
|
||||
}
|
||||
late_initcall(of_selftest);
|
||||
|
|
15
drivers/of/testcase-data/testcases.dts
Normal file
15
drivers/of/testcase-data/testcases.dts
Normal file
|
@ -0,0 +1,15 @@
|
|||
/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"
|
|
@ -1,4 +0,0 @@
|
|||
#include "tests-phandle.dtsi"
|
||||
#include "tests-interrupts.dtsi"
|
||||
#include "tests-match.dtsi"
|
||||
#include "tests-platform.dtsi"
|
|
@ -375,11 +375,11 @@ static void __exit cleanup_slots(void)
|
|||
|
||||
static int __init rpaphp_init(void)
|
||||
{
|
||||
struct device_node *dn = NULL;
|
||||
struct device_node *dn;
|
||||
|
||||
info(DRIVER_DESC " version: " DRIVER_VERSION "\n");
|
||||
|
||||
while ((dn = of_find_node_by_name(dn, "pci")))
|
||||
for_each_node_by_name(dn, "pci")
|
||||
rpaphp_add_slot(dn);
|
||||
|
||||
return 0;
|
||||
|
|
|
@ -108,55 +108,23 @@ static void disable_tx_interrupt(struct ehv_bc_data *bc)
|
|||
*
|
||||
* The byte channel to be used for the console is specified via a "stdout"
|
||||
* property in the /chosen node.
|
||||
*
|
||||
* For compatible with legacy device trees, we also look for a "stdout" alias.
|
||||
*/
|
||||
static int find_console_handle(void)
|
||||
{
|
||||
struct device_node *np, *np2;
|
||||
struct device_node *np = of_stdout;
|
||||
const char *sprop = NULL;
|
||||
const uint32_t *iprop;
|
||||
|
||||
np = of_find_node_by_path("/chosen");
|
||||
if (np)
|
||||
sprop = of_get_property(np, "stdout-path", NULL);
|
||||
|
||||
if (!np || !sprop) {
|
||||
of_node_put(np);
|
||||
np = of_find_node_by_name(NULL, "aliases");
|
||||
if (np)
|
||||
sprop = of_get_property(np, "stdout", NULL);
|
||||
}
|
||||
|
||||
if (!sprop) {
|
||||
of_node_put(np);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* We don't care what the aliased node is actually called. We only
|
||||
* care if it's compatible with "epapr,hv-byte-channel", because that
|
||||
* indicates that it's a byte channel node. We use a temporary
|
||||
* variable, 'np2', because we can't release 'np' until we're done with
|
||||
* 'sprop'.
|
||||
* indicates that it's a byte channel node.
|
||||
*/
|
||||
np2 = of_find_node_by_path(sprop);
|
||||
of_node_put(np);
|
||||
np = np2;
|
||||
if (!np) {
|
||||
pr_warning("ehv-bc: stdout node '%s' does not exist\n", sprop);
|
||||
if (!np || !of_device_is_compatible(np, "epapr,hv-byte-channel"))
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Is it a byte channel? */
|
||||
if (!of_device_is_compatible(np, "epapr,hv-byte-channel")) {
|
||||
of_node_put(np);
|
||||
return 0;
|
||||
}
|
||||
|
||||
stdout_irq = irq_of_parse_and_map(np, 0);
|
||||
if (stdout_irq == NO_IRQ) {
|
||||
pr_err("ehv-bc: no 'interrupts' property in %s node\n", sprop);
|
||||
of_node_put(np);
|
||||
pr_err("ehv-bc: no 'interrupts' property in %s node\n", np->full_name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -167,12 +135,9 @@ static int find_console_handle(void)
|
|||
if (!iprop) {
|
||||
pr_err("ehv-bc: no 'hv-handle' property in %s node\n",
|
||||
np->name);
|
||||
of_node_put(np);
|
||||
return 0;
|
||||
}
|
||||
stdout_bc = be32_to_cpu(*iprop);
|
||||
|
||||
of_node_put(np);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
|
|
@ -342,22 +342,13 @@ static void udbg_init_opal_common(void)
|
|||
|
||||
void __init hvc_opal_init_early(void)
|
||||
{
|
||||
struct device_node *stdout_node = NULL;
|
||||
struct device_node *stdout_node = of_node_get(of_stdout);
|
||||
const __be32 *termno;
|
||||
const char *name = NULL;
|
||||
const struct hv_ops *ops;
|
||||
u32 index;
|
||||
|
||||
/* find the boot console from /chosen/stdout */
|
||||
if (of_chosen)
|
||||
name = of_get_property(of_chosen, "linux,stdout-path", NULL);
|
||||
if (name) {
|
||||
stdout_node = of_find_node_by_path(name);
|
||||
if (!stdout_node) {
|
||||
pr_err("hvc_opal: Failed to locate default console!\n");
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
/* If the console wasn't in /chosen, try /ibm,opal */
|
||||
if (!stdout_node) {
|
||||
struct device_node *opal, *np;
|
||||
|
||||
/* Current OPAL takeover doesn't provide the stdout
|
||||
|
|
|
@ -404,42 +404,35 @@ module_exit(hvc_vio_exit);
|
|||
|
||||
void __init hvc_vio_init_early(void)
|
||||
{
|
||||
struct device_node *stdout_node;
|
||||
const __be32 *termno;
|
||||
const char *name;
|
||||
const struct hv_ops *ops;
|
||||
|
||||
/* find the boot console from /chosen/stdout */
|
||||
if (!of_chosen)
|
||||
if (!of_stdout)
|
||||
return;
|
||||
name = of_get_property(of_chosen, "linux,stdout-path", NULL);
|
||||
if (name == NULL)
|
||||
return;
|
||||
stdout_node = of_find_node_by_path(name);
|
||||
if (!stdout_node)
|
||||
return;
|
||||
name = of_get_property(stdout_node, "name", NULL);
|
||||
name = of_get_property(of_stdout, "name", NULL);
|
||||
if (!name) {
|
||||
printk(KERN_WARNING "stdout node missing 'name' property!\n");
|
||||
goto out;
|
||||
return;
|
||||
}
|
||||
|
||||
/* Check if it's a virtual terminal */
|
||||
if (strncmp(name, "vty", 3) != 0)
|
||||
goto out;
|
||||
termno = of_get_property(stdout_node, "reg", NULL);
|
||||
return;
|
||||
termno = of_get_property(of_stdout, "reg", NULL);
|
||||
if (termno == NULL)
|
||||
goto out;
|
||||
return;
|
||||
hvterm_priv0.termno = of_read_number(termno, 1);
|
||||
spin_lock_init(&hvterm_priv0.buf_lock);
|
||||
hvterm_privs[0] = &hvterm_priv0;
|
||||
|
||||
/* Check the protocol */
|
||||
if (of_device_is_compatible(stdout_node, "hvterm1")) {
|
||||
if (of_device_is_compatible(of_stdout, "hvterm1")) {
|
||||
hvterm_priv0.proto = HV_PROTOCOL_RAW;
|
||||
ops = &hvterm_raw_ops;
|
||||
}
|
||||
else if (of_device_is_compatible(stdout_node, "hvterm-protocol")) {
|
||||
else if (of_device_is_compatible(of_stdout, "hvterm-protocol")) {
|
||||
hvterm_priv0.proto = HV_PROTOCOL_HVSI;
|
||||
ops = &hvterm_hvsi_ops;
|
||||
hvsilib_init(&hvterm_priv0.hvsi, hvc_get_chars, hvc_put_chars,
|
||||
|
@ -447,7 +440,7 @@ void __init hvc_vio_init_early(void)
|
|||
/* HVSI, perform the handshake now */
|
||||
hvsilib_establish(&hvterm_priv0.hvsi);
|
||||
} else
|
||||
goto out;
|
||||
return;
|
||||
udbg_putc = udbg_hvc_putc;
|
||||
udbg_getc = udbg_hvc_getc;
|
||||
udbg_getc_poll = udbg_hvc_getc_poll;
|
||||
|
@ -456,14 +449,12 @@ void __init hvc_vio_init_early(void)
|
|||
* backend for HVSI, only do udbg
|
||||
*/
|
||||
if (hvterm_priv0.proto == HV_PROTOCOL_HVSI)
|
||||
goto out;
|
||||
return;
|
||||
#endif
|
||||
/* Check whether the user has requested a different console. */
|
||||
if (!strstr(cmd_line, "console="))
|
||||
add_preferred_console("hvc", 0, NULL);
|
||||
hvc_instantiate(0, 0, ops);
|
||||
out:
|
||||
of_node_put(stdout_node);
|
||||
}
|
||||
|
||||
/* call this from early_init() for a working debug console on
|
||||
|
|
|
@ -1653,8 +1653,7 @@ static int __init pmz_probe(void)
|
|||
/*
|
||||
* Find all escc chips in the system
|
||||
*/
|
||||
node_p = of_find_node_by_name(NULL, "escc");
|
||||
while (node_p) {
|
||||
for_each_node_by_name(node_p, "escc") {
|
||||
/*
|
||||
* First get channel A/B node pointers
|
||||
*
|
||||
|
@ -1672,7 +1671,7 @@ static int __init pmz_probe(void)
|
|||
of_node_put(node_b);
|
||||
printk(KERN_ERR "pmac_zilog: missing node %c for escc %s\n",
|
||||
(!node_a) ? 'a' : 'b', node_p->full_name);
|
||||
goto next;
|
||||
continue;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -1699,11 +1698,9 @@ static int __init pmz_probe(void)
|
|||
of_node_put(node_b);
|
||||
memset(&pmz_ports[count], 0, sizeof(struct uart_pmac_port));
|
||||
memset(&pmz_ports[count+1], 0, sizeof(struct uart_pmac_port));
|
||||
goto next;
|
||||
continue;
|
||||
}
|
||||
count += 2;
|
||||
next:
|
||||
node_p = of_find_node_by_name(node_p, "escc");
|
||||
}
|
||||
pmz_ports_count = count;
|
||||
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
#include <linux/slab.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/console.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/proc_fs.h>
|
||||
#include <linux/seq_file.h>
|
||||
#include <linux/device.h>
|
||||
|
@ -2611,6 +2612,8 @@ int uart_add_one_port(struct uart_driver *drv, struct uart_port *uport)
|
|||
spin_lock_init(&uport->lock);
|
||||
lockdep_set_class(&uport->lock, &port_lock_key);
|
||||
}
|
||||
if (uport->cons && uport->dev)
|
||||
of_console_check(uport->dev->of_node, uport->cons->name, uport->line);
|
||||
|
||||
uart_configure_port(drv, state, uport);
|
||||
|
||||
|
|
|
@ -74,8 +74,6 @@ struct of_phandle_args {
|
|||
uint32_t args[MAX_PHANDLE_ARGS];
|
||||
};
|
||||
|
||||
extern int of_node_add(struct device_node *node);
|
||||
|
||||
/* initialize a node */
|
||||
extern struct kobj_type of_node_ktype;
|
||||
static inline void of_node_init(struct device_node *node)
|
||||
|
@ -113,6 +111,7 @@ static inline void of_node_put(struct device_node *node) { }
|
|||
extern struct device_node *of_allnodes;
|
||||
extern struct device_node *of_chosen;
|
||||
extern struct device_node *of_aliases;
|
||||
extern struct device_node *of_stdout;
|
||||
extern raw_spinlock_t devtree_lock;
|
||||
|
||||
static inline bool of_have_populated_dt(void)
|
||||
|
@ -204,6 +203,7 @@ static inline unsigned long of_read_ulong(const __be32 *cell, int size)
|
|||
#define OF_DYNAMIC 1 /* node and properties were allocated via kmalloc */
|
||||
#define OF_DETACHED 2 /* node has been detached from the device tree */
|
||||
#define OF_POPULATED 3 /* device already created for the node */
|
||||
#define OF_POPULATED_BUS 4 /* of_platform_populate recursed to children of this node */
|
||||
|
||||
#define OF_IS_DYNAMIC(x) test_bit(OF_DYNAMIC, &x->_flags)
|
||||
#define OF_MARK_DYNAMIC(x) set_bit(OF_DYNAMIC, &x->_flags)
|
||||
|
@ -322,6 +322,7 @@ extern int of_update_property(struct device_node *np, struct property *newprop);
|
|||
struct of_prop_reconfig {
|
||||
struct device_node *dn;
|
||||
struct property *prop;
|
||||
struct property *old_prop;
|
||||
};
|
||||
|
||||
extern int of_reconfig_notifier_register(struct notifier_block *);
|
||||
|
@ -352,7 +353,7 @@ const __be32 *of_prop_next_u32(struct property *prop, const __be32 *cur,
|
|||
*/
|
||||
const char *of_prop_next_string(struct property *prop, const char *cur);
|
||||
|
||||
int of_device_is_stdout_path(struct device_node *dn);
|
||||
bool of_console_check(struct device_node *dn, char *name, int index);
|
||||
|
||||
#else /* CONFIG_OF */
|
||||
|
||||
|
@ -564,9 +565,9 @@ static inline int of_machine_is_compatible(const char *compat)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static inline int of_device_is_stdout_path(struct device_node *dn)
|
||||
static inline bool of_console_check(const struct device_node *dn, const char *name, int index)
|
||||
{
|
||||
return 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline const __be32 *of_prop_next_u32(struct property *prop,
|
||||
|
@ -786,4 +787,80 @@ typedef void (*of_init_fn_1)(struct device_node *);
|
|||
#define OF_DECLARE_2(table, name, compat, fn) \
|
||||
_OF_DECLARE(table, name, compat, fn, of_init_fn_2)
|
||||
|
||||
/**
|
||||
* struct of_changeset_entry - Holds a changeset entry
|
||||
*
|
||||
* @node: list_head for the log list
|
||||
* @action: notifier action
|
||||
* @np: pointer to the device node affected
|
||||
* @prop: pointer to the property affected
|
||||
* @old_prop: hold a pointer to the original property
|
||||
*
|
||||
* Every modification of the device tree during a changeset
|
||||
* is held in a list of of_changeset_entry structures.
|
||||
* That way we can recover from a partial application, or we can
|
||||
* revert the changeset
|
||||
*/
|
||||
struct of_changeset_entry {
|
||||
struct list_head node;
|
||||
unsigned long action;
|
||||
struct device_node *np;
|
||||
struct property *prop;
|
||||
struct property *old_prop;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct of_changeset - changeset tracker structure
|
||||
*
|
||||
* @entries: list_head for the changeset entries
|
||||
*
|
||||
* changesets are a convenient way to apply bulk changes to the
|
||||
* live tree. In case of an error, changes are rolled-back.
|
||||
* changesets live on after initial application, and if not
|
||||
* destroyed after use, they can be reverted in one single call.
|
||||
*/
|
||||
struct of_changeset {
|
||||
struct list_head entries;
|
||||
};
|
||||
|
||||
#ifdef CONFIG_OF_DYNAMIC
|
||||
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);
|
||||
extern int of_changeset_revert(struct of_changeset *ocs);
|
||||
extern int of_changeset_action(struct of_changeset *ocs,
|
||||
unsigned long action, struct device_node *np,
|
||||
struct property *prop);
|
||||
|
||||
static inline int of_changeset_attach_node(struct of_changeset *ocs,
|
||||
struct device_node *np)
|
||||
{
|
||||
return of_changeset_action(ocs, OF_RECONFIG_ATTACH_NODE, np, NULL);
|
||||
}
|
||||
|
||||
static inline int of_changeset_detach_node(struct of_changeset *ocs,
|
||||
struct device_node *np)
|
||||
{
|
||||
return of_changeset_action(ocs, OF_RECONFIG_DETACH_NODE, np, NULL);
|
||||
}
|
||||
|
||||
static inline int of_changeset_add_property(struct of_changeset *ocs,
|
||||
struct device_node *np, struct property *prop)
|
||||
{
|
||||
return of_changeset_action(ocs, OF_RECONFIG_ADD_PROPERTY, np, prop);
|
||||
}
|
||||
|
||||
static inline int of_changeset_remove_property(struct of_changeset *ocs,
|
||||
struct device_node *np, struct property *prop)
|
||||
{
|
||||
return of_changeset_action(ocs, OF_RECONFIG_REMOVE_PROPERTY, np, prop);
|
||||
}
|
||||
|
||||
static inline int of_changeset_update_property(struct of_changeset *ocs,
|
||||
struct device_node *np, struct property *prop)
|
||||
{
|
||||
return of_changeset_action(ocs, OF_RECONFIG_UPDATE_PROPERTY, np, prop);
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _LINUX_OF_H */
|
||||
|
|
|
@ -72,7 +72,7 @@ extern int of_platform_populate(struct device_node *root,
|
|||
const struct of_device_id *matches,
|
||||
const struct of_dev_auxdata *lookup,
|
||||
struct device *parent);
|
||||
extern int of_platform_depopulate(struct device *parent);
|
||||
extern void of_platform_depopulate(struct device *parent);
|
||||
#else
|
||||
static inline int of_platform_populate(struct device_node *root,
|
||||
const struct of_device_id *matches,
|
||||
|
@ -81,10 +81,7 @@ static inline int of_platform_populate(struct device_node *root,
|
|||
{
|
||||
return -ENODEV;
|
||||
}
|
||||
static inline int of_platform_depopulate(struct device *parent)
|
||||
{
|
||||
return -ENODEV;
|
||||
}
|
||||
static inline void of_platform_depopulate(struct device *parent) { }
|
||||
#endif
|
||||
|
||||
#endif /* _LINUX_OF_PLATFORM_H */
|
||||
|
|
|
@ -8,6 +8,7 @@ struct reserved_mem_ops;
|
|||
struct reserved_mem {
|
||||
const char *name;
|
||||
unsigned long fdt_node;
|
||||
unsigned long phandle;
|
||||
const struct reserved_mem_ops *ops;
|
||||
phys_addr_t base;
|
||||
phys_addr_t size;
|
||||
|
@ -27,10 +28,16 @@ typedef int (*reservedmem_of_init_fn)(struct reserved_mem *rmem);
|
|||
_OF_DECLARE(reservedmem, name, compat, init, reservedmem_of_init_fn)
|
||||
|
||||
#ifdef CONFIG_OF_RESERVED_MEM
|
||||
void of_reserved_mem_device_init(struct device *dev);
|
||||
void of_reserved_mem_device_release(struct device *dev);
|
||||
|
||||
void fdt_init_reserved_mem(void);
|
||||
void fdt_reserved_mem_save_node(unsigned long node, const char *uname,
|
||||
phys_addr_t base, phys_addr_t size);
|
||||
#else
|
||||
static inline void of_reserved_mem_device_init(struct device *dev) { }
|
||||
static inline void of_reserved_mem_device_release(struct device *pdev) { }
|
||||
|
||||
static inline void fdt_init_reserved_mem(void) { }
|
||||
static inline void fdt_reserved_mem_save_node(unsigned long node,
|
||||
const char *uname, phys_addr_t base, phys_addr_t size) { }
|
||||
|
|
|
@ -992,9 +992,9 @@ static int snd_pmac_detect(struct snd_pmac *chip)
|
|||
return -ENODEV;
|
||||
|
||||
if (!sound) {
|
||||
sound = of_find_node_by_name(NULL, "sound");
|
||||
while (sound && sound->parent != chip->node)
|
||||
sound = of_find_node_by_name(sound, "sound");
|
||||
for_each_node_by_name(sound, "sound")
|
||||
if (sound->parent == chip->node)
|
||||
break;
|
||||
}
|
||||
if (! sound) {
|
||||
of_node_put(chip->node);
|
||||
|
|
Loading…
Reference in a new issue