Commit d1e86325 authored by Russell King (Oracle)'s avatar Russell King (Oracle) Committed by David S. Miller

net: phylink: add mac_select_pcs() method to phylink_mac_ops

mac_select_pcs() allows us to have an explicit point to query which
PCS the MAC wishes to use for a particular PHY interface mode, thereby
allowing us to add support to validate the link settings with the PCS.

Phylink will also use this to select the PCS to be used during a major
configuration event without the MAC driver needing to call
phylink_set_pcs().

Note that if mac_select_pcs() is present, the supported_interfaces
bitmap must be filled in; this avoids mac_select_pcs() being called
with PHY_INTERFACE_MODE_NA when we want to get support for all
interface types. Phylink will return an error in phylink_create()
unless this condition is satisfied.
Signed-off-by: default avatarRussell King (Oracle) <rmk+kernel@armlinux.org.uk>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 4134c846
...@@ -419,6 +419,23 @@ void phylink_generic_validate(struct phylink_config *config, ...@@ -419,6 +419,23 @@ void phylink_generic_validate(struct phylink_config *config,
} }
EXPORT_SYMBOL_GPL(phylink_generic_validate); EXPORT_SYMBOL_GPL(phylink_generic_validate);
static int phylink_validate_mac_and_pcs(struct phylink *pl,
unsigned long *supported,
struct phylink_link_state *state)
{
struct phylink_pcs *pcs;
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);
}
pl->mac_ops->validate(pl->config, supported, state);
return phylink_is_empty_linkmode(supported) ? -EINVAL : 0;
}
static int phylink_validate_any(struct phylink *pl, unsigned long *supported, static int phylink_validate_any(struct phylink *pl, unsigned long *supported,
struct phylink_link_state *state) struct phylink_link_state *state)
{ {
...@@ -434,9 +451,10 @@ static int phylink_validate_any(struct phylink *pl, unsigned long *supported, ...@@ -434,9 +451,10 @@ static int phylink_validate_any(struct phylink *pl, unsigned long *supported,
t = *state; t = *state;
t.interface = intf; t.interface = intf;
pl->mac_ops->validate(pl->config, s, &t); if (!phylink_validate_mac_and_pcs(pl, s, &t)) {
linkmode_or(all_s, all_s, s); linkmode_or(all_s, all_s, s);
linkmode_or(all_adv, all_adv, t.advertising); linkmode_or(all_adv, all_adv, t.advertising);
}
} }
} }
...@@ -458,9 +476,7 @@ static int phylink_validate(struct phylink *pl, unsigned long *supported, ...@@ -458,9 +476,7 @@ static int phylink_validate(struct phylink *pl, unsigned long *supported,
return -EINVAL; return -EINVAL;
} }
pl->mac_ops->validate(pl->config, supported, state); return phylink_validate_mac_and_pcs(pl, supported, state);
return phylink_is_empty_linkmode(supported) ? -EINVAL : 0;
} }
static int phylink_parse_fixedlink(struct phylink *pl, static int phylink_parse_fixedlink(struct phylink *pl,
...@@ -750,10 +766,21 @@ static void phylink_mac_pcs_an_restart(struct phylink *pl) ...@@ -750,10 +766,21 @@ static void phylink_mac_pcs_an_restart(struct phylink *pl)
static void phylink_major_config(struct phylink *pl, bool restart, static void phylink_major_config(struct phylink *pl, bool restart,
const struct phylink_link_state *state) const struct phylink_link_state *state)
{ {
struct phylink_pcs *pcs = NULL;
int err; int err;
phylink_dbg(pl, "major config %s\n", phy_modes(state->interface)); phylink_dbg(pl, "major config %s\n", phy_modes(state->interface));
if (pl->mac_ops->mac_select_pcs) {
pcs = pl->mac_ops->mac_select_pcs(pl->config, state->interface);
if (IS_ERR(pcs)) {
phylink_err(pl,
"mac_select_pcs unexpectedly failed: %pe\n",
pcs);
return;
}
}
if (pl->mac_ops->mac_prepare) { if (pl->mac_ops->mac_prepare) {
err = pl->mac_ops->mac_prepare(pl->config, pl->cur_link_an_mode, err = pl->mac_ops->mac_prepare(pl->config, pl->cur_link_an_mode,
state->interface); state->interface);
...@@ -764,6 +791,12 @@ static void phylink_major_config(struct phylink *pl, bool restart, ...@@ -764,6 +791,12 @@ static void phylink_major_config(struct phylink *pl, bool restart,
} }
} }
/* If we have a new PCS, switch to the new PCS after preparing the MAC
* for the change.
*/
if (pcs)
phylink_set_pcs(pl, pcs);
phylink_mac_config(pl, state); phylink_mac_config(pl, state);
if (pl->pcs_ops) { if (pl->pcs_ops) {
...@@ -1155,6 +1188,14 @@ struct phylink *phylink_create(struct phylink_config *config, ...@@ -1155,6 +1188,14 @@ struct phylink *phylink_create(struct phylink_config *config,
struct phylink *pl; struct phylink *pl;
int ret; int ret;
/* Validate the supplied configuration */
if (mac_ops->mac_select_pcs &&
phy_interface_empty(config->supported_interfaces)) {
dev_err(config->dev,
"phylink: error: empty supported_interfaces but mac_select_pcs() method present\n");
return ERR_PTR(-EINVAL);
}
pl = kzalloc(sizeof(*pl), GFP_KERNEL); pl = kzalloc(sizeof(*pl), GFP_KERNEL);
if (!pl) if (!pl)
return ERR_PTR(-ENOMEM); return ERR_PTR(-ENOMEM);
...@@ -1222,9 +1263,10 @@ EXPORT_SYMBOL_GPL(phylink_create); ...@@ -1222,9 +1263,10 @@ EXPORT_SYMBOL_GPL(phylink_create);
* @pl: a pointer to a &struct phylink returned from phylink_create() * @pl: a pointer to a &struct phylink returned from phylink_create()
* @pcs: a pointer to the &struct phylink_pcs * @pcs: a pointer to the &struct phylink_pcs
* *
* Bind the MAC PCS to phylink. This may be called after phylink_create(), * 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 * If it is desired to dynamically change the PCS, then the preferred method
* change the PCS. * is to use mac_select_pcs(), but it may also be called in mac_prepare()
* or mac_config().
* *
* Please note that there are behavioural changes with the mac_config() * 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 * callback if a PCS is present (denoting a newer setup) so removing a PCS
...@@ -1235,6 +1277,14 @@ void phylink_set_pcs(struct phylink *pl, struct phylink_pcs *pcs) ...@@ -1235,6 +1277,14 @@ void phylink_set_pcs(struct phylink *pl, struct phylink_pcs *pcs)
{ {
pl->pcs = pcs; pl->pcs = pcs;
pl->pcs_ops = pcs->ops; pl->pcs_ops = pcs->ops;
if (!pl->phylink_disable_state &&
pl->cfg_link_an_mode == MLO_AN_INBAND) {
if (pl->config->pcs_poll || pcs->poll)
mod_timer(&pl->link_poll, jiffies + HZ);
else
del_timer(&pl->link_poll);
}
} }
EXPORT_SYMBOL_GPL(phylink_set_pcs); EXPORT_SYMBOL_GPL(phylink_set_pcs);
......
...@@ -112,6 +112,7 @@ struct phylink_config { ...@@ -112,6 +112,7 @@ struct phylink_config {
/** /**
* struct phylink_mac_ops - MAC operations structure. * struct phylink_mac_ops - MAC operations structure.
* @validate: Validate and update the link configuration. * @validate: Validate and update the link configuration.
* @mac_select_pcs: Select a PCS for the interface mode.
* @mac_pcs_get_state: Read the current link state from the hardware. * @mac_pcs_get_state: Read the current link state from the hardware.
* @mac_prepare: prepare for a major reconfiguration of the interface. * @mac_prepare: prepare for a major reconfiguration of the interface.
* @mac_config: configure the MAC for the selected mode and state. * @mac_config: configure the MAC for the selected mode and state.
...@@ -126,6 +127,8 @@ struct phylink_mac_ops { ...@@ -126,6 +127,8 @@ struct phylink_mac_ops {
void (*validate)(struct phylink_config *config, void (*validate)(struct phylink_config *config,
unsigned long *supported, unsigned long *supported,
struct phylink_link_state *state); struct phylink_link_state *state);
struct phylink_pcs *(*mac_select_pcs)(struct phylink_config *config,
phy_interface_t interface);
void (*mac_pcs_get_state)(struct phylink_config *config, void (*mac_pcs_get_state)(struct phylink_config *config,
struct phylink_link_state *state); struct phylink_link_state *state);
int (*mac_prepare)(struct phylink_config *config, unsigned int mode, int (*mac_prepare)(struct phylink_config *config, unsigned int mode,
...@@ -178,6 +181,21 @@ struct phylink_mac_ops { ...@@ -178,6 +181,21 @@ struct phylink_mac_ops {
*/ */
void validate(struct phylink_config *config, unsigned long *supported, void validate(struct phylink_config *config, unsigned long *supported,
struct phylink_link_state *state); struct phylink_link_state *state);
/**
* mac_select_pcs: Select a PCS for the interface mode.
* @config: a pointer to a &struct phylink_config.
* @interface: PHY interface mode for PCS
*
* Return the &struct phylink_pcs for the specified interface mode, or
* NULL if none is required, or an error pointer on error.
*
* This must not modify any state. It is used to query which PCS should
* be used. Phylink will use this during validation to ensure that the
* configuration is valid, and when setting a configuration to internally
* set the PCS that will be used.
*/
struct phylink_pcs *mac_select_pcs(struct phylink_config *config,
phy_interface_t interface);
/** /**
* mac_pcs_get_state() - Read the current inband link state from the hardware * mac_pcs_get_state() - Read the current inband link state from the hardware
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment