summaryrefslogtreecommitdiff
path: root/drivers/of
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/of')
-rw-r--r--drivers/of/address.c2
-rw-r--r--drivers/of/dynamic.c65
-rw-r--r--drivers/of/irq.c45
-rw-r--r--drivers/of/of_mdio.c134
-rw-r--r--drivers/of/of_private.h2
-rw-r--r--drivers/of/overlay.c8
-rw-r--r--drivers/of/platform.c1
-rw-r--r--drivers/of/unittest.c18
8 files changed, 196 insertions, 79 deletions
diff --git a/drivers/of/address.c b/drivers/of/address.c
index 9582c5703..91a469d55 100644
--- a/drivers/of/address.c
+++ b/drivers/of/address.c
@@ -597,7 +597,7 @@ static u64 __of_translate_address(struct device_node *dev,
pbus = of_match_bus(parent);
pbus->count_cells(dev, &pna, &pns);
if (!OF_CHECK_COUNTS(pna, pns)) {
- printk(KERN_ERR "prom_parse: Bad cell count for %s\n",
+ pr_err("prom_parse: Bad cell count for %s\n",
of_node_full_name(dev));
break;
}
diff --git a/drivers/of/dynamic.c b/drivers/of/dynamic.c
index 53826b84e..c647bd1b6 100644
--- a/drivers/of/dynamic.c
+++ b/drivers/of/dynamic.c
@@ -646,6 +646,7 @@ void of_changeset_init(struct of_changeset *ocs)
memset(ocs, 0, sizeof(*ocs));
INIT_LIST_HEAD(&ocs->entries);
}
+EXPORT_SYMBOL_GPL(of_changeset_init);
/**
* of_changeset_destroy - Destroy a changeset
@@ -662,20 +663,9 @@ void of_changeset_destroy(struct of_changeset *ocs)
list_for_each_entry_safe_reverse(ce, cen, &ocs->entries, node)
__of_changeset_entry_destroy(ce);
}
+EXPORT_SYMBOL_GPL(of_changeset_destroy);
-/**
- * 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)
+int __of_changeset_apply(struct of_changeset *ocs)
{
struct of_changeset_entry *ce;
int ret;
@@ -704,17 +694,30 @@ int of_changeset_apply(struct of_changeset *ocs)
}
/**
- * of_changeset_revert - Reverts an applied changeset
+ * of_changeset_apply - Applies a 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.
+ * Applies a changeset to the live tree.
+ * Any side-effects of live tree state changes are applied here on
+ * success, 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_revert(struct of_changeset *ocs)
+int of_changeset_apply(struct of_changeset *ocs)
+{
+ int ret;
+
+ mutex_lock(&of_mutex);
+ ret = __of_changeset_apply(ocs);
+ mutex_unlock(&of_mutex);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(of_changeset_apply);
+
+int __of_changeset_revert(struct of_changeset *ocs)
{
struct of_changeset_entry *ce;
int ret;
@@ -742,6 +745,29 @@ int of_changeset_revert(struct of_changeset *ocs)
}
/**
+ * 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)
+{
+ int ret;
+
+ mutex_lock(&of_mutex);
+ ret = __of_changeset_revert(ocs);
+ mutex_unlock(&of_mutex);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(of_changeset_revert);
+
+/**
* of_changeset_action - Perform a changeset action
*
* @ocs: changeset pointer
@@ -779,3 +805,4 @@ int of_changeset_action(struct of_changeset *ocs, unsigned long action,
list_add_tail(&ce->node, &ocs->entries);
return 0;
}
+EXPORT_SYMBOL_GPL(of_changeset_action);
diff --git a/drivers/of/irq.c b/drivers/of/irq.c
index 72a2c1969..e7bfc175b 100644
--- a/drivers/of/irq.c
+++ b/drivers/of/irq.c
@@ -473,6 +473,7 @@ EXPORT_SYMBOL_GPL(of_irq_to_resource_table);
struct of_intc_desc {
struct list_head list;
+ of_irq_init_cb_t irq_init_cb;
struct device_node *dev;
struct device_node *interrupt_parent;
};
@@ -486,6 +487,7 @@ struct of_intc_desc {
*/
void __init of_irq_init(const struct of_device_id *matches)
{
+ const struct of_device_id *match;
struct device_node *np, *parent = NULL;
struct of_intc_desc *desc, *temp_desc;
struct list_head intc_desc_list, intc_parent_list;
@@ -493,10 +495,15 @@ void __init of_irq_init(const struct of_device_id *matches)
INIT_LIST_HEAD(&intc_desc_list);
INIT_LIST_HEAD(&intc_parent_list);
- for_each_matching_node(np, matches) {
+ for_each_matching_node_and_match(np, matches, &match) {
if (!of_find_property(np, "interrupt-controller", NULL) ||
!of_device_is_available(np))
continue;
+
+ if (WARN(!match->data, "of_irq_init: no init function for %s\n",
+ match->compatible))
+ continue;
+
/*
* Here, we allocate and populate an of_intc_desc with the node
* pointer, interrupt-parent device_node etc.
@@ -507,6 +514,7 @@ void __init of_irq_init(const struct of_device_id *matches)
goto err;
}
+ desc->irq_init_cb = match->data;
desc->dev = of_node_get(np);
desc->interrupt_parent = of_irq_find_parent(np);
if (desc->interrupt_parent == np)
@@ -526,27 +534,18 @@ void __init of_irq_init(const struct of_device_id *matches)
* The assumption is that NULL parent means a root controller.
*/
list_for_each_entry_safe(desc, temp_desc, &intc_desc_list, list) {
- const struct of_device_id *match;
int ret;
- of_irq_init_cb_t irq_init_cb;
if (desc->interrupt_parent != parent)
continue;
list_del(&desc->list);
- match = of_match_node(matches, desc->dev);
- if (WARN(!match->data,
- "of_irq_init: no init function for %s\n",
- match->compatible)) {
- kfree(desc);
- continue;
- }
- pr_debug("of_irq_init: init %s @ %p, parent %p\n",
- match->compatible,
+ pr_debug("of_irq_init: init %s (%p), parent %p\n",
+ desc->dev->full_name,
desc->dev, desc->interrupt_parent);
- irq_init_cb = (of_irq_init_cb_t)match->data;
- ret = irq_init_cb(desc->dev, desc->interrupt_parent);
+ ret = desc->irq_init_cb(desc->dev,
+ desc->interrupt_parent);
if (ret) {
kfree(desc);
continue;
@@ -687,18 +686,6 @@ u32 of_msi_map_rid(struct device *dev, struct device_node *msi_np, u32 rid_in)
return __of_msi_map_rid(dev, &msi_np, rid_in);
}
-static struct irq_domain *__of_get_msi_domain(struct device_node *np,
- enum irq_domain_bus_token token)
-{
- struct irq_domain *d;
-
- d = irq_find_matching_host(np, token);
- if (!d)
- d = irq_find_host(np);
-
- return d;
-}
-
/**
* of_msi_map_get_device_domain - Use msi-map to find the relevant MSI domain
* @dev: device for which the mapping is to be done.
@@ -714,7 +701,7 @@ struct irq_domain *of_msi_map_get_device_domain(struct device *dev, u32 rid)
struct device_node *np = NULL;
__of_msi_map_rid(dev, &np, rid);
- return __of_get_msi_domain(np, DOMAIN_BUS_PCI_MSI);
+ return irq_find_matching_host(np, DOMAIN_BUS_PCI_MSI);
}
/**
@@ -738,7 +725,7 @@ struct irq_domain *of_msi_get_domain(struct device *dev,
/* Check for a single msi-parent property */
msi_np = of_parse_phandle(np, "msi-parent", 0);
if (msi_np && !of_property_read_bool(msi_np, "#msi-cells")) {
- d = __of_get_msi_domain(msi_np, token);
+ d = irq_find_matching_host(msi_np, token);
if (!d)
of_node_put(msi_np);
return d;
@@ -752,7 +739,7 @@ struct irq_domain *of_msi_get_domain(struct device *dev,
while (!of_parse_phandle_with_args(np, "msi-parent",
"#msi-cells",
index, &args)) {
- d = __of_get_msi_domain(args.np, token);
+ d = irq_find_matching_host(args.np, token);
if (d)
return d;
diff --git a/drivers/of/of_mdio.c b/drivers/of/of_mdio.c
index a87a868fe..365dc7e83 100644
--- a/drivers/of/of_mdio.c
+++ b/drivers/of/of_mdio.c
@@ -62,11 +62,9 @@ static int of_mdiobus_register_phy(struct mii_bus *mdio, struct device_node *chi
rc = irq_of_parse_and_map(child, 0);
if (rc > 0) {
phy->irq = rc;
- if (mdio->irq)
- mdio->irq[addr] = rc;
+ mdio->irq[addr] = rc;
} else {
- if (mdio->irq)
- phy->irq = mdio->irq[addr];
+ phy->irq = mdio->irq[addr];
}
if (of_property_read_bool(child, "broken-turn-around"))
@@ -75,7 +73,7 @@ static int of_mdiobus_register_phy(struct mii_bus *mdio, struct device_node *chi
/* Associate the OF node with the device structure so it
* can be looked up later */
of_node_get(child);
- phy->dev.of_node = child;
+ phy->mdio.dev.of_node = child;
/* All data is now stored in the phy struct;
* register it */
@@ -92,6 +90,37 @@ static int of_mdiobus_register_phy(struct mii_bus *mdio, struct device_node *chi
return 0;
}
+static int of_mdiobus_register_device(struct mii_bus *mdio,
+ struct device_node *child,
+ u32 addr)
+{
+ struct mdio_device *mdiodev;
+ int rc;
+
+ mdiodev = mdio_device_create(mdio, addr);
+ if (!mdiodev || IS_ERR(mdiodev))
+ return 1;
+
+ /* Associate the OF node with the device structure so it
+ * can be looked up later.
+ */
+ of_node_get(child);
+ mdiodev->dev.of_node = child;
+
+ /* All data is now stored in the mdiodev struct; register it. */
+ rc = mdio_device_register(mdiodev);
+ if (rc) {
+ mdio_device_free(mdiodev);
+ of_node_put(child);
+ return 1;
+ }
+
+ dev_dbg(&mdio->dev, "registered mdio device %s at address %i\n",
+ child->name, addr);
+
+ return 0;
+}
+
int of_mdio_parse_addr(struct device *dev, const struct device_node *np)
{
u32 addr;
@@ -114,6 +143,63 @@ int of_mdio_parse_addr(struct device *dev, const struct device_node *np)
}
EXPORT_SYMBOL(of_mdio_parse_addr);
+/* The following is a list of PHY compatible strings which appear in
+ * some DTBs. The compatible string is never matched against a PHY
+ * driver, so is pointless. We only expect devices which are not PHYs
+ * to have a compatible string, so they can be matched to an MDIO
+ * driver. Encourage users to upgrade their DT blobs to remove these.
+ */
+static const struct of_device_id whitelist_phys[] = {
+ { .compatible = "brcm,40nm-ephy" },
+ { .compatible = "marvell,88E1111", },
+ { .compatible = "marvell,88e1116", },
+ { .compatible = "marvell,88e1118", },
+ { .compatible = "marvell,88e1145", },
+ { .compatible = "marvell,88e1149r", },
+ { .compatible = "marvell,88e1310", },
+ { .compatible = "marvell,88E1510", },
+ { .compatible = "marvell,88E1514", },
+ { .compatible = "moxa,moxart-rtl8201cp", },
+ {}
+};
+
+/*
+ * Return true if the child node is for a phy. It must either:
+ * o Compatible string of "ethernet-phy-idX.X"
+ * o Compatible string of "ethernet-phy-ieee802.3-c45"
+ * o Compatible string of "ethernet-phy-ieee802.3-c22"
+ * o In the white list above (and issue a warning)
+ * o No compatibility string
+ *
+ * A device which is not a phy is expected to have a compatible string
+ * indicating what sort of device it is.
+ */
+static bool of_mdiobus_child_is_phy(struct device_node *child)
+{
+ u32 phy_id;
+
+ if (of_get_phy_id(child, &phy_id) != -EINVAL)
+ return true;
+
+ if (of_device_is_compatible(child, "ethernet-phy-ieee802.3-c45"))
+ return true;
+
+ if (of_device_is_compatible(child, "ethernet-phy-ieee802.3-c22"))
+ return true;
+
+ if (of_match_node(whitelist_phys, child)) {
+ pr_warn(FW_WARN
+ "%s: Whitelisted compatible string. Please remove\n",
+ child->full_name);
+ return true;
+ }
+
+ if (!of_find_property(child, "compatible", NULL))
+ return true;
+
+ return false;
+}
+
/**
* of_mdiobus_register - Register mii_bus and create PHYs from the device tree
* @mdio: pointer to mii_bus structure
@@ -127,17 +213,12 @@ int of_mdiobus_register(struct mii_bus *mdio, struct device_node *np)
struct device_node *child;
const __be32 *paddr;
bool scanphys = false;
- int addr, rc, i;
+ int addr, rc;
/* Mask out all PHYs from auto probing. Instead the PHYs listed in
* the device tree are populated after the bus has been registered */
mdio->phy_mask = ~0;
- /* Clear all the IRQ properties */
- if (mdio->irq)
- for (i=0; i<PHY_MAX_ADDR; i++)
- mdio->irq[i] = PHY_POLL;
-
mdio->dev.of_node = np;
/* Register the MDIO bus */
@@ -145,7 +226,7 @@ int of_mdiobus_register(struct mii_bus *mdio, struct device_node *np)
if (rc)
return rc;
- /* Loop over the child nodes and register a phy_device for each one */
+ /* Loop over the child nodes and register a phy_device for each phy */
for_each_available_child_of_node(np, child) {
addr = of_mdio_parse_addr(&mdio->dev, child);
if (addr < 0) {
@@ -153,9 +234,10 @@ int of_mdiobus_register(struct mii_bus *mdio, struct device_node *np)
continue;
}
- rc = of_mdiobus_register_phy(mdio, child, addr);
- if (rc)
- continue;
+ if (of_mdiobus_child_is_phy(child))
+ of_mdiobus_register_phy(mdio, child, addr);
+ else
+ of_mdiobus_register_device(mdio, child, addr);
}
if (!scanphys)
@@ -170,16 +252,15 @@ int of_mdiobus_register(struct mii_bus *mdio, struct device_node *np)
for (addr = 0; addr < PHY_MAX_ADDR; addr++) {
/* skip already registered PHYs */
- if (mdio->phy_map[addr])
+ if (mdiobus_is_registered_device(mdio, addr))
continue;
/* be noisy to encourage people to set reg property */
dev_info(&mdio->dev, "scan phy %s at address %i\n",
child->name, addr);
- rc = of_mdiobus_register_phy(mdio, child, addr);
- if (rc)
- continue;
+ if (of_mdiobus_child_is_phy(child))
+ of_mdiobus_register_phy(mdio, child, addr);
}
}
@@ -203,11 +284,19 @@ static int of_phy_match(struct device *dev, void *phy_np)
struct phy_device *of_phy_find_device(struct device_node *phy_np)
{
struct device *d;
+ struct mdio_device *mdiodev;
+
if (!phy_np)
return NULL;
d = bus_find_device(&mdio_bus_type, NULL, phy_np, of_phy_match);
- return d ? to_phy_device(d) : NULL;
+ if (d) {
+ mdiodev = to_mdio_device(d);
+ if (mdiodev->flags & MDIO_DEVICE_FLAG_PHY)
+ return to_phy_device(d);
+ }
+
+ return NULL;
}
EXPORT_SYMBOL(of_phy_find_device);
@@ -216,6 +305,7 @@ EXPORT_SYMBOL(of_phy_find_device);
* @dev: pointer to net_device claiming the phy
* @phy_np: Pointer to device tree node for the PHY
* @hndlr: Link state callback for the network device
+ * @flags: flags to pass to the PHY
* @iface: PHY data interface type
*
* If successful, returns a pointer to the phy_device with the embedded
@@ -238,7 +328,7 @@ struct phy_device *of_phy_connect(struct net_device *dev,
ret = phy_connect_direct(dev, phy, hndlr, iface);
/* refcount is held by phy_connect_direct() on success */
- put_device(&phy->dev);
+ put_device(&phy->mdio.dev);
return ret ? NULL : phy;
}
@@ -268,7 +358,7 @@ struct phy_device *of_phy_attach(struct net_device *dev,
ret = phy_attach_direct(dev, phy, flags, iface);
/* refcount is held by phy_attach_direct() on success */
- put_device(&phy->dev);
+ put_device(&phy->mdio.dev);
return ret ? NULL : phy;
}
diff --git a/drivers/of/of_private.h b/drivers/of/of_private.h
index 8e882e706..829469fae 100644
--- a/drivers/of/of_private.h
+++ b/drivers/of/of_private.h
@@ -45,6 +45,8 @@ static inline struct device_node *kobj_to_device_node(struct kobject *kobj)
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);
+extern int __of_changeset_apply(struct of_changeset *ocs);
+extern int __of_changeset_revert(struct of_changeset *ocs);
#else /* CONFIG_OF_DYNAMIC */
static inline int of_property_notify(int action, struct device_node *np,
struct property *prop, struct property *old_prop)
diff --git a/drivers/of/overlay.c b/drivers/of/overlay.c
index 54e5af9d7..82250815e 100644
--- a/drivers/of/overlay.c
+++ b/drivers/of/overlay.c
@@ -379,9 +379,9 @@ int of_overlay_create(struct device_node *tree)
}
/* apply the changeset */
- err = of_changeset_apply(&ov->cset);
+ err = __of_changeset_apply(&ov->cset);
if (err) {
- pr_err("%s: of_changeset_apply() failed for tree@%s\n",
+ pr_err("%s: __of_changeset_apply() failed for tree@%s\n",
__func__, tree->full_name);
goto err_revert_overlay;
}
@@ -511,7 +511,7 @@ int of_overlay_destroy(int id)
list_del(&ov->node);
- of_changeset_revert(&ov->cset);
+ __of_changeset_revert(&ov->cset);
of_free_overlay_info(ov);
idr_remove(&ov_idr, id);
of_changeset_destroy(&ov->cset);
@@ -542,7 +542,7 @@ int of_overlay_destroy_all(void)
/* the tail of list is guaranteed to be safe to remove */
list_for_each_entry_safe_reverse(ov, ovn, &ov_list, node) {
list_del(&ov->node);
- of_changeset_revert(&ov->cset);
+ __of_changeset_revert(&ov->cset);
of_free_overlay_info(ov);
idr_remove(&ov_idr, ov->id);
kfree(ov);
diff --git a/drivers/of/platform.c b/drivers/of/platform.c
index af9834361..8d103e496 100644
--- a/drivers/of/platform.c
+++ b/drivers/of/platform.c
@@ -31,6 +31,7 @@ const struct of_device_id of_default_bus_match_table[] = {
#endif /* CONFIG_ARM_AMBA */
{} /* Empty terminated list */
};
+EXPORT_SYMBOL(of_default_bus_match_table);
static int of_dev_node_match(struct device *dev, void *data)
{
diff --git a/drivers/of/unittest.c b/drivers/of/unittest.c
index e16ea5717..979b6e415 100644
--- a/drivers/of/unittest.c
+++ b/drivers/of/unittest.c
@@ -530,18 +530,14 @@ static void __init of_unittest_changeset(void)
unittest(!of_changeset_add_property(&chgset, parent, ppadd), "fail add prop\n");
unittest(!of_changeset_update_property(&chgset, parent, ppupdate), "fail update prop\n");
unittest(!of_changeset_remove_property(&chgset, parent, ppremove), "fail remove prop\n");
- mutex_lock(&of_mutex);
unittest(!of_changeset_apply(&chgset), "apply failed\n");
- mutex_unlock(&of_mutex);
/* Make sure node names are constructed correctly */
unittest((np = of_find_node_by_path("/testcase-data/changeset/n2/n21")),
"'%s' not added\n", n21->full_name);
of_node_put(np);
- mutex_lock(&of_mutex);
unittest(!of_changeset_revert(&chgset), "revert failed\n");
- mutex_unlock(&of_mutex);
of_changeset_destroy(&chgset);
#endif
@@ -757,6 +753,11 @@ static void __init of_unittest_match_node(void)
}
}
+static struct resource test_bus_res = {
+ .start = 0xfffffff8,
+ .end = 0xfffffff9,
+ .flags = IORESOURCE_MEM,
+};
static const struct platform_device_info test_bus_info = {
.name = "unittest-bus",
};
@@ -800,6 +801,15 @@ static void __init of_unittest_platform_populate(void)
return;
test_bus->dev.of_node = np;
+ /*
+ * Add a dummy resource to the test bus node after it is
+ * registered to catch problems with un-inserted resources. The
+ * DT code doesn't insert the resources, and it has caused the
+ * kernel to oops in the past. This makes sure the same bug
+ * doesn't crop up again.
+ */
+ platform_device_add_resources(test_bus, &test_bus_res, 1);
+
of_platform_populate(np, match, NULL, &test_bus->dev);
for_each_child_of_node(np, child) {
for_each_child_of_node(child, grandchild)