Commit 6dd7f1a1 authored by David S. Miller's avatar David S. Miller

Merge branch 'rework-phylink-interface-for-split-MAC-PCS-support'

Russell King says:

====================
rework phylink interface for split MAC/PCS support

The following series changes the phylink interface to allow us to
better support split MAC / MAC PCS setups.  The fundamental change
required for this turns out to be quite simple.

Today, mac_config() is used for everything to do with setting the
parameters for the MAC, and mac_link_up() is used to inform the
MAC driver that the link is now up (and so to allow packet flow.)
mac_config() also has had a few implementation issues, with folk
who believe that members such as "speed" and "duplex" are always
valid, where "link" gets used inappropriately, etc.

With the proposed patches, all this changes subtly - but in a
backwards compatible way at this stage.

We pass the the full resolved link state (speed, duplex, pause) to
mac_link_up(), and it is now guaranteed that these parameters to
this function will always be valid (no more SPEED_UNKNOWN or
DUPLEX_UNKNOWN here - unless phylink is fed with such things.)

Drivers should convert over to using the state in mac_link_up()
rather than configuring the speed, duplex and pause in the
mac_config() method. The patch series includes a number of MAC
drivers which I've thought have been easy targets - I've left the
remainder as I think they need maintainer input. However, *all*
drivers will need conversion for future phylink development.

v2: add ocelot/felix and qca/ar9331 DSA drivers to patch 2, add
  received tested-by so far.
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 2e6af0f3 24cb72df
......@@ -74,10 +74,13 @@ phylib to the sfp/phylink support. Please send patches to improve
this documentation.
1. Optionally split the network driver's phylib update function into
three parts dealing with link-down, link-up and reconfiguring the
MAC settings. This can be done as a separate preparation commit.
two parts dealing with link-down and link-up. This can be done as
a separate preparation commit.
An example of this preparation can be found in git commit fc548b991fb0.
An older example of this preparation can be found in git commit
fc548b991fb0, although this was splitting into three parts; the
link-up part now includes configuring the MAC for the link settings.
Please see :c:func:`mac_link_up` for more information on this.
2. Replace::
......@@ -207,6 +210,14 @@ this documentation.
using. This is particularly important for in-band negotiation
methods such as 1000base-X and SGMII.
The :c:func:`mac_link_up` method is used to inform the MAC that the
link has come up. The call includes the negotiation mode and interface
for reference only. The finalised link parameters are also supplied
(speed, duplex and flow control/pause enablement settings) which
should be used to configure the MAC when the MAC and PCS are not
tightly integrated, or when the settings are not coming from in-band
negotiation.
The :c:func:`mac_config` method is used to update the MAC with the
requested state, and must avoid unnecessarily taking the link down
when making changes to the MAC configuration. This means the
......
......@@ -1289,7 +1289,9 @@ EXPORT_SYMBOL(b53_phylink_mac_link_down);
void b53_phylink_mac_link_up(struct dsa_switch *ds, int port,
unsigned int mode,
phy_interface_t interface,
struct phy_device *phydev)
struct phy_device *phydev,
int speed, int duplex,
bool tx_pause, bool rx_pause)
{
struct b53_device *dev = ds->priv;
......
......@@ -338,7 +338,9 @@ void b53_phylink_mac_link_down(struct dsa_switch *ds, int port,
void b53_phylink_mac_link_up(struct dsa_switch *ds, int port,
unsigned int mode,
phy_interface_t interface,
struct phy_device *phydev);
struct phy_device *phydev,
int speed, int duplex,
bool tx_pause, bool rx_pause);
int b53_vlan_filtering(struct dsa_switch *ds, int port, bool vlan_filtering);
int b53_vlan_prepare(struct dsa_switch *ds, int port,
const struct switchdev_obj_port_vlan *vlan);
......
......@@ -649,7 +649,9 @@ static void bcm_sf2_sw_mac_link_down(struct dsa_switch *ds, int port,
static void bcm_sf2_sw_mac_link_up(struct dsa_switch *ds, int port,
unsigned int mode,
phy_interface_t interface,
struct phy_device *phydev)
struct phy_device *phydev,
int speed, int duplex,
bool tx_pause, bool rx_pause)
{
struct bcm_sf2_priv *priv = bcm_sf2_to_priv(ds);
struct ethtool_eee *p = &priv->dev->ports[port].eee;
......
......@@ -1517,7 +1517,9 @@ static void gswip_phylink_mac_link_down(struct dsa_switch *ds, int port,
static void gswip_phylink_mac_link_up(struct dsa_switch *ds, int port,
unsigned int mode,
phy_interface_t interface,
struct phy_device *phydev)
struct phy_device *phydev,
int speed, int duplex,
bool tx_pause, bool rx_pause)
{
struct gswip_priv *priv = ds->priv;
......
......@@ -1482,7 +1482,9 @@ static void mt7530_phylink_mac_link_down(struct dsa_switch *ds, int port,
static void mt7530_phylink_mac_link_up(struct dsa_switch *ds, int port,
unsigned int mode,
phy_interface_t interface,
struct phy_device *phydev)
struct phy_device *phydev,
int speed, int duplex,
bool tx_pause, bool rx_pause)
{
struct mt7530_priv *priv = ds->priv;
......
......@@ -632,33 +632,78 @@ static void mv88e6xxx_mac_config(struct dsa_switch *ds, int port,
dev_err(ds->dev, "p%d: failed to configure MAC\n", port);
}
static void mv88e6xxx_mac_link_force(struct dsa_switch *ds, int port, int link)
static void mv88e6xxx_mac_link_down(struct dsa_switch *ds, int port,
unsigned int mode,
phy_interface_t interface)
{
struct mv88e6xxx_chip *chip = ds->priv;
int err;
const struct mv88e6xxx_ops *ops;
int err = 0;
mv88e6xxx_reg_lock(chip);
err = chip->info->ops->port_set_link(chip, port, link);
mv88e6xxx_reg_unlock(chip);
ops = chip->info->ops;
if (err)
dev_err(chip->dev, "p%d: failed to force MAC link\n", port);
}
/* Internal PHYs propagate their configuration directly to the MAC.
* External PHYs depend on whether the PPU is enabled for this port.
* FIXME: we should be using the PPU enable state here. What about
* an automedia port?
*/
if (!mv88e6xxx_phy_is_internal(ds, port) && ops->port_set_link) {
mv88e6xxx_reg_lock(chip);
err = ops->port_set_link(chip, port, LINK_FORCED_DOWN);
mv88e6xxx_reg_unlock(chip);
static void mv88e6xxx_mac_link_down(struct dsa_switch *ds, int port,
unsigned int mode,
phy_interface_t interface)
{
if (mode == MLO_AN_FIXED)
mv88e6xxx_mac_link_force(ds, port, LINK_FORCED_DOWN);
if (err)
dev_err(chip->dev,
"p%d: failed to force MAC link down\n", port);
}
}
static void mv88e6xxx_mac_link_up(struct dsa_switch *ds, int port,
unsigned int mode, phy_interface_t interface,
struct phy_device *phydev)
struct phy_device *phydev,
int speed, int duplex,
bool tx_pause, bool rx_pause)
{
if (mode == MLO_AN_FIXED)
mv88e6xxx_mac_link_force(ds, port, LINK_FORCED_UP);
struct mv88e6xxx_chip *chip = ds->priv;
const struct mv88e6xxx_ops *ops;
int err = 0;
ops = chip->info->ops;
/* Internal PHYs propagate their configuration directly to the MAC.
* External PHYs depend on whether the PPU is enabled for this port.
* FIXME: we should be using the PPU enable state here. What about
* an automedia port?
*/
if (!mv88e6xxx_phy_is_internal(ds, port)) {
mv88e6xxx_reg_lock(chip);
/* FIXME: for an automedia port, should we force the link
* down here - what if the link comes up due to "other" media
* while we're bringing the port up, how is the exclusivity
* handled in the Marvell hardware? E.g. port 4 on 88E6532
* shared between internal PHY and Serdes.
*/
if (ops->port_set_speed) {
err = ops->port_set_speed(chip, port, speed);
if (err && err != -EOPNOTSUPP)
goto error;
}
if (ops->port_set_duplex) {
err = ops->port_set_duplex(chip, port, duplex);
if (err && err != -EOPNOTSUPP)
goto error;
}
if (ops->port_set_link)
err = ops->port_set_link(chip, port, LINK_FORCED_UP);
error:
mv88e6xxx_reg_unlock(chip);
if (err && err != -EOPNOTSUPP)
dev_err(ds->dev,
"p%d: failed to configure MAC link up\n", port);
}
}
static int mv88e6xxx_stats_snapshot(struct mv88e6xxx_chip *chip, int port)
......
......@@ -263,7 +263,9 @@ static void felix_phylink_mac_link_down(struct dsa_switch *ds, int port,
static void felix_phylink_mac_link_up(struct dsa_switch *ds, int port,
unsigned int link_an_mode,
phy_interface_t interface,
struct phy_device *phydev)
struct phy_device *phydev,
int speed, int duplex,
bool tx_pause, bool rx_pause)
{
struct ocelot *ocelot = ds->priv;
struct ocelot_port *ocelot_port = ocelot->ports[port];
......
......@@ -458,7 +458,9 @@ static void ar9331_sw_phylink_mac_link_down(struct dsa_switch *ds, int port,
static void ar9331_sw_phylink_mac_link_up(struct dsa_switch *ds, int port,
unsigned int mode,
phy_interface_t interface,
struct phy_device *phydev)
struct phy_device *phydev,
int speed, int duplex,
bool tx_pause, bool rx_pause)
{
struct ar9331_sw_priv *priv = (struct ar9331_sw_priv *)ds->priv;
struct regmap *regmap = priv->regmap;
......
......@@ -786,7 +786,9 @@ static void sja1105_mac_link_down(struct dsa_switch *ds, int port,
static void sja1105_mac_link_up(struct dsa_switch *ds, int port,
unsigned int mode,
phy_interface_t interface,
struct phy_device *phydev)
struct phy_device *phydev,
int speed, int duplex,
bool tx_pause, bool rx_pause)
{
sja1105_inhibit_tx(ds->priv, BIT(port), false);
}
......
......@@ -1200,7 +1200,6 @@ struct macb {
unsigned int dma_burst_length;
phy_interface_t phy_interface;
int speed;
/* AT91RM9200 transmit */
struct sk_buff *skb; /* holds skb until xmit interrupt completes */
......
......@@ -571,37 +571,20 @@ static void macb_mac_config(struct phylink_config *config, unsigned int mode,
old_ctrl = ctrl = macb_or_gem_readl(bp, NCFGR);
/* Clear all the bits we might set later */
ctrl &= ~(MACB_BIT(SPD) | MACB_BIT(FD) | MACB_BIT(PAE));
if (bp->caps & MACB_CAPS_MACB_IS_EMAC) {
if (state->interface == PHY_INTERFACE_MODE_RMII)
ctrl |= MACB_BIT(RM9200_RMII);
} else {
ctrl &= ~(GEM_BIT(GBE) | GEM_BIT(SGMIIEN) | GEM_BIT(PCSSEL));
/* We do not support MLO_PAUSE_RX yet */
if (state->pause & MLO_PAUSE_TX)
ctrl |= MACB_BIT(PAE);
ctrl &= ~(GEM_BIT(SGMIIEN) | GEM_BIT(PCSSEL));
if (state->interface == PHY_INTERFACE_MODE_SGMII)
ctrl |= GEM_BIT(SGMIIEN) | GEM_BIT(PCSSEL);
}
if (state->speed == SPEED_1000)
ctrl |= GEM_BIT(GBE);
else if (state->speed == SPEED_100)
ctrl |= MACB_BIT(SPD);
if (state->duplex)
ctrl |= MACB_BIT(FD);
/* Apply the new configuration, if any */
if (old_ctrl ^ ctrl)
macb_or_gem_writel(bp, NCFGR, ctrl);
bp->speed = state->speed;
spin_unlock_irqrestore(&bp->lock, flags);
}
......@@ -626,16 +609,42 @@ static void macb_mac_link_down(struct phylink_config *config, unsigned int mode,
netif_tx_stop_all_queues(ndev);
}
static void macb_mac_link_up(struct phylink_config *config, unsigned int mode,
phy_interface_t interface, struct phy_device *phy)
static void macb_mac_link_up(struct phylink_config *config,
struct phy_device *phy,
unsigned int mode, phy_interface_t interface,
int speed, int duplex,
bool tx_pause, bool rx_pause)
{
struct net_device *ndev = to_net_dev(config->dev);
struct macb *bp = netdev_priv(ndev);
struct macb_queue *queue;
unsigned long flags;
unsigned int q;
u32 ctrl;
spin_lock_irqsave(&bp->lock, flags);
ctrl = macb_or_gem_readl(bp, NCFGR);
ctrl &= ~(MACB_BIT(SPD) | MACB_BIT(FD));
if (speed == SPEED_100)
ctrl |= MACB_BIT(SPD);
if (duplex)
ctrl |= MACB_BIT(FD);
if (!(bp->caps & MACB_CAPS_MACB_IS_EMAC)) {
macb_set_tx_clk(bp->tx_clk, bp->speed, ndev);
ctrl &= ~(GEM_BIT(GBE) | MACB_BIT(PAE));
if (speed == SPEED_1000)
ctrl |= GEM_BIT(GBE);
/* We do not support MLO_PAUSE_RX yet */
if (tx_pause)
ctrl |= MACB_BIT(PAE);
macb_set_tx_clk(bp->tx_clk, speed, ndev);
/* Initialize rings & buffers as clearing MACB_BIT(TE) in link down
* cleared the pipeline and control registers.
......@@ -648,6 +657,10 @@ static void macb_mac_link_up(struct phylink_config *config, unsigned int mode,
bp->rx_intr_mask | MACB_TX_INT_FLAGS | MACB_BIT(HRESP));
}
macb_or_gem_writel(bp, NCFGR, ctrl);
spin_unlock_irqrestore(&bp->lock, flags);
/* Enable Rx and Tx */
macb_writel(bp, NCR, macb_readl(bp, NCR) | MACB_BIT(RE) | MACB_BIT(TE));
......@@ -4429,8 +4442,6 @@ static int macb_probe(struct platform_device *pdev)
else
bp->phy_interface = interface;
bp->speed = SPEED_UNKNOWN;
/* IP specific init */
err = init(pdev);
if (err)
......
......@@ -123,49 +123,60 @@ static void dpaa2_mac_config(struct phylink_config *config, unsigned int mode,
struct dpmac_link_state *dpmac_state = &mac->state;
int err;
if (state->speed != SPEED_UNKNOWN)
dpmac_state->rate = state->speed;
if (state->duplex != DUPLEX_UNKNOWN) {
if (!state->duplex)
dpmac_state->options |= DPMAC_LINK_OPT_HALF_DUPLEX;
else
dpmac_state->options &= ~DPMAC_LINK_OPT_HALF_DUPLEX;
}
if (state->an_enabled)
dpmac_state->options |= DPMAC_LINK_OPT_AUTONEG;
else
dpmac_state->options &= ~DPMAC_LINK_OPT_AUTONEG;
if (state->pause & MLO_PAUSE_RX)
dpmac_state->options |= DPMAC_LINK_OPT_PAUSE;
else
dpmac_state->options &= ~DPMAC_LINK_OPT_PAUSE;
if (!!(state->pause & MLO_PAUSE_RX) ^ !!(state->pause & MLO_PAUSE_TX))
dpmac_state->options |= DPMAC_LINK_OPT_ASYM_PAUSE;
else
dpmac_state->options &= ~DPMAC_LINK_OPT_ASYM_PAUSE;
err = dpmac_set_link_state(mac->mc_io, 0,
mac->mc_dev->mc_handle, dpmac_state);
if (err)
netdev_err(mac->net_dev, "dpmac_set_link_state() = %d\n", err);
netdev_err(mac->net_dev, "%s: dpmac_set_link_state() = %d\n",
__func__, err);
}
static void dpaa2_mac_link_up(struct phylink_config *config, unsigned int mode,
phy_interface_t interface, struct phy_device *phy)
static void dpaa2_mac_link_up(struct phylink_config *config,
struct phy_device *phy,
unsigned int mode, phy_interface_t interface,
int speed, int duplex,
bool tx_pause, bool rx_pause)
{
struct dpaa2_mac *mac = phylink_to_dpaa2_mac(config);
struct dpmac_link_state *dpmac_state = &mac->state;
int err;
dpmac_state->up = 1;
if (mac->if_link_type == DPMAC_LINK_TYPE_PHY) {
/* If the DPMAC is configured for PHY mode, we need
* to pass the link parameters to the MC firmware.
*/
dpmac_state->rate = speed;
if (duplex == DUPLEX_HALF)
dpmac_state->options |= DPMAC_LINK_OPT_HALF_DUPLEX;
else if (duplex == DUPLEX_FULL)
dpmac_state->options &= ~DPMAC_LINK_OPT_HALF_DUPLEX;
/* This is lossy; the firmware really should take the pause
* enablement status rather than pause/asym pause status.
*/
if (rx_pause)
dpmac_state->options |= DPMAC_LINK_OPT_PAUSE;
else
dpmac_state->options &= ~DPMAC_LINK_OPT_PAUSE;
if (rx_pause ^ tx_pause)
dpmac_state->options |= DPMAC_LINK_OPT_ASYM_PAUSE;
else
dpmac_state->options &= ~DPMAC_LINK_OPT_ASYM_PAUSE;
}
err = dpmac_set_link_state(mac->mc_io, 0,
mac->mc_dev->mc_handle, dpmac_state);
if (err)
netdev_err(mac->net_dev, "dpmac_set_link_state() = %d\n", err);
netdev_err(mac->net_dev, "%s: dpmac_set_link_state() = %d\n",
__func__, err);
}
static void dpaa2_mac_link_down(struct phylink_config *config,
......@@ -238,6 +249,8 @@ int dpaa2_mac_connect(struct dpaa2_mac *mac)
goto err_close_dpmac;
}
mac->if_link_type = attr.link_type;
dpmac_node = dpaa2_mac_get_node(attr.id);
if (!dpmac_node) {
netdev_err(net_dev, "No dpmac@%d node found.\n", attr.id);
......
......@@ -20,6 +20,7 @@ struct dpaa2_mac {
struct phylink_config phylink_config;
struct phylink *phylink;
phy_interface_t if_mode;
enum dpmac_link_type if_link_type;
};
bool dpaa2_mac_is_type_fixed(struct fsl_mc_device *dpmac_dev,
......
......@@ -3830,13 +3830,9 @@ static void mvneta_mac_config(struct phylink_config *config, unsigned int mode,
new_clk = gmac_clk & ~MVNETA_GMAC_1MS_CLOCK_ENABLE;
new_an = gmac_an & ~(MVNETA_GMAC_INBAND_AN_ENABLE |
MVNETA_GMAC_INBAND_RESTART_AN |
MVNETA_GMAC_CONFIG_MII_SPEED |
MVNETA_GMAC_CONFIG_GMII_SPEED |
MVNETA_GMAC_AN_SPEED_EN |
MVNETA_GMAC_ADVERT_SYM_FLOW_CTRL |
MVNETA_GMAC_CONFIG_FLOW_CTRL |
MVNETA_GMAC_AN_FLOW_CTRL_EN |
MVNETA_GMAC_CONFIG_FULL_DUPLEX |
MVNETA_GMAC_AN_DUPLEX_EN);
/* Even though it might look weird, when we're configured in
......@@ -3851,24 +3847,20 @@ static void mvneta_mac_config(struct phylink_config *config, unsigned int mode,
if (phylink_test(state->advertising, Pause))
new_an |= MVNETA_GMAC_ADVERT_SYM_FLOW_CTRL;
if (state->pause & MLO_PAUSE_TXRX_MASK)
new_an |= MVNETA_GMAC_CONFIG_FLOW_CTRL;
if (!phylink_autoneg_inband(mode)) {
/* Phy or fixed speed */
if (state->duplex)
new_an |= MVNETA_GMAC_CONFIG_FULL_DUPLEX;
if (state->speed == SPEED_1000 || state->speed == SPEED_2500)
new_an |= MVNETA_GMAC_CONFIG_GMII_SPEED;
else if (state->speed == SPEED_100)
new_an |= MVNETA_GMAC_CONFIG_MII_SPEED;
/* Phy or fixed speed - nothing to do, leave the
* configured speed, duplex and flow control as-is.
*/
} else if (state->interface == PHY_INTERFACE_MODE_SGMII) {
/* SGMII mode receives the state from the PHY */
new_ctrl2 |= MVNETA_GMAC2_INBAND_AN_ENABLE;
new_clk |= MVNETA_GMAC_1MS_CLOCK_ENABLE;
new_an = (new_an & ~(MVNETA_GMAC_FORCE_LINK_DOWN |
MVNETA_GMAC_FORCE_LINK_PASS)) |
MVNETA_GMAC_FORCE_LINK_PASS |
MVNETA_GMAC_CONFIG_MII_SPEED |
MVNETA_GMAC_CONFIG_GMII_SPEED |
MVNETA_GMAC_CONFIG_FULL_DUPLEX)) |
MVNETA_GMAC_INBAND_AN_ENABLE |
MVNETA_GMAC_AN_SPEED_EN |
MVNETA_GMAC_AN_DUPLEX_EN;
......@@ -3877,7 +3869,8 @@ static void mvneta_mac_config(struct phylink_config *config, unsigned int mode,
new_ctrl0 |= MVNETA_GMAC0_PORT_1000BASE_X;
new_clk |= MVNETA_GMAC_1MS_CLOCK_ENABLE;
new_an = (new_an & ~(MVNETA_GMAC_FORCE_LINK_DOWN |
MVNETA_GMAC_FORCE_LINK_PASS)) |
MVNETA_GMAC_FORCE_LINK_PASS |
MVNETA_GMAC_CONFIG_MII_SPEED)) |
MVNETA_GMAC_INBAND_AN_ENABLE |
MVNETA_GMAC_CONFIG_GMII_SPEED |
/* The MAC only supports FD mode */
......@@ -3965,9 +3958,11 @@ static void mvneta_mac_link_down(struct phylink_config *config,
mvneta_set_eee(pp, false);
}
static void mvneta_mac_link_up(struct phylink_config *config, unsigned int mode,
phy_interface_t interface,
struct phy_device *phy)
static void mvneta_mac_link_up(struct phylink_config *config,
struct phy_device *phy,
unsigned int mode, phy_interface_t interface,
int speed, int duplex,
bool tx_pause, bool rx_pause)
{
struct net_device *ndev = to_net_dev(config->dev);
struct mvneta_port *pp = netdev_priv(ndev);
......@@ -3975,8 +3970,36 @@ static void mvneta_mac_link_up(struct phylink_config *config, unsigned int mode,
if (!phylink_autoneg_inband(mode)) {
val = mvreg_read(pp, MVNETA_GMAC_AUTONEG_CONFIG);
val &= ~MVNETA_GMAC_FORCE_LINK_DOWN;
val &= ~(MVNETA_GMAC_FORCE_LINK_DOWN |
MVNETA_GMAC_CONFIG_MII_SPEED |
MVNETA_GMAC_CONFIG_GMII_SPEED |
MVNETA_GMAC_CONFIG_FLOW_CTRL |
MVNETA_GMAC_CONFIG_FULL_DUPLEX);
val |= MVNETA_GMAC_FORCE_LINK_PASS;
if (speed == SPEED_1000 || speed == SPEED_2500)
val |= MVNETA_GMAC_CONFIG_GMII_SPEED;
else if (speed == SPEED_100)
val |= MVNETA_GMAC_CONFIG_MII_SPEED;
if (duplex == DUPLEX_FULL)
val |= MVNETA_GMAC_CONFIG_FULL_DUPLEX;
if (tx_pause || rx_pause)
val |= MVNETA_GMAC_CONFIG_FLOW_CTRL;
mvreg_write(pp, MVNETA_GMAC_AUTONEG_CONFIG, val);
} else {
/* When inband doesn't cover flow control or flow control is
* disabled, we need to manually configure it. This bit will
* only have effect if MVNETA_GMAC_AN_FLOW_CTRL_EN is unset.
*/
val = mvreg_read(pp, MVNETA_GMAC_AUTONEG_CONFIG);
val &= ~MVNETA_GMAC_CONFIG_FLOW_CTRL;
if (tx_pause || rx_pause)
val |= MVNETA_GMAC_CONFIG_FLOW_CTRL;
mvreg_write(pp, MVNETA_GMAC_AUTONEG_CONFIG, val);
}
......
......@@ -58,8 +58,11 @@ static struct {
*/
static void mvpp2_mac_config(struct phylink_config *config, unsigned int mode,
const struct phylink_link_state *state);
static void mvpp2_mac_link_up(struct phylink_config *config, unsigned int mode,
phy_interface_t interface, struct phy_device *phy);
static void mvpp2_mac_link_up(struct phylink_config *config,
struct phy_device *phy,
unsigned int mode, phy_interface_t interface,
int speed, int duplex,
bool tx_pause, bool rx_pause);
/* Queue modes */
#define MVPP2_QDIST_SINGLE_MODE 0
......@@ -3473,8 +3476,9 @@ static void mvpp2_start_dev(struct mvpp2_port *port)
.interface = port->phy_interface,
};
mvpp2_mac_config(&port->phylink_config, MLO_AN_INBAND, &state);
mvpp2_mac_link_up(&port->phylink_config, MLO_AN_INBAND,
port->phy_interface, NULL);
mvpp2_mac_link_up(&port->phylink_config, NULL,
MLO_AN_INBAND, port->phy_interface,
SPEED_UNKNOWN, DUPLEX_UNKNOWN, false, false);
}
netif_tx_start_all_queues(port->dev);
......@@ -4972,15 +4976,13 @@ static void mvpp2_gmac_config(struct mvpp2_port *port, unsigned int mode,
old_ctrl2 = ctrl2 = readl(port->base + MVPP2_GMAC_CTRL_2_REG);
old_ctrl4 = ctrl4 = readl(port->base + MVPP22_GMAC_CTRL_4_REG);
an &= ~(MVPP2_GMAC_CONFIG_MII_SPEED | MVPP2_GMAC_CONFIG_GMII_SPEED |
MVPP2_GMAC_AN_SPEED_EN | MVPP2_GMAC_FC_ADV_EN |
an &= ~(MVPP2_GMAC_AN_SPEED_EN | MVPP2_GMAC_FC_ADV_EN |
MVPP2_GMAC_FC_ADV_ASM_EN | MVPP2_GMAC_FLOW_CTRL_AUTONEG |
MVPP2_GMAC_CONFIG_FULL_DUPLEX | MVPP2_GMAC_AN_DUPLEX_EN |
MVPP2_GMAC_IN_BAND_AUTONEG | MVPP2_GMAC_IN_BAND_AUTONEG_BYPASS);
MVPP2_GMAC_AN_DUPLEX_EN | MVPP2_GMAC_IN_BAND_AUTONEG |
MVPP2_GMAC_IN_BAND_AUTONEG_BYPASS);
ctrl0 &= ~MVPP2_GMAC_PORT_TYPE_MASK;
ctrl2 &= ~(MVPP2_GMAC_INBAND_AN_MASK | MVPP2_GMAC_PORT_RESET_MASK |
MVPP2_GMAC_PCS_ENABLE_MASK);
ctrl4 &= ~(MVPP22_CTRL4_RX_FC_EN | MVPP22_CTRL4_TX_FC_EN);
/* Configure port type */
if (phy_interface_mode_is_8023z(state->interface)) {
......@@ -5010,31 +5012,20 @@ static void mvpp2_gmac_config(struct mvpp2_port *port, unsigned int mode,
/* Configure negotiation style */
if (!phylink_autoneg_inband(mode)) {
/* Phy or fixed speed - no in-band AN */
if (state->duplex)
an |= MVPP2_GMAC_CONFIG_FULL_DUPLEX;
if (state->speed == SPEED_1000 || state->speed == SPEED_2500)
an |= MVPP2_GMAC_CONFIG_GMII_SPEED;
else if (state->speed == SPEED_100)
an |= MVPP2_GMAC_CONFIG_MII_SPEED;
if (state->pause & MLO_PAUSE_TX)
ctrl4 |= MVPP22_CTRL4_TX_FC_EN;
if (state->pause & MLO_PAUSE_RX)
ctrl4 |= MVPP22_CTRL4_RX_FC_EN;
/* Phy or fixed speed - no in-band AN, nothing to do, leave the
* configured speed, duplex and flow control as-is.
*/
} else if (state->interface == PHY_INTERFACE_MODE_SGMII) {
/* SGMII in-band mode receives the speed and duplex from
* the PHY. Flow control information is not received. */
an &= ~(MVPP2_GMAC_FORCE_LINK_DOWN | MVPP2_GMAC_FORCE_LINK_PASS);
an &= ~(MVPP2_GMAC_FORCE_LINK_DOWN |
MVPP2_GMAC_FORCE_LINK_PASS |
MVPP2_GMAC_CONFIG_MII_SPEED |
MVPP2_GMAC_CONFIG_GMII_SPEED |
MVPP2_GMAC_CONFIG_FULL_DUPLEX);
an |= MVPP2_GMAC_IN_BAND_AUTONEG |
MVPP2_GMAC_AN_SPEED_EN |
MVPP2_GMAC_AN_DUPLEX_EN;
if (state->pause & MLO_PAUSE_TX)
ctrl4 |= MVPP22_CTRL4_TX_FC_EN;
if (state->pause & MLO_PAUSE_RX)
ctrl4 |= MVPP22_CTRL4_RX_FC_EN;
} else if (phy_interface_mode_is_8023z(state->interface)) {
/* 1000BaseX and 2500BaseX ports cannot negotiate speed nor can
* they negotiate duplex: they are always operating with a fixed
......@@ -5042,19 +5033,17 @@ static void mvpp2_gmac_config(struct mvpp2_port *port, unsigned int mode,
* speed and full duplex here.
*/
ctrl0 |= MVPP2_GMAC_PORT_TYPE_MASK;
an &= ~(MVPP2_GMAC_FORCE_LINK_DOWN | MVPP2_GMAC_FORCE_LINK_PASS);
an &= ~(MVPP2_GMAC_FORCE_LINK_DOWN |
MVPP2_GMAC_FORCE_LINK_PASS |
MVPP2_GMAC_CONFIG_MII_SPEED |
MVPP2_GMAC_CONFIG_GMII_SPEED |
MVPP2_GMAC_CONFIG_FULL_DUPLEX);
an |= MVPP2_GMAC_IN_BAND_AUTONEG |
MVPP2_GMAC_CONFIG_GMII_SPEED |
MVPP2_GMAC_CONFIG_FULL_DUPLEX;
if (state->pause & MLO_PAUSE_AN && state->an_enabled) {
if (state->pause & MLO_PAUSE_AN && state->an_enabled)
an |= MVPP2_GMAC_FLOW_CTRL_AUTONEG;
} else {
if (state->pause & MLO_PAUSE_TX)
ctrl4 |= MVPP22_CTRL4_TX_FC_EN;
if (state->pause & MLO_PAUSE_RX)
ctrl4 |= MVPP22_CTRL4_RX_FC_EN;
}
}
/* Some fields of the auto-negotiation register require the port to be down when
......@@ -5141,25 +5130,54 @@ static void mvpp2_mac_config(struct phylink_config *config, unsigned int mode,
mvpp2_port_enable(port);
}
static void mvpp2_mac_link_up(struct phylink_config *config, unsigned int mode,
phy_interface_t interface, struct phy_device *phy)
static void mvpp2_mac_link_up(struct phylink_config *config,
struct phy_device *phy,
unsigned int mode, phy_interface_t interface,
int speed, int duplex,
bool tx_pause, bool rx_pause)
{
struct net_device *dev = to_net_dev(config->dev);
struct mvpp2_port *port = netdev_priv(dev);
u32 val;
if (!phylink_autoneg_inband(mode)) {
if (mvpp2_is_xlg(interface)) {
if (mvpp2_is_xlg(interface)) {
if (!phylink_autoneg_inband(mode)) {
val = readl(port->base + MVPP22_XLG_CTRL0_REG);
val &= ~MVPP22_XLG_CTRL0_FORCE_LINK_DOWN;
val |= MVPP22_XLG_CTRL0_FORCE_LINK_PASS;
writel(val, port->base + MVPP22_XLG_CTRL0_REG);
} else {
}
} else {
if (!phylink_autoneg_inband(mode)) {
val = readl(port->base + MVPP2_GMAC_AUTONEG_CONFIG);
val &= ~MVPP2_GMAC_FORCE_LINK_DOWN;
val &= ~(MVPP2_GMAC_FORCE_LINK_DOWN |
MVPP2_GMAC_CONFIG_MII_SPEED |
MVPP2_GMAC_CONFIG_GMII_SPEED |
MVPP2_GMAC_CONFIG_FULL_DUPLEX);
val |= MVPP2_GMAC_FORCE_LINK_PASS;
if (speed == SPEED_1000 || speed == SPEED_2500)
val |= MVPP2_GMAC_CONFIG_GMII_SPEED;
else if (speed == SPEED_100)
val |= MVPP2_GMAC_CONFIG_MII_SPEED;
if (duplex == DUPLEX_FULL)
val |= MVPP2_GMAC_CONFIG_FULL_DUPLEX;
writel(val, port->base + MVPP2_GMAC_AUTONEG_CONFIG);
}
/* We can always update the flow control enable bits;
* these will only be effective if flow control AN
* (MVPP2_GMAC_FLOW_CTRL_AUTONEG) is disabled.
*/
val = readl(port->base + MVPP22_GMAC_CTRL_4_REG);
val &= ~(MVPP22_CTRL4_RX_FC_EN | MVPP22_CTRL4_TX_FC_EN);
if (tx_pause)
val |= MVPP22_CTRL4_TX_FC_EN;
if (rx_pause)
val |= MVPP22_CTRL4_RX_FC_EN;
writel(val, port->base + MVPP22_GMAC_CTRL_4_REG);
}
mvpp2_port_enable(port);
......
......@@ -412,9 +412,10 @@ static void mtk_mac_link_down(struct phylink_config *config, unsigned int mode,
mtk_w32(mac->hw, mcr, MTK_MAC_MCR(mac->id));
}
static void mtk_mac_link_up(struct phylink_config *config, unsigned int mode,
phy_interface_t interface,
struct phy_device *phy)
static void mtk_mac_link_up(struct phylink_config *config,
struct phy_device *phy,
unsigned int mode, phy_interface_t interface,
int speed, int duplex, bool tx_pause, bool rx_pause)
{
struct mtk_mac *mac = container_of(config, struct mtk_mac,
phylink_config);
......
......@@ -950,8 +950,10 @@ static void stmmac_mac_link_down(struct phylink_config *config,
}
static void stmmac_mac_link_up(struct phylink_config *config,
struct phy_device *phy,
unsigned int mode, phy_interface_t interface,
struct phy_device *phy)
int speed, int duplex,
bool tx_pause, bool rx_pause)
{
struct stmmac_priv *priv = netdev_priv(to_net_dev(config->dev));
......
......@@ -1440,6 +1440,22 @@ static void axienet_mac_an_restart(struct phylink_config *config)
static void axienet_mac_config(struct phylink_config *config, unsigned int mode,
const struct phylink_link_state *state)
{
/* nothing meaningful to do */
}
static void axienet_mac_link_down(struct phylink_config *config,
unsigned int mode,
phy_interface_t interface)
{
/* nothing meaningful to do */
}
static void axienet_mac_link_up(struct phylink_config *config,
struct phy_device *phy,
unsigned int mode, phy_interface_t interface,
int speed, int duplex,
bool tx_pause, bool rx_pause)
{
struct net_device *ndev = to_net_dev(config->dev);
struct axienet_local *lp = netdev_priv(ndev);
......@@ -1448,7 +1464,7 @@ static void axienet_mac_config(struct phylink_config *config, unsigned int mode,
emmc_reg = axienet_ior(lp, XAE_EMMC_OFFSET);
emmc_reg &= ~XAE_EMMC_LINKSPEED_MASK;
switch (state->speed) {
switch (speed) {
case SPEED_1000:
emmc_reg |= XAE_EMMC_LINKSPD_1000;
break;
......@@ -1467,32 +1483,17 @@ static void axienet_mac_config(struct phylink_config *config, unsigned int mode,
axienet_iow(lp, XAE_EMMC_OFFSET, emmc_reg);
fcc_reg = axienet_ior(lp, XAE_FCC_OFFSET);
if (state->pause & MLO_PAUSE_TX)
if (tx_pause)
fcc_reg |= XAE_FCC_FCTX_MASK;
else
fcc_reg &= ~XAE_FCC_FCTX_MASK;
if (state->pause & MLO_PAUSE_RX)
if (rx_pause)
fcc_reg |= XAE_FCC_FCRX_MASK;
else
fcc_reg &= ~XAE_FCC_FCRX_MASK;
axienet_iow(lp, XAE_FCC_OFFSET, fcc_reg);
}
static void axienet_mac_link_down(struct phylink_config *config,
unsigned int mode,
phy_interface_t interface)
{
/* nothing meaningful to do */
}
static void axienet_mac_link_up(struct phylink_config *config,
unsigned int mode,
phy_interface_t interface,
struct phy_device *phy)
{
/* nothing meaningful to do */
}
static const struct phylink_mac_ops axienet_phylink_ops = {
.validate = axienet_validate,
.mac_pcs_get_state = axienet_mac_pcs_get_state,
......
......@@ -480,8 +480,11 @@ static void phylink_mac_link_up(struct phylink *pl,
struct net_device *ndev = pl->netdev;
pl->cur_interface = link_state.interface;
pl->ops->mac_link_up(pl->config, pl->cur_link_an_mode,
pl->cur_interface, pl->phydev);
pl->ops->mac_link_up(pl->config, pl->phydev,
pl->cur_link_an_mode, pl->cur_interface,
link_state.speed, link_state.duplex,
!!(link_state.pause & MLO_PAUSE_TX),
!!(link_state.pause & MLO_PAUSE_RX));
if (ndev)
netif_carrier_on(ndev);
......@@ -547,6 +550,8 @@ static void phylink_resolve(struct work_struct *w)
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);
}
break;
}
......
......@@ -91,9 +91,10 @@ struct phylink_mac_ops {
void (*mac_an_restart)(struct phylink_config *config);
void (*mac_link_down)(struct phylink_config *config, unsigned int mode,
phy_interface_t interface);
void (*mac_link_up)(struct phylink_config *config, unsigned int mode,
phy_interface_t interface,
struct phy_device *phy);
void (*mac_link_up)(struct phylink_config *config,
struct phy_device *phy, unsigned int mode,
phy_interface_t interface, int speed, int duplex,
bool tx_pause, bool rx_pause);
};
#if 0 /* For kernel-doc purposes only. */
......@@ -152,6 +153,9 @@ void mac_pcs_get_state(struct phylink_config *config,
* guaranteed to be correct, and so any mac_config() implementation must
* never reference these fields.
*
* (this requires a rewrite - please refer to mac_link_up() for situations
* where the PCS and MAC are not tightly integrated.)
*
* In all negotiation modes, as defined by @mode, @state->pause indicates the
* pause settings which should be applied as follows. If %MLO_PAUSE_AN is not
* set, %MLO_PAUSE_TX and %MLO_PAUSE_RX indicate whether the MAC should send
......@@ -162,12 +166,20 @@ void mac_pcs_get_state(struct phylink_config *config,
* The action performed depends on the currently selected mode:
*
* %MLO_AN_FIXED, %MLO_AN_PHY:
* Configure the specified @state->speed and @state->duplex over a link
* specified by @state->interface. @state->advertising may be used, but
* is not required. Pause modes as above. Other members of @state must
* be ignored.
* Configure for non-inband negotiation mode, where the link settings
* are completely communicated via mac_link_up(). The physical link
* protocol from the MAC is specified by @state->interface.
*
* @state->advertising may be used, but is not required.
*
* Older drivers (prior to the mac_link_up() change) may use @state->speed,
* @state->duplex and @state->pause to configure the MAC, but this is
* deprecated; such drivers should be converted to use mac_link_up().
*
* Valid state members: interface, speed, duplex, pause, advertising.
* Other members of @state must be ignored.
*
* Valid state members: interface, advertising.
* Deprecated state members: speed, duplex, pause.
*
* %MLO_AN_INBAND:
* place the link in an inband negotiation mode (such as 802.3z
......@@ -228,19 +240,34 @@ void mac_link_down(struct phylink_config *config, unsigned int mode,
/**
* mac_link_up() - allow the link to come up
* @config: a pointer to a &struct phylink_config.
* @phy: any attached phy
* @mode: link autonegotiation mode
* @interface: link &typedef phy_interface_t mode
* @phy: any attached phy
* @speed: link speed
* @duplex: link duplex
* @tx_pause: link transmit pause enablement status
* @rx_pause: link receive pause enablement status
*
* If @mode is not an in-band negotiation mode (as defined by
* phylink_autoneg_inband()), allow the link to come up. If @phy
* is non-%NULL, configure Energy Efficient Ethernet by calling
* Configure the MAC for an established link.
*
* @speed, @duplex, @tx_pause and @rx_pause indicate the finalised link
* settings, and should be used to configure the MAC block appropriately
* where these settings are not automatically conveyed from the PCS block,
* or if in-band negotiation (as defined by phylink_autoneg_inband(@mode))
* is disabled.
*
* Note that when 802.3z in-band negotiation is in use, it is possible
* that the user wishes to override the pause settings, and this should
* be allowed when considering the implementation of this method.
*
* If in-band negotiation mode is disabled, allow the link to come up. If
* @phy is non-%NULL, configure Energy Efficient Ethernet by calling
* phy_init_eee() and perform appropriate MAC configuration for EEE.
* Interface type selection must be done in mac_config().
*/
void mac_link_up(struct phylink_config *config, unsigned int mode,
phy_interface_t interface,
struct phy_device *phy);
void mac_link_up(struct phylink_config *config, struct phy_device *phy,
unsigned int mode, phy_interface_t interface,
int speed, int duplex, bool tx_pause, bool rx_pause);
#endif
struct phylink *phylink_create(struct phylink_config *, struct fwnode_handle *,
......
......@@ -420,7 +420,9 @@ struct dsa_switch_ops {
void (*phylink_mac_link_up)(struct dsa_switch *ds, int port,
unsigned int mode,
phy_interface_t interface,
struct phy_device *phydev);
struct phy_device *phydev,
int speed, int duplex,
bool tx_pause, bool rx_pause);
void (*phylink_fixed_state)(struct dsa_switch *ds, int port,
struct phylink_link_state *state);
/*
......
......@@ -489,9 +489,11 @@ static void dsa_port_phylink_mac_link_down(struct phylink_config *config,
}
static void dsa_port_phylink_mac_link_up(struct phylink_config *config,
struct phy_device *phydev,
unsigned int mode,
phy_interface_t interface,
struct phy_device *phydev)
int speed, int duplex,
bool tx_pause, bool rx_pause)
{
struct dsa_port *dp = container_of(config, struct dsa_port, pl_config);
struct dsa_switch *ds = dp->ds;
......@@ -502,7 +504,8 @@ static void dsa_port_phylink_mac_link_up(struct phylink_config *config,
return;
}
ds->ops->phylink_mac_link_up(ds, dp->index, mode, interface, phydev);
ds->ops->phylink_mac_link_up(ds, dp->index, mode, interface, phydev,
speed, duplex, tx_pause, rx_pause);
}
const struct phylink_mac_ops dsa_port_phylink_mac_ops = {
......
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