[][kernel][common][eth][Add phylink_pcs support for the SGMII/USXGMII]
[Description]
Add phylink_pcs support for the SGMII/USXGMII.
If without this patch, phylink framework cannot configure MAC and PCS
properly for the inband mode.
[Release-log]
N/A
Change-Id: I6cda355eeb2abb3c90a9120f3b77b3721200dc81
Reviewed-on: https://gerrit.mediatek.inc/c/openwrt/feeds/mtk_openwrt_feeds/+/7353960
diff --git a/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_eth_reset.c b/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_eth_reset.c
index f246e85..eebec74 100644
--- a/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_eth_reset.c
+++ b/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_eth_reset.c
@@ -209,9 +209,9 @@
if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V3)) {
mtk_dump_reg(eth, "XGMAC0", 0x12000, 0x300);
mtk_dump_reg(eth, "XGMAC1", 0x13000, 0x300);
- mtk_dump_usxgmii(eth->xgmii->regmap_usxgmii[0],
+ mtk_dump_usxgmii(eth->usxgmii->pcs[0].regmap,
"USXGMII0", 0, 0x1000);
- mtk_dump_usxgmii(eth->xgmii->regmap_usxgmii[1],
+ mtk_dump_usxgmii(eth->usxgmii->pcs[1].regmap,
"USXGMII1", 0, 0x1000);
}
}
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 134e579..1368a7b 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
@@ -500,6 +500,34 @@
mtk_w32(eth, mcr, MTK_MAC_MCR(mac->id));
}
+static struct phylink_pcs *mtk_mac_select_pcs(struct phylink_config *config,
+ phy_interface_t interface)
+{
+ struct mtk_mac *mac = container_of(config, struct mtk_mac,
+ phylink_config);
+ struct mtk_eth *eth = mac->hw;
+ unsigned int sid;
+
+ if (interface == PHY_INTERFACE_MODE_SGMII ||
+ phy_interface_mode_is_8023z(interface)) {
+ sid = (MTK_HAS_CAPS(eth->soc->caps, MTK_SHARED_SGMII)) ?
+ 0 : mtk_mac2xgmii_id(eth, mac->id);
+
+ return mtk_sgmii_select_pcs(eth->sgmii, sid);
+ } else if (interface == PHY_INTERFACE_MODE_USXGMII ||
+ interface == PHY_INTERFACE_MODE_10GKR ||
+ interface == PHY_INTERFACE_MODE_5GBASER) {
+ if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V3) &&
+ mac->id != MTK_GMAC1_ID) {
+ sid = mtk_mac2xgmii_id(eth, mac->id);
+
+ return mtk_usxgmii_select_pcs(eth->usxgmii, sid);
+ }
+ }
+
+ return NULL;
+}
+
static void mtk_mac_config(struct phylink_config *config, unsigned int mode,
const struct phylink_link_state *state)
{
@@ -649,38 +677,13 @@
sid = (MTK_HAS_CAPS(eth->soc->caps, MTK_SHARED_SGMII)) ?
0 : mac->id;
- /* Setup SGMIISYS with the determined property */
- if (state->interface != PHY_INTERFACE_MODE_SGMII)
- err = mtk_sgmii_setup_mode_force(eth->xgmii, sid,
- state);
- else
- err = mtk_sgmii_setup_mode_an(eth->xgmii, sid);
-
- if (err) {
- spin_unlock(ð->syscfg0_lock);
- goto init_err;
- }
-
- regmap_update_bits(eth->ethsys, ETHSYS_SYSCFG0,
- SYSCFG0_SGMII_MASK, val);
+ /* Save the syscfg0 value for mac_finish */
+ mac->syscfg0 = val;
spin_unlock(ð->syscfg0_lock);
} else if (state->interface == PHY_INTERFACE_MODE_USXGMII ||
state->interface == PHY_INTERFACE_MODE_10GKR ||
state->interface == PHY_INTERFACE_MODE_5GBASER) {
- sid = mac->id;
-
- if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V3) &&
- sid != MTK_GMAC1_ID) {
- if (phylink_autoneg_inband(mode))
- err = mtk_usxgmii_setup_mode_force(eth->xgmii, sid,
- state);
- else
- err = mtk_usxgmii_setup_mode_an(eth->xgmii, sid,
- SPEED_10000);
-
- if (err)
- goto init_err;
- }
+ /* Nothing to do */
} else if (phylink_autoneg_inband(mode)) {
dev_err(eth->dev,
"In-band mode not supported in non SGMII mode!\n");
@@ -730,6 +733,10 @@
}
}
+ /* FIXME: In current hardware design, we have to reset FE
+ * when swtiching XGDM to GDM. Therefore, here trigger an SER
+ * to let GDM go back to the initial state.
+ */
if (mac->type != mac_type) {
if (atomic_read(&reset_pending) == 0) {
atomic_inc(&force);
@@ -752,6 +759,22 @@
mac->id, phy_modes(state->interface), err);
}
+static int mtk_mac_finish(struct phylink_config *config, unsigned int mode,
+ phy_interface_t interface)
+{
+ struct mtk_mac *mac = container_of(config, struct mtk_mac,
+ phylink_config);
+ struct mtk_eth *eth = mac->hw;
+
+ /* Enable SGMII */
+ if (interface == PHY_INTERFACE_MODE_SGMII ||
+ phy_interface_mode_is_8023z(interface))
+ regmap_update_bits(eth->ethsys, ETHSYS_SYSCFG0,
+ SYSCFG0_SGMII_MASK, mac->syscfg0);
+
+ return 0;
+}
+
static int mtk_mac_pcs_get_state(struct phylink_config *config,
struct phylink_link_state *state)
{
@@ -764,7 +787,7 @@
if (mac->id == MTK_GMAC2_ID)
sts = sts >> 16;
- state->duplex = 1;
+ state->duplex = DUPLEX_FULL;
switch (FIELD_GET(MTK_USXGMII_PCS_MODE, sts)) {
case 0:
@@ -785,18 +808,19 @@
state->link = FIELD_GET(MTK_USXGMII_PCS_LINK, sts);
} else if (mac->type == MTK_GDM_TYPE) {
struct mtk_eth *eth = mac->hw;
- struct mtk_xgmii *ss = eth->xgmii;
+ 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 val = 0;
+ u32 rgc3, val = 0;
- regmap_read(ss->regmap_sgmii[id], SGMSYS_PCS_CONTROL_1, &val);
+ regmap_read(ss->pcs[id].regmap, SGMSYS_PCS_CONTROL_1, &val);
state->interface = mac->interface;
state->link = FIELD_GET(SGMII_LINK_STATYS, val);
if (FIELD_GET(SGMII_AN_ENABLE, val)) {
- regmap_read(ss->regmap_sgmii[id], SGMII_PCS_SPEED_ABILITY, &val);
+ regmap_read(ss->pcs[id].regmap,
+ SGMII_PCS_SPEED_ABILITY, &val);
val = val >> 16;
@@ -814,9 +838,10 @@
break;
}
} else {
- regmap_read(ss->regmap_sgmii[id], SGMSYS_SGMII_MODE, &val);
+ regmap_read(ss->pcs[id].regmap,
+ SGMSYS_SGMII_MODE, &val);
- state->duplex = !FIELD_GET(SGMII_DUPLEX_FULL, val);
+ state->duplex = !FIELD_GET(SGMII_DUPLEX_HALF, val);
switch (FIELD_GET(SGMII_SPEED_MASK, val)) {
case 0:
@@ -826,8 +851,10 @@
state->speed = SPEED_100;
break;
case 2:
- regmap_read(ss->regmap_sgmii[id], ss->ana_rgc3, &val);
- state->speed = (FIELD_GET(RG_PHY_SPEED_3_125G, val)) ? SPEED_2500 : SPEED_1000;
+ regmap_read(ss->pcs[id].regmap,
+ ss->pcs[id].ana_rgc3, &val);
+ rgc3 = FIELD_GET(RG_PHY_SPEED_3_125G, val);
+ state->speed = rgc3 ? SPEED_2500 : SPEED_1000;
break;
}
}
@@ -842,15 +869,6 @@
return 1;
}
-static void mtk_mac_an_restart(struct phylink_config *config)
-{
- struct mtk_mac *mac = container_of(config, struct mtk_mac,
- phylink_config);
-
- if (mac->type != MTK_XGDM_TYPE)
- mtk_sgmii_restart_an(mac->hw, mac->id);
-}
-
static void mtk_mac_link_down(struct phylink_config *config, unsigned int mode,
phy_interface_t interface)
{
@@ -1074,9 +1092,10 @@
static const struct phylink_mac_ops mtk_phylink_ops = {
.validate = mtk_validate,
+ .mac_select_pcs = mtk_mac_select_pcs,
.mac_link_state = mtk_mac_pcs_get_state,
- .mac_an_restart = mtk_mac_an_restart,
.mac_config = mtk_mac_config,
+ .mac_finish = mtk_mac_finish,
.mac_link_down = mtk_mac_link_down,
.mac_link_up = mtk_mac_link_up,
};
@@ -3397,6 +3416,7 @@
struct mtk_mac *mac = netdev_priv(dev);
struct mtk_eth *eth = mac->hw;
struct mtk_phylink_priv *phylink_priv = &mac->phylink_priv;
+ u32 id = mtk_mac2xgmii_id(eth, mac->id);
int err, i;
struct device_node *phy_node;
@@ -3472,8 +3492,9 @@
phylink_start(mac->phylink);
netif_start_queue(dev);
phy_node = of_parse_phandle(mac->of_node, "phy-handle", 0);
- if (!phy_node && eth->xgmii->regmap_sgmii[mac->id])
- regmap_write(eth->xgmii->regmap_sgmii[mac->id], SGMSYS_QPHY_PWR_STATE_CTRL, 0);
+ if (!phy_node && eth->sgmii->pcs[id].regmap)
+ regmap_write(eth->sgmii->pcs[id].regmap,
+ SGMSYS_QPHY_PWR_STATE_CTRL, 0);
mtk_gdm_config(eth, mac->id, MTK_GDMA_TO_PDMA);
@@ -3508,6 +3529,7 @@
struct mtk_mac *mac = netdev_priv(dev);
struct mtk_eth *eth = mac->hw;
int i;
+ u32 id = mtk_mac2xgmii_id(eth, mac->id);
u32 val = 0;
struct device_node *phy_node;
@@ -3515,10 +3537,12 @@
netif_tx_disable(dev);
phy_node = of_parse_phandle(mac->of_node, "phy-handle", 0);
- if (!phy_node && eth->xgmii->regmap_sgmii[mac->id]) {
- regmap_read(eth->xgmii->regmap_sgmii[mac->id], SGMSYS_QPHY_PWR_STATE_CTRL, &val);
+ if (!phy_node && eth->sgmii->pcs[id].regmap) {
+ regmap_read(eth->sgmii->pcs[id].regmap,
+ SGMSYS_QPHY_PWR_STATE_CTRL, &val);
val |= SGMII_PHYA_PWD;
- regmap_write(eth->xgmii->regmap_sgmii[mac->id], SGMSYS_QPHY_PWR_STATE_CTRL, val);
+ regmap_write(eth->sgmii->pcs[id].regmap,
+ SGMSYS_QPHY_PWR_STATE_CTRL, val);
}
//GMAC RX disable
@@ -4589,29 +4613,24 @@
}
if (MTK_HAS_CAPS(eth->soc->caps, MTK_SGMII)) {
- eth->xgmii = devm_kzalloc(eth->dev, sizeof(*eth->xgmii),
+ eth->sgmii = devm_kzalloc(eth->dev, sizeof(*eth->sgmii),
GFP_KERNEL);
- if (!eth->xgmii)
+ if (!eth->sgmii)
return -ENOMEM;
- eth->xgmii->eth = eth;
- err = mtk_sgmii_init(eth->xgmii, pdev->dev.of_node,
+ err = mtk_sgmii_init(eth, pdev->dev.of_node,
eth->soc->ana_rgc3);
-
if (err)
return err;
}
if (MTK_HAS_CAPS(eth->soc->caps, MTK_USXGMII)) {
- err = mtk_usxgmii_init(eth->xgmii, pdev->dev.of_node);
- if (err)
- return err;
-
- err = mtk_xfi_pextp_init(eth->xgmii, pdev->dev.of_node);
- if (err)
- return err;
+ eth->usxgmii = devm_kzalloc(eth->dev, sizeof(*eth->usxgmii),
+ GFP_KERNEL);
+ if (!eth->usxgmii)
+ return -ENOMEM;
- err = mtk_xfi_pll_init(eth->xgmii, pdev->dev.of_node);
+ err = mtk_usxgmii_init(eth, pdev->dev.of_node);
if (err)
return err;
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 6035f46..646e557 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
@@ -852,7 +852,7 @@
#define SGMII_SPEED_10 0x0
#define SGMII_SPEED_100 BIT(2)
#define SGMII_SPEED_1000 BIT(3)
-#define SGMII_DUPLEX_FULL BIT(4)
+#define SGMII_DUPLEX_HALF BIT(4)
#define SGMII_IF_MODE_BIT5 BIT(5)
#define SGMII_REMOTE_FAULT_DIS BIT(8)
#define SGMII_CODE_SYNC_SET_VAL BIT(9)
@@ -902,7 +902,21 @@
/* Register to control PCS AN */
#define RG_PCS_AN_CTRL0 0x810
-#define RG_AN_ENABLE BIT(0)
+#define USXGMII_AN_RESTART BIT(31)
+#define USXGMII_AN_ENABLE BIT(0)
+
+/* Register to control PCS AN */
+#define RG_PCS_AN_STS0 0x81C
+#define USXGMII_LPA_SPEED_MASK GENMASK(11, 9)
+#define USXGMII_LPA_SPEED_10 0
+#define USXGMII_LPA_SPEED_100 1
+#define USXGMII_LPA_SPEED_1000 2
+#define USXGMII_LPA_SPEED_10000 3
+#define USXGMII_LPA_SPEED_2500 4
+#define USXGMII_LPA_SPEED_5000 5
+#define USXGMII_LPA_DUPLEX BIT(12)
+#define USXGMII_LPA_LINK BIT(15)
+#define USXGMII_LPA_LATCH BIT(31)
/* Register to control USXGMII XFI PLL digital */
#define XFI_PLL_DIG_GLB8 0x08
@@ -1610,24 +1624,66 @@
#define MTK_SGMII_PN_SWAP BIT(16)
#define MTK_HAS_FLAGS(flags, _x) (((flags) & (_x)) == (_x))
-/* struct mtk_xgmii - This is the structure holding sgmii/usxgmii regmap and
- * its characteristics
- * @regmap: The register map pointing at the range used to setup
- * SGMII/USXGMII modes
- * @flags: The enum refers to which mode the sgmii wants to run on
- * @ana_rgc3: The offset refers to register ANA_RGC3 related to regmap
+/* struct mtk_sgmii_pcs - This structure holds each sgmii regmap and associated
+ * data
+ * @regmap: The register map pointing at the range used to setup
+ * SGMII modes
+ * @regmap_pextp: The register map pointing at the range used to setup
+ * PHYA
+ * @ana_rgc3: The offset refers to register ANA_RGC3 related to regmap
+ * @id: The element is used to record the index of PCS
+ * @pcs: Phylink PCS structure
*/
+struct mtk_sgmii_pcs {
+ struct mtk_eth *eth;
+ struct regmap *regmap;
+ struct regmap *regmap_pextp;
+ phy_interface_t interface;
+ u32 flags;
+ u32 ana_rgc3;
+ u8 id;
+ struct phylink_pcs pcs;
+};
-struct mtk_xgmii {
- struct mtk_eth *eth;
- struct regmap *regmap_sgmii[MTK_MAX_DEVS];
- struct regmap *regmap_usxgmii[MTK_MAX_DEVS];
- struct regmap *regmap_pextp[MTK_MAX_DEVS];
- struct regmap *regmap_pll;
- u32 flags[MTK_MAX_DEVS];
- u32 ana_rgc3;
+/* struct mtk_sgmii - This is the structure holding sgmii regmap and its
+ * characteristics
+ * @pll: The register map pointing at the range used to setup
+ * PLL
+ * @pcs Array of individual PCS structures
+ */
+struct mtk_sgmii {
+ struct mtk_sgmii_pcs pcs[MTK_MAX_DEVS];
+ struct regmap *pll;
};
+/* struct mtk_usxgmii_pcs - This structure holds each usxgmii regmap and
+ * associated data
+ * @regmap: The register map pointing at the range used to setup
+ * USXGMII modes
+ * @regmap_pextp: The register map pointing at the range used to setup
+ * PHYA
+ * @id: The element is used to record the index of PCS
+ * @pcs: Phylink PCS structure
+ */
+struct mtk_usxgmii_pcs {
+ struct mtk_eth *eth;
+ struct regmap *regmap;
+ struct regmap *regmap_pextp;
+ phy_interface_t interface;
+ u8 id;
+ struct phylink_pcs pcs;
+};
+
+/* struct mtk_usxgmii - This is the structure holding usxgmii regmap and its
+ * characteristics
+ * @pll: The register map pointing at the range used to setup
+ * PLL
+ * @pcs Array of individual PCS structures
+ */
+struct mtk_usxgmii {
+ struct mtk_usxgmii_pcs pcs[MTK_MAX_DEVS];
+ struct regmap *pll;
+};
/* struct mtk_reset_event - This is the structure holding statistics counters
* for reset events
@@ -1705,7 +1761,8 @@
struct regmap *ethsys;
struct regmap *infra;
struct regmap *toprgu;
- struct mtk_xgmii *xgmii;
+ struct mtk_sgmii *sgmii;
+ struct mtk_usxgmii *usxgmii;
struct regmap *pctl;
bool hwlro;
refcount_t dma_refcnt;
@@ -1754,6 +1811,7 @@
struct mtk_hw_stats *hw_stats;
__be32 hwlro_ip[MTK_MAX_LRO_IP_CNT];
int hwlro_ip_cnt;
+ unsigned int syscfg0;
bool tx_lpi_enabled;
u32 tx_lpi_timer;
};
@@ -1770,13 +1828,9 @@
u32 mtk_r32(struct mtk_eth *eth, unsigned reg);
u32 mtk_m32(struct mtk_eth *eth, u32 mask, u32 set, unsigned reg);
-int mtk_sgmii_init(struct mtk_xgmii *ss, struct device_node *np,
+struct phylink_pcs *mtk_sgmii_select_pcs(struct mtk_sgmii *ss, int id);
+int mtk_sgmii_init(struct mtk_eth *eth, struct device_node *np,
u32 ana_rgc3);
-int mtk_sgmii_setup_mode_an(struct mtk_xgmii *ss, unsigned int mac_id);
-int mtk_sgmii_setup_mode_force(struct mtk_xgmii *ss, unsigned int mac_id,
- const struct phylink_link_state *state);
-void mtk_sgmii_restart_an(struct mtk_eth *eth, int mac_id);
-void mtk_sgmii_setup_phya_gen2(struct mtk_xgmii *ss, int mac_id);
int mtk_gmac_sgmii_path_setup(struct mtk_eth *eth, int mac_id);
int mtk_gmac_xgmii_path_setup(struct mtk_eth *eth, int mac_id);
@@ -1787,17 +1841,9 @@
void ethsys_reset(struct mtk_eth *eth, u32 reset_bits);
int mtk_mac2xgmii_id(struct mtk_eth *eth, int mac_id);
-int mtk_usxgmii_init(struct mtk_xgmii *ss, struct device_node *r);
-int mtk_xfi_pextp_init(struct mtk_xgmii *ss, struct device_node *r);
-int mtk_xfi_pll_init(struct mtk_xgmii *ss, struct device_node *r);
+struct phylink_pcs *mtk_usxgmii_select_pcs(struct mtk_usxgmii *ss, int id);
+int mtk_usxgmii_init(struct mtk_eth *eth, struct device_node *r);
int mtk_toprgu_init(struct mtk_eth *eth, struct device_node *r);
-int mtk_xfi_pll_enable(struct mtk_xgmii *ss);
-int mtk_usxgmii_setup_mode_an(struct mtk_xgmii *ss, int mac_id,
- int max_speed);
-int mtk_usxgmii_setup_mode_force(struct mtk_xgmii *ss, int mac_id,
- const struct phylink_link_state *state);
-void mtk_usxgmii_setup_phya_an_10000(struct mtk_xgmii *ss, int mac_id);
-void mtk_usxgmii_reset(struct mtk_xgmii *ss, int mac_id);
int mtk_dump_usxgmii(struct regmap *pmap, char *name, u32 offset, u32 range);
void mtk_eth_set_dma_device(struct mtk_eth *eth, struct device *dma_dev);
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 5d792ae..3c37656 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
@@ -13,52 +13,85 @@
#include "mtk_eth_soc.h"
-int mtk_sgmii_init(struct mtk_xgmii *ss, struct device_node *r, u32 ana_rgc3)
+static struct mtk_sgmii_pcs *pcs_to_mtk_sgmii_pcs(struct phylink_pcs *pcs)
+{
+ return container_of(pcs, struct mtk_sgmii_pcs, pcs);
+}
+
+static int mtk_sgmii_xfi_pextp_init(struct mtk_sgmii *ss, struct device_node *r)
{
struct device_node *np;
int i;
- ss->ana_rgc3 = ana_rgc3;
-
for (i = 0; i < MTK_MAX_DEVS; i++) {
- np = of_parse_phandle(r, "mediatek,sgmiisys", i);
+ np = of_parse_phandle(r, "mediatek,xfi_pextp", i);
if (!np)
break;
- ss->regmap_sgmii[i] = syscon_node_to_regmap(np);
- if (IS_ERR(ss->regmap_sgmii[i]))
- return PTR_ERR(ss->regmap_sgmii[i]);
+ ss->pcs[i].regmap_pextp = syscon_node_to_regmap(np);
+ if (IS_ERR(ss->pcs[i].regmap_pextp))
+ return PTR_ERR(ss->pcs[i].regmap_pextp);
- ss->flags[i] &= ~(MTK_SGMII_PN_SWAP);
- if (of_property_read_bool(np, "pn_swap"))
- ss->flags[i] |= MTK_SGMII_PN_SWAP;
+ of_node_put(np);
}
return 0;
}
+static int mtk_sgmii_xfi_pll_init(struct mtk_sgmii *ss, struct device_node *r)
+{
+ struct device_node *np;
+
+ np = of_parse_phandle(r, "mediatek,xfi_pll", 0);
+ if (!np)
+ return -1;
+
+ ss->pll = syscon_node_to_regmap(np);
+ if (IS_ERR(ss->pll))
+ return PTR_ERR(ss->pll);
+
+ of_node_put(np);
+
+ return 0;
+}
+
-void mtk_sgmii_reset(struct mtk_xgmii *ss, int mac_id)
+static int mtk_sgmii_xfi_pll_enable(struct mtk_sgmii *ss)
{
- struct mtk_eth *eth = ss->eth;
- u32 id = mtk_mac2xgmii_id(eth, mac_id);
u32 val = 0;
- if (id >= MTK_MAX_DEVS || !eth->toprgu)
+ if (!ss->pll)
+ return -EINVAL;
+
+ /* Add software workaround for USXGMII PLL TCL issue */
+ regmap_write(ss->pll, XFI_PLL_ANA_GLB8, RG_XFI_PLL_ANA_SWWA);
+
+ regmap_read(ss->pll, XFI_PLL_DIG_GLB8, &val);
+ val |= RG_XFI_PLL_EN;
+ regmap_write(ss->pll, XFI_PLL_DIG_GLB8, val);
+
+ return 0;
+}
+
+void mtk_sgmii_reset(struct mtk_eth *eth, int id)
+{
+ u32 val = 0;
+
+ if (!eth->toprgu)
return;
- switch (mac_id) {
- case MTK_GMAC2_ID:
+ switch (id) {
+ case 0:
/* Enable software reset */
regmap_read(eth->toprgu, TOPRGU_SWSYSRST_EN, &val);
- val |= SWSYSRST_XFI_PEXPT1_GRST |
- SWSYSRST_SGMII1_GRST;
+ val |= SWSYSRST_XFI_PEXPT0_GRST |
+ SWSYSRST_SGMII0_GRST;
regmap_write(eth->toprgu, TOPRGU_SWSYSRST_EN, val);
/* Assert SGMII reset */
regmap_read(eth->toprgu, TOPRGU_SWSYSRST, &val);
val |= FIELD_PREP(SWSYSRST_UNLOCK_KEY, 0x88) |
- SWSYSRST_XFI_PEXPT1_GRST |
- SWSYSRST_SGMII1_GRST;
+ SWSYSRST_XFI_PEXPT0_GRST |
+ SWSYSRST_SGMII0_GRST;
regmap_write(eth->toprgu, TOPRGU_SWSYSRST, val);
udelay(100);
@@ -66,28 +99,28 @@
/* De-assert SGMII reset */
regmap_read(eth->toprgu, TOPRGU_SWSYSRST, &val);
val |= FIELD_PREP(SWSYSRST_UNLOCK_KEY, 0x88);
- val &= ~(SWSYSRST_XFI_PEXPT1_GRST |
- SWSYSRST_SGMII1_GRST);
+ val &= ~(SWSYSRST_XFI_PEXPT0_GRST |
+ SWSYSRST_SGMII0_GRST);
regmap_write(eth->toprgu, TOPRGU_SWSYSRST, val);
/* Disable software reset */
regmap_read(eth->toprgu, TOPRGU_SWSYSRST_EN, &val);
- val &= ~(SWSYSRST_XFI_PEXPT1_GRST |
- SWSYSRST_SGMII1_GRST);
+ val &= ~(SWSYSRST_XFI_PEXPT0_GRST |
+ SWSYSRST_SGMII0_GRST);
regmap_write(eth->toprgu, TOPRGU_SWSYSRST_EN, val);
break;
- case MTK_GMAC3_ID:
- /* Enable Software reset */
+ case 1:
+ /* Enable software reset */
regmap_read(eth->toprgu, TOPRGU_SWSYSRST_EN, &val);
- val |= SWSYSRST_XFI_PEXPT0_GRST |
- SWSYSRST_SGMII0_GRST;
+ val |= SWSYSRST_XFI_PEXPT1_GRST |
+ SWSYSRST_SGMII1_GRST;
regmap_write(eth->toprgu, TOPRGU_SWSYSRST_EN, val);
/* Assert SGMII reset */
regmap_read(eth->toprgu, TOPRGU_SWSYSRST, &val);
val |= FIELD_PREP(SWSYSRST_UNLOCK_KEY, 0x88) |
- SWSYSRST_XFI_PEXPT0_GRST |
- SWSYSRST_SGMII0_GRST;
+ SWSYSRST_XFI_PEXPT1_GRST |
+ SWSYSRST_SGMII1_GRST;
regmap_write(eth->toprgu, TOPRGU_SWSYSRST, val);
udelay(100);
@@ -95,14 +128,14 @@
/* De-assert SGMII reset */
regmap_read(eth->toprgu, TOPRGU_SWSYSRST, &val);
val |= FIELD_PREP(SWSYSRST_UNLOCK_KEY, 0x88);
- val &= ~(SWSYSRST_XFI_PEXPT0_GRST |
- SWSYSRST_SGMII0_GRST);
+ val &= ~(SWSYSRST_XFI_PEXPT1_GRST |
+ SWSYSRST_SGMII1_GRST);
regmap_write(eth->toprgu, TOPRGU_SWSYSRST, val);
/* Disable software reset */
regmap_read(eth->toprgu, TOPRGU_SWSYSRST_EN, &val);
- val &= ~(SWSYSRST_XFI_PEXPT0_GRST |
- SWSYSRST_SGMII0_GRST);
+ val &= ~(SWSYSRST_XFI_PEXPT1_GRST |
+ SWSYSRST_SGMII1_GRST);
regmap_write(eth->toprgu, TOPRGU_SWSYSRST_EN, val);
break;
}
@@ -110,18 +143,19 @@
mdelay(1);
}
-int mtk_sgmii_need_powerdown(struct mtk_xgmii *ss, int id, int speed)
+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(ss->regmap_sgmii[id], SGMSYS_PCS_CONTROL_1, &val);
+ regmap_read(mpcs->regmap, SGMSYS_PCS_CONTROL_1, &val);
if (!(val & SGMII_LINK_STATYS))
return true;
- /* need to power down sgmii if link speed changed */
- regmap_read(ss->regmap_sgmii[id], ss->ana_rgc3, &val);
- if (speed == SPEED_2500) {
+ /* 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 {
@@ -132,272 +166,469 @@
return false;
}
-void mtk_sgmii_setup_phya_gen1(struct mtk_xgmii *ss, int mac_id)
+void mtk_sgmii_setup_phya_gen1(struct mtk_sgmii_pcs *mpcs)
{
- u32 id = mtk_mac2xgmii_id(ss->eth, mac_id);
-
- if (id >= MTK_MAX_DEVS ||
- !ss->regmap_sgmii[id] || !ss->regmap_pextp[id])
+ if (!mpcs->regmap_pextp)
return;
- regmap_update_bits(ss->regmap_pextp[id], 0x9024, GENMASK(31, 0), 0x00D9071C);
- regmap_update_bits(ss->regmap_pextp[id], 0x2020, GENMASK(31, 0), 0xAA8585AA);
- regmap_update_bits(ss->regmap_pextp[id], 0x2030, GENMASK(31, 0), 0x0C020207);
- regmap_update_bits(ss->regmap_pextp[id], 0x2034, GENMASK(31, 0), 0x0E05050F);
- regmap_update_bits(ss->regmap_pextp[id], 0x2040, GENMASK(31, 0), 0x00200032);
- regmap_update_bits(ss->regmap_pextp[id], 0x50F0, GENMASK(31, 0), 0x00C014BA);
- regmap_update_bits(ss->regmap_pextp[id], 0x50E0, GENMASK(31, 0), 0x3777C12B);
- regmap_update_bits(ss->regmap_pextp[id], 0x506C, GENMASK(31, 0), 0x005F9CFF);
- regmap_update_bits(ss->regmap_pextp[id], 0x5070, GENMASK(31, 0), 0x9D9DFAFA);
- regmap_update_bits(ss->regmap_pextp[id], 0x5074, GENMASK(31, 0), 0x27273F3F);
- regmap_update_bits(ss->regmap_pextp[id], 0x5078, GENMASK(31, 0), 0xA7883C68);
- regmap_update_bits(ss->regmap_pextp[id], 0x507C, GENMASK(31, 0), 0x11661166);
- regmap_update_bits(ss->regmap_pextp[id], 0x5080, GENMASK(31, 0), 0x0E000EAF);
- regmap_update_bits(ss->regmap_pextp[id], 0x5084, GENMASK(31, 0), 0x08080E0D);
- regmap_update_bits(ss->regmap_pextp[id], 0x5088, GENMASK(31, 0), 0x02030B09);
- regmap_update_bits(ss->regmap_pextp[id], 0x50E4, GENMASK(31, 0), 0x0C0C0000);
- regmap_update_bits(ss->regmap_pextp[id], 0x50E8, GENMASK(31, 0), 0x04040000);
- regmap_update_bits(ss->regmap_pextp[id], 0x50EC, GENMASK(31, 0), 0x0F0F0606);
- regmap_update_bits(ss->regmap_pextp[id], 0x50A8, GENMASK(31, 0), 0x506E8C8C);
- regmap_update_bits(ss->regmap_pextp[id], 0x6004, GENMASK(31, 0), 0x18190000);
- regmap_update_bits(ss->regmap_pextp[id], 0x00F8, GENMASK(31, 0), 0x00FA32FA);
- regmap_update_bits(ss->regmap_pextp[id], 0x00F4, GENMASK(31, 0), 0x80201F21);
- regmap_update_bits(ss->regmap_pextp[id], 0x0030, GENMASK(31, 0), 0x00050C00);
- regmap_update_bits(ss->regmap_pextp[id], 0x0070, GENMASK(31, 0), 0x02002800);
+ regmap_update_bits(mpcs->regmap_pextp, 0x9024, GENMASK(31, 0),
+ 0x00D9071C);
+ regmap_update_bits(mpcs->regmap_pextp, 0x2020, GENMASK(31, 0),
+ 0xAA8585AA);
+ regmap_update_bits(mpcs->regmap_pextp, 0x2030, GENMASK(31, 0),
+ 0x0C020207);
+ regmap_update_bits(mpcs->regmap_pextp, 0x2034, GENMASK(31, 0),
+ 0x0E05050F);
+ regmap_update_bits(mpcs->regmap_pextp, 0x2040, GENMASK(31, 0),
+ 0x00200032);
+ regmap_update_bits(mpcs->regmap_pextp, 0x50F0, GENMASK(31, 0),
+ 0x00C014BA);
+ regmap_update_bits(mpcs->regmap_pextp, 0x50E0, GENMASK(31, 0),
+ 0x3777C12B);
+ regmap_update_bits(mpcs->regmap_pextp, 0x506C, GENMASK(31, 0),
+ 0x005F9CFF);
+ regmap_update_bits(mpcs->regmap_pextp, 0x5070, GENMASK(31, 0),
+ 0x9D9DFAFA);
+ regmap_update_bits(mpcs->regmap_pextp, 0x5074, GENMASK(31, 0),
+ 0x27273F3F);
+ regmap_update_bits(mpcs->regmap_pextp, 0x5078, GENMASK(31, 0),
+ 0xA7883C68);
+ regmap_update_bits(mpcs->regmap_pextp, 0x507C, GENMASK(31, 0),
+ 0x11661166);
+ regmap_update_bits(mpcs->regmap_pextp, 0x5080, GENMASK(31, 0),
+ 0x0E000EAF);
+ regmap_update_bits(mpcs->regmap_pextp, 0x5084, GENMASK(31, 0),
+ 0x08080E0D);
+ regmap_update_bits(mpcs->regmap_pextp, 0x5088, GENMASK(31, 0),
+ 0x02030B09);
+ regmap_update_bits(mpcs->regmap_pextp, 0x50E4, GENMASK(31, 0),
+ 0x0C0C0000);
+ regmap_update_bits(mpcs->regmap_pextp, 0x50E8, GENMASK(31, 0),
+ 0x04040000);
+ regmap_update_bits(mpcs->regmap_pextp, 0x50EC, GENMASK(31, 0),
+ 0x0F0F0606);
+ regmap_update_bits(mpcs->regmap_pextp, 0x50A8, GENMASK(31, 0),
+ 0x506E8C8C);
+ regmap_update_bits(mpcs->regmap_pextp, 0x6004, GENMASK(31, 0),
+ 0x18190000);
+ regmap_update_bits(mpcs->regmap_pextp, 0x00F8, GENMASK(31, 0),
+ 0x00FA32FA);
+ regmap_update_bits(mpcs->regmap_pextp, 0x00F4, GENMASK(31, 0),
+ 0x80201F21);
+ regmap_update_bits(mpcs->regmap_pextp, 0x0030, GENMASK(31, 0),
+ 0x00050C00);
+ regmap_update_bits(mpcs->regmap_pextp, 0x0070, GENMASK(31, 0),
+ 0x02002800);
ndelay(1020);
- regmap_update_bits(ss->regmap_pextp[id], 0x30B0, GENMASK(31, 0), 0x00000020);
- regmap_update_bits(ss->regmap_pextp[id], 0x3028, GENMASK(31, 0), 0x00008A01);
- regmap_update_bits(ss->regmap_pextp[id], 0x302C, GENMASK(31, 0), 0x0000A884);
- regmap_update_bits(ss->regmap_pextp[id], 0x3024, GENMASK(31, 0), 0x00083002);
- regmap_update_bits(ss->regmap_pextp[id], 0x3010, GENMASK(31, 0), 0x00011110);
- regmap_update_bits(ss->regmap_pextp[id], 0x3048, GENMASK(31, 0), 0x40704000);
- regmap_update_bits(ss->regmap_pextp[id], 0x3064, GENMASK(31, 0), 0x0000C000);
- regmap_update_bits(ss->regmap_pextp[id], 0x3050, GENMASK(31, 0), 0xA8000000);
- regmap_update_bits(ss->regmap_pextp[id], 0x3054, GENMASK(31, 0), 0x000000AA);
- regmap_update_bits(ss->regmap_pextp[id], 0x306C, GENMASK(31, 0), 0x20200F00);
- regmap_update_bits(ss->regmap_pextp[id], 0xA060, GENMASK(31, 0), 0x00050000);
- regmap_update_bits(ss->regmap_pextp[id], 0x90D0, GENMASK(31, 0), 0x00000007);
- regmap_update_bits(ss->regmap_pextp[id], 0x0070, GENMASK(31, 0), 0x0200E800);
+ regmap_update_bits(mpcs->regmap_pextp, 0x30B0, GENMASK(31, 0),
+ 0x00000020);
+ regmap_update_bits(mpcs->regmap_pextp, 0x3028, GENMASK(31, 0),
+ 0x00008A01);
+ regmap_update_bits(mpcs->regmap_pextp, 0x302C, GENMASK(31, 0),
+ 0x0000A884);
+ regmap_update_bits(mpcs->regmap_pextp, 0x3024, GENMASK(31, 0),
+ 0x00083002);
+ regmap_update_bits(mpcs->regmap_pextp, 0x3010, GENMASK(31, 0),
+ 0x00011110);
+ regmap_update_bits(mpcs->regmap_pextp, 0x3048, GENMASK(31, 0),
+ 0x40704000);
+ regmap_update_bits(mpcs->regmap_pextp, 0x3064, GENMASK(31, 0),
+ 0x0000C000);
+ regmap_update_bits(mpcs->regmap_pextp, 0x3050, GENMASK(31, 0),
+ 0xA8000000);
+ regmap_update_bits(mpcs->regmap_pextp, 0x3054, GENMASK(31, 0),
+ 0x000000AA);
+ regmap_update_bits(mpcs->regmap_pextp, 0x306C, GENMASK(31, 0),
+ 0x20200F00);
+ regmap_update_bits(mpcs->regmap_pextp, 0xA060, GENMASK(31, 0),
+ 0x00050000);
+ regmap_update_bits(mpcs->regmap_pextp, 0x90D0, GENMASK(31, 0),
+ 0x00000007);
+ regmap_update_bits(mpcs->regmap_pextp, 0x0070, GENMASK(31, 0),
+ 0x0200E800);
udelay(150);
- regmap_update_bits(ss->regmap_pextp[id], 0x0070, GENMASK(31, 0), 0x0200C111);
+ regmap_update_bits(mpcs->regmap_pextp, 0x0070, GENMASK(31, 0),
+ 0x0200C111);
ndelay(1020);
- regmap_update_bits(ss->regmap_pextp[id], 0x0070, GENMASK(31, 0), 0x0200C101);
+ regmap_update_bits(mpcs->regmap_pextp, 0x0070, GENMASK(31, 0),
+ 0x0200C101);
udelay(15);
- regmap_update_bits(ss->regmap_pextp[id], 0x0070, GENMASK(31, 0), 0x0201C111);
+ regmap_update_bits(mpcs->regmap_pextp, 0x0070, GENMASK(31, 0),
+ 0x0201C111);
ndelay(1020);
- regmap_update_bits(ss->regmap_pextp[id], 0x0070, GENMASK(31, 0), 0x0201C101);
+ regmap_update_bits(mpcs->regmap_pextp, 0x0070, GENMASK(31, 0),
+ 0x0201C101);
udelay(100);
- regmap_update_bits(ss->regmap_pextp[id], 0x30B0, GENMASK(31, 0), 0x00000030);
- regmap_update_bits(ss->regmap_pextp[id], 0x00F4, GENMASK(31, 0), 0x80201F01);
- regmap_update_bits(ss->regmap_pextp[id], 0x3040, GENMASK(31, 0), 0x30000000);
+ regmap_update_bits(mpcs->regmap_pextp, 0x30B0, GENMASK(31, 0),
+ 0x00000030);
+ regmap_update_bits(mpcs->regmap_pextp, 0x00F4, GENMASK(31, 0),
+ 0x80201F01);
+ regmap_update_bits(mpcs->regmap_pextp, 0x3040, GENMASK(31, 0),
+ 0x30000000);
udelay(400);
}
-void mtk_sgmii_setup_phya_gen2(struct mtk_xgmii *ss, int mac_id)
+void mtk_sgmii_setup_phya_gen2(struct mtk_sgmii_pcs *mpcs)
{
- u32 id = mtk_mac2xgmii_id(ss->eth, mac_id);
-
- if (id >= MTK_MAX_DEVS ||
- !ss->regmap_sgmii[id] || !ss->regmap_pextp[id])
+ if (!mpcs->regmap_pextp)
return;
- regmap_update_bits(ss->regmap_pextp[id], 0x9024, GENMASK(31, 0), 0x00D9071C);
- regmap_update_bits(ss->regmap_pextp[id], 0x2020, GENMASK(31, 0), 0xAA8585AA);
- regmap_update_bits(ss->regmap_pextp[id], 0x2030, GENMASK(31, 0), 0x0C020707);
- regmap_update_bits(ss->regmap_pextp[id], 0x2034, GENMASK(31, 0), 0x0E050F0F);
- regmap_update_bits(ss->regmap_pextp[id], 0x2040, GENMASK(31, 0), 0x00140032);
- regmap_update_bits(ss->regmap_pextp[id], 0x50F0, GENMASK(31, 0), 0x00C014AA);
- regmap_update_bits(ss->regmap_pextp[id], 0x50E0, GENMASK(31, 0), 0x3777C12B);
- regmap_update_bits(ss->regmap_pextp[id], 0x506C, GENMASK(31, 0), 0x005F9CFF);
- regmap_update_bits(ss->regmap_pextp[id], 0x5070, GENMASK(31, 0), 0x9D9DFAFA);
- regmap_update_bits(ss->regmap_pextp[id], 0x5074, GENMASK(31, 0), 0x27273F3F);
- regmap_update_bits(ss->regmap_pextp[id], 0x5078, GENMASK(31, 0), 0xA7883C68);
- regmap_update_bits(ss->regmap_pextp[id], 0x507C, GENMASK(31, 0), 0x11661166);
- regmap_update_bits(ss->regmap_pextp[id], 0x5080, GENMASK(31, 0), 0x0E000AAF);
- regmap_update_bits(ss->regmap_pextp[id], 0x5084, GENMASK(31, 0), 0x08080D0D);
- regmap_update_bits(ss->regmap_pextp[id], 0x5088, GENMASK(31, 0), 0x02030909);
- regmap_update_bits(ss->regmap_pextp[id], 0x50E4, GENMASK(31, 0), 0x0C0C0000);
- regmap_update_bits(ss->regmap_pextp[id], 0x50E8, GENMASK(31, 0), 0x04040000);
- regmap_update_bits(ss->regmap_pextp[id], 0x50EC, GENMASK(31, 0), 0x0F0F0C06);
- regmap_update_bits(ss->regmap_pextp[id], 0x50A8, GENMASK(31, 0), 0x506E8C8C);
- regmap_update_bits(ss->regmap_pextp[id], 0x6004, GENMASK(31, 0), 0x18190000);
- regmap_update_bits(ss->regmap_pextp[id], 0x00F8, GENMASK(31, 0), 0x009C329C);
- regmap_update_bits(ss->regmap_pextp[id], 0x00F4, GENMASK(31, 0), 0x80201F21);
- regmap_update_bits(ss->regmap_pextp[id], 0x0030, GENMASK(31, 0), 0x00050C00);
- regmap_update_bits(ss->regmap_pextp[id], 0x0070, GENMASK(31, 0), 0x02002800);
+ regmap_update_bits(mpcs->regmap_pextp, 0x9024, GENMASK(31, 0),
+ 0x00D9071C);
+ regmap_update_bits(mpcs->regmap_pextp, 0x2020, GENMASK(31, 0),
+ 0xAA8585AA);
+ regmap_update_bits(mpcs->regmap_pextp, 0x2030, GENMASK(31, 0),
+ 0x0C020707);
+ regmap_update_bits(mpcs->regmap_pextp, 0x2034, GENMASK(31, 0),
+ 0x0E050F0F);
+ regmap_update_bits(mpcs->regmap_pextp, 0x2040, GENMASK(31, 0),
+ 0x00140032);
+ regmap_update_bits(mpcs->regmap_pextp, 0x50F0, GENMASK(31, 0),
+ 0x00C014AA);
+ regmap_update_bits(mpcs->regmap_pextp, 0x50E0, GENMASK(31, 0),
+ 0x3777C12B);
+ regmap_update_bits(mpcs->regmap_pextp, 0x506C, GENMASK(31, 0),
+ 0x005F9CFF);
+ regmap_update_bits(mpcs->regmap_pextp, 0x5070, GENMASK(31, 0),
+ 0x9D9DFAFA);
+ regmap_update_bits(mpcs->regmap_pextp, 0x5074, GENMASK(31, 0),
+ 0x27273F3F);
+ regmap_update_bits(mpcs->regmap_pextp, 0x5078, GENMASK(31, 0),
+ 0xA7883C68);
+ regmap_update_bits(mpcs->regmap_pextp, 0x507C, GENMASK(31, 0),
+ 0x11661166);
+ regmap_update_bits(mpcs->regmap_pextp, 0x5080, GENMASK(31, 0),
+ 0x0E000AAF);
+ regmap_update_bits(mpcs->regmap_pextp, 0x5084, GENMASK(31, 0),
+ 0x08080D0D);
+ regmap_update_bits(mpcs->regmap_pextp, 0x5088, GENMASK(31, 0),
+ 0x02030909);
+ regmap_update_bits(mpcs->regmap_pextp, 0x50E4, GENMASK(31, 0),
+ 0x0C0C0000);
+ regmap_update_bits(mpcs->regmap_pextp, 0x50E8, GENMASK(31, 0),
+ 0x04040000);
+ regmap_update_bits(mpcs->regmap_pextp, 0x50EC, GENMASK(31, 0),
+ 0x0F0F0C06);
+ regmap_update_bits(mpcs->regmap_pextp, 0x50A8, GENMASK(31, 0),
+ 0x506E8C8C);
+ regmap_update_bits(mpcs->regmap_pextp, 0x6004, GENMASK(31, 0),
+ 0x18190000);
+ regmap_update_bits(mpcs->regmap_pextp, 0x00F8, GENMASK(31, 0),
+ 0x009C329C);
+ regmap_update_bits(mpcs->regmap_pextp, 0x00F4, GENMASK(31, 0),
+ 0x80201F21);
+ regmap_update_bits(mpcs->regmap_pextp, 0x0030, GENMASK(31, 0),
+ 0x00050C00);
+ regmap_update_bits(mpcs->regmap_pextp, 0x0070, GENMASK(31, 0),
+ 0x02002800);
ndelay(1020);
- regmap_update_bits(ss->regmap_pextp[id], 0x30B0, GENMASK(31, 0), 0x00000020);
- regmap_update_bits(ss->regmap_pextp[id], 0x3028, GENMASK(31, 0), 0x00008A01);
- regmap_update_bits(ss->regmap_pextp[id], 0x302C, GENMASK(31, 0), 0x0000A884);
- regmap_update_bits(ss->regmap_pextp[id], 0x3024, GENMASK(31, 0), 0x00083002);
- regmap_update_bits(ss->regmap_pextp[id], 0x3010, GENMASK(31, 0), 0x00011110);
- regmap_update_bits(ss->regmap_pextp[id], 0x3048, GENMASK(31, 0), 0x40704000);
- regmap_update_bits(ss->regmap_pextp[id], 0x3050, GENMASK(31, 0), 0xA8000000);
- regmap_update_bits(ss->regmap_pextp[id], 0x3054, GENMASK(31, 0), 0x000000AA);
- regmap_update_bits(ss->regmap_pextp[id], 0x306C, GENMASK(31, 0), 0x22000F00);
- regmap_update_bits(ss->regmap_pextp[id], 0xA060, GENMASK(31, 0), 0x00050000);
- regmap_update_bits(ss->regmap_pextp[id], 0x90D0, GENMASK(31, 0), 0x00000005);
- regmap_update_bits(ss->regmap_pextp[id], 0x0070, GENMASK(31, 0), 0x0200E800);
+ regmap_update_bits(mpcs->regmap_pextp, 0x30B0, GENMASK(31, 0),
+ 0x00000020);
+ regmap_update_bits(mpcs->regmap_pextp, 0x3028, GENMASK(31, 0),
+ 0x00008A01);
+ regmap_update_bits(mpcs->regmap_pextp, 0x302C, GENMASK(31, 0),
+ 0x0000A884);
+ regmap_update_bits(mpcs->regmap_pextp, 0x3024, GENMASK(31, 0),
+ 0x00083002);
+ regmap_update_bits(mpcs->regmap_pextp, 0x3010, GENMASK(31, 0),
+ 0x00011110);
+ regmap_update_bits(mpcs->regmap_pextp, 0x3048, GENMASK(31, 0),
+ 0x40704000);
+ regmap_update_bits(mpcs->regmap_pextp, 0x3050, GENMASK(31, 0),
+ 0xA8000000);
+ regmap_update_bits(mpcs->regmap_pextp, 0x3054, GENMASK(31, 0),
+ 0x000000AA);
+ regmap_update_bits(mpcs->regmap_pextp, 0x306C, GENMASK(31, 0),
+ 0x22000F00);
+ regmap_update_bits(mpcs->regmap_pextp, 0xA060, GENMASK(31, 0),
+ 0x00050000);
+ regmap_update_bits(mpcs->regmap_pextp, 0x90D0, GENMASK(31, 0),
+ 0x00000005);
+ regmap_update_bits(mpcs->regmap_pextp, 0x0070, GENMASK(31, 0),
+ 0x0200E800);
udelay(150);
- regmap_update_bits(ss->regmap_pextp[id], 0x0070, GENMASK(31, 0), 0x0200C111);
+ regmap_update_bits(mpcs->regmap_pextp, 0x0070, GENMASK(31, 0),
+ 0x0200C111);
ndelay(1020);
- regmap_update_bits(ss->regmap_pextp[id], 0x0070, GENMASK(31, 0), 0x0200C101);
+ regmap_update_bits(mpcs->regmap_pextp, 0x0070, GENMASK(31, 0),
+ 0x0200C101);
udelay(15);
- regmap_update_bits(ss->regmap_pextp[id], 0x0070, GENMASK(31, 0), 0x0201C111);
+ regmap_update_bits(mpcs->regmap_pextp, 0x0070, GENMASK(31, 0),
+ 0x0201C111);
ndelay(1020);
- regmap_update_bits(ss->regmap_pextp[id], 0x0070, GENMASK(31, 0), 0x0201C101);
+ regmap_update_bits(mpcs->regmap_pextp, 0x0070, GENMASK(31, 0),
+ 0x0201C101);
udelay(100);
- regmap_update_bits(ss->regmap_pextp[id], 0x30B0, GENMASK(31, 0), 0x00000030);
- regmap_update_bits(ss->regmap_pextp[id], 0x00F4, GENMASK(31, 0), 0x80201F01);
- regmap_update_bits(ss->regmap_pextp[id], 0x3040, GENMASK(31, 0), 0x30000000);
+ regmap_update_bits(mpcs->regmap_pextp, 0x30B0, GENMASK(31, 0),
+ 0x00000030);
+ regmap_update_bits(mpcs->regmap_pextp, 0x00F4, GENMASK(31, 0),
+ 0x80201F01);
+ regmap_update_bits(mpcs->regmap_pextp, 0x3040, GENMASK(31, 0),
+ 0x30000000);
udelay(400);
}
-int mtk_sgmii_setup_mode_an(struct mtk_xgmii *ss, unsigned int mac_id)
+int mtk_sgmii_setup_mode_an(struct mtk_sgmii_pcs *mpcs)
{
- struct mtk_eth *eth = ss->eth;
+ struct mtk_eth *eth = mpcs->eth;
unsigned int val = 0;
- u32 id = mtk_mac2xgmii_id(ss->eth, mac_id);
- if (!ss->regmap_sgmii[id])
+ if (!mpcs->regmap)
return -EINVAL;
if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V3)) {
- mtk_xfi_pll_enable(ss);
- mtk_sgmii_reset(ss, mac_id);
+ 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(ss, id, SPEED_1000))
- regmap_write(ss->regmap_sgmii[id], SGMSYS_QPHY_PWR_STATE_CTRL,
+ 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(ss->regmap_sgmii[id], SGMII_RESERVED_0, SGMII_SW_RESET);
+ regmap_write(mpcs->regmap, SGMII_RESERVED_0, SGMII_SW_RESET);
- regmap_read(ss->regmap_sgmii[id], ss->ana_rgc3, &val);
+ regmap_read(mpcs->regmap, mpcs->ana_rgc3, &val);
val &= ~RG_PHY_SPEED_3_125G;
- regmap_write(ss->regmap_sgmii[id], ss->ana_rgc3, val);
+ regmap_write(mpcs->regmap, mpcs->ana_rgc3, val);
/* Setup the link timer and QPHY power up inside SGMIISYS */
- regmap_write(ss->regmap_sgmii[id], SGMSYS_PCS_LINK_TIMER,
+ regmap_write(mpcs->regmap, SGMSYS_PCS_LINK_TIMER,
SGMII_LINK_TIMER_DEFAULT);
- regmap_read(ss->regmap_sgmii[id], SGMSYS_SGMII_MODE, &val);
+ regmap_read(mpcs->regmap, SGMSYS_SGMII_MODE, &val);
val |= SGMII_REMOTE_FAULT_DIS;
- regmap_write(ss->regmap_sgmii[id], SGMSYS_SGMII_MODE, val);
+ regmap_write(mpcs->regmap, SGMSYS_SGMII_MODE, val);
/* SGMII AN mode setting */
- regmap_read(ss->regmap_sgmii[id], SGMSYS_SGMII_MODE, &val);
+ regmap_read(mpcs->regmap, SGMSYS_SGMII_MODE, &val);
val &= ~SGMII_IF_MODE_MASK;
val |= SGMII_SPEED_DUPLEX_AN;
- regmap_write(ss->regmap_sgmii[id], SGMSYS_SGMII_MODE, val);
+ regmap_write(mpcs->regmap, SGMSYS_SGMII_MODE, val);
/* Enable SGMII AN */
- regmap_read(ss->regmap_sgmii[id], SGMSYS_PCS_CONTROL_1, &val);
+ regmap_read(mpcs->regmap, SGMSYS_PCS_CONTROL_1, &val);
val |= SGMII_AN_ENABLE;
- regmap_write(ss->regmap_sgmii[id], SGMSYS_PCS_CONTROL_1, val);
+ regmap_write(mpcs->regmap, SGMSYS_PCS_CONTROL_1, val);
- if(MTK_HAS_FLAGS(ss->flags[id],MTK_SGMII_PN_SWAP))
- regmap_update_bits(ss->regmap_sgmii[id], SGMSYS_QPHY_WRAP_CTRL,
+ 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);
/* Release PHYA power down state */
- regmap_write(ss->regmap_sgmii[id], SGMSYS_QPHY_PWR_STATE_CTRL, 0);
+ regmap_write(mpcs->regmap, SGMSYS_QPHY_PWR_STATE_CTRL, 0);
if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V3))
- mtk_sgmii_setup_phya_gen1(ss, mac_id);
+ mtk_sgmii_setup_phya_gen1(mpcs);
return 0;
}
-int mtk_sgmii_setup_mode_force(struct mtk_xgmii *ss, unsigned int mac_id,
- const struct phylink_link_state *state)
+int mtk_sgmii_setup_mode_force(struct mtk_sgmii_pcs *mpcs,
+ phy_interface_t interface)
{
- struct mtk_eth *eth = ss->eth;
+ struct mtk_eth *eth = mpcs->eth;
unsigned int val = 0;
- u32 id = mtk_mac2xgmii_id(eth, mac_id);
- if (!ss->regmap_sgmii[id])
+ if (!mpcs->regmap)
return -EINVAL;
if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V3)) {
- mtk_xfi_pll_enable(ss);
- mtk_sgmii_reset(ss, mac_id);
+ 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(ss, id, state->speed))
- regmap_write(ss->regmap_sgmii[id], SGMSYS_QPHY_PWR_STATE_CTRL,
+ if (mtk_sgmii_need_powerdown(mpcs, interface))
+ regmap_write(mpcs->regmap, SGMSYS_QPHY_PWR_STATE_CTRL,
SGMII_PHYA_PWD);
/* Reset SGMII PCS state */
- regmap_write(ss->regmap_sgmii[id], SGMII_RESERVED_0, SGMII_SW_RESET);
+ regmap_write(mpcs->regmap, SGMII_RESERVED_0, SGMII_SW_RESET);
- regmap_read(ss->regmap_sgmii[id], ss->ana_rgc3, &val);
+ regmap_read(mpcs->regmap, mpcs->ana_rgc3, &val);
val &= ~RG_PHY_SPEED_MASK;
- if (state->interface == PHY_INTERFACE_MODE_2500BASEX)
+ if (interface == PHY_INTERFACE_MODE_2500BASEX)
val |= RG_PHY_SPEED_3_125G;
- regmap_write(ss->regmap_sgmii[id], ss->ana_rgc3, val);
+ regmap_write(mpcs->regmap, mpcs->ana_rgc3, val);
/* Disable SGMII AN */
- regmap_read(ss->regmap_sgmii[id], SGMSYS_PCS_CONTROL_1, &val);
+ regmap_read(mpcs->regmap, SGMSYS_PCS_CONTROL_1, &val);
val &= ~SGMII_AN_ENABLE;
- regmap_write(ss->regmap_sgmii[id], SGMSYS_PCS_CONTROL_1, val);
+ regmap_write(mpcs->regmap, SGMSYS_PCS_CONTROL_1, val);
- /* SGMII force mode setting */
- regmap_read(ss->regmap_sgmii[id], SGMSYS_SGMII_MODE, &val);
- val &= ~SGMII_IF_MODE_MASK;
+ /* 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);
- switch (state->speed) {
- case SPEED_10:
- val |= SGMII_SPEED_10;
- break;
- case SPEED_100:
- val |= SGMII_SPEED_100;
- break;
- case SPEED_2500:
- case SPEED_1000:
- default:
- val |= SGMII_SPEED_1000;
- break;
- };
-
- /* SGMII 1G and 2.5G force mode can only work in full duplex
- * mode, no matter SGMII_FORCE_HALF_DUPLEX is set or not.
- */
- if (state->duplex != DUPLEX_FULL)
- val |= SGMII_DUPLEX_FULL;
-
- regmap_write(ss->regmap_sgmii[id], SGMSYS_SGMII_MODE, val);
-
- if(MTK_HAS_FLAGS(ss->flags[id],MTK_SGMII_PN_SWAP))
- regmap_update_bits(ss->regmap_sgmii[id], SGMSYS_QPHY_WRAP_CTRL,
+ 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);
/* Release PHYA power down state */
- regmap_write(ss->regmap_sgmii[id], SGMSYS_QPHY_PWR_STATE_CTRL, 0);
+ regmap_write(mpcs->regmap, SGMSYS_QPHY_PWR_STATE_CTRL, 0);
if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V3))
- mtk_sgmii_setup_phya_gen2(ss, mac_id);
+ mtk_sgmii_setup_phya_gen2(mpcs);
return 0;
}
+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;
+
+ state->interface = mpcs->interface;
+
+ 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);
+
+ 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;
+ }
+ }
+}
+
-void mtk_sgmii_restart_an(struct mtk_eth *eth, int mac_id)
+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_xgmii *ss = eth->xgmii;
- unsigned int val = 0, sid = mtk_mac2xgmii_id(eth, mac_id);
+ struct mtk_sgmii_pcs *mpcs = pcs_to_mtk_sgmii_pcs(pcs);
+ int err = 0;
+
+ mpcs->interface = interface;
- /* Decide how GMAC and SGMIISYS be mapped */
- sid = (MTK_HAS_CAPS(eth->soc->caps, MTK_SHARED_SGMII)) ?
- 0 : sid;
+ /* 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);
- if (!ss->regmap_sgmii[sid])
+ return err;
+}
+
+void mtk_sgmii_pcs_restart_an(struct phylink_pcs *pcs)
+{
+ struct mtk_sgmii_pcs *mpcs = pcs_to_mtk_sgmii_pcs(pcs);
+ unsigned int val = 0;
+
+ if (!mpcs->regmap)
return;
- regmap_read(ss->regmap_sgmii[sid], SGMSYS_PCS_CONTROL_1, &val);
+ regmap_read(mpcs->regmap, SGMSYS_PCS_CONTROL_1, &val);
val |= SGMII_AN_RESTART;
- regmap_write(ss->regmap_sgmii[sid], SGMSYS_PCS_CONTROL_1, val);
+ regmap_write(mpcs->regmap, SGMSYS_PCS_CONTROL_1, val);
+}
+
+static void mtk_sgmii_pcs_link_up(struct phylink_pcs *pcs, unsigned int mode,
+ phy_interface_t interface,
+ int speed, int duplex)
+{
+ struct mtk_sgmii_pcs *mpcs = pcs_to_mtk_sgmii_pcs(pcs);
+ unsigned int val;
+
+ if (!phy_interface_mode_is_8023z(interface))
+ return;
+
+ /* SGMII force duplex setting */
+ regmap_read(mpcs->regmap, SGMSYS_SGMII_MODE, &val);
+ val &= ~SGMII_DUPLEX_HALF;
+ if (duplex != DUPLEX_FULL)
+ val |= SGMII_DUPLEX_HALF;
+
+ regmap_write(mpcs->regmap, SGMSYS_SGMII_MODE, val);
+}
+
+static const struct phylink_pcs_ops mtk_sgmii_pcs_ops = {
+ .pcs_config = mtk_sgmii_pcs_config,
+ .pcs_get_state = mtk_sgmii_pcs_get_state,
+ .pcs_an_restart = mtk_sgmii_pcs_restart_an,
+ .pcs_link_up = mtk_sgmii_pcs_link_up,
+};
+
+int mtk_sgmii_init(struct mtk_eth *eth, struct device_node *r, u32 ana_rgc3)
+{
+ struct mtk_sgmii *ss = eth->sgmii;
+ struct device_node *np;
+ int ret, i;
+
+ for (i = 0; i < MTK_MAX_DEVS; i++) {
+ np = of_parse_phandle(r, "mediatek,sgmiisys", i);
+ if (!np)
+ break;
+
+ ss->pcs[i].id = i;
+ ss->pcs[i].eth = eth;
+ ss->pcs[i].ana_rgc3 = ana_rgc3;
+
+ ss->pcs[i].regmap = syscon_node_to_regmap(np);
+ if (IS_ERR(ss->pcs[i].regmap))
+ return PTR_ERR(ss->pcs[i].regmap);
+
+ ss->pcs[i].flags &= ~(MTK_SGMII_PN_SWAP);
+ if (of_property_read_bool(np, "pn_swap"))
+ ss->pcs[i].flags |= MTK_SGMII_PN_SWAP;
+
+ ss->pcs[i].pcs.ops = &mtk_sgmii_pcs_ops;
+ ss->pcs[i].pcs.poll = true;
+ ss->pcs[i].interface = PHY_INTERFACE_MODE_NA;
+
+ of_node_put(np);
+ }
+
+ if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V3)) {
+ ret = mtk_sgmii_xfi_pextp_init(ss, r);
+ if (ret)
+ return ret;
+
+ ret = mtk_sgmii_xfi_pll_init(ss, r);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+struct phylink_pcs *mtk_sgmii_select_pcs(struct mtk_sgmii *ss, int id)
+{
+ if (!ss->pcs[id].regmap)
+ return NULL;
+
+ return &ss->pcs[id].pcs;
}
diff --git a/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_usxgmii.c b/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_usxgmii.c
index 8fb28ca..73f84f6 100644
--- a/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_usxgmii.c
+++ b/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_usxgmii.c
@@ -9,25 +9,12 @@
#include <linux/regmap.h>
#include "mtk_eth_soc.h"
-int mtk_usxgmii_init(struct mtk_xgmii *ss, struct device_node *r)
+static struct mtk_usxgmii_pcs *pcs_to_mtk_usxgmii_pcs(struct phylink_pcs *pcs)
{
- struct device_node *np;
- int i;
-
- for (i = 0; i < MTK_MAX_DEVS; i++) {
- np = of_parse_phandle(r, "mediatek,usxgmiisys", i);
- if (!np)
- break;
-
- ss->regmap_usxgmii[i] = syscon_node_to_regmap(np);
- if (IS_ERR(ss->regmap_usxgmii[i]))
- return PTR_ERR(ss->regmap_usxgmii[i]);
- }
-
- return 0;
+ return container_of(pcs, struct mtk_usxgmii_pcs, pcs);
}
-int mtk_xfi_pextp_init(struct mtk_xgmii *ss, struct device_node *r)
+int mtk_usxgmii_xfi_pextp_init(struct mtk_usxgmii *ss, struct device_node *r)
{
struct device_node *np;
int i;
@@ -37,25 +24,32 @@
if (!np)
break;
- ss->regmap_pextp[i] = syscon_node_to_regmap(np);
- if (IS_ERR(ss->regmap_pextp[i]))
- return PTR_ERR(ss->regmap_pextp[i]);
+ ss->pcs[i].regmap_pextp = syscon_node_to_regmap(np);
+ if (IS_ERR(ss->pcs[i].regmap_pextp))
+ return PTR_ERR(ss->pcs[i].regmap_pextp);
+
+ of_node_put(np);
}
return 0;
}
-int mtk_xfi_pll_init(struct mtk_xgmii *ss, struct device_node *r)
+int mtk_usxgmii_xfi_pll_init(struct mtk_usxgmii *ss, struct device_node *r)
{
struct device_node *np;
+ int i;
np = of_parse_phandle(r, "mediatek,xfi_pll", 0);
if (!np)
return -1;
+ for (i = 0; i < MTK_MAX_DEVS; i++) {
+ ss->pll = syscon_node_to_regmap(np);
+ if (IS_ERR(ss->pll))
+ return PTR_ERR(ss->pll);
+ }
+
- ss->regmap_pll = syscon_node_to_regmap(np);
- if (IS_ERR(ss->regmap_pll))
- return PTR_ERR(ss->regmap_pll);
+ of_node_put(np);
return 0;
}
@@ -75,19 +69,19 @@
return 0;
}
-int mtk_xfi_pll_enable(struct mtk_xgmii *ss)
+static int mtk_usxgmii_xfi_pll_enable(struct mtk_usxgmii *ss)
{
u32 val = 0;
- if (!ss->regmap_pll)
+ if (!ss->pll)
return -EINVAL;
/* Add software workaround for USXGMII PLL TCL issue */
- regmap_write(ss->regmap_pll, XFI_PLL_ANA_GLB8, RG_XFI_PLL_ANA_SWWA);
+ regmap_write(ss->pll, XFI_PLL_ANA_GLB8, RG_XFI_PLL_ANA_SWWA);
- regmap_read(ss->regmap_pll, XFI_PLL_DIG_GLB8, &val);
+ regmap_read(ss->pll, XFI_PLL_DIG_GLB8, &val);
val |= RG_XFI_PLL_EN;
- regmap_write(ss->regmap_pll, XFI_PLL_DIG_GLB8, val);
+ regmap_write(ss->pll, XFI_PLL_DIG_GLB8, val);
return 0;
}
@@ -114,376 +108,444 @@
return xgmii_id;
}
-void mtk_usxgmii_setup_phya_an_10000(struct mtk_xgmii *ss, int mac_id)
+int mtk_xgmii2mac_id(struct mtk_eth *eth, int xgmii_id)
{
- u32 id = mtk_mac2xgmii_id(ss->eth, mac_id);
+ u32 mac_id = xgmii_id;
- if (id >= MTK_MAX_DEVS ||
- !ss->regmap_usxgmii[id] || !ss->regmap_pextp[id])
- return;
+ if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V3)) {
+ switch (xgmii_id) {
+ case 0:
+ mac_id = 2;
+ break;
+ case 1:
+ mac_id = 1;
+ break;
+ default:
+ pr_info("[%s] Warning: get illegal xgmii_id=%d !=!!!\n",
+ __func__, xgmii_id);
+ }
+ }
+
+ return mac_id;
+}
+
+int mtk_usxgmii_setup_phya_an_10000(struct mtk_usxgmii_pcs *mpcs)
+{
+ if (!mpcs->regmap || !mpcs->regmap_pextp)
+ return -EINVAL;
- regmap_update_bits(ss->regmap_usxgmii[id], 0x810, GENMASK(31, 0), 0x000FFE6D);
- regmap_update_bits(ss->regmap_usxgmii[id], 0x818, GENMASK(31, 0), 0x07B1EC7B);
- regmap_update_bits(ss->regmap_usxgmii[id], 0x80C, GENMASK(31, 0), 0x30000000);
+ regmap_update_bits(mpcs->regmap, 0x810, GENMASK(31, 0),
+ 0x000FFE6D);
+ regmap_update_bits(mpcs->regmap, 0x818, GENMASK(31, 0),
+ 0x07B1EC7B);
+ regmap_update_bits(mpcs->regmap, 0x80C, GENMASK(31, 0),
+ 0x30000000);
ndelay(1020);
- regmap_update_bits(ss->regmap_usxgmii[id], 0x80C, GENMASK(31, 0), 0x10000000);
+ regmap_update_bits(mpcs->regmap, 0x80C, GENMASK(31, 0),
+ 0x10000000);
ndelay(1020);
- regmap_update_bits(ss->regmap_usxgmii[id], 0x80C, GENMASK(31, 0), 0x00000000);
+ regmap_update_bits(mpcs->regmap, 0x80C, GENMASK(31, 0),
+ 0x00000000);
- regmap_update_bits(ss->regmap_pextp[id], 0x9024, GENMASK(31, 0), 0x00C9071C);
- regmap_update_bits(ss->regmap_pextp[id], 0x2020, GENMASK(31, 0), 0xAA8585AA);
- regmap_update_bits(ss->regmap_pextp[id], 0x2030, GENMASK(31, 0), 0x0C020707);
- regmap_update_bits(ss->regmap_pextp[id], 0x2034, GENMASK(31, 0), 0x0E050F0F);
- regmap_update_bits(ss->regmap_pextp[id], 0x2040, GENMASK(31, 0), 0x00140032);
- regmap_update_bits(ss->regmap_pextp[id], 0x50F0, GENMASK(31, 0), 0x00C014AA);
- regmap_update_bits(ss->regmap_pextp[id], 0x50E0, GENMASK(31, 0), 0x3777C12B);
- regmap_update_bits(ss->regmap_pextp[id], 0x506C, GENMASK(31, 0), 0x005F9CFF);
- regmap_update_bits(ss->regmap_pextp[id], 0x5070, GENMASK(31, 0), 0x9D9DFAFA);
- regmap_update_bits(ss->regmap_pextp[id], 0x5074, GENMASK(31, 0), 0x27273F3F);
- regmap_update_bits(ss->regmap_pextp[id], 0x5078, GENMASK(31, 0), 0xA7883C68);
- regmap_update_bits(ss->regmap_pextp[id], 0x507C, GENMASK(31, 0), 0x11661166);
- regmap_update_bits(ss->regmap_pextp[id], 0x5080, GENMASK(31, 0), 0x0E000AAF);
- regmap_update_bits(ss->regmap_pextp[id], 0x5084, GENMASK(31, 0), 0x08080D0D);
- regmap_update_bits(ss->regmap_pextp[id], 0x5088, GENMASK(31, 0), 0x02030909);
- regmap_update_bits(ss->regmap_pextp[id], 0x50E4, GENMASK(31, 0), 0x0C0C0000);
- regmap_update_bits(ss->regmap_pextp[id], 0x50E8, GENMASK(31, 0), 0x04040000);
- regmap_update_bits(ss->regmap_pextp[id], 0x50EC, GENMASK(31, 0), 0x0F0F0C06);
- regmap_update_bits(ss->regmap_pextp[id], 0x50A8, GENMASK(31, 0), 0x506E8C8C);
- regmap_update_bits(ss->regmap_pextp[id], 0x6004, GENMASK(31, 0), 0x18190000);
- regmap_update_bits(ss->regmap_pextp[id], 0x00F8, GENMASK(31, 0), 0x01423342);
- regmap_update_bits(ss->regmap_pextp[id], 0x00F4, GENMASK(31, 0), 0x80201F20);
- regmap_update_bits(ss->regmap_pextp[id], 0x0030, GENMASK(31, 0), 0x00050C00);
- regmap_update_bits(ss->regmap_pextp[id], 0x0070, GENMASK(31, 0), 0x02002800);
+ regmap_update_bits(mpcs->regmap_pextp, 0x9024, GENMASK(31, 0),
+ 0x00C9071C);
+ regmap_update_bits(mpcs->regmap_pextp, 0x2020, GENMASK(31, 0),
+ 0xAA8585AA);
+ regmap_update_bits(mpcs->regmap_pextp, 0x2030, GENMASK(31, 0),
+ 0x0C020707);
+ regmap_update_bits(mpcs->regmap_pextp, 0x2034, GENMASK(31, 0),
+ 0x0E050F0F);
+ regmap_update_bits(mpcs->regmap_pextp, 0x2040, GENMASK(31, 0),
+ 0x00140032);
+ regmap_update_bits(mpcs->regmap_pextp, 0x50F0, GENMASK(31, 0),
+ 0x00C014AA);
+ regmap_update_bits(mpcs->regmap_pextp, 0x50E0, GENMASK(31, 0),
+ 0x3777C12B);
+ regmap_update_bits(mpcs->regmap_pextp, 0x506C, GENMASK(31, 0),
+ 0x005F9CFF);
+ regmap_update_bits(mpcs->regmap_pextp, 0x5070, GENMASK(31, 0),
+ 0x9D9DFAFA);
+ regmap_update_bits(mpcs->regmap_pextp, 0x5074, GENMASK(31, 0),
+ 0x27273F3F);
+ regmap_update_bits(mpcs->regmap_pextp, 0x5078, GENMASK(31, 0),
+ 0xA7883C68);
+ regmap_update_bits(mpcs->regmap_pextp, 0x507C, GENMASK(31, 0),
+ 0x11661166);
+ regmap_update_bits(mpcs->regmap_pextp, 0x5080, GENMASK(31, 0),
+ 0x0E000AAF);
+ regmap_update_bits(mpcs->regmap_pextp, 0x5084, GENMASK(31, 0),
+ 0x08080D0D);
+ regmap_update_bits(mpcs->regmap_pextp, 0x5088, GENMASK(31, 0),
+ 0x02030909);
+ regmap_update_bits(mpcs->regmap_pextp, 0x50E4, GENMASK(31, 0),
+ 0x0C0C0000);
+ regmap_update_bits(mpcs->regmap_pextp, 0x50E8, GENMASK(31, 0),
+ 0x04040000);
+ regmap_update_bits(mpcs->regmap_pextp, 0x50EC, GENMASK(31, 0),
+ 0x0F0F0C06);
+ regmap_update_bits(mpcs->regmap_pextp, 0x50A8, GENMASK(31, 0),
+ 0x506E8C8C);
+ regmap_update_bits(mpcs->regmap_pextp, 0x6004, GENMASK(31, 0),
+ 0x18190000);
+ regmap_update_bits(mpcs->regmap_pextp, 0x00F8, GENMASK(31, 0),
+ 0x01423342);
+ regmap_update_bits(mpcs->regmap_pextp, 0x00F4, GENMASK(31, 0),
+ 0x80201F20);
+ regmap_update_bits(mpcs->regmap_pextp, 0x0030, GENMASK(31, 0),
+ 0x00050C00);
+ regmap_update_bits(mpcs->regmap_pextp, 0x0070, GENMASK(31, 0),
+ 0x02002800);
ndelay(1020);
- regmap_update_bits(ss->regmap_pextp[id], 0x30B0, GENMASK(31, 0), 0x00000020);
- regmap_update_bits(ss->regmap_pextp[id], 0x3028, GENMASK(31, 0), 0x00008A01);
- regmap_update_bits(ss->regmap_pextp[id], 0x302C, GENMASK(31, 0), 0x0000A884);
- regmap_update_bits(ss->regmap_pextp[id], 0x3024, GENMASK(31, 0), 0x00083002);
- regmap_update_bits(ss->regmap_pextp[id], 0x3010, GENMASK(31, 0), 0x00022220);
- regmap_update_bits(ss->regmap_pextp[id], 0x5064, GENMASK(31, 0), 0x0F020A01);
- regmap_update_bits(ss->regmap_pextp[id], 0x50B4, GENMASK(31, 0), 0x06100600);
- regmap_update_bits(ss->regmap_pextp[id], 0x3048, GENMASK(31, 0), 0x40704000);
- regmap_update_bits(ss->regmap_pextp[id], 0x3050, GENMASK(31, 0), 0xA8000000);
- regmap_update_bits(ss->regmap_pextp[id], 0x3054, GENMASK(31, 0), 0x000000AA);
- regmap_update_bits(ss->regmap_pextp[id], 0x306C, GENMASK(31, 0), 0x00000F00);
- regmap_update_bits(ss->regmap_pextp[id], 0xA060, GENMASK(31, 0), 0x00040000);
- regmap_update_bits(ss->regmap_pextp[id], 0x90D0, GENMASK(31, 0), 0x00000001);
- regmap_update_bits(ss->regmap_pextp[id], 0x0070, GENMASK(31, 0), 0x0200E800);
+ regmap_update_bits(mpcs->regmap_pextp, 0x30B0, GENMASK(31, 0),
+ 0x00000020);
+ regmap_update_bits(mpcs->regmap_pextp, 0x3028, GENMASK(31, 0),
+ 0x00008A01);
+ regmap_update_bits(mpcs->regmap_pextp, 0x302C, GENMASK(31, 0),
+ 0x0000A884);
+ regmap_update_bits(mpcs->regmap_pextp, 0x3024, GENMASK(31, 0),
+ 0x00083002);
+ regmap_update_bits(mpcs->regmap_pextp, 0x3010, GENMASK(31, 0),
+ 0x00022220);
+ regmap_update_bits(mpcs->regmap_pextp, 0x5064, GENMASK(31, 0),
+ 0x0F020A01);
+ regmap_update_bits(mpcs->regmap_pextp, 0x50B4, GENMASK(31, 0),
+ 0x06100600);
+ regmap_update_bits(mpcs->regmap_pextp, 0x3048, GENMASK(31, 0),
+ 0x40704000);
+ regmap_update_bits(mpcs->regmap_pextp, 0x3050, GENMASK(31, 0),
+ 0xA8000000);
+ regmap_update_bits(mpcs->regmap_pextp, 0x3054, GENMASK(31, 0),
+ 0x000000AA);
+ regmap_update_bits(mpcs->regmap_pextp, 0x306C, GENMASK(31, 0),
+ 0x00000F00);
+ regmap_update_bits(mpcs->regmap_pextp, 0xA060, GENMASK(31, 0),
+ 0x00040000);
+ regmap_update_bits(mpcs->regmap_pextp, 0x90D0, GENMASK(31, 0),
+ 0x00000001);
+ regmap_update_bits(mpcs->regmap_pextp, 0x0070, GENMASK(31, 0),
+ 0x0200E800);
udelay(150);
- regmap_update_bits(ss->regmap_pextp[id], 0x0070, GENMASK(31, 0), 0x0200C111);
+ regmap_update_bits(mpcs->regmap_pextp, 0x0070, GENMASK(31, 0),
+ 0x0200C111);
ndelay(1020);
- regmap_update_bits(ss->regmap_pextp[id], 0x0070, GENMASK(31, 0), 0x0200C101);
+ regmap_update_bits(mpcs->regmap_pextp, 0x0070, GENMASK(31, 0),
+ 0x0200C101);
udelay(15);
- regmap_update_bits(ss->regmap_pextp[id], 0x0070, GENMASK(31, 0), 0x0202C111);
+ regmap_update_bits(mpcs->regmap_pextp, 0x0070, GENMASK(31, 0),
+ 0x0202C111);
ndelay(1020);
- regmap_update_bits(ss->regmap_pextp[id], 0x0070, GENMASK(31, 0), 0x0202C101);
+ regmap_update_bits(mpcs->regmap_pextp, 0x0070, GENMASK(31, 0),
+ 0x0202C101);
udelay(100);
- regmap_update_bits(ss->regmap_pextp[id], 0x30B0, GENMASK(31, 0), 0x00000030);
- regmap_update_bits(ss->regmap_pextp[id], 0x00F4, GENMASK(31, 0), 0x80201F00);
- regmap_update_bits(ss->regmap_pextp[id], 0x3040, GENMASK(31, 0), 0x30000000);
+ regmap_update_bits(mpcs->regmap_pextp, 0x30B0, GENMASK(31, 0),
+ 0x00000030);
+ regmap_update_bits(mpcs->regmap_pextp, 0x00F4, GENMASK(31, 0),
+ 0x80201F00);
+ regmap_update_bits(mpcs->regmap_pextp, 0x3040, GENMASK(31, 0),
+ 0x30000000);
udelay(400);
+
+ return 0;
}
-void mtk_usxgmii_setup_phya_force_5000(struct mtk_xgmii *ss, int mac_id)
+int mtk_usxgmii_setup_phya_force_5000(struct mtk_usxgmii_pcs *mpcs)
{
unsigned int val;
- u32 id = mtk_mac2xgmii_id(ss->eth, mac_id);
- if (id >= MTK_MAX_DEVS ||
- !ss->regmap_usxgmii[id] || !ss->regmap_pextp[id])
- return;
+ if (!mpcs->regmap || !mpcs->regmap_pextp)
+ return -EINVAL;
/* Setup USXGMII speed */
val = FIELD_PREP(RG_XFI_RX_MODE, RG_XFI_RX_MODE_5G) |
FIELD_PREP(RG_XFI_TX_MODE, RG_XFI_TX_MODE_5G);
- regmap_write(ss->regmap_usxgmii[id], RG_PHY_TOP_SPEED_CTRL1, val);
+ regmap_write(mpcs->regmap, RG_PHY_TOP_SPEED_CTRL1, val);
/* Disable USXGMII AN mode */
- regmap_read(ss->regmap_usxgmii[id], RG_PCS_AN_CTRL0, &val);
- val &= ~RG_AN_ENABLE;
- regmap_write(ss->regmap_usxgmii[id], RG_PCS_AN_CTRL0, val);
+ regmap_read(mpcs->regmap, RG_PCS_AN_CTRL0, &val);
+ val &= ~USXGMII_AN_ENABLE;
+ regmap_write(mpcs->regmap, RG_PCS_AN_CTRL0, val);
/* Gated USXGMII */
- regmap_read(ss->regmap_usxgmii[id], RG_PHY_TOP_SPEED_CTRL1, &val);
+ regmap_read(mpcs->regmap, RG_PHY_TOP_SPEED_CTRL1, &val);
val |= RG_MAC_CK_GATED;
- regmap_write(ss->regmap_usxgmii[id], RG_PHY_TOP_SPEED_CTRL1, val);
+ regmap_write(mpcs->regmap, RG_PHY_TOP_SPEED_CTRL1, val);
ndelay(1020);
/* USXGMII force mode setting */
- regmap_read(ss->regmap_usxgmii[id], RG_PHY_TOP_SPEED_CTRL1, &val);
+ regmap_read(mpcs->regmap, RG_PHY_TOP_SPEED_CTRL1, &val);
val |= RG_USXGMII_RATE_UPDATE_MODE;
val |= RG_IF_FORCE_EN;
val |= FIELD_PREP(RG_RATE_ADAPT_MODE, RG_RATE_ADAPT_MODE_X1);
- regmap_write(ss->regmap_usxgmii[id], RG_PHY_TOP_SPEED_CTRL1, val);
+ regmap_write(mpcs->regmap, RG_PHY_TOP_SPEED_CTRL1, val);
/* Un-gated USXGMII */
- regmap_read(ss->regmap_usxgmii[id], RG_PHY_TOP_SPEED_CTRL1, &val);
+ regmap_read(mpcs->regmap, RG_PHY_TOP_SPEED_CTRL1, &val);
val &= ~RG_MAC_CK_GATED;
- regmap_write(ss->regmap_usxgmii[id], RG_PHY_TOP_SPEED_CTRL1, val);
+ regmap_write(mpcs->regmap, RG_PHY_TOP_SPEED_CTRL1, val);
ndelay(1020);
- regmap_update_bits(ss->regmap_pextp[id], 0x9024, GENMASK(31, 0),
+ regmap_update_bits(mpcs->regmap_pextp, 0x9024, GENMASK(31, 0),
0x00D9071C);
- regmap_update_bits(ss->regmap_pextp[id], 0x2020, GENMASK(31, 0),
+ regmap_update_bits(mpcs->regmap_pextp, 0x2020, GENMASK(31, 0),
0xAAA5A5AA);
- regmap_update_bits(ss->regmap_pextp[id], 0x2030, GENMASK(31, 0),
+ regmap_update_bits(mpcs->regmap_pextp, 0x2030, GENMASK(31, 0),
0x0C020707);
- regmap_update_bits(ss->regmap_pextp[id], 0x2034, GENMASK(31, 0),
+ regmap_update_bits(mpcs->regmap_pextp, 0x2034, GENMASK(31, 0),
0x0E050F0F);
- regmap_update_bits(ss->regmap_pextp[id], 0x2040, GENMASK(31, 0),
+ regmap_update_bits(mpcs->regmap_pextp, 0x2040, GENMASK(31, 0),
0x00140032);
- regmap_update_bits(ss->regmap_pextp[id], 0x50F0, GENMASK(31, 0),
+ regmap_update_bits(mpcs->regmap_pextp, 0x50F0, GENMASK(31, 0),
0x00C018AA);
- regmap_update_bits(ss->regmap_pextp[id], 0x50E0, GENMASK(31, 0),
+ regmap_update_bits(mpcs->regmap_pextp, 0x50E0, GENMASK(31, 0),
0x3777812B);
- regmap_update_bits(ss->regmap_pextp[id], 0x506C, GENMASK(31, 0),
+ regmap_update_bits(mpcs->regmap_pextp, 0x506C, GENMASK(31, 0),
0x005C9CFF);
- regmap_update_bits(ss->regmap_pextp[id], 0x5070, GENMASK(31, 0),
+ regmap_update_bits(mpcs->regmap_pextp, 0x5070, GENMASK(31, 0),
0x9DFAFAFA);
- regmap_update_bits(ss->regmap_pextp[id], 0x5074, GENMASK(31, 0),
+ regmap_update_bits(mpcs->regmap_pextp, 0x5074, GENMASK(31, 0),
0x273F3F3F);
- regmap_update_bits(ss->regmap_pextp[id], 0x5078, GENMASK(31, 0),
+ regmap_update_bits(mpcs->regmap_pextp, 0x5078, GENMASK(31, 0),
0xA8883868);
- regmap_update_bits(ss->regmap_pextp[id], 0x507C, GENMASK(31, 0),
+ regmap_update_bits(mpcs->regmap_pextp, 0x507C, GENMASK(31, 0),
0x14661466);
- regmap_update_bits(ss->regmap_pextp[id], 0x5080, GENMASK(31, 0),
+ regmap_update_bits(mpcs->regmap_pextp, 0x5080, GENMASK(31, 0),
0x0E001ABF);
- regmap_update_bits(ss->regmap_pextp[id], 0x5084, GENMASK(31, 0),
+ regmap_update_bits(mpcs->regmap_pextp, 0x5084, GENMASK(31, 0),
0x080B0D0D);
- regmap_update_bits(ss->regmap_pextp[id], 0x5088, GENMASK(31, 0),
+ regmap_update_bits(mpcs->regmap_pextp, 0x5088, GENMASK(31, 0),
0x02050909);
- regmap_update_bits(ss->regmap_pextp[id], 0x50E4, GENMASK(31, 0),
+ regmap_update_bits(mpcs->regmap_pextp, 0x50E4, GENMASK(31, 0),
0x0C000000);
- regmap_update_bits(ss->regmap_pextp[id], 0x50E8, GENMASK(31, 0),
+ regmap_update_bits(mpcs->regmap_pextp, 0x50E8, GENMASK(31, 0),
0x04000000);
- regmap_update_bits(ss->regmap_pextp[id], 0x50EC, GENMASK(31, 0),
+ regmap_update_bits(mpcs->regmap_pextp, 0x50EC, GENMASK(31, 0),
0x0F0F0C06);
- regmap_update_bits(ss->regmap_pextp[id], 0x50A8, GENMASK(31, 0),
+ regmap_update_bits(mpcs->regmap_pextp, 0x50A8, GENMASK(31, 0),
0x50808C8C);
- regmap_update_bits(ss->regmap_pextp[id], 0x6004, GENMASK(31, 0),
+ regmap_update_bits(mpcs->regmap_pextp, 0x6004, GENMASK(31, 0),
0x18000000);
- regmap_update_bits(ss->regmap_pextp[id], 0x00F8, GENMASK(31, 0),
+ regmap_update_bits(mpcs->regmap_pextp, 0x00F8, GENMASK(31, 0),
0x00A132A1);
- regmap_update_bits(ss->regmap_pextp[id], 0x00F4, GENMASK(31, 0),
+ regmap_update_bits(mpcs->regmap_pextp, 0x00F4, GENMASK(31, 0),
0x80201F20);
- regmap_update_bits(ss->regmap_pextp[id], 0x0030, GENMASK(31, 0),
+ regmap_update_bits(mpcs->regmap_pextp, 0x0030, GENMASK(31, 0),
0x00050C00);
- regmap_update_bits(ss->regmap_pextp[id], 0x0070, GENMASK(31, 0),
+ regmap_update_bits(mpcs->regmap_pextp, 0x0070, GENMASK(31, 0),
0x02002800);
ndelay(1020);
- regmap_update_bits(ss->regmap_pextp[id], 0x30B0, GENMASK(31, 0),
+ regmap_update_bits(mpcs->regmap_pextp, 0x30B0, GENMASK(31, 0),
0x00000020);
- regmap_update_bits(ss->regmap_pextp[id], 0x3028, GENMASK(31, 0),
+ regmap_update_bits(mpcs->regmap_pextp, 0x3028, GENMASK(31, 0),
0x00008A01);
- regmap_update_bits(ss->regmap_pextp[id], 0x302C, GENMASK(31, 0),
+ regmap_update_bits(mpcs->regmap_pextp, 0x302C, GENMASK(31, 0),
0x0000A884);
- regmap_update_bits(ss->regmap_pextp[id], 0x3024, GENMASK(31, 0),
+ regmap_update_bits(mpcs->regmap_pextp, 0x3024, GENMASK(31, 0),
0x00083002);
- regmap_update_bits(ss->regmap_pextp[id], 0x3010, GENMASK(31, 0),
+ regmap_update_bits(mpcs->regmap_pextp, 0x3010, GENMASK(31, 0),
0x00022220);
- regmap_update_bits(ss->regmap_pextp[id], 0x5064, GENMASK(31, 0),
+ regmap_update_bits(mpcs->regmap_pextp, 0x5064, GENMASK(31, 0),
0x0F020A01);
- regmap_update_bits(ss->regmap_pextp[id], 0x50B4, GENMASK(31, 0),
+ regmap_update_bits(mpcs->regmap_pextp, 0x50B4, GENMASK(31, 0),
0x06100600);
- regmap_update_bits(ss->regmap_pextp[id], 0x3048, GENMASK(31, 0),
+ regmap_update_bits(mpcs->regmap_pextp, 0x3048, GENMASK(31, 0),
0x40704000);
- regmap_update_bits(ss->regmap_pextp[id], 0x3050, GENMASK(31, 0),
+ regmap_update_bits(mpcs->regmap_pextp, 0x3050, GENMASK(31, 0),
0xA8000000);
- regmap_update_bits(ss->regmap_pextp[id], 0x3054, GENMASK(31, 0),
+ regmap_update_bits(mpcs->regmap_pextp, 0x3054, GENMASK(31, 0),
0x000000AA);
- regmap_update_bits(ss->regmap_pextp[id], 0x306C, GENMASK(31, 0),
+ regmap_update_bits(mpcs->regmap_pextp, 0x306C, GENMASK(31, 0),
0x00000F00);
- regmap_update_bits(ss->regmap_pextp[id], 0xA060, GENMASK(31, 0),
+ regmap_update_bits(mpcs->regmap_pextp, 0xA060, GENMASK(31, 0),
0x00040000);
- regmap_update_bits(ss->regmap_pextp[id], 0x90D0, GENMASK(31, 0),
+ regmap_update_bits(mpcs->regmap_pextp, 0x90D0, GENMASK(31, 0),
0x00000003);
- regmap_update_bits(ss->regmap_pextp[id], 0x0070, GENMASK(31, 0),
+ regmap_update_bits(mpcs->regmap_pextp, 0x0070, GENMASK(31, 0),
0x0200E800);
udelay(150);
- regmap_update_bits(ss->regmap_pextp[id], 0x0070, GENMASK(31, 0),
+ regmap_update_bits(mpcs->regmap_pextp, 0x0070, GENMASK(31, 0),
0x0200C111);
ndelay(1020);
- regmap_update_bits(ss->regmap_pextp[id], 0x0070, GENMASK(31, 0),
+ regmap_update_bits(mpcs->regmap_pextp, 0x0070, GENMASK(31, 0),
0x0200C101);
udelay(15);
- regmap_update_bits(ss->regmap_pextp[id], 0x0070, GENMASK(31, 0),
+ regmap_update_bits(mpcs->regmap_pextp, 0x0070, GENMASK(31, 0),
0x0202C111);
ndelay(1020);
- regmap_update_bits(ss->regmap_pextp[id], 0x0070, GENMASK(31, 0),
+ regmap_update_bits(mpcs->regmap_pextp, 0x0070, GENMASK(31, 0),
0x0202C101);
udelay(100);
- regmap_update_bits(ss->regmap_pextp[id], 0x30B0, GENMASK(31, 0),
+ regmap_update_bits(mpcs->regmap_pextp, 0x30B0, GENMASK(31, 0),
0x00000030);
- regmap_update_bits(ss->regmap_pextp[id], 0x00F4, GENMASK(31, 0),
+ regmap_update_bits(mpcs->regmap_pextp, 0x00F4, GENMASK(31, 0),
0x80201F00);
- regmap_update_bits(ss->regmap_pextp[id], 0x3040, GENMASK(31, 0),
+ regmap_update_bits(mpcs->regmap_pextp, 0x3040, GENMASK(31, 0),
0x30000000);
udelay(400);
+
+ return 0;
}
-void mtk_usxgmii_setup_phya_force_10000(struct mtk_xgmii *ss, int mac_id)
+int mtk_usxgmii_setup_phya_force_10000(struct mtk_usxgmii_pcs *mpcs)
{
unsigned int val;
- u32 id = mtk_mac2xgmii_id(ss->eth, mac_id);
- if (id >= MTK_MAX_DEVS ||
- !ss->regmap_usxgmii[id] || !ss->regmap_pextp[id])
- return;
+ if (!mpcs->regmap || !mpcs->regmap_pextp)
+ return -EINVAL;
/* Setup USXGMII speed */
val = FIELD_PREP(RG_XFI_RX_MODE, RG_XFI_RX_MODE_10G) |
FIELD_PREP(RG_XFI_TX_MODE, RG_XFI_TX_MODE_10G);
- regmap_write(ss->regmap_usxgmii[id], RG_PHY_TOP_SPEED_CTRL1, val);
+ regmap_write(mpcs->regmap, RG_PHY_TOP_SPEED_CTRL1, val);
/* Disable USXGMII AN mode */
- regmap_read(ss->regmap_usxgmii[id], RG_PCS_AN_CTRL0, &val);
- val &= ~RG_AN_ENABLE;
- regmap_write(ss->regmap_usxgmii[id], RG_PCS_AN_CTRL0, val);
+ regmap_read(mpcs->regmap, RG_PCS_AN_CTRL0, &val);
+ val &= ~USXGMII_AN_ENABLE;
+ regmap_write(mpcs->regmap, RG_PCS_AN_CTRL0, val);
/* Gated USXGMII */
- regmap_read(ss->regmap_usxgmii[id], RG_PHY_TOP_SPEED_CTRL1, &val);
+ regmap_read(mpcs->regmap, RG_PHY_TOP_SPEED_CTRL1, &val);
val |= RG_MAC_CK_GATED;
- regmap_write(ss->regmap_usxgmii[id], RG_PHY_TOP_SPEED_CTRL1, val);
+ regmap_write(mpcs->regmap, RG_PHY_TOP_SPEED_CTRL1, val);
ndelay(1020);
/* USXGMII force mode setting */
- regmap_read(ss->regmap_usxgmii[id], RG_PHY_TOP_SPEED_CTRL1, &val);
+ regmap_read(mpcs->regmap, RG_PHY_TOP_SPEED_CTRL1, &val);
val |= RG_USXGMII_RATE_UPDATE_MODE;
val |= RG_IF_FORCE_EN;
val |= FIELD_PREP(RG_RATE_ADAPT_MODE, RG_RATE_ADAPT_MODE_X1);
- regmap_write(ss->regmap_usxgmii[id], RG_PHY_TOP_SPEED_CTRL1, val);
+ regmap_write(mpcs->regmap, RG_PHY_TOP_SPEED_CTRL1, val);
/* Un-gated USXGMII */
- regmap_read(ss->regmap_usxgmii[id], RG_PHY_TOP_SPEED_CTRL1, &val);
+ regmap_read(mpcs->regmap, RG_PHY_TOP_SPEED_CTRL1, &val);
val &= ~RG_MAC_CK_GATED;
- regmap_write(ss->regmap_usxgmii[id], RG_PHY_TOP_SPEED_CTRL1, val);
+ regmap_write(mpcs->regmap, RG_PHY_TOP_SPEED_CTRL1, val);
ndelay(1020);
- regmap_update_bits(ss->regmap_pextp[id], 0x9024, GENMASK(31, 0),
+ regmap_update_bits(mpcs->regmap_pextp, 0x9024, GENMASK(31, 0),
0x00C9071C);
- regmap_update_bits(ss->regmap_pextp[id], 0x2020, GENMASK(31, 0),
+ regmap_update_bits(mpcs->regmap_pextp, 0x2020, GENMASK(31, 0),
0xAA8585AA);
- regmap_update_bits(ss->regmap_pextp[id], 0x2030, GENMASK(31, 0),
+ regmap_update_bits(mpcs->regmap_pextp, 0x2030, GENMASK(31, 0),
0x0C020707);
- regmap_update_bits(ss->regmap_pextp[id], 0x2034, GENMASK(31, 0),
+ regmap_update_bits(mpcs->regmap_pextp, 0x2034, GENMASK(31, 0),
0x0E050F0F);
- regmap_update_bits(ss->regmap_pextp[id], 0x2040, GENMASK(31, 0),
+ regmap_update_bits(mpcs->regmap_pextp, 0x2040, GENMASK(31, 0),
0x00140032);
- regmap_update_bits(ss->regmap_pextp[id], 0x50F0, GENMASK(31, 0),
+ regmap_update_bits(mpcs->regmap_pextp, 0x50F0, GENMASK(31, 0),
0x00C014AA);
- regmap_update_bits(ss->regmap_pextp[id], 0x50E0, GENMASK(31, 0),
+ regmap_update_bits(mpcs->regmap_pextp, 0x50E0, GENMASK(31, 0),
0x3777C12B);
- regmap_update_bits(ss->regmap_pextp[id], 0x506C, GENMASK(31, 0),
+ regmap_update_bits(mpcs->regmap_pextp, 0x506C, GENMASK(31, 0),
0x005F9CFF);
- regmap_update_bits(ss->regmap_pextp[id], 0x5070, GENMASK(31, 0),
+ regmap_update_bits(mpcs->regmap_pextp, 0x5070, GENMASK(31, 0),
0x9D9DFAFA);
- regmap_update_bits(ss->regmap_pextp[id], 0x5074, GENMASK(31, 0),
+ regmap_update_bits(mpcs->regmap_pextp, 0x5074, GENMASK(31, 0),
0x27273F3F);
- regmap_update_bits(ss->regmap_pextp[id], 0x5078, GENMASK(31, 0),
+ regmap_update_bits(mpcs->regmap_pextp, 0x5078, GENMASK(31, 0),
0xA7883C68);
- regmap_update_bits(ss->regmap_pextp[id], 0x507C, GENMASK(31, 0),
+ regmap_update_bits(mpcs->regmap_pextp, 0x507C, GENMASK(31, 0),
0x11661166);
- regmap_update_bits(ss->regmap_pextp[id], 0x5080, GENMASK(31, 0),
+ regmap_update_bits(mpcs->regmap_pextp, 0x5080, GENMASK(31, 0),
0x0E000AAF);
- regmap_update_bits(ss->regmap_pextp[id], 0x5084, GENMASK(31, 0),
+ regmap_update_bits(mpcs->regmap_pextp, 0x5084, GENMASK(31, 0),
0x08080D0D);
- regmap_update_bits(ss->regmap_pextp[id], 0x5088, GENMASK(31, 0),
+ regmap_update_bits(mpcs->regmap_pextp, 0x5088, GENMASK(31, 0),
0x02030909);
- regmap_update_bits(ss->regmap_pextp[id], 0x50E4, GENMASK(31, 0),
+ regmap_update_bits(mpcs->regmap_pextp, 0x50E4, GENMASK(31, 0),
0x0C0C0000);
- regmap_update_bits(ss->regmap_pextp[id], 0x50E8, GENMASK(31, 0),
+ regmap_update_bits(mpcs->regmap_pextp, 0x50E8, GENMASK(31, 0),
0x04040000);
- regmap_update_bits(ss->regmap_pextp[id], 0x50EC, GENMASK(31, 0),
+ regmap_update_bits(mpcs->regmap_pextp, 0x50EC, GENMASK(31, 0),
0x0F0F0C06);
- regmap_update_bits(ss->regmap_pextp[id], 0x50A8, GENMASK(31, 0),
+ regmap_update_bits(mpcs->regmap_pextp, 0x50A8, GENMASK(31, 0),
0x506E8C8C);
- regmap_update_bits(ss->regmap_pextp[id], 0x6004, GENMASK(31, 0),
+ regmap_update_bits(mpcs->regmap_pextp, 0x6004, GENMASK(31, 0),
0x18190000);
- regmap_update_bits(ss->regmap_pextp[id], 0x00F8, GENMASK(31, 0),
+ regmap_update_bits(mpcs->regmap_pextp, 0x00F8, GENMASK(31, 0),
0x01423342);
- regmap_update_bits(ss->regmap_pextp[id], 0x00F4, GENMASK(31, 0),
+ regmap_update_bits(mpcs->regmap_pextp, 0x00F4, GENMASK(31, 0),
0x80201F20);
- regmap_update_bits(ss->regmap_pextp[id], 0x0030, GENMASK(31, 0),
+ regmap_update_bits(mpcs->regmap_pextp, 0x0030, GENMASK(31, 0),
0x00050C00);
- regmap_update_bits(ss->regmap_pextp[id], 0x0070, GENMASK(31, 0),
+ regmap_update_bits(mpcs->regmap_pextp, 0x0070, GENMASK(31, 0),
0x02002800);
ndelay(1020);
- regmap_update_bits(ss->regmap_pextp[id], 0x30B0, GENMASK(31, 0),
+ regmap_update_bits(mpcs->regmap_pextp, 0x30B0, GENMASK(31, 0),
0x00000020);
- regmap_update_bits(ss->regmap_pextp[id], 0x3028, GENMASK(31, 0),
+ regmap_update_bits(mpcs->regmap_pextp, 0x3028, GENMASK(31, 0),
0x00008A01);
- regmap_update_bits(ss->regmap_pextp[id], 0x302C, GENMASK(31, 0),
+ regmap_update_bits(mpcs->regmap_pextp, 0x302C, GENMASK(31, 0),
0x0000A884);
- regmap_update_bits(ss->regmap_pextp[id], 0x3024, GENMASK(31, 0),
+ regmap_update_bits(mpcs->regmap_pextp, 0x3024, GENMASK(31, 0),
0x00083002);
- regmap_update_bits(ss->regmap_pextp[id], 0x3010, GENMASK(31, 0),
+ regmap_update_bits(mpcs->regmap_pextp, 0x3010, GENMASK(31, 0),
0x00022220);
- regmap_update_bits(ss->regmap_pextp[id], 0x5064, GENMASK(31, 0),
+ regmap_update_bits(mpcs->regmap_pextp, 0x5064, GENMASK(31, 0),
0x0F020A01);
- regmap_update_bits(ss->regmap_pextp[id], 0x50B4, GENMASK(31, 0),
+ regmap_update_bits(mpcs->regmap_pextp, 0x50B4, GENMASK(31, 0),
0x06100600);
- regmap_update_bits(ss->regmap_pextp[id], 0x3048, GENMASK(31, 0),
+ regmap_update_bits(mpcs->regmap_pextp, 0x3048, GENMASK(31, 0),
0x49664100);
- regmap_update_bits(ss->regmap_pextp[id], 0x3050, GENMASK(31, 0),
+ regmap_update_bits(mpcs->regmap_pextp, 0x3050, GENMASK(31, 0),
0x00000000);
- regmap_update_bits(ss->regmap_pextp[id], 0x3054, GENMASK(31, 0),
+ regmap_update_bits(mpcs->regmap_pextp, 0x3054, GENMASK(31, 0),
0x00000000);
- regmap_update_bits(ss->regmap_pextp[id], 0x306C, GENMASK(31, 0),
+ regmap_update_bits(mpcs->regmap_pextp, 0x306C, GENMASK(31, 0),
0x00000F00);
- regmap_update_bits(ss->regmap_pextp[id], 0xA060, GENMASK(31, 0),
+ regmap_update_bits(mpcs->regmap_pextp, 0xA060, GENMASK(31, 0),
0x00040000);
- regmap_update_bits(ss->regmap_pextp[id], 0x90D0, GENMASK(31, 0),
+ regmap_update_bits(mpcs->regmap_pextp, 0x90D0, GENMASK(31, 0),
0x00000001);
- regmap_update_bits(ss->regmap_pextp[id], 0x0070, GENMASK(31, 0),
+ regmap_update_bits(mpcs->regmap_pextp, 0x0070, GENMASK(31, 0),
0x0200E800);
udelay(150);
- regmap_update_bits(ss->regmap_pextp[id], 0x0070, GENMASK(31, 0),
+ regmap_update_bits(mpcs->regmap_pextp, 0x0070, GENMASK(31, 0),
0x0200C111);
ndelay(1020);
- regmap_update_bits(ss->regmap_pextp[id], 0x0070, GENMASK(31, 0),
+ regmap_update_bits(mpcs->regmap_pextp, 0x0070, GENMASK(31, 0),
0x0200C101);
udelay(15);
- regmap_update_bits(ss->regmap_pextp[id], 0x0070, GENMASK(31, 0),
+ regmap_update_bits(mpcs->regmap_pextp, 0x0070, GENMASK(31, 0),
0x0202C111);
ndelay(1020);
- regmap_update_bits(ss->regmap_pextp[id], 0x0070, GENMASK(31, 0),
+ regmap_update_bits(mpcs->regmap_pextp, 0x0070, GENMASK(31, 0),
0x0202C101);
udelay(100);
- regmap_update_bits(ss->regmap_pextp[id], 0x30B0, GENMASK(31, 0),
+ regmap_update_bits(mpcs->regmap_pextp, 0x30B0, GENMASK(31, 0),
0x00000030);
- regmap_update_bits(ss->regmap_pextp[id], 0x00F4, GENMASK(31, 0),
+ regmap_update_bits(mpcs->regmap_pextp, 0x00F4, GENMASK(31, 0),
0x80201F00);
- regmap_update_bits(ss->regmap_pextp[id], 0x3040, GENMASK(31, 0),
+ regmap_update_bits(mpcs->regmap_pextp, 0x3040, GENMASK(31, 0),
0x30000000);
udelay(400);
+
+ return 0;
}
-void mtk_usxgmii_reset(struct mtk_xgmii *ss, int mac_id)
+void mtk_usxgmii_reset(struct mtk_eth *eth, int id)
{
- struct mtk_eth *eth = ss->eth;
- u32 id = mtk_mac2xgmii_id(eth, mac_id);
u32 val = 0;
if (id >= MTK_MAX_DEVS || !eth->toprgu)
return;
- switch (mac_id) {
- case MTK_GMAC2_ID:
+ switch (id) {
+ case 0:
/* Enable software reset */
regmap_read(eth->toprgu, TOPRGU_SWSYSRST_EN, &val);
- val |= SWSYSRST_XFI_PEXPT1_GRST |
- SWSYSRST_XFI1_GRST;
+ val |= SWSYSRST_XFI_PEXPT0_GRST |
+ SWSYSRST_XFI0_GRST;
regmap_write(eth->toprgu, TOPRGU_SWSYSRST_EN, val);
/* Assert USXGMII reset */
regmap_read(eth->toprgu, TOPRGU_SWSYSRST, &val);
val |= FIELD_PREP(SWSYSRST_UNLOCK_KEY, 0x88) |
- SWSYSRST_XFI_PEXPT1_GRST |
- SWSYSRST_XFI1_GRST;
+ SWSYSRST_XFI_PEXPT0_GRST |
+ SWSYSRST_XFI0_GRST;
regmap_write(eth->toprgu, TOPRGU_SWSYSRST, val);
udelay(100);
@@ -491,28 +553,28 @@
/* De-assert USXGMII reset */
regmap_read(eth->toprgu, TOPRGU_SWSYSRST, &val);
val |= FIELD_PREP(SWSYSRST_UNLOCK_KEY, 0x88);
- val &= ~(SWSYSRST_XFI_PEXPT1_GRST |
- SWSYSRST_XFI1_GRST);
+ val &= ~(SWSYSRST_XFI_PEXPT0_GRST |
+ SWSYSRST_XFI0_GRST);
regmap_write(eth->toprgu, TOPRGU_SWSYSRST, val);
/* Disable software reset */
regmap_read(eth->toprgu, TOPRGU_SWSYSRST_EN, &val);
- val &= ~(SWSYSRST_XFI_PEXPT1_GRST |
- SWSYSRST_XFI1_GRST);
+ val &= ~(SWSYSRST_XFI_PEXPT0_GRST |
+ SWSYSRST_XFI0_GRST);
regmap_write(eth->toprgu, TOPRGU_SWSYSRST_EN, val);
break;
- case MTK_GMAC3_ID:
+ case 1:
/* Enable software reset */
regmap_read(eth->toprgu, TOPRGU_SWSYSRST_EN, &val);
- val |= SWSYSRST_XFI_PEXPT0_GRST |
- SWSYSRST_XFI0_GRST;
+ val |= SWSYSRST_XFI_PEXPT1_GRST |
+ SWSYSRST_XFI1_GRST;
regmap_write(eth->toprgu, TOPRGU_SWSYSRST_EN, val);
/* Assert USXGMII reset */
regmap_read(eth->toprgu, TOPRGU_SWSYSRST, &val);
val |= FIELD_PREP(SWSYSRST_UNLOCK_KEY, 0x88) |
- SWSYSRST_XFI_PEXPT0_GRST |
- SWSYSRST_XFI0_GRST;
+ SWSYSRST_XFI_PEXPT1_GRST |
+ SWSYSRST_XFI1_GRST;
regmap_write(eth->toprgu, TOPRGU_SWSYSRST, val);
udelay(100);
@@ -520,14 +582,14 @@
/* De-assert USXGMII reset */
regmap_read(eth->toprgu, TOPRGU_SWSYSRST, &val);
val |= FIELD_PREP(SWSYSRST_UNLOCK_KEY, 0x88);
- val &= ~(SWSYSRST_XFI_PEXPT0_GRST |
- SWSYSRST_XFI0_GRST);
+ val &= ~(SWSYSRST_XFI_PEXPT1_GRST |
+ SWSYSRST_XFI1_GRST);
regmap_write(eth->toprgu, TOPRGU_SWSYSRST, val);
/* Disable software reset */
regmap_read(eth->toprgu, TOPRGU_SWSYSRST_EN, &val);
- val &= ~(SWSYSRST_XFI_PEXPT0_GRST |
- SWSYSRST_XFI0_GRST);
+ val &= ~(SWSYSRST_XFI_PEXPT1_GRST |
+ SWSYSRST_XFI1_GRST);
regmap_write(eth->toprgu, TOPRGU_SWSYSRST_EN, val);
break;
}
@@ -535,37 +597,163 @@
mdelay(10);
}
-int mtk_usxgmii_setup_mode_an(struct mtk_xgmii *ss, int mac_id, int max_speed)
+static void mtk_usxgmii_pcs_get_state(struct phylink_pcs *pcs,
+ struct phylink_link_state *state)
{
- if (mac_id < 0 || mac_id >= MTK_MAX_DEVS)
- return -EINVAL;
+ struct mtk_usxgmii_pcs *mpcs = pcs_to_mtk_usxgmii_pcs(pcs);
+ struct mtk_eth *eth = mpcs->eth;
+ struct mtk_mac *mac = eth->mac[mtk_xgmii2mac_id(eth, mpcs->id)];
+ u32 val = 0;
- if ((max_speed != SPEED_10000) && (max_speed != SPEED_5000))
- return -EINVAL;
+ regmap_read(mpcs->regmap, RG_PCS_AN_CTRL0, &val);
+ if (FIELD_GET(USXGMII_AN_ENABLE, val)) {
+ /* Refresh LPA by inverting LPA_LATCH */
+ regmap_read(mpcs->regmap, RG_PCS_AN_STS0, &val);
+ regmap_update_bits(mpcs->regmap, RG_PCS_AN_STS0,
+ USXGMII_LPA_LATCH,
+ !(val & USXGMII_LPA_LATCH));
- mtk_xfi_pll_enable(ss);
- mtk_usxgmii_reset(ss, mac_id);
- mtk_usxgmii_setup_phya_an_10000(ss, mac_id);
+ regmap_read(mpcs->regmap, RG_PCS_AN_STS0, &val);
- return 0;
+ state->interface = mpcs->interface;
+ state->link = FIELD_GET(USXGMII_LPA_LINK, val);
+ state->duplex = FIELD_GET(USXGMII_LPA_DUPLEX, val);
+
+ switch (FIELD_GET(USXGMII_LPA_SPEED_MASK, val)) {
+ case USXGMII_LPA_SPEED_10:
+ state->speed = SPEED_10;
+ break;
+ case USXGMII_LPA_SPEED_100:
+ state->speed = SPEED_100;
+ break;
+ case USXGMII_LPA_SPEED_1000:
+ state->speed = SPEED_1000;
+ break;
+ case USXGMII_LPA_SPEED_2500:
+ state->speed = SPEED_2500;
+ break;
+ case USXGMII_LPA_SPEED_5000:
+ state->speed = SPEED_5000;
+ break;
+ case USXGMII_LPA_SPEED_10000:
+ state->speed = SPEED_10000;
+ break;
+ }
+ } else {
+ val = mtk_r32(mac->hw, MTK_XGMAC_STS(mac->id));
+
+ if (mac->id == MTK_GMAC2_ID)
+ val = val >> 16;
+
+ switch (FIELD_GET(MTK_USXGMII_PCS_MODE, val)) {
+ case 0:
+ state->speed = SPEED_10000;
+ break;
+ case 1:
+ state->speed = SPEED_5000;
+ break;
+ case 2:
+ state->speed = SPEED_2500;
+ break;
+ case 3:
+ state->speed = SPEED_1000;
+ break;
+ }
+
+ state->interface = mpcs->interface;
+ state->link = FIELD_GET(MTK_USXGMII_PCS_LINK, val);
+ state->duplex = DUPLEX_FULL;
+ }
}
-int mtk_usxgmii_setup_mode_force(struct mtk_xgmii *ss, int mac_id,
- const struct phylink_link_state *state)
+static int mtk_usxgmii_pcs_config(struct phylink_pcs *pcs, unsigned int mode,
+ phy_interface_t interface,
+ const unsigned long *advertising,
+ bool permit_pause_to_mac)
{
- if (mac_id < 0 || mac_id >= MTK_MAX_DEVS)
- return -EINVAL;
+ struct mtk_usxgmii_pcs *mpcs = pcs_to_mtk_usxgmii_pcs(pcs);
+ struct mtk_eth *eth = mpcs->eth;
+ int err = 0;
+
+ mpcs->interface = interface;
+
+ mtk_usxgmii_xfi_pll_enable(eth->usxgmii);
+ mtk_usxgmii_reset(eth, mpcs->id);
+
+ /* Setup USXGMIISYS with the determined property */
+ if (interface == PHY_INTERFACE_MODE_USXGMII)
+ err = mtk_usxgmii_setup_phya_an_10000(mpcs);
+ else if (interface == PHY_INTERFACE_MODE_10GKR)
+ err = mtk_usxgmii_setup_phya_force_10000(mpcs);
+ else if (interface == PHY_INTERFACE_MODE_5GBASER)
+ err = mtk_usxgmii_setup_phya_force_5000(mpcs);
+
+ return err;
+}
+
+void mtk_usxgmii_pcs_restart_an(struct phylink_pcs *pcs)
+{
+ struct mtk_usxgmii_pcs *mpcs = pcs_to_mtk_usxgmii_pcs(pcs);
+ unsigned int val = 0;
+
+ if (!mpcs->regmap)
+ return;
- mtk_xfi_pll_enable(ss);
- mtk_usxgmii_reset(ss, mac_id);
- if (state->interface == PHY_INTERFACE_MODE_5GBASER)
- mtk_usxgmii_setup_phya_force_5000(ss, mac_id);
- else
- mtk_usxgmii_setup_phya_force_10000(ss, mac_id);
+ regmap_read(mpcs->regmap, RG_PCS_AN_CTRL0, &val);
+ val |= USXGMII_AN_RESTART;
+ regmap_write(mpcs->regmap, RG_PCS_AN_CTRL0, val);
+}
+
+static const struct phylink_pcs_ops mtk_usxgmii_pcs_ops = {
+ .pcs_config = mtk_usxgmii_pcs_config,
+ .pcs_get_state = mtk_usxgmii_pcs_get_state,
+ .pcs_an_restart = mtk_usxgmii_pcs_restart_an,
+};
+
+int mtk_usxgmii_init(struct mtk_eth *eth, struct device_node *r)
+{
+ struct mtk_usxgmii *ss = eth->usxgmii;
+ struct device_node *np;
+ int ret, i;
+
+ for (i = 0; i < MTK_MAX_DEVS; i++) {
+ np = of_parse_phandle(r, "mediatek,usxgmiisys", i);
+ if (!np)
+ break;
+
+ ss->pcs[i].id = i;
+ ss->pcs[i].eth = eth;
+
+ ss->pcs[i].regmap = syscon_node_to_regmap(np);
+ if (IS_ERR(ss->pcs[i].regmap))
+ return PTR_ERR(ss->pcs[i].regmap);
+
+ ss->pcs[i].pcs.ops = &mtk_usxgmii_pcs_ops;
+ ss->pcs[i].pcs.poll = true;
+ ss->pcs[i].interface = PHY_INTERFACE_MODE_NA;
+
+ of_node_put(np);
+ }
+
+ ret = mtk_usxgmii_xfi_pextp_init(ss, r);
+ if (ret)
+ return ret;
+
+ ret = mtk_usxgmii_xfi_pll_init(ss, r);
+ if (ret)
+ return ret;
return 0;
}
+struct phylink_pcs *mtk_usxgmii_select_pcs(struct mtk_usxgmii *ss, int id)
+{
+ if (!ss->pcs[id].regmap)
+ return NULL;
+
+ return &ss->pcs[id].pcs;
+}
+
int mtk_dump_usxgmii(struct regmap *pmap, char *name, u32 offset, u32 range)
{
unsigned int cur = offset;
diff --git a/target/linux/mediatek/patches-5.4/757-net-phy-add-phylink-pcs-support.patch b/target/linux/mediatek/patches-5.4/757-net-phy-add-phylink-pcs-support.patch
new file mode 100644
index 0000000..f449505
--- /dev/null
+++ b/target/linux/mediatek/patches-5.4/757-net-phy-add-phylink-pcs-support.patch
@@ -0,0 +1,774 @@
+diff --git a/drivers/net/phy/phylink.c b/drivers/net/phy/phylink.c
+index 67f34ed..ead9b37 100644
+--- a/drivers/net/phy/phylink.c
++++ b/drivers/net/phy/phylink.c
+@@ -40,8 +40,9 @@ enum {
+ struct phylink {
+ /* private: */
+ struct net_device *netdev;
+- const struct phylink_mac_ops *ops;
++ const struct phylink_mac_ops *mac_ops;
+ struct phylink_config *config;
++ struct phylink_pcs *pcs;
+ struct device *dev;
+ unsigned int old_link_state:1;
+
+@@ -70,6 +71,7 @@ struct phylink {
+ struct work_struct resolve;
+
+ bool mac_link_dropped;
++ bool using_mac_select_pcs;
+
+ struct sfp_bus *sfp_bus;
+ bool sfp_may_have_phy;
+@@ -153,14 +155,60 @@ static const char *phylink_an_mode_str(unsigned int mode)
+ return mode < ARRAY_SIZE(modestr) ? modestr[mode] : "unknown";
+ }
+
+-static int phylink_validate(struct phylink *pl, unsigned long *supported,
+- struct phylink_link_state *state)
++static int phylink_validate_mac_and_pcs(struct phylink *pl,
++ unsigned long *supported,
++ struct phylink_link_state *state)
+ {
+- pl->ops->validate(pl->config, supported, state);
++ struct phylink_pcs *pcs;
++ int ret;
++
++ /* Get the PCS for this interface mode */
++ if (pl->using_mac_select_pcs) {
++ pcs = pl->mac_ops->mac_select_pcs(pl->config, state->interface);
++ if (IS_ERR(pcs))
++ return PTR_ERR(pcs);
++ } else {
++ pcs = pl->pcs;
++ }
++
++ if (pcs) {
++ /* The PCS, if present, must be setup before phylink_create()
++ * has been called. If the ops is not initialised, print an
++ * error and backtrace rather than oopsing the kernel.
++ */
++ if (!pcs->ops) {
++ phylink_err(pl, "interface %s: uninitialised PCS\n",
++ phy_modes(state->interface));
++ dump_stack();
++ return -EINVAL;
++ }
++
++ /* Validate the link parameters with the PCS */
++ if (pcs->ops->pcs_validate) {
++ ret = pcs->ops->pcs_validate(pcs, supported, state);
++ if (ret < 0 || phylink_is_empty_linkmode(supported))
++ return -EINVAL;
++
++ /* Ensure the advertising mask is a subset of the
++ * supported mask.
++ */
++ linkmode_and(state->advertising, state->advertising,
++ supported);
++ }
++ }
++
++ /* Then validate the link parameters with the MAC */
++ pl->mac_ops->validate(pl->config, supported, state);
+
+ return phylink_is_empty_linkmode(supported) ? -EINVAL : 0;
+ }
+
++static int phylink_validate(struct phylink *pl, unsigned long *supported,
++ struct phylink_link_state *state)
++{
++ return phylink_validate_mac_and_pcs(pl, supported, state);
++}
++
+ static int phylink_parse_fixedlink(struct phylink *pl,
+ struct fwnode_handle *fwnode)
+ {
+@@ -338,6 +386,18 @@ static int phylink_parse_mode(struct phylink *pl, struct fwnode_handle *fwnode)
+ return 0;
+ }
+
++static void phylink_pcs_poll_stop(struct phylink *pl)
++{
++ if (pl->cfg_link_an_mode == MLO_AN_INBAND)
++ del_timer(&pl->link_poll);
++}
++
++static void phylink_pcs_poll_start(struct phylink *pl)
++{
++ if (pl->pcs && pl->pcs->poll && pl->cfg_link_an_mode == MLO_AN_INBAND)
++ mod_timer(&pl->link_poll, jiffies + HZ);
++}
++
+ static void phylink_mac_config(struct phylink *pl,
+ const struct phylink_link_state *state)
+ {
+@@ -350,37 +410,113 @@ static void phylink_mac_config(struct phylink *pl,
+ __ETHTOOL_LINK_MODE_MASK_NBITS, state->advertising,
+ state->pause, state->link, state->an_enabled);
+
+- pl->ops->mac_config(pl->config, pl->cur_link_an_mode, state);
++ pl->mac_ops->mac_config(pl->config, pl->cur_link_an_mode, state);
+ }
+
+-static void phylink_mac_config_up(struct phylink *pl,
+- const struct phylink_link_state *state)
++static void phylink_mac_pcs_an_restart(struct phylink *pl)
+ {
+- if (state->link)
+- phylink_mac_config(pl, state);
++ if (pl->link_config.an_enabled &&
++ phy_interface_mode_is_8023z(pl->link_config.interface) &&
++ phylink_autoneg_inband(pl->cur_link_an_mode)) {
++ if (pl->pcs)
++ pl->pcs->ops->pcs_an_restart(pl->pcs);
++ else if (pl->mac_ops->mac_an_restart)
++ pl->mac_ops->mac_an_restart(pl->config);
++ }
+ }
+
+-static void phylink_mac_an_restart(struct phylink *pl)
++static void phylink_major_config(struct phylink *pl, bool restart,
++ const struct phylink_link_state *state)
+ {
+- if (pl->link_config.an_enabled &&
+- phy_interface_mode_is_8023z(pl->link_config.interface))
+- pl->ops->mac_an_restart(pl->config);
++ struct phylink_pcs *pcs = NULL;
++ bool pcs_changed = false;
++ int err;
++
++ phylink_dbg(pl, "major config %s\n", phy_modes(state->interface));
++
++ if (pl->using_mac_select_pcs) {
++ pcs = pl->mac_ops->mac_select_pcs(pl->config, state->interface);
++ if (IS_ERR(pcs)) {
++ phylink_err(pl,
++ "mac_select_pcs unexpectedly failed: %pe\n",
++ pcs);
++ return;
++ }
++
++ pcs_changed = pcs && pl->pcs != pcs;
++ }
++
++ phylink_pcs_poll_stop(pl);
++
++ if (pl->mac_ops->mac_prepare) {
++ err = pl->mac_ops->mac_prepare(pl->config, pl->cur_link_an_mode,
++ state->interface);
++ if (err < 0) {
++ phylink_err(pl, "mac_prepare failed: %pe\n",
++ ERR_PTR(err));
++ return;
++ }
++ }
++
++ /* If we have a new PCS, switch to the new PCS after preparing the MAC
++ * for the change.
++ */
++ if (pcs_changed)
++ pl->pcs = pcs;
++
++ phylink_mac_config(pl, state);
++
++ if (pl->pcs) {
++ err = pl->pcs->ops->pcs_config(pl->pcs, pl->cur_link_an_mode,
++ state->interface,
++ state->advertising,
++ !!(pl->link_config.pause &
++ MLO_PAUSE_AN));
++ if (err < 0)
++ phylink_err(pl, "pcs_config failed: %pe\n",
++ ERR_PTR(err));
++ if (err > 0)
++ restart = true;
++ }
++ if (restart)
++ phylink_mac_pcs_an_restart(pl);
++
++ if (pl->mac_ops->mac_finish) {
++ err = pl->mac_ops->mac_finish(pl->config, pl->cur_link_an_mode,
++ state->interface);
++ if (err < 0)
++ phylink_err(pl, "mac_finish failed: %pe\n",
++ ERR_PTR(err));
++ }
++
++ phylink_pcs_poll_start(pl);
+ }
+
+-static int phylink_get_mac_state(struct phylink *pl, struct phylink_link_state *state)
++static void phylink_mac_pcs_get_state(struct phylink *pl,
++ struct phylink_link_state *state)
+ {
+-
+ linkmode_copy(state->advertising, pl->link_config.advertising);
+ linkmode_zero(state->lp_advertising);
+ state->interface = pl->link_config.interface;
+ state->an_enabled = pl->link_config.an_enabled;
+- state->speed = SPEED_UNKNOWN;
+- state->duplex = DUPLEX_UNKNOWN;
+- state->pause = MLO_PAUSE_NONE;
++ if (state->an_enabled) {
++ state->speed = SPEED_UNKNOWN;
++ state->duplex = DUPLEX_UNKNOWN;
++ state->pause = MLO_PAUSE_NONE;
++ } else {
++ state->speed = pl->link_config.speed;
++ state->duplex = pl->link_config.duplex;
++ state->pause = pl->link_config.pause;
++ }
+ state->an_complete = 0;
+ state->link = 1;
+
+- return pl->ops->mac_link_state(pl->config, state);
++ if (pl->pcs)
++ pl->pcs->ops->pcs_get_state(pl->pcs, state);
++ else if (pl->mac_ops->mac_link_state)
++ pl->mac_ops->mac_link_state(pl->config, state);
++ else
++ state->link = 0;
+ }
+
+ /* The fixed state is... fixed except for the link state,
+@@ -395,6 +531,34 @@ static void phylink_get_fixed_state(struct phylink *pl, struct phylink_link_stat
+ state->link = !!gpiod_get_value_cansleep(pl->link_gpio);
+ }
+
++static void phylink_mac_initial_config(struct phylink *pl, bool force_restart)
++{
++ struct phylink_link_state link_state;
++
++ switch (pl->cur_link_an_mode) {
++ case MLO_AN_PHY:
++ link_state = pl->phy_state;
++ break;
++
++ case MLO_AN_FIXED:
++ phylink_get_fixed_state(pl, &link_state);
++ break;
++
++ case MLO_AN_INBAND:
++ link_state = pl->link_config;
++ if (link_state.interface == PHY_INTERFACE_MODE_SGMII)
++ link_state.pause = MLO_PAUSE_NONE;
++ break;
++
++ default: /* can't happen */
++ return;
++ }
++
++ link_state.link = false;
++
++ phylink_major_config(pl, force_restart, &link_state);
++}
++
+ /* Flow control is resolved according to our and the link partners
+ * advertisements using the following drawn from the 802.3 specs:
+ * Local device Link partner
+@@ -445,17 +609,25 @@ static const char *phylink_pause_to_str(int pause)
+ }
+ }
+
+-static void phylink_mac_link_up(struct phylink *pl,
+- struct phylink_link_state link_state)
++static void phylink_link_up(struct phylink *pl,
++ struct phylink_link_state link_state)
+ {
+ struct net_device *ndev = pl->netdev;
++ int speed, duplex;
++
++ speed = link_state.speed;
++ duplex = link_state.duplex;
+
+ pl->cur_interface = link_state.interface;
+- 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 (pl->pcs && pl->pcs->ops->pcs_link_up)
++ pl->pcs->ops->pcs_link_up(pl->pcs, pl->cur_link_an_mode,
++ pl->cur_interface, speed, duplex);
++
++ pl->mac_ops->mac_link_up(pl->config, pl->phydev, pl->cur_link_an_mode,
++ pl->cur_interface, speed, duplex,
++ !!(link_state.pause & MLO_PAUSE_TX),
++ !!(link_state.pause & MLO_PAUSE_RX));
+
+ if (ndev)
+ netif_carrier_on(ndev);
+@@ -467,14 +639,14 @@ static void phylink_mac_link_up(struct phylink *pl,
+ phylink_pause_to_str(link_state.pause));
+ }
+
+-static void phylink_mac_link_down(struct phylink *pl)
++static void phylink_link_down(struct phylink *pl)
+ {
+ struct net_device *ndev = pl->netdev;
+
+ if (ndev)
+ netif_carrier_off(ndev);
+- pl->ops->mac_link_down(pl->config, pl->cur_link_an_mode,
+- pl->cur_interface);
++ pl->mac_ops->mac_link_down(pl->config, pl->cur_link_an_mode,
++ pl->cur_interface);
+ phylink_info(pl, "Link is Down\n");
+ }
+
+@@ -513,7 +685,7 @@ static void phylink_resolve(struct work_struct *w)
+ break;
+
+ case MLO_AN_INBAND:
+- phylink_get_mac_state(pl, &link_state);
++ phylink_mac_pcs_get_state(pl, &link_state);
+
+ /* The PCS may have a latching link-fail indicator.
+ * If the link was up, bring the link down and
+@@ -524,8 +696,8 @@ static void phylink_resolve(struct work_struct *w)
+ if (cur_link_state)
+ retrigger = true;
+ else
+- phylink_get_mac_state(pl,
+- &link_state);
++ phylink_mac_pcs_get_state(pl,
++ &link_state);
+ }
+
+ /* If we have a phy, the "up" state is the union of
+@@ -564,12 +736,17 @@ static void phylink_resolve(struct work_struct *w)
+ * then reconfigure.
+ */
+ if (cur_link_state) {
+- phylink_mac_link_down(pl);
++ phylink_link_down(pl);
+ cur_link_state = false;
+ }
+- phylink_mac_config(pl, &link_state);
++ phylink_major_config(pl, false, &link_state);
+ pl->link_config.interface = link_state.interface;
+- } else {
++ } else if (!pl->pcs) {
++ /* The interface remains unchanged, only the speed,
++ * duplex or pause settings have changed. Call the
++ * old mac_config() method to configure the MAC/PCS
++ * only if we do not have a legacy MAC driver.
++ */
+ phylink_mac_config(pl, &link_state);
+ }
+ }
+@@ -577,9 +754,9 @@ static void phylink_resolve(struct work_struct *w)
+ if (link_state.link != cur_link_state) {
+ pl->old_link_state = link_state.link;
+ if (!link_state.link)
+- phylink_mac_link_down(pl);
++ phylink_link_down(pl);
+ else
+- phylink_mac_link_up(pl, link_state);
++ phylink_link_up(pl, link_state);
+ }
+ if (!link_state.link && retrigger) {
+ pl->mac_link_dropped = false;
+@@ -643,7 +820,7 @@ static int phylink_register_sfp(struct phylink *pl,
+ * @fwnode: a pointer to a &struct fwnode_handle describing the network
+ * interface
+ * @iface: the desired link mode defined by &typedef phy_interface_t
+- * @ops: a pointer to a &struct phylink_mac_ops for the MAC.
++ * @mac_ops: a pointer to a &struct phylink_mac_ops for the MAC.
+ *
+ * Create a new phylink instance, and parse the link parameters found in @np.
+ * This will parse in-band modes, fixed-link or SFP configuration.
+@@ -656,11 +833,17 @@ static int phylink_register_sfp(struct phylink *pl,
+ struct phylink *phylink_create(struct phylink_config *config,
+ struct fwnode_handle *fwnode,
+ phy_interface_t iface,
+- const struct phylink_mac_ops *ops)
++ const struct phylink_mac_ops *mac_ops)
+ {
++ bool using_mac_select_pcs = false;
+ struct phylink *pl;
+ int ret;
+
++ if (mac_ops->mac_select_pcs &&
++ mac_ops->mac_select_pcs(config, PHY_INTERFACE_MODE_NA) !=
++ ERR_PTR(-EOPNOTSUPP))
++ using_mac_select_pcs = true;
++
+ pl = kzalloc(sizeof(*pl), GFP_KERNEL);
+ if (!pl)
+ return ERR_PTR(-ENOMEM);
+@@ -678,6 +861,7 @@ struct phylink *phylink_create(struct phylink_config *config,
+ return ERR_PTR(-EINVAL);
+ }
+
++ pl->using_mac_select_pcs = using_mac_select_pcs;
+ pl->phy_state.interface = iface;
+ pl->link_interface = iface;
+ if (iface == PHY_INTERFACE_MODE_MOCA)
+@@ -689,7 +873,7 @@ struct phylink *phylink_create(struct phylink_config *config,
+ pl->link_config.speed = SPEED_UNKNOWN;
+ pl->link_config.duplex = DUPLEX_UNKNOWN;
+ pl->link_config.an_enabled = true;
+- pl->ops = ops;
++ pl->mac_ops = mac_ops;
+ __set_bit(PHYLINK_DISABLE_STOPPED, &pl->phylink_disable_state);
+ timer_setup(&pl->link_poll, phylink_fixed_poll, 0);
+
+@@ -1016,6 +1200,8 @@ static irqreturn_t phylink_link_handler(int irq, void *data)
+ */
+ void phylink_start(struct phylink *pl)
+ {
++ bool poll = false;
++
+ ASSERT_RTNL();
+
+ phylink_info(pl, "configuring for %s/%s link mode\n",
+@@ -1029,15 +1215,12 @@ void phylink_start(struct phylink *pl)
+ /* Apply the link configuration to the MAC when starting. This allows
+ * a fixed-link to start with the correct parameters, and also
+ * ensures that we set the appropriate advertisement for Serdes links.
+- */
+- phylink_resolve_flow(pl, &pl->link_config);
+- phylink_mac_config(pl, &pl->link_config);
+-
+- /* Restart autonegotiation if using 802.3z to ensure that the link
++ *
++ * Restart autonegotiation if using 802.3z to ensure that the link
+ * parameters are properly negotiated. This is necessary for DSA
+ * switches using 802.3z negotiation to ensure they see our modes.
+ */
+- phylink_mac_an_restart(pl);
++ phylink_mac_initial_config(pl, true);
+
+ clear_bit(PHYLINK_DISABLE_STOPPED, &pl->phylink_disable_state);
+ phylink_run_resolve(pl);
+@@ -1055,10 +1238,19 @@ void phylink_start(struct phylink *pl)
+ irq = 0;
+ }
+ if (irq <= 0)
+- mod_timer(&pl->link_poll, jiffies + HZ);
++ poll = true;
+ }
+- if ((pl->cfg_link_an_mode == MLO_AN_FIXED && pl->get_fixed_state) ||
+- (pl->cfg_link_an_mode == MLO_AN_INBAND))
++
++ switch (pl->cfg_link_an_mode) {
++ case MLO_AN_FIXED:
++ poll |= pl->config->poll_fixed_state;
++ break;
++ case MLO_AN_INBAND:
++ if (pl->pcs)
++ poll |= pl->pcs->poll;
++ break;
++ }
++ if (poll)
+ mod_timer(&pl->link_poll, jiffies + HZ);
+ if (pl->phydev)
+ phy_start(pl->phydev);
+@@ -1202,7 +1394,7 @@ int phylink_ethtool_ksettings_get(struct phylink *pl,
+ if (pl->phydev)
+ break;
+
+- phylink_get_mac_state(pl, &link_state);
++ phylink_mac_pcs_get_state(pl, &link_state);
+
+ /* The MAC is reporting the link results from its own PCS
+ * layer via in-band status. Report these as the current
+@@ -1314,7 +1506,7 @@ int phylink_ethtool_ksettings_set(struct phylink *pl,
+ if (pl->cur_link_an_mode == MLO_AN_INBAND &&
+ !test_bit(PHYLINK_DISABLE_STOPPED, &pl->phylink_disable_state)) {
+ phylink_mac_config(pl, &pl->link_config);
+- phylink_mac_an_restart(pl);
++ phylink_mac_pcs_an_restart(pl);
+ }
+ mutex_unlock(&pl->state_mutex);
+
+@@ -1341,7 +1533,7 @@ int phylink_ethtool_nway_reset(struct phylink *pl)
+
+ if (pl->phydev)
+ ret = phy_restart_aneg(pl->phydev);
+- phylink_mac_an_restart(pl);
++ phylink_mac_pcs_an_restart(pl);
+
+ return ret;
+ }
+@@ -1410,7 +1602,7 @@ int phylink_ethtool_set_pauseparam(struct phylink *pl,
+
+ case MLO_AN_INBAND:
+ phylink_mac_config(pl, config);
+- phylink_mac_an_restart(pl);
++ phylink_mac_pcs_an_restart(pl);
+ break;
+ }
+ }
+@@ -1621,10 +1813,7 @@ static int phylink_mii_read(struct phylink *pl, unsigned int phy_id,
+
+ case MLO_AN_INBAND:
+ if (phy_id == 0) {
+- val = phylink_get_mac_state(pl, &state);
+- if (val < 0)
+- return val;
+-
++ phylink_mac_pcs_get_state(pl, &state);
+ val = phylink_mii_emul_read(reg, &state);
+ }
+ break;
+diff --git a/include/linux/phylink.h b/include/linux/phylink.h
+index 8229f56..ba0f09d 100644
+--- a/include/linux/phylink.h
++++ b/include/linux/phylink.h
+@@ -63,17 +63,23 @@ enum phylink_op_type {
+ * struct phylink_config - PHYLINK configuration structure
+ * @dev: a pointer to a struct device associated with the MAC
+ * @type: operation type of PHYLINK instance
++ * @poll_fixed_state: if true, starts link_poll,
++ * if MAC link is at %MLO_AN_FIXED mode.
+ */
+ struct phylink_config {
+ struct device *dev;
+ enum phylink_op_type type;
++ bool poll_fixed_state;
+ };
+
+ /**
+ * struct phylink_mac_ops - MAC operations structure.
+ * @validate: Validate and update the link configuration.
++ * @mac_select_pcs: Select a PCS for the interface mode.
+ * @mac_link_state: Read the current link state from the hardware.
++ * @mac_prepare: prepare for a major reconfiguration of the interface.
+ * @mac_config: configure the MAC for the selected mode and state.
++ * @mac_finish: finish a major reconfiguration of the interface.
+ * @mac_an_restart: restart 802.3z BaseX autonegotiation.
+ * @mac_link_down: take the link down.
+ * @mac_link_up: allow the link to come up.
+@@ -84,10 +90,16 @@ struct phylink_mac_ops {
+ void (*validate)(struct phylink_config *config,
+ unsigned long *supported,
+ struct phylink_link_state *state);
++ struct phylink_pcs *(*mac_select_pcs)(struct phylink_config *config,
++ phy_interface_t interface);
+ int (*mac_link_state)(struct phylink_config *config,
+ struct phylink_link_state *state);
++ int (*mac_prepare)(struct phylink_config *config, unsigned int mode,
++ phy_interface_t iface);
+ void (*mac_config)(struct phylink_config *config, unsigned int mode,
+ const struct phylink_link_state *state);
++ int (*mac_finish)(struct phylink_config *config, unsigned int mode,
++ phy_interface_t iface);
+ void (*mac_an_restart)(struct phylink_config *config);
+ void (*mac_link_down)(struct phylink_config *config, unsigned int mode,
+ phy_interface_t interface);
+@@ -126,6 +138,21 @@ struct phylink_mac_ops {
+ */
+ void validate(struct phylink_config *config, unsigned long *supported,
+ struct phylink_link_state *state);
++/**
++ * mac_select_pcs: Select a PCS for the interface mode.
++ * @config: a pointer to a &struct phylink_config.
++ * @interface: PHY interface mode for PCS
++ *
++ * Return the &struct phylink_pcs for the specified interface mode, or
++ * NULL if none is required, or an error pointer on error.
++ *
++ * This must not modify any state. It is used to query which PCS should
++ * be used. Phylink will use this during validation to ensure that the
++ * configuration is valid, and when setting a configuration to internally
++ * set the PCS that will be used.
++ */
++struct phylink_pcs *mac_select_pcs(struct phylink_config *config,
++ phy_interface_t interface);
+
+ /**
+ * mac_link_state() - Read the current link state from the hardware
+@@ -141,6 +168,31 @@ void validate(struct phylink_config *config, unsigned long *supported,
+ int mac_link_state(struct phylink_config *config,
+ struct phylink_link_state *state);
+
++/**
++ * mac_prepare() - prepare to change the PHY interface mode
++ * @config: a pointer to a &struct phylink_config.
++ * @mode: one of %MLO_AN_FIXED, %MLO_AN_PHY, %MLO_AN_INBAND.
++ * @iface: interface mode to switch to
++ *
++ * phylink will call this method at the beginning of a full initialisation
++ * of the link, which includes changing the interface mode or at initial
++ * startup time. It may be called for the current mode. The MAC driver
++ * should perform whatever actions are required, e.g. disabling the
++ * Serdes PHY.
++ *
++ * This will be the first call in the sequence:
++ * - mac_prepare()
++ * - mac_config()
++ * - pcs_config()
++ * - possible pcs_an_restart()
++ * - mac_finish()
++ *
++ * Returns zero on success, or negative errno on failure which will be
++ * reported to the kernel log.
++ */
++int mac_prepare(struct phylink_config *config, unsigned int mode,
++ phy_interface_t iface);
++
+ /**
+ * mac_config() - configure the MAC for the selected mode and state
+ * @config: a pointer to a &struct phylink_config.
+@@ -195,6 +247,23 @@ int mac_link_state(struct phylink_config *config,
+ void mac_config(struct phylink_config *config, unsigned int mode,
+ const struct phylink_link_state *state);
+
++/**
++ * mac_finish() - finish a to change the PHY interface mode
++ * @config: a pointer to a &struct phylink_config.
++ * @mode: one of %MLO_AN_FIXED, %MLO_AN_PHY, %MLO_AN_INBAND.
++ * @iface: interface mode to switch to
++ *
++ * phylink will call this if it called mac_prepare() to allow the MAC to
++ * complete any necessary steps after the MAC and PCS have been configured
++ * for the @mode and @iface. E.g. a MAC driver may wish to re-enable the
++ * Serdes PHY here if it was previously disabled by mac_prepare().
++ *
++ * Returns zero on success, or negative errno on failure which will be
++ * reported to the kernel log.
++ */
++int mac_finish(struct phylink_config *config, unsigned int mode,
++ phy_interface_t iface);
++
+ /**
+ * mac_an_restart() - restart 802.3z BaseX autonegotiation
+ * @config: a pointer to a &struct phylink_config.
+@@ -248,6 +317,132 @@ void mac_link_up(struct phylink_config *config, struct phy_device *phy,
+ int speed, int duplex, bool tx_pause, bool rx_pause);
+ #endif
+
++struct phylink_pcs_ops;
++
++/**
++ * struct phylink_pcs - PHYLINK PCS instance
++ * @ops: a pointer to the &struct phylink_pcs_ops structure
++ * @poll: poll the PCS for link changes
++ *
++ * This structure is designed to be embedded within the PCS private data,
++ * and will be passed between phylink and the PCS.
++ */
++struct phylink_pcs {
++ const struct phylink_pcs_ops *ops;
++ bool poll;
++};
++
++/**
++ * struct phylink_pcs_ops - MAC PCS operations structure.
++ * @pcs_validate: validate the link configuration.
++ * @pcs_get_state: read the current MAC PCS link state from the hardware.
++ * @pcs_config: configure the MAC PCS for the selected mode and state.
++ * @pcs_an_restart: restart 802.3z BaseX autonegotiation.
++ * @pcs_link_up: program the PCS for the resolved link configuration
++ * (where necessary).
++ */
++struct phylink_pcs_ops {
++ int (*pcs_validate)(struct phylink_pcs *pcs, unsigned long *supported,
++ const struct phylink_link_state *state);
++ void (*pcs_get_state)(struct phylink_pcs *pcs,
++ struct phylink_link_state *state);
++ int (*pcs_config)(struct phylink_pcs *pcs, unsigned int mode,
++ phy_interface_t interface,
++ const unsigned long *advertising,
++ bool permit_pause_to_mac);
++ void (*pcs_an_restart)(struct phylink_pcs *pcs);
++ void (*pcs_link_up)(struct phylink_pcs *pcs, unsigned int mode,
++ phy_interface_t interface, int speed, int duplex);
++};
++
++#if 0 /* For kernel-doc purposes only. */
++/**
++ * pcs_validate() - validate the link configuration.
++ * @pcs: a pointer to a &struct phylink_pcs.
++ * @supported: ethtool bitmask for supported link modes.
++ * @state: a const pointer to a &struct phylink_link_state.
++ *
++ * Validate the interface mode, and advertising's autoneg bit, removing any
++ * media ethtool link modes that would not be supportable from the supported
++ * mask. Phylink will propagate the changes to the advertising mask. See the
++ * &struct phylink_mac_ops validate() method.
++ *
++ * Returns -EINVAL if the interface mode/autoneg mode is not supported.
++ * Returns non-zero positive if the link state can be supported.
++ */
++int pcs_validate(struct phylink_pcs *pcs, unsigned long *supported,
++ const struct phylink_link_state *state);
++
++/**
++ * pcs_get_state() - Read the current inband link state from the hardware
++ * @pcs: a pointer to a &struct phylink_pcs.
++ * @state: a pointer to a &struct phylink_link_state.
++ *
++ * Read the current inband link state from the MAC PCS, reporting the
++ * current speed in @state->speed, duplex mode in @state->duplex, pause
++ * mode in @state->pause using the %MLO_PAUSE_RX and %MLO_PAUSE_TX bits,
++ * negotiation completion state in @state->an_complete, and link up state
++ * in @state->link. If possible, @state->lp_advertising should also be
++ * populated.
++ *
++ * When present, this overrides mac_pcs_get_state() in &struct
++ * phylink_mac_ops.
++ */
++void pcs_get_state(struct phylink_pcs *pcs,
++ struct phylink_link_state *state);
++
++/**
++ * pcs_config() - Configure the PCS mode and advertisement
++ * @pcs: a pointer to a &struct phylink_pcs.
++ * @mode: one of %MLO_AN_FIXED, %MLO_AN_PHY, %MLO_AN_INBAND.
++ * @interface: interface mode to be used
++ * @advertising: adertisement ethtool link mode mask
++ * @permit_pause_to_mac: permit forwarding pause resolution to MAC
++ *
++ * Configure the PCS for the operating mode, the interface mode, and set
++ * the advertisement mask. @permit_pause_to_mac indicates whether the
++ * hardware may forward the pause mode resolution to the MAC.
++ *
++ * When operating in %MLO_AN_INBAND, inband should always be enabled,
++ * otherwise inband should be disabled.
++ *
++ * For SGMII, there is no advertisement from the MAC side, the PCS should
++ * be programmed to acknowledge the inband word from the PHY.
++ *
++ * For 1000BASE-X, the advertisement should be programmed into the PCS.
++ *
++ * For most 10GBASE-R, there is no advertisement.
++ */
++int pcs_config(struct phylink_pcs *pcs, unsigned int mode,
++ phy_interface_t interface, const unsigned long *advertising,
++ bool permit_pause_to_mac);
++
++/**
++ * pcs_an_restart() - restart 802.3z BaseX autonegotiation
++ * @pcs: a pointer to a &struct phylink_pcs.
++ *
++ * When PCS ops are present, this overrides mac_an_restart() in &struct
++ * phylink_mac_ops.
++ */
++void pcs_an_restart(struct phylink_pcs *pcs);
++
++/**
++ * pcs_link_up() - program the PCS for the resolved link configuration
++ * @pcs: a pointer to a &struct phylink_pcs.
++ * @mode: link autonegotiation mode
++ * @interface: link &typedef phy_interface_t mode
++ * @speed: link speed
++ * @duplex: link duplex
++ *
++ * This call will be made just before mac_link_up() to inform the PCS of
++ * the resolved link parameters. For example, a PCS operating in SGMII
++ * mode without in-band AN needs to be manually configured for the link
++ * and duplex setting. Otherwise, this should be a no-op.
++ */
++void pcs_link_up(struct phylink_pcs *pcs, unsigned int mode,
++ phy_interface_t interface, int speed, int duplex);
++#endif
++
+ struct phylink *phylink_create(struct phylink_config *, struct fwnode_handle *,
+ phy_interface_t iface,
+ const struct phylink_mac_ops *ops);