Re: [RFC 2/2] net: emac: add support for device-tree based PHY discovery and setup
From: Florian Fainelli <hidden>
Date: 2017-02-05 22:44:54
Also in:
linux-devicetree
Le 02/05/17 à 14:25, Christian Lamparter a écrit :
From: Christian Lamparter <redacted> This patch adds glue-code that allows the EMAC driver to interface with the existing dt-supported PHYs in drivers/net/phy. Because currently, the emac driver maintains a small library of supported phys for in a private phy.c file located in the drivers directory. The support is limited to mostly single ethernet transceiver like the: CIS8201, BCM5248, ET1011C, Marvell 88E1111 and 88E1112, AR8035. However, routers like the Netgear WNDR4700 and Cisco Meraki MX60(W) have a 5-port switch (QCA8327N) attached to the MDIO of the EMAC. The switch chip has already a proper phy-driver (qca8k) that uses the generic phy library.
Technically, it's a mdio_device in the upstream kernel that registers a switch with DSA (and a PHY device in the OpenWrt/LEDE downstream kernel). If your goal is to specifically support that device you should consider making the EMAC interface with a fixed link PHY to properly initialize the EMAC <=> CPU port of the switch link, and then declare the qca8k device as a child MDIO device (not a PHY), similar to what is done in arch/arm/boot/dts/vf610-zii-dev-rev-b.dts for instance.
quoted hunk
Signed-off-by: Christian Lamparter <chunkeey-gM/Ye1E23mwN+BqQ9rBEUg@public.gmane.org> --- drivers/net/ethernet/ibm/emac/core.c | 188 +++++++++++++++++++++++++++++++++++ drivers/net/ethernet/ibm/emac/core.h | 4 + 2 files changed, 192 insertions(+)diff --git a/drivers/net/ethernet/ibm/emac/core.c b/drivers/net/ethernet/ibm/emac/core.c index 6ead2335a169..ea9234cdb227 100644 --- a/drivers/net/ethernet/ibm/emac/core.c +++ b/drivers/net/ethernet/ibm/emac/core.c@@ -42,6 +42,7 @@ #include <linux/of_address.h> #include <linux/of_irq.h> #include <linux/of_net.h> +#include <linux/of_mdio.h> #include <linux/slab.h> #include <asm/processor.h>@@ -2420,6 +2421,179 @@ static int emac_read_uint_prop(struct device_node *np, const char *name, return 0; } +static void emac_adjust_link(struct net_device *ndev) +{ + struct emac_instance *dev = netdev_priv(ndev); + struct phy_device *phy = dev->phy_dev; + + mutex_lock(&dev->link_lock); + dev->phy.autoneg = phy->autoneg; + dev->phy.speed = phy->speed; + dev->phy.duplex = phy->duplex; + dev->phy.pause = phy->pause; + dev->phy.asym_pause = phy->asym_pause; + dev->phy.advertising = phy->advertising; + mutex_unlock(&dev->link_lock);
PHYLIB already executes grabbing the phy device's mutex, is this really needed here?
+}
+
+static int emac_mii_bus_read(struct mii_bus *bus, int addr, int regnum)
+{
+ return emac_mdio_read(bus->priv, addr, regnum);
+}
+
+static int emac_mii_bus_write(struct mii_bus *bus, int addr, int regnum,
+ u16 val)
+{
+ emac_mdio_write(bus->priv, addr, regnum, val);
+ return 0;
+}
+
+static int emac_mii_bus_reset(struct mii_bus *bus)
+{
+ struct emac_instance *dev = netdev_priv(bus->priv);
+
+ emac_mii_reset_phy(&dev->phy);This seems wrong, emac_mii_reset_phy() does a BMCR software reset, which PHYLIB is already going to do (phy_init_hw), yet you do this here at the MDIO bus level towards a specify PHY, whereas this should be affecting the MDIO bus itself (and/or *all* PHY child devices for quirks).
+ return 0;
+}
+
+static int emac_mdio_probe(struct emac_instance *dev)
+{
+ struct device_node *mii_np;
+ struct mii_bus *bus;
+ int res;
+
+ bus = mdiobus_alloc();
+ if (!bus)
+ return -ENOMEM;
+
+ mii_np = of_get_child_by_name(dev->ofdev->dev.of_node, "mdio");
+ if (!mii_np) {
+ dev_err(&dev->ndev->dev, "no mdio definition found.");
+ return -ENODEV;
+ }
+
+ if (!of_device_is_available(mii_np))
+ return 0;
+
+ bus->priv = dev->ndev;
+ bus->parent = dev->ndev->dev.parent;
+ bus->name = "emac_mdio";
+ bus->read = &emac_mii_bus_read;
+ bus->write = &emac_mii_bus_write;
+ bus->reset = &emac_mii_bus_reset;
+
+ snprintf(bus->id, MII_BUS_ID_SIZE, "%s", bus->name);You should pick a more unique name here, if you ever have a second instance it would just clash with the previous one.
+
+ res = of_mdiobus_register(bus, mii_np);
+ if (res) {
+ dev_err(&dev->ndev->dev, "cannot register MDIO bus %s\n",
+ bus->name);
+ mdiobus_free(bus);
+ }
+
+ dev->mii_bus = bus;
+ return res;
+}
+
+static void emac_mdio_cleanup(struct emac_instance *dev)
+{
+ if (dev->mii_bus) {
+ if (dev->mii_bus->state == MDIOBUS_REGISTERED)
+ mdiobus_unregister(dev->mii_bus);If you need to make that kind of check, why not separate how the mdio bus structure's lifecycle is managed? This seems to be avoiding to hit the BUG_ON() in mdiobus_unregister..
+ mdiobus_free(dev->mii_bus);
+ dev->mii_bus = NULL;
+ kfree(dev->phy.def);
+ }
+}
+
+static int stub_setup_aneg(struct mii_phy *phy, u32 advertise)
+{
+ return 0;
+}
+
+static int stub_setup_forced(struct mii_phy *phy, int speed, int fd)
+{
+ return 0;
+}
+
+static int stub_poll_link(struct mii_phy *phy)
+{
+ struct net_device *ndev = phy->dev;
+ struct emac_instance *dev = netdev_priv(ndev);
+
+ return dev->opened;
+}
+
+static int stub_read_link(struct mii_phy *phy)
+{
+ struct net_device *ndev = phy->dev;
+ struct emac_instance *dev = netdev_priv(ndev);
+
+ phy_start(dev->phy_dev);Are you sure the read_link function is supposed to start the PHY state machine? Either the name is confusing, or it's not the right thing to do here.
+ return 0;
+}
+
+static const struct mii_phy_ops emac_stub_phy_ops = {
+ .setup_aneg = stub_setup_aneg,
+ .setup_forced = stub_setup_forced,
+ .poll_link = stub_poll_link,
+ .read_link = stub_read_link,
+};
+
+static int emac_probe_dt_phy(struct emac_instance *dev)
+{
+ struct device_node *np = dev->ofdev->dev.of_node;
+ struct device_node *phy_handle;
+ struct net_device *ndev = dev->ndev;
+ int res;
+
+ phy_handle = of_parse_phandle(np, "phy-handle", 0);
+
+ if (phy_handle) {
+ res = emac_mdio_probe(dev);
+ if (res)
+ goto err_cleanup;
+
+ dev->phy.def = kzalloc(sizeof(*dev->phy.def), GFP_KERNEL);
+ if (!dev->phy.def) {
+ res = -ENOMEM;
+ goto err_cleanup;
+ }
+
+ dev->phy_dev = of_phy_connect(ndev, phy_handle,
+ &emac_adjust_link, 0,
+ PHY_INTERFACE_MODE_RGMII);You should call of_get_phy_mode() since there should be a proper "phy-mode" or "phy-connection-type" property describing how it's connected to the EMAC.
quoted hunk
+ if (!dev->phy_dev) { + res = -ENODEV; + goto err_cleanup; + } + + of_node_put(phy_handle); + dev->phy.def->phy_id = dev->phy_dev->drv->phy_id; + dev->phy.def->phy_id_mask = dev->phy_dev->drv->phy_id_mask; + dev->phy.def->name = dev->phy_dev->drv->name; + dev->phy.def->ops = &emac_stub_phy_ops; + /* Disable any PHY features not supported by the platform */ + dev->phy.def->features = dev->phy_dev->drv->features & + ~dev->phy_feat_exc; + dev->phy.features = dev->phy.def->features; + dev->phy.address = dev->phy_dev->mdio.addr; + dev->phy.mode = dev->phy_dev->interface; + return 0; + } + + /* if the device tree didn't specifiy the the phy, then + * we simply fallback to the old emac_phy.c probe code + * for compatibility reasons. + */ + return 1; + + err_cleanup: + of_node_put(phy_handle); + kfree(dev->phy.def); + return res; +} + static int emac_init_phy(struct emac_instance *dev) { struct device_node *np = dev->ofdev->dev.of_node;@@ -2490,6 +2664,13 @@ static int emac_init_phy(struct emac_instance *dev) emac_configure(dev); + if (emac_has_feature(dev, EMAC_FTR_HAS_RGMII)) { + int res = emac_probe_dt_phy(dev); + + if (res <= 0) + return res; + }
Why is this limited to EMAC_FTR_HAS_RGMII here?
quoted hunk
+ if (dev->phy_address != 0xffffffff) phy_map = ~(1 << dev->phy_address);@@ -2938,6 +3119,8 @@ static int emac_probe(struct platform_device *ofdev) /* I have a bad feeling about this ... */ err_detach_tah: + emac_mdio_cleanup(dev); + if (emac_has_feature(dev, EMAC_FTR_HAS_TAH)) tah_detach(dev->tah_dev, dev->tah_port); err_detach_rgmii:@@ -2988,6 +3171,11 @@ static int emac_remove(struct platform_device *ofdev) if (emac_has_feature(dev, EMAC_FTR_HAS_ZMII)) zmii_detach(dev->zmii_dev, dev->zmii_port); + if (dev->phy_dev) + phy_disconnect(dev->phy_dev); + + emac_mdio_cleanup(dev); + busy_phy_map &= ~(1 << dev->phy.address); DBG(dev, "busy_phy_map now %#x" NL, busy_phy_map);diff --git a/drivers/net/ethernet/ibm/emac/core.h b/drivers/net/ethernet/ibm/emac/core.h index 93ae11494810..0710a6685489 100644 --- a/drivers/net/ethernet/ibm/emac/core.h +++ b/drivers/net/ethernet/ibm/emac/core.h@@ -199,6 +199,10 @@ struct emac_instance { struct emac_instance *mdio_instance; struct mutex mdio_lock; + /* Device-tree based phy configuration */ + struct mii_bus *mii_bus; + struct phy_device *phy_dev; + /* ZMII infos if any */ u32 zmii_ph; u32 zmii_port;
-- Florian -- To unsubscribe from this list: send the line "unsubscribe devicetree" in the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org More majordomo info at http://vger.kernel.org/majordomo-info.html