mirror of
https://github.com/AuxXxilium/linux_dsm_epyc7002.git
synced 2025-03-07 04:57:52 +07:00
Merge branch 'Phylink-integration-improvements-for-Felix-DSA-driver'
Vladimir Oltean says: ==================== Phylink integration improvements for Felix DSA driver This is an overhaul of the Felix switch driver's phylink operations. Patches 1, 3, 4 and 5 are cleanup, patch 2 is adding a new feature and and patch 6 is adaptation to the new format of an existing phylink API (mac_link_up). Changes since v2: - Replaced "PHYLINK" with "phylink". - Rewrote commit message of patch 5/6. Changes since v1: - Now using phy_clear_bits and phy_set_bits instead of plain writes to MII_BMCR. This combines former patches 1/7 and 6/7 into a single new patch 1/6. - Updated commit message of patch 5/6. ==================== Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
commit
dbacfd8ca7
@ -194,13 +194,15 @@ static void felix_phylink_validate(struct dsa_switch *ds, int port,
|
||||
return;
|
||||
}
|
||||
|
||||
/* No half-duplex. */
|
||||
phylink_set_port_modes(mask);
|
||||
phylink_set(mask, Autoneg);
|
||||
phylink_set(mask, Pause);
|
||||
phylink_set(mask, Asym_Pause);
|
||||
phylink_set(mask, 10baseT_Half);
|
||||
phylink_set(mask, 10baseT_Full);
|
||||
phylink_set(mask, 100baseT_Half);
|
||||
phylink_set(mask, 100baseT_Full);
|
||||
phylink_set(mask, 1000baseT_Half);
|
||||
phylink_set(mask, 1000baseT_Full);
|
||||
|
||||
if (state->interface == PHY_INTERFACE_MODE_INTERNAL ||
|
||||
@ -231,52 +233,12 @@ static int felix_phylink_mac_pcs_get_state(struct dsa_switch *ds, int port,
|
||||
static void felix_phylink_mac_config(struct dsa_switch *ds, int port,
|
||||
unsigned int link_an_mode,
|
||||
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 CLOCK_CFG
|
||||
*/
|
||||
ocelot_port_writel(ocelot_port, DEV_CLOCK_CFG_LINK_SPEED(state->speed),
|
||||
DEV_CLOCK_CFG);
|
||||
|
||||
/* Flow control. Link speed is only used here to evaluate the time
|
||||
* specification in incoming pause frames.
|
||||
*/
|
||||
mac_fc_cfg = SYS_MAC_FC_CFG_FC_LINK_SPEED(state->speed);
|
||||
|
||||
/* 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;
|
||||
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);
|
||||
}
|
||||
|
||||
static void felix_phylink_mac_an_restart(struct dsa_switch *ds, int port)
|
||||
{
|
||||
struct ocelot *ocelot = ds->priv;
|
||||
struct felix *felix = ocelot_to_felix(ocelot);
|
||||
|
||||
if (felix->info->pcs_an_restart)
|
||||
felix->info->pcs_an_restart(ocelot, port);
|
||||
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,
|
||||
@ -300,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);
|
||||
|
||||
@ -318,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)
|
||||
@ -784,7 +803,6 @@ static const struct dsa_switch_ops felix_switch_ops = {
|
||||
.phylink_validate = felix_phylink_validate,
|
||||
.phylink_mac_link_state = felix_phylink_mac_pcs_get_state,
|
||||
.phylink_mac_config = felix_phylink_mac_config,
|
||||
.phylink_mac_an_restart = felix_phylink_mac_an_restart,
|
||||
.phylink_mac_link_down = felix_phylink_mac_link_down,
|
||||
.phylink_mac_link_up = felix_phylink_mac_link_up,
|
||||
.port_enable = felix_port_enable,
|
||||
|
@ -28,10 +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,
|
||||
unsigned int link_an_mode,
|
||||
const struct phylink_link_state *state);
|
||||
void (*pcs_an_restart)(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,
|
||||
|
@ -728,42 +728,6 @@ static int vsc9959_reset(struct ocelot *ocelot)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void vsc9959_pcs_an_restart_sgmii(struct phy_device *pcs)
|
||||
{
|
||||
phy_set_bits(pcs, MII_BMCR, BMCR_ANRESTART);
|
||||
}
|
||||
|
||||
static void vsc9959_pcs_an_restart_usxgmii(struct phy_device *pcs)
|
||||
{
|
||||
phy_write_mmd(pcs, MDIO_MMD_VEND2, MII_BMCR,
|
||||
USXGMII_BMCR_RESET |
|
||||
USXGMII_BMCR_AN_EN |
|
||||
USXGMII_BMCR_RST_AN);
|
||||
}
|
||||
|
||||
static void vsc9959_pcs_an_restart(struct ocelot *ocelot, int port)
|
||||
{
|
||||
struct felix *felix = ocelot_to_felix(ocelot);
|
||||
struct phy_device *pcs = felix->pcs[port];
|
||||
|
||||
if (!pcs)
|
||||
return;
|
||||
|
||||
switch (pcs->interface) {
|
||||
case PHY_INTERFACE_MODE_SGMII:
|
||||
case PHY_INTERFACE_MODE_QSGMII:
|
||||
vsc9959_pcs_an_restart_sgmii(pcs);
|
||||
break;
|
||||
case PHY_INTERFACE_MODE_USXGMII:
|
||||
vsc9959_pcs_an_restart_usxgmii(pcs);
|
||||
break;
|
||||
default:
|
||||
dev_err(ocelot->dev, "Invalid PCS interface type %s\n",
|
||||
phy_modes(pcs->interface));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* We enable SGMII AN only when the PHY has managed = "in-band-status" in the
|
||||
* device tree. If we are in MLO_AN_PHY mode, we program directly state->speed
|
||||
* into the PCS, which is retrieved out-of-band over MDIO. This also has the
|
||||
@ -773,85 +737,145 @@ static void vsc9959_pcs_an_restart(struct ocelot *ocelot, int port)
|
||||
* 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,
|
||||
unsigned int link_an_mode,
|
||||
const struct phylink_link_state *state)
|
||||
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;
|
||||
int bmsr, bmcr;
|
||||
|
||||
/* Some PHYs like VSC8234 don't like it when AN restarts on
|
||||
* their system side and they restart line side AN too, going
|
||||
* into an endless link up/down loop. Don't restart PCS AN if
|
||||
* link is up already.
|
||||
* We do check that AN is enabled just in case this is the 1st
|
||||
* call, PCS detects a carrier but AN is disabled from power on
|
||||
* or by boot loader.
|
||||
*/
|
||||
bmcr = phy_read(pcs, MII_BMCR);
|
||||
if (bmcr < 0)
|
||||
return;
|
||||
/* Some PHYs like VSC8234 don't like it when AN restarts on
|
||||
* their system side and they restart line side AN too, going
|
||||
* into an endless link up/down loop. Don't restart PCS AN if
|
||||
* link is up already.
|
||||
* We do check that AN is enabled just in case this is the 1st
|
||||
* call, PCS detects a carrier but AN is disabled from power on
|
||||
* or by boot loader.
|
||||
*/
|
||||
bmcr = phy_read(pcs, MII_BMCR);
|
||||
if (bmcr < 0)
|
||||
return;
|
||||
|
||||
bmsr = phy_read(pcs, MII_BMSR);
|
||||
if (bmsr < 0)
|
||||
return;
|
||||
bmsr = phy_read(pcs, MII_BMSR);
|
||||
if (bmsr < 0)
|
||||
return;
|
||||
|
||||
if ((bmcr & BMCR_ANENABLE) && (bmsr & BMSR_LSTATUS))
|
||||
return;
|
||||
if ((bmcr & BMCR_ANENABLE) && (bmsr & BMSR_LSTATUS))
|
||||
return;
|
||||
|
||||
/* SGMII spec requires tx_config_Reg[15:0] to be exactly 0x4001
|
||||
* for the MAC PCS in order to acknowledge the AN.
|
||||
*/
|
||||
phy_write(pcs, MII_ADVERTISE, ADVERTISE_SGMII |
|
||||
ADVERTISE_LPACK);
|
||||
/* SGMII spec requires tx_config_Reg[15:0] to be exactly 0x4001
|
||||
* for the MAC PCS in order to acknowledge the AN.
|
||||
*/
|
||||
phy_write(pcs, MII_ADVERTISE, ADVERTISE_SGMII |
|
||||
ADVERTISE_LPACK);
|
||||
|
||||
phy_write(pcs, ENETC_PCS_IF_MODE,
|
||||
ENETC_PCS_IF_MODE_SGMII_EN |
|
||||
ENETC_PCS_IF_MODE_USE_SGMII_AN);
|
||||
phy_write(pcs, ENETC_PCS_IF_MODE,
|
||||
ENETC_PCS_IF_MODE_SGMII_EN |
|
||||
ENETC_PCS_IF_MODE_USE_SGMII_AN);
|
||||
|
||||
/* Adjust link timer for SGMII */
|
||||
phy_write(pcs, ENETC_PCS_LINK_TIMER1,
|
||||
ENETC_PCS_LINK_TIMER1_VAL);
|
||||
phy_write(pcs, ENETC_PCS_LINK_TIMER2,
|
||||
ENETC_PCS_LINK_TIMER2_VAL);
|
||||
/* Adjust link timer for SGMII */
|
||||
phy_write(pcs, ENETC_PCS_LINK_TIMER1,
|
||||
ENETC_PCS_LINK_TIMER1_VAL);
|
||||
phy_write(pcs, ENETC_PCS_LINK_TIMER2,
|
||||
ENETC_PCS_LINK_TIMER2_VAL);
|
||||
|
||||
phy_write(pcs, MII_BMCR, BMCR_ANRESTART | BMCR_ANENABLE);
|
||||
} else {
|
||||
int speed;
|
||||
phy_set_bits(pcs, MII_BMCR, BMCR_ANENABLE);
|
||||
}
|
||||
|
||||
if (state->duplex == DUPLEX_HALF) {
|
||||
phydev_err(pcs, "Half duplex not supported\n");
|
||||
return;
|
||||
}
|
||||
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;
|
||||
}
|
||||
static void vsc9959_pcs_config_usxgmii(struct phy_device *pcs,
|
||||
unsigned int link_an_mode,
|
||||
const struct phylink_link_state *state)
|
||||
{
|
||||
/* Configure device ability for the USXGMII Replicator */
|
||||
phy_write_mmd(pcs, MDIO_MMD_VEND2, MII_ADVERTISE,
|
||||
USXGMII_ADVERTISE_SPEED(USXGMII_SPEED_2500) |
|
||||
USXGMII_ADVERTISE_LNKS(1) |
|
||||
ADVERTISE_SGMII |
|
||||
ADVERTISE_LPACK |
|
||||
USXGMII_ADVERTISE_FDX);
|
||||
}
|
||||
|
||||
phy_write(pcs, ENETC_PCS_IF_MODE,
|
||||
ENETC_PCS_IF_MODE_SGMII_EN |
|
||||
ENETC_PCS_IF_MODE_SGMII_SPEED(speed));
|
||||
static void vsc9959_pcs_config(struct ocelot *ocelot, int port,
|
||||
unsigned int link_an_mode,
|
||||
const struct phylink_link_state *state)
|
||||
{
|
||||
struct felix *felix = ocelot_to_felix(ocelot);
|
||||
struct phy_device *pcs = felix->pcs[port];
|
||||
|
||||
/* Yes, not a mistake: speed is given by IF_MODE. */
|
||||
phy_write(pcs, MII_BMCR, BMCR_RESET |
|
||||
BMCR_SPEED1000 |
|
||||
BMCR_FULLDPLX);
|
||||
if (!pcs)
|
||||
return;
|
||||
|
||||
/* The PCS does not implement the BMSR register fully, so capability
|
||||
* detection via genphy_read_abilities does not work. Since we can get
|
||||
* the PHY config word from the LPA register though, there is still
|
||||
* value in using the generic phy_resolve_aneg_linkmode function. So
|
||||
* populate the supported and advertising link modes manually here.
|
||||
*/
|
||||
linkmode_set_bit_array(phy_basic_ports_array,
|
||||
ARRAY_SIZE(phy_basic_ports_array),
|
||||
pcs->supported);
|
||||
linkmode_set_bit(ETHTOOL_LINK_MODE_10baseT_Half_BIT, pcs->supported);
|
||||
linkmode_set_bit(ETHTOOL_LINK_MODE_10baseT_Full_BIT, pcs->supported);
|
||||
linkmode_set_bit(ETHTOOL_LINK_MODE_100baseT_Half_BIT, pcs->supported);
|
||||
linkmode_set_bit(ETHTOOL_LINK_MODE_100baseT_Full_BIT, pcs->supported);
|
||||
linkmode_set_bit(ETHTOOL_LINK_MODE_1000baseT_Half_BIT, pcs->supported);
|
||||
linkmode_set_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT, pcs->supported);
|
||||
if (pcs->interface == PHY_INTERFACE_MODE_2500BASEX ||
|
||||
pcs->interface == PHY_INTERFACE_MODE_USXGMII)
|
||||
linkmode_set_bit(ETHTOOL_LINK_MODE_2500baseX_Full_BIT,
|
||||
pcs->supported);
|
||||
if (pcs->interface != PHY_INTERFACE_MODE_2500BASEX)
|
||||
linkmode_set_bit(ETHTOOL_LINK_MODE_Autoneg_BIT,
|
||||
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_config_sgmii(pcs, link_an_mode, state);
|
||||
break;
|
||||
case PHY_INTERFACE_MODE_2500BASEX:
|
||||
phydev_err(pcs, "AN not supported on 3.125GHz SerDes lane\n");
|
||||
break;
|
||||
case PHY_INTERFACE_MODE_USXGMII:
|
||||
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
|
||||
@ -869,45 +893,24 @@ static void vsc9959_pcs_init_sgmii(struct phy_device *pcs,
|
||||
* 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,
|
||||
unsigned int link_an_mode,
|
||||
const struct phylink_link_state *state)
|
||||
static void vsc9959_pcs_link_up_2500basex(struct phy_device *pcs,
|
||||
unsigned int link_an_mode,
|
||||
int speed, int duplex)
|
||||
{
|
||||
if (link_an_mode == MLO_AN_INBAND) {
|
||||
phydev_err(pcs, "AN not supported on 3.125GHz SerDes lane\n");
|
||||
return;
|
||||
}
|
||||
u16 if_mode = ENETC_PCS_IF_MODE_SGMII_SPEED(ENETC_PCS_SPEED_2500) |
|
||||
ENETC_PCS_IF_MODE_SGMII_EN;
|
||||
|
||||
phy_write(pcs, ENETC_PCS_IF_MODE,
|
||||
ENETC_PCS_IF_MODE_SGMII_EN |
|
||||
ENETC_PCS_IF_MODE_SGMII_SPEED(ENETC_PCS_SPEED_2500));
|
||||
if (duplex == DUPLEX_HALF)
|
||||
if_mode |= ENETC_PCS_IF_MODE_DUPLEX_HALF;
|
||||
|
||||
phy_write(pcs, MII_BMCR, BMCR_SPEED1000 |
|
||||
BMCR_FULLDPLX |
|
||||
BMCR_RESET);
|
||||
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) |
|
||||
USXGMII_ADVERTISE_LNKS(1) |
|
||||
ADVERTISE_SGMII |
|
||||
ADVERTISE_LPACK |
|
||||
USXGMII_ADVERTISE_FDX);
|
||||
}
|
||||
|
||||
static void vsc9959_pcs_init(struct ocelot *ocelot, int port,
|
||||
unsigned int link_an_mode,
|
||||
const struct phylink_link_state *state)
|
||||
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];
|
||||
@ -915,37 +918,20 @@ static void vsc9959_pcs_init(struct ocelot *ocelot, int port,
|
||||
if (!pcs)
|
||||
return;
|
||||
|
||||
/* The PCS does not implement the BMSR register fully, so capability
|
||||
* detection via genphy_read_abilities does not work. Since we can get
|
||||
* the PHY config word from the LPA register though, there is still
|
||||
* value in using the generic phy_resolve_aneg_linkmode function. So
|
||||
* populate the supported and advertising link modes manually here.
|
||||
*/
|
||||
linkmode_set_bit_array(phy_basic_ports_array,
|
||||
ARRAY_SIZE(phy_basic_ports_array),
|
||||
pcs->supported);
|
||||
linkmode_set_bit(ETHTOOL_LINK_MODE_10baseT_Full_BIT, pcs->supported);
|
||||
linkmode_set_bit(ETHTOOL_LINK_MODE_100baseT_Full_BIT, pcs->supported);
|
||||
linkmode_set_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT, pcs->supported);
|
||||
if (pcs->interface == PHY_INTERFACE_MODE_2500BASEX ||
|
||||
pcs->interface == PHY_INTERFACE_MODE_USXGMII)
|
||||
linkmode_set_bit(ETHTOOL_LINK_MODE_2500baseX_Full_BIT,
|
||||
pcs->supported);
|
||||
if (pcs->interface != PHY_INTERFACE_MODE_2500BASEX)
|
||||
linkmode_set_bit(ETHTOOL_LINK_MODE_Autoneg_BIT,
|
||||
pcs->supported);
|
||||
phy_advertise_supported(pcs);
|
||||
if (phylink_autoneg_inband(link_an_mode))
|
||||
return;
|
||||
|
||||
switch (pcs->interface) {
|
||||
switch (interface) {
|
||||
case PHY_INTERFACE_MODE_SGMII:
|
||||
case PHY_INTERFACE_MODE_QSGMII:
|
||||
vsc9959_pcs_init_sgmii(pcs, link_an_mode, state);
|
||||
vsc9959_pcs_link_up_sgmii(pcs, link_an_mode, speed, duplex);
|
||||
break;
|
||||
case PHY_INTERFACE_MODE_2500BASEX:
|
||||
vsc9959_pcs_init_2500basex(pcs, link_an_mode, state);
|
||||
vsc9959_pcs_link_up_2500basex(pcs, link_an_mode, speed,
|
||||
duplex);
|
||||
break;
|
||||
case PHY_INTERFACE_MODE_USXGMII:
|
||||
vsc9959_pcs_init_usxgmii(pcs, link_an_mode, state);
|
||||
phydev_err(pcs, "USXGMII only supports in-band AN for now\n");
|
||||
break;
|
||||
default:
|
||||
dev_err(ocelot->dev, "Unsupported link mode %s\n",
|
||||
@ -1412,8 +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_an_restart = vsc9959_pcs_an_restart,
|
||||
.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,
|
||||
|
@ -15,6 +15,7 @@
|
||||
#define ENETC_PCS_IF_MODE_SGMII_EN BIT(0)
|
||||
#define ENETC_PCS_IF_MODE_USE_SGMII_AN BIT(1)
|
||||
#define ENETC_PCS_IF_MODE_SGMII_SPEED(x) (((x) << 2) & GENMASK(3, 2))
|
||||
#define ENETC_PCS_IF_MODE_DUPLEX_HALF BIT(3)
|
||||
|
||||
/* Not a mistake, the SerDes PLL needs to be set at 3.125 GHz by Reset
|
||||
* Configuration Word (RCW, outside Linux control) for 2.5G SGMII mode. The PCS
|
||||
|
Loading…
Reference in New Issue
Block a user