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