net: dsa: felix: use resolved link config in mac_link_up()

Phylink now requires that parameters established through
auto-negotiation be written into the MAC at the time of the
mac_link_up() callback. In the case of felix, that means taking the port
out of reset, setting the correct timers for PAUSE frames, and
enabling/disabling TX flow control.

This patch also splits the inband and noinband configuration of the
vsc9959 PCS (currently found in a function called "init") into 2
different functions, which have a nomenclature closer to phylink:
"config", for inband setup, and "link_up", for noinband (forced) setup.

This is necessary as a preparation step for giving up control of the PCS
to phylink, which will be done in further patch series.

Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
Vladimir Oltean 2020-07-05 19:16:26 +03:00 committed by David S. Miller
parent b4c2354537
commit 7e14a2dc8c
3 changed files with 213 additions and 182 deletions

View File

@ -235,65 +235,10 @@ static void felix_phylink_mac_config(struct dsa_switch *ds, int port,
const struct phylink_link_state *state)
{
struct ocelot *ocelot = ds->priv;
struct ocelot_port *ocelot_port = ocelot->ports[port];
struct felix *felix = ocelot_to_felix(ocelot);
u32 mac_fc_cfg;
/* Take port out of reset by clearing the MAC_TX_RST, MAC_RX_RST and
* PORT_RST bits in DEV_CLOCK_CFG. Note that the way this system is
* integrated is that the MAC speed is fixed and it's the PCS who is
* performing the rate adaptation, so we have to write "1000Mbps" into
* the LINK_SPEED field of DEV_CLOCK_CFG (which is also its default
* value).
*/
ocelot_port_writel(ocelot_port,
DEV_CLOCK_CFG_LINK_SPEED(OCELOT_SPEED_1000),
DEV_CLOCK_CFG);
switch (state->speed) {
case SPEED_10:
mac_fc_cfg = SYS_MAC_FC_CFG_FC_LINK_SPEED(3);
break;
case SPEED_100:
mac_fc_cfg = SYS_MAC_FC_CFG_FC_LINK_SPEED(2);
break;
case SPEED_1000:
case SPEED_2500:
mac_fc_cfg = SYS_MAC_FC_CFG_FC_LINK_SPEED(1);
break;
case SPEED_UNKNOWN:
mac_fc_cfg = SYS_MAC_FC_CFG_FC_LINK_SPEED(0);
break;
default:
dev_err(ocelot->dev, "Unsupported speed on port %d: %d\n",
port, state->speed);
return;
}
/* handle Rx pause in all cases, with 2500base-X this is used for rate
* adaptation.
*/
mac_fc_cfg |= SYS_MAC_FC_CFG_RX_FC_ENA;
if (state->pause & MLO_PAUSE_TX)
mac_fc_cfg |= SYS_MAC_FC_CFG_TX_FC_ENA |
SYS_MAC_FC_CFG_PAUSE_VAL_CFG(0xffff) |
SYS_MAC_FC_CFG_FC_LATENCY_CFG(0x7) |
SYS_MAC_FC_CFG_ZERO_PAUSE_ENA;
/* Flow control. Link speed is only used here to evaluate the time
* specification in incoming pause frames.
*/
ocelot_write_rix(ocelot, mac_fc_cfg, SYS_MAC_FC_CFG, port);
ocelot_write_rix(ocelot, 0, ANA_POL_FLOWC, port);
if (felix->info->pcs_init)
felix->info->pcs_init(ocelot, port, link_an_mode, state);
if (felix->info->port_sched_speed_set)
felix->info->port_sched_speed_set(ocelot, port,
state->speed);
if (felix->info->pcs_config)
felix->info->pcs_config(ocelot, port, link_an_mode, state);
}
static void felix_phylink_mac_link_down(struct dsa_switch *ds, int port,
@ -317,8 +262,58 @@ static void felix_phylink_mac_link_up(struct dsa_switch *ds, int port,
{
struct ocelot *ocelot = ds->priv;
struct ocelot_port *ocelot_port = ocelot->ports[port];
struct felix *felix = ocelot_to_felix(ocelot);
u32 mac_fc_cfg;
/* Enable MAC module */
/* Take port out of reset by clearing the MAC_TX_RST, MAC_RX_RST and
* PORT_RST bits in DEV_CLOCK_CFG. Note that the way this system is
* integrated is that the MAC speed is fixed and it's the PCS who is
* performing the rate adaptation, so we have to write "1000Mbps" into
* the LINK_SPEED field of DEV_CLOCK_CFG (which is also its default
* value).
*/
ocelot_port_writel(ocelot_port,
DEV_CLOCK_CFG_LINK_SPEED(OCELOT_SPEED_1000),
DEV_CLOCK_CFG);
switch (speed) {
case SPEED_10:
mac_fc_cfg = SYS_MAC_FC_CFG_FC_LINK_SPEED(3);
break;
case SPEED_100:
mac_fc_cfg = SYS_MAC_FC_CFG_FC_LINK_SPEED(2);
break;
case SPEED_1000:
case SPEED_2500:
mac_fc_cfg = SYS_MAC_FC_CFG_FC_LINK_SPEED(1);
break;
default:
dev_err(ocelot->dev, "Unsupported speed on port %d: %d\n",
port, speed);
return;
}
/* handle Rx pause in all cases, with 2500base-X this is used for rate
* adaptation.
*/
mac_fc_cfg |= SYS_MAC_FC_CFG_RX_FC_ENA;
if (tx_pause)
mac_fc_cfg |= SYS_MAC_FC_CFG_TX_FC_ENA |
SYS_MAC_FC_CFG_PAUSE_VAL_CFG(0xffff) |
SYS_MAC_FC_CFG_FC_LATENCY_CFG(0x7) |
SYS_MAC_FC_CFG_ZERO_PAUSE_ENA;
/* Flow control. Link speed is only used here to evaluate the time
* specification in incoming pause frames.
*/
ocelot_write_rix(ocelot, mac_fc_cfg, SYS_MAC_FC_CFG, port);
ocelot_write_rix(ocelot, 0, ANA_POL_FLOWC, port);
/* Undo the effects of felix_phylink_mac_link_down:
* enable MAC module
*/
ocelot_port_writel(ocelot_port, DEV_MAC_ENA_CFG_RX_ENA |
DEV_MAC_ENA_CFG_TX_ENA, DEV_MAC_ENA_CFG);
@ -335,6 +330,13 @@ static void felix_phylink_mac_link_up(struct dsa_switch *ds, int port,
QSYS_SWITCH_PORT_MODE_SCH_NEXT_CFG(1) |
QSYS_SWITCH_PORT_MODE_PORT_ENA,
QSYS_SWITCH_PORT_MODE, port);
if (felix->info->pcs_link_up)
felix->info->pcs_link_up(ocelot, port, link_an_mode, interface,
speed, duplex);
if (felix->info->port_sched_speed_set)
felix->info->port_sched_speed_set(ocelot, port, speed);
}
static void felix_port_qos_map_init(struct ocelot *ocelot, int port)

View File

@ -28,9 +28,13 @@ struct felix_info {
int imdio_pci_bar;
int (*mdio_bus_alloc)(struct ocelot *ocelot);
void (*mdio_bus_free)(struct ocelot *ocelot);
void (*pcs_init)(struct ocelot *ocelot, int port,
void (*pcs_config)(struct ocelot *ocelot, int port,
unsigned int link_an_mode,
const struct phylink_link_state *state);
void (*pcs_link_up)(struct ocelot *ocelot, int port,
unsigned int link_an_mode,
phy_interface_t interface,
int speed, int duplex);
void (*pcs_link_state)(struct ocelot *ocelot, int port,
struct phylink_link_state *state);
int (*prevalidate_phy_mode)(struct ocelot *ocelot, int port,

View File

@ -737,11 +737,10 @@ static int vsc9959_reset(struct ocelot *ocelot)
* traffic if SGMII AN is enabled but not completed (acknowledged by us), so
* setting MLO_AN_INBAND is actually required for those.
*/
static void vsc9959_pcs_init_sgmii(struct phy_device *pcs,
static void vsc9959_pcs_config_sgmii(struct phy_device *pcs,
unsigned int link_an_mode,
const struct phylink_link_state *state)
{
if (link_an_mode == MLO_AN_INBAND) {
int bmsr, bmcr;
/* Some PHYs like VSC8234 don't like it when AN restarts on
@ -780,81 +779,12 @@ static void vsc9959_pcs_init_sgmii(struct phy_device *pcs,
ENETC_PCS_LINK_TIMER2_VAL);
phy_set_bits(pcs, MII_BMCR, BMCR_ANENABLE);
} else {
u16 if_mode = ENETC_PCS_IF_MODE_SGMII_EN;
int speed;
switch (state->speed) {
case SPEED_1000:
speed = ENETC_PCS_SPEED_1000;
break;
case SPEED_100:
speed = ENETC_PCS_SPEED_100;
break;
case SPEED_10:
speed = ENETC_PCS_SPEED_10;
break;
case SPEED_UNKNOWN:
/* Silently don't do anything */
return;
default:
phydev_err(pcs, "Invalid PCS speed %d\n", state->speed);
return;
}
if_mode |= ENETC_PCS_IF_MODE_SGMII_SPEED(speed);
if (state->duplex == DUPLEX_HALF)
if_mode |= ENETC_PCS_IF_MODE_DUPLEX_HALF;
phy_clear_bits(pcs, MII_BMCR, BMCR_ANENABLE);
}
}
/* 2500Base-X is SerDes protocol 7 on Felix and 6 on ENETC. It is a SerDes lane
* clocked at 3.125 GHz which encodes symbols with 8b/10b and does not have
* auto-negotiation of any link parameters. Electrically it is compatible with
* a single lane of XAUI.
* The hardware reference manual wants to call this mode SGMII, but it isn't
* really, since the fundamental features of SGMII:
* - Downgrading the link speed by duplicating symbols
* - Auto-negotiation
* are not there.
* The speed is configured at 1000 in the IF_MODE and BMCR MDIO registers
* because the clock frequency is actually given by a PLL configured in the
* Reset Configuration Word (RCW).
* Since there is no difference between fixed speed SGMII w/o AN and 802.3z w/o
* AN, we call this PHY interface type 2500Base-X. In case a PHY negotiates a
* lower link speed on line side, the system-side interface remains fixed at
* 2500 Mbps and we do rate adaptation through pause frames.
*/
static void vsc9959_pcs_init_2500basex(struct phy_device *pcs,
static void vsc9959_pcs_config_usxgmii(struct phy_device *pcs,
unsigned int link_an_mode,
const struct phylink_link_state *state)
{
u16 if_mode = ENETC_PCS_IF_MODE_SGMII_SPEED(ENETC_PCS_SPEED_2500) |
ENETC_PCS_IF_MODE_SGMII_EN;
if (link_an_mode == MLO_AN_INBAND) {
phydev_err(pcs, "AN not supported on 3.125GHz SerDes lane\n");
return;
}
if (state->duplex == DUPLEX_HALF)
if_mode |= ENETC_PCS_IF_MODE_DUPLEX_HALF;
phy_write(pcs, ENETC_PCS_IF_MODE, if_mode);
phy_clear_bits(pcs, MII_BMCR, BMCR_ANENABLE);
}
static void vsc9959_pcs_init_usxgmii(struct phy_device *pcs,
unsigned int link_an_mode,
const struct phylink_link_state *state)
{
if (link_an_mode != MLO_AN_INBAND) {
phydev_err(pcs, "USXGMII only supports in-band AN for now\n");
return;
}
/* Configure device ability for the USXGMII Replicator */
phy_write_mmd(pcs, MDIO_MMD_VEND2, MII_ADVERTISE,
USXGMII_ADVERTISE_SPEED(USXGMII_SPEED_2500) |
@ -864,7 +794,7 @@ static void vsc9959_pcs_init_usxgmii(struct phy_device *pcs,
USXGMII_ADVERTISE_FDX);
}
static void vsc9959_pcs_init(struct ocelot *ocelot, int port,
static void vsc9959_pcs_config(struct ocelot *ocelot, int port,
unsigned int link_an_mode,
const struct phylink_link_state *state)
{
@ -898,16 +828,110 @@ static void vsc9959_pcs_init(struct ocelot *ocelot, int port,
pcs->supported);
phy_advertise_supported(pcs);
if (!phylink_autoneg_inband(link_an_mode))
return;
switch (pcs->interface) {
case PHY_INTERFACE_MODE_SGMII:
case PHY_INTERFACE_MODE_QSGMII:
vsc9959_pcs_init_sgmii(pcs, link_an_mode, state);
vsc9959_pcs_config_sgmii(pcs, link_an_mode, state);
break;
case PHY_INTERFACE_MODE_2500BASEX:
vsc9959_pcs_init_2500basex(pcs, link_an_mode, state);
phydev_err(pcs, "AN not supported on 3.125GHz SerDes lane\n");
break;
case PHY_INTERFACE_MODE_USXGMII:
vsc9959_pcs_init_usxgmii(pcs, link_an_mode, state);
vsc9959_pcs_config_usxgmii(pcs, link_an_mode, state);
break;
default:
dev_err(ocelot->dev, "Unsupported link mode %s\n",
phy_modes(pcs->interface));
}
}
static void vsc9959_pcs_link_up_sgmii(struct phy_device *pcs,
unsigned int link_an_mode,
int speed, int duplex)
{
u16 if_mode = ENETC_PCS_IF_MODE_SGMII_EN;
switch (speed) {
case SPEED_1000:
if_mode |= ENETC_PCS_IF_MODE_SGMII_SPEED(ENETC_PCS_SPEED_1000);
break;
case SPEED_100:
if_mode |= ENETC_PCS_IF_MODE_SGMII_SPEED(ENETC_PCS_SPEED_100);
break;
case SPEED_10:
if_mode |= ENETC_PCS_IF_MODE_SGMII_SPEED(ENETC_PCS_SPEED_10);
break;
default:
phydev_err(pcs, "Invalid PCS speed %d\n", speed);
return;
}
if (duplex == DUPLEX_HALF)
if_mode |= ENETC_PCS_IF_MODE_DUPLEX_HALF;
phy_write(pcs, ENETC_PCS_IF_MODE, if_mode);
phy_clear_bits(pcs, MII_BMCR, BMCR_ANENABLE);
}
/* 2500Base-X is SerDes protocol 7 on Felix and 6 on ENETC. It is a SerDes lane
* clocked at 3.125 GHz which encodes symbols with 8b/10b and does not have
* auto-negotiation of any link parameters. Electrically it is compatible with
* a single lane of XAUI.
* The hardware reference manual wants to call this mode SGMII, but it isn't
* really, since the fundamental features of SGMII:
* - Downgrading the link speed by duplicating symbols
* - Auto-negotiation
* are not there.
* The speed is configured at 1000 in the IF_MODE and BMCR MDIO registers
* because the clock frequency is actually given by a PLL configured in the
* Reset Configuration Word (RCW).
* Since there is no difference between fixed speed SGMII w/o AN and 802.3z w/o
* AN, we call this PHY interface type 2500Base-X. In case a PHY negotiates a
* lower link speed on line side, the system-side interface remains fixed at
* 2500 Mbps and we do rate adaptation through pause frames.
*/
static void vsc9959_pcs_link_up_2500basex(struct phy_device *pcs,
unsigned int link_an_mode,
int speed, int duplex)
{
u16 if_mode = ENETC_PCS_IF_MODE_SGMII_SPEED(ENETC_PCS_SPEED_2500) |
ENETC_PCS_IF_MODE_SGMII_EN;
if (duplex == DUPLEX_HALF)
if_mode |= ENETC_PCS_IF_MODE_DUPLEX_HALF;
phy_write(pcs, ENETC_PCS_IF_MODE, if_mode);
phy_clear_bits(pcs, MII_BMCR, BMCR_ANENABLE);
}
static void vsc9959_pcs_link_up(struct ocelot *ocelot, int port,
unsigned int link_an_mode,
phy_interface_t interface,
int speed, int duplex)
{
struct felix *felix = ocelot_to_felix(ocelot);
struct phy_device *pcs = felix->pcs[port];
if (!pcs)
return;
if (phylink_autoneg_inband(link_an_mode))
return;
switch (interface) {
case PHY_INTERFACE_MODE_SGMII:
case PHY_INTERFACE_MODE_QSGMII:
vsc9959_pcs_link_up_sgmii(pcs, link_an_mode, speed, duplex);
break;
case PHY_INTERFACE_MODE_2500BASEX:
vsc9959_pcs_link_up_2500basex(pcs, link_an_mode, speed,
duplex);
break;
case PHY_INTERFACE_MODE_USXGMII:
phydev_err(pcs, "USXGMII only supports in-band AN for now\n");
break;
default:
dev_err(ocelot->dev, "Unsupported link mode %s\n",
@ -1374,7 +1398,8 @@ struct felix_info felix_info_vsc9959 = {
.imdio_pci_bar = 0,
.mdio_bus_alloc = vsc9959_mdio_bus_alloc,
.mdio_bus_free = vsc9959_mdio_bus_free,
.pcs_init = vsc9959_pcs_init,
.pcs_config = vsc9959_pcs_config,
.pcs_link_up = vsc9959_pcs_link_up,
.pcs_link_state = vsc9959_pcs_link_state,
.prevalidate_phy_mode = vsc9959_prevalidate_phy_mode,
.port_setup_tc = vsc9959_port_setup_tc,