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

net: phylink: add pcs_enable()/pcs_disable() methods

Add phylink PCS enable/disable callbacks that will allow us to place
IEEE 802.3 register compliant PCS in power-down mode while not being
used.
Signed-off-by: default avatarRussell King (Oracle) <rmk+kernel@armlinux.org.uk>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 6963e463
...@@ -34,6 +34,10 @@ enum { ...@@ -34,6 +34,10 @@ enum {
PHYLINK_DISABLE_STOPPED, PHYLINK_DISABLE_STOPPED,
PHYLINK_DISABLE_LINK, PHYLINK_DISABLE_LINK,
PHYLINK_DISABLE_MAC_WOL, PHYLINK_DISABLE_MAC_WOL,
PCS_STATE_DOWN = 0,
PCS_STATE_STARTING,
PCS_STATE_STARTED,
}; };
/** /**
...@@ -72,6 +76,7 @@ struct phylink { ...@@ -72,6 +76,7 @@ struct phylink {
struct phylink_link_state phy_state; struct phylink_link_state phy_state;
struct work_struct resolve; struct work_struct resolve;
unsigned int pcs_neg_mode; unsigned int pcs_neg_mode;
unsigned int pcs_state;
bool mac_link_dropped; bool mac_link_dropped;
bool using_mac_select_pcs; bool using_mac_select_pcs;
...@@ -993,6 +998,22 @@ static void phylink_resolve_an_pause(struct phylink_link_state *state) ...@@ -993,6 +998,22 @@ static void phylink_resolve_an_pause(struct phylink_link_state *state)
} }
} }
static void phylink_pcs_disable(struct phylink_pcs *pcs)
{
if (pcs && pcs->ops->pcs_disable)
pcs->ops->pcs_disable(pcs);
}
static int phylink_pcs_enable(struct phylink_pcs *pcs)
{
int err = 0;
if (pcs && pcs->ops->pcs_enable)
err = pcs->ops->pcs_enable(pcs);
return err;
}
static int phylink_pcs_config(struct phylink_pcs *pcs, unsigned int neg_mode, static int phylink_pcs_config(struct phylink_pcs *pcs, unsigned int neg_mode,
const struct phylink_link_state *state, const struct phylink_link_state *state,
bool permit_pause_to_mac) bool permit_pause_to_mac)
...@@ -1095,11 +1116,17 @@ static void phylink_major_config(struct phylink *pl, bool restart, ...@@ -1095,11 +1116,17 @@ 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 /* If we have a new PCS, switch to the new PCS after preparing the MAC
* for the change. * for the change.
*/ */
if (pcs_changed) if (pcs_changed) {
phylink_pcs_disable(pl->pcs);
pl->pcs = pcs; pl->pcs = pcs;
}
phylink_mac_config(pl, state); phylink_mac_config(pl, state);
if (pl->pcs_state == PCS_STATE_STARTING || pcs_changed)
phylink_pcs_enable(pl->pcs);
neg_mode = pl->cur_link_an_mode; neg_mode = pl->cur_link_an_mode;
if (pl->pcs && pl->pcs->neg_mode) if (pl->pcs && pl->pcs->neg_mode)
neg_mode = pl->pcs_neg_mode; neg_mode = pl->pcs_neg_mode;
...@@ -1586,6 +1613,7 @@ struct phylink *phylink_create(struct phylink_config *config, ...@@ -1586,6 +1613,7 @@ struct phylink *phylink_create(struct phylink_config *config,
pl->link_config.pause = MLO_PAUSE_AN; pl->link_config.pause = MLO_PAUSE_AN;
pl->link_config.speed = SPEED_UNKNOWN; pl->link_config.speed = SPEED_UNKNOWN;
pl->link_config.duplex = DUPLEX_UNKNOWN; pl->link_config.duplex = DUPLEX_UNKNOWN;
pl->pcs_state = PCS_STATE_DOWN;
pl->mac_ops = mac_ops; pl->mac_ops = mac_ops;
__set_bit(PHYLINK_DISABLE_STOPPED, &pl->phylink_disable_state); __set_bit(PHYLINK_DISABLE_STOPPED, &pl->phylink_disable_state);
timer_setup(&pl->link_poll, phylink_fixed_poll, 0); timer_setup(&pl->link_poll, phylink_fixed_poll, 0);
...@@ -1987,6 +2015,8 @@ void phylink_start(struct phylink *pl) ...@@ -1987,6 +2015,8 @@ void phylink_start(struct phylink *pl)
if (pl->netdev) if (pl->netdev)
netif_carrier_off(pl->netdev); netif_carrier_off(pl->netdev);
pl->pcs_state = PCS_STATE_STARTING;
/* Apply the link configuration to the MAC when starting. This allows /* Apply the link configuration to the MAC when starting. This allows
* a fixed-link to start with the correct parameters, and also * a fixed-link to start with the correct parameters, and also
* ensures that we set the appropriate advertisement for Serdes links. * ensures that we set the appropriate advertisement for Serdes links.
...@@ -1997,6 +2027,8 @@ void phylink_start(struct phylink *pl) ...@@ -1997,6 +2027,8 @@ void phylink_start(struct phylink *pl)
*/ */
phylink_mac_initial_config(pl, true); phylink_mac_initial_config(pl, true);
pl->pcs_state = PCS_STATE_STARTED;
phylink_enable_and_run_resolve(pl, PHYLINK_DISABLE_STOPPED); phylink_enable_and_run_resolve(pl, PHYLINK_DISABLE_STOPPED);
if (pl->cfg_link_an_mode == MLO_AN_FIXED && pl->link_gpio) { if (pl->cfg_link_an_mode == MLO_AN_FIXED && pl->link_gpio) {
...@@ -2015,15 +2047,9 @@ void phylink_start(struct phylink *pl) ...@@ -2015,15 +2047,9 @@ void phylink_start(struct phylink *pl)
poll = true; poll = true;
} }
switch (pl->cfg_link_an_mode) { if (pl->cfg_link_an_mode == MLO_AN_FIXED)
case MLO_AN_FIXED:
poll |= pl->config->poll_fixed_state; poll |= pl->config->poll_fixed_state;
break;
case MLO_AN_INBAND:
if (pl->pcs)
poll |= pl->pcs->poll;
break;
}
if (poll) if (poll)
mod_timer(&pl->link_poll, jiffies + HZ); mod_timer(&pl->link_poll, jiffies + HZ);
if (pl->phydev) if (pl->phydev)
...@@ -2060,6 +2086,10 @@ void phylink_stop(struct phylink *pl) ...@@ -2060,6 +2086,10 @@ void phylink_stop(struct phylink *pl)
} }
phylink_run_resolve_and_disable(pl, PHYLINK_DISABLE_STOPPED); phylink_run_resolve_and_disable(pl, PHYLINK_DISABLE_STOPPED);
pl->pcs_state = PCS_STATE_DOWN;
phylink_pcs_disable(pl->pcs);
} }
EXPORT_SYMBOL_GPL(phylink_stop); EXPORT_SYMBOL_GPL(phylink_stop);
......
...@@ -535,6 +535,8 @@ struct phylink_pcs { ...@@ -535,6 +535,8 @@ struct phylink_pcs {
/** /**
* struct phylink_pcs_ops - MAC PCS operations structure. * struct phylink_pcs_ops - MAC PCS operations structure.
* @pcs_validate: validate the link configuration. * @pcs_validate: validate the link configuration.
* @pcs_enable: enable the PCS.
* @pcs_disable: disable the PCS.
* @pcs_get_state: read the current MAC PCS link state from the hardware. * @pcs_get_state: read the current MAC PCS link state from the hardware.
* @pcs_config: configure the MAC PCS for the selected mode and state. * @pcs_config: configure the MAC PCS for the selected mode and state.
* @pcs_an_restart: restart 802.3z BaseX autonegotiation. * @pcs_an_restart: restart 802.3z BaseX autonegotiation.
...@@ -544,6 +546,8 @@ struct phylink_pcs { ...@@ -544,6 +546,8 @@ struct phylink_pcs {
struct phylink_pcs_ops { struct phylink_pcs_ops {
int (*pcs_validate)(struct phylink_pcs *pcs, unsigned long *supported, int (*pcs_validate)(struct phylink_pcs *pcs, unsigned long *supported,
const struct phylink_link_state *state); const struct phylink_link_state *state);
int (*pcs_enable)(struct phylink_pcs *pcs);
void (*pcs_disable)(struct phylink_pcs *pcs);
void (*pcs_get_state)(struct phylink_pcs *pcs, void (*pcs_get_state)(struct phylink_pcs *pcs,
struct phylink_link_state *state); struct phylink_link_state *state);
int (*pcs_config)(struct phylink_pcs *pcs, unsigned int neg_mode, int (*pcs_config)(struct phylink_pcs *pcs, unsigned int neg_mode,
...@@ -573,6 +577,18 @@ struct phylink_pcs_ops { ...@@ -573,6 +577,18 @@ struct phylink_pcs_ops {
int pcs_validate(struct phylink_pcs *pcs, unsigned long *supported, int pcs_validate(struct phylink_pcs *pcs, unsigned long *supported,
const struct phylink_link_state *state); const struct phylink_link_state *state);
/**
* pcs_enable() - enable the PCS.
* @pcs: a pointer to a &struct phylink_pcs.
*/
int pcs_enable(struct phylink_pcs *pcs);
/**
* pcs_disable() - disable the PCS.
* @pcs: a pointer to a &struct phylink_pcs.
*/
void pcs_disable(struct phylink_pcs *pcs);
/** /**
* pcs_get_state() - Read the current inband link state from the hardware * pcs_get_state() - Read the current inband link state from the hardware
* @pcs: a pointer to a &struct phylink_pcs. * @pcs: a pointer to a &struct phylink_pcs.
......
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