Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 57 additions & 1 deletion drivers/net/ethernet/freescale/fec_main.c
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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,
Expand Down