Merge branch 'Phylink-PCS-updates'
Russell King says: ==================== Phylink PCS updates This series updates the rudimentary phylink PCS support with the results of the last four months of development of that. Phylink PCS support was initially added back at the end of March, when it became clear that the current approach of treating everything at the MAC end as being part of the MAC was inadequate. However, this rudimentary implementation was fine initially for mvneta and similar, but in practice had a fair number of issues, particularly when ethtool interfaces were used to change various link properties. It became apparent that relying on the phylink_config structure for the PCS was also bad when it became clear that the same PCS was used in DSA drivers as well as in NXPs other offerings, and there was a desire to re-use that code. It also became apparent that splitting the "configuration" step on an interface mode configuration between the MAC and PCS using just mac_config() and pcs_config() methods was not sufficient for some setups, as the MAC needed to be "taken down" prior to making changes, and once all settings were complete, the MAC could only then be resumed. This series addresses these points, progressing PCS support, and has been developed with mvneta and DPAA2 setups, with work on both those drivers to prove this approach. It has been rigorously tested with mvneta, as that provides the most flexibility for testing the various code paths. To solve the phylink_config reuse problem, we introduce a struct phylink_pcs, which contains the minimal information necessary, and it is intended that this is embedded in the PCS private data structure. To solve the interface mode configuration problem, we introduce two new MAC methods, mac_prepare() and mac_finish() which wrap the entire interface mode configuration only. This has the additional benefit of relieving MAC drivers from working out whether an interface change has occurred, and whether they need to do some major work. I have not yet updated all the interface documentation for these changes yet, that work remains, but this patch set is provided in the hope that those working on PCS support in NXP will find this useful. Since there is a lot of change here, this is the reason why I strongly advise that everyone has converted to the mac_link_up() way of configuring the link parameters when the link comes up, rather than the old way of using mac_config() - especially as splitting the PCS changes how and when phylink calls mac_config(). Although no change for existing users is intended, that is something I no longer am able to test. Changes since RFC: - fix bisect build failure - add patch to use config.an_enabled - rename phylink_config_interface to phylink_major_reconfig - add expanded documentation for phylink_set_pcs() ==================== Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
commit
11de5770c7
|
@ -43,6 +43,7 @@ struct phylink {
|
|||
const struct phylink_mac_ops *mac_ops;
|
||||
const struct phylink_pcs_ops *pcs_ops;
|
||||
struct phylink_config *config;
|
||||
struct phylink_pcs *pcs;
|
||||
struct device *dev;
|
||||
unsigned int old_link_state:1;
|
||||
|
||||
|
@ -241,8 +242,10 @@ static int phylink_parse_fixedlink(struct phylink *pl,
|
|||
phylink_set(pl->supported, MII);
|
||||
phylink_set(pl->supported, Pause);
|
||||
phylink_set(pl->supported, Asym_Pause);
|
||||
phylink_set(pl->supported, Autoneg);
|
||||
if (s) {
|
||||
__set_bit(s->bit, pl->supported);
|
||||
__set_bit(s->bit, pl->link_config.lp_advertising);
|
||||
} else {
|
||||
phylink_warn(pl, "fixed link %s duplex %dMbps not recognised\n",
|
||||
pl->link_config.duplex == DUPLEX_FULL ? "full" : "half",
|
||||
|
@ -419,40 +422,102 @@ static void phylink_mac_config(struct phylink *pl,
|
|||
pl->mac_ops->mac_config(pl->config, pl->cur_link_an_mode, state);
|
||||
}
|
||||
|
||||
static void phylink_mac_config_up(struct phylink *pl,
|
||||
const struct phylink_link_state *state)
|
||||
{
|
||||
if (state->link)
|
||||
phylink_mac_config(pl, state);
|
||||
}
|
||||
|
||||
static void phylink_mac_pcs_an_restart(struct phylink *pl)
|
||||
{
|
||||
if (pl->link_config.an_enabled &&
|
||||
phy_interface_mode_is_8023z(pl->link_config.interface) &&
|
||||
phylink_autoneg_inband(pl->cur_link_an_mode)) {
|
||||
if (pl->pcs_ops)
|
||||
pl->pcs_ops->pcs_an_restart(pl->config);
|
||||
pl->pcs_ops->pcs_an_restart(pl->pcs);
|
||||
else
|
||||
pl->mac_ops->mac_an_restart(pl->config);
|
||||
}
|
||||
}
|
||||
|
||||
static void phylink_pcs_config(struct phylink *pl, bool force_restart,
|
||||
const struct phylink_link_state *state)
|
||||
static void phylink_major_config(struct phylink *pl, bool restart,
|
||||
const struct phylink_link_state *state)
|
||||
{
|
||||
bool restart = force_restart;
|
||||
int err;
|
||||
|
||||
if (pl->pcs_ops && pl->pcs_ops->pcs_config(pl->config,
|
||||
pl->cur_link_an_mode,
|
||||
state->interface,
|
||||
state->advertising))
|
||||
restart = true;
|
||||
phylink_dbg(pl, "major config %s\n", phy_modes(state->interface));
|
||||
|
||||
if (pl->mac_ops->mac_prepare) {
|
||||
err = pl->mac_ops->mac_prepare(pl->config, pl->cur_link_an_mode,
|
||||
state->interface);
|
||||
if (err < 0) {
|
||||
phylink_err(pl, "mac_prepare failed: %pe\n",
|
||||
ERR_PTR(err));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
phylink_mac_config(pl, state);
|
||||
|
||||
if (pl->pcs_ops) {
|
||||
err = pl->pcs_ops->pcs_config(pl->pcs, pl->cur_link_an_mode,
|
||||
state->interface,
|
||||
state->advertising,
|
||||
!!(pl->link_config.pause &
|
||||
MLO_PAUSE_AN));
|
||||
if (err < 0)
|
||||
phylink_err(pl, "pcs_config failed: %pe\n",
|
||||
ERR_PTR(err));
|
||||
if (err > 0)
|
||||
restart = true;
|
||||
}
|
||||
if (restart)
|
||||
phylink_mac_pcs_an_restart(pl);
|
||||
|
||||
if (pl->mac_ops->mac_finish) {
|
||||
err = pl->mac_ops->mac_finish(pl->config, pl->cur_link_an_mode,
|
||||
state->interface);
|
||||
if (err < 0)
|
||||
phylink_err(pl, "mac_prepare failed: %pe\n",
|
||||
ERR_PTR(err));
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Reconfigure for a change of inband advertisement.
|
||||
* If we have a separate PCS, we only need to call its pcs_config() method,
|
||||
* and then restart AN if it indicates something changed. Otherwise, we do
|
||||
* the full MAC reconfiguration.
|
||||
*/
|
||||
static int phylink_change_inband_advert(struct phylink *pl)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (test_bit(PHYLINK_DISABLE_STOPPED, &pl->phylink_disable_state))
|
||||
return 0;
|
||||
|
||||
if (!pl->pcs_ops) {
|
||||
/* Legacy method */
|
||||
phylink_mac_config(pl, &pl->link_config);
|
||||
phylink_mac_pcs_an_restart(pl);
|
||||
return 0;
|
||||
}
|
||||
|
||||
phylink_dbg(pl, "%s: mode=%s/%s adv=%*pb pause=%02x\n", __func__,
|
||||
phylink_an_mode_str(pl->cur_link_an_mode),
|
||||
phy_modes(pl->link_config.interface),
|
||||
__ETHTOOL_LINK_MODE_MASK_NBITS, pl->link_config.advertising,
|
||||
pl->link_config.pause);
|
||||
|
||||
/* Modern PCS-based method; update the advert at the PCS, and
|
||||
* restart negotiation if the pcs_config() helper indicates that
|
||||
* the programmed advertisement has changed.
|
||||
*/
|
||||
ret = pl->pcs_ops->pcs_config(pl->pcs, pl->cur_link_an_mode,
|
||||
pl->link_config.interface,
|
||||
pl->link_config.advertising,
|
||||
!!(pl->link_config.pause & MLO_PAUSE_AN));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (ret > 0)
|
||||
phylink_mac_pcs_an_restart(pl);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void phylink_mac_pcs_get_state(struct phylink *pl,
|
||||
|
@ -469,7 +534,7 @@ static void phylink_mac_pcs_get_state(struct phylink *pl,
|
|||
state->link = 1;
|
||||
|
||||
if (pl->pcs_ops)
|
||||
pl->pcs_ops->pcs_get_state(pl->config, state);
|
||||
pl->pcs_ops->pcs_get_state(pl->pcs, state);
|
||||
else
|
||||
pl->mac_ops->mac_pcs_get_state(pl->config, state);
|
||||
}
|
||||
|
@ -515,7 +580,7 @@ static void phylink_mac_initial_config(struct phylink *pl, bool force_restart)
|
|||
link_state.link = false;
|
||||
|
||||
phylink_apply_manual_flow(pl, &link_state);
|
||||
phylink_pcs_config(pl, force_restart, &link_state);
|
||||
phylink_major_config(pl, force_restart, &link_state);
|
||||
}
|
||||
|
||||
static const char *phylink_pause_to_str(int pause)
|
||||
|
@ -540,7 +605,7 @@ static void phylink_link_up(struct phylink *pl,
|
|||
pl->cur_interface = link_state.interface;
|
||||
|
||||
if (pl->pcs_ops && pl->pcs_ops->pcs_link_up)
|
||||
pl->pcs_ops->pcs_link_up(pl->config, pl->cur_link_an_mode,
|
||||
pl->pcs_ops->pcs_link_up(pl->pcs, pl->cur_link_an_mode,
|
||||
pl->cur_interface,
|
||||
link_state.speed, link_state.duplex);
|
||||
|
||||
|
@ -576,9 +641,15 @@ static void phylink_resolve(struct work_struct *w)
|
|||
struct phylink *pl = container_of(w, struct phylink, resolve);
|
||||
struct phylink_link_state link_state;
|
||||
struct net_device *ndev = pl->netdev;
|
||||
int link_changed;
|
||||
bool mac_config = false;
|
||||
bool cur_link_state;
|
||||
|
||||
mutex_lock(&pl->state_mutex);
|
||||
if (pl->netdev)
|
||||
cur_link_state = netif_carrier_ok(ndev);
|
||||
else
|
||||
cur_link_state = pl->old_link_state;
|
||||
|
||||
if (pl->phylink_disable_state) {
|
||||
pl->mac_link_dropped = false;
|
||||
link_state.link = false;
|
||||
|
@ -589,12 +660,12 @@ static void phylink_resolve(struct work_struct *w)
|
|||
case MLO_AN_PHY:
|
||||
link_state = pl->phy_state;
|
||||
phylink_apply_manual_flow(pl, &link_state);
|
||||
phylink_mac_config_up(pl, &link_state);
|
||||
mac_config = link_state.link;
|
||||
break;
|
||||
|
||||
case MLO_AN_FIXED:
|
||||
phylink_get_fixed_state(pl, &link_state);
|
||||
phylink_mac_config_up(pl, &link_state);
|
||||
mac_config = link_state.link;
|
||||
break;
|
||||
|
||||
case MLO_AN_INBAND:
|
||||
|
@ -612,21 +683,36 @@ static void phylink_resolve(struct work_struct *w)
|
|||
/* If we have a PHY, we need to update with
|
||||
* the PHY flow control bits. */
|
||||
link_state.pause = pl->phy_state.pause;
|
||||
phylink_apply_manual_flow(pl, &link_state);
|
||||
phylink_mac_config(pl, &link_state);
|
||||
} else {
|
||||
phylink_apply_manual_flow(pl, &link_state);
|
||||
mac_config = true;
|
||||
}
|
||||
phylink_apply_manual_flow(pl, &link_state);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (pl->netdev)
|
||||
link_changed = (link_state.link != netif_carrier_ok(ndev));
|
||||
else
|
||||
link_changed = (link_state.link != pl->old_link_state);
|
||||
if (mac_config) {
|
||||
if (link_state.interface != pl->link_config.interface) {
|
||||
/* The interface has changed, force the link down and
|
||||
* then reconfigure.
|
||||
*/
|
||||
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;
|
||||
} else if (!pl->pcs_ops) {
|
||||
/* The interface remains unchanged, only the speed,
|
||||
* duplex or pause settings have changed. Call the
|
||||
* old mac_config() method to configure the MAC/PCS
|
||||
* only if we do not have a PCS installed (an
|
||||
* unconverted user.)
|
||||
*/
|
||||
phylink_mac_config(pl, &link_state);
|
||||
}
|
||||
}
|
||||
|
||||
if (link_changed) {
|
||||
if (link_state.link != cur_link_state) {
|
||||
pl->old_link_state = link_state.link;
|
||||
if (!link_state.link)
|
||||
phylink_link_down(pl);
|
||||
|
@ -778,11 +864,26 @@ struct phylink *phylink_create(struct phylink_config *config,
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(phylink_create);
|
||||
|
||||
void phylink_add_pcs(struct phylink *pl, const struct phylink_pcs_ops *ops)
|
||||
/**
|
||||
* phylink_set_pcs() - set the current PCS for phylink to use
|
||||
* @pl: a pointer to a &struct phylink returned from phylink_create()
|
||||
* @pcs: a pointer to the &struct phylink_pcs
|
||||
*
|
||||
* Bind the MAC PCS to phylink. This may be called after phylink_create(),
|
||||
* in mac_prepare() or mac_config() methods if it is desired to dynamically
|
||||
* change the PCS.
|
||||
*
|
||||
* Please note that there are behavioural changes with the mac_config()
|
||||
* callback if a PCS is present (denoting a newer setup) so removing a PCS
|
||||
* is not supported, and if a PCS is going to be used, it must be registered
|
||||
* by calling phylink_set_pcs() at the latest in the first mac_config() call.
|
||||
*/
|
||||
void phylink_set_pcs(struct phylink *pl, struct phylink_pcs *pcs)
|
||||
{
|
||||
pl->pcs_ops = ops;
|
||||
pl->pcs = pcs;
|
||||
pl->pcs_ops = pcs->ops;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(phylink_add_pcs);
|
||||
EXPORT_SYMBOL_GPL(phylink_set_pcs);
|
||||
|
||||
/**
|
||||
* phylink_destroy() - cleanup and destroy the phylink instance
|
||||
|
@ -1127,6 +1228,8 @@ void phylink_start(struct phylink *pl)
|
|||
break;
|
||||
case MLO_AN_INBAND:
|
||||
poll |= pl->config->pcs_poll;
|
||||
if (pl->pcs)
|
||||
poll |= pl->pcs->poll;
|
||||
break;
|
||||
}
|
||||
if (poll)
|
||||
|
@ -1296,27 +1399,46 @@ int phylink_ethtool_ksettings_set(struct phylink *pl,
|
|||
const struct ethtool_link_ksettings *kset)
|
||||
{
|
||||
__ETHTOOL_DECLARE_LINK_MODE_MASK(support);
|
||||
struct ethtool_link_ksettings our_kset;
|
||||
struct phylink_link_state config;
|
||||
int ret;
|
||||
const struct phy_setting *s;
|
||||
|
||||
ASSERT_RTNL();
|
||||
|
||||
if (kset->base.autoneg != AUTONEG_DISABLE &&
|
||||
kset->base.autoneg != AUTONEG_ENABLE)
|
||||
return -EINVAL;
|
||||
if (pl->phydev) {
|
||||
/* 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 configuration for PHY mode comes from the
|
||||
* last phy state updated via phylink_phy_change().
|
||||
* - other configuration changes (e.g. pause modes) are
|
||||
* performed directly via phylib.
|
||||
* - if in in-band mode with a PHY, the link configuration
|
||||
* is passed on the link from the PHY, and all of
|
||||
* link_config.{speed,duplex,an_enabled,pause} are not used.
|
||||
* - the only possible use would be link_config.advertising
|
||||
* pause modes when in 1000base-X mode with 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, kset);
|
||||
}
|
||||
|
||||
linkmode_copy(support, pl->supported);
|
||||
config = pl->link_config;
|
||||
config.an_enabled = kset->base.autoneg == AUTONEG_ENABLE;
|
||||
|
||||
/* Mask out unsupported advertisements */
|
||||
/* Mask out unsupported advertisements, and force the autoneg bit */
|
||||
linkmode_and(config.advertising, kset->link_modes.advertising,
|
||||
support);
|
||||
linkmode_mod_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, config.advertising,
|
||||
config.an_enabled);
|
||||
|
||||
/* FIXME: should we reject autoneg if phy/mac does not support it? */
|
||||
if (kset->base.autoneg == AUTONEG_DISABLE) {
|
||||
const struct phy_setting *s;
|
||||
|
||||
switch (kset->base.autoneg) {
|
||||
case AUTONEG_DISABLE:
|
||||
/* Autonegotiation disabled, select a suitable speed and
|
||||
* duplex.
|
||||
*/
|
||||
|
@ -1325,90 +1447,73 @@ int phylink_ethtool_ksettings_set(struct phylink *pl,
|
|||
if (!s)
|
||||
return -EINVAL;
|
||||
|
||||
/* If we have a fixed link (as specified by firmware), refuse
|
||||
* to change link parameters.
|
||||
/* If we have a fixed link, refuse to change link parameters.
|
||||
* If the link parameters match, accept them but do nothing.
|
||||
*/
|
||||
if (pl->cur_link_an_mode == MLO_AN_FIXED &&
|
||||
(s->speed != pl->link_config.speed ||
|
||||
s->duplex != pl->link_config.duplex))
|
||||
return -EINVAL;
|
||||
if (pl->cur_link_an_mode == MLO_AN_FIXED) {
|
||||
if (s->speed != pl->link_config.speed ||
|
||||
s->duplex != pl->link_config.duplex)
|
||||
return -EINVAL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
config.speed = s->speed;
|
||||
config.duplex = s->duplex;
|
||||
config.an_enabled = false;
|
||||
break;
|
||||
|
||||
__clear_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, config.advertising);
|
||||
} else {
|
||||
/* If we have a fixed link, refuse to enable autonegotiation */
|
||||
if (pl->cur_link_an_mode == MLO_AN_FIXED)
|
||||
return -EINVAL;
|
||||
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->cur_link_an_mode == MLO_AN_FIXED) {
|
||||
if (!linkmode_equal(config.advertising,
|
||||
pl->link_config.advertising))
|
||||
return -EINVAL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
config.speed = SPEED_UNKNOWN;
|
||||
config.duplex = DUPLEX_UNKNOWN;
|
||||
config.an_enabled = true;
|
||||
break;
|
||||
|
||||
__set_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, config.advertising);
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (pl->phydev) {
|
||||
/* If we have a PHY, we process the kset change via phylib.
|
||||
* phylib will call our link state function if the PHY
|
||||
* parameters have changed, which will trigger a resolve
|
||||
* and update the MAC configuration.
|
||||
*/
|
||||
our_kset = *kset;
|
||||
linkmode_copy(our_kset.link_modes.advertising,
|
||||
config.advertising);
|
||||
our_kset.base.speed = config.speed;
|
||||
our_kset.base.duplex = config.duplex;
|
||||
/* We have ruled out the case with a PHY attached, and the
|
||||
* fixed-link cases. All that is left are in-band links.
|
||||
*/
|
||||
if (phylink_validate(pl, support, &config))
|
||||
return -EINVAL;
|
||||
|
||||
ret = phy_ethtool_ksettings_set(pl->phydev, &our_kset);
|
||||
if (ret)
|
||||
return ret;
|
||||
/* If autonegotiation is enabled, we must have an advertisement */
|
||||
if (config.an_enabled && phylink_is_empty_linkmode(config.advertising))
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&pl->state_mutex);
|
||||
/* Save the new configuration */
|
||||
linkmode_copy(pl->link_config.advertising,
|
||||
our_kset.link_modes.advertising);
|
||||
pl->link_config.interface = config.interface;
|
||||
pl->link_config.speed = our_kset.base.speed;
|
||||
pl->link_config.duplex = our_kset.base.duplex;
|
||||
pl->link_config.an_enabled = our_kset.base.autoneg !=
|
||||
AUTONEG_DISABLE;
|
||||
mutex_unlock(&pl->state_mutex);
|
||||
} else {
|
||||
/* For a fixed link, this isn't able to change any parameters,
|
||||
* which just leaves inband mode.
|
||||
*/
|
||||
if (phylink_validate(pl, support, &config))
|
||||
return -EINVAL;
|
||||
mutex_lock(&pl->state_mutex);
|
||||
pl->link_config.speed = config.speed;
|
||||
pl->link_config.duplex = config.duplex;
|
||||
pl->link_config.an_enabled = config.an_enabled;
|
||||
|
||||
/* If autonegotiation is enabled, we must have an advertisement */
|
||||
if (config.an_enabled &&
|
||||
phylink_is_empty_linkmode(config.advertising))
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&pl->state_mutex);
|
||||
linkmode_copy(pl->link_config.advertising, config.advertising);
|
||||
pl->link_config.interface = config.interface;
|
||||
pl->link_config.speed = config.speed;
|
||||
pl->link_config.duplex = config.duplex;
|
||||
pl->link_config.an_enabled = kset->base.autoneg !=
|
||||
AUTONEG_DISABLE;
|
||||
|
||||
if (pl->cur_link_an_mode == MLO_AN_INBAND &&
|
||||
!test_bit(PHYLINK_DISABLE_STOPPED,
|
||||
&pl->phylink_disable_state)) {
|
||||
/* If in 802.3z mode, this updates the advertisement.
|
||||
*
|
||||
* If we are in SGMII mode without a PHY, there is no
|
||||
* advertisement; the only thing we have is the pause
|
||||
* modes which can only come from a PHY.
|
||||
*/
|
||||
phylink_pcs_config(pl, true, &pl->link_config);
|
||||
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;
|
||||
}
|
||||
mutex_unlock(&pl->state_mutex);
|
||||
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);
|
||||
} else if (!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);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -1511,9 +1616,11 @@ int phylink_ethtool_set_pauseparam(struct phylink *pl,
|
|||
|
||||
config->pause = pause_state;
|
||||
|
||||
if (!pl->phydev && !test_bit(PHYLINK_DISABLE_STOPPED,
|
||||
&pl->phylink_disable_state))
|
||||
phylink_pcs_config(pl, true, &pl->link_config);
|
||||
/* 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);
|
||||
|
||||
|
@ -2335,6 +2442,43 @@ int phylink_mii_c22_pcs_set_advertisement(struct mdio_device *pcs,
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(phylink_mii_c22_pcs_set_advertisement);
|
||||
|
||||
/**
|
||||
* phylink_mii_c22_pcs_config() - configure clause 22 PCS
|
||||
* @pcs: a pointer to a &struct mdio_device.
|
||||
* @mode: link autonegotiation mode
|
||||
* @interface: the PHY interface mode being configured
|
||||
* @advertising: the ethtool advertisement mask
|
||||
*
|
||||
* Configure a Clause 22 PCS PHY with the appropriate negotiation
|
||||
* parameters for the @mode, @interface and @advertising parameters.
|
||||
* Returns negative error number on failure, zero if the advertisement
|
||||
* has not changed, or positive if there is a change.
|
||||
*/
|
||||
int phylink_mii_c22_pcs_config(struct mdio_device *pcs, unsigned int mode,
|
||||
phy_interface_t interface,
|
||||
const unsigned long *advertising)
|
||||
{
|
||||
bool changed;
|
||||
u16 bmcr;
|
||||
int ret;
|
||||
|
||||
ret = phylink_mii_c22_pcs_set_advertisement(pcs, interface,
|
||||
advertising);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
changed = ret > 0;
|
||||
|
||||
bmcr = mode == MLO_AN_INBAND ? BMCR_ANENABLE : 0;
|
||||
ret = mdiobus_modify(pcs->bus, pcs->addr, MII_BMCR,
|
||||
BMCR_ANENABLE, bmcr);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return changed ? 1 : 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(phylink_mii_c22_pcs_config);
|
||||
|
||||
/**
|
||||
* phylink_mii_c22_pcs_an_restart() - restart 802.3z autonegotiation
|
||||
* @pcs: a pointer to a &struct mdio_device.
|
||||
|
|
|
@ -76,7 +76,9 @@ struct phylink_config {
|
|||
* struct phylink_mac_ops - MAC operations structure.
|
||||
* @validate: Validate and update the link configuration.
|
||||
* @mac_pcs_get_state: Read the current link state from the hardware.
|
||||
* @mac_prepare: prepare for a major reconfiguration of the interface.
|
||||
* @mac_config: configure the MAC for the selected mode and state.
|
||||
* @mac_finish: finish a major reconfiguration of the interface.
|
||||
* @mac_an_restart: restart 802.3z BaseX autonegotiation.
|
||||
* @mac_link_down: take the link down.
|
||||
* @mac_link_up: allow the link to come up.
|
||||
|
@ -89,8 +91,12 @@ struct phylink_mac_ops {
|
|||
struct phylink_link_state *state);
|
||||
void (*mac_pcs_get_state)(struct phylink_config *config,
|
||||
struct phylink_link_state *state);
|
||||
int (*mac_prepare)(struct phylink_config *config, unsigned int mode,
|
||||
phy_interface_t iface);
|
||||
void (*mac_config)(struct phylink_config *config, unsigned int mode,
|
||||
const struct phylink_link_state *state);
|
||||
int (*mac_finish)(struct phylink_config *config, unsigned int mode,
|
||||
phy_interface_t iface);
|
||||
void (*mac_an_restart)(struct phylink_config *config);
|
||||
void (*mac_link_down)(struct phylink_config *config, unsigned int mode,
|
||||
phy_interface_t interface);
|
||||
|
@ -145,6 +151,31 @@ void validate(struct phylink_config *config, unsigned long *supported,
|
|||
void mac_pcs_get_state(struct phylink_config *config,
|
||||
struct phylink_link_state *state);
|
||||
|
||||
/**
|
||||
* mac_prepare() - prepare to change the PHY interface mode
|
||||
* @config: a pointer to a &struct phylink_config.
|
||||
* @mode: one of %MLO_AN_FIXED, %MLO_AN_PHY, %MLO_AN_INBAND.
|
||||
* @iface: interface mode to switch to
|
||||
*
|
||||
* phylink will call this method at the beginning of a full initialisation
|
||||
* of the link, which includes changing the interface mode or at initial
|
||||
* startup time. It may be called for the current mode. The MAC driver
|
||||
* should perform whatever actions are required, e.g. disabling the
|
||||
* Serdes PHY.
|
||||
*
|
||||
* This will be the first call in the sequence:
|
||||
* - mac_prepare()
|
||||
* - mac_config()
|
||||
* - pcs_config()
|
||||
* - possible pcs_an_restart()
|
||||
* - mac_finish()
|
||||
*
|
||||
* Returns zero on success, or negative errno on failure which will be
|
||||
* reported to the kernel log.
|
||||
*/
|
||||
int mac_prepare(struct phylink_config *config, unsigned int mode,
|
||||
phy_interface_t iface);
|
||||
|
||||
/**
|
||||
* mac_config() - configure the MAC for the selected mode and state
|
||||
* @config: a pointer to a &struct phylink_config.
|
||||
|
@ -220,6 +251,23 @@ void mac_pcs_get_state(struct phylink_config *config,
|
|||
void mac_config(struct phylink_config *config, unsigned int mode,
|
||||
const struct phylink_link_state *state);
|
||||
|
||||
/**
|
||||
* mac_finish() - finish a to change the PHY interface mode
|
||||
* @config: a pointer to a &struct phylink_config.
|
||||
* @mode: one of %MLO_AN_FIXED, %MLO_AN_PHY, %MLO_AN_INBAND.
|
||||
* @iface: interface mode to switch to
|
||||
*
|
||||
* phylink will call this if it called mac_prepare() to allow the MAC to
|
||||
* complete any necessary steps after the MAC and PCS have been configured
|
||||
* for the @mode and @iface. E.g. a MAC driver may wish to re-enable the
|
||||
* Serdes PHY here if it was previously disabled by mac_prepare().
|
||||
*
|
||||
* Returns zero on success, or negative errno on failure which will be
|
||||
* reported to the kernel log.
|
||||
*/
|
||||
int mac_finish(struct phylink_config *config, unsigned int mode,
|
||||
phy_interface_t iface);
|
||||
|
||||
/**
|
||||
* mac_an_restart() - restart 802.3z BaseX autonegotiation
|
||||
* @config: a pointer to a &struct phylink_config.
|
||||
|
@ -273,6 +321,21 @@ void mac_link_up(struct phylink_config *config, struct phy_device *phy,
|
|||
int speed, int duplex, bool tx_pause, bool rx_pause);
|
||||
#endif
|
||||
|
||||
struct phylink_pcs_ops;
|
||||
|
||||
/**
|
||||
* struct phylink_pcs - PHYLINK PCS instance
|
||||
* @ops: a pointer to the &struct phylink_pcs_ops structure
|
||||
* @poll: poll the PCS for link changes
|
||||
*
|
||||
* This structure is designed to be embedded within the PCS private data,
|
||||
* and will be passed between phylink and the PCS.
|
||||
*/
|
||||
struct phylink_pcs {
|
||||
const struct phylink_pcs_ops *ops;
|
||||
bool poll;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct phylink_pcs_ops - MAC PCS operations structure.
|
||||
* @pcs_get_state: read the current MAC PCS link state from the hardware.
|
||||
|
@ -282,20 +345,21 @@ void mac_link_up(struct phylink_config *config, struct phy_device *phy,
|
|||
* (where necessary).
|
||||
*/
|
||||
struct phylink_pcs_ops {
|
||||
void (*pcs_get_state)(struct phylink_config *config,
|
||||
void (*pcs_get_state)(struct phylink_pcs *pcs,
|
||||
struct phylink_link_state *state);
|
||||
int (*pcs_config)(struct phylink_config *config, unsigned int mode,
|
||||
int (*pcs_config)(struct phylink_pcs *pcs, unsigned int mode,
|
||||
phy_interface_t interface,
|
||||
const unsigned long *advertising);
|
||||
void (*pcs_an_restart)(struct phylink_config *config);
|
||||
void (*pcs_link_up)(struct phylink_config *config, unsigned int mode,
|
||||
const unsigned long *advertising,
|
||||
bool permit_pause_to_mac);
|
||||
void (*pcs_an_restart)(struct phylink_pcs *pcs);
|
||||
void (*pcs_link_up)(struct phylink_pcs *pcs, unsigned int mode,
|
||||
phy_interface_t interface, int speed, int duplex);
|
||||
};
|
||||
|
||||
#if 0 /* For kernel-doc purposes only. */
|
||||
/**
|
||||
* pcs_get_state() - Read the current inband link state from the hardware
|
||||
* @config: a pointer to a &struct phylink_config.
|
||||
* @pcs: a pointer to a &struct phylink_pcs.
|
||||
* @state: a pointer to a &struct phylink_link_state.
|
||||
*
|
||||
* Read the current inband link state from the MAC PCS, reporting the
|
||||
|
@ -308,18 +372,20 @@ struct phylink_pcs_ops {
|
|||
* When present, this overrides mac_pcs_get_state() in &struct
|
||||
* phylink_mac_ops.
|
||||
*/
|
||||
void pcs_get_state(struct phylink_config *config,
|
||||
void pcs_get_state(struct phylink_pcs *pcs,
|
||||
struct phylink_link_state *state);
|
||||
|
||||
/**
|
||||
* pcs_config() - Configure the PCS mode and advertisement
|
||||
* @config: a pointer to a &struct phylink_config.
|
||||
* @pcs: a pointer to a &struct phylink_pcs.
|
||||
* @mode: one of %MLO_AN_FIXED, %MLO_AN_PHY, %MLO_AN_INBAND.
|
||||
* @interface: interface mode to be used
|
||||
* @advertising: adertisement ethtool link mode mask
|
||||
* @permit_pause_to_mac: permit forwarding pause resolution to MAC
|
||||
*
|
||||
* Configure the PCS for the operating mode, the interface mode, and set
|
||||
* the advertisement mask.
|
||||
* the advertisement mask. @permit_pause_to_mac indicates whether the
|
||||
* hardware may forward the pause mode resolution to the MAC.
|
||||
*
|
||||
* When operating in %MLO_AN_INBAND, inband should always be enabled,
|
||||
* otherwise inband should be disabled.
|
||||
|
@ -331,21 +397,21 @@ void pcs_get_state(struct phylink_config *config,
|
|||
*
|
||||
* For most 10GBASE-R, there is no advertisement.
|
||||
*/
|
||||
int (*pcs_config)(struct phylink_config *config, unsigned int mode,
|
||||
phy_interface_t interface, const unsigned long *advertising);
|
||||
int pcs_config(struct phylink_pcs *pcs, unsigned int mode,
|
||||
phy_interface_t interface, const unsigned long *advertising);
|
||||
|
||||
/**
|
||||
* pcs_an_restart() - restart 802.3z BaseX autonegotiation
|
||||
* @config: a pointer to a &struct phylink_config.
|
||||
* @pcs: a pointer to a &struct phylink_pcs.
|
||||
*
|
||||
* When PCS ops are present, this overrides mac_an_restart() in &struct
|
||||
* phylink_mac_ops.
|
||||
*/
|
||||
void (*pcs_an_restart)(struct phylink_config *config);
|
||||
void pcs_an_restart(struct phylink_pcs *pcs);
|
||||
|
||||
/**
|
||||
* pcs_link_up() - program the PCS for the resolved link configuration
|
||||
* @config: a pointer to a &struct phylink_config.
|
||||
* @pcs: a pointer to a &struct phylink_pcs.
|
||||
* @mode: link autonegotiation mode
|
||||
* @interface: link &typedef phy_interface_t mode
|
||||
* @speed: link speed
|
||||
|
@ -356,14 +422,14 @@ void (*pcs_an_restart)(struct phylink_config *config);
|
|||
* mode without in-band AN needs to be manually configured for the link
|
||||
* and duplex setting. Otherwise, this should be a no-op.
|
||||
*/
|
||||
void (*pcs_link_up)(struct phylink_config *config, unsigned int mode,
|
||||
phy_interface_t interface, int speed, int duplex);
|
||||
void pcs_link_up(struct phylink_pcs *pcs, unsigned int mode,
|
||||
phy_interface_t interface, int speed, int duplex);
|
||||
#endif
|
||||
|
||||
struct phylink *phylink_create(struct phylink_config *, struct fwnode_handle *,
|
||||
phy_interface_t iface,
|
||||
const struct phylink_mac_ops *mac_ops);
|
||||
void phylink_add_pcs(struct phylink *, const struct phylink_pcs_ops *ops);
|
||||
void phylink_set_pcs(struct phylink *, struct phylink_pcs *pcs);
|
||||
void phylink_destroy(struct phylink *);
|
||||
|
||||
int phylink_connect_phy(struct phylink *, struct phy_device *);
|
||||
|
@ -412,6 +478,9 @@ void phylink_mii_c22_pcs_get_state(struct mdio_device *pcs,
|
|||
int phylink_mii_c22_pcs_set_advertisement(struct mdio_device *pcs,
|
||||
phy_interface_t interface,
|
||||
const unsigned long *advertising);
|
||||
int phylink_mii_c22_pcs_config(struct mdio_device *pcs, unsigned int mode,
|
||||
phy_interface_t interface,
|
||||
const unsigned long *advertising);
|
||||
void phylink_mii_c22_pcs_an_restart(struct mdio_device *pcs);
|
||||
|
||||
void phylink_mii_c45_pcs_get_state(struct mdio_device *pcs,
|
||||
|
|
Loading…
Reference in New Issue