/* This function is provided to cope with the possible failures of this phy * during aneg process. When aneg fails, the PHY reports that aneg is done * but the value found in MII_LPA is wrong: * - Early failures: MII_LPA is just 0x0001. if MII_EXPANSION reports that * the link partner (LP) supports aneg but the LP never acked our base * code word, it is likely that we never sent it to begin with. * - Late failures: MII_LPA is filled with a value which seems to make sense * but it actually is not what the LP is advertising. It seems that we * can detect this using a magic bit in the WOL bank (reg 12 - bit 12). * If this particular bit is not set when aneg is reported being done, * it means MII_LPA is likely to be wrong. * * In both case, forcing a restart of the aneg process solve the problem. * When this failure happens, the first retry is usually successful but, * in some cases, it may take up to 6 retries to get a decent result */ static int meson_gxl_read_status(struct phy_device *phydev) { int ret, wol, lpa, exp; if (phydev->autoneg == AUTONEG_ENABLE) { ret = genphy_aneg_done(phydev); if (ret < 0) return ret; else if (!ret) goto read_status_continue; /* Aneg is done, let's check everything is fine */ wol = meson_gxl_read_reg(phydev, BANK_WOL, LPI_STATUS); if (wol < 0) return wol; lpa = phy_read(phydev, MII_LPA); if (lpa < 0) return lpa; exp = phy_read(phydev, MII_EXPANSION); if (exp < 0) return exp; if (!(wol & LPI_STATUS_RSV12) || ((exp & EXPANSION_NWAY) && !(lpa & LPA_LPACK))) { /* Looks like aneg failed after all */ phydev_dbg(phydev, "LPA corruption - aneg restart\n"); return genphy_restart_aneg(phydev); } } read_status_continue: return genphy_read_status(phydev); }
/* * The LAN8710/LAN8720 requires a minimum of 2 link pulses within 64ms of each * other in order to set the ENERGYON bit and exit EDPD mode. If a link partner * does send the pulses within this interval, the PHY will remained powered * down. * * This workaround will manually toggle the PHY on/off upon calls to read_status * in order to generate link test pulses if the link is down. If a link partner * is present, it will respond to the pulses, which will cause the ENERGYON bit * to be set and will cause the EDPD mode to be exited. */ static int lan87xx_read_status(struct phy_device *phydev) { int err = genphy_read_status(phydev); if (!phydev->link) { /* Disable EDPD to wake up PHY */ int rc = phy_read(phydev, MII_LAN83C185_CTRL_STATUS); if (rc < 0) return rc; rc = phy_write(phydev, MII_LAN83C185_CTRL_STATUS, rc & ~MII_LAN83C185_EDPWRDOWN); if (rc < 0) return rc; /* Sleep 64 ms to allow ~5 link test pulses to be sent */ msleep(64); /* Re-enable EDPD */ rc = phy_read(phydev, MII_LAN83C185_CTRL_STATUS); if (rc < 0) return rc; rc = phy_write(phydev, MII_LAN83C185_CTRL_STATUS, rc | MII_LAN83C185_EDPWRDOWN); if (rc < 0) return rc; } return err; }
static int ip175c_read_status(struct phy_device *phydev) { if (phydev->addr == 4) /* WAN port */ genphy_read_status(phydev); else /* Don't need to read status for switch ports */ phydev->irq = PHY_IGNORE_INTERRUPT; return 0; }
static int ksz9031RNX_read_status(struct phy_device *phydev){ int val = 0; int err = genphy_read_status(phydev); if((!phydev->link)&& (cnt <= 1)){ cnt++; phy_write(phydev,0x4,0x5e1); val=phy_read(phydev,0x0); phy_write(phydev,0x0,val| 1<<9); } return err; }
static int bcm5482_read_status(struct phy_device *phydev) { int err; err = genphy_read_status(phydev); if (phydev->dev_flags & PHY_BCM_FLAGS_MODE_1000BX) { /* * Only link status matters for 1000Base-X mode, so force * 1000 Mbit/s full-duplex status */ if (phydev->link) { phydev->speed = SPEED_1000; phydev->duplex = DUPLEX_FULL; } } return err; }
/* * The LAN87xx suffers from rare absence of the ENERGYON-bit when Ethernet cable * plugs in while LAN87xx is in Energy Detect Power-Down mode. This leads to * unstable detection of plugging in Ethernet cable. * This workaround disables Energy Detect Power-Down mode and waiting for * response on link pulses to detect presence of plugged Ethernet cable. * The Energy Detect Power-Down mode is enabled again in the end of procedure to * save approximately 220 mW of power if cable is unplugged. */ static int lan87xx_read_status(struct phy_device *phydev) { struct smsc_phy_priv *priv = phydev->priv; int err = genphy_read_status(phydev); if (!phydev->link && priv->energy_enable) { int i; /* Disable EDPD to wake up PHY */ int rc = phy_read(phydev, MII_LAN83C185_CTRL_STATUS); if (rc < 0) return rc; rc = phy_write(phydev, MII_LAN83C185_CTRL_STATUS, rc & ~MII_LAN83C185_EDPWRDOWN); if (rc < 0) return rc; /* Wait max 640 ms to detect energy */ for (i = 0; i < 64; i++) { /* Sleep to allow link test pulses to be sent */ msleep(10); rc = phy_read(phydev, MII_LAN83C185_CTRL_STATUS); if (rc < 0) return rc; if (rc & MII_LAN83C185_ENERGYON) break; } /* Re-enable EDPD */ rc = phy_read(phydev, MII_LAN83C185_CTRL_STATUS); if (rc < 0) return rc; rc = phy_write(phydev, MII_LAN83C185_CTRL_STATUS, rc | MII_LAN83C185_EDPWRDOWN); if (rc < 0) return rc; } return err; }
static int ksz9031_read_status(struct phy_device *phydev) { int err; int regval; err = genphy_read_status(phydev); if (err) return err; /* Make sure the PHY is not broken. Read idle error count, * and reset the PHY if it is maxed out. */ regval = phy_read(phydev, MII_STAT1000); if ((regval & 0xff) == 0xff) { phy_init_hw(phydev); phydev->link = 0; } return 0; }
/* * This workaround will manually toggle the PHY on/off upon calls to read_status * in order to generate link test pulses if the link is down. If a link partner * is present, it will respond to the pulses, which will cause the ENERGYON bit * to be set and will cause the EDPD mode to be exited. */ static int internal_read_status(struct phy_device *phydev) { int err = genphy_read_status(phydev); if(phydev->speed == SPEED_10){ init_internal_phy_10B(phydev); } if(phydev->speed == SPEED_100){ init_internal_phy_100B(phydev); } if (AUTONEG_ENABLE == phydev->autoneg){ NULL; } else if (!phydev->link) { /* Disable EDPD to wake up PHY */ int rc = phy_read(phydev, MII_INTERNAL_CTRL_STATUS); if (rc < 0) return rc; rc = phy_write(phydev, MII_INTERNAL_CTRL_STATUS, rc & ~MII_INTERNAL_EDPWRDOWN); if (rc < 0) return rc; /* Sleep 64 ms to allow ~5 link test pulses to be sent */ msleep(64); /* Re-enable EDPD */ rc = phy_read(phydev, MII_INTERNAL_CTRL_STATUS); if (rc < 0) return rc; rc = phy_write(phydev, MII_INTERNAL_CTRL_STATUS, rc | MII_INTERNAL_EDPWRDOWN); if (rc < 0) return rc; } return err; }
/** *hns_nic_get_link - get current link status *@net_dev: net_device *retuen 0 - success , negative --fail */ static u32 hns_nic_get_link(struct net_device *net_dev) { struct hns_nic_priv *priv = netdev_priv(net_dev); u32 link_stat = priv->link; struct hnae_handle *h; h = priv->ae_handle; if (net_dev->phydev) { if (!genphy_read_status(net_dev->phydev)) link_stat = net_dev->phydev->link; else link_stat = 0; } if (h->dev && h->dev->ops && h->dev->ops->get_status) link_stat = link_stat && h->dev->ops->get_status(h); else link_stat = 0; return link_stat; }
static int et1011c_read_status(struct phy_device *phydev) { int ret; u32 val; static int speed; ret = genphy_read_status(phydev); if (speed != phydev->speed) { speed = phydev->speed; val = phy_read(phydev, ET1011C_STATUS_REG); if ((val & ET1011C_SPEED_MASK) == ET1011C_GIGABIT_SPEED) { val = phy_read(phydev, ET1011C_CONFIG_REG); val &= ~ET1011C_TX_FIFO_MASK; phy_write(phydev, ET1011C_CONFIG_REG, val\ | ET1011C_GMII_INTERFACE\ | ET1011C_SYS_CLK_EN\ | ET1011C_TX_FIFO_DEPTH_16); } } return ret; }
/** * @brief Get Settings * @param[in] pointer to struct net_device. * @param[in] pointer to struct ethtool_cmd. */ static int32_t nss_gmac_get_settings(struct net_device *netdev, struct ethtool_cmd *ecmd) { struct nss_gmac_dev *gmacdev = NULL; struct phy_device *phydev = NULL; uint16_t phyreg; gmacdev = (struct nss_gmac_dev *)netdev_priv(netdev); BUG_ON(gmacdev == NULL); /* * If the speed/duplex for this GMAC is forced and we are not * polling for link state changes, return the values as specified by * platform. This will be true for GMACs connected to switch, and * interfaces that do not use a PHY. */ if (!test_bit(__NSS_GMAC_LINKPOLL, &gmacdev->flags)) { if (gmacdev->forced_speed != SPEED_UNKNOWN) { ethtool_cmd_speed_set(ecmd, gmacdev->forced_speed); ecmd->duplex = gmacdev->forced_duplex; ecmd->mdio_support = 0; ecmd->lp_advertising = 0; return 0; } else { /* Non-link polled interfaced must have a forced * speed/duplex */ return -EIO; } } phydev = gmacdev->phydev; /* update PHY status */ if (phydev->is_c45 == true) { ecmd->mdio_support = ETH_MDIO_SUPPORTS_C45; } else { if (genphy_read_status(phydev) != 0) { return -EIO; } ecmd->mdio_support = ETH_MDIO_SUPPORTS_C22; } /* Populate capabilities advertised by self */ ecmd->advertising = phydev->advertising; ecmd->autoneg = phydev->autoneg; if (gmacdev->link_state == LINKDOWN) { ethtool_cmd_speed_set(ecmd, SPEED_UNKNOWN); ecmd->duplex = DUPLEX_UNKNOWN; } else { ethtool_cmd_speed_set(ecmd, phydev->speed); ecmd->duplex = phydev->duplex; } ecmd->port = PORT_TP; ecmd->phy_address = gmacdev->phy_base; ecmd->transceiver = XCVR_EXTERNAL; /* Populate supported capabilities */ ecmd->supported = phydev->supported; if (phydev->is_c45 == true) return 0; /* Populate capabilities advertised by link partner */ phyreg = nss_gmac_mii_rd_reg(gmacdev, gmacdev->phy_base, MII_LPA); if (phyreg & LPA_10HALF) ecmd->lp_advertising |= ADVERTISED_10baseT_Half; if (phyreg & LPA_10FULL) ecmd->lp_advertising |= ADVERTISED_10baseT_Full; if (phyreg & LPA_100HALF) ecmd->lp_advertising |= ADVERTISED_100baseT_Half; if (phyreg & LPA_100FULL) ecmd->lp_advertising |= ADVERTISED_100baseT_Full; if (phyreg & LPA_PAUSE_CAP) ecmd->lp_advertising |= ADVERTISED_Pause; if (phyreg & LPA_PAUSE_ASYM) ecmd->lp_advertising |= ADVERTISED_Asym_Pause; phyreg = nss_gmac_mii_rd_reg(gmacdev, gmacdev->phy_base, MII_STAT1000); if (phyreg & LPA_1000HALF) ecmd->lp_advertising |= ADVERTISED_1000baseT_Half; if (phyreg & LPA_1000FULL) ecmd->lp_advertising |= ADVERTISED_1000baseT_Full; return 0; }
int dsa_port_bridge_join(struct dsa_port *dp, struct net_device *br) { struct dsa_notifier_bridge_info info = { .sw_index = dp->ds->index, .port = dp->index, .br = br, }; int err; /* Here the port is already bridged. Reflect the current configuration * so that drivers can program their chips accordingly. */ dp->bridge_dev = br; err = dsa_port_notify(dp, DSA_NOTIFIER_BRIDGE_JOIN, &info); /* The bridging is rolled back on error */ if (err) dp->bridge_dev = NULL; return err; } void dsa_port_bridge_leave(struct dsa_port *dp, struct net_device *br) { struct dsa_notifier_bridge_info info = { .sw_index = dp->ds->index, .port = dp->index, .br = br, }; int err; /* Here the port is already unbridged. Reflect the current configuration * so that drivers can program their chips accordingly. */ dp->bridge_dev = NULL; err = dsa_port_notify(dp, DSA_NOTIFIER_BRIDGE_LEAVE, &info); if (err) pr_err("DSA: failed to notify DSA_NOTIFIER_BRIDGE_LEAVE\n"); /* Port left the bridge, put in BR_STATE_DISABLED by the bridge layer, * so allow it to be in BR_STATE_FORWARDING to be kept functional */ dsa_port_set_state_now(dp, BR_STATE_FORWARDING); } int dsa_port_vlan_filtering(struct dsa_port *dp, bool vlan_filtering, struct switchdev_trans *trans) { struct dsa_switch *ds = dp->ds; /* bridge skips -EOPNOTSUPP, so skip the prepare phase */ if (switchdev_trans_ph_prepare(trans)) return 0; if (ds->ops->port_vlan_filtering) return ds->ops->port_vlan_filtering(ds, dp->index, vlan_filtering); return 0; } int dsa_port_ageing_time(struct dsa_port *dp, clock_t ageing_clock, struct switchdev_trans *trans) { unsigned long ageing_jiffies = clock_t_to_jiffies(ageing_clock); unsigned int ageing_time = jiffies_to_msecs(ageing_jiffies); struct dsa_notifier_ageing_time_info info = { .ageing_time = ageing_time, .trans = trans, }; if (switchdev_trans_ph_prepare(trans)) return dsa_port_notify(dp, DSA_NOTIFIER_AGEING_TIME, &info); dp->ageing_time = ageing_time; return dsa_port_notify(dp, DSA_NOTIFIER_AGEING_TIME, &info); } int dsa_port_fdb_add(struct dsa_port *dp, const unsigned char *addr, u16 vid) { struct dsa_notifier_fdb_info info = { .sw_index = dp->ds->index, .port = dp->index, .addr = addr, .vid = vid, }; return dsa_port_notify(dp, DSA_NOTIFIER_FDB_ADD, &info); } int dsa_port_fdb_del(struct dsa_port *dp, const unsigned char *addr, u16 vid) { struct dsa_notifier_fdb_info info = { .sw_index = dp->ds->index, .port = dp->index, .addr = addr, .vid = vid, }; return dsa_port_notify(dp, DSA_NOTIFIER_FDB_DEL, &info); } int dsa_port_fdb_dump(struct dsa_port *dp, dsa_fdb_dump_cb_t *cb, void *data) { struct dsa_switch *ds = dp->ds; int port = dp->index; if (!ds->ops->port_fdb_dump) return -EOPNOTSUPP; return ds->ops->port_fdb_dump(ds, port, cb, data); } int dsa_port_mdb_add(const struct dsa_port *dp, const struct switchdev_obj_port_mdb *mdb, struct switchdev_trans *trans) { struct dsa_notifier_mdb_info info = { .sw_index = dp->ds->index, .port = dp->index, .trans = trans, .mdb = mdb, }; return dsa_port_notify(dp, DSA_NOTIFIER_MDB_ADD, &info); } int dsa_port_mdb_del(const struct dsa_port *dp, const struct switchdev_obj_port_mdb *mdb) { struct dsa_notifier_mdb_info info = { .sw_index = dp->ds->index, .port = dp->index, .mdb = mdb, }; return dsa_port_notify(dp, DSA_NOTIFIER_MDB_DEL, &info); } int dsa_port_vlan_add(struct dsa_port *dp, const struct switchdev_obj_port_vlan *vlan, struct switchdev_trans *trans) { struct dsa_notifier_vlan_info info = { .sw_index = dp->ds->index, .port = dp->index, .trans = trans, .vlan = vlan, }; if (netif_is_bridge_master(vlan->obj.orig_dev)) return -EOPNOTSUPP; if (br_vlan_enabled(dp->bridge_dev)) return dsa_port_notify(dp, DSA_NOTIFIER_VLAN_ADD, &info); return 0; } int dsa_port_vlan_del(struct dsa_port *dp, const struct switchdev_obj_port_vlan *vlan) { struct dsa_notifier_vlan_info info = { .sw_index = dp->ds->index, .port = dp->index, .vlan = vlan, }; if (netif_is_bridge_master(vlan->obj.orig_dev)) return -EOPNOTSUPP; if (br_vlan_enabled(dp->bridge_dev)) return dsa_port_notify(dp, DSA_NOTIFIER_VLAN_DEL, &info); return 0; } static struct phy_device *dsa_port_get_phy_device(struct dsa_port *dp) { struct device_node *phy_dn; struct phy_device *phydev; phy_dn = of_parse_phandle(dp->dn, "phy-handle", 0); if (!phy_dn) return NULL; phydev = of_phy_find_device(phy_dn); if (!phydev) { of_node_put(phy_dn); return ERR_PTR(-EPROBE_DEFER); } return phydev; } 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; } 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; } static int dsa_port_fixed_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; 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); if (mode < 0) mode = PHY_INTERFACE_MODE_NA; phydev->interface = mode; genphy_config_init(phydev); genphy_read_status(phydev); if (ds->ops->adjust_link) ds->ops->adjust_link(ds, port, phydev); put_device(&phydev->mdio.dev); 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); } 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); } int dsa_port_get_phy_strings(struct dsa_port *dp, uint8_t *data) { struct phy_device *phydev; int ret = -EOPNOTSUPP; if (of_phy_is_fixed_link(dp->dn)) return ret; phydev = dsa_port_get_phy_device(dp); if (IS_ERR_OR_NULL(phydev)) return ret; ret = phy_ethtool_get_strings(phydev, data); put_device(&phydev->mdio.dev); return ret; } EXPORT_SYMBOL_GPL(dsa_port_get_phy_strings); int dsa_port_get_ethtool_phy_stats(struct dsa_port *dp, uint64_t *data) { struct phy_device *phydev; int ret = -EOPNOTSUPP; if (of_phy_is_fixed_link(dp->dn)) return ret; phydev = dsa_port_get_phy_device(dp); if (IS_ERR_OR_NULL(phydev)) return ret; ret = phy_ethtool_get_stats(phydev, NULL, data); put_device(&phydev->mdio.dev); return ret; } EXPORT_SYMBOL_GPL(dsa_port_get_ethtool_phy_stats); int dsa_port_get_phy_sset_count(struct dsa_port *dp) { struct phy_device *phydev; int ret = -EOPNOTSUPP; if (of_phy_is_fixed_link(dp->dn)) return ret; phydev = dsa_port_get_phy_device(dp); if (IS_ERR_OR_NULL(phydev)) return ret; ret = phy_ethtool_get_sset_count(phydev); put_device(&phydev->mdio.dev); return ret; } EXPORT_SYMBOL_GPL(dsa_port_get_phy_sset_count);