staticconstchar *phylink_pcs_mode_str(unsignedint mode)
{ if (!mode) return"none";
if (mode & PHYLINK_PCS_NEG_OUTBAND) return"outband";
if (mode & PHYLINK_PCS_NEG_INBAND) { if (mode & PHYLINK_PCS_NEG_ENABLED) return"inband,an-enabled"; else return"inband,an-disabled";
}
return"unknown";
}
staticunsignedint phylink_interface_signal_rate(phy_interface_t interface)
{ switch (interface) { case PHY_INTERFACE_MODE_SGMII: case PHY_INTERFACE_MODE_1000BASEX: /* 1.25Mbd */ return1250; case PHY_INTERFACE_MODE_2500BASEX: /* 3.125Mbd */ return3125; case PHY_INTERFACE_MODE_5GBASER: /* 5.15625Mbd */ return5156; case PHY_INTERFACE_MODE_10GBASER: /* 10.3125Mbd */ return10313; default: return0;
}
}
/** *phylink_interface_max_speed()-getthemaximumspeedofaphyinterface *@interface:phyinterfacemodedefinedby&typedefphy_interface_t * *Determinethemaximumspeedofaphyinterface.Thisisintendedtohelp *determinethecorrectspeedtopasstotheMACwhenthephyisperforming *ratematching. * *Return:Themaximumspeedof@interface
*/ staticint phylink_interface_max_speed(phy_interface_t interface)
{ switch (interface) { case PHY_INTERFACE_MODE_100BASEX: case PHY_INTERFACE_MODE_REVRMII: case PHY_INTERFACE_MODE_RMII: case PHY_INTERFACE_MODE_SMII: case PHY_INTERFACE_MODE_REVMII: case PHY_INTERFACE_MODE_MII: case PHY_INTERFACE_MODE_MIILITE: return SPEED_100;
case PHY_INTERFACE_MODE_TBI: case PHY_INTERFACE_MODE_MOCA: case PHY_INTERFACE_MODE_RTBI: case PHY_INTERFACE_MODE_1000BASEX: case PHY_INTERFACE_MODE_1000BASEKX: case PHY_INTERFACE_MODE_TRGMII: case PHY_INTERFACE_MODE_RGMII_TXID: case PHY_INTERFACE_MODE_RGMII_RXID: case PHY_INTERFACE_MODE_RGMII_ID: case PHY_INTERFACE_MODE_RGMII: case PHY_INTERFACE_MODE_PSGMII: case PHY_INTERFACE_MODE_QSGMII: case PHY_INTERFACE_MODE_QUSGMII: case PHY_INTERFACE_MODE_SGMII: case PHY_INTERFACE_MODE_GMII: return SPEED_1000;
case PHY_INTERFACE_MODE_2500BASEX: case PHY_INTERFACE_MODE_10G_QXGMII: return SPEED_2500;
case PHY_INTERFACE_MODE_5GBASER: return SPEED_5000;
case PHY_INTERFACE_MODE_XGMII: case PHY_INTERFACE_MODE_RXAUI: case PHY_INTERFACE_MODE_XAUI: case PHY_INTERFACE_MODE_10GBASER: case PHY_INTERFACE_MODE_10GKR: case PHY_INTERFACE_MODE_USXGMII: return SPEED_10000;
case PHY_INTERFACE_MODE_25GBASER: return SPEED_25000;
case PHY_INTERFACE_MODE_XLGMII: return SPEED_40000;
case PHY_INTERFACE_MODE_50GBASER: case PHY_INTERFACE_MODE_LAUI: return SPEED_50000;
case PHY_INTERFACE_MODE_100GBASEP: return SPEED_100000;
case PHY_INTERFACE_MODE_INTERNAL: case PHY_INTERFACE_MODE_NA: case PHY_INTERFACE_MODE_MAX: /* No idea! Garbage in, unknown out */ return SPEED_UNKNOWN;
}
/* If we get here, someone forgot to add an interface mode above */
WARN_ON_ONCE(1); return SPEED_UNKNOWN;
}
for (i = 0; i < ARRAY_SIZE(phylink_caps_params); i++) { if (speed == phylink_caps_params[i].speed &&
duplex == phylink_caps_params[i].duplex) return phylink_caps_params[i].mask;
}
switch (rate_matching) { case RATE_MATCH_OPEN_LOOP: /* TODO */
fallthrough; case RATE_MATCH_NONE:
matched_caps = 0; break; case RATE_MATCH_PAUSE: { /* The MAC must support asymmetric pause towards the local *deviceforthis.Wecouldallowjustsymmetricpause,but *thenwemighthavetorenegotiateifthelinkpartner *doesn'tsupportpause.Thisisbecausethere'snowayto *acceptpauseframeswithouttransmittingthemifweonly *supportsymmetricpause.
*/ if (!(mac_capabilities & MAC_SYM_PAUSE) ||
!(mac_capabilities & MAC_ASYM_PAUSE)) break;
/* We can't adapt if the MAC doesn't support the interface's *maxspeedatfullduplex.
*/ if (mac_capabilities &
phylink_cap_from_speed_duplex(max_speed, DUPLEX_FULL))
matched_caps = GENMASK(__fls(caps), __fls(MAC_10HD)); break;
} case RATE_MATCH_CRS: /* The MAC must support half duplex at the interface's max *speed.
*/ if (mac_capabilities &
phylink_cap_from_speed_duplex(max_speed, DUPLEX_HALF)) {
matched_caps = GENMASK(__fls(caps), __fls(MAC_10HD));
matched_caps &= mac_capabilities;
} break;
}
/* Get the PCS for this interface mode */ if (pl->mac_ops->mac_select_pcs) {
pcs = pl->mac_ops->mac_select_pcs(pl->config, state->interface); if (IS_ERR(pcs)) return PTR_ERR(pcs);
}
if (pcs) { /* The PCS, if present, must be setup before phylink_create() *hasbeencalled.Iftheopsisnotinitialised,printan *errorandbacktraceratherthanoopsingthekernel.
*/ if (!pcs->ops) {
phylink_err(pl, "interface %s: uninitialised PCS\n",
phy_modes(state->interface));
dump_stack(); return -EINVAL;
}
/* Ensure that this PCS supports the interface which the MAC *returneditfor.ItisanerrorfortheMACtoreturnaPCS *thatdoesnotsupporttheinterfacemode.
*/ if (!phy_interface_empty(pcs->supported_interfaces) &&
!test_bit(state->interface, pcs->supported_interfaces)) {
phylink_err(pl, "MAC returned PCS which does not support %s\n",
phy_modes(state->interface)); return -EINVAL;
}
/* Validate the link parameters with the PCS */ if (pcs->ops->pcs_validate) {
ret = pcs->ops->pcs_validate(pcs, supported, state); if (ret < 0 || phylink_is_empty_linkmode(supported)) return -EINVAL;
/* Ensure the advertising mask is a subset of the *supportedmask.
*/
linkmode_and(state->advertising, state->advertising,
supported);
}
}
/* Then validate the link parameters with the MAC */ if (pl->mac_ops->mac_get_caps)
capabilities = pl->mac_ops->mac_get_caps(pl->config,
state->interface); else
capabilities = pl->config->mac_capabilities;
if (fwnode_property_read_bool(fixed_node, "full-duplex"))
pl->link_config.duplex = DUPLEX_FULL;
/* We treat the "pause" and "asym-pause" terminology as *definingthelinkpartner'sability.
*/ if (fwnode_property_read_bool(fixed_node, "pause"))
__set_bit(ETHTOOL_LINK_MODE_Pause_BIT,
pl->link_config.lp_advertising); if (fwnode_property_read_bool(fixed_node, "asym-pause"))
__set_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT,
pl->link_config.lp_advertising);
switch (pl->link_config.interface) { case PHY_INTERFACE_MODE_SGMII: case PHY_INTERFACE_MODE_PSGMII: case PHY_INTERFACE_MODE_QSGMII: case PHY_INTERFACE_MODE_QUSGMII: case PHY_INTERFACE_MODE_RGMII: case PHY_INTERFACE_MODE_RGMII_ID: case PHY_INTERFACE_MODE_RGMII_RXID: case PHY_INTERFACE_MODE_RGMII_TXID: case PHY_INTERFACE_MODE_RTBI: case PHY_INTERFACE_MODE_1000BASEX: case PHY_INTERFACE_MODE_2500BASEX: case PHY_INTERFACE_MODE_5GBASER: case PHY_INTERFACE_MODE_25GBASER: case PHY_INTERFACE_MODE_USXGMII: case PHY_INTERFACE_MODE_10G_QXGMII: case PHY_INTERFACE_MODE_10GKR: case PHY_INTERFACE_MODE_10GBASER: case PHY_INTERFACE_MODE_XLGMII: case PHY_INTERFACE_MODE_50GBASER: case PHY_INTERFACE_MODE_LAUI: case PHY_INTERFACE_MODE_100GBASEP:
caps = ~(MAC_SYM_PAUSE | MAC_ASYM_PAUSE);
caps = phylink_get_capabilities(pl->link_config.interface, caps,
RATE_MATCH_NONE);
phylink_caps_to_linkmodes(pl->supported, caps); break;
default:
phylink_err(pl, "incorrect link mode %s for in-band status\n",
phy_modes(pl->link_config.interface)); return -EINVAL;
}
if (phylink_validate(pl, pl->supported, &pl->link_config)) {
phylink_err(pl, "failed to validate link configuration for in-band status\n"); return -EINVAL;
}
}
return0;
}
staticvoid phylink_apply_manual_flow(struct phylink *pl, struct phylink_link_state *state)
{ /* If autoneg is disabled, pause AN is also disabled */ if (!linkmode_test_bit(ETHTOOL_LINK_MODE_Autoneg_BIT,
state->advertising))
state->pause &= ~MLO_PAUSE_AN;
/* Manual configuration of pause modes */ if (!(pl->link_config.pause & MLO_PAUSE_AN))
state->pause = pl->link_config.pause;
}
/* Query inband for a specific interface mode, asking the MAC for the *PCSwhichwillbeusedtohandletheinterfacemode.
*/ staticunsignedint phylink_inband_caps(struct phylink *pl,
phy_interface_t interface)
{ struct phylink_pcs *pcs;
if (!pl->mac_ops->mac_select_pcs) return0;
pcs = pl->mac_ops->mac_select_pcs(pl->config, interface); if (!pcs) return0;
staticenum inband_type phylink_get_inband_type(phy_interface_t interface)
{ switch (interface) { case PHY_INTERFACE_MODE_SGMII: case PHY_INTERFACE_MODE_QSGMII: case PHY_INTERFACE_MODE_QUSGMII: case PHY_INTERFACE_MODE_USXGMII: case PHY_INTERFACE_MODE_10G_QXGMII: /* These protocols are designed for use with a PHY which *communicatesitsnegotiationresultbacktotheMACvia *inbandcommunication.Note:thereexistPHYsthatrun *withSGMIIbutdonotsendtheinbanddata.
*/ return INBAND_CISCO_SGMII;
case PHY_INTERFACE_MODE_1000BASEX: case PHY_INTERFACE_MODE_2500BASEX: /* 1000base-X is designed for use media-side for Fibre *connections,andthustheAutonegbitneedstobe *takenintoaccount.Wealsodothisfor2500base-X *aswell,butdriversmaynotsupportthis,somay *needtooverridethis.
*/ return INBAND_BASEX;
/* Modern PCS-based method; update the advert at the PCS, and *restartnegotiationifthepcs_config()helperindicatesthat *theprogrammedadvertisementhaschanged.
*/
ret = phylink_pcs_config(pl->pcs, pl->pcs_neg_mode, &pl->link_config,
!!(pl->link_config.pause & MLO_PAUSE_AN)); if (ret < 0) return ret;
staticvoid phylink_activate_lpi(struct phylink *pl)
{ int err;
if (!test_bit(pl->cur_interface, pl->config->lpi_interfaces)) {
phylink_dbg(pl, "MAC does not support LPI with %s\n",
phy_modes(pl->cur_interface)); return;
}
switch (link_state.rate_matching) { case RATE_MATCH_PAUSE: /* The PHY is doing rate matchion from the media rate (in *thelink_state)totheinterfacespeed,andwillsend *pauseframestotheMACtolimititstransmissionspeed.
*/
speed = phylink_interface_max_speed(link_state.interface);
duplex = DUPLEX_FULL;
rx_pause = true; break;
case RATE_MATCH_CRS: /* The PHY is doing rate matchion from the media rate (in *thelink_state)totheinterfacespeed,andwillcause *collisionstotheMACtolimititstransmissionspeed.
*/
speed = phylink_interface_max_speed(link_state.interface);
duplex = DUPLEX_HALF; break;
}
if (pl->mac_supports_eee && pl->phy_enable_tx_lpi)
phylink_activate_lpi(pl);
if (ndev)
netif_carrier_on(ndev);
phylink_info(pl, "Link is Up - %s/%s - flow control %s\n",
phy_speed_to_str(link_state.speed),
phy_duplex_to_str(link_state.duplex),
phylink_pause_to_str(link_state.pause));
}
/* The PCS may have a latching link-fail indicator. If the link *wasup,bringthelinkdownandre-triggertheresolve. *Otherwise,re-readthePCSstatetogetthecurrentstatus *ofthelink.
*/ if (!link_state.link) { if (cur_link_state)
retrigger = true; else
phylink_mac_pcs_get_state(pl, &link_state);
}
/* If we have a phy, the "up" state is the union of both the *PHYandtheMAC
*/ if (phy)
link_state.link &= pl->phy_state.link;
/* Only update if the PHY link is up */ if (phy && pl->phy_state.link) { /* If the interface has changed, force a link down *eventifthelinkisn'talreadydown,andre-resolve.
*/ if (link_state.interface != pl->phy_state.interface) {
retrigger = true;
link_state.link = false;
}
link_state.interface = pl->phy_state.interface;
/* If we are doing rate matching, then the link *speed/duplexcomesfromthePHY
*/ if (pl->phy_state.rate_matching) {
link_state.rate_matching =
pl->phy_state.rate_matching;
link_state.speed = pl->phy_state.speed;
link_state.duplex = pl->phy_state.duplex;
}
/* If we have a PHY, we need to update with the PHY *flowcontrolbits.
*/
link_state.pause = pl->phy_state.pause;
mac_config = true;
}
}
if (pl->act_link_an_mode != MLO_AN_FIXED)
phylink_apply_manual_flow(pl, &link_state);
if (mac_config) { if (link_state.interface != pl->link_config.interface) { /* The interface has changed, force the link down and *thenreconfigure.
*/ if (cur_link_state) {
phylink_link_down(pl);
cur_link_state = false;
}
phylink_major_config(pl, false, &link_state);
pl->link_config.interface = link_state.interface;
}
}
/* If configuration of the interface failed, force the link down *untilwegetasuccessfulconfiguration.
*/ if (pl->major_config_failed)
link_state.link = false;
if (link_state.link != cur_link_state) {
pl->old_link_state = link_state.link; if (!link_state.link)
phylink_link_down(pl); else
phylink_link_up(pl, link_state);
} if (!link_state.link && retrigger) {
pl->link_failed = false;
queue_work(system_power_efficient_wq, &pl->resolve);
}
mutex_unlock(&pl->state_mutex); if (phy)
mutex_unlock(&phy->lock);
mutex_unlock(&pl->phydev_mutex);
}
staticvoid phylink_run_resolve(struct phylink *pl)
{ if (!pl->phylink_disable_state)
queue_work(system_power_efficient_wq, &pl->resolve);
}
staticvoid phylink_run_resolve_and_disable(struct phylink *pl, int bit)
{ unsignedlong state = pl->phylink_disable_state;
/* Get the LPI state from phylib */
pl->phy_enable_tx_lpi = phydev->enable_tx_lpi;
pl->mac_tx_lpi_timer = phydev->eee_cfg.tx_lpi_timer;
mutex_unlock(&pl->state_mutex);
/* If the PHY provides a bitmap of the interfaces it will be using *dependingonthenegotiatedmediaspeeds,usethistovalidate *whichethtoollinkmodescanbeused.
*/ if (!phy_interface_empty(phy->possible_interfaces)) { /* We only care about the union of the PHY's interfaces and *thosewhichthehostsupports.
*/
phy_interface_and(interfaces, phy->possible_interfaces,
pl->config->supported_interfaces);
if (phy_interface_empty(interfaces)) {
phylink_err(pl, "PHY has no common interfaces\n"); return -EINVAL;
}
if (phy_on_sfp(phy)) { /* If the PHY is on a SFP, limit the interfaces to *thosethatcanbeusedwithaSFPmodule.
*/
phy_interface_and(interfaces, interfaces,
phylink_sfp_interfaces);
if (phy_interface_empty(interfaces)) {
phylink_err(pl, "SFP PHY's possible interfaces becomes empty\n"); return -EINVAL;
}
}
ret = phylink_validate_phy(pl, phy, supported, &config); if (ret) {
phylink_warn(pl, "validation of %s with support %*pb and advertisement %*pb failed: %pe\n",
phy_modes(config.interface),
__ETHTOOL_LINK_MODE_MASK_NBITS, phy->supported,
__ETHTOOL_LINK_MODE_MASK_NBITS, config.advertising,
ERR_PTR(ret)); return ret;
}
/* Restrict the phy advertisement according to the MAC support. */
linkmode_copy(phy->advertising, config.advertising);
/* If the MAC supports phylink managed EEE, restrict the EEE *advertisementaccordingtotheMAC'sLPIcapabilities.
*/ if (pl->mac_supports_eee) { /* If EEE is enabled, then we need to call phy_support_eee() *toensurethattheadvertisingmaskisappropriatelyset. *ThisalsoenablesEEEatthePHY.
*/ if (pl->eee_cfg.eee_enabled)
phy_support_eee(phy);
if (pl->config->mac_managed_pm)
phy->mac_managed_pm = true;
/* Allow the MAC to stop its clock if the PHY has the capability */
pl->mac_tx_clk_stop = phy_eee_tx_clock_stop_capable(phy) > 0;
if (pl->mac_supports_eee_ops) { /* Explicitly configure whether the PHY is allowed to stop it's *receiveclock.
*/
ret = phy_eee_rx_clock_stop(phy,
pl->config->eee_rx_clk_stop_enable); if (ret == -EOPNOTSUPP)
ret = 0;
}
if (ret == 0 && phy_interrupt_is_valid(phy))
phy_request_interrupt(phy);
/* Fixed links and 802.3z are handled without needing a PHY */ if (pl->cfg_link_an_mode == MLO_AN_FIXED ||
(pl->cfg_link_an_mode == MLO_AN_INBAND &&
phy_interface_mode_is_8023z(pl->link_interface))) return0;
phy_fwnode = fwnode_get_phy_node(fwnode); if (IS_ERR(phy_fwnode)) { if (pl->cfg_link_an_mode == MLO_AN_PHY) return -ENODEV; return0;
}
phy_dev = fwnode_phy_find_device(phy_fwnode); /* We're done with the phy_node handle */
fwnode_handle_put(phy_fwnode); if (!phy_dev) return -ENODEV;
/* Use PHY device/driver interface */ if (pl->link_interface == PHY_INTERFACE_MODE_NA) {
pl->link_interface = phy_dev->interface;
pl->link_config.interface = pl->link_interface;
}
if (pl->config->mac_requires_rxc)
flags |= PHY_F_RXC_ALWAYS_ON;
ret = phy_attach_direct(pl->netdev, phy_dev, flags,
pl->link_interface);
phy_device_free(phy_dev); if (ret) return ret;
ret = phylink_bringup_phy(pl, phy_dev, pl->link_config.interface); if (ret)
phy_detach(phy_dev);
phylink_info(pl, "configuring for %s/%s link mode\n",
phylink_an_mode_str(pl->req_link_an_mode),
phy_modes(pl->link_config.interface));
/* Always set the carrier off */ if (pl->netdev)
netif_carrier_off(pl->netdev);
pl->pcs_state = PCS_STATE_STARTING;
/* Apply the link configuration to the MAC when starting. This allows *afixed-linktostartwiththecorrectparameters,andalso *ensuresthatwesettheappropriateadvertisementforSerdeslinks. * *Restartautonegotiationifusing802.3ztoensurethatthelink *parametersareproperlynegotiated.ThisisnecessaryforDSA *switchesusing802.3znegotiationtoensuretheyseeourmodes.
*/
phylink_mac_initial_config(pl, true);
if (pl->cfg_link_an_mode == MLO_AN_FIXED && pl->link_gpio) { int irq = gpiod_to_irq(pl->link_gpio);
if (irq > 0) { if (!request_irq(irq, phylink_link_handler,
IRQF_TRIGGER_RISING |
IRQF_TRIGGER_FALLING, "netdev link", pl))
pl->link_irq = irq; else
irq = 0;
} if (irq <= 0)
poll = true;
}
if (pl->cfg_link_an_mode == MLO_AN_FIXED)
poll |= pl->config->poll_fixed_state;
if (poll)
mod_timer(&pl->link_poll, jiffies + HZ); if (pl->phydev)
phy_start(pl->phydev); if (pl->sfp_bus)
sfp_upstream_start(pl->sfp_bus);
}
EXPORT_SYMBOL_GPL(phylink_start);
if (pl->mac_rx_clk_stop_blocked == U8_MAX) {
phylink_warn(pl, "%s called too many times - ignoring\n",
__func__);
dump_stack(); return;
}
/* Disable PHY receive clock stop if this is the first time this *functionhasbeencalledandclock-stopwaspreviouslyenabled.
*/ if (pl->mac_rx_clk_stop_blocked++ == 0 &&
pl->mac_supports_eee_ops && pl->phydev &&
pl->config->eee_rx_clk_stop_enable)
phy_eee_rx_clock_stop(pl->phydev, false);
}
EXPORT_SYMBOL_GPL(phylink_rx_clk_stop_block);
if (mac_wol && (!pl->netdev || pl->netdev->ethtool->wol_enabled)) { /* Wake-on-Lan enabled, MAC handling */
mutex_lock(&pl->state_mutex);
/* Stop the resolver bringing the link up */
__set_bit(PHYLINK_DISABLE_MAC_WOL, &pl->phylink_disable_state);
pl->suspend_link_up = phylink_link_is_up(pl); if (pl->suspend_link_up) { /* Disable the carrier, to prevent transmit timeouts, *butonewouldhopeallpacketshavebeensent.This *alsomeansphylink_resolve()willdonothing.
*/ if (pl->netdev)
netif_carrier_off(pl->netdev);
pl->old_link_state = false;
}
/* We do not call mac_link_down() here as we want the *linktoremainuptoreceivetheWoLpackets.
*/
mutex_unlock(&pl->state_mutex);
} else {
phylink_stop(pl);
}
}
EXPORT_SYMBOL_GPL(phylink_suspend);
if (test_bit(PHYLINK_DISABLE_MAC_WOL, &pl->phylink_disable_state)) { /* Wake-on-Lan enabled, MAC handling */
if (pl->suspend_link_up) { /* Call mac_link_down() so we keep the overall state *balanced.Dothisunderthestate_mutexlockfor *consistency.Thiswillcausea"LinkDown"message *tobeprintedduringresume,whichisharmless- *thetruelinkstatewillbeprintedwhenweruna *resolve.
*/
mutex_lock(&pl->state_mutex);
phylink_link_down(pl);
mutex_unlock(&pl->state_mutex);
}
/* Re-apply the link parameters so that all the settings get *restoredtotheMAC.
*/
phylink_mac_initial_config(pl, true);
/* Re-enable and re-resolve the link parameters */
phylink_enable_and_run_resolve(pl, PHYLINK_DISABLE_MAC_WOL);
} else {
phylink_start(pl);
}
}
EXPORT_SYMBOL_GPL(phylink_resume);
for (i = 0; i < ARRAY_SIZE(phylink_sfp_interface_preference); i++) {
interface = phylink_sfp_interface_preference[i]; if (!test_bit(interface, pl->sfp_interfaces)) continue;
/* The logic here is: if speed == max_speed, then we've found *thebestinterface.Otherwisewefindtheinterfacethat *canjustsupporttherequestedspeed.
*/ if (max_speed >= speed)
best_interface = interface;
if (max_speed <= speed) break;
}
if (best_interface == PHY_INTERFACE_MODE_NA)
phylink_err(pl, "selection of interface failed, speed %u\n",
speed);
switch (pl->act_link_an_mode) { case MLO_AN_FIXED: /* We are using fixed settings. Report these as the *currentlinksettings-andnotethatthesealso *representthesupportedspeeds/duplex/pausemodes.
*/
phylink_get_fixed_state(pl, &link_state);
phylink_get_ksettings(&link_state, kset); break;
case MLO_AN_INBAND: /* If there is a phy attached, then use the reported *settingsfromthephywithnomodification.
*/ if (pl->phydev) break;
phylink_mac_pcs_get_state(pl, &link_state);
/* The MAC is reporting the link results from its own PCS *layerviain-bandstatus.Reporttheseasthecurrent *linksettings.
*/
phylink_get_ksettings(&link_state, kset); break;
}
/* If the PCS doesn't implement inband support, be permissive. */ if (!inband) returntrue;
if (linkmode_test_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, adv))
mask = LINK_INBAND_ENABLE; else
mask = LINK_INBAND_DISABLE;
/* Check whether the PCS implements the required mode */ return !!(inband & mask);
}
/**
* phylink_ethtool_ksettings_set() - set the link settings
* @pl: a pointerto a &struct phylink returned from phylink_create()
* @kset: a pointerto a &struct ethtool_link_ksettings for the desired modes
*/
int phylink_ethtool_ksettings_set(struct phylink *pl,
const struct ethtool_link_ksettings *kset)
{
__ETHTOOL_DECLARE_LINK_MODE_MASK(support);
const struct link_capabilities *c;
struct phylink_link_state config;
ASSERT_RTNL();
if (pl->phydev) {
struct ethtool_link_ksettings phy_kset = *kset;
/* We can rely on phylib for this update; we also do not need
* to update the pl->link_config settings:
* - the configuration returned via ksettings_get() will come
* from phylib whenever a PHY is present.
* - link_config.interface will be updated by the PHY calling
* back via phylink_phy_change() and a subsequent resolve.
* - initial link configurationfor PHY mode comes from the
* last phy state updated via phylink_phy_change().
* - otherconfiguration changes (e.g. pause modes) are
* performed directly via phylib.
* - ifin in-band modewith a PHY, the link configuration
* is passed on the link from the PHY, andallof
* link_config.{speed,duplex,an_enabled,pause} arenot used.
* - the only possible use would be link_config.advertising
* pause modes whenin1000base-X modewith a PHY, but in
* the presence of a PHY, this should not be changed as that
* should be determined from the media side advertisement.
*/ return phy_ethtool_ksettings_set(pl->phydev, &phy_kset);
}
/* FIXME: should we reject autoneg if phy/mac does not support it? */
switch (kset->base.autoneg) {
case AUTONEG_DISABLE:
/* Autonegotiation disabled, select a suitable speed and
* duplex.
*/
c = phy_caps_lookup(kset->base.speed, kset->base.duplex,
pl->supported, false); if (!c) return -EINVAL;
/* If we have a fixed link, refuse to change link parameters.
* If the link parameters match, accept them but do nothing.
*/ if (pl->req_link_an_mode == MLO_AN_FIXED) { if (c->speed != pl->link_config.speed ||
c->duplex != pl->link_config.duplex) return -EINVAL; return0;
}
case AUTONEG_ENABLE:
/* If we have a fixed link, allow autonegotiation (since that
* is our default case) but do not allow the advertisement to
* be changed. If the advertisement matches, simply return.
*/ if (pl->req_link_an_mode == MLO_AN_FIXED) { if (!linkmode_equal(config.advertising,
pl->link_config.advertising)) return -EINVAL; return0;
}
/* We have ruled out the case with a PHY attached, and the
* fixed-link cases. All that isleftare in-band links.
*/
linkmode_mod_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, config.advertising,
kset->base.autoneg == AUTONEG_ENABLE);
/* If this link iswith an SFP, ensure that changes to advertised modes
* also cause the associated interface to be selected such that the
* link can be configured correctly.
*/ if (pl->sfp_bus) { if (kset->base.autoneg == AUTONEG_ENABLE)
config.interface =
phylink_sfp_select_interface(pl,
config.advertising); else
config.interface =
phylink_sfp_select_interface_speed(pl,
config.speed); if (config.interface == PHY_INTERFACE_MODE_NA) return -EINVAL;
/* Revalidate with the selected interface */
linkmode_copy(support, pl->supported); if (phylink_validate(pl, support, &config)) {
phylink_err(pl, "validation of %s/%s with support %*pb failed\n",
phylink_an_mode_str(pl->req_link_an_mode),
phy_modes(config.interface),
__ETHTOOL_LINK_MODE_MASK_NBITS, support); return -EINVAL;
}
} else {
/* Validate without changing the current supported mask. */
linkmode_copy(support, pl->supported); if (phylink_validate(pl, support, &config)) return -EINVAL;
}
/* If autonegotiation is enabled, we must have an advertisement */ if (linkmode_test_bit(ETHTOOL_LINK_MODE_Autoneg_BIT,
config.advertising) &&
phylink_is_empty_linkmode(config.advertising)) return -EINVAL;
/* Validate the autonegotiation state. We don't have a PHY in this
* situation, so the PCS is the media-facing entity.
*/ if (!phylink_validate_pcs_inband_autoneg(pl, config.interface,
config.advertising)) return -EINVAL;
if (pl->link_config.interface != config.interface) {
/* The interface changed, e.g. 1000base-X <-> 2500base-X */
/* We need to force the link down, then change the interface */ if (pl->old_link_state) {
phylink_link_down(pl);
pl->old_link_state = false;
} if (!test_bit(PHYLINK_DISABLE_STOPPED,
&pl->phylink_disable_state))
phylink_major_config(pl, false, &config);
pl->link_config.interface = config.interface;
linkmode_copy(pl->link_config.advertising, config.advertising);
} elseif (!linkmode_equal(pl->link_config.advertising,
config.advertising)) {
linkmode_copy(pl->link_config.advertising, config.advertising);
phylink_change_inband_advert(pl);
}
mutex_unlock(&pl->state_mutex);
/**
* phylink_ethtool_nway_reset() - restart negotiation
* @pl: a pointerto a &struct phylink returned from phylink_create()
*
* Restart negotiation for the phylink instance specified by @pl. This will
* cause any attached phy to restart negotiation with the link partner, and
* if the MAC isin a BaseX mode, the MAC will also be requested to restart
* negotiation.
*
* Returns zeroon success, ornegativeerrorcode.
*/
int phylink_ethtool_nway_reset(struct phylink *pl)
{
int ret = 0;
ASSERT_RTNL();
if (pl->phydev)
ret = phy_restart_aneg(pl->phydev);
phylink_pcs_an_restart(pl);
/**
* phylink_ethtool_get_pauseparam() - get the current pause parameters
* @pl: a pointerto a &struct phylink returned from phylink_create()
* @pause: a pointerto a &struct ethtool_pauseparam
*/
void phylink_ethtool_get_pauseparam(struct phylink *pl,
struct ethtool_pauseparam *pause)
{
ASSERT_RTNL();
/**
* phylink_ethtool_set_pauseparam() - set the current pause parameters
* @pl: a pointerto a &struct phylink returned from phylink_create()
* @pause: a pointerto a &struct ethtool_pauseparam
*/
int phylink_ethtool_set_pauseparam(struct phylink *pl,
struct ethtool_pauseparam *pause)
{
struct phylink_link_state *config = &pl->link_config;
bool manual_changed;
int pause_state;
ASSERT_RTNL();
if (pl->req_link_an_mode == MLO_AN_FIXED) return -EOPNOTSUPP;
if (!phylink_test(pl->supported, Pause) &&
!phylink_test(pl->supported, Asym_Pause)) return -EOPNOTSUPP;
if (!phylink_test(pl->supported, Asym_Pause) &&
pause->rx_pause != pause->tx_pause) return -EINVAL;
pause_state = 0; if (pause->autoneg)
pause_state |= MLO_PAUSE_AN; if (pause->rx_pause)
pause_state |= MLO_PAUSE_RX; if (pause->tx_pause)
pause_state |= MLO_PAUSE_TX;
mutex_lock(&pl->state_mutex);
/*
* See the comments for linkmode_set_pause(), wrt the deficiencies
* with the current implementation. A solution to this issue would
* be:
* ethtool Local device
* rx tx Pause AsymDir
* 0000
* 1011
* 0101
* 1111
* andthenuse the ethtool rx/tx enablement statusto mask the
* rx/tx pause resolution.
*/
linkmode_set_pause(config->advertising, pause->tx_pause,
pause->rx_pause);
/* Update our in-band advertisement, triggering a renegotiation if
* the advertisement changed.
*/ if (!pl->phydev)
phylink_change_inband_advert(pl);
mutex_unlock(&pl->state_mutex);
/* If we have a PHY, a change of the pause frame advertisement will
* cause phylib to renegotiate (if AN is enabled) which will in turn
* call our phylink_phy_change() and trigger a resolve. Note that
* we can't hold our state mutex while calling phy_set_asym_pause().
*/ if (pl->phydev)
phy_set_asym_pause(pl->phydev, pause->rx_pause,
pause->tx_pause);
/* If the manual pause settings changed, make sure we trigger a
* resolve to update their state; we can not guarantee that the
* link will cycle.
*/ if (manual_changed) {
pl->link_failed = true;
phylink_run_resolve(pl);
}
/**
* phylink_get_eee_err() - read the energy efficient ethernet error
* counter
* @pl: a pointerto a &struct phylink returned from phylink_create().
*
* Read the Energy Efficient Ethernet error counter from the PHY associated
* with the phylink instance specified by @pl.
*
* Returns positiveerror counter value, ornegativeerrorcode.
*/
int phylink_get_eee_err(struct phylink *pl)
{
int ret = 0;
ASSERT_RTNL();
if (pl->phydev)
ret = phy_get_eee_err(pl->phydev);
/**
* phylink_ethtool_get_eee() - read the energy efficient ethernet parameters
* @pl: a pointerto a &struct phylink returned from phylink_create()
* @eee: a pointerto a &struct ethtool_keee for the read parameters
*/
int phylink_ethtool_get_eee(struct phylink *pl, struct ethtool_keee *eee)
{
int ret = -EOPNOTSUPP;
ASSERT_RTNL();
if (pl->mac_supports_eee_ops && !pl->mac_supports_eee) return ret;
if (pl->phydev) {
ret = phy_ethtool_get_eee(pl->phydev, eee);
/* Restrict supported linkmode mask */ if (ret == 0 && pl->mac_supports_eee_ops)
linkmode_and(eee->supported, eee->supported,
pl->supported_lpi);
}
/**
* phylink_ethtool_set_eee() - set the energy efficient ethernet parameters
* @pl: a pointerto a &struct phylink returned from phylink_create()
* @eee: a pointerto a &struct ethtool_keee for the desired parameters
*/
int phylink_ethtool_set_eee(struct phylink *pl, struct ethtool_keee *eee)
{
bool mac_eee = pl->mac_supports_eee;
int ret = -EOPNOTSUPP;
/* This emulates MII registers for a fixed-mode phy operating as per the
* passed in state. "aneg" defines if we report negotiation is possible.
*
* FIXME: should deal with negotiation state too.
*/
static int phylink_mii_emul_read(unsigned int reg,
struct phylink_link_state *state)
{
struct fixed_phy_status fs;
unsigned long *lpa = state->lp_advertising;
int val;
static int phylink_phy_write(struct phylink *pl, unsigned int phy_id,
unsigned int reg, unsigned int val)
{
struct phy_device *phydev = pl->phydev;
int prtad, devad;
static int phylink_mii_read(struct phylink *pl, unsigned int phy_id,
unsigned int reg)
{
struct phylink_link_state state;
int val = 0xffff;
switch (pl->act_link_an_mode) {
case MLO_AN_FIXED: if (phy_id == 0) {
phylink_get_fixed_state(pl, &state);
val = phylink_mii_emul_read(reg, &state);
}
break;
case MLO_AN_PHY: return -EOPNOTSUPP;
case MLO_AN_INBAND: if (phy_id == 0) {
phylink_mac_pcs_get_state(pl, &state);
val = phylink_mii_emul_read(reg, &state);
}
break;
}
return val & 0xffff;
}
static int phylink_mii_write(struct phylink *pl, unsigned int phy_id,
unsigned int reg, unsigned int val)
{
switch (pl->act_link_an_mode) {
case MLO_AN_FIXED:
break;
case MLO_AN_PHY: return -EOPNOTSUPP;
case MLO_AN_INBAND:
break;
}
return0;
}
/**
* phylink_mii_ioctl() - generic mii ioctl interface
* @pl: a pointerto a &struct phylink returned from phylink_create()
* @ifr: a pointerto a &struct ifreq for socket ioctls
* @cmd: ioctl cmd to execute
*
* Perform the specified MII ioctl on the PHY attached to the phylink instance
* specified by @pl. Ifno PHY is attached, emulate the presence of the PHY.
*
* Returns: zeroon success ornegativeerrorcode.
*
* %SIOCGMIIPHY:
* read register from the current PHY.
* %SIOCGMIIREG:
* read register from the specified PHY.
* %SIOCSMIIREG:
* set a register on the specified PHY.
*/
int phylink_mii_ioctl(struct phylink *pl, struct ifreq *ifr, int cmd)
{
struct mii_ioctl_data *mii = if_mii(ifr);
int ret;
ASSERT_RTNL();
if (pl->phydev) {
/* PHYs only exist for MLO_AN_PHY and SGMII */
switch (cmd) {
case SIOCGMIIPHY:
mii->phy_id = pl->phydev->mdio.addr;
fallthrough;
case SIOCGMIIREG:
ret = phylink_phy_read(pl, mii->phy_id, mii->reg_num); if (ret >= 0) {
mii->val_out = ret;
ret = 0;
}
break;
case SIOCSMIIREG:
ret = phylink_phy_write(pl, mii->phy_id, mii->reg_num,
mii->val_in);
break;
default:
ret = phy_mii_ioctl(pl->phydev, ifr, cmd);
break;
}
} else {
switch (cmd) {
case SIOCGMIIPHY:
mii->phy_id = 0;
fallthrough;
case SIOCGMIIREG:
ret = phylink_mii_read(pl, mii->phy_id, mii->reg_num); if (ret >= 0) {
mii->val_out = ret;
ret = 0;
}
break;
case SIOCSMIIREG:
ret = phylink_mii_write(pl, mii->phy_id, mii->reg_num,
mii->val_in);
break;
/**
* phylink_speed_down() - set the non-SFP PHY to lowest speed supported by both
* link partners
* @pl: a pointerto a &struct phylink returned from phylink_create()
* @sync: perform action synchronously
*
* If we have a PHY that isnot part of a SFP module, thenset the speed
* as described in the phy_speed_down() function. Please see this function
* for a description of the @sync parameter.
*
* Returns zeroif there isno PHY, otherwise as per phy_speed_down().
*/
int phylink_speed_down(struct phylink *pl, bool sync)
{
int ret = 0;
ASSERT_RTNL();
if (!pl->sfp_bus && pl->phydev)
ret = phy_speed_down(pl->phydev, sync);
/**
* phylink_speed_up() - restore the advertised speeds prior to the callto
* phylink_speed_down()
* @pl: a pointerto a &struct phylink returned from phylink_create()
*
* If we have a PHY that isnot part of a SFP module, then restore the
* PHY speeds as per phy_speed_up().
*
* Returns zeroif there isno PHY, otherwise as per phy_speed_up().
*/
int phylink_speed_up(struct phylink *pl)
{
int ret = 0;
ASSERT_RTNL();
if (!pl->sfp_bus && pl->phydev)
ret = phy_speed_up(pl->phydev);
interface = PHY_INTERFACE_MODE_NA; for (i = 0; i < ARRAY_SIZE(phylink_sfp_interface_preference); i++) if (test_bit(phylink_sfp_interface_preference[i], intf)) {
interface = phylink_sfp_interface_preference[i];
break;
}
phylink_dbg(pl, "requesting link mode %s/%s with support %*pb\n",
phylink_an_mode_str(mode), phy_modes(state->interface),
__ETHTOOL_LINK_MODE_MASK_NBITS, supported);
if (!linkmode_equal(pl->supported, supported)) {
linkmode_copy(pl->supported, supported);
changed = true;
}
if (!linkmode_equal(pl->link_config.advertising, state->advertising)) {
linkmode_copy(pl->link_config.advertising, state->advertising);
changed = true;
}
/* Ignore errors if we're expecting a PHY to attach later */
ret = phylink_validate(pl, support, &config); if (ret) {
phylink_err(pl, "validation with support %*pb failed: %pe\n",
__ETHTOOL_LINK_MODE_MASK_NBITS, support,
ERR_PTR(ret)); return ret;
}
config.interface = phylink_sfp_select_interface(pl, config.advertising); if (config.interface == PHY_INTERFACE_MODE_NA) return -EINVAL;
/* Attach the PHY so that the PHY is present when we do the major
* configuration step.
*/
ret = phylink_attach_phy(pl, phy, config.interface); if (ret < 0) return ret;
/* This will validate the configurationfor us. */
ret = phylink_bringup_phy(pl, phy, config.interface); if (ret < 0) {
phy_detach(phy); return ret;
}
/* Find the union of the supported interfaces by the PCS/MAC and
* the SFP module.
*/
phy_interface_and(pl->sfp_interfaces, pl->config->supported_interfaces,
pl->sfp_interfaces); if (phy_interface_empty(pl->sfp_interfaces)) {
phylink_err(pl, "unsupported SFP module: no common interface modes\n"); return -EINVAL;
}
/* Forall the interfaces that are supported, reduce the sfp_support
* mask to only those link modes that can be supported.
*/
ret = phylink_validate_mask(pl, NULL, pl->sfp_support, &config,
pl->sfp_interfaces); if (ret) {
phylink_err(pl, "unsupported SFP module: validation with support %*pb failed\n",
__ETHTOOL_LINK_MODE_MASK_NBITS, support); return ret;
}
interface = phylink_choose_sfp_interface(pl, pl->sfp_interfaces); if (interface == PHY_INTERFACE_MODE_NA) {
phylink_err(pl, "failed to select SFP interface\n"); return -EINVAL;
}
inband_type = phylink_get_inband_type(interface); if (inband_type == INBAND_NONE) {
/* If this is the sole interface, and there isno inband
* support, clear the advertising mask and Autoneg bit in
* the support mask. Otherwise, just clear the Autoneg bit
* in the advertising mask.
*/ if (phy_interface_weight(pl->sfp_interfaces) == 1) {
linkmode_clear_bit(ETHTOOL_LINK_MODE_Autoneg_BIT,
pl->sfp_support);
linkmode_zero(config.advertising);
} else {
linkmode_clear_bit(ETHTOOL_LINK_MODE_Autoneg_BIT,
config.advertising);
}
}
if (!phylink_validate_pcs_inband_autoneg(pl, interface,
config.advertising)) {
phylink_err(pl, "autoneg setting not compatible with PCS"); return -EINVAL;
}
config.interface = interface;
/* Ignore errors if we're expecting a PHY to attach later */
ret = phylink_validate(pl, support, &config); if (ret) {
phylink_err(pl, "validation with support %*pb failed: %pe\n",
__ETHTOOL_LINK_MODE_MASK_NBITS, support,
ERR_PTR(ret)); return ret;
}
/* If this module may have a PHY connecting later, defer until later */
pl->sfp_may_have_phy = sfp_may_have_phy(pl->sfp_bus, id); if (pl->sfp_may_have_phy) return0;
if (!phy->drv) {
phylink_err(pl, "PHY %s (id 0x%.8lx) has no driver loaded\n",
phydev_name(phy), (unsigned long)phy->phy_id);
phylink_err(pl, "Drivers which handle known common cases: CONFIG_BCM84881_PHY, CONFIG_MARVELL_PHY\n"); return -EINVAL;
}
/*
* This is the new way of dealing with flow control for PHYs,
* as described by Timur Tabi incommit529ed1275263 ("net: phy:
* phy drivers should notset SUPPORTED_[Asym_]Pause") except
* using our validate callto the MAC, we rely upon the MAC
* clearing the bits from both supported and advertising fields.
*/
phy_support_asym_pause(phy);
/* Set the PHY's host supported interfaces */
phy_interface_and(phy->host_interfaces, phylink_sfp_interfaces,
pl->config->supported_interfaces);
/* Do the initialconfiguration */ return phylink_sfp_config_phy(pl, phy);
}
void phylink_resolve_c73(struct phylink_link_state *state)
{
int i;
for (i = 0; i < ARRAY_SIZE(phylink_c73_priority_resolution); i++) {
int bit = phylink_c73_priority_resolution[i].bit; if (linkmode_test_bit(bit, state->advertising) &&
linkmode_test_bit(bit, state->lp_advertising))
break;
}
/**
* phylink_decode_usxgmii_word() - decode the USXGMII word from a MAC PCS
* @state: a pointerto a struct phylink_link_state.
* @lpa: a 16 bit value which stores the USXGMII auto-negotiation word
*
* Helper for MAC PCS supporting the USXGMII protocol and the auto-negotiation
* code word. Decode the USXGMII code word and populate the corresponding fields
* (speed, duplex) into the phylink_link_state structure.
*/
void phylink_decode_usxgmii_word(struct phylink_link_state *state,
uint16_t lpa)
{
switch (lpa & MDIO_USXGMII_SPD_MASK) {
case MDIO_USXGMII_10:
state->speed = SPEED_10;
break;
case MDIO_USXGMII_100:
state->speed = SPEED_100;
break;
case MDIO_USXGMII_1000:
state->speed = SPEED_1000;
break;
case MDIO_USXGMII_2500:
state->speed = SPEED_2500;
break;
case MDIO_USXGMII_5000:
state->speed = SPEED_5000;
break;
case MDIO_USXGMII_10G:
state->speed = SPEED_10000;
break;
default:
state->link = false; return;
}
/**
* phylink_decode_usgmii_word() - decode the USGMII word from a MAC PCS
* @state: a pointerto a struct phylink_link_state.
* @lpa: a 16 bit value which stores the USGMII auto-negotiation word
*
* Helper for MAC PCS supporting the USGMII protocol and the auto-negotiation
* code word. Decode the USGMII code word and populate the corresponding fields
* (speed, duplex) into the phylink_link_state structure. The structure for this
* word is the same as the USXGMII word, except it only supports speeds upto
* 1Gbps.
*/
static void phylink_decode_usgmii_word(struct phylink_link_state *state,
uint16_t lpa)
{
switch (lpa & MDIO_USXGMII_SPD_MASK) {
case MDIO_USXGMII_10:
state->speed = SPEED_10;
break;
case MDIO_USXGMII_100:
state->speed = SPEED_100;
break;
case MDIO_USXGMII_1000:
state->speed = SPEED_1000;
break;
default:
state->link = false; return;
}
/**
* phylink_mii_c22_pcs_decode_state() - Decode MAC PCS state from MII registers
* @state: a pointerto a &struct phylink_link_state.
* @neg_mode: link negotiation mode (PHYLINK_PCS_NEG_xxx)
* @bmsr: The valueof the %MII_BMSR register
* @lpa: The valueof the %MII_LPA register
*
* Helper for MAC PCS supporting the 802.3 clause 22 register setfor
* clause 37 negotiation and/or SGMII control.
*
* Parse the Clause 37or Cisco SGMII link partner negotiation word into
* the phylink @state structure. This is suitable to be used for implementing
* the pcs_get_state() member of the struct phylink_pcs_ops structure if
* accessing @bmsr and @lpa cannot be done with MDIO directly.
*/
void phylink_mii_c22_pcs_decode_state(struct phylink_link_state *state,
unsigned int neg_mode, u16 bmsr, u16 lpa)
{
state->link = !!(bmsr & BMSR_LSTATUS);
state->an_complete = !!(bmsr & BMSR_ANEGCOMPLETE);
/* If the link isdown, the advertisement datais undefined. */ if (!state->link) return;
case PHY_INTERFACE_MODE_SGMII:
case PHY_INTERFACE_MODE_QSGMII: if (neg_mode == PHYLINK_PCS_NEG_INBAND_ENABLED)
phylink_decode_sgmii_word(state, lpa);
break;
case PHY_INTERFACE_MODE_QUSGMII: if (neg_mode == PHYLINK_PCS_NEG_INBAND_ENABLED)
phylink_decode_usgmii_word(state, lpa);
break;
/**
* phylink_mii_c22_pcs_get_state() - read the MAC PCS state
* @pcs: a pointerto a &struct mdio_device.
* @neg_mode: link negotiation mode (PHYLINK_PCS_NEG_xxx)
* @state: a pointerto a &struct phylink_link_state.
*
* Helper for MAC PCS supporting the 802.3 clause 22 register setfor
* clause 37 negotiation and/or SGMII control.
*
* Read the MAC PCS state from the MII device configured in @config and
* parse the Clause 37or Cisco SGMII link partner negotiation word into
* the phylink @state structure. This is suitable to be directly plugged
* into the pcs_get_state() member of the struct phylink_pcs_ops
* structure.
*/
void phylink_mii_c22_pcs_get_state(struct mdio_device *pcs,
unsigned int neg_mode,
struct phylink_link_state *state)
{
int bmsr, lpa;
/**
* phylink_mii_c22_pcs_encode_advertisement() - configure the clause 37 PCS
* advertisement
* @interface: the PHY interface mode being configured
* @advertising: the ethtool advertisement mask
*
* Helper for MAC PCS supporting the 802.3 clause 22 register setfor
* clause 37 negotiation and/or SGMII control.
*
* Encode the clause 37 PCS advertisement as specified by @interface and
* @advertising.
*
* Return: The new valuefor @adv, or ``-EINVAL`` if it should not be changed.
*/
int phylink_mii_c22_pcs_encode_advertisement(phy_interface_t interface,
const unsigned long *advertising)
{
u16 adv;
switch (interface) {
case PHY_INTERFACE_MODE_1000BASEX:
case PHY_INTERFACE_MODE_2500BASEX:
adv = ADVERTISE_1000XFULL; if (linkmode_test_bit(ETHTOOL_LINK_MODE_Pause_BIT,
advertising))
adv |= ADVERTISE_1000XPAUSE; if (linkmode_test_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT,
advertising))
adv |= ADVERTISE_1000XPSE_ASYM; return adv;
case PHY_INTERFACE_MODE_SGMII:
case PHY_INTERFACE_MODE_QSGMII: return0x0001;
default:
/* Nothing to do forother modes */ return -EINVAL;
}
}
EXPORT_SYMBOL_GPL(phylink_mii_c22_pcs_encode_advertisement);
/**
* phylink_mii_c22_pcs_config() - configure clause 22 PCS
* @pcs: a pointerto a &struct mdio_device.
* @interface: the PHY interface mode being configured
* @advertising: the ethtool advertisement mask
* @neg_mode: PCS negotiation mode
*
* Configure a Clause 22 PCS PHY with the appropriate negotiation
* parameters for the @mode, @interface and @advertising parameters.
* Returns negativeerror number on failure, zeroif the advertisement
* has not changed, orpositiveif there is a change.
*/
int phylink_mii_c22_pcs_config(struct mdio_device *pcs,
phy_interface_t interface,
const unsigned long *advertising,
unsigned int neg_mode)
{
bool changed = 0;
u16 bmcr;
int ret, adv;
adv = phylink_mii_c22_pcs_encode_advertisement(interface, advertising); if (adv >= 0) {
ret = mdiobus_modify_changed(pcs->bus, pcs->addr,
MII_ADVERTISE, 0xffff, adv); if (ret < 0) return ret;
changed = ret;
}
/* Configure the inband state. Ensure ISOLATE bit is disabled */
ret = mdiodev_modify(pcs, MII_BMCR, BMCR_ANENABLE | BMCR_ISOLATE, bmcr); if (ret < 0) return ret;
/**
* phylink_mii_c22_pcs_an_restart() - restart 802.3z autonegotiation
* @pcs: a pointerto a &struct mdio_device.
*
* Helper for MAC PCS supporting the 802.3 clause 22 register setfor
* clause 37 negotiation.
*
* Restart the clause 37 negotiation with the link partner. This is
* suitable to be directly plugged into the pcs_get_state() member
* of the struct phylink_pcs_ops structure.
*/
void phylink_mii_c22_pcs_an_restart(struct mdio_device *pcs)
{
int val = mdiodev_read(pcs, MII_BMCR);
static int __init phylink_init(void)
{ for (int i = 0; i < ARRAY_SIZE(phylink_sfp_interface_preference); ++i)
__set_bit(phylink_sfp_interface_preference[i],
phylink_sfp_interfaces);
return0;
}
module_init(phylink_init);
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("phylink models the MAC to optional PHY connection");
Messung V0.5 in Prozent
¤ Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.0.124Bemerkung:
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.