summaryrefslogtreecommitdiffstats
path: root/drivers/of/of_mdio.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/of/of_mdio.c')
-rw-r--r--drivers/of/of_mdio.c133
1 files changed, 111 insertions, 22 deletions
diff --git a/drivers/of/of_mdio.c b/drivers/of/of_mdio.c
index a87a868fed64..39c4be41ef83 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);
@@ -238,7 +327,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 +357,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;
}