[][kernel][common][eth][Add 1000basex support and refactor to upstream style for the SGMII]
[Description]
Add 1000basex support and refactor to upstream style for the SGMII.
Current support types:
- 1000BASEX w/wo autoneg
- SGMII w autoneg
- HSGMII wo autoneg
If without this patch, the SGMII might unable to link with the 1000basex
link partner.
[Release-log]
N/A
Change-Id: I457b84ee944c259b7fe1c61936bb7d2b431cfee2
Reviewed-on: https://gerrit.mediatek.inc/c/openwrt/feeds/mtk_openwrt_feeds/+/7378663
diff --git a/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_eth_soc.c b/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_eth_soc.c
index 1368a7b..ad73fdb 100755
--- a/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+++ b/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_eth_soc.c
@@ -811,52 +811,44 @@
struct mtk_sgmii *ss = eth->sgmii;
u32 id = mtk_mac2xgmii_id(eth, mac->id);
u32 pmsr = mtk_r32(mac->hw, MTK_MAC_MSR(mac->id));
- u32 rgc3, val = 0;
-
- regmap_read(ss->pcs[id].regmap, SGMSYS_PCS_CONTROL_1, &val);
+ u32 bm, adv, rgc3, sgm_mode;
state->interface = mac->interface;
- state->link = FIELD_GET(SGMII_LINK_STATYS, val);
- if (FIELD_GET(SGMII_AN_ENABLE, val)) {
+ regmap_read(ss->pcs[id].regmap, SGMSYS_PCS_CONTROL_1, &bm);
+ if (bm & SGMII_AN_ENABLE) {
regmap_read(ss->pcs[id].regmap,
- SGMII_PCS_SPEED_ABILITY, &val);
-
- val = val >> 16;
-
- state->duplex = FIELD_GET(SGMII_PCS_SPEED_DUPLEX, val);
+ SGMSYS_PCS_ADVERTISE, &adv);
- switch (FIELD_GET(SGMII_PCS_SPEED_MASK, val)) {
- case 0:
- state->speed = SPEED_10;
- break;
- case 1:
- state->speed = SPEED_100;
- break;
- case 2:
- state->speed = SPEED_1000;
- break;
- }
+ phylink_mii_c22_pcs_decode_state(
+ state,
+ FIELD_GET(SGMII_BMSR, bm),
+ FIELD_GET(SGMII_LPA, adv));
} else {
- regmap_read(ss->pcs[id].regmap,
- SGMSYS_SGMII_MODE, &val);
+ state->link = !!(bm & SGMII_LINK_STATYS);
- state->duplex = !FIELD_GET(SGMII_DUPLEX_HALF, val);
+ regmap_read(ss->pcs[id].regmap,
+ SGMSYS_SGMII_MODE, &sgm_mode);
- switch (FIELD_GET(SGMII_SPEED_MASK, val)) {
- case 0:
+ switch (sgm_mode & SGMII_SPEED_MASK) {
+ case SGMII_SPEED_10:
state->speed = SPEED_10;
break;
- case 1:
+ case SGMII_SPEED_100:
state->speed = SPEED_100;
break;
- case 2:
+ case SGMII_SPEED_1000:
regmap_read(ss->pcs[id].regmap,
- ss->pcs[id].ana_rgc3, &val);
- rgc3 = FIELD_GET(RG_PHY_SPEED_3_125G, val);
+ ss->pcs[id].ana_rgc3, &rgc3);
+ rgc3 = FIELD_GET(RG_PHY_SPEED_3_125G, rgc3);
state->speed = rgc3 ? SPEED_2500 : SPEED_1000;
break;
}
+
+ if (sgm_mode & SGMII_DUPLEX_HALF)
+ state->duplex = DUPLEX_HALF;
+ else
+ state->duplex = DUPLEX_FULL;
}
state->pause &= (MLO_PAUSE_RX | MLO_PAUSE_TX);
diff --git a/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_eth_soc.h b/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_eth_soc.h
index 646e557..f279ad5 100755
--- a/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_eth_soc.h
+++ b/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_eth_soc.h
@@ -820,8 +820,8 @@
#define ETHSYS_DMA_AG_MAP_PPE BIT(2)
/* SGMII subsystem config registers */
-/* Register to auto-negotiation restart */
#define SGMSYS_PCS_CONTROL_1 0x0
+#define SGMII_BMSR GENMASK(31, 16)
#define SGMII_AN_RESTART BIT(9)
#define SGMII_ISOLATE BIT(10)
#define SGMII_AN_ENABLE BIT(12)
@@ -832,13 +832,15 @@
#define SGMII_AN_EXPANSION_CLR BIT(30)
/* Register to set SGMII speed */
-#define SGMII_PCS_SPEED_ABILITY 0x08
-#define SGMII_PCS_SPEED_MASK GENMASK(11, 10)
-#define SGMII_PCS_SPEED_10 0
-#define SGMII_PCS_SPEED_100 1
-#define SGMII_PCS_SPEED_1000 2
-#define SGMII_PCS_SPEED_DUPLEX BIT(12)
-#define SGMII_PCS_SPEED_LINK BIT(15)
+#define SGMSYS_PCS_ADVERTISE 0x08
+#define SGMII_ADVERTISE GENMASK(15, 0)
+#define SGMII_LPA GENMASK(31, 16)
+#define SGMII_LPA_SPEED_MASK GENMASK(11, 10)
+#define SGMII_LPA_SPEED_10 0
+#define SGMII_LPA_SPEED_100 1
+#define SGMII_LPA_SPEED_1000 2
+#define SGMII_LPA_DUPLEX BIT(12)
+#define SGMII_LPA_LINK BIT(15)
/* Register to programmable link timer, the unit in 2 * 8ns */
#define SGMSYS_PCS_LINK_TIMER 0x18
@@ -846,7 +848,7 @@
/* Register to control remote fault */
#define SGMSYS_SGMII_MODE 0x20
-#define SGMII_IF_MODE_BIT0 BIT(0)
+#define SGMII_IF_MODE_SGMII BIT(0)
#define SGMII_SPEED_DUPLEX_AN BIT(1)
#define SGMII_SPEED_MASK GENMASK(3, 2)
#define SGMII_SPEED_10 0x0
diff --git a/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_sgmii.c b/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_sgmii.c
index 3c37656..282fd30 100755
--- a/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_sgmii.c
+++ b/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_sgmii.c
@@ -9,6 +9,7 @@
#include <linux/mfd/syscon.h>
#include <linux/of.h>
+#include <linux/phylink.h>
#include <linux/regmap.h>
#include "mtk_eth_soc.h"
@@ -143,29 +144,6 @@
mdelay(1);
}
-int mtk_sgmii_need_powerdown(struct mtk_sgmii_pcs *mpcs,
- phy_interface_t interface)
-{
- u32 val;
-
- /* need to power down sgmii if link down */
- regmap_read(mpcs->regmap, SGMSYS_PCS_CONTROL_1, &val);
- if (!(val & SGMII_LINK_STATYS))
- return true;
-
- /* need to power down sgmii if interface changed */
- regmap_read(mpcs->regmap, mpcs->ana_rgc3, &val);
- if (interface == PHY_INTERFACE_MODE_2500BASEX) {
- if (!(val & RG_PHY_SPEED_3_125G))
- return true;
- } else {
- if (val & RG_PHY_SPEED_3_125G)
- return true;
- }
-
- return false;
-}
-
void mtk_sgmii_setup_phya_gen1(struct mtk_sgmii_pcs *mpcs)
{
if (!mpcs->regmap_pextp)
@@ -368,179 +346,140 @@
udelay(400);
}
-int mtk_sgmii_setup_mode_an(struct mtk_sgmii_pcs *mpcs)
+static void mtk_sgmii_pcs_get_state(struct phylink_pcs *pcs,
+ struct phylink_link_state *state)
{
- struct mtk_eth *eth = mpcs->eth;
- unsigned int val = 0;
-
- if (!mpcs->regmap)
- return -EINVAL;
-
- if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V3)) {
- mtk_sgmii_xfi_pll_enable(eth->sgmii);
- mtk_sgmii_reset(eth, mpcs->id);
- }
-
- /* Assert PHYA power down state when needed */
- if (mtk_sgmii_need_powerdown(mpcs, PHY_INTERFACE_MODE_SGMII))
- regmap_write(mpcs->regmap, SGMSYS_QPHY_PWR_STATE_CTRL,
- SGMII_PHYA_PWD);
-
- /* Reset SGMII PCS state */
- regmap_write(mpcs->regmap, SGMII_RESERVED_0, SGMII_SW_RESET);
-
- regmap_read(mpcs->regmap, mpcs->ana_rgc3, &val);
- val &= ~RG_PHY_SPEED_3_125G;
- regmap_write(mpcs->regmap, mpcs->ana_rgc3, val);
-
- /* Setup the link timer and QPHY power up inside SGMIISYS */
- regmap_write(mpcs->regmap, SGMSYS_PCS_LINK_TIMER,
- SGMII_LINK_TIMER_DEFAULT);
-
- regmap_read(mpcs->regmap, SGMSYS_SGMII_MODE, &val);
- val |= SGMII_REMOTE_FAULT_DIS;
- regmap_write(mpcs->regmap, SGMSYS_SGMII_MODE, val);
+ struct mtk_sgmii_pcs *mpcs = pcs_to_mtk_sgmii_pcs(pcs);
+ unsigned int bm, adv, rgc3, sgm_mode;
- /* SGMII AN mode setting */
- regmap_read(mpcs->regmap, SGMSYS_SGMII_MODE, &val);
- val &= ~SGMII_IF_MODE_MASK;
- val |= SGMII_SPEED_DUPLEX_AN;
- regmap_write(mpcs->regmap, SGMSYS_SGMII_MODE, val);
+ state->interface = mpcs->interface;
- /* Enable SGMII AN */
- regmap_read(mpcs->regmap, SGMSYS_PCS_CONTROL_1, &val);
- val |= SGMII_AN_ENABLE;
- regmap_write(mpcs->regmap, SGMSYS_PCS_CONTROL_1, val);
+ regmap_read(mpcs->regmap, SGMSYS_PCS_CONTROL_1, &bm);
+ if (bm & SGMII_AN_ENABLE) {
+ regmap_read(mpcs->regmap, SGMSYS_PCS_ADVERTISE, &adv);
- if (MTK_HAS_FLAGS(mpcs->flags, MTK_SGMII_PN_SWAP))
- regmap_update_bits(mpcs->regmap, SGMSYS_QPHY_WRAP_CTRL,
- SGMII_PN_SWAP_MASK, SGMII_PN_SWAP_TX_RX);
+ phylink_mii_c22_pcs_decode_state(state,
+ FIELD_GET(SGMII_BMSR, bm),
+ FIELD_GET(SGMII_LPA, adv));
+ } else {
+ state->link = !!(bm & SGMII_LINK_STATYS);
- /* Release PHYA power down state */
- regmap_write(mpcs->regmap, SGMSYS_QPHY_PWR_STATE_CTRL, 0);
+ regmap_read(mpcs->regmap, SGMSYS_SGMII_MODE, &sgm_mode);
- if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V3))
- mtk_sgmii_setup_phya_gen1(mpcs);
+ switch (sgm_mode & SGMII_SPEED_MASK) {
+ case SGMII_SPEED_10:
+ state->speed = SPEED_10;
+ break;
+ case SGMII_SPEED_100:
+ state->speed = SPEED_100;
+ break;
+ case SGMII_SPEED_1000:
+ regmap_read(mpcs->regmap, mpcs->ana_rgc3, &rgc3);
+ rgc3 = FIELD_GET(RG_PHY_SPEED_3_125G, rgc3);
+ state->speed = rgc3 ? SPEED_2500 : SPEED_1000;
+ break;
+ }
- return 0;
+ if (sgm_mode & SGMII_DUPLEX_HALF)
+ state->duplex = DUPLEX_HALF;
+ else
+ state->duplex = DUPLEX_FULL;
+ }
}
-int mtk_sgmii_setup_mode_force(struct mtk_sgmii_pcs *mpcs,
- phy_interface_t interface)
+static int mtk_sgmii_pcs_config(struct phylink_pcs *pcs, unsigned int mode,
+ phy_interface_t interface,
+ const unsigned long *advertising,
+ bool permit_pause_to_mac)
{
+ struct mtk_sgmii_pcs *mpcs = pcs_to_mtk_sgmii_pcs(pcs);
struct mtk_eth *eth = mpcs->eth;
- unsigned int val = 0;
+ unsigned int rgc3, sgm_mode = 0, bmcr = 0, speed = 0;
+ bool mode_changed = false, changed;
+ int advertise, link_timer;
- if (!mpcs->regmap)
- return -EINVAL;
+ advertise = phylink_mii_c22_pcs_encode_advertisement(interface,
+ advertising);
+ if (advertise < 0)
+ return advertise;
if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V3)) {
mtk_sgmii_xfi_pll_enable(eth->sgmii);
mtk_sgmii_reset(eth, mpcs->id);
}
- /* Assert PHYA power down state when needed */
- if (mtk_sgmii_need_powerdown(mpcs, interface))
- regmap_write(mpcs->regmap, SGMSYS_QPHY_PWR_STATE_CTRL,
- SGMII_PHYA_PWD);
+ /* Clearing IF_MODE_BIT0 switches the PCS to BASE-X mode, and
+ * we assume that fixes it's speed at bitrate = line rate (in
+ * other words, 1000Mbps or 2500Mbps).
+ */
+ if (interface == PHY_INTERFACE_MODE_SGMII) {
+ bmcr = SGMII_AN_ENABLE;
+ sgm_mode = SGMII_IF_MODE_SGMII |
+ SGMII_REMOTE_FAULT_DIS |
+ SGMII_SPEED_DUPLEX_AN;
+ } else {
+ /* 1000base-X or HSGMII without autoneg */
+ speed = SGMII_SPEED_1000;
+ if (interface == PHY_INTERFACE_MODE_2500BASEX)
+ sgm_mode = SGMII_IF_MODE_SGMII;
+ }
- /* Reset SGMII PCS state */
- regmap_write(mpcs->regmap, SGMII_RESERVED_0, SGMII_SW_RESET);
+ if (mpcs->interface != interface) {
+ link_timer = phylink_get_link_timer_ns(interface);
+ if (link_timer < 0)
+ return link_timer;
- regmap_read(mpcs->regmap, mpcs->ana_rgc3, &val);
- val &= ~RG_PHY_SPEED_MASK;
- if (interface == PHY_INTERFACE_MODE_2500BASEX)
- val |= RG_PHY_SPEED_3_125G;
- regmap_write(mpcs->regmap, mpcs->ana_rgc3, val);
+ /* PHYA power down */
+ regmap_update_bits(mpcs->regmap, SGMSYS_QPHY_PWR_STATE_CTRL,
+ SGMII_PHYA_PWD, SGMII_PHYA_PWD);
- /* Disable SGMII AN */
- regmap_read(mpcs->regmap, SGMSYS_PCS_CONTROL_1, &val);
- val &= ~SGMII_AN_ENABLE;
- regmap_write(mpcs->regmap, SGMSYS_PCS_CONTROL_1, val);
+ /* Reset SGMII PCS state */
+ regmap_update_bits(mpcs->regmap, SGMII_RESERVED_0,
+ SGMII_SW_RESET, SGMII_SW_RESET);
- /* Set the speed etc but leave the duplex unchanged */
- regmap_read(mpcs->regmap, SGMSYS_SGMII_MODE, &val);
- val &= SGMII_DUPLEX_HALF | ~SGMII_IF_MODE_MASK;
- val &= ~SGMII_REMOTE_FAULT_DIS;
- val |= SGMII_SPEED_1000;
- regmap_write(mpcs->regmap, SGMSYS_SGMII_MODE, val);
+ if (interface == PHY_INTERFACE_MODE_2500BASEX)
+ rgc3 = RG_PHY_SPEED_3_125G;
+ else
+ rgc3 = 0;
- if (MTK_HAS_FLAGS(mpcs->flags, MTK_SGMII_PN_SWAP))
- regmap_update_bits(mpcs->regmap, SGMSYS_QPHY_WRAP_CTRL,
- SGMII_PN_SWAP_MASK, SGMII_PN_SWAP_TX_RX);
+ /* Configure the underlying interface speed */
+ regmap_update_bits(mpcs->regmap, mpcs->ana_rgc3,
+ RG_PHY_SPEED_3_125G, rgc3);
- /* Release PHYA power down state */
- regmap_write(mpcs->regmap, SGMSYS_QPHY_PWR_STATE_CTRL, 0);
+ /* Setup the link timer */
+ regmap_write(mpcs->regmap, SGMSYS_PCS_LINK_TIMER,
+ link_timer / 2 / 8);
- if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V3))
- mtk_sgmii_setup_phya_gen2(mpcs);
+ mpcs->interface = interface;
+ mode_changed = true;
+ }
- return 0;
-}
+ /* Update the advertisement, noting whether it has changed */
+ regmap_update_bits_check(mpcs->regmap, SGMSYS_PCS_ADVERTISE,
+ SGMII_ADVERTISE, advertise, &changed);
-static void mtk_sgmii_pcs_get_state(struct phylink_pcs *pcs,
- struct phylink_link_state *state)
-{
- struct mtk_sgmii_pcs *mpcs = pcs_to_mtk_sgmii_pcs(pcs);
- u32 rgc3, val = 0;
+ /* Update the sgmsys mode register */
+ regmap_update_bits(mpcs->regmap, SGMSYS_SGMII_MODE,
+ SGMII_REMOTE_FAULT_DIS | SGMII_DUPLEX_HALF |
+ SGMII_SPEED_MASK | SGMII_SPEED_DUPLEX_AN |
+ SGMII_IF_MODE_SGMII, sgm_mode | speed);
- state->interface = mpcs->interface;
+ /* Update the BMCR */
+ regmap_update_bits(mpcs->regmap, SGMSYS_PCS_CONTROL_1,
+ SGMII_AN_ENABLE, bmcr);
- regmap_read(mpcs->regmap, SGMSYS_PCS_CONTROL_1, &val);
- if (FIELD_GET(SGMII_AN_ENABLE, val)) {
- regmap_read(mpcs->regmap, SGMII_PCS_SPEED_ABILITY, &val);
- val = val >> 16;
- state->link = FIELD_GET(SGMII_PCS_SPEED_LINK, val);
- state->duplex = FIELD_GET(SGMII_PCS_SPEED_DUPLEX, val);
- switch (FIELD_GET(SGMII_PCS_SPEED_MASK, val)) {
- case 0:
- state->speed = SPEED_10;
- break;
- case 1:
- state->speed = SPEED_100;
- break;
- case 2:
- state->speed = SPEED_1000;
- break;
- }
- } else {
- state->link = FIELD_GET(SGMII_LINK_STATYS, val);
+ /* Release PHYA power down state */
+ usleep_range(50, 100);
+ regmap_write(mpcs->regmap, SGMSYS_QPHY_PWR_STATE_CTRL, 0);
- regmap_read(mpcs->regmap, SGMSYS_SGMII_MODE, &val);
- state->duplex = !FIELD_GET(SGMII_DUPLEX_HALF, val);
- switch (FIELD_GET(SGMII_SPEED_MASK, val)) {
- case 0:
- state->speed = SPEED_10;
- break;
- case 1:
- state->speed = SPEED_100;
- break;
- case 2:
- regmap_read(mpcs->regmap, mpcs->ana_rgc3, &val);
- rgc3 = FIELD_GET(RG_PHY_SPEED_3_125G, val);
- state->speed = rgc3 ? SPEED_2500 : SPEED_1000;
- break;
- }
+ if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V3)) {
+ if (interface == PHY_INTERFACE_MODE_2500BASEX)
+ mtk_sgmii_setup_phya_gen2(mpcs);
+ else
+ mtk_sgmii_setup_phya_gen1(mpcs);
}
-}
-
-static int mtk_sgmii_pcs_config(struct phylink_pcs *pcs, unsigned int mode,
- phy_interface_t interface,
- const unsigned long *advertising,
- bool permit_pause_to_mac)
-{
- struct mtk_sgmii_pcs *mpcs = pcs_to_mtk_sgmii_pcs(pcs);
- int err = 0;
- mpcs->interface = interface;
-
- /* Setup SGMIISYS with the determined property */
- if (interface != PHY_INTERFACE_MODE_SGMII)
- err = mtk_sgmii_setup_mode_force(mpcs, interface);
- else
- err = mtk_sgmii_setup_mode_an(mpcs);
-
- return err;
+ return changed || mode_changed;
}
void mtk_sgmii_pcs_restart_an(struct phylink_pcs *pcs)
@@ -561,18 +500,29 @@
int speed, int duplex)
{
struct mtk_sgmii_pcs *mpcs = pcs_to_mtk_sgmii_pcs(pcs);
- unsigned int val;
+ unsigned int sgm_mode, val;
- if (!phy_interface_mode_is_8023z(interface))
+ /* If autoneg is enabled, the force speed and duplex
+ * are not useful, so don't go any further.
+ */
+ regmap_read(mpcs->regmap, SGMSYS_PCS_CONTROL_1, &val);
+ if (val & SGMII_AN_ENABLE)
return;
+ /* SGMII force speed and duplex setting */
+ if (speed == SPEED_10)
+ sgm_mode = SGMII_SPEED_10;
+ else if (speed == SPEED_100)
+ sgm_mode = SGMII_SPEED_100;
+ else
+ sgm_mode = SGMII_SPEED_1000;
+
- /* SGMII force duplex setting */
- regmap_read(mpcs->regmap, SGMSYS_SGMII_MODE, &val);
- val &= ~SGMII_DUPLEX_HALF;
if (duplex != DUPLEX_FULL)
- val |= SGMII_DUPLEX_HALF;
+ sgm_mode |= SGMII_DUPLEX_HALF;
- regmap_write(mpcs->regmap, SGMSYS_SGMII_MODE, val);
+ regmap_update_bits(mpcs->regmap, SGMSYS_SGMII_MODE,
+ SGMII_DUPLEX_HALF | SGMII_SPEED_MASK,
+ sgm_mode);
}
static const struct phylink_pcs_ops mtk_sgmii_pcs_ops = {