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:
Linus Torvalds 2014-08-14 09:53:39 -06:00
commit ae36e95cf8
36 changed files with 1385 additions and 584 deletions

View 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)

View 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

View File

@ -15,6 +15,10 @@
i2c0 = &i2c0;
};
chosen {
stdout-path = &uart0;
};
memory {
reg = <0x0 0x08000000>;
};

View File

@ -56,5 +56,3 @@
};
};
};
#include <testcases.dtsi>

View File

@ -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);

View File

@ -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 */

View File

@ -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;

View File

@ -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);

View File

@ -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);

View File

@ -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;

View File

@ -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);

View File

@ -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)

View File

@ -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);

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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
View 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;
}

View File

@ -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) {

View File

@ -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 */

View File

@ -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);
}

View File

@ -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);

View File

@ -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);

View 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"

View File

@ -1,4 +0,0 @@
#include "tests-phandle.dtsi"
#include "tests-interrupts.dtsi"
#include "tests-match.dtsi"
#include "tests-platform.dtsi"

View File

@ -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;

View File

@ -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;
}

View File

@ -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

View File

@ -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

View File

@ -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;

View File

@ -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);

View File

@ -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 */

View File

@ -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 */

View File

@ -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) { }

View File

@ -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);