The integrated PCS supports 802.3z (BASE-X) modes when the Synopsys IP is coupled with an appropriate SerDes to provide the electrical interface. The PCS presents a TBI interface to the SerDes for this. Thus, the BASE-X related registers are only present when TBI mode is supported. dwmac-qcom-ethqos added support for using 2.5G with the integrated PCS by calling dwmac_ctrl_ane() directly. Add support for the following to the integrated PCS: - 1000BASE-X protocol unconditionally. - 2500BASE-X if the coupled SerDes supports 2.5G speed. - The above without autonegotiation. - If the PCS supports TBI, then optional BASE-X autonegotiation for each of the above. Signed-off-by: Russell King (Oracle) --- .../net/ethernet/stmicro/stmmac/stmmac_pcs.c | 96 ++++++++++++++++++- .../net/ethernet/stmicro/stmmac/stmmac_pcs.h | 1 + include/linux/stmmac.h | 1 + 3 files changed, 95 insertions(+), 3 deletions(-) diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_pcs.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_pcs.c index 88fa359ea716..e606dfb85f94 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_pcs.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_pcs.c @@ -16,6 +16,27 @@ #define GMAC_ANE_LPA 0x0c /* ANE link partener ability */ #define GMAC_TBI 0x14 /* TBI extend status */ +static unsigned int dwmac_integrated_pcs_inband_caps(struct phylink_pcs *pcs, + phy_interface_t interface) +{ + struct stmmac_pcs *spcs = phylink_pcs_to_stmmac_pcs(pcs); + unsigned int ib_caps; + + if (phy_interface_mode_is_8023z(interface)) { + ib_caps = LINK_INBAND_DISABLE; + + /* If the PCS supports TBI/RTBI, then BASE-X negotiation is + * supported. + */ + if (spcs->support_tbi_rtbi) + ib_caps |= LINK_INBAND_ENABLE; + + return ib_caps; + } + + return 0; +} + static int dwmac_integrated_pcs_enable(struct phylink_pcs *pcs) { struct stmmac_pcs *spcs = phylink_pcs_to_stmmac_pcs(pcs); @@ -36,7 +57,39 @@ static void dwmac_integrated_pcs_get_state(struct phylink_pcs *pcs, unsigned int neg_mode, struct phylink_link_state *state) { - state->link = false; + struct stmmac_pcs *spcs = phylink_pcs_to_stmmac_pcs(pcs); + u32 status, lpa; + + status = readl(spcs->base + GMAC_AN_STATUS); + + if (phy_interface_mode_is_8023z(state->interface)) { + /* For BASE-X modes, the PCS block supports the advertisement + * and link partner advertisement registers using standard + * 802.3 format. The status register also has the link status + * and AN complete bits in the same bit location. This will + * only be used when AN is enabled. + */ + lpa = readl(spcs->base + GMAC_ANE_LPA); + + phylink_mii_c22_pcs_decode_state(state, neg_mode, status, lpa); + } else { + state->link = false; + } +} + +static int dwmac_integrated_pcs_config_aneg(struct stmmac_pcs *spcs, + phy_interface_t interface, + const unsigned long *advertising) +{ + bool changed = false; + u32 adv; + + adv = phylink_mii_c22_pcs_encode_advertisement(interface, advertising); + if (readl(spcs->base + GMAC_ANE_ADV) != adv) + changed = true; + writel(adv, spcs->base + GMAC_ANE_ADV); + + return changed; } static int dwmac_integrated_pcs_config(struct phylink_pcs *pcs, @@ -46,13 +99,28 @@ static int dwmac_integrated_pcs_config(struct phylink_pcs *pcs, bool permit_pause_to_mac) { struct stmmac_pcs *spcs = phylink_pcs_to_stmmac_pcs(pcs); + bool changed = false, ane = true; + + /* Only configure the advertisement and allow AN in BASE-X mode if + * the core supports TBI/RTBI. AN will be filtered out by via phylink + * and the .pcs_inband_caps() method above. + */ + if (phy_interface_mode_is_8023z(interface) && + spcs->support_tbi_rtbi) { + ane = neg_mode == PHYLINK_PCS_NEG_INBAND_ENABLED; + + changed = dwmac_integrated_pcs_config_aneg(spcs, interface, + advertising); + } - dwmac_ctrl_ane(spcs->base, 0, 1, spcs->priv->hw->reverse_sgmii_enable); + dwmac_ctrl_ane(spcs->base, 0, ane, + spcs->priv->hw->reverse_sgmii_enable); - return 0; + return changed; } static const struct phylink_pcs_ops dwmac_integrated_pcs_ops = { + .pcs_inband_caps = dwmac_integrated_pcs_inband_caps, .pcs_enable = dwmac_integrated_pcs_enable, .pcs_disable = dwmac_integrated_pcs_disable, .pcs_get_state = dwmac_integrated_pcs_get_state, @@ -84,9 +152,18 @@ void stmmac_integrated_pcs_irq(struct stmmac_priv *priv, u32 status, int stmmac_integrated_pcs_get_phy_intf_sel(struct phylink_pcs *pcs, phy_interface_t interface) { + struct stmmac_pcs *spcs = phylink_pcs_to_stmmac_pcs(pcs); + if (interface == PHY_INTERFACE_MODE_SGMII) return PHY_INTF_SEL_SGMII; + if (phy_interface_mode_is_8023z(interface)) { + if (spcs->support_tbi_rtbi) + return PHY_INTF_SEL_TBI; + else + return PHY_INTF_SEL_SGMII; + } + return -EINVAL; } @@ -104,7 +181,20 @@ int stmmac_integrated_pcs_init(struct stmmac_priv *priv, unsigned int offset, spcs->int_mask = int_mask; spcs->pcs.ops = &dwmac_integrated_pcs_ops; + /* If the PCS supports extended status, then it supports BASE-X AN + * with a TBI interface to the SerDes. Otherwise, we can support + * BASE-X without AN using SGMII, which is required for qcom-ethqos. + */ + if (readl(spcs->base + GMAC_AN_STATUS) & BMSR_ESTATEN) + spcs->support_tbi_rtbi = true; + __set_bit(PHY_INTERFACE_MODE_SGMII, spcs->pcs.supported_interfaces); + __set_bit(PHY_INTERFACE_MODE_1000BASEX, spcs->pcs.supported_interfaces); + + /* Only allow 2500BASE-X if the SerDes has support. */ + if (priv->plat->flags & STMMAC_FLAG_SERDES_SUPPORTS_2500M) + __set_bit(PHY_INTERFACE_MODE_2500BASEX, + spcs->pcs.supported_interfaces); priv->integrated_pcs = spcs; diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_pcs.h b/drivers/net/ethernet/stmicro/stmmac/stmmac_pcs.h index 23bbd4f10bf8..12ea87792fcb 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_pcs.h +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_pcs.h @@ -32,6 +32,7 @@ struct stmmac_pcs { void __iomem *base; u32 int_mask; struct phylink_pcs pcs; + bool support_tbi_rtbi; }; static inline struct stmmac_pcs * diff --git a/include/linux/stmmac.h b/include/linux/stmmac.h index b96ae9dadfab..aa57ee327606 100644 --- a/include/linux/stmmac.h +++ b/include/linux/stmmac.h @@ -192,6 +192,7 @@ enum dwmac_core_type { #define STMMAC_FLAG_EN_TX_LPI_CLK_PHY_CAP BIT(12) #define STMMAC_FLAG_HWTSTAMP_CORRECT_LATENCY BIT(13) #define STMMAC_FLAG_KEEP_PREAMBLE_BEFORE_SFD BIT(14) +#define STMMAC_FLAG_SERDES_SUPPORTS_2500M BIT(15) struct mac_device_info; -- 2.47.3 Signed-off-by: Russell King (Oracle) --- drivers/net/ethernet/stmicro/stmmac/dwmac-qcom-ethqos.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-qcom-ethqos.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-qcom-ethqos.c index cb1c074c2053..f4b63544895e 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac-qcom-ethqos.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-qcom-ethqos.c @@ -828,6 +828,7 @@ static int qcom_ethqos_probe(struct platform_device *pdev) if (ethqos->serdes_phy) { plat_dat->serdes_powerup = qcom_ethqos_serdes_powerup; plat_dat->serdes_powerdown = qcom_ethqos_serdes_powerdown; + plat_dat->flags |= STMMAC_FLAG_SERDES_SUPPORTS_2500M; } /* Enable TSO on queue0 and enable TBS on rest of the queues */ -- 2.47.3 dwmac-qcom-ethqos supports SGMII and 2500BASE-X using the integrated PCS, so we need to expand the PCS support to include support for BASE-X modes. Add support to the prereset configuration to detect 2500BASE-X, and arrange for stmmac_mac_select_pcs() to return the integrated PCS if its supported_interfaces bitmap reports support for the interface mode. This results in priv->hw->pcs now being write-only, so remove it. Signed-off-by: Russell King (Oracle) --- drivers/net/ethernet/stmicro/stmmac/common.h | 4 ---- drivers/net/ethernet/stmicro/stmmac/stmmac_main.c | 8 ++------ 2 files changed, 2 insertions(+), 10 deletions(-) diff --git a/drivers/net/ethernet/stmicro/stmmac/common.h b/drivers/net/ethernet/stmicro/stmmac/common.h index d26e8a063022..6492af5f8a06 100644 --- a/drivers/net/ethernet/stmicro/stmmac/common.h +++ b/drivers/net/ethernet/stmicro/stmmac/common.h @@ -277,9 +277,6 @@ struct stmmac_safety_stats { #define FLOW_TX 2 #define FLOW_AUTO (FLOW_TX | FLOW_RX) -/* PCS defines */ -#define STMMAC_PCS_SGMII (1 << 1) - #define SF_DMA_MODE 1 /* DMA STORE-AND-FORWARD Operation Mode */ /* DMA HW feature register fields */ @@ -634,7 +631,6 @@ struct mac_device_info { unsigned int unicast_filter_entries; unsigned int mcast_bits_log2; unsigned int rx_csum; - unsigned int pcs; unsigned int xlgmac; unsigned int num_vlan; u32 vlan_filter[32]; diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c index d7c730179a7f..1eeae680d777 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c @@ -953,11 +953,8 @@ static struct phylink_pcs *stmmac_mac_select_pcs(struct phylink_config *config, return pcs; } - /* The PCS control register is only relevant for SGMII, TBI and RTBI - * modes. We no longer support TBI or RTBI, so only configure this - * register when operating in SGMII mode with the integrated PCS. - */ - if (priv->hw->pcs & STMMAC_PCS_SGMII && priv->integrated_pcs) + if (priv->integrated_pcs && + test_bit(interface, priv->integrated_pcs->pcs.supported_interfaces)) return &priv->integrated_pcs->pcs; return NULL; @@ -1215,7 +1212,6 @@ static void stmmac_check_pcs_mode(struct stmmac_priv *priv) if (priv->dma_cap.pcs && interface == PHY_INTERFACE_MODE_SGMII) { netdev_dbg(priv->dev, "PCS SGMII support enabled\n"); - priv->hw->pcs = STMMAC_PCS_SGMII; switch (speed) { case SPEED_10: -- 2.47.3 We need to describe one more register (offset and field bitmask) to the PCS code. Move the existing PCS offset and interrupt enable bits to a new struct and pass that in to stmmac_integrated_pcs_init(). Signed-off-by: Russell King (Oracle) --- drivers/net/ethernet/stmicro/stmmac/dwmac1000_core.c | 9 ++++++--- drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c | 8 ++++++-- drivers/net/ethernet/stmicro/stmmac/stmmac_pcs.c | 8 ++++---- drivers/net/ethernet/stmicro/stmmac/stmmac_pcs.h | 9 +++++++-- 4 files changed, 23 insertions(+), 11 deletions(-) diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac1000_core.c b/drivers/net/ethernet/stmicro/stmmac/dwmac1000_core.c index af566636fad9..a3ef237de1b8 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac1000_core.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac1000_core.c @@ -22,14 +22,17 @@ #include "stmmac_ptp.h" #include "dwmac1000.h" +static const struct stmmac_pcs_info dwmac1000_pcs_info = { + .pcs_offset = GMAC_PCS_BASE, + .int_mask = GMAC_INT_DISABLE_PCSLINK | GMAC_INT_DISABLE_PCSAN, +}; + static int dwmac1000_pcs_init(struct stmmac_priv *priv) { if (!priv->dma_cap.pcs) return 0; - return stmmac_integrated_pcs_init(priv, GMAC_PCS_BASE, - GMAC_INT_DISABLE_PCSLINK | - GMAC_INT_DISABLE_PCSAN); + return stmmac_integrated_pcs_init(priv, &dwmac1000_pcs_info); } static void dwmac1000_core_init(struct mac_device_info *hw, diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c b/drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c index 623868afe93d..7f4949229288 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c @@ -22,13 +22,17 @@ #include "dwmac4.h" #include "dwmac5.h" +static const struct stmmac_pcs_info dwmac4_pcs_info = { + .pcs_offset = GMAC_PCS_BASE, + .int_mask = GMAC_INT_PCS_LINK | GMAC_INT_PCS_ANE, +}; + static int dwmac4_pcs_init(struct stmmac_priv *priv) { if (!priv->dma_cap.pcs) return 0; - return stmmac_integrated_pcs_init(priv, GMAC_PCS_BASE, - GMAC_INT_PCS_LINK | GMAC_INT_PCS_ANE); + return stmmac_integrated_pcs_init(priv, &dwmac4_pcs_info); } static void dwmac4_core_init(struct mac_device_info *hw, diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_pcs.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_pcs.c index e606dfb85f94..77a83e415597 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_pcs.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_pcs.c @@ -167,8 +167,8 @@ int stmmac_integrated_pcs_get_phy_intf_sel(struct phylink_pcs *pcs, return -EINVAL; } -int stmmac_integrated_pcs_init(struct stmmac_priv *priv, unsigned int offset, - u32 int_mask) +int stmmac_integrated_pcs_init(struct stmmac_priv *priv, + const struct stmmac_pcs_info *pcs_info) { struct stmmac_pcs *spcs; @@ -177,8 +177,8 @@ int stmmac_integrated_pcs_init(struct stmmac_priv *priv, unsigned int offset, return -ENOMEM; spcs->priv = priv; - spcs->base = priv->ioaddr + offset; - spcs->int_mask = int_mask; + spcs->base = priv->ioaddr + pcs_info->pcs_offset; + spcs->int_mask = pcs_info->int_mask; spcs->pcs.ops = &dwmac_integrated_pcs_ops; /* If the PCS supports extended status, then it supports BASE-X AN diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_pcs.h b/drivers/net/ethernet/stmicro/stmmac/stmmac_pcs.h index 12ea87792fcb..f3dada71e381 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_pcs.h +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_pcs.h @@ -27,6 +27,11 @@ struct stmmac_priv; +struct stmmac_pcs_info { + unsigned int pcs_offset; + u32 int_mask; +}; + struct stmmac_pcs { struct stmmac_priv *priv; void __iomem *base; @@ -45,8 +50,8 @@ void stmmac_integrated_pcs_irq(struct stmmac_priv *priv, u32 status, struct stmmac_extra_stats *x); int stmmac_integrated_pcs_get_phy_intf_sel(struct phylink_pcs *pcs, phy_interface_t interface); -int stmmac_integrated_pcs_init(struct stmmac_priv *priv, unsigned int offset, - u32 int_mask); +int stmmac_integrated_pcs_init(struct stmmac_priv *priv, + const struct stmmac_pcs_info *pcs_info); /** * dwmac_ctrl_ane - To program the AN Control Register. -- 2.47.3 Report the link, speed and duplex for SGMII links, read from the SGMII, RGMII and SMII status and control register. Signed-off-by: Russell King (Oracle) --- rfc->v1: fix setting SGMII's link status - depend on both link status. v2: - fill in rsgmii offset and status fields in dwmac cores - rename new GMAC_RGSMIII_xxx constants to GMAC_RGSMII_xxx --- .../net/ethernet/stmicro/stmmac/dwmac1000.h | 12 +---- .../ethernet/stmicro/stmmac/dwmac1000_core.c | 2 + drivers/net/ethernet/stmicro/stmmac/dwmac4.h | 10 +--- .../net/ethernet/stmicro/stmmac/dwmac4_core.c | 2 + .../net/ethernet/stmicro/stmmac/stmmac_pcs.c | 46 ++++++++++++++++++- .../net/ethernet/stmicro/stmmac/stmmac_pcs.h | 4 ++ 6 files changed, 54 insertions(+), 22 deletions(-) diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac1000.h b/drivers/net/ethernet/stmicro/stmmac/dwmac1000.h index 9fe639fb06bb..1de1f929d61c 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac1000.h +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac1000.h @@ -70,18 +70,8 @@ enum power_event { #define GMAC_RGSMIIIS 0x000000d8 /* RGMII/SMII status */ /* SGMII/RGMII status register */ -#define GMAC_RGSMIIIS_LNKMODE BIT(0) -#define GMAC_RGSMIIIS_SPEED GENMASK(2, 1) -#define GMAC_RGSMIIIS_LNKSTS BIT(3) -#define GMAC_RGSMIIIS_JABTO BIT(4) -#define GMAC_RGSMIIIS_FALSECARDET BIT(5) +#define GMAC_RSGMIIIS_MASK GENMASK(15, 0) #define GMAC_RGSMIIIS_SMIDRXS BIT(16) -/* LNKMOD */ -#define GMAC_RGSMIIIS_LNKMOD_MASK 0x1 -/* LNKSPEED */ -#define GMAC_RGSMIIIS_SPEED_125 0x2 -#define GMAC_RGSMIIIS_SPEED_25 0x1 -#define GMAC_RGSMIIIS_SPEED_2_5 0x0 /* GMAC Configuration defines */ #define GMAC_CONTROL_2K 0x08000000 /* IEEE 802.3as 2K packets */ diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac1000_core.c b/drivers/net/ethernet/stmicro/stmmac/dwmac1000_core.c index a3ef237de1b8..31f6ac5432f2 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac1000_core.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac1000_core.c @@ -24,6 +24,8 @@ static const struct stmmac_pcs_info dwmac1000_pcs_info = { .pcs_offset = GMAC_PCS_BASE, + .rgsmii_offset = GMAC_RGSMIIIS, + .rgsmii_status_mask = GMAC_RSGMIIIS_MASK, .int_mask = GMAC_INT_DISABLE_PCSLINK | GMAC_INT_DISABLE_PCSAN, }; diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac4.h b/drivers/net/ethernet/stmicro/stmmac/dwmac4.h index d797d936aee1..ffcd036d4c02 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac4.h +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac4.h @@ -470,15 +470,7 @@ static inline u32 mtl_low_credx_base_addr(const struct dwmac4_addrs *addrs, #define GMAC_PHYIF_CTRLSTATUS_TC BIT(0) #define GMAC_PHYIF_CTRLSTATUS_LUD BIT(1) #define GMAC_PHYIF_CTRLSTATUS_SMIDRXS BIT(4) -#define GMAC_PHYIF_CTRLSTATUS_LNKMOD BIT(16) -#define GMAC_PHYIF_CTRLSTATUS_SPEED GENMASK(18, 17) -#define GMAC_PHYIF_CTRLSTATUS_LNKSTS BIT(19) -#define GMAC_PHYIF_CTRLSTATUS_JABTO BIT(20) -#define GMAC_PHYIF_CTRLSTATUS_FALSECARDET BIT(21) -/* LNKSPEED */ -#define GMAC_PHYIF_CTRLSTATUS_SPEED_125 0x2 -#define GMAC_PHYIF_CTRLSTATUS_SPEED_25 0x1 -#define GMAC_PHYIF_CTRLSTATUS_SPEED_2_5 0x0 +#define GMAC_PHYIF_CTRLSTATUS_RSGMII_MASK GENMASK(31, 16) extern const struct stmmac_dma_ops dwmac4_dma_ops; extern const struct stmmac_dma_ops dwmac410_dma_ops; diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c b/drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c index 7f4949229288..ced2cd98cc54 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c @@ -24,6 +24,8 @@ static const struct stmmac_pcs_info dwmac4_pcs_info = { .pcs_offset = GMAC_PCS_BASE, + .rgsmii_offset = GMAC_PHYIF_CONTROL_STATUS, + .rgsmii_status_mask = GMAC_PHYIF_CTRLSTATUS_RSGMII_MASK, .int_mask = GMAC_INT_PCS_LINK | GMAC_INT_PCS_ANE, }; diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_pcs.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_pcs.c index 77a83e415597..ba0cf51e67f7 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_pcs.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_pcs.c @@ -16,6 +16,16 @@ #define GMAC_ANE_LPA 0x0c /* ANE link partener ability */ #define GMAC_TBI 0x14 /* TBI extend status */ +/* + * RGSMII status bitfield definitions. + */ +#define GMAC_RGSMII_LNKMOD BIT(0) +#define GMAC_RGSMII_SPEED_MASK GENMASK(2, 1) +#define GMAC_RGSMII_SPEED_125 2 +#define GMAC_RGSMII_SPEED_25 1 +#define GMAC_RGSMII_SPEED_2_5 0 +#define GMAC_RGSMII_LNKSTS BIT(3) + static unsigned int dwmac_integrated_pcs_inband_caps(struct phylink_pcs *pcs, phy_interface_t interface) { @@ -58,7 +68,7 @@ static void dwmac_integrated_pcs_get_state(struct phylink_pcs *pcs, struct phylink_link_state *state) { struct stmmac_pcs *spcs = phylink_pcs_to_stmmac_pcs(pcs); - u32 status, lpa; + u32 status, lpa, rgsmii; status = readl(spcs->base + GMAC_AN_STATUS); @@ -73,7 +83,37 @@ static void dwmac_integrated_pcs_get_state(struct phylink_pcs *pcs, phylink_mii_c22_pcs_decode_state(state, neg_mode, status, lpa); } else { - state->link = false; + rgsmii = field_get(spcs->rgsmii_status_mask, + readl(spcs->rgsmii)); + + state->link = status & BMSR_LSTATUS && + rgsmii & GMAC_RGSMII_LNKSTS; + + if (state->link && neg_mode == PHYLINK_PCS_NEG_INBAND_ENABLED) { + /* FIXME: fill in speed and duplex. This requires the + * contents of the dwmac1000 GMAC_RGSMIIS or dwmac4 + * GMAC_PHYIF_CONTROL_STATUS register. + */ + state->duplex = rgsmii & GMAC_RGSMII_LNKMOD ? + DUPLEX_FULL : DUPLEX_HALF; + switch (FIELD_GET(GMAC_RGSMII_SPEED_MASK, rgsmii)) { + case GMAC_RGSMII_SPEED_2_5: + state->speed = SPEED_10; + break; + + case GMAC_RGSMII_SPEED_25: + state->speed = SPEED_100; + break; + + case GMAC_RGSMII_SPEED_125: + state->speed = SPEED_1000; + break; + + default: + state->link = false; + break; + } + } } } @@ -178,6 +218,8 @@ int stmmac_integrated_pcs_init(struct stmmac_priv *priv, spcs->priv = priv; spcs->base = priv->ioaddr + pcs_info->pcs_offset; + spcs->rgsmii = priv->ioaddr + pcs_info->rgsmii_offset; + spcs->rgsmii_status_mask = pcs_info->rgsmii_status_mask; spcs->int_mask = pcs_info->int_mask; spcs->pcs.ops = &dwmac_integrated_pcs_ops; diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_pcs.h b/drivers/net/ethernet/stmicro/stmmac/stmmac_pcs.h index f3dada71e381..b2b12d34b3dd 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_pcs.h +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_pcs.h @@ -29,12 +29,16 @@ struct stmmac_priv; struct stmmac_pcs_info { unsigned int pcs_offset; + unsigned int rgsmii_offset; + u32 rgsmii_status_mask; u32 int_mask; }; struct stmmac_pcs { struct stmmac_priv *priv; void __iomem *base; + void __iomem *rgsmii; + u32 rgsmii_status_mask; u32 int_mask; struct phylink_pcs pcs; bool support_tbi_rtbi; -- 2.47.3 Provide phylink with the integrated PCS autonegotiation capabilities, and configure the PCS's AN settings according to phylink's requested requirements. This may cause regressions. Signed-off-by: Russell King (Oracle) --- .../net/ethernet/stmicro/stmmac/stmmac_pcs.c | 24 +++++++++++++------ 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_pcs.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_pcs.c index ba0cf51e67f7..0f9616fad4ef 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_pcs.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_pcs.c @@ -32,6 +32,9 @@ static unsigned int dwmac_integrated_pcs_inband_caps(struct phylink_pcs *pcs, struct stmmac_pcs *spcs = phylink_pcs_to_stmmac_pcs(pcs); unsigned int ib_caps; + if (interface == PHY_INTERFACE_MODE_SGMII) + return LINK_INBAND_ENABLE | LINK_INBAND_DISABLE; + if (phy_interface_mode_is_8023z(interface)) { ib_caps = LINK_INBAND_DISABLE; @@ -139,22 +142,29 @@ static int dwmac_integrated_pcs_config(struct phylink_pcs *pcs, bool permit_pause_to_mac) { struct stmmac_pcs *spcs = phylink_pcs_to_stmmac_pcs(pcs); - bool changed = false, ane = true; + void __iomem *an_control = spcs->base + GMAC_AN_CTRL(0); + bool ane, changed = false; + u32 ctrl; + + ane = neg_mode == PHYLINK_PCS_NEG_INBAND_ENABLED; /* Only configure the advertisement and allow AN in BASE-X mode if * the core supports TBI/RTBI. AN will be filtered out by via phylink * and the .pcs_inband_caps() method above. */ if (phy_interface_mode_is_8023z(interface) && - spcs->support_tbi_rtbi) { - ane = neg_mode == PHYLINK_PCS_NEG_INBAND_ENABLED; - + spcs->support_tbi_rtbi) changed = dwmac_integrated_pcs_config_aneg(spcs, interface, advertising); - } - dwmac_ctrl_ane(spcs->base, 0, ane, - spcs->priv->hw->reverse_sgmii_enable); + ctrl = readl(an_control) & ~(GMAC_AN_CTRL_ANE | GMAC_AN_CTRL_SGMRAL); + if (spcs->priv->hw->reverse_sgmii_enable) + ctrl |= GMAC_AN_CTRL_SGMRAL | GMAC_AN_CTRL_ANE; + else if (ane) + ctrl |= GMAC_AN_CTRL_ANE; + else + ctrl |= GMAC_AN_CTRL_SGMRAL; + writel(ctrl, an_control); return changed; } -- 2.47.3 Report if/when qcom-ethqos changes the PCS configuration. With phylink now setting the PCS configuration, there should be no need for drivers to change this. Signed-off-by: Russell King (Oracle) --- drivers/net/ethernet/stmicro/stmmac/stmmac_pcs.h | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_pcs.h b/drivers/net/ethernet/stmicro/stmmac/stmmac_pcs.h index b2b12d34b3dd..83651292578c 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_pcs.h +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_pcs.h @@ -71,6 +71,7 @@ static inline void dwmac_ctrl_ane(void __iomem *ioaddr, u32 reg, bool ane, bool srgmi_ral) { u32 value = readl(ioaddr + GMAC_AN_CTRL(reg)); + u32 old = value, diff; /* Enable and restart the Auto-Negotiation */ if (ane) @@ -84,6 +85,20 @@ static inline void dwmac_ctrl_ane(void __iomem *ioaddr, u32 reg, bool ane, if (srgmi_ral) value |= GMAC_AN_CTRL_SGMRAL; + diff = old ^ value; + if (diff & ~GMAC_AN_CTRL_RAN) { + pr_warn("dwmac: PCS configuration changed from phylink by glue, please report: 0x%08x -> 0x%08x\n", + old & ~GMAC_AN_CTRL_RAN, value & ~GMAC_AN_CTRL_RAN); +#define REPORT_BIT(x) \ + if (diff & GMAC_AN_CTRL_##x) \ + pr_warn("dwmac: %8s %u -> %u\n", #x, \ + !!(old & GMAC_AN_CTRL_##x), \ + !!(value & GMAC_AN_CTRL_##x)) + REPORT_BIT(ANE); + REPORT_BIT(SGMRAL); +#undef REPORT_BIT + } + writel(value, ioaddr + GMAC_AN_CTRL(reg)); } #endif /* __STMMAC_PCS_H__ */ -- 2.47.3