diff --git a/drivers/net/ethernet/freescale/fec_main.c b/drivers/net/ethernet/freescale/fec_main.c index e7eb6f598f58..cb886aa138d9 100644 --- a/drivers/net/ethernet/freescale/fec_main.c +++ b/drivers/net/ethernet/freescale/fec_main.c @@ -4024,6 +4024,62 @@ static int fec_hwtstamp_set(struct net_device *ndev, return fec_ptp_set(ndev, config, extack); } +/* Provide direct MDIO bus register access via SIOCGMIIREG/SIOCSMIIREG. + * + * The generic phy_do_ioctl_running() path only works when a phydev is + * attached to this net_device and routes register access through the PHY + * state machine (with side effects such as autoneg restarts). On boards + * where the FEC is wired to a DSA switch (e.g. SJA1105) over a fixed-link, + * there is no attached phydev, so the generic path returns -ENODEV and the + * MDIO bus is unreachable from userspace. + * + * Intercept the two MII register commands and service them directly against + * fep->mii_bus so bring-up/diagnostic tooling can reach any device address + * on the bus without side effects. A Clause-45-encoded phy_id (MII_ADDR_C45) + * is split into prtad/devad and routed through the C45 bus accessors, mirroring + * phy_mii_ioctl() so C45 PHYs on the FEC bus remain reachable. All other + * commands fall through to the standard handler. (Reworked from a patch shipped + * with the Audinate EVK.) + */ +static int fec_enet_ioctl(struct net_device *ndev, struct ifreq *rq, int cmd) +{ + struct fec_enet_private *fep = netdev_priv(ndev); + struct mii_ioctl_data *mii = if_mii(rq); + int prtad, devad, ret; + bool is_c45; + + switch (cmd) { + case SIOCGMIIREG: + is_c45 = mdio_phy_id_is_c45(mii->phy_id); + if (is_c45) { + prtad = mdio_phy_id_prtad(mii->phy_id); + devad = mdio_phy_id_devad(mii->phy_id); + ret = mdiobus_c45_read_nested(fep->mii_bus, prtad, devad, + mii->reg_num); + } else { + ret = mdiobus_read_nested(fep->mii_bus, mii->phy_id, + mii->reg_num); + } + if (ret < 0) + return ret; + mii->val_out = ret; + return 0; + case SIOCSMIIREG: + is_c45 = mdio_phy_id_is_c45(mii->phy_id); + if (is_c45) { + prtad = mdio_phy_id_prtad(mii->phy_id); + devad = mdio_phy_id_devad(mii->phy_id); + return mdiobus_c45_write_nested(fep->mii_bus, prtad, + devad, mii->reg_num, + mii->val_in); + } + return mdiobus_write_nested(fep->mii_bus, mii->phy_id, + mii->reg_num, mii->val_in); + default: + return phy_do_ioctl_running(ndev, rq, cmd); + } +} + static const struct net_device_ops fec_netdev_ops = { .ndo_open = fec_enet_open, .ndo_stop = fec_enet_close, @@ -4033,7 +4089,7 @@ static const struct net_device_ops fec_netdev_ops = { .ndo_validate_addr = eth_validate_addr, .ndo_tx_timeout = fec_timeout, .ndo_set_mac_address = fec_set_mac_address, - .ndo_eth_ioctl = phy_do_ioctl_running, + .ndo_eth_ioctl = fec_enet_ioctl, .ndo_set_features = fec_set_features, .ndo_bpf = fec_enet_bpf, .ndo_xdp_xmit = fec_enet_xdp_xmit,