This completely removes the usage of PHYLIB from DSA, namely for the aforementioned switch ports which used to drive a software PHY manually using genphy operations.
For these ports, the newly introduced phylink_create_raw API must be used, and the callbacks are received through a notifier block registered per dsa_port, but otherwise the implementation is fairly straightforward, and the handling of the regular vs raw PHYLINK instances is common from the perspective of the driver. What changes for drivers: The .adjust_link callback is no longer called for the fixed-link CPU/DSA ports and drivers must migrate to standard PHYLINK operations (e.g. .phylink_mac_config). The reason why we can't do anything for them is because PHYLINK does not wrap the fixed link state behind a phydev object, so we cannot wrap .phylink_mac_config into .adjust_link unless we fabricate a phy_device structure. Signed-off-by: Ioana Ciornei <ioana.cior...@nxp.com> --- include/net/dsa.h | 3 + net/dsa/port.c | 137 +++++++++++++++++++++------------------------- 2 files changed, 64 insertions(+), 76 deletions(-) diff --git a/include/net/dsa.h b/include/net/dsa.h index 685294817712..87616ff00919 100644 --- a/include/net/dsa.h +++ b/include/net/dsa.h @@ -212,6 +212,9 @@ struct dsa_port { * Original copy of the master netdev net_device_ops */ const struct net_device_ops *orig_ndo_ops; + + /* Listener for phylink events on ports with no netdev */ + struct notifier_block nb; }; struct dsa_switch { diff --git a/net/dsa/port.c b/net/dsa/port.c index d0f955e8b731..31bd07dd42db 100644 --- a/net/dsa/port.c +++ b/net/dsa/port.c @@ -13,6 +13,7 @@ #include <linux/if_bridge.h> #include <linux/notifier.h> #include <linux/of_mdio.h> +#include <linux/phylink.h> #include <linux/of_net.h> #include "dsa_priv.h" @@ -511,104 +512,88 @@ void dsa_port_phylink_fixed_state(struct dsa_port *dp, { struct dsa_switch *ds = dp->ds; - /* No need to check that this operation is valid, the callback would - * not be called if it was not. + /* We need to check that the callback exists because phylink raw will + * send PHYLINK_GET_FIXED_STATE events without an explicit register. */ - ds->ops->phylink_fixed_state(ds, dp->index, state); + if (ds->ops->phylink_fixed_state) + ds->ops->phylink_fixed_state(ds, dp->index, state); } EXPORT_SYMBOL(dsa_port_phylink_fixed_state); -static int dsa_port_setup_phy_of(struct dsa_port *dp, bool enable) -{ - struct dsa_switch *ds = dp->ds; - struct phy_device *phydev; - int port = dp->index; - int err = 0; - - phydev = dsa_port_get_phy_device(dp); - if (!phydev) - return 0; - - if (IS_ERR(phydev)) - return PTR_ERR(phydev); - - if (enable) { - err = genphy_config_init(phydev); - if (err < 0) - goto err_put_dev; - - err = genphy_resume(phydev); - if (err < 0) - goto err_put_dev; - - err = genphy_read_status(phydev); - if (err < 0) - goto err_put_dev; - } else { - err = genphy_suspend(phydev); - if (err < 0) - goto err_put_dev; +static int dsa_cpu_port_event(struct notifier_block *nb, + unsigned long event, void *ptr) +{ + struct phylink_notifier_info *info = ptr; + struct dsa_port *dp = container_of(nb, struct dsa_port, nb); + + switch (event) { + case PHYLINK_VALIDATE: + dsa_port_phylink_validate(dp, info->supported, info->state); + break; + case PHYLINK_MAC_AN_RESTART: + dsa_port_phylink_mac_an_restart(dp); + break; + case PHYLINK_MAC_CONFIG: + dsa_port_phylink_mac_config(dp, info->link_an_mode, + info->state); + break; + case PHYLINK_MAC_LINK_DOWN: + dsa_port_phylink_mac_link_down(dp, info->link_an_mode, + info->interface, info->phydev); + break; + case PHYLINK_MAC_LINK_UP: + dsa_port_phylink_mac_link_up(dp, info->link_an_mode, + info->interface, info->phydev); + break; + case PHYLINK_GET_FIXED_STATE: + dsa_port_phylink_fixed_state(dp, info->state); + break; + default: + return NOTIFY_OK; } - if (ds->ops->adjust_link) - ds->ops->adjust_link(ds, port, phydev); - - dev_dbg(ds->dev, "enabled port's phy: %s", phydev_name(phydev)); - -err_put_dev: - put_device(&phydev->mdio.dev); - return err; + return NOTIFY_DONE; } -static int dsa_port_fixed_link_register_of(struct dsa_port *dp) +int dsa_port_link_register_of(struct dsa_port *dp) { - struct device_node *dn = dp->dn; - struct dsa_switch *ds = dp->ds; - struct phy_device *phydev; - int port = dp->index; - int mode; - int err; + struct device_node *port_dn = dp->dn; + int mode, err; - err = of_phy_register_fixed_link(dn); - if (err) { - dev_err(ds->dev, - "failed to register the fixed PHY of port %d\n", - port); - return err; - } - - phydev = of_phy_find_device(dn); - - mode = of_get_phy_mode(dn); + mode = of_get_phy_mode(port_dn); if (mode < 0) mode = PHY_INTERFACE_MODE_NA; - phydev->interface = mode; - genphy_config_init(phydev); - genphy_read_status(phydev); + dp->nb.notifier_call = dsa_cpu_port_event; + dp->pl = phylink_create_raw(&dp->nb, of_fwnode_handle(port_dn), mode); + if (IS_ERR(dp->pl)) { + pr_err("error creating PHYLINK: %ld\n", PTR_ERR(dp->pl)); + return PTR_ERR(dp->pl); + } - if (ds->ops->adjust_link) - ds->ops->adjust_link(ds, port, phydev); + err = phylink_of_phy_connect(dp->pl, port_dn, 0); + if (err) { + pr_err("could not attach to PHY: %d\n", err); + goto err_phy_connect; + } - put_device(&phydev->mdio.dev); + rtnl_lock(); + phylink_start(dp->pl); + rtnl_unlock(); return 0; -} -int dsa_port_link_register_of(struct dsa_port *dp) -{ - if (of_phy_is_fixed_link(dp->dn)) - return dsa_port_fixed_link_register_of(dp); - else - return dsa_port_setup_phy_of(dp, true); +err_phy_connect: + phylink_destroy(dp->pl); + return err; } void dsa_port_link_unregister_of(struct dsa_port *dp) { - if (of_phy_is_fixed_link(dp->dn)) - of_phy_deregister_fixed_link(dp->dn); - else - dsa_port_setup_phy_of(dp, false); + rtnl_lock(); + phylink_disconnect_phy(dp->pl); + rtnl_unlock(); + phylink_destroy(dp->pl); } int dsa_port_get_phy_strings(struct dsa_port *dp, uint8_t *data) -- 2.21.0