xref: /openbmc/linux/drivers/net/pcs/pcs-xpcs.c (revision 1e952e95843d437b8a904dbd5b48d72db8ac23ec)
12fa4e4b7SAndrew Lunn // SPDX-License-Identifier: GPL-2.0
22fa4e4b7SAndrew Lunn /*
32fa4e4b7SAndrew Lunn  * Copyright (c) 2020 Synopsys, Inc. and/or its affiliates.
42fa4e4b7SAndrew Lunn  * Synopsys DesignWare XPCS helpers
52fa4e4b7SAndrew Lunn  *
62fa4e4b7SAndrew Lunn  * Author: Jose Abreu <Jose.Abreu@synopsys.com>
72fa4e4b7SAndrew Lunn  */
82fa4e4b7SAndrew Lunn 
92fa4e4b7SAndrew Lunn #include <linux/delay.h>
102fa4e4b7SAndrew Lunn #include <linux/pcs/pcs-xpcs.h>
112fa4e4b7SAndrew Lunn #include <linux/mdio.h>
122fa4e4b7SAndrew Lunn #include <linux/phylink.h>
132fa4e4b7SAndrew Lunn #include <linux/workqueue.h>
14d4433d5bSVladimir Oltean #include "pcs-xpcs.h"
157617af3dSMichael Sit Wei Hong 
1611059740SVladimir Oltean #define phylink_pcs_to_xpcs(pl_pcs) \
175673ef86SVladimir Oltean 	container_of((pl_pcs), struct dw_xpcs, pcs)
1811059740SVladimir Oltean 
192fa4e4b7SAndrew Lunn static const int xpcs_usxgmii_features[] = {
202fa4e4b7SAndrew Lunn 	ETHTOOL_LINK_MODE_Pause_BIT,
212fa4e4b7SAndrew Lunn 	ETHTOOL_LINK_MODE_Asym_Pause_BIT,
222fa4e4b7SAndrew Lunn 	ETHTOOL_LINK_MODE_Autoneg_BIT,
232fa4e4b7SAndrew Lunn 	ETHTOOL_LINK_MODE_1000baseKX_Full_BIT,
242fa4e4b7SAndrew Lunn 	ETHTOOL_LINK_MODE_10000baseKX4_Full_BIT,
252fa4e4b7SAndrew Lunn 	ETHTOOL_LINK_MODE_10000baseKR_Full_BIT,
262fa4e4b7SAndrew Lunn 	ETHTOOL_LINK_MODE_2500baseX_Full_BIT,
272fa4e4b7SAndrew Lunn 	__ETHTOOL_LINK_MODE_MASK_NBITS,
282fa4e4b7SAndrew Lunn };
292fa4e4b7SAndrew Lunn 
302fa4e4b7SAndrew Lunn static const int xpcs_10gkr_features[] = {
312fa4e4b7SAndrew Lunn 	ETHTOOL_LINK_MODE_Pause_BIT,
322fa4e4b7SAndrew Lunn 	ETHTOOL_LINK_MODE_Asym_Pause_BIT,
332fa4e4b7SAndrew Lunn 	ETHTOOL_LINK_MODE_10000baseKR_Full_BIT,
342fa4e4b7SAndrew Lunn 	__ETHTOOL_LINK_MODE_MASK_NBITS,
352fa4e4b7SAndrew Lunn };
362fa4e4b7SAndrew Lunn 
372fa4e4b7SAndrew Lunn static const int xpcs_xlgmii_features[] = {
382fa4e4b7SAndrew Lunn 	ETHTOOL_LINK_MODE_Pause_BIT,
392fa4e4b7SAndrew Lunn 	ETHTOOL_LINK_MODE_Asym_Pause_BIT,
402fa4e4b7SAndrew Lunn 	ETHTOOL_LINK_MODE_25000baseCR_Full_BIT,
412fa4e4b7SAndrew Lunn 	ETHTOOL_LINK_MODE_25000baseKR_Full_BIT,
422fa4e4b7SAndrew Lunn 	ETHTOOL_LINK_MODE_25000baseSR_Full_BIT,
432fa4e4b7SAndrew Lunn 	ETHTOOL_LINK_MODE_40000baseKR4_Full_BIT,
442fa4e4b7SAndrew Lunn 	ETHTOOL_LINK_MODE_40000baseCR4_Full_BIT,
452fa4e4b7SAndrew Lunn 	ETHTOOL_LINK_MODE_40000baseSR4_Full_BIT,
462fa4e4b7SAndrew Lunn 	ETHTOOL_LINK_MODE_40000baseLR4_Full_BIT,
472fa4e4b7SAndrew Lunn 	ETHTOOL_LINK_MODE_50000baseCR2_Full_BIT,
482fa4e4b7SAndrew Lunn 	ETHTOOL_LINK_MODE_50000baseKR2_Full_BIT,
492fa4e4b7SAndrew Lunn 	ETHTOOL_LINK_MODE_50000baseSR2_Full_BIT,
502fa4e4b7SAndrew Lunn 	ETHTOOL_LINK_MODE_50000baseKR_Full_BIT,
512fa4e4b7SAndrew Lunn 	ETHTOOL_LINK_MODE_50000baseSR_Full_BIT,
522fa4e4b7SAndrew Lunn 	ETHTOOL_LINK_MODE_50000baseCR_Full_BIT,
532fa4e4b7SAndrew Lunn 	ETHTOOL_LINK_MODE_50000baseLR_ER_FR_Full_BIT,
542fa4e4b7SAndrew Lunn 	ETHTOOL_LINK_MODE_50000baseDR_Full_BIT,
552fa4e4b7SAndrew Lunn 	ETHTOOL_LINK_MODE_100000baseKR4_Full_BIT,
562fa4e4b7SAndrew Lunn 	ETHTOOL_LINK_MODE_100000baseSR4_Full_BIT,
572fa4e4b7SAndrew Lunn 	ETHTOOL_LINK_MODE_100000baseCR4_Full_BIT,
582fa4e4b7SAndrew Lunn 	ETHTOOL_LINK_MODE_100000baseLR4_ER4_Full_BIT,
592fa4e4b7SAndrew Lunn 	ETHTOOL_LINK_MODE_100000baseKR2_Full_BIT,
602fa4e4b7SAndrew Lunn 	ETHTOOL_LINK_MODE_100000baseSR2_Full_BIT,
612fa4e4b7SAndrew Lunn 	ETHTOOL_LINK_MODE_100000baseCR2_Full_BIT,
622fa4e4b7SAndrew Lunn 	ETHTOOL_LINK_MODE_100000baseLR2_ER2_FR2_Full_BIT,
632fa4e4b7SAndrew Lunn 	ETHTOOL_LINK_MODE_100000baseDR2_Full_BIT,
642fa4e4b7SAndrew Lunn 	__ETHTOOL_LINK_MODE_MASK_NBITS,
652fa4e4b7SAndrew Lunn };
662fa4e4b7SAndrew Lunn 
67af8de1e3SJiawen Wu static const int xpcs_10gbaser_features[] = {
68af8de1e3SJiawen Wu 	ETHTOOL_LINK_MODE_Pause_BIT,
69af8de1e3SJiawen Wu 	ETHTOOL_LINK_MODE_Asym_Pause_BIT,
70af8de1e3SJiawen Wu 	ETHTOOL_LINK_MODE_10000baseSR_Full_BIT,
71af8de1e3SJiawen Wu 	ETHTOOL_LINK_MODE_10000baseLR_Full_BIT,
72af8de1e3SJiawen Wu 	ETHTOOL_LINK_MODE_10000baseLRM_Full_BIT,
73af8de1e3SJiawen Wu 	ETHTOOL_LINK_MODE_10000baseER_Full_BIT,
74af8de1e3SJiawen Wu 	__ETHTOOL_LINK_MODE_MASK_NBITS,
75af8de1e3SJiawen Wu };
76af8de1e3SJiawen Wu 
77b97b5331SOng Boon Leong static const int xpcs_sgmii_features[] = {
78849d2f83SWong Vee Khee 	ETHTOOL_LINK_MODE_Pause_BIT,
79849d2f83SWong Vee Khee 	ETHTOOL_LINK_MODE_Asym_Pause_BIT,
80849d2f83SWong Vee Khee 	ETHTOOL_LINK_MODE_Autoneg_BIT,
81b97b5331SOng Boon Leong 	ETHTOOL_LINK_MODE_10baseT_Half_BIT,
82b97b5331SOng Boon Leong 	ETHTOOL_LINK_MODE_10baseT_Full_BIT,
83b97b5331SOng Boon Leong 	ETHTOOL_LINK_MODE_100baseT_Half_BIT,
84b97b5331SOng Boon Leong 	ETHTOOL_LINK_MODE_100baseT_Full_BIT,
85b97b5331SOng Boon Leong 	ETHTOOL_LINK_MODE_1000baseT_Half_BIT,
86b97b5331SOng Boon Leong 	ETHTOOL_LINK_MODE_1000baseT_Full_BIT,
87b97b5331SOng Boon Leong 	__ETHTOOL_LINK_MODE_MASK_NBITS,
88b97b5331SOng Boon Leong };
89b97b5331SOng Boon Leong 
90b47aec88SOng Boon Leong static const int xpcs_1000basex_features[] = {
91b47aec88SOng Boon Leong 	ETHTOOL_LINK_MODE_Pause_BIT,
92b47aec88SOng Boon Leong 	ETHTOOL_LINK_MODE_Asym_Pause_BIT,
93b47aec88SOng Boon Leong 	ETHTOOL_LINK_MODE_Autoneg_BIT,
94b47aec88SOng Boon Leong 	ETHTOOL_LINK_MODE_1000baseX_Full_BIT,
95b47aec88SOng Boon Leong 	__ETHTOOL_LINK_MODE_MASK_NBITS,
96b47aec88SOng Boon Leong };
97b47aec88SOng Boon Leong 
98f27abde3SVoon Weifeng static const int xpcs_2500basex_features[] = {
99849d2f83SWong Vee Khee 	ETHTOOL_LINK_MODE_Pause_BIT,
100f27abde3SVoon Weifeng 	ETHTOOL_LINK_MODE_Asym_Pause_BIT,
101f27abde3SVoon Weifeng 	ETHTOOL_LINK_MODE_Autoneg_BIT,
102f27abde3SVoon Weifeng 	ETHTOOL_LINK_MODE_2500baseX_Full_BIT,
103f27abde3SVoon Weifeng 	ETHTOOL_LINK_MODE_2500baseT_Full_BIT,
104f27abde3SVoon Weifeng 	__ETHTOOL_LINK_MODE_MASK_NBITS,
105f27abde3SVoon Weifeng };
106f27abde3SVoon Weifeng 
1072fa4e4b7SAndrew Lunn static const phy_interface_t xpcs_usxgmii_interfaces[] = {
1082fa4e4b7SAndrew Lunn 	PHY_INTERFACE_MODE_USXGMII,
1092fa4e4b7SAndrew Lunn };
1102fa4e4b7SAndrew Lunn 
1112fa4e4b7SAndrew Lunn static const phy_interface_t xpcs_10gkr_interfaces[] = {
1122fa4e4b7SAndrew Lunn 	PHY_INTERFACE_MODE_10GKR,
1132fa4e4b7SAndrew Lunn };
1142fa4e4b7SAndrew Lunn 
1152fa4e4b7SAndrew Lunn static const phy_interface_t xpcs_xlgmii_interfaces[] = {
1162fa4e4b7SAndrew Lunn 	PHY_INTERFACE_MODE_XLGMII,
1172fa4e4b7SAndrew Lunn };
1182fa4e4b7SAndrew Lunn 
119af8de1e3SJiawen Wu static const phy_interface_t xpcs_10gbaser_interfaces[] = {
120af8de1e3SJiawen Wu 	PHY_INTERFACE_MODE_10GBASER,
121af8de1e3SJiawen Wu };
122af8de1e3SJiawen Wu 
123b97b5331SOng Boon Leong static const phy_interface_t xpcs_sgmii_interfaces[] = {
124b97b5331SOng Boon Leong 	PHY_INTERFACE_MODE_SGMII,
125b97b5331SOng Boon Leong };
126b97b5331SOng Boon Leong 
127b47aec88SOng Boon Leong static const phy_interface_t xpcs_1000basex_interfaces[] = {
128b47aec88SOng Boon Leong 	PHY_INTERFACE_MODE_1000BASEX,
129b47aec88SOng Boon Leong };
130b47aec88SOng Boon Leong 
131f27abde3SVoon Weifeng static const phy_interface_t xpcs_2500basex_interfaces[] = {
132f27abde3SVoon Weifeng 	PHY_INTERFACE_MODE_2500BASEX,
133f27abde3SVoon Weifeng 	PHY_INTERFACE_MODE_MAX,
134f27abde3SVoon Weifeng };
135f27abde3SVoon Weifeng 
136a54a8b71SVladimir Oltean enum {
137a54a8b71SVladimir Oltean 	DW_XPCS_USXGMII,
138a54a8b71SVladimir Oltean 	DW_XPCS_10GKR,
139a54a8b71SVladimir Oltean 	DW_XPCS_XLGMII,
140af8de1e3SJiawen Wu 	DW_XPCS_10GBASER,
141a54a8b71SVladimir Oltean 	DW_XPCS_SGMII,
142b47aec88SOng Boon Leong 	DW_XPCS_1000BASEX,
143f27abde3SVoon Weifeng 	DW_XPCS_2500BASEX,
144a54a8b71SVladimir Oltean 	DW_XPCS_INTERFACE_MAX,
145a54a8b71SVladimir Oltean };
146a54a8b71SVladimir Oltean 
147a54a8b71SVladimir Oltean struct xpcs_compat {
1482fa4e4b7SAndrew Lunn 	const int *supported;
1492fa4e4b7SAndrew Lunn 	const phy_interface_t *interface;
150a54a8b71SVladimir Oltean 	int num_interfaces;
15107a4bc51SOng Boon Leong 	int an_mode;
152dd0721eaSVladimir Oltean 	int (*pma_config)(struct dw_xpcs *xpcs);
153a54a8b71SVladimir Oltean };
154a54a8b71SVladimir Oltean 
155a54a8b71SVladimir Oltean struct xpcs_id {
156a54a8b71SVladimir Oltean 	u32 id;
157a54a8b71SVladimir Oltean 	u32 mask;
158a54a8b71SVladimir Oltean 	const struct xpcs_compat *compat;
1592fa4e4b7SAndrew Lunn };
1602fa4e4b7SAndrew Lunn 
xpcs_find_compat(const struct xpcs_id * id,phy_interface_t interface)1619900074eSVladimir Oltean static const struct xpcs_compat *xpcs_find_compat(const struct xpcs_id *id,
1629900074eSVladimir Oltean 						  phy_interface_t interface)
1639900074eSVladimir Oltean {
1649900074eSVladimir Oltean 	int i, j;
1659900074eSVladimir Oltean 
1669900074eSVladimir Oltean 	for (i = 0; i < DW_XPCS_INTERFACE_MAX; i++) {
1679900074eSVladimir Oltean 		const struct xpcs_compat *compat = &id->compat[i];
1689900074eSVladimir Oltean 
1699900074eSVladimir Oltean 		for (j = 0; j < compat->num_interfaces; j++)
1709900074eSVladimir Oltean 			if (compat->interface[j] == interface)
1719900074eSVladimir Oltean 				return compat;
1729900074eSVladimir Oltean 	}
1739900074eSVladimir Oltean 
1749900074eSVladimir Oltean 	return NULL;
1759900074eSVladimir Oltean }
1769900074eSVladimir Oltean 
xpcs_get_an_mode(struct dw_xpcs * xpcs,phy_interface_t interface)1775673ef86SVladimir Oltean int xpcs_get_an_mode(struct dw_xpcs *xpcs, phy_interface_t interface)
1789900074eSVladimir Oltean {
1799900074eSVladimir Oltean 	const struct xpcs_compat *compat;
1809900074eSVladimir Oltean 
1819900074eSVladimir Oltean 	compat = xpcs_find_compat(xpcs->id, interface);
1829900074eSVladimir Oltean 	if (!compat)
1839900074eSVladimir Oltean 		return -ENODEV;
1849900074eSVladimir Oltean 
1859900074eSVladimir Oltean 	return compat->an_mode;
1869900074eSVladimir Oltean }
1879900074eSVladimir Oltean EXPORT_SYMBOL_GPL(xpcs_get_an_mode);
1889900074eSVladimir Oltean 
__xpcs_linkmode_supported(const struct xpcs_compat * compat,enum ethtool_link_mode_bit_indices linkmode)1899900074eSVladimir Oltean static bool __xpcs_linkmode_supported(const struct xpcs_compat *compat,
1909900074eSVladimir Oltean 				      enum ethtool_link_mode_bit_indices linkmode)
1919900074eSVladimir Oltean {
1929900074eSVladimir Oltean 	int i;
1939900074eSVladimir Oltean 
1949900074eSVladimir Oltean 	for (i = 0; compat->supported[i] != __ETHTOOL_LINK_MODE_MASK_NBITS; i++)
1959900074eSVladimir Oltean 		if (compat->supported[i] == linkmode)
1969900074eSVladimir Oltean 			return true;
1979900074eSVladimir Oltean 
1989900074eSVladimir Oltean 	return false;
1999900074eSVladimir Oltean }
2009900074eSVladimir Oltean 
2019900074eSVladimir Oltean #define xpcs_linkmode_supported(compat, mode) \
2029900074eSVladimir Oltean 	__xpcs_linkmode_supported(compat, ETHTOOL_LINK_MODE_ ## mode ## _BIT)
2039900074eSVladimir Oltean 
xpcs_read(struct dw_xpcs * xpcs,int dev,u32 reg)204dd0721eaSVladimir Oltean int xpcs_read(struct dw_xpcs *xpcs, int dev, u32 reg)
2052fa4e4b7SAndrew Lunn {
20685a2b4acSRussell King (Oracle) 	return mdiodev_c45_read(xpcs->mdiodev, dev, reg);
2072fa4e4b7SAndrew Lunn }
2082fa4e4b7SAndrew Lunn 
xpcs_write(struct dw_xpcs * xpcs,int dev,u32 reg,u16 val)209dd0721eaSVladimir Oltean int xpcs_write(struct dw_xpcs *xpcs, int dev, u32 reg, u16 val)
2102fa4e4b7SAndrew Lunn {
21185a2b4acSRussell King (Oracle) 	return mdiodev_c45_write(xpcs->mdiodev, dev, reg, val);
2122fa4e4b7SAndrew Lunn }
2132fa4e4b7SAndrew Lunn 
xpcs_modify_changed(struct dw_xpcs * xpcs,int dev,u32 reg,u16 mask,u16 set)214b47aec88SOng Boon Leong static int xpcs_modify_changed(struct dw_xpcs *xpcs, int dev, u32 reg,
215b47aec88SOng Boon Leong 			       u16 mask, u16 set)
216b47aec88SOng Boon Leong {
2173a65e5f9SAndrew Lunn 	return mdiodev_c45_modify_changed(xpcs->mdiodev, dev, reg, mask, set);
218b47aec88SOng Boon Leong }
219b47aec88SOng Boon Leong 
xpcs_read_vendor(struct dw_xpcs * xpcs,int dev,u32 reg)2205673ef86SVladimir Oltean static int xpcs_read_vendor(struct dw_xpcs *xpcs, int dev, u32 reg)
2212fa4e4b7SAndrew Lunn {
2222fa4e4b7SAndrew Lunn 	return xpcs_read(xpcs, dev, DW_VENDOR | reg);
2232fa4e4b7SAndrew Lunn }
2242fa4e4b7SAndrew Lunn 
xpcs_write_vendor(struct dw_xpcs * xpcs,int dev,int reg,u16 val)2255673ef86SVladimir Oltean static int xpcs_write_vendor(struct dw_xpcs *xpcs, int dev, int reg,
2262fa4e4b7SAndrew Lunn 			     u16 val)
2272fa4e4b7SAndrew Lunn {
2282fa4e4b7SAndrew Lunn 	return xpcs_write(xpcs, dev, DW_VENDOR | reg, val);
2292fa4e4b7SAndrew Lunn }
2302fa4e4b7SAndrew Lunn 
xpcs_read_vpcs(struct dw_xpcs * xpcs,int reg)231f629acc6SJiawen Wu int xpcs_read_vpcs(struct dw_xpcs *xpcs, int reg)
2322fa4e4b7SAndrew Lunn {
2332fa4e4b7SAndrew Lunn 	return xpcs_read_vendor(xpcs, MDIO_MMD_PCS, reg);
2342fa4e4b7SAndrew Lunn }
2352fa4e4b7SAndrew Lunn 
xpcs_write_vpcs(struct dw_xpcs * xpcs,int reg,u16 val)236f629acc6SJiawen Wu int xpcs_write_vpcs(struct dw_xpcs *xpcs, int reg, u16 val)
2372fa4e4b7SAndrew Lunn {
2382fa4e4b7SAndrew Lunn 	return xpcs_write_vendor(xpcs, MDIO_MMD_PCS, reg, val);
2392fa4e4b7SAndrew Lunn }
2402fa4e4b7SAndrew Lunn 
xpcs_dev_flag(struct dw_xpcs * xpcs)241d55595f0SJiawen Wu static int xpcs_dev_flag(struct dw_xpcs *xpcs)
242d55595f0SJiawen Wu {
243d55595f0SJiawen Wu 	int ret, oui;
244d55595f0SJiawen Wu 
245d55595f0SJiawen Wu 	ret = xpcs_read(xpcs, MDIO_MMD_PMAPMD, MDIO_DEVID1);
246d55595f0SJiawen Wu 	if (ret < 0)
247d55595f0SJiawen Wu 		return ret;
248d55595f0SJiawen Wu 
249d55595f0SJiawen Wu 	oui = ret;
250d55595f0SJiawen Wu 
251d55595f0SJiawen Wu 	ret = xpcs_read(xpcs, MDIO_MMD_PMAPMD, MDIO_DEVID2);
252d55595f0SJiawen Wu 	if (ret < 0)
253d55595f0SJiawen Wu 		return ret;
254d55595f0SJiawen Wu 
255d55595f0SJiawen Wu 	ret = (ret >> 10) & 0x3F;
256d55595f0SJiawen Wu 	oui |= ret << 16;
257d55595f0SJiawen Wu 
258d55595f0SJiawen Wu 	if (oui == DW_OUI_WX)
259d55595f0SJiawen Wu 		xpcs->dev_flag = DW_DEV_TXGBE;
260d55595f0SJiawen Wu 
261d55595f0SJiawen Wu 	return 0;
262d55595f0SJiawen Wu }
263d55595f0SJiawen Wu 
xpcs_poll_reset(struct dw_xpcs * xpcs,int dev)2645673ef86SVladimir Oltean static int xpcs_poll_reset(struct dw_xpcs *xpcs, int dev)
2652fa4e4b7SAndrew Lunn {
2662fa4e4b7SAndrew Lunn 	/* Poll until the reset bit clears (50ms per retry == 0.6 sec) */
2672fa4e4b7SAndrew Lunn 	unsigned int retries = 12;
2682fa4e4b7SAndrew Lunn 	int ret;
2692fa4e4b7SAndrew Lunn 
2702fa4e4b7SAndrew Lunn 	do {
2712fa4e4b7SAndrew Lunn 		msleep(50);
2722fa4e4b7SAndrew Lunn 		ret = xpcs_read(xpcs, dev, MDIO_CTRL1);
2732fa4e4b7SAndrew Lunn 		if (ret < 0)
2742fa4e4b7SAndrew Lunn 			return ret;
2752fa4e4b7SAndrew Lunn 	} while (ret & MDIO_CTRL1_RESET && --retries);
2762fa4e4b7SAndrew Lunn 
2772fa4e4b7SAndrew Lunn 	return (ret & MDIO_CTRL1_RESET) ? -ETIMEDOUT : 0;
2782fa4e4b7SAndrew Lunn }
2792fa4e4b7SAndrew Lunn 
xpcs_soft_reset(struct dw_xpcs * xpcs,const struct xpcs_compat * compat)2805673ef86SVladimir Oltean static int xpcs_soft_reset(struct dw_xpcs *xpcs,
2819900074eSVladimir Oltean 			   const struct xpcs_compat *compat)
2822fa4e4b7SAndrew Lunn {
28307a4bc51SOng Boon Leong 	int ret, dev;
28407a4bc51SOng Boon Leong 
2859900074eSVladimir Oltean 	switch (compat->an_mode) {
28607a4bc51SOng Boon Leong 	case DW_AN_C73:
287af8de1e3SJiawen Wu 	case DW_10GBASER:
28807a4bc51SOng Boon Leong 		dev = MDIO_MMD_PCS;
28907a4bc51SOng Boon Leong 		break;
290b97b5331SOng Boon Leong 	case DW_AN_C37_SGMII:
291f27abde3SVoon Weifeng 	case DW_2500BASEX:
292b47aec88SOng Boon Leong 	case DW_AN_C37_1000BASEX:
293b97b5331SOng Boon Leong 		dev = MDIO_MMD_VEND2;
294b97b5331SOng Boon Leong 		break;
29507a4bc51SOng Boon Leong 	default:
296*9470114dSSerge Semin 		return -EINVAL;
29707a4bc51SOng Boon Leong 	}
2982fa4e4b7SAndrew Lunn 
2992fa4e4b7SAndrew Lunn 	ret = xpcs_write(xpcs, dev, MDIO_CTRL1, MDIO_CTRL1_RESET);
3002fa4e4b7SAndrew Lunn 	if (ret < 0)
3012fa4e4b7SAndrew Lunn 		return ret;
3022fa4e4b7SAndrew Lunn 
3032fa4e4b7SAndrew Lunn 	return xpcs_poll_reset(xpcs, dev);
3042fa4e4b7SAndrew Lunn }
3052fa4e4b7SAndrew Lunn 
3062fa4e4b7SAndrew Lunn #define xpcs_warn(__xpcs, __state, __args...) \
3072fa4e4b7SAndrew Lunn ({ \
3082fa4e4b7SAndrew Lunn 	if ((__state)->link) \
3092cac15daSVladimir Oltean 		dev_warn(&(__xpcs)->mdiodev->dev, ##__args); \
3102fa4e4b7SAndrew Lunn })
3112fa4e4b7SAndrew Lunn 
xpcs_read_fault_c73(struct dw_xpcs * xpcs,struct phylink_link_state * state,u16 pcs_stat1)3125673ef86SVladimir Oltean static int xpcs_read_fault_c73(struct dw_xpcs *xpcs,
313883a98edSRussell King (Oracle) 			       struct phylink_link_state *state,
314883a98edSRussell King (Oracle) 			       u16 pcs_stat1)
3152fa4e4b7SAndrew Lunn {
3162fa4e4b7SAndrew Lunn 	int ret;
3172fa4e4b7SAndrew Lunn 
318883a98edSRussell King (Oracle) 	if (pcs_stat1 & MDIO_STAT1_FAULT) {
3192fa4e4b7SAndrew Lunn 		xpcs_warn(xpcs, state, "Link fault condition detected!\n");
3202fa4e4b7SAndrew Lunn 		return -EFAULT;
3212fa4e4b7SAndrew Lunn 	}
3222fa4e4b7SAndrew Lunn 
3232fa4e4b7SAndrew Lunn 	ret = xpcs_read(xpcs, MDIO_MMD_PCS, MDIO_STAT2);
3242fa4e4b7SAndrew Lunn 	if (ret < 0)
3252fa4e4b7SAndrew Lunn 		return ret;
3262fa4e4b7SAndrew Lunn 
3272fa4e4b7SAndrew Lunn 	if (ret & MDIO_STAT2_RXFAULT)
3282fa4e4b7SAndrew Lunn 		xpcs_warn(xpcs, state, "Receiver fault detected!\n");
3292fa4e4b7SAndrew Lunn 	if (ret & MDIO_STAT2_TXFAULT)
3302fa4e4b7SAndrew Lunn 		xpcs_warn(xpcs, state, "Transmitter fault detected!\n");
3312fa4e4b7SAndrew Lunn 
3322fa4e4b7SAndrew Lunn 	ret = xpcs_read_vendor(xpcs, MDIO_MMD_PCS, DW_VR_XS_PCS_DIG_STS);
3332fa4e4b7SAndrew Lunn 	if (ret < 0)
3342fa4e4b7SAndrew Lunn 		return ret;
3352fa4e4b7SAndrew Lunn 
3362fa4e4b7SAndrew Lunn 	if (ret & DW_RXFIFO_ERR) {
3372fa4e4b7SAndrew Lunn 		xpcs_warn(xpcs, state, "FIFO fault condition detected!\n");
3382fa4e4b7SAndrew Lunn 		return -EFAULT;
3392fa4e4b7SAndrew Lunn 	}
3402fa4e4b7SAndrew Lunn 
3412fa4e4b7SAndrew Lunn 	ret = xpcs_read(xpcs, MDIO_MMD_PCS, MDIO_PCS_10GBRT_STAT1);
3422fa4e4b7SAndrew Lunn 	if (ret < 0)
3432fa4e4b7SAndrew Lunn 		return ret;
3442fa4e4b7SAndrew Lunn 
3452fa4e4b7SAndrew Lunn 	if (!(ret & MDIO_PCS_10GBRT_STAT1_BLKLK))
3462fa4e4b7SAndrew Lunn 		xpcs_warn(xpcs, state, "Link is not locked!\n");
3472fa4e4b7SAndrew Lunn 
3482fa4e4b7SAndrew Lunn 	ret = xpcs_read(xpcs, MDIO_MMD_PCS, MDIO_PCS_10GBRT_STAT2);
3492fa4e4b7SAndrew Lunn 	if (ret < 0)
3502fa4e4b7SAndrew Lunn 		return ret;
3512fa4e4b7SAndrew Lunn 
3522fa4e4b7SAndrew Lunn 	if (ret & MDIO_PCS_10GBRT_STAT2_ERR) {
3532fa4e4b7SAndrew Lunn 		xpcs_warn(xpcs, state, "Link has errors!\n");
3542fa4e4b7SAndrew Lunn 		return -EFAULT;
3552fa4e4b7SAndrew Lunn 	}
3562fa4e4b7SAndrew Lunn 
3572fa4e4b7SAndrew Lunn 	return 0;
3582fa4e4b7SAndrew Lunn }
3592fa4e4b7SAndrew Lunn 
xpcs_config_usxgmii(struct dw_xpcs * xpcs,int speed)3605673ef86SVladimir Oltean static void xpcs_config_usxgmii(struct dw_xpcs *xpcs, int speed)
3612fa4e4b7SAndrew Lunn {
3622fa4e4b7SAndrew Lunn 	int ret, speed_sel;
3632fa4e4b7SAndrew Lunn 
3642fa4e4b7SAndrew Lunn 	switch (speed) {
3652fa4e4b7SAndrew Lunn 	case SPEED_10:
3662fa4e4b7SAndrew Lunn 		speed_sel = DW_USXGMII_10;
3672fa4e4b7SAndrew Lunn 		break;
3682fa4e4b7SAndrew Lunn 	case SPEED_100:
3692fa4e4b7SAndrew Lunn 		speed_sel = DW_USXGMII_100;
3702fa4e4b7SAndrew Lunn 		break;
3712fa4e4b7SAndrew Lunn 	case SPEED_1000:
3722fa4e4b7SAndrew Lunn 		speed_sel = DW_USXGMII_1000;
3732fa4e4b7SAndrew Lunn 		break;
3742fa4e4b7SAndrew Lunn 	case SPEED_2500:
3752fa4e4b7SAndrew Lunn 		speed_sel = DW_USXGMII_2500;
3762fa4e4b7SAndrew Lunn 		break;
3772fa4e4b7SAndrew Lunn 	case SPEED_5000:
3782fa4e4b7SAndrew Lunn 		speed_sel = DW_USXGMII_5000;
3792fa4e4b7SAndrew Lunn 		break;
3802fa4e4b7SAndrew Lunn 	case SPEED_10000:
3812fa4e4b7SAndrew Lunn 		speed_sel = DW_USXGMII_10000;
3822fa4e4b7SAndrew Lunn 		break;
3832fa4e4b7SAndrew Lunn 	default:
3842fa4e4b7SAndrew Lunn 		/* Nothing to do here */
38511059740SVladimir Oltean 		return;
3862fa4e4b7SAndrew Lunn 	}
3872fa4e4b7SAndrew Lunn 
3882fa4e4b7SAndrew Lunn 	ret = xpcs_read_vpcs(xpcs, MDIO_CTRL1);
3892fa4e4b7SAndrew Lunn 	if (ret < 0)
39011059740SVladimir Oltean 		goto out;
3912fa4e4b7SAndrew Lunn 
3922fa4e4b7SAndrew Lunn 	ret = xpcs_write_vpcs(xpcs, MDIO_CTRL1, ret | DW_USXGMII_EN);
3932fa4e4b7SAndrew Lunn 	if (ret < 0)
39411059740SVladimir Oltean 		goto out;
3952fa4e4b7SAndrew Lunn 
3962fa4e4b7SAndrew Lunn 	ret = xpcs_read(xpcs, MDIO_MMD_VEND2, MDIO_CTRL1);
3972fa4e4b7SAndrew Lunn 	if (ret < 0)
39811059740SVladimir Oltean 		goto out;
3992fa4e4b7SAndrew Lunn 
4002fa4e4b7SAndrew Lunn 	ret &= ~DW_USXGMII_SS_MASK;
4012fa4e4b7SAndrew Lunn 	ret |= speed_sel | DW_USXGMII_FULL;
4022fa4e4b7SAndrew Lunn 
4032fa4e4b7SAndrew Lunn 	ret = xpcs_write(xpcs, MDIO_MMD_VEND2, MDIO_CTRL1, ret);
4042fa4e4b7SAndrew Lunn 	if (ret < 0)
40511059740SVladimir Oltean 		goto out;
4062fa4e4b7SAndrew Lunn 
4072fa4e4b7SAndrew Lunn 	ret = xpcs_read_vpcs(xpcs, MDIO_CTRL1);
4082fa4e4b7SAndrew Lunn 	if (ret < 0)
40911059740SVladimir Oltean 		goto out;
4102fa4e4b7SAndrew Lunn 
41111059740SVladimir Oltean 	ret = xpcs_write_vpcs(xpcs, MDIO_CTRL1, ret | DW_USXGMII_RST);
41211059740SVladimir Oltean 	if (ret < 0)
41311059740SVladimir Oltean 		goto out;
41411059740SVladimir Oltean 
41511059740SVladimir Oltean 	return;
41611059740SVladimir Oltean 
41711059740SVladimir Oltean out:
41811059740SVladimir Oltean 	pr_err("%s: XPCS access returned %pe\n", __func__, ERR_PTR(ret));
4192fa4e4b7SAndrew Lunn }
4202fa4e4b7SAndrew Lunn 
_xpcs_config_aneg_c73(struct dw_xpcs * xpcs,const struct xpcs_compat * compat)4215673ef86SVladimir Oltean static int _xpcs_config_aneg_c73(struct dw_xpcs *xpcs,
4229900074eSVladimir Oltean 				 const struct xpcs_compat *compat)
4232fa4e4b7SAndrew Lunn {
4242fa4e4b7SAndrew Lunn 	int ret, adv;
4252fa4e4b7SAndrew Lunn 
4262fa4e4b7SAndrew Lunn 	/* By default, in USXGMII mode XPCS operates at 10G baud and
4272fa4e4b7SAndrew Lunn 	 * replicates data to achieve lower speeds. Hereby, in this
4282fa4e4b7SAndrew Lunn 	 * default configuration we need to advertise all supported
4292fa4e4b7SAndrew Lunn 	 * modes and not only the ones we want to use.
4302fa4e4b7SAndrew Lunn 	 */
4312fa4e4b7SAndrew Lunn 
4322fa4e4b7SAndrew Lunn 	/* SR_AN_ADV3 */
4332fa4e4b7SAndrew Lunn 	adv = 0;
4349900074eSVladimir Oltean 	if (xpcs_linkmode_supported(compat, 2500baseX_Full))
4352fa4e4b7SAndrew Lunn 		adv |= DW_C73_2500KX;
4362fa4e4b7SAndrew Lunn 
4372fa4e4b7SAndrew Lunn 	/* TODO: 5000baseKR */
4382fa4e4b7SAndrew Lunn 
4392fa4e4b7SAndrew Lunn 	ret = xpcs_write(xpcs, MDIO_MMD_AN, DW_SR_AN_ADV3, adv);
4402fa4e4b7SAndrew Lunn 	if (ret < 0)
4412fa4e4b7SAndrew Lunn 		return ret;
4422fa4e4b7SAndrew Lunn 
4432fa4e4b7SAndrew Lunn 	/* SR_AN_ADV2 */
4442fa4e4b7SAndrew Lunn 	adv = 0;
4459900074eSVladimir Oltean 	if (xpcs_linkmode_supported(compat, 1000baseKX_Full))
4462fa4e4b7SAndrew Lunn 		adv |= DW_C73_1000KX;
4479900074eSVladimir Oltean 	if (xpcs_linkmode_supported(compat, 10000baseKX4_Full))
4482fa4e4b7SAndrew Lunn 		adv |= DW_C73_10000KX4;
4499900074eSVladimir Oltean 	if (xpcs_linkmode_supported(compat, 10000baseKR_Full))
4502fa4e4b7SAndrew Lunn 		adv |= DW_C73_10000KR;
4512fa4e4b7SAndrew Lunn 
4522fa4e4b7SAndrew Lunn 	ret = xpcs_write(xpcs, MDIO_MMD_AN, DW_SR_AN_ADV2, adv);
4532fa4e4b7SAndrew Lunn 	if (ret < 0)
4542fa4e4b7SAndrew Lunn 		return ret;
4552fa4e4b7SAndrew Lunn 
4562fa4e4b7SAndrew Lunn 	/* SR_AN_ADV1 */
4572fa4e4b7SAndrew Lunn 	adv = DW_C73_AN_ADV_SF;
4589900074eSVladimir Oltean 	if (xpcs_linkmode_supported(compat, Pause))
4592fa4e4b7SAndrew Lunn 		adv |= DW_C73_PAUSE;
4609900074eSVladimir Oltean 	if (xpcs_linkmode_supported(compat, Asym_Pause))
4612fa4e4b7SAndrew Lunn 		adv |= DW_C73_ASYM_PAUSE;
4622fa4e4b7SAndrew Lunn 
4632fa4e4b7SAndrew Lunn 	return xpcs_write(xpcs, MDIO_MMD_AN, DW_SR_AN_ADV1, adv);
4642fa4e4b7SAndrew Lunn }
4652fa4e4b7SAndrew Lunn 
xpcs_config_aneg_c73(struct dw_xpcs * xpcs,const struct xpcs_compat * compat)4665673ef86SVladimir Oltean static int xpcs_config_aneg_c73(struct dw_xpcs *xpcs,
4679900074eSVladimir Oltean 				const struct xpcs_compat *compat)
4682fa4e4b7SAndrew Lunn {
4692fa4e4b7SAndrew Lunn 	int ret;
4702fa4e4b7SAndrew Lunn 
4719900074eSVladimir Oltean 	ret = _xpcs_config_aneg_c73(xpcs, compat);
4722fa4e4b7SAndrew Lunn 	if (ret < 0)
4732fa4e4b7SAndrew Lunn 		return ret;
4742fa4e4b7SAndrew Lunn 
4752fa4e4b7SAndrew Lunn 	ret = xpcs_read(xpcs, MDIO_MMD_AN, MDIO_CTRL1);
4762fa4e4b7SAndrew Lunn 	if (ret < 0)
4772fa4e4b7SAndrew Lunn 		return ret;
4782fa4e4b7SAndrew Lunn 
4792fa4e4b7SAndrew Lunn 	ret |= MDIO_AN_CTRL1_ENABLE | MDIO_AN_CTRL1_RESTART;
4802fa4e4b7SAndrew Lunn 
4812fa4e4b7SAndrew Lunn 	return xpcs_write(xpcs, MDIO_MMD_AN, MDIO_CTRL1, ret);
4822fa4e4b7SAndrew Lunn }
4832fa4e4b7SAndrew Lunn 
xpcs_aneg_done_c73(struct dw_xpcs * xpcs,struct phylink_link_state * state,const struct xpcs_compat * compat,u16 an_stat1)4845673ef86SVladimir Oltean static int xpcs_aneg_done_c73(struct dw_xpcs *xpcs,
4859900074eSVladimir Oltean 			      struct phylink_link_state *state,
486883a98edSRussell King (Oracle) 			      const struct xpcs_compat *compat, u16 an_stat1)
4872fa4e4b7SAndrew Lunn {
4882fa4e4b7SAndrew Lunn 	int ret;
4892fa4e4b7SAndrew Lunn 
490883a98edSRussell King (Oracle) 	if (an_stat1 & MDIO_AN_STAT1_COMPLETE) {
4916f7b89b4SRussell King (Oracle) 		ret = xpcs_read(xpcs, MDIO_MMD_AN, MDIO_AN_LPA);
4922fa4e4b7SAndrew Lunn 		if (ret < 0)
4932fa4e4b7SAndrew Lunn 			return ret;
4942fa4e4b7SAndrew Lunn 
4952fa4e4b7SAndrew Lunn 		/* Check if Aneg outcome is valid */
4962fa4e4b7SAndrew Lunn 		if (!(ret & DW_C73_AN_ADV_SF)) {
4979900074eSVladimir Oltean 			xpcs_config_aneg_c73(xpcs, compat);
4982fa4e4b7SAndrew Lunn 			return 0;
4992fa4e4b7SAndrew Lunn 		}
5002fa4e4b7SAndrew Lunn 
5012fa4e4b7SAndrew Lunn 		return 1;
5022fa4e4b7SAndrew Lunn 	}
5032fa4e4b7SAndrew Lunn 
5042fa4e4b7SAndrew Lunn 	return 0;
5052fa4e4b7SAndrew Lunn }
5062fa4e4b7SAndrew Lunn 
xpcs_read_lpa_c73(struct dw_xpcs * xpcs,struct phylink_link_state * state,u16 an_stat1)5075673ef86SVladimir Oltean static int xpcs_read_lpa_c73(struct dw_xpcs *xpcs,
508883a98edSRussell King (Oracle) 			     struct phylink_link_state *state, u16 an_stat1)
5092fa4e4b7SAndrew Lunn {
5106f7b89b4SRussell King (Oracle) 	u16 lpa[3];
5116f7b89b4SRussell King (Oracle) 	int i, ret;
5122fa4e4b7SAndrew Lunn 
513883a98edSRussell King (Oracle) 	if (!(an_stat1 & MDIO_AN_STAT1_LPABLE)) {
5142fa4e4b7SAndrew Lunn 		phylink_clear(state->lp_advertising, Autoneg);
5152fa4e4b7SAndrew Lunn 		return 0;
5162fa4e4b7SAndrew Lunn 	}
5172fa4e4b7SAndrew Lunn 
5182fa4e4b7SAndrew Lunn 	phylink_set(state->lp_advertising, Autoneg);
5192fa4e4b7SAndrew Lunn 
5206f7b89b4SRussell King (Oracle) 	/* Read Clause 73 link partner advertisement */
5216f7b89b4SRussell King (Oracle) 	for (i = ARRAY_SIZE(lpa); --i >= 0; ) {
5226f7b89b4SRussell King (Oracle) 		ret = xpcs_read(xpcs, MDIO_MMD_AN, MDIO_AN_LPA + i);
5232fa4e4b7SAndrew Lunn 		if (ret < 0)
5242fa4e4b7SAndrew Lunn 			return ret;
5252fa4e4b7SAndrew Lunn 
5266f7b89b4SRussell King (Oracle) 		lpa[i] = ret;
5276f7b89b4SRussell King (Oracle) 	}
5286f7b89b4SRussell King (Oracle) 
5293f0360e0SRussell King (Oracle) 	mii_c73_mod_linkmode(state->lp_advertising, lpa);
5302fa4e4b7SAndrew Lunn 
5312fa4e4b7SAndrew Lunn 	return 0;
5322fa4e4b7SAndrew Lunn }
5332fa4e4b7SAndrew Lunn 
xpcs_get_max_xlgmii_speed(struct dw_xpcs * xpcs,struct phylink_link_state * state)5345673ef86SVladimir Oltean static int xpcs_get_max_xlgmii_speed(struct dw_xpcs *xpcs,
5352fa4e4b7SAndrew Lunn 				     struct phylink_link_state *state)
5362fa4e4b7SAndrew Lunn {
5372fa4e4b7SAndrew Lunn 	unsigned long *adv = state->advertising;
5382fa4e4b7SAndrew Lunn 	int speed = SPEED_UNKNOWN;
5392fa4e4b7SAndrew Lunn 	int bit;
5402fa4e4b7SAndrew Lunn 
5412fa4e4b7SAndrew Lunn 	for_each_set_bit(bit, adv, __ETHTOOL_LINK_MODE_MASK_NBITS) {
5422fa4e4b7SAndrew Lunn 		int new_speed = SPEED_UNKNOWN;
5432fa4e4b7SAndrew Lunn 
5442fa4e4b7SAndrew Lunn 		switch (bit) {
5452fa4e4b7SAndrew Lunn 		case ETHTOOL_LINK_MODE_25000baseCR_Full_BIT:
5462fa4e4b7SAndrew Lunn 		case ETHTOOL_LINK_MODE_25000baseKR_Full_BIT:
5472fa4e4b7SAndrew Lunn 		case ETHTOOL_LINK_MODE_25000baseSR_Full_BIT:
5482fa4e4b7SAndrew Lunn 			new_speed = SPEED_25000;
5492fa4e4b7SAndrew Lunn 			break;
5502fa4e4b7SAndrew Lunn 		case ETHTOOL_LINK_MODE_40000baseKR4_Full_BIT:
5512fa4e4b7SAndrew Lunn 		case ETHTOOL_LINK_MODE_40000baseCR4_Full_BIT:
5522fa4e4b7SAndrew Lunn 		case ETHTOOL_LINK_MODE_40000baseSR4_Full_BIT:
5532fa4e4b7SAndrew Lunn 		case ETHTOOL_LINK_MODE_40000baseLR4_Full_BIT:
5542fa4e4b7SAndrew Lunn 			new_speed = SPEED_40000;
5552fa4e4b7SAndrew Lunn 			break;
5562fa4e4b7SAndrew Lunn 		case ETHTOOL_LINK_MODE_50000baseCR2_Full_BIT:
5572fa4e4b7SAndrew Lunn 		case ETHTOOL_LINK_MODE_50000baseKR2_Full_BIT:
5582fa4e4b7SAndrew Lunn 		case ETHTOOL_LINK_MODE_50000baseSR2_Full_BIT:
5592fa4e4b7SAndrew Lunn 		case ETHTOOL_LINK_MODE_50000baseKR_Full_BIT:
5602fa4e4b7SAndrew Lunn 		case ETHTOOL_LINK_MODE_50000baseSR_Full_BIT:
5612fa4e4b7SAndrew Lunn 		case ETHTOOL_LINK_MODE_50000baseCR_Full_BIT:
5622fa4e4b7SAndrew Lunn 		case ETHTOOL_LINK_MODE_50000baseLR_ER_FR_Full_BIT:
5632fa4e4b7SAndrew Lunn 		case ETHTOOL_LINK_MODE_50000baseDR_Full_BIT:
5642fa4e4b7SAndrew Lunn 			new_speed = SPEED_50000;
5652fa4e4b7SAndrew Lunn 			break;
5662fa4e4b7SAndrew Lunn 		case ETHTOOL_LINK_MODE_100000baseKR4_Full_BIT:
5672fa4e4b7SAndrew Lunn 		case ETHTOOL_LINK_MODE_100000baseSR4_Full_BIT:
5682fa4e4b7SAndrew Lunn 		case ETHTOOL_LINK_MODE_100000baseCR4_Full_BIT:
5692fa4e4b7SAndrew Lunn 		case ETHTOOL_LINK_MODE_100000baseLR4_ER4_Full_BIT:
5702fa4e4b7SAndrew Lunn 		case ETHTOOL_LINK_MODE_100000baseKR2_Full_BIT:
5712fa4e4b7SAndrew Lunn 		case ETHTOOL_LINK_MODE_100000baseSR2_Full_BIT:
5722fa4e4b7SAndrew Lunn 		case ETHTOOL_LINK_MODE_100000baseCR2_Full_BIT:
5732fa4e4b7SAndrew Lunn 		case ETHTOOL_LINK_MODE_100000baseLR2_ER2_FR2_Full_BIT:
5742fa4e4b7SAndrew Lunn 		case ETHTOOL_LINK_MODE_100000baseDR2_Full_BIT:
5752fa4e4b7SAndrew Lunn 			new_speed = SPEED_100000;
5762fa4e4b7SAndrew Lunn 			break;
5772fa4e4b7SAndrew Lunn 		default:
5782fa4e4b7SAndrew Lunn 			continue;
5792fa4e4b7SAndrew Lunn 		}
5802fa4e4b7SAndrew Lunn 
5812fa4e4b7SAndrew Lunn 		if (new_speed > speed)
5822fa4e4b7SAndrew Lunn 			speed = new_speed;
5832fa4e4b7SAndrew Lunn 	}
5842fa4e4b7SAndrew Lunn 
5852fa4e4b7SAndrew Lunn 	return speed;
5862fa4e4b7SAndrew Lunn }
5872fa4e4b7SAndrew Lunn 
xpcs_resolve_pma(struct dw_xpcs * xpcs,struct phylink_link_state * state)5885673ef86SVladimir Oltean static void xpcs_resolve_pma(struct dw_xpcs *xpcs,
5892fa4e4b7SAndrew Lunn 			     struct phylink_link_state *state)
5902fa4e4b7SAndrew Lunn {
5912fa4e4b7SAndrew Lunn 	state->pause = MLO_PAUSE_TX | MLO_PAUSE_RX;
5922fa4e4b7SAndrew Lunn 	state->duplex = DUPLEX_FULL;
5932fa4e4b7SAndrew Lunn 
5942fa4e4b7SAndrew Lunn 	switch (state->interface) {
5952fa4e4b7SAndrew Lunn 	case PHY_INTERFACE_MODE_10GKR:
5962fa4e4b7SAndrew Lunn 		state->speed = SPEED_10000;
5972fa4e4b7SAndrew Lunn 		break;
5982fa4e4b7SAndrew Lunn 	case PHY_INTERFACE_MODE_XLGMII:
5992fa4e4b7SAndrew Lunn 		state->speed = xpcs_get_max_xlgmii_speed(xpcs, state);
6002fa4e4b7SAndrew Lunn 		break;
6012fa4e4b7SAndrew Lunn 	default:
6022fa4e4b7SAndrew Lunn 		state->speed = SPEED_UNKNOWN;
6032fa4e4b7SAndrew Lunn 		break;
6042fa4e4b7SAndrew Lunn 	}
6052fa4e4b7SAndrew Lunn }
6062fa4e4b7SAndrew Lunn 
xpcs_validate(struct phylink_pcs * pcs,unsigned long * supported,const struct phylink_link_state * state)607fe70fb74SRussell King (Oracle) static int xpcs_validate(struct phylink_pcs *pcs, unsigned long *supported,
608fe70fb74SRussell King (Oracle) 			 const struct phylink_link_state *state)
6092fa4e4b7SAndrew Lunn {
610fe70fb74SRussell King (Oracle) 	__ETHTOOL_DECLARE_LINK_MODE_MASK(xpcs_supported) = { 0, };
6119900074eSVladimir Oltean 	const struct xpcs_compat *compat;
612fe70fb74SRussell King (Oracle) 	struct dw_xpcs *xpcs;
6139900074eSVladimir Oltean 	int i;
6149900074eSVladimir Oltean 
615fe70fb74SRussell King (Oracle) 	xpcs = phylink_pcs_to_xpcs(pcs);
6169900074eSVladimir Oltean 	compat = xpcs_find_compat(xpcs->id, state->interface);
6179900074eSVladimir Oltean 
618fe70fb74SRussell King (Oracle) 	/* Populate the supported link modes for this PHY interface type.
619fe70fb74SRussell King (Oracle) 	 * FIXME: what about the port modes and autoneg bit? This masks
620fe70fb74SRussell King (Oracle) 	 * all those away.
6219900074eSVladimir Oltean 	 */
6229900074eSVladimir Oltean 	if (compat)
6239900074eSVladimir Oltean 		for (i = 0; compat->supported[i] != __ETHTOOL_LINK_MODE_MASK_NBITS; i++)
6249900074eSVladimir Oltean 			set_bit(compat->supported[i], xpcs_supported);
6259900074eSVladimir Oltean 
6269900074eSVladimir Oltean 	linkmode_and(supported, supported, xpcs_supported);
627fe70fb74SRussell King (Oracle) 
628fe70fb74SRussell King (Oracle) 	return 0;
6292fa4e4b7SAndrew Lunn }
6302fa4e4b7SAndrew Lunn 
xpcs_get_interfaces(struct dw_xpcs * xpcs,unsigned long * interfaces)631be6ec5b7SRussell King (Oracle) void xpcs_get_interfaces(struct dw_xpcs *xpcs, unsigned long *interfaces)
632be6ec5b7SRussell King (Oracle) {
633be6ec5b7SRussell King (Oracle) 	int i, j;
634be6ec5b7SRussell King (Oracle) 
635be6ec5b7SRussell King (Oracle) 	for (i = 0; i < DW_XPCS_INTERFACE_MAX; i++) {
636be6ec5b7SRussell King (Oracle) 		const struct xpcs_compat *compat = &xpcs->id->compat[i];
637be6ec5b7SRussell King (Oracle) 
638be6ec5b7SRussell King (Oracle) 		for (j = 0; j < compat->num_interfaces; j++)
639be6ec5b7SRussell King (Oracle) 			if (compat->interface[j] < PHY_INTERFACE_MODE_MAX)
640be6ec5b7SRussell King (Oracle) 				__set_bit(compat->interface[j], interfaces);
641be6ec5b7SRussell King (Oracle) 	}
642be6ec5b7SRussell King (Oracle) }
643be6ec5b7SRussell King (Oracle) EXPORT_SYMBOL_GPL(xpcs_get_interfaces);
644be6ec5b7SRussell King (Oracle) 
xpcs_config_eee(struct dw_xpcs * xpcs,int mult_fact_100ns,int enable)6455673ef86SVladimir Oltean int xpcs_config_eee(struct dw_xpcs *xpcs, int mult_fact_100ns, int enable)
6467617af3dSMichael Sit Wei Hong {
6477617af3dSMichael Sit Wei Hong 	int ret;
6487617af3dSMichael Sit Wei Hong 
649590df78bSWong Vee Khee 	ret = xpcs_read(xpcs, MDIO_MMD_VEND2, DW_VR_MII_EEE_MCTRL0);
650590df78bSWong Vee Khee 	if (ret < 0)
651590df78bSWong Vee Khee 		return ret;
652590df78bSWong Vee Khee 
6537617af3dSMichael Sit Wei Hong 	if (enable) {
6547617af3dSMichael Sit Wei Hong 	/* Enable EEE */
6557617af3dSMichael Sit Wei Hong 		ret = DW_VR_MII_EEE_LTX_EN | DW_VR_MII_EEE_LRX_EN |
6567617af3dSMichael Sit Wei Hong 		      DW_VR_MII_EEE_TX_QUIET_EN | DW_VR_MII_EEE_RX_QUIET_EN |
6577617af3dSMichael Sit Wei Hong 		      DW_VR_MII_EEE_TX_EN_CTRL | DW_VR_MII_EEE_RX_EN_CTRL |
6587617af3dSMichael Sit Wei Hong 		      mult_fact_100ns << DW_VR_MII_EEE_MULT_FACT_100NS_SHIFT;
6597617af3dSMichael Sit Wei Hong 	} else {
6607617af3dSMichael Sit Wei Hong 		ret &= ~(DW_VR_MII_EEE_LTX_EN | DW_VR_MII_EEE_LRX_EN |
6617617af3dSMichael Sit Wei Hong 		       DW_VR_MII_EEE_TX_QUIET_EN | DW_VR_MII_EEE_RX_QUIET_EN |
6627617af3dSMichael Sit Wei Hong 		       DW_VR_MII_EEE_TX_EN_CTRL | DW_VR_MII_EEE_RX_EN_CTRL |
6637617af3dSMichael Sit Wei Hong 		       DW_VR_MII_EEE_MULT_FACT_100NS);
6647617af3dSMichael Sit Wei Hong 	}
6657617af3dSMichael Sit Wei Hong 
6667617af3dSMichael Sit Wei Hong 	ret = xpcs_write(xpcs, MDIO_MMD_VEND2, DW_VR_MII_EEE_MCTRL0, ret);
6677617af3dSMichael Sit Wei Hong 	if (ret < 0)
6687617af3dSMichael Sit Wei Hong 		return ret;
6697617af3dSMichael Sit Wei Hong 
6707617af3dSMichael Sit Wei Hong 	ret = xpcs_read(xpcs, MDIO_MMD_VEND2, DW_VR_MII_EEE_MCTRL1);
6717617af3dSMichael Sit Wei Hong 	if (ret < 0)
6727617af3dSMichael Sit Wei Hong 		return ret;
6737617af3dSMichael Sit Wei Hong 
674590df78bSWong Vee Khee 	if (enable)
6757617af3dSMichael Sit Wei Hong 		ret |= DW_VR_MII_EEE_TRN_LPI;
676590df78bSWong Vee Khee 	else
677590df78bSWong Vee Khee 		ret &= ~DW_VR_MII_EEE_TRN_LPI;
678590df78bSWong Vee Khee 
6797617af3dSMichael Sit Wei Hong 	return xpcs_write(xpcs, MDIO_MMD_VEND2, DW_VR_MII_EEE_MCTRL1, ret);
6807617af3dSMichael Sit Wei Hong }
68114b517cbSVladimir Oltean EXPORT_SYMBOL_GPL(xpcs_config_eee);
6827617af3dSMichael Sit Wei Hong 
xpcs_config_aneg_c37_sgmii(struct dw_xpcs * xpcs,unsigned int neg_mode)683a3a47cfbSRussell King (Oracle) static int xpcs_config_aneg_c37_sgmii(struct dw_xpcs *xpcs,
684a3a47cfbSRussell King (Oracle) 				      unsigned int neg_mode)
685b97b5331SOng Boon Leong {
6862a22b7aeSJiawen Wu 	int ret, mdio_ctrl, tx_conf;
6872a22b7aeSJiawen Wu 
6882a22b7aeSJiawen Wu 	if (xpcs->dev_flag == DW_DEV_TXGBE)
6892a22b7aeSJiawen Wu 		xpcs_write_vpcs(xpcs, DW_VR_XS_PCS_DIG_CTRL1, DW_CL37_BP | DW_EN_VSMMD1);
690b97b5331SOng Boon Leong 
691b97b5331SOng Boon Leong 	/* For AN for C37 SGMII mode, the settings are :-
692e3cf002dSWong Vee Khee 	 * 1) VR_MII_MMD_CTRL Bit(12) [AN_ENABLE] = 0b (Disable SGMII AN in case
693e3cf002dSWong Vee Khee 	      it is already enabled)
694e3cf002dSWong Vee Khee 	 * 2) VR_MII_AN_CTRL Bit(2:1)[PCS_MODE] = 10b (SGMII AN)
695e3cf002dSWong Vee Khee 	 * 3) VR_MII_AN_CTRL Bit(3) [TX_CONFIG] = 0b (MAC side SGMII)
696b97b5331SOng Boon Leong 	 *    DW xPCS used with DW EQoS MAC is always MAC side SGMII.
697e3cf002dSWong Vee Khee 	 * 4) VR_MII_DIG_CTRL1 Bit(9) [MAC_AUTO_SW] = 1b (Automatic
698b97b5331SOng Boon Leong 	 *    speed/duplex mode change by HW after SGMII AN complete)
699e3cf002dSWong Vee Khee 	 * 5) VR_MII_MMD_CTRL Bit(12) [AN_ENABLE] = 1b (Enable SGMII AN)
700b97b5331SOng Boon Leong 	 *
701b97b5331SOng Boon Leong 	 * Note: Since it is MAC side SGMII, there is no need to set
702b97b5331SOng Boon Leong 	 *	 SR_MII_AN_ADV. MAC side SGMII receives AN Tx Config from
703b97b5331SOng Boon Leong 	 *	 PHY about the link state change after C28 AN is completed
704b97b5331SOng Boon Leong 	 *	 between PHY and Link Partner. There is also no need to
705b97b5331SOng Boon Leong 	 *	 trigger AN restart for MAC-side SGMII.
706b97b5331SOng Boon Leong 	 */
707e3cf002dSWong Vee Khee 	mdio_ctrl = xpcs_read(xpcs, MDIO_MMD_VEND2, DW_VR_MII_MMD_CTRL);
708e3cf002dSWong Vee Khee 	if (mdio_ctrl < 0)
709e3cf002dSWong Vee Khee 		return mdio_ctrl;
710e3cf002dSWong Vee Khee 
711e3cf002dSWong Vee Khee 	if (mdio_ctrl & AN_CL37_EN) {
712e3cf002dSWong Vee Khee 		ret = xpcs_write(xpcs, MDIO_MMD_VEND2, DW_VR_MII_MMD_CTRL,
713e3cf002dSWong Vee Khee 				 mdio_ctrl & ~AN_CL37_EN);
714e3cf002dSWong Vee Khee 		if (ret < 0)
715e3cf002dSWong Vee Khee 			return ret;
716e3cf002dSWong Vee Khee 	}
717e3cf002dSWong Vee Khee 
718b97b5331SOng Boon Leong 	ret = xpcs_read(xpcs, MDIO_MMD_VEND2, DW_VR_MII_AN_CTRL);
719b97b5331SOng Boon Leong 	if (ret < 0)
720b97b5331SOng Boon Leong 		return ret;
721b97b5331SOng Boon Leong 
722b97b5331SOng Boon Leong 	ret &= ~(DW_VR_MII_PCS_MODE_MASK | DW_VR_MII_TX_CONFIG_MASK);
723b97b5331SOng Boon Leong 	ret |= (DW_VR_MII_PCS_MODE_C37_SGMII <<
724b97b5331SOng Boon Leong 		DW_VR_MII_AN_CTRL_PCS_MODE_SHIFT &
725b97b5331SOng Boon Leong 		DW_VR_MII_PCS_MODE_MASK);
7262a22b7aeSJiawen Wu 	if (xpcs->dev_flag == DW_DEV_TXGBE) {
7272a22b7aeSJiawen Wu 		ret |= DW_VR_MII_AN_CTRL_8BIT;
7282a22b7aeSJiawen Wu 		/* Hardware requires it to be PHY side SGMII */
7292a22b7aeSJiawen Wu 		tx_conf = DW_VR_MII_TX_CONFIG_PHY_SIDE_SGMII;
7302a22b7aeSJiawen Wu 	} else {
7312a22b7aeSJiawen Wu 		tx_conf = DW_VR_MII_TX_CONFIG_MAC_SIDE_SGMII;
7322a22b7aeSJiawen Wu 	}
7332a22b7aeSJiawen Wu 	ret |= tx_conf << DW_VR_MII_AN_CTRL_TX_CONFIG_SHIFT &
7342a22b7aeSJiawen Wu 		DW_VR_MII_TX_CONFIG_MASK;
735b97b5331SOng Boon Leong 	ret = xpcs_write(xpcs, MDIO_MMD_VEND2, DW_VR_MII_AN_CTRL, ret);
736b97b5331SOng Boon Leong 	if (ret < 0)
737b97b5331SOng Boon Leong 		return ret;
738b97b5331SOng Boon Leong 
739b97b5331SOng Boon Leong 	ret = xpcs_read(xpcs, MDIO_MMD_VEND2, DW_VR_MII_DIG_CTRL1);
740b97b5331SOng Boon Leong 	if (ret < 0)
741b97b5331SOng Boon Leong 		return ret;
742b97b5331SOng Boon Leong 
743a3a47cfbSRussell King (Oracle) 	if (neg_mode == PHYLINK_PCS_NEG_INBAND_ENABLED)
744b97b5331SOng Boon Leong 		ret |= DW_VR_MII_DIG_CTRL1_MAC_AUTO_SW;
7452031c09eSVladimir Oltean 	else
7462031c09eSVladimir Oltean 		ret &= ~DW_VR_MII_DIG_CTRL1_MAC_AUTO_SW;
747b97b5331SOng Boon Leong 
7482a22b7aeSJiawen Wu 	if (xpcs->dev_flag == DW_DEV_TXGBE)
7492a22b7aeSJiawen Wu 		ret |= DW_VR_MII_DIG_CTRL1_PHY_MODE_CTRL;
7502a22b7aeSJiawen Wu 
751e3cf002dSWong Vee Khee 	ret = xpcs_write(xpcs, MDIO_MMD_VEND2, DW_VR_MII_DIG_CTRL1, ret);
752e3cf002dSWong Vee Khee 	if (ret < 0)
753e3cf002dSWong Vee Khee 		return ret;
754e3cf002dSWong Vee Khee 
755a3a47cfbSRussell King (Oracle) 	if (neg_mode == PHYLINK_PCS_NEG_INBAND_ENABLED)
756e3cf002dSWong Vee Khee 		ret = xpcs_write(xpcs, MDIO_MMD_VEND2, DW_VR_MII_MMD_CTRL,
757e3cf002dSWong Vee Khee 				 mdio_ctrl | AN_CL37_EN);
758e3cf002dSWong Vee Khee 
759e3cf002dSWong Vee Khee 	return ret;
760b97b5331SOng Boon Leong }
761b97b5331SOng Boon Leong 
xpcs_config_aneg_c37_1000basex(struct dw_xpcs * xpcs,unsigned int neg_mode,const unsigned long * advertising)762a3a47cfbSRussell King (Oracle) static int xpcs_config_aneg_c37_1000basex(struct dw_xpcs *xpcs,
763a3a47cfbSRussell King (Oracle) 					  unsigned int neg_mode,
764b47aec88SOng Boon Leong 					  const unsigned long *advertising)
765b47aec88SOng Boon Leong {
766b47aec88SOng Boon Leong 	phy_interface_t interface = PHY_INTERFACE_MODE_1000BASEX;
767b47aec88SOng Boon Leong 	int ret, mdio_ctrl, adv;
768b47aec88SOng Boon Leong 	bool changed = 0;
769b47aec88SOng Boon Leong 
7702deea43fSJiawen Wu 	if (xpcs->dev_flag == DW_DEV_TXGBE)
7712deea43fSJiawen Wu 		xpcs_write_vpcs(xpcs, DW_VR_XS_PCS_DIG_CTRL1, DW_CL37_BP | DW_EN_VSMMD1);
7722deea43fSJiawen Wu 
773b47aec88SOng Boon Leong 	/* According to Chap 7.12, to set 1000BASE-X C37 AN, AN must
774b47aec88SOng Boon Leong 	 * be disabled first:-
775b47aec88SOng Boon Leong 	 * 1) VR_MII_MMD_CTRL Bit(12)[AN_ENABLE] = 0b
776b47aec88SOng Boon Leong 	 * 2) VR_MII_AN_CTRL Bit(2:1)[PCS_MODE] = 00b (1000BASE-X C37)
777b47aec88SOng Boon Leong 	 */
778b47aec88SOng Boon Leong 	mdio_ctrl = xpcs_read(xpcs, MDIO_MMD_VEND2, DW_VR_MII_MMD_CTRL);
779b47aec88SOng Boon Leong 	if (mdio_ctrl < 0)
780b47aec88SOng Boon Leong 		return mdio_ctrl;
781b47aec88SOng Boon Leong 
782b47aec88SOng Boon Leong 	if (mdio_ctrl & AN_CL37_EN) {
783b47aec88SOng Boon Leong 		ret = xpcs_write(xpcs, MDIO_MMD_VEND2, DW_VR_MII_MMD_CTRL,
784b47aec88SOng Boon Leong 				 mdio_ctrl & ~AN_CL37_EN);
785b47aec88SOng Boon Leong 		if (ret < 0)
786b47aec88SOng Boon Leong 			return ret;
787b47aec88SOng Boon Leong 	}
788b47aec88SOng Boon Leong 
789b47aec88SOng Boon Leong 	ret = xpcs_read(xpcs, MDIO_MMD_VEND2, DW_VR_MII_AN_CTRL);
790b47aec88SOng Boon Leong 	if (ret < 0)
791b47aec88SOng Boon Leong 		return ret;
792b47aec88SOng Boon Leong 
793b47aec88SOng Boon Leong 	ret &= ~DW_VR_MII_PCS_MODE_MASK;
7942deea43fSJiawen Wu 	if (!xpcs->pcs.poll)
7952deea43fSJiawen Wu 		ret |= DW_VR_MII_AN_INTR_EN;
796b47aec88SOng Boon Leong 	ret = xpcs_write(xpcs, MDIO_MMD_VEND2, DW_VR_MII_AN_CTRL, ret);
797b47aec88SOng Boon Leong 	if (ret < 0)
798b47aec88SOng Boon Leong 		return ret;
799b47aec88SOng Boon Leong 
800b47aec88SOng Boon Leong 	/* Check for advertising changes and update the C45 MII ADV
801b47aec88SOng Boon Leong 	 * register accordingly.
802b47aec88SOng Boon Leong 	 */
803b47aec88SOng Boon Leong 	adv = phylink_mii_c22_pcs_encode_advertisement(interface,
804b47aec88SOng Boon Leong 						       advertising);
805b47aec88SOng Boon Leong 	if (adv >= 0) {
806b47aec88SOng Boon Leong 		ret = xpcs_modify_changed(xpcs, MDIO_MMD_VEND2,
807b47aec88SOng Boon Leong 					  MII_ADVERTISE, 0xffff, adv);
808b47aec88SOng Boon Leong 		if (ret < 0)
809b47aec88SOng Boon Leong 			return ret;
810b47aec88SOng Boon Leong 
811b47aec88SOng Boon Leong 		changed = ret;
812b47aec88SOng Boon Leong 	}
813b47aec88SOng Boon Leong 
814b47aec88SOng Boon Leong 	/* Clear CL37 AN complete status */
815b47aec88SOng Boon Leong 	ret = xpcs_write(xpcs, MDIO_MMD_VEND2, DW_VR_MII_AN_INTR_STS, 0);
816b47aec88SOng Boon Leong 	if (ret < 0)
817b47aec88SOng Boon Leong 		return ret;
818b47aec88SOng Boon Leong 
819a3a47cfbSRussell King (Oracle) 	if (neg_mode == PHYLINK_PCS_NEG_INBAND_ENABLED) {
820b47aec88SOng Boon Leong 		ret = xpcs_write(xpcs, MDIO_MMD_VEND2, DW_VR_MII_MMD_CTRL,
821b47aec88SOng Boon Leong 				 mdio_ctrl | AN_CL37_EN);
822b47aec88SOng Boon Leong 		if (ret < 0)
823b47aec88SOng Boon Leong 			return ret;
824b47aec88SOng Boon Leong 	}
825b47aec88SOng Boon Leong 
826b47aec88SOng Boon Leong 	return changed;
827b47aec88SOng Boon Leong }
828b47aec88SOng Boon Leong 
xpcs_config_2500basex(struct dw_xpcs * xpcs)8295673ef86SVladimir Oltean static int xpcs_config_2500basex(struct dw_xpcs *xpcs)
830f27abde3SVoon Weifeng {
831f27abde3SVoon Weifeng 	int ret;
832f27abde3SVoon Weifeng 
833f27abde3SVoon Weifeng 	ret = xpcs_read(xpcs, MDIO_MMD_VEND2, DW_VR_MII_DIG_CTRL1);
834f27abde3SVoon Weifeng 	if (ret < 0)
835f27abde3SVoon Weifeng 		return ret;
836f27abde3SVoon Weifeng 	ret |= DW_VR_MII_DIG_CTRL1_2G5_EN;
837f27abde3SVoon Weifeng 	ret &= ~DW_VR_MII_DIG_CTRL1_MAC_AUTO_SW;
838f27abde3SVoon Weifeng 	ret = xpcs_write(xpcs, MDIO_MMD_VEND2, DW_VR_MII_DIG_CTRL1, ret);
839f27abde3SVoon Weifeng 	if (ret < 0)
840f27abde3SVoon Weifeng 		return ret;
841f27abde3SVoon Weifeng 
842f27abde3SVoon Weifeng 	ret = xpcs_read(xpcs, MDIO_MMD_VEND2, DW_VR_MII_MMD_CTRL);
843f27abde3SVoon Weifeng 	if (ret < 0)
844f27abde3SVoon Weifeng 		return ret;
845f27abde3SVoon Weifeng 	ret &= ~AN_CL37_EN;
846f27abde3SVoon Weifeng 	ret |= SGMII_SPEED_SS6;
847f27abde3SVoon Weifeng 	ret &= ~SGMII_SPEED_SS13;
848f27abde3SVoon Weifeng 	return xpcs_write(xpcs, MDIO_MMD_VEND2, DW_VR_MII_MMD_CTRL, ret);
849f27abde3SVoon Weifeng }
850f27abde3SVoon Weifeng 
xpcs_do_config(struct dw_xpcs * xpcs,phy_interface_t interface,const unsigned long * advertising,unsigned int neg_mode)851a853c68eSVladimir Oltean int xpcs_do_config(struct dw_xpcs *xpcs, phy_interface_t interface,
852a3a47cfbSRussell King (Oracle) 		   const unsigned long *advertising, unsigned int neg_mode)
8532fa4e4b7SAndrew Lunn {
8549900074eSVladimir Oltean 	const struct xpcs_compat *compat;
8552fa4e4b7SAndrew Lunn 	int ret;
8562fa4e4b7SAndrew Lunn 
85711059740SVladimir Oltean 	compat = xpcs_find_compat(xpcs->id, interface);
8589900074eSVladimir Oltean 	if (!compat)
8599900074eSVladimir Oltean 		return -ENODEV;
8609900074eSVladimir Oltean 
861f629acc6SJiawen Wu 	if (xpcs->dev_flag == DW_DEV_TXGBE) {
862f629acc6SJiawen Wu 		ret = txgbe_xpcs_switch_mode(xpcs, interface);
863f629acc6SJiawen Wu 		if (ret)
864f629acc6SJiawen Wu 			return ret;
865f629acc6SJiawen Wu 	}
866f629acc6SJiawen Wu 
8679900074eSVladimir Oltean 	switch (compat->an_mode) {
868af8de1e3SJiawen Wu 	case DW_10GBASER:
869af8de1e3SJiawen Wu 		break;
87007a4bc51SOng Boon Leong 	case DW_AN_C73:
871a3a47cfbSRussell King (Oracle) 		if (neg_mode == PHYLINK_PCS_NEG_INBAND_ENABLED) {
8729900074eSVladimir Oltean 			ret = xpcs_config_aneg_c73(xpcs, compat);
8732fa4e4b7SAndrew Lunn 			if (ret)
8742fa4e4b7SAndrew Lunn 				return ret;
8752fa4e4b7SAndrew Lunn 		}
87607a4bc51SOng Boon Leong 		break;
877b97b5331SOng Boon Leong 	case DW_AN_C37_SGMII:
878a3a47cfbSRussell King (Oracle) 		ret = xpcs_config_aneg_c37_sgmii(xpcs, neg_mode);
879b97b5331SOng Boon Leong 		if (ret)
880b97b5331SOng Boon Leong 			return ret;
881b97b5331SOng Boon Leong 		break;
882b47aec88SOng Boon Leong 	case DW_AN_C37_1000BASEX:
883a3a47cfbSRussell King (Oracle) 		ret = xpcs_config_aneg_c37_1000basex(xpcs, neg_mode,
884b47aec88SOng Boon Leong 						     advertising);
885b47aec88SOng Boon Leong 		if (ret)
886b47aec88SOng Boon Leong 			return ret;
887b47aec88SOng Boon Leong 		break;
888f27abde3SVoon Weifeng 	case DW_2500BASEX:
889f27abde3SVoon Weifeng 		ret = xpcs_config_2500basex(xpcs);
890f27abde3SVoon Weifeng 		if (ret)
891f27abde3SVoon Weifeng 			return ret;
892f27abde3SVoon Weifeng 		break;
89307a4bc51SOng Boon Leong 	default:
894*9470114dSSerge Semin 		return -EINVAL;
89507a4bc51SOng Boon Leong 	}
89607a4bc51SOng Boon Leong 
897dd0721eaSVladimir Oltean 	if (compat->pma_config) {
898dd0721eaSVladimir Oltean 		ret = compat->pma_config(xpcs);
899dd0721eaSVladimir Oltean 		if (ret)
900dd0721eaSVladimir Oltean 			return ret;
901dd0721eaSVladimir Oltean 	}
902dd0721eaSVladimir Oltean 
90307a4bc51SOng Boon Leong 	return 0;
90407a4bc51SOng Boon Leong }
905a853c68eSVladimir Oltean EXPORT_SYMBOL_GPL(xpcs_do_config);
90607a4bc51SOng Boon Leong 
xpcs_config(struct phylink_pcs * pcs,unsigned int neg_mode,phy_interface_t interface,const unsigned long * advertising,bool permit_pause_to_mac)907a3a47cfbSRussell King (Oracle) static int xpcs_config(struct phylink_pcs *pcs, unsigned int neg_mode,
90811059740SVladimir Oltean 		       phy_interface_t interface,
90911059740SVladimir Oltean 		       const unsigned long *advertising,
91011059740SVladimir Oltean 		       bool permit_pause_to_mac)
91111059740SVladimir Oltean {
9125673ef86SVladimir Oltean 	struct dw_xpcs *xpcs = phylink_pcs_to_xpcs(pcs);
91311059740SVladimir Oltean 
914a3a47cfbSRussell King (Oracle) 	return xpcs_do_config(xpcs, interface, advertising, neg_mode);
91511059740SVladimir Oltean }
91611059740SVladimir Oltean 
xpcs_get_state_c73(struct dw_xpcs * xpcs,struct phylink_link_state * state,const struct xpcs_compat * compat)9175673ef86SVladimir Oltean static int xpcs_get_state_c73(struct dw_xpcs *xpcs,
9189900074eSVladimir Oltean 			      struct phylink_link_state *state,
9199900074eSVladimir Oltean 			      const struct xpcs_compat *compat)
92007a4bc51SOng Boon Leong {
921459fd2f1SRussell King (Oracle) 	bool an_enabled;
922883a98edSRussell King (Oracle) 	int pcs_stat1;
923883a98edSRussell King (Oracle) 	int an_stat1;
92407a4bc51SOng Boon Leong 	int ret;
92507a4bc51SOng Boon Leong 
926883a98edSRussell King (Oracle) 	/* The link status bit is latching-low, so it is important to
927883a98edSRussell King (Oracle) 	 * avoid unnecessary re-reads of this register to avoid missing
928883a98edSRussell King (Oracle) 	 * a link-down event.
929883a98edSRussell King (Oracle) 	 */
930883a98edSRussell King (Oracle) 	pcs_stat1 = xpcs_read(xpcs, MDIO_MMD_PCS, MDIO_STAT1);
931883a98edSRussell King (Oracle) 	if (pcs_stat1 < 0) {
932883a98edSRussell King (Oracle) 		state->link = false;
933883a98edSRussell King (Oracle) 		return pcs_stat1;
934883a98edSRussell King (Oracle) 	}
935883a98edSRussell King (Oracle) 
93607a4bc51SOng Boon Leong 	/* Link needs to be read first ... */
937883a98edSRussell King (Oracle) 	state->link = !!(pcs_stat1 & MDIO_STAT1_LSTATUS);
93807a4bc51SOng Boon Leong 
93907a4bc51SOng Boon Leong 	/* ... and then we check the faults. */
940883a98edSRussell King (Oracle) 	ret = xpcs_read_fault_c73(xpcs, state, pcs_stat1);
94107a4bc51SOng Boon Leong 	if (ret) {
9429900074eSVladimir Oltean 		ret = xpcs_soft_reset(xpcs, compat);
94307a4bc51SOng Boon Leong 		if (ret)
94407a4bc51SOng Boon Leong 			return ret;
94507a4bc51SOng Boon Leong 
94607a4bc51SOng Boon Leong 		state->link = 0;
94707a4bc51SOng Boon Leong 
948a3a47cfbSRussell King (Oracle) 		return xpcs_do_config(xpcs, state->interface, NULL,
949a3a47cfbSRussell King (Oracle) 				      PHYLINK_PCS_NEG_INBAND_ENABLED);
95007a4bc51SOng Boon Leong 	}
95107a4bc51SOng Boon Leong 
952883a98edSRussell King (Oracle) 	/* There is no point doing anything else if the link is down. */
953883a98edSRussell King (Oracle) 	if (!state->link)
954883a98edSRussell King (Oracle) 		return 0;
955883a98edSRussell King (Oracle) 
956459fd2f1SRussell King (Oracle) 	an_enabled = linkmode_test_bit(ETHTOOL_LINK_MODE_Autoneg_BIT,
957459fd2f1SRussell King (Oracle) 				       state->advertising);
958883a98edSRussell King (Oracle) 	if (an_enabled) {
959883a98edSRussell King (Oracle) 		/* The link status bit is latching-low, so it is important to
960883a98edSRussell King (Oracle) 		 * avoid unnecessary re-reads of this register to avoid missing
961883a98edSRussell King (Oracle) 		 * a link-down event.
962883a98edSRussell King (Oracle) 		 */
963883a98edSRussell King (Oracle) 		an_stat1 = xpcs_read(xpcs, MDIO_MMD_AN, MDIO_STAT1);
964883a98edSRussell King (Oracle) 		if (an_stat1 < 0) {
965883a98edSRussell King (Oracle) 			state->link = false;
966883a98edSRussell King (Oracle) 			return an_stat1;
967883a98edSRussell King (Oracle) 		}
968883a98edSRussell King (Oracle) 
969883a98edSRussell King (Oracle) 		state->an_complete = xpcs_aneg_done_c73(xpcs, state, compat,
970883a98edSRussell King (Oracle) 							an_stat1);
971883a98edSRussell King (Oracle) 		if (!state->an_complete) {
972883a98edSRussell King (Oracle) 			state->link = false;
973883a98edSRussell King (Oracle) 			return 0;
974883a98edSRussell King (Oracle) 		}
975883a98edSRussell King (Oracle) 
976883a98edSRussell King (Oracle) 		ret = xpcs_read_lpa_c73(xpcs, state, an_stat1);
977883a98edSRussell King (Oracle) 		if (ret < 0) {
978883a98edSRussell King (Oracle) 			state->link = false;
979883a98edSRussell King (Oracle) 			return ret;
980883a98edSRussell King (Oracle) 		}
981883a98edSRussell King (Oracle) 
98221234ef1SRussell King (Oracle) 		phylink_resolve_c73(state);
983883a98edSRussell King (Oracle) 	} else {
98407a4bc51SOng Boon Leong 		xpcs_resolve_pma(xpcs, state);
98507a4bc51SOng Boon Leong 	}
9862fa4e4b7SAndrew Lunn 
9872fa4e4b7SAndrew Lunn 	return 0;
9882fa4e4b7SAndrew Lunn }
9892fa4e4b7SAndrew Lunn 
xpcs_get_state_c37_sgmii(struct dw_xpcs * xpcs,struct phylink_link_state * state)9905673ef86SVladimir Oltean static int xpcs_get_state_c37_sgmii(struct dw_xpcs *xpcs,
991b97b5331SOng Boon Leong 				    struct phylink_link_state *state)
992b97b5331SOng Boon Leong {
993b97b5331SOng Boon Leong 	int ret;
994b97b5331SOng Boon Leong 
995b97b5331SOng Boon Leong 	/* Reset link_state */
996b97b5331SOng Boon Leong 	state->link = false;
997b97b5331SOng Boon Leong 	state->speed = SPEED_UNKNOWN;
998b97b5331SOng Boon Leong 	state->duplex = DUPLEX_UNKNOWN;
999b97b5331SOng Boon Leong 	state->pause = 0;
1000b97b5331SOng Boon Leong 
1001b97b5331SOng Boon Leong 	/* For C37 SGMII mode, we check DW_VR_MII_AN_INTR_STS for link
1002b97b5331SOng Boon Leong 	 * status, speed and duplex.
1003b97b5331SOng Boon Leong 	 */
1004b97b5331SOng Boon Leong 	ret = xpcs_read(xpcs, MDIO_MMD_VEND2, DW_VR_MII_AN_INTR_STS);
1005b97b5331SOng Boon Leong 	if (ret < 0)
100627161db0SVladimir Oltean 		return ret;
1007b97b5331SOng Boon Leong 
1008b97b5331SOng Boon Leong 	if (ret & DW_VR_MII_C37_ANSGM_SP_LNKSTS) {
1009b97b5331SOng Boon Leong 		int speed_value;
1010b97b5331SOng Boon Leong 
1011b97b5331SOng Boon Leong 		state->link = true;
1012b97b5331SOng Boon Leong 
1013b97b5331SOng Boon Leong 		speed_value = (ret & DW_VR_MII_AN_STS_C37_ANSGM_SP) >>
1014b97b5331SOng Boon Leong 			      DW_VR_MII_AN_STS_C37_ANSGM_SP_SHIFT;
1015b97b5331SOng Boon Leong 		if (speed_value == DW_VR_MII_C37_ANSGM_SP_1000)
1016b97b5331SOng Boon Leong 			state->speed = SPEED_1000;
1017b97b5331SOng Boon Leong 		else if (speed_value == DW_VR_MII_C37_ANSGM_SP_100)
1018b97b5331SOng Boon Leong 			state->speed = SPEED_100;
1019b97b5331SOng Boon Leong 		else
1020b97b5331SOng Boon Leong 			state->speed = SPEED_10;
1021b97b5331SOng Boon Leong 
1022b97b5331SOng Boon Leong 		if (ret & DW_VR_MII_AN_STS_C37_ANSGM_FD)
1023b97b5331SOng Boon Leong 			state->duplex = DUPLEX_FULL;
1024b97b5331SOng Boon Leong 		else
1025b97b5331SOng Boon Leong 			state->duplex = DUPLEX_HALF;
10262a22b7aeSJiawen Wu 	} else if (ret == DW_VR_MII_AN_STS_C37_ANCMPLT_INTR) {
10272a22b7aeSJiawen Wu 		int speed, duplex;
10282a22b7aeSJiawen Wu 
10292a22b7aeSJiawen Wu 		state->link = true;
10302a22b7aeSJiawen Wu 
10312a22b7aeSJiawen Wu 		speed = xpcs_read(xpcs, MDIO_MMD_VEND2, MDIO_CTRL1);
10322a22b7aeSJiawen Wu 		if (speed < 0)
10332a22b7aeSJiawen Wu 			return speed;
10342a22b7aeSJiawen Wu 
10352a22b7aeSJiawen Wu 		speed &= SGMII_SPEED_SS13 | SGMII_SPEED_SS6;
10362a22b7aeSJiawen Wu 		if (speed == SGMII_SPEED_SS6)
10372a22b7aeSJiawen Wu 			state->speed = SPEED_1000;
10382a22b7aeSJiawen Wu 		else if (speed == SGMII_SPEED_SS13)
10392a22b7aeSJiawen Wu 			state->speed = SPEED_100;
10402a22b7aeSJiawen Wu 		else if (speed == 0)
10412a22b7aeSJiawen Wu 			state->speed = SPEED_10;
10422a22b7aeSJiawen Wu 
10432a22b7aeSJiawen Wu 		duplex = xpcs_read(xpcs, MDIO_MMD_VEND2, MII_ADVERTISE);
10442a22b7aeSJiawen Wu 		if (duplex < 0)
10452a22b7aeSJiawen Wu 			return duplex;
10462a22b7aeSJiawen Wu 
10472a22b7aeSJiawen Wu 		if (duplex & DW_FULL_DUPLEX)
10482a22b7aeSJiawen Wu 			state->duplex = DUPLEX_FULL;
10492a22b7aeSJiawen Wu 		else if (duplex & DW_HALF_DUPLEX)
10502a22b7aeSJiawen Wu 			state->duplex = DUPLEX_HALF;
10512a22b7aeSJiawen Wu 
10522a22b7aeSJiawen Wu 		xpcs_write(xpcs, MDIO_MMD_VEND2, DW_VR_MII_AN_INTR_STS, 0);
1053b97b5331SOng Boon Leong 	}
1054b97b5331SOng Boon Leong 
1055b97b5331SOng Boon Leong 	return 0;
1056b97b5331SOng Boon Leong }
1057b97b5331SOng Boon Leong 
xpcs_get_state_c37_1000basex(struct dw_xpcs * xpcs,struct phylink_link_state * state)1058b47aec88SOng Boon Leong static int xpcs_get_state_c37_1000basex(struct dw_xpcs *xpcs,
1059b47aec88SOng Boon Leong 					struct phylink_link_state *state)
1060b47aec88SOng Boon Leong {
1061b47aec88SOng Boon Leong 	int lpa, bmsr;
1062b47aec88SOng Boon Leong 
1063459fd2f1SRussell King (Oracle) 	if (linkmode_test_bit(ETHTOOL_LINK_MODE_Autoneg_BIT,
1064459fd2f1SRussell King (Oracle) 			      state->advertising)) {
1065b47aec88SOng Boon Leong 		/* Reset link state */
1066b47aec88SOng Boon Leong 		state->link = false;
1067b47aec88SOng Boon Leong 
1068b47aec88SOng Boon Leong 		lpa = xpcs_read(xpcs, MDIO_MMD_VEND2, MII_LPA);
1069b47aec88SOng Boon Leong 		if (lpa < 0 || lpa & LPA_RFAULT)
1070b47aec88SOng Boon Leong 			return lpa;
1071b47aec88SOng Boon Leong 
1072b47aec88SOng Boon Leong 		bmsr = xpcs_read(xpcs, MDIO_MMD_VEND2, MII_BMSR);
1073b47aec88SOng Boon Leong 		if (bmsr < 0)
1074b47aec88SOng Boon Leong 			return bmsr;
1075b47aec88SOng Boon Leong 
10762deea43fSJiawen Wu 		/* Clear AN complete interrupt */
10772deea43fSJiawen Wu 		if (!xpcs->pcs.poll) {
10782deea43fSJiawen Wu 			int an_intr;
10792deea43fSJiawen Wu 
10802deea43fSJiawen Wu 			an_intr = xpcs_read(xpcs, MDIO_MMD_VEND2, DW_VR_MII_AN_INTR_STS);
10812deea43fSJiawen Wu 			if (an_intr & DW_VR_MII_AN_STS_C37_ANCMPLT_INTR) {
10822deea43fSJiawen Wu 				an_intr &= ~DW_VR_MII_AN_STS_C37_ANCMPLT_INTR;
10832deea43fSJiawen Wu 				xpcs_write(xpcs, MDIO_MMD_VEND2, DW_VR_MII_AN_INTR_STS, an_intr);
10842deea43fSJiawen Wu 			}
10852deea43fSJiawen Wu 		}
10862deea43fSJiawen Wu 
1087b47aec88SOng Boon Leong 		phylink_mii_c22_pcs_decode_state(state, bmsr, lpa);
1088b47aec88SOng Boon Leong 	}
1089b47aec88SOng Boon Leong 
1090b47aec88SOng Boon Leong 	return 0;
1091b47aec88SOng Boon Leong }
1092b47aec88SOng Boon Leong 
xpcs_get_state(struct phylink_pcs * pcs,struct phylink_link_state * state)109311059740SVladimir Oltean static void xpcs_get_state(struct phylink_pcs *pcs,
10942fa4e4b7SAndrew Lunn 			   struct phylink_link_state *state)
10952fa4e4b7SAndrew Lunn {
10965673ef86SVladimir Oltean 	struct dw_xpcs *xpcs = phylink_pcs_to_xpcs(pcs);
10979900074eSVladimir Oltean 	const struct xpcs_compat *compat;
10982fa4e4b7SAndrew Lunn 	int ret;
10992fa4e4b7SAndrew Lunn 
11009900074eSVladimir Oltean 	compat = xpcs_find_compat(xpcs->id, state->interface);
11019900074eSVladimir Oltean 	if (!compat)
110211059740SVladimir Oltean 		return;
11039900074eSVladimir Oltean 
11049900074eSVladimir Oltean 	switch (compat->an_mode) {
1105af8de1e3SJiawen Wu 	case DW_10GBASER:
1106af8de1e3SJiawen Wu 		phylink_mii_c45_pcs_get_state(xpcs->mdiodev, state);
1107af8de1e3SJiawen Wu 		break;
110807a4bc51SOng Boon Leong 	case DW_AN_C73:
11099900074eSVladimir Oltean 		ret = xpcs_get_state_c73(xpcs, state, compat);
111011059740SVladimir Oltean 		if (ret) {
111111059740SVladimir Oltean 			pr_err("xpcs_get_state_c73 returned %pe\n",
111211059740SVladimir Oltean 			       ERR_PTR(ret));
111311059740SVladimir Oltean 			return;
111411059740SVladimir Oltean 		}
111507a4bc51SOng Boon Leong 		break;
1116b97b5331SOng Boon Leong 	case DW_AN_C37_SGMII:
1117b97b5331SOng Boon Leong 		ret = xpcs_get_state_c37_sgmii(xpcs, state);
111811059740SVladimir Oltean 		if (ret) {
111911059740SVladimir Oltean 			pr_err("xpcs_get_state_c37_sgmii returned %pe\n",
112011059740SVladimir Oltean 			       ERR_PTR(ret));
112111059740SVladimir Oltean 		}
1122b97b5331SOng Boon Leong 		break;
1123b47aec88SOng Boon Leong 	case DW_AN_C37_1000BASEX:
1124b47aec88SOng Boon Leong 		ret = xpcs_get_state_c37_1000basex(xpcs, state);
1125b47aec88SOng Boon Leong 		if (ret) {
1126b47aec88SOng Boon Leong 			pr_err("xpcs_get_state_c37_1000basex returned %pe\n",
1127b47aec88SOng Boon Leong 			       ERR_PTR(ret));
1128b47aec88SOng Boon Leong 		}
1129b47aec88SOng Boon Leong 		break;
113007a4bc51SOng Boon Leong 	default:
113111059740SVladimir Oltean 		return;
113211059740SVladimir Oltean 	}
11332fa4e4b7SAndrew Lunn }
11342fa4e4b7SAndrew Lunn 
xpcs_link_up_sgmii(struct dw_xpcs * xpcs,unsigned int neg_mode,int speed,int duplex)1135a3a47cfbSRussell King (Oracle) static void xpcs_link_up_sgmii(struct dw_xpcs *xpcs, unsigned int neg_mode,
11362031c09eSVladimir Oltean 			       int speed, int duplex)
11372031c09eSVladimir Oltean {
11382031c09eSVladimir Oltean 	int val, ret;
11392031c09eSVladimir Oltean 
1140a3a47cfbSRussell King (Oracle) 	if (neg_mode == PHYLINK_PCS_NEG_INBAND_ENABLED)
11412031c09eSVladimir Oltean 		return;
11422031c09eSVladimir Oltean 
1143449b7a15SRussell King (Oracle) 	val = mii_bmcr_encode_fixed(speed, duplex);
11442031c09eSVladimir Oltean 	ret = xpcs_write(xpcs, MDIO_MMD_VEND2, MDIO_CTRL1, val);
11452031c09eSVladimir Oltean 	if (ret)
11462031c09eSVladimir Oltean 		pr_err("%s: xpcs_write returned %pe\n", __func__, ERR_PTR(ret));
11472031c09eSVladimir Oltean }
11482031c09eSVladimir Oltean 
xpcs_link_up_1000basex(struct dw_xpcs * xpcs,unsigned int neg_mode,int speed,int duplex)1149a3a47cfbSRussell King (Oracle) static void xpcs_link_up_1000basex(struct dw_xpcs *xpcs, unsigned int neg_mode,
1150b47aec88SOng Boon Leong 				   int speed, int duplex)
1151b47aec88SOng Boon Leong {
1152b47aec88SOng Boon Leong 	int val, ret;
1153b47aec88SOng Boon Leong 
1154a3a47cfbSRussell King (Oracle) 	if (neg_mode == PHYLINK_PCS_NEG_INBAND_ENABLED)
1155b47aec88SOng Boon Leong 		return;
1156b47aec88SOng Boon Leong 
1157b47aec88SOng Boon Leong 	switch (speed) {
1158b47aec88SOng Boon Leong 	case SPEED_1000:
1159b47aec88SOng Boon Leong 		val = BMCR_SPEED1000;
1160b47aec88SOng Boon Leong 		break;
1161b47aec88SOng Boon Leong 	case SPEED_100:
1162b47aec88SOng Boon Leong 	case SPEED_10:
1163b47aec88SOng Boon Leong 	default:
1164b47aec88SOng Boon Leong 		pr_err("%s: speed = %d\n", __func__, speed);
1165b47aec88SOng Boon Leong 		return;
1166b47aec88SOng Boon Leong 	}
1167b47aec88SOng Boon Leong 
1168b47aec88SOng Boon Leong 	if (duplex == DUPLEX_FULL)
1169b47aec88SOng Boon Leong 		val |= BMCR_FULLDPLX;
1170b47aec88SOng Boon Leong 	else
1171b47aec88SOng Boon Leong 		pr_err("%s: half duplex not supported\n", __func__);
1172b47aec88SOng Boon Leong 
1173b47aec88SOng Boon Leong 	ret = xpcs_write(xpcs, MDIO_MMD_VEND2, MDIO_CTRL1, val);
1174b47aec88SOng Boon Leong 	if (ret)
1175b47aec88SOng Boon Leong 		pr_err("%s: xpcs_write returned %pe\n", __func__, ERR_PTR(ret));
1176b47aec88SOng Boon Leong }
1177b47aec88SOng Boon Leong 
xpcs_link_up(struct phylink_pcs * pcs,unsigned int neg_mode,phy_interface_t interface,int speed,int duplex)1178a3a47cfbSRussell King (Oracle) void xpcs_link_up(struct phylink_pcs *pcs, unsigned int neg_mode,
117911059740SVladimir Oltean 		  phy_interface_t interface, int speed, int duplex)
11802fa4e4b7SAndrew Lunn {
11815673ef86SVladimir Oltean 	struct dw_xpcs *xpcs = phylink_pcs_to_xpcs(pcs);
118211059740SVladimir Oltean 
11832fa4e4b7SAndrew Lunn 	if (interface == PHY_INTERFACE_MODE_USXGMII)
11842fa4e4b7SAndrew Lunn 		return xpcs_config_usxgmii(xpcs, speed);
11852031c09eSVladimir Oltean 	if (interface == PHY_INTERFACE_MODE_SGMII)
1186a3a47cfbSRussell King (Oracle) 		return xpcs_link_up_sgmii(xpcs, neg_mode, speed, duplex);
1187b47aec88SOng Boon Leong 	if (interface == PHY_INTERFACE_MODE_1000BASEX)
1188a3a47cfbSRussell King (Oracle) 		return xpcs_link_up_1000basex(xpcs, neg_mode, speed, duplex);
11892fa4e4b7SAndrew Lunn }
1190a853c68eSVladimir Oltean EXPORT_SYMBOL_GPL(xpcs_link_up);
11912fa4e4b7SAndrew Lunn 
xpcs_an_restart(struct phylink_pcs * pcs)1192b47aec88SOng Boon Leong static void xpcs_an_restart(struct phylink_pcs *pcs)
1193b47aec88SOng Boon Leong {
1194b47aec88SOng Boon Leong 	struct dw_xpcs *xpcs = phylink_pcs_to_xpcs(pcs);
1195b47aec88SOng Boon Leong 	int ret;
1196b47aec88SOng Boon Leong 
1197b47aec88SOng Boon Leong 	ret = xpcs_read(xpcs, MDIO_MMD_VEND2, MDIO_CTRL1);
1198b47aec88SOng Boon Leong 	if (ret >= 0) {
1199b47aec88SOng Boon Leong 		ret |= BMCR_ANRESTART;
1200b47aec88SOng Boon Leong 		xpcs_write(xpcs, MDIO_MMD_VEND2, MDIO_CTRL1, ret);
1201b47aec88SOng Boon Leong 	}
1202b47aec88SOng Boon Leong }
1203b47aec88SOng Boon Leong 
xpcs_get_id(struct dw_xpcs * xpcs)12045673ef86SVladimir Oltean static u32 xpcs_get_id(struct dw_xpcs *xpcs)
12052fa4e4b7SAndrew Lunn {
12062fa4e4b7SAndrew Lunn 	int ret;
12072fa4e4b7SAndrew Lunn 	u32 id;
12082fa4e4b7SAndrew Lunn 
1209b97b5331SOng Boon Leong 	/* First, search C73 PCS using PCS MMD */
12102fa4e4b7SAndrew Lunn 	ret = xpcs_read(xpcs, MDIO_MMD_PCS, MII_PHYSID1);
12112fa4e4b7SAndrew Lunn 	if (ret < 0)
12122fa4e4b7SAndrew Lunn 		return 0xffffffff;
12132fa4e4b7SAndrew Lunn 
12142fa4e4b7SAndrew Lunn 	id = ret << 16;
12152fa4e4b7SAndrew Lunn 
12162fa4e4b7SAndrew Lunn 	ret = xpcs_read(xpcs, MDIO_MMD_PCS, MII_PHYSID2);
12172fa4e4b7SAndrew Lunn 	if (ret < 0)
12182fa4e4b7SAndrew Lunn 		return 0xffffffff;
12192fa4e4b7SAndrew Lunn 
122036641b04SVladimir Oltean 	/* If Device IDs are not all zeros or all ones,
122136641b04SVladimir Oltean 	 * we found C73 AN-type device
122236641b04SVladimir Oltean 	 */
122336641b04SVladimir Oltean 	if ((id | ret) && (id | ret) != 0xffffffff)
12242fa4e4b7SAndrew Lunn 		return id | ret;
1225b97b5331SOng Boon Leong 
1226b97b5331SOng Boon Leong 	/* Next, search C37 PCS using Vendor-Specific MII MMD */
1227b97b5331SOng Boon Leong 	ret = xpcs_read(xpcs, MDIO_MMD_VEND2, MII_PHYSID1);
1228b97b5331SOng Boon Leong 	if (ret < 0)
1229b97b5331SOng Boon Leong 		return 0xffffffff;
1230b97b5331SOng Boon Leong 
1231b97b5331SOng Boon Leong 	id = ret << 16;
1232b97b5331SOng Boon Leong 
1233b97b5331SOng Boon Leong 	ret = xpcs_read(xpcs, MDIO_MMD_VEND2, MII_PHYSID2);
1234b97b5331SOng Boon Leong 	if (ret < 0)
1235b97b5331SOng Boon Leong 		return 0xffffffff;
1236b97b5331SOng Boon Leong 
1237b97b5331SOng Boon Leong 	/* If Device IDs are not all zeros, we found C37 AN-type device */
1238b97b5331SOng Boon Leong 	if (id | ret)
1239b97b5331SOng Boon Leong 		return id | ret;
1240b97b5331SOng Boon Leong 
1241b97b5331SOng Boon Leong 	return 0xffffffff;
12422fa4e4b7SAndrew Lunn }
12432fa4e4b7SAndrew Lunn 
1244a54a8b71SVladimir Oltean static const struct xpcs_compat synopsys_xpcs_compat[DW_XPCS_INTERFACE_MAX] = {
1245a54a8b71SVladimir Oltean 	[DW_XPCS_USXGMII] = {
1246a54a8b71SVladimir Oltean 		.supported = xpcs_usxgmii_features,
1247a54a8b71SVladimir Oltean 		.interface = xpcs_usxgmii_interfaces,
1248a54a8b71SVladimir Oltean 		.num_interfaces = ARRAY_SIZE(xpcs_usxgmii_interfaces),
1249a54a8b71SVladimir Oltean 		.an_mode = DW_AN_C73,
1250a54a8b71SVladimir Oltean 	},
1251a54a8b71SVladimir Oltean 	[DW_XPCS_10GKR] = {
1252a54a8b71SVladimir Oltean 		.supported = xpcs_10gkr_features,
1253a54a8b71SVladimir Oltean 		.interface = xpcs_10gkr_interfaces,
1254a54a8b71SVladimir Oltean 		.num_interfaces = ARRAY_SIZE(xpcs_10gkr_interfaces),
1255a54a8b71SVladimir Oltean 		.an_mode = DW_AN_C73,
1256a54a8b71SVladimir Oltean 	},
1257a54a8b71SVladimir Oltean 	[DW_XPCS_XLGMII] = {
1258a54a8b71SVladimir Oltean 		.supported = xpcs_xlgmii_features,
1259a54a8b71SVladimir Oltean 		.interface = xpcs_xlgmii_interfaces,
1260a54a8b71SVladimir Oltean 		.num_interfaces = ARRAY_SIZE(xpcs_xlgmii_interfaces),
1261a54a8b71SVladimir Oltean 		.an_mode = DW_AN_C73,
1262a54a8b71SVladimir Oltean 	},
1263af8de1e3SJiawen Wu 	[DW_XPCS_10GBASER] = {
1264af8de1e3SJiawen Wu 		.supported = xpcs_10gbaser_features,
1265af8de1e3SJiawen Wu 		.interface = xpcs_10gbaser_interfaces,
1266af8de1e3SJiawen Wu 		.num_interfaces = ARRAY_SIZE(xpcs_10gbaser_interfaces),
1267af8de1e3SJiawen Wu 		.an_mode = DW_10GBASER,
1268af8de1e3SJiawen Wu 	},
1269a54a8b71SVladimir Oltean 	[DW_XPCS_SGMII] = {
1270a54a8b71SVladimir Oltean 		.supported = xpcs_sgmii_features,
1271a54a8b71SVladimir Oltean 		.interface = xpcs_sgmii_interfaces,
1272a54a8b71SVladimir Oltean 		.num_interfaces = ARRAY_SIZE(xpcs_sgmii_interfaces),
1273a54a8b71SVladimir Oltean 		.an_mode = DW_AN_C37_SGMII,
1274a54a8b71SVladimir Oltean 	},
1275b47aec88SOng Boon Leong 	[DW_XPCS_1000BASEX] = {
1276b47aec88SOng Boon Leong 		.supported = xpcs_1000basex_features,
1277b47aec88SOng Boon Leong 		.interface = xpcs_1000basex_interfaces,
1278b47aec88SOng Boon Leong 		.num_interfaces = ARRAY_SIZE(xpcs_1000basex_interfaces),
1279b47aec88SOng Boon Leong 		.an_mode = DW_AN_C37_1000BASEX,
1280b47aec88SOng Boon Leong 	},
1281f27abde3SVoon Weifeng 	[DW_XPCS_2500BASEX] = {
1282f27abde3SVoon Weifeng 		.supported = xpcs_2500basex_features,
1283f27abde3SVoon Weifeng 		.interface = xpcs_2500basex_interfaces,
128443fb622dSRussell King (Oracle) 		.num_interfaces = ARRAY_SIZE(xpcs_2500basex_interfaces),
1285f27abde3SVoon Weifeng 		.an_mode = DW_2500BASEX,
1286f27abde3SVoon Weifeng 	},
1287a54a8b71SVladimir Oltean };
1288a54a8b71SVladimir Oltean 
1289dd0721eaSVladimir Oltean static const struct xpcs_compat nxp_sja1105_xpcs_compat[DW_XPCS_INTERFACE_MAX] = {
1290dd0721eaSVladimir Oltean 	[DW_XPCS_SGMII] = {
1291dd0721eaSVladimir Oltean 		.supported = xpcs_sgmii_features,
1292dd0721eaSVladimir Oltean 		.interface = xpcs_sgmii_interfaces,
1293dd0721eaSVladimir Oltean 		.num_interfaces = ARRAY_SIZE(xpcs_sgmii_interfaces),
1294dd0721eaSVladimir Oltean 		.an_mode = DW_AN_C37_SGMII,
1295dd0721eaSVladimir Oltean 		.pma_config = nxp_sja1105_sgmii_pma_config,
1296dd0721eaSVladimir Oltean 	},
1297dd0721eaSVladimir Oltean };
1298dd0721eaSVladimir Oltean 
1299f7380bbaSVladimir Oltean static const struct xpcs_compat nxp_sja1110_xpcs_compat[DW_XPCS_INTERFACE_MAX] = {
1300f7380bbaSVladimir Oltean 	[DW_XPCS_SGMII] = {
1301f7380bbaSVladimir Oltean 		.supported = xpcs_sgmii_features,
1302f7380bbaSVladimir Oltean 		.interface = xpcs_sgmii_interfaces,
1303f7380bbaSVladimir Oltean 		.num_interfaces = ARRAY_SIZE(xpcs_sgmii_interfaces),
1304f7380bbaSVladimir Oltean 		.an_mode = DW_AN_C37_SGMII,
1305f7380bbaSVladimir Oltean 		.pma_config = nxp_sja1110_sgmii_pma_config,
1306f7380bbaSVladimir Oltean 	},
1307f7380bbaSVladimir Oltean 	[DW_XPCS_2500BASEX] = {
1308f7380bbaSVladimir Oltean 		.supported = xpcs_2500basex_features,
1309f7380bbaSVladimir Oltean 		.interface = xpcs_2500basex_interfaces,
1310f7380bbaSVladimir Oltean 		.num_interfaces = ARRAY_SIZE(xpcs_2500basex_interfaces),
1311f7380bbaSVladimir Oltean 		.an_mode = DW_2500BASEX,
1312f7380bbaSVladimir Oltean 		.pma_config = nxp_sja1110_2500basex_pma_config,
1313f7380bbaSVladimir Oltean 	},
1314f7380bbaSVladimir Oltean };
1315f7380bbaSVladimir Oltean 
1316a54a8b71SVladimir Oltean static const struct xpcs_id xpcs_id_list[] = {
1317a54a8b71SVladimir Oltean 	{
1318a54a8b71SVladimir Oltean 		.id = SYNOPSYS_XPCS_ID,
1319a54a8b71SVladimir Oltean 		.mask = SYNOPSYS_XPCS_MASK,
1320a54a8b71SVladimir Oltean 		.compat = synopsys_xpcs_compat,
1321dd0721eaSVladimir Oltean 	}, {
1322dd0721eaSVladimir Oltean 		.id = NXP_SJA1105_XPCS_ID,
1323dd0721eaSVladimir Oltean 		.mask = SYNOPSYS_XPCS_MASK,
1324dd0721eaSVladimir Oltean 		.compat = nxp_sja1105_xpcs_compat,
1325f7380bbaSVladimir Oltean 	}, {
1326f7380bbaSVladimir Oltean 		.id = NXP_SJA1110_XPCS_ID,
1327f7380bbaSVladimir Oltean 		.mask = SYNOPSYS_XPCS_MASK,
1328f7380bbaSVladimir Oltean 		.compat = nxp_sja1110_xpcs_compat,
1329a54a8b71SVladimir Oltean 	},
1330a54a8b71SVladimir Oltean };
1331a54a8b71SVladimir Oltean 
133211059740SVladimir Oltean static const struct phylink_pcs_ops xpcs_phylink_ops = {
1333fe70fb74SRussell King (Oracle) 	.pcs_validate = xpcs_validate,
133411059740SVladimir Oltean 	.pcs_config = xpcs_config,
133511059740SVladimir Oltean 	.pcs_get_state = xpcs_get_state,
1336b47aec88SOng Boon Leong 	.pcs_an_restart = xpcs_an_restart,
133711059740SVladimir Oltean 	.pcs_link_up = xpcs_link_up,
133811059740SVladimir Oltean };
133911059740SVladimir Oltean 
xpcs_create(struct mdio_device * mdiodev,phy_interface_t interface)13404739b9f3SRussell King (Oracle) static struct dw_xpcs *xpcs_create(struct mdio_device *mdiodev,
13412cac15daSVladimir Oltean 				   phy_interface_t interface)
13422fa4e4b7SAndrew Lunn {
13435673ef86SVladimir Oltean 	struct dw_xpcs *xpcs;
13442cac15daSVladimir Oltean 	u32 xpcs_id;
13452cac15daSVladimir Oltean 	int i, ret;
13462cac15daSVladimir Oltean 
13472cac15daSVladimir Oltean 	xpcs = kzalloc(sizeof(*xpcs), GFP_KERNEL);
13482cac15daSVladimir Oltean 	if (!xpcs)
13492cad5d2eSWong Vee Khee 		return ERR_PTR(-ENOMEM);
13502cac15daSVladimir Oltean 
13519a5d500cSRussell King (Oracle) 	mdio_device_get(mdiodev);
13522cac15daSVladimir Oltean 	xpcs->mdiodev = mdiodev;
13532cac15daSVladimir Oltean 
13542cac15daSVladimir Oltean 	xpcs_id = xpcs_get_id(xpcs);
13552fa4e4b7SAndrew Lunn 
13562fa4e4b7SAndrew Lunn 	for (i = 0; i < ARRAY_SIZE(xpcs_id_list); i++) {
1357a54a8b71SVladimir Oltean 		const struct xpcs_id *entry = &xpcs_id_list[i];
13589900074eSVladimir Oltean 		const struct xpcs_compat *compat;
13592fa4e4b7SAndrew Lunn 
13609900074eSVladimir Oltean 		if ((xpcs_id & entry->mask) != entry->id)
13619900074eSVladimir Oltean 			continue;
13622fa4e4b7SAndrew Lunn 
13639900074eSVladimir Oltean 		xpcs->id = entry;
13649900074eSVladimir Oltean 
13659900074eSVladimir Oltean 		compat = xpcs_find_compat(entry, interface);
13662cac15daSVladimir Oltean 		if (!compat) {
13672cac15daSVladimir Oltean 			ret = -ENODEV;
13682cac15daSVladimir Oltean 			goto out;
13692fa4e4b7SAndrew Lunn 		}
13702fa4e4b7SAndrew Lunn 
1371d55595f0SJiawen Wu 		ret = xpcs_dev_flag(xpcs);
1372d55595f0SJiawen Wu 		if (ret)
1373d55595f0SJiawen Wu 			goto out;
1374d55595f0SJiawen Wu 
137511059740SVladimir Oltean 		xpcs->pcs.ops = &xpcs_phylink_ops;
1376a3a47cfbSRussell King (Oracle) 		xpcs->pcs.neg_mode = true;
137711059740SVladimir Oltean 
1378d55595f0SJiawen Wu 		if (xpcs->dev_flag != DW_DEV_TXGBE) {
13792deea43fSJiawen Wu 			xpcs->pcs.poll = true;
13802deea43fSJiawen Wu 
13812cac15daSVladimir Oltean 			ret = xpcs_soft_reset(xpcs, compat);
13822cac15daSVladimir Oltean 			if (ret)
13832cac15daSVladimir Oltean 				goto out;
1384d55595f0SJiawen Wu 		}
13852cac15daSVladimir Oltean 
13862cac15daSVladimir Oltean 		return xpcs;
13872fa4e4b7SAndrew Lunn 	}
13882cac15daSVladimir Oltean 
13892cac15daSVladimir Oltean 	ret = -ENODEV;
13902cac15daSVladimir Oltean 
13912cac15daSVladimir Oltean out:
13929a5d500cSRussell King (Oracle) 	mdio_device_put(mdiodev);
13932cac15daSVladimir Oltean 	kfree(xpcs);
13942cac15daSVladimir Oltean 
13952cac15daSVladimir Oltean 	return ERR_PTR(ret);
13962cac15daSVladimir Oltean }
13972cac15daSVladimir Oltean 
xpcs_destroy(struct dw_xpcs * xpcs)13985673ef86SVladimir Oltean void xpcs_destroy(struct dw_xpcs *xpcs)
13992cac15daSVladimir Oltean {
14009a5d500cSRussell King (Oracle) 	if (xpcs)
14019a5d500cSRussell King (Oracle) 		mdio_device_put(xpcs->mdiodev);
14022cac15daSVladimir Oltean 	kfree(xpcs);
14032cac15daSVladimir Oltean }
14042cac15daSVladimir Oltean EXPORT_SYMBOL_GPL(xpcs_destroy);
14052fa4e4b7SAndrew Lunn 
xpcs_create_mdiodev(struct mii_bus * bus,int addr,phy_interface_t interface)14069a5d500cSRussell King (Oracle) struct dw_xpcs *xpcs_create_mdiodev(struct mii_bus *bus, int addr,
14079a5d500cSRussell King (Oracle) 				    phy_interface_t interface)
14089a5d500cSRussell King (Oracle) {
14099a5d500cSRussell King (Oracle) 	struct mdio_device *mdiodev;
14109a5d500cSRussell King (Oracle) 	struct dw_xpcs *xpcs;
14119a5d500cSRussell King (Oracle) 
14129a5d500cSRussell King (Oracle) 	mdiodev = mdio_device_create(bus, addr);
14139a5d500cSRussell King (Oracle) 	if (IS_ERR(mdiodev))
14149a5d500cSRussell King (Oracle) 		return ERR_CAST(mdiodev);
14159a5d500cSRussell King (Oracle) 
14169a5d500cSRussell King (Oracle) 	xpcs = xpcs_create(mdiodev, interface);
14179a5d500cSRussell King (Oracle) 
14189a5d500cSRussell King (Oracle) 	/* xpcs_create() has taken a refcount on the mdiodev if it was
14199a5d500cSRussell King (Oracle) 	 * successful. If xpcs_create() fails, this will free the mdio
14209a5d500cSRussell King (Oracle) 	 * device here. In any case, we don't need to hold our reference
14219a5d500cSRussell King (Oracle) 	 * anymore, and putting it here will allow mdio_device_put() in
14229a5d500cSRussell King (Oracle) 	 * xpcs_destroy() to automatically free the mdio device.
14239a5d500cSRussell King (Oracle) 	 */
14249a5d500cSRussell King (Oracle) 	mdio_device_put(mdiodev);
14259a5d500cSRussell King (Oracle) 
14269a5d500cSRussell King (Oracle) 	return xpcs;
14279a5d500cSRussell King (Oracle) }
14289a5d500cSRussell King (Oracle) EXPORT_SYMBOL_GPL(xpcs_create_mdiodev);
14299a5d500cSRussell King (Oracle) 
14302fa4e4b7SAndrew Lunn MODULE_LICENSE("GPL v2");
1431