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 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 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 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 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 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 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 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 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 2315673ef86SVladimir Oltean static 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 2365673ef86SVladimir Oltean static 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 241*d55595f0SJiawen Wu static int xpcs_dev_flag(struct dw_xpcs *xpcs) 242*d55595f0SJiawen Wu { 243*d55595f0SJiawen Wu int ret, oui; 244*d55595f0SJiawen Wu 245*d55595f0SJiawen Wu ret = xpcs_read(xpcs, MDIO_MMD_PMAPMD, MDIO_DEVID1); 246*d55595f0SJiawen Wu if (ret < 0) 247*d55595f0SJiawen Wu return ret; 248*d55595f0SJiawen Wu 249*d55595f0SJiawen Wu oui = ret; 250*d55595f0SJiawen Wu 251*d55595f0SJiawen Wu ret = xpcs_read(xpcs, MDIO_MMD_PMAPMD, MDIO_DEVID2); 252*d55595f0SJiawen Wu if (ret < 0) 253*d55595f0SJiawen Wu return ret; 254*d55595f0SJiawen Wu 255*d55595f0SJiawen Wu ret = (ret >> 10) & 0x3F; 256*d55595f0SJiawen Wu oui |= ret << 16; 257*d55595f0SJiawen Wu 258*d55595f0SJiawen Wu if (oui == DW_OUI_WX) 259*d55595f0SJiawen Wu xpcs->dev_flag = DW_DEV_TXGBE; 260*d55595f0SJiawen Wu 261*d55595f0SJiawen Wu return 0; 262*d55595f0SJiawen Wu } 263*d55595f0SJiawen Wu 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 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: 29607a4bc51SOng Boon Leong return -1; 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 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 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 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 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 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 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 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 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 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 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) 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 683a3a47cfbSRussell King (Oracle) static int xpcs_config_aneg_c37_sgmii(struct dw_xpcs *xpcs, 684a3a47cfbSRussell King (Oracle) unsigned int neg_mode) 685b97b5331SOng Boon Leong { 686e3cf002dSWong Vee Khee int ret, mdio_ctrl; 687b97b5331SOng Boon Leong 688b97b5331SOng Boon Leong /* For AN for C37 SGMII mode, the settings are :- 689e3cf002dSWong Vee Khee * 1) VR_MII_MMD_CTRL Bit(12) [AN_ENABLE] = 0b (Disable SGMII AN in case 690e3cf002dSWong Vee Khee it is already enabled) 691e3cf002dSWong Vee Khee * 2) VR_MII_AN_CTRL Bit(2:1)[PCS_MODE] = 10b (SGMII AN) 692e3cf002dSWong Vee Khee * 3) VR_MII_AN_CTRL Bit(3) [TX_CONFIG] = 0b (MAC side SGMII) 693b97b5331SOng Boon Leong * DW xPCS used with DW EQoS MAC is always MAC side SGMII. 694e3cf002dSWong Vee Khee * 4) VR_MII_DIG_CTRL1 Bit(9) [MAC_AUTO_SW] = 1b (Automatic 695b97b5331SOng Boon Leong * speed/duplex mode change by HW after SGMII AN complete) 696e3cf002dSWong Vee Khee * 5) VR_MII_MMD_CTRL Bit(12) [AN_ENABLE] = 1b (Enable SGMII AN) 697b97b5331SOng Boon Leong * 698b97b5331SOng Boon Leong * Note: Since it is MAC side SGMII, there is no need to set 699b97b5331SOng Boon Leong * SR_MII_AN_ADV. MAC side SGMII receives AN Tx Config from 700b97b5331SOng Boon Leong * PHY about the link state change after C28 AN is completed 701b97b5331SOng Boon Leong * between PHY and Link Partner. There is also no need to 702b97b5331SOng Boon Leong * trigger AN restart for MAC-side SGMII. 703b97b5331SOng Boon Leong */ 704e3cf002dSWong Vee Khee mdio_ctrl = xpcs_read(xpcs, MDIO_MMD_VEND2, DW_VR_MII_MMD_CTRL); 705e3cf002dSWong Vee Khee if (mdio_ctrl < 0) 706e3cf002dSWong Vee Khee return mdio_ctrl; 707e3cf002dSWong Vee Khee 708e3cf002dSWong Vee Khee if (mdio_ctrl & AN_CL37_EN) { 709e3cf002dSWong Vee Khee ret = xpcs_write(xpcs, MDIO_MMD_VEND2, DW_VR_MII_MMD_CTRL, 710e3cf002dSWong Vee Khee mdio_ctrl & ~AN_CL37_EN); 711e3cf002dSWong Vee Khee if (ret < 0) 712e3cf002dSWong Vee Khee return ret; 713e3cf002dSWong Vee Khee } 714e3cf002dSWong Vee Khee 715b97b5331SOng Boon Leong ret = xpcs_read(xpcs, MDIO_MMD_VEND2, DW_VR_MII_AN_CTRL); 716b97b5331SOng Boon Leong if (ret < 0) 717b97b5331SOng Boon Leong return ret; 718b97b5331SOng Boon Leong 719b97b5331SOng Boon Leong ret &= ~(DW_VR_MII_PCS_MODE_MASK | DW_VR_MII_TX_CONFIG_MASK); 720b97b5331SOng Boon Leong ret |= (DW_VR_MII_PCS_MODE_C37_SGMII << 721b97b5331SOng Boon Leong DW_VR_MII_AN_CTRL_PCS_MODE_SHIFT & 722b97b5331SOng Boon Leong DW_VR_MII_PCS_MODE_MASK); 723b97b5331SOng Boon Leong ret |= (DW_VR_MII_TX_CONFIG_MAC_SIDE_SGMII << 724b97b5331SOng Boon Leong DW_VR_MII_AN_CTRL_TX_CONFIG_SHIFT & 725b97b5331SOng Boon Leong DW_VR_MII_TX_CONFIG_MASK); 726b97b5331SOng Boon Leong ret = xpcs_write(xpcs, MDIO_MMD_VEND2, DW_VR_MII_AN_CTRL, ret); 727b97b5331SOng Boon Leong if (ret < 0) 728b97b5331SOng Boon Leong return ret; 729b97b5331SOng Boon Leong 730b97b5331SOng Boon Leong ret = xpcs_read(xpcs, MDIO_MMD_VEND2, DW_VR_MII_DIG_CTRL1); 731b97b5331SOng Boon Leong if (ret < 0) 732b97b5331SOng Boon Leong return ret; 733b97b5331SOng Boon Leong 734a3a47cfbSRussell King (Oracle) if (neg_mode == PHYLINK_PCS_NEG_INBAND_ENABLED) 735b97b5331SOng Boon Leong ret |= DW_VR_MII_DIG_CTRL1_MAC_AUTO_SW; 7362031c09eSVladimir Oltean else 7372031c09eSVladimir Oltean ret &= ~DW_VR_MII_DIG_CTRL1_MAC_AUTO_SW; 738b97b5331SOng Boon Leong 739e3cf002dSWong Vee Khee ret = xpcs_write(xpcs, MDIO_MMD_VEND2, DW_VR_MII_DIG_CTRL1, ret); 740e3cf002dSWong Vee Khee if (ret < 0) 741e3cf002dSWong Vee Khee return ret; 742e3cf002dSWong Vee Khee 743a3a47cfbSRussell King (Oracle) if (neg_mode == PHYLINK_PCS_NEG_INBAND_ENABLED) 744e3cf002dSWong Vee Khee ret = xpcs_write(xpcs, MDIO_MMD_VEND2, DW_VR_MII_MMD_CTRL, 745e3cf002dSWong Vee Khee mdio_ctrl | AN_CL37_EN); 746e3cf002dSWong Vee Khee 747e3cf002dSWong Vee Khee return ret; 748b97b5331SOng Boon Leong } 749b97b5331SOng Boon Leong 750a3a47cfbSRussell King (Oracle) static int xpcs_config_aneg_c37_1000basex(struct dw_xpcs *xpcs, 751a3a47cfbSRussell King (Oracle) unsigned int neg_mode, 752b47aec88SOng Boon Leong const unsigned long *advertising) 753b47aec88SOng Boon Leong { 754b47aec88SOng Boon Leong phy_interface_t interface = PHY_INTERFACE_MODE_1000BASEX; 755b47aec88SOng Boon Leong int ret, mdio_ctrl, adv; 756b47aec88SOng Boon Leong bool changed = 0; 757b47aec88SOng Boon Leong 758b47aec88SOng Boon Leong /* According to Chap 7.12, to set 1000BASE-X C37 AN, AN must 759b47aec88SOng Boon Leong * be disabled first:- 760b47aec88SOng Boon Leong * 1) VR_MII_MMD_CTRL Bit(12)[AN_ENABLE] = 0b 761b47aec88SOng Boon Leong * 2) VR_MII_AN_CTRL Bit(2:1)[PCS_MODE] = 00b (1000BASE-X C37) 762b47aec88SOng Boon Leong */ 763b47aec88SOng Boon Leong mdio_ctrl = xpcs_read(xpcs, MDIO_MMD_VEND2, DW_VR_MII_MMD_CTRL); 764b47aec88SOng Boon Leong if (mdio_ctrl < 0) 765b47aec88SOng Boon Leong return mdio_ctrl; 766b47aec88SOng Boon Leong 767b47aec88SOng Boon Leong if (mdio_ctrl & AN_CL37_EN) { 768b47aec88SOng Boon Leong ret = xpcs_write(xpcs, MDIO_MMD_VEND2, DW_VR_MII_MMD_CTRL, 769b47aec88SOng Boon Leong mdio_ctrl & ~AN_CL37_EN); 770b47aec88SOng Boon Leong if (ret < 0) 771b47aec88SOng Boon Leong return ret; 772b47aec88SOng Boon Leong } 773b47aec88SOng Boon Leong 774b47aec88SOng Boon Leong ret = xpcs_read(xpcs, MDIO_MMD_VEND2, DW_VR_MII_AN_CTRL); 775b47aec88SOng Boon Leong if (ret < 0) 776b47aec88SOng Boon Leong return ret; 777b47aec88SOng Boon Leong 778b47aec88SOng Boon Leong ret &= ~DW_VR_MII_PCS_MODE_MASK; 779b47aec88SOng Boon Leong ret = xpcs_write(xpcs, MDIO_MMD_VEND2, DW_VR_MII_AN_CTRL, ret); 780b47aec88SOng Boon Leong if (ret < 0) 781b47aec88SOng Boon Leong return ret; 782b47aec88SOng Boon Leong 783b47aec88SOng Boon Leong /* Check for advertising changes and update the C45 MII ADV 784b47aec88SOng Boon Leong * register accordingly. 785b47aec88SOng Boon Leong */ 786b47aec88SOng Boon Leong adv = phylink_mii_c22_pcs_encode_advertisement(interface, 787b47aec88SOng Boon Leong advertising); 788b47aec88SOng Boon Leong if (adv >= 0) { 789b47aec88SOng Boon Leong ret = xpcs_modify_changed(xpcs, MDIO_MMD_VEND2, 790b47aec88SOng Boon Leong MII_ADVERTISE, 0xffff, adv); 791b47aec88SOng Boon Leong if (ret < 0) 792b47aec88SOng Boon Leong return ret; 793b47aec88SOng Boon Leong 794b47aec88SOng Boon Leong changed = ret; 795b47aec88SOng Boon Leong } 796b47aec88SOng Boon Leong 797b47aec88SOng Boon Leong /* Clear CL37 AN complete status */ 798b47aec88SOng Boon Leong ret = xpcs_write(xpcs, MDIO_MMD_VEND2, DW_VR_MII_AN_INTR_STS, 0); 799b47aec88SOng Boon Leong if (ret < 0) 800b47aec88SOng Boon Leong return ret; 801b47aec88SOng Boon Leong 802a3a47cfbSRussell King (Oracle) if (neg_mode == PHYLINK_PCS_NEG_INBAND_ENABLED) { 803b47aec88SOng Boon Leong ret = xpcs_write(xpcs, MDIO_MMD_VEND2, DW_VR_MII_MMD_CTRL, 804b47aec88SOng Boon Leong mdio_ctrl | AN_CL37_EN); 805b47aec88SOng Boon Leong if (ret < 0) 806b47aec88SOng Boon Leong return ret; 807b47aec88SOng Boon Leong } 808b47aec88SOng Boon Leong 809b47aec88SOng Boon Leong return changed; 810b47aec88SOng Boon Leong } 811b47aec88SOng Boon Leong 8125673ef86SVladimir Oltean static int xpcs_config_2500basex(struct dw_xpcs *xpcs) 813f27abde3SVoon Weifeng { 814f27abde3SVoon Weifeng int ret; 815f27abde3SVoon Weifeng 816f27abde3SVoon Weifeng ret = xpcs_read(xpcs, MDIO_MMD_VEND2, DW_VR_MII_DIG_CTRL1); 817f27abde3SVoon Weifeng if (ret < 0) 818f27abde3SVoon Weifeng return ret; 819f27abde3SVoon Weifeng ret |= DW_VR_MII_DIG_CTRL1_2G5_EN; 820f27abde3SVoon Weifeng ret &= ~DW_VR_MII_DIG_CTRL1_MAC_AUTO_SW; 821f27abde3SVoon Weifeng ret = xpcs_write(xpcs, MDIO_MMD_VEND2, DW_VR_MII_DIG_CTRL1, ret); 822f27abde3SVoon Weifeng if (ret < 0) 823f27abde3SVoon Weifeng return ret; 824f27abde3SVoon Weifeng 825f27abde3SVoon Weifeng ret = xpcs_read(xpcs, MDIO_MMD_VEND2, DW_VR_MII_MMD_CTRL); 826f27abde3SVoon Weifeng if (ret < 0) 827f27abde3SVoon Weifeng return ret; 828f27abde3SVoon Weifeng ret &= ~AN_CL37_EN; 829f27abde3SVoon Weifeng ret |= SGMII_SPEED_SS6; 830f27abde3SVoon Weifeng ret &= ~SGMII_SPEED_SS13; 831f27abde3SVoon Weifeng return xpcs_write(xpcs, MDIO_MMD_VEND2, DW_VR_MII_MMD_CTRL, ret); 832f27abde3SVoon Weifeng } 833f27abde3SVoon Weifeng 834a853c68eSVladimir Oltean int xpcs_do_config(struct dw_xpcs *xpcs, phy_interface_t interface, 835a3a47cfbSRussell King (Oracle) const unsigned long *advertising, unsigned int neg_mode) 8362fa4e4b7SAndrew Lunn { 8379900074eSVladimir Oltean const struct xpcs_compat *compat; 8382fa4e4b7SAndrew Lunn int ret; 8392fa4e4b7SAndrew Lunn 84011059740SVladimir Oltean compat = xpcs_find_compat(xpcs->id, interface); 8419900074eSVladimir Oltean if (!compat) 8429900074eSVladimir Oltean return -ENODEV; 8439900074eSVladimir Oltean 8449900074eSVladimir Oltean switch (compat->an_mode) { 845af8de1e3SJiawen Wu case DW_10GBASER: 846af8de1e3SJiawen Wu break; 84707a4bc51SOng Boon Leong case DW_AN_C73: 848a3a47cfbSRussell King (Oracle) if (neg_mode == PHYLINK_PCS_NEG_INBAND_ENABLED) { 8499900074eSVladimir Oltean ret = xpcs_config_aneg_c73(xpcs, compat); 8502fa4e4b7SAndrew Lunn if (ret) 8512fa4e4b7SAndrew Lunn return ret; 8522fa4e4b7SAndrew Lunn } 85307a4bc51SOng Boon Leong break; 854b97b5331SOng Boon Leong case DW_AN_C37_SGMII: 855a3a47cfbSRussell King (Oracle) ret = xpcs_config_aneg_c37_sgmii(xpcs, neg_mode); 856b97b5331SOng Boon Leong if (ret) 857b97b5331SOng Boon Leong return ret; 858b97b5331SOng Boon Leong break; 859b47aec88SOng Boon Leong case DW_AN_C37_1000BASEX: 860a3a47cfbSRussell King (Oracle) ret = xpcs_config_aneg_c37_1000basex(xpcs, neg_mode, 861b47aec88SOng Boon Leong advertising); 862b47aec88SOng Boon Leong if (ret) 863b47aec88SOng Boon Leong return ret; 864b47aec88SOng Boon Leong break; 865f27abde3SVoon Weifeng case DW_2500BASEX: 866f27abde3SVoon Weifeng ret = xpcs_config_2500basex(xpcs); 867f27abde3SVoon Weifeng if (ret) 868f27abde3SVoon Weifeng return ret; 869f27abde3SVoon Weifeng break; 87007a4bc51SOng Boon Leong default: 87107a4bc51SOng Boon Leong return -1; 87207a4bc51SOng Boon Leong } 87307a4bc51SOng Boon Leong 874dd0721eaSVladimir Oltean if (compat->pma_config) { 875dd0721eaSVladimir Oltean ret = compat->pma_config(xpcs); 876dd0721eaSVladimir Oltean if (ret) 877dd0721eaSVladimir Oltean return ret; 878dd0721eaSVladimir Oltean } 879dd0721eaSVladimir Oltean 88007a4bc51SOng Boon Leong return 0; 88107a4bc51SOng Boon Leong } 882a853c68eSVladimir Oltean EXPORT_SYMBOL_GPL(xpcs_do_config); 88307a4bc51SOng Boon Leong 884a3a47cfbSRussell King (Oracle) static int xpcs_config(struct phylink_pcs *pcs, unsigned int neg_mode, 88511059740SVladimir Oltean phy_interface_t interface, 88611059740SVladimir Oltean const unsigned long *advertising, 88711059740SVladimir Oltean bool permit_pause_to_mac) 88811059740SVladimir Oltean { 8895673ef86SVladimir Oltean struct dw_xpcs *xpcs = phylink_pcs_to_xpcs(pcs); 89011059740SVladimir Oltean 891a3a47cfbSRussell King (Oracle) return xpcs_do_config(xpcs, interface, advertising, neg_mode); 89211059740SVladimir Oltean } 89311059740SVladimir Oltean 8945673ef86SVladimir Oltean static int xpcs_get_state_c73(struct dw_xpcs *xpcs, 8959900074eSVladimir Oltean struct phylink_link_state *state, 8969900074eSVladimir Oltean const struct xpcs_compat *compat) 89707a4bc51SOng Boon Leong { 898459fd2f1SRussell King (Oracle) bool an_enabled; 899883a98edSRussell King (Oracle) int pcs_stat1; 900883a98edSRussell King (Oracle) int an_stat1; 90107a4bc51SOng Boon Leong int ret; 90207a4bc51SOng Boon Leong 903883a98edSRussell King (Oracle) /* The link status bit is latching-low, so it is important to 904883a98edSRussell King (Oracle) * avoid unnecessary re-reads of this register to avoid missing 905883a98edSRussell King (Oracle) * a link-down event. 906883a98edSRussell King (Oracle) */ 907883a98edSRussell King (Oracle) pcs_stat1 = xpcs_read(xpcs, MDIO_MMD_PCS, MDIO_STAT1); 908883a98edSRussell King (Oracle) if (pcs_stat1 < 0) { 909883a98edSRussell King (Oracle) state->link = false; 910883a98edSRussell King (Oracle) return pcs_stat1; 911883a98edSRussell King (Oracle) } 912883a98edSRussell King (Oracle) 91307a4bc51SOng Boon Leong /* Link needs to be read first ... */ 914883a98edSRussell King (Oracle) state->link = !!(pcs_stat1 & MDIO_STAT1_LSTATUS); 91507a4bc51SOng Boon Leong 91607a4bc51SOng Boon Leong /* ... and then we check the faults. */ 917883a98edSRussell King (Oracle) ret = xpcs_read_fault_c73(xpcs, state, pcs_stat1); 91807a4bc51SOng Boon Leong if (ret) { 9199900074eSVladimir Oltean ret = xpcs_soft_reset(xpcs, compat); 92007a4bc51SOng Boon Leong if (ret) 92107a4bc51SOng Boon Leong return ret; 92207a4bc51SOng Boon Leong 92307a4bc51SOng Boon Leong state->link = 0; 92407a4bc51SOng Boon Leong 925a3a47cfbSRussell King (Oracle) return xpcs_do_config(xpcs, state->interface, NULL, 926a3a47cfbSRussell King (Oracle) PHYLINK_PCS_NEG_INBAND_ENABLED); 92707a4bc51SOng Boon Leong } 92807a4bc51SOng Boon Leong 929883a98edSRussell King (Oracle) /* There is no point doing anything else if the link is down. */ 930883a98edSRussell King (Oracle) if (!state->link) 931883a98edSRussell King (Oracle) return 0; 932883a98edSRussell King (Oracle) 933459fd2f1SRussell King (Oracle) an_enabled = linkmode_test_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, 934459fd2f1SRussell King (Oracle) state->advertising); 935883a98edSRussell King (Oracle) if (an_enabled) { 936883a98edSRussell King (Oracle) /* The link status bit is latching-low, so it is important to 937883a98edSRussell King (Oracle) * avoid unnecessary re-reads of this register to avoid missing 938883a98edSRussell King (Oracle) * a link-down event. 939883a98edSRussell King (Oracle) */ 940883a98edSRussell King (Oracle) an_stat1 = xpcs_read(xpcs, MDIO_MMD_AN, MDIO_STAT1); 941883a98edSRussell King (Oracle) if (an_stat1 < 0) { 942883a98edSRussell King (Oracle) state->link = false; 943883a98edSRussell King (Oracle) return an_stat1; 944883a98edSRussell King (Oracle) } 945883a98edSRussell King (Oracle) 946883a98edSRussell King (Oracle) state->an_complete = xpcs_aneg_done_c73(xpcs, state, compat, 947883a98edSRussell King (Oracle) an_stat1); 948883a98edSRussell King (Oracle) if (!state->an_complete) { 949883a98edSRussell King (Oracle) state->link = false; 950883a98edSRussell King (Oracle) return 0; 951883a98edSRussell King (Oracle) } 952883a98edSRussell King (Oracle) 953883a98edSRussell King (Oracle) ret = xpcs_read_lpa_c73(xpcs, state, an_stat1); 954883a98edSRussell King (Oracle) if (ret < 0) { 955883a98edSRussell King (Oracle) state->link = false; 956883a98edSRussell King (Oracle) return ret; 957883a98edSRussell King (Oracle) } 958883a98edSRussell King (Oracle) 95921234ef1SRussell King (Oracle) phylink_resolve_c73(state); 960883a98edSRussell King (Oracle) } else { 96107a4bc51SOng Boon Leong xpcs_resolve_pma(xpcs, state); 96207a4bc51SOng Boon Leong } 9632fa4e4b7SAndrew Lunn 9642fa4e4b7SAndrew Lunn return 0; 9652fa4e4b7SAndrew Lunn } 9662fa4e4b7SAndrew Lunn 9675673ef86SVladimir Oltean static int xpcs_get_state_c37_sgmii(struct dw_xpcs *xpcs, 968b97b5331SOng Boon Leong struct phylink_link_state *state) 969b97b5331SOng Boon Leong { 970b97b5331SOng Boon Leong int ret; 971b97b5331SOng Boon Leong 972b97b5331SOng Boon Leong /* Reset link_state */ 973b97b5331SOng Boon Leong state->link = false; 974b97b5331SOng Boon Leong state->speed = SPEED_UNKNOWN; 975b97b5331SOng Boon Leong state->duplex = DUPLEX_UNKNOWN; 976b97b5331SOng Boon Leong state->pause = 0; 977b97b5331SOng Boon Leong 978b97b5331SOng Boon Leong /* For C37 SGMII mode, we check DW_VR_MII_AN_INTR_STS for link 979b97b5331SOng Boon Leong * status, speed and duplex. 980b97b5331SOng Boon Leong */ 981b97b5331SOng Boon Leong ret = xpcs_read(xpcs, MDIO_MMD_VEND2, DW_VR_MII_AN_INTR_STS); 982b97b5331SOng Boon Leong if (ret < 0) 98327161db0SVladimir Oltean return ret; 984b97b5331SOng Boon Leong 985b97b5331SOng Boon Leong if (ret & DW_VR_MII_C37_ANSGM_SP_LNKSTS) { 986b97b5331SOng Boon Leong int speed_value; 987b97b5331SOng Boon Leong 988b97b5331SOng Boon Leong state->link = true; 989b97b5331SOng Boon Leong 990b97b5331SOng Boon Leong speed_value = (ret & DW_VR_MII_AN_STS_C37_ANSGM_SP) >> 991b97b5331SOng Boon Leong DW_VR_MII_AN_STS_C37_ANSGM_SP_SHIFT; 992b97b5331SOng Boon Leong if (speed_value == DW_VR_MII_C37_ANSGM_SP_1000) 993b97b5331SOng Boon Leong state->speed = SPEED_1000; 994b97b5331SOng Boon Leong else if (speed_value == DW_VR_MII_C37_ANSGM_SP_100) 995b97b5331SOng Boon Leong state->speed = SPEED_100; 996b97b5331SOng Boon Leong else 997b97b5331SOng Boon Leong state->speed = SPEED_10; 998b97b5331SOng Boon Leong 999b97b5331SOng Boon Leong if (ret & DW_VR_MII_AN_STS_C37_ANSGM_FD) 1000b97b5331SOng Boon Leong state->duplex = DUPLEX_FULL; 1001b97b5331SOng Boon Leong else 1002b97b5331SOng Boon Leong state->duplex = DUPLEX_HALF; 1003b97b5331SOng Boon Leong } 1004b97b5331SOng Boon Leong 1005b97b5331SOng Boon Leong return 0; 1006b97b5331SOng Boon Leong } 1007b97b5331SOng Boon Leong 1008b47aec88SOng Boon Leong static int xpcs_get_state_c37_1000basex(struct dw_xpcs *xpcs, 1009b47aec88SOng Boon Leong struct phylink_link_state *state) 1010b47aec88SOng Boon Leong { 1011b47aec88SOng Boon Leong int lpa, bmsr; 1012b47aec88SOng Boon Leong 1013459fd2f1SRussell King (Oracle) if (linkmode_test_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, 1014459fd2f1SRussell King (Oracle) state->advertising)) { 1015b47aec88SOng Boon Leong /* Reset link state */ 1016b47aec88SOng Boon Leong state->link = false; 1017b47aec88SOng Boon Leong 1018b47aec88SOng Boon Leong lpa = xpcs_read(xpcs, MDIO_MMD_VEND2, MII_LPA); 1019b47aec88SOng Boon Leong if (lpa < 0 || lpa & LPA_RFAULT) 1020b47aec88SOng Boon Leong return lpa; 1021b47aec88SOng Boon Leong 1022b47aec88SOng Boon Leong bmsr = xpcs_read(xpcs, MDIO_MMD_VEND2, MII_BMSR); 1023b47aec88SOng Boon Leong if (bmsr < 0) 1024b47aec88SOng Boon Leong return bmsr; 1025b47aec88SOng Boon Leong 1026b47aec88SOng Boon Leong phylink_mii_c22_pcs_decode_state(state, bmsr, lpa); 1027b47aec88SOng Boon Leong } 1028b47aec88SOng Boon Leong 1029b47aec88SOng Boon Leong return 0; 1030b47aec88SOng Boon Leong } 1031b47aec88SOng Boon Leong 103211059740SVladimir Oltean static void xpcs_get_state(struct phylink_pcs *pcs, 10332fa4e4b7SAndrew Lunn struct phylink_link_state *state) 10342fa4e4b7SAndrew Lunn { 10355673ef86SVladimir Oltean struct dw_xpcs *xpcs = phylink_pcs_to_xpcs(pcs); 10369900074eSVladimir Oltean const struct xpcs_compat *compat; 10372fa4e4b7SAndrew Lunn int ret; 10382fa4e4b7SAndrew Lunn 10399900074eSVladimir Oltean compat = xpcs_find_compat(xpcs->id, state->interface); 10409900074eSVladimir Oltean if (!compat) 104111059740SVladimir Oltean return; 10429900074eSVladimir Oltean 10439900074eSVladimir Oltean switch (compat->an_mode) { 1044af8de1e3SJiawen Wu case DW_10GBASER: 1045af8de1e3SJiawen Wu phylink_mii_c45_pcs_get_state(xpcs->mdiodev, state); 1046af8de1e3SJiawen Wu break; 104707a4bc51SOng Boon Leong case DW_AN_C73: 10489900074eSVladimir Oltean ret = xpcs_get_state_c73(xpcs, state, compat); 104911059740SVladimir Oltean if (ret) { 105011059740SVladimir Oltean pr_err("xpcs_get_state_c73 returned %pe\n", 105111059740SVladimir Oltean ERR_PTR(ret)); 105211059740SVladimir Oltean return; 105311059740SVladimir Oltean } 105407a4bc51SOng Boon Leong break; 1055b97b5331SOng Boon Leong case DW_AN_C37_SGMII: 1056b97b5331SOng Boon Leong ret = xpcs_get_state_c37_sgmii(xpcs, state); 105711059740SVladimir Oltean if (ret) { 105811059740SVladimir Oltean pr_err("xpcs_get_state_c37_sgmii returned %pe\n", 105911059740SVladimir Oltean ERR_PTR(ret)); 106011059740SVladimir Oltean } 1061b97b5331SOng Boon Leong break; 1062b47aec88SOng Boon Leong case DW_AN_C37_1000BASEX: 1063b47aec88SOng Boon Leong ret = xpcs_get_state_c37_1000basex(xpcs, state); 1064b47aec88SOng Boon Leong if (ret) { 1065b47aec88SOng Boon Leong pr_err("xpcs_get_state_c37_1000basex returned %pe\n", 1066b47aec88SOng Boon Leong ERR_PTR(ret)); 1067b47aec88SOng Boon Leong } 1068b47aec88SOng Boon Leong break; 106907a4bc51SOng Boon Leong default: 107011059740SVladimir Oltean return; 107111059740SVladimir Oltean } 10722fa4e4b7SAndrew Lunn } 10732fa4e4b7SAndrew Lunn 1074a3a47cfbSRussell King (Oracle) static void xpcs_link_up_sgmii(struct dw_xpcs *xpcs, unsigned int neg_mode, 10752031c09eSVladimir Oltean int speed, int duplex) 10762031c09eSVladimir Oltean { 10772031c09eSVladimir Oltean int val, ret; 10782031c09eSVladimir Oltean 1079a3a47cfbSRussell King (Oracle) if (neg_mode == PHYLINK_PCS_NEG_INBAND_ENABLED) 10802031c09eSVladimir Oltean return; 10812031c09eSVladimir Oltean 1082449b7a15SRussell King (Oracle) val = mii_bmcr_encode_fixed(speed, duplex); 10832031c09eSVladimir Oltean ret = xpcs_write(xpcs, MDIO_MMD_VEND2, MDIO_CTRL1, val); 10842031c09eSVladimir Oltean if (ret) 10852031c09eSVladimir Oltean pr_err("%s: xpcs_write returned %pe\n", __func__, ERR_PTR(ret)); 10862031c09eSVladimir Oltean } 10872031c09eSVladimir Oltean 1088a3a47cfbSRussell King (Oracle) static void xpcs_link_up_1000basex(struct dw_xpcs *xpcs, unsigned int neg_mode, 1089b47aec88SOng Boon Leong int speed, int duplex) 1090b47aec88SOng Boon Leong { 1091b47aec88SOng Boon Leong int val, ret; 1092b47aec88SOng Boon Leong 1093a3a47cfbSRussell King (Oracle) if (neg_mode == PHYLINK_PCS_NEG_INBAND_ENABLED) 1094b47aec88SOng Boon Leong return; 1095b47aec88SOng Boon Leong 1096b47aec88SOng Boon Leong switch (speed) { 1097b47aec88SOng Boon Leong case SPEED_1000: 1098b47aec88SOng Boon Leong val = BMCR_SPEED1000; 1099b47aec88SOng Boon Leong break; 1100b47aec88SOng Boon Leong case SPEED_100: 1101b47aec88SOng Boon Leong case SPEED_10: 1102b47aec88SOng Boon Leong default: 1103b47aec88SOng Boon Leong pr_err("%s: speed = %d\n", __func__, speed); 1104b47aec88SOng Boon Leong return; 1105b47aec88SOng Boon Leong } 1106b47aec88SOng Boon Leong 1107b47aec88SOng Boon Leong if (duplex == DUPLEX_FULL) 1108b47aec88SOng Boon Leong val |= BMCR_FULLDPLX; 1109b47aec88SOng Boon Leong else 1110b47aec88SOng Boon Leong pr_err("%s: half duplex not supported\n", __func__); 1111b47aec88SOng Boon Leong 1112b47aec88SOng Boon Leong ret = xpcs_write(xpcs, MDIO_MMD_VEND2, MDIO_CTRL1, val); 1113b47aec88SOng Boon Leong if (ret) 1114b47aec88SOng Boon Leong pr_err("%s: xpcs_write returned %pe\n", __func__, ERR_PTR(ret)); 1115b47aec88SOng Boon Leong } 1116b47aec88SOng Boon Leong 1117a3a47cfbSRussell King (Oracle) void xpcs_link_up(struct phylink_pcs *pcs, unsigned int neg_mode, 111811059740SVladimir Oltean phy_interface_t interface, int speed, int duplex) 11192fa4e4b7SAndrew Lunn { 11205673ef86SVladimir Oltean struct dw_xpcs *xpcs = phylink_pcs_to_xpcs(pcs); 112111059740SVladimir Oltean 11222fa4e4b7SAndrew Lunn if (interface == PHY_INTERFACE_MODE_USXGMII) 11232fa4e4b7SAndrew Lunn return xpcs_config_usxgmii(xpcs, speed); 11242031c09eSVladimir Oltean if (interface == PHY_INTERFACE_MODE_SGMII) 1125a3a47cfbSRussell King (Oracle) return xpcs_link_up_sgmii(xpcs, neg_mode, speed, duplex); 1126b47aec88SOng Boon Leong if (interface == PHY_INTERFACE_MODE_1000BASEX) 1127a3a47cfbSRussell King (Oracle) return xpcs_link_up_1000basex(xpcs, neg_mode, speed, duplex); 11282fa4e4b7SAndrew Lunn } 1129a853c68eSVladimir Oltean EXPORT_SYMBOL_GPL(xpcs_link_up); 11302fa4e4b7SAndrew Lunn 1131b47aec88SOng Boon Leong static void xpcs_an_restart(struct phylink_pcs *pcs) 1132b47aec88SOng Boon Leong { 1133b47aec88SOng Boon Leong struct dw_xpcs *xpcs = phylink_pcs_to_xpcs(pcs); 1134b47aec88SOng Boon Leong int ret; 1135b47aec88SOng Boon Leong 1136b47aec88SOng Boon Leong ret = xpcs_read(xpcs, MDIO_MMD_VEND2, MDIO_CTRL1); 1137b47aec88SOng Boon Leong if (ret >= 0) { 1138b47aec88SOng Boon Leong ret |= BMCR_ANRESTART; 1139b47aec88SOng Boon Leong xpcs_write(xpcs, MDIO_MMD_VEND2, MDIO_CTRL1, ret); 1140b47aec88SOng Boon Leong } 1141b47aec88SOng Boon Leong } 1142b47aec88SOng Boon Leong 11435673ef86SVladimir Oltean static u32 xpcs_get_id(struct dw_xpcs *xpcs) 11442fa4e4b7SAndrew Lunn { 11452fa4e4b7SAndrew Lunn int ret; 11462fa4e4b7SAndrew Lunn u32 id; 11472fa4e4b7SAndrew Lunn 1148b97b5331SOng Boon Leong /* First, search C73 PCS using PCS MMD */ 11492fa4e4b7SAndrew Lunn ret = xpcs_read(xpcs, MDIO_MMD_PCS, MII_PHYSID1); 11502fa4e4b7SAndrew Lunn if (ret < 0) 11512fa4e4b7SAndrew Lunn return 0xffffffff; 11522fa4e4b7SAndrew Lunn 11532fa4e4b7SAndrew Lunn id = ret << 16; 11542fa4e4b7SAndrew Lunn 11552fa4e4b7SAndrew Lunn ret = xpcs_read(xpcs, MDIO_MMD_PCS, MII_PHYSID2); 11562fa4e4b7SAndrew Lunn if (ret < 0) 11572fa4e4b7SAndrew Lunn return 0xffffffff; 11582fa4e4b7SAndrew Lunn 115936641b04SVladimir Oltean /* If Device IDs are not all zeros or all ones, 116036641b04SVladimir Oltean * we found C73 AN-type device 116136641b04SVladimir Oltean */ 116236641b04SVladimir Oltean if ((id | ret) && (id | ret) != 0xffffffff) 11632fa4e4b7SAndrew Lunn return id | ret; 1164b97b5331SOng Boon Leong 1165b97b5331SOng Boon Leong /* Next, search C37 PCS using Vendor-Specific MII MMD */ 1166b97b5331SOng Boon Leong ret = xpcs_read(xpcs, MDIO_MMD_VEND2, MII_PHYSID1); 1167b97b5331SOng Boon Leong if (ret < 0) 1168b97b5331SOng Boon Leong return 0xffffffff; 1169b97b5331SOng Boon Leong 1170b97b5331SOng Boon Leong id = ret << 16; 1171b97b5331SOng Boon Leong 1172b97b5331SOng Boon Leong ret = xpcs_read(xpcs, MDIO_MMD_VEND2, MII_PHYSID2); 1173b97b5331SOng Boon Leong if (ret < 0) 1174b97b5331SOng Boon Leong return 0xffffffff; 1175b97b5331SOng Boon Leong 1176b97b5331SOng Boon Leong /* If Device IDs are not all zeros, we found C37 AN-type device */ 1177b97b5331SOng Boon Leong if (id | ret) 1178b97b5331SOng Boon Leong return id | ret; 1179b97b5331SOng Boon Leong 1180b97b5331SOng Boon Leong return 0xffffffff; 11812fa4e4b7SAndrew Lunn } 11822fa4e4b7SAndrew Lunn 1183a54a8b71SVladimir Oltean static const struct xpcs_compat synopsys_xpcs_compat[DW_XPCS_INTERFACE_MAX] = { 1184a54a8b71SVladimir Oltean [DW_XPCS_USXGMII] = { 1185a54a8b71SVladimir Oltean .supported = xpcs_usxgmii_features, 1186a54a8b71SVladimir Oltean .interface = xpcs_usxgmii_interfaces, 1187a54a8b71SVladimir Oltean .num_interfaces = ARRAY_SIZE(xpcs_usxgmii_interfaces), 1188a54a8b71SVladimir Oltean .an_mode = DW_AN_C73, 1189a54a8b71SVladimir Oltean }, 1190a54a8b71SVladimir Oltean [DW_XPCS_10GKR] = { 1191a54a8b71SVladimir Oltean .supported = xpcs_10gkr_features, 1192a54a8b71SVladimir Oltean .interface = xpcs_10gkr_interfaces, 1193a54a8b71SVladimir Oltean .num_interfaces = ARRAY_SIZE(xpcs_10gkr_interfaces), 1194a54a8b71SVladimir Oltean .an_mode = DW_AN_C73, 1195a54a8b71SVladimir Oltean }, 1196a54a8b71SVladimir Oltean [DW_XPCS_XLGMII] = { 1197a54a8b71SVladimir Oltean .supported = xpcs_xlgmii_features, 1198a54a8b71SVladimir Oltean .interface = xpcs_xlgmii_interfaces, 1199a54a8b71SVladimir Oltean .num_interfaces = ARRAY_SIZE(xpcs_xlgmii_interfaces), 1200a54a8b71SVladimir Oltean .an_mode = DW_AN_C73, 1201a54a8b71SVladimir Oltean }, 1202af8de1e3SJiawen Wu [DW_XPCS_10GBASER] = { 1203af8de1e3SJiawen Wu .supported = xpcs_10gbaser_features, 1204af8de1e3SJiawen Wu .interface = xpcs_10gbaser_interfaces, 1205af8de1e3SJiawen Wu .num_interfaces = ARRAY_SIZE(xpcs_10gbaser_interfaces), 1206af8de1e3SJiawen Wu .an_mode = DW_10GBASER, 1207af8de1e3SJiawen Wu }, 1208a54a8b71SVladimir Oltean [DW_XPCS_SGMII] = { 1209a54a8b71SVladimir Oltean .supported = xpcs_sgmii_features, 1210a54a8b71SVladimir Oltean .interface = xpcs_sgmii_interfaces, 1211a54a8b71SVladimir Oltean .num_interfaces = ARRAY_SIZE(xpcs_sgmii_interfaces), 1212a54a8b71SVladimir Oltean .an_mode = DW_AN_C37_SGMII, 1213a54a8b71SVladimir Oltean }, 1214b47aec88SOng Boon Leong [DW_XPCS_1000BASEX] = { 1215b47aec88SOng Boon Leong .supported = xpcs_1000basex_features, 1216b47aec88SOng Boon Leong .interface = xpcs_1000basex_interfaces, 1217b47aec88SOng Boon Leong .num_interfaces = ARRAY_SIZE(xpcs_1000basex_interfaces), 1218b47aec88SOng Boon Leong .an_mode = DW_AN_C37_1000BASEX, 1219b47aec88SOng Boon Leong }, 1220f27abde3SVoon Weifeng [DW_XPCS_2500BASEX] = { 1221f27abde3SVoon Weifeng .supported = xpcs_2500basex_features, 1222f27abde3SVoon Weifeng .interface = xpcs_2500basex_interfaces, 122343fb622dSRussell King (Oracle) .num_interfaces = ARRAY_SIZE(xpcs_2500basex_interfaces), 1224f27abde3SVoon Weifeng .an_mode = DW_2500BASEX, 1225f27abde3SVoon Weifeng }, 1226a54a8b71SVladimir Oltean }; 1227a54a8b71SVladimir Oltean 1228dd0721eaSVladimir Oltean static const struct xpcs_compat nxp_sja1105_xpcs_compat[DW_XPCS_INTERFACE_MAX] = { 1229dd0721eaSVladimir Oltean [DW_XPCS_SGMII] = { 1230dd0721eaSVladimir Oltean .supported = xpcs_sgmii_features, 1231dd0721eaSVladimir Oltean .interface = xpcs_sgmii_interfaces, 1232dd0721eaSVladimir Oltean .num_interfaces = ARRAY_SIZE(xpcs_sgmii_interfaces), 1233dd0721eaSVladimir Oltean .an_mode = DW_AN_C37_SGMII, 1234dd0721eaSVladimir Oltean .pma_config = nxp_sja1105_sgmii_pma_config, 1235dd0721eaSVladimir Oltean }, 1236dd0721eaSVladimir Oltean }; 1237dd0721eaSVladimir Oltean 1238f7380bbaSVladimir Oltean static const struct xpcs_compat nxp_sja1110_xpcs_compat[DW_XPCS_INTERFACE_MAX] = { 1239f7380bbaSVladimir Oltean [DW_XPCS_SGMII] = { 1240f7380bbaSVladimir Oltean .supported = xpcs_sgmii_features, 1241f7380bbaSVladimir Oltean .interface = xpcs_sgmii_interfaces, 1242f7380bbaSVladimir Oltean .num_interfaces = ARRAY_SIZE(xpcs_sgmii_interfaces), 1243f7380bbaSVladimir Oltean .an_mode = DW_AN_C37_SGMII, 1244f7380bbaSVladimir Oltean .pma_config = nxp_sja1110_sgmii_pma_config, 1245f7380bbaSVladimir Oltean }, 1246f7380bbaSVladimir Oltean [DW_XPCS_2500BASEX] = { 1247f7380bbaSVladimir Oltean .supported = xpcs_2500basex_features, 1248f7380bbaSVladimir Oltean .interface = xpcs_2500basex_interfaces, 1249f7380bbaSVladimir Oltean .num_interfaces = ARRAY_SIZE(xpcs_2500basex_interfaces), 1250f7380bbaSVladimir Oltean .an_mode = DW_2500BASEX, 1251f7380bbaSVladimir Oltean .pma_config = nxp_sja1110_2500basex_pma_config, 1252f7380bbaSVladimir Oltean }, 1253f7380bbaSVladimir Oltean }; 1254f7380bbaSVladimir Oltean 1255a54a8b71SVladimir Oltean static const struct xpcs_id xpcs_id_list[] = { 1256a54a8b71SVladimir Oltean { 1257a54a8b71SVladimir Oltean .id = SYNOPSYS_XPCS_ID, 1258a54a8b71SVladimir Oltean .mask = SYNOPSYS_XPCS_MASK, 1259a54a8b71SVladimir Oltean .compat = synopsys_xpcs_compat, 1260dd0721eaSVladimir Oltean }, { 1261dd0721eaSVladimir Oltean .id = NXP_SJA1105_XPCS_ID, 1262dd0721eaSVladimir Oltean .mask = SYNOPSYS_XPCS_MASK, 1263dd0721eaSVladimir Oltean .compat = nxp_sja1105_xpcs_compat, 1264f7380bbaSVladimir Oltean }, { 1265f7380bbaSVladimir Oltean .id = NXP_SJA1110_XPCS_ID, 1266f7380bbaSVladimir Oltean .mask = SYNOPSYS_XPCS_MASK, 1267f7380bbaSVladimir Oltean .compat = nxp_sja1110_xpcs_compat, 1268a54a8b71SVladimir Oltean }, 1269a54a8b71SVladimir Oltean }; 1270a54a8b71SVladimir Oltean 127111059740SVladimir Oltean static const struct phylink_pcs_ops xpcs_phylink_ops = { 1272fe70fb74SRussell King (Oracle) .pcs_validate = xpcs_validate, 127311059740SVladimir Oltean .pcs_config = xpcs_config, 127411059740SVladimir Oltean .pcs_get_state = xpcs_get_state, 1275b47aec88SOng Boon Leong .pcs_an_restart = xpcs_an_restart, 127611059740SVladimir Oltean .pcs_link_up = xpcs_link_up, 127711059740SVladimir Oltean }; 127811059740SVladimir Oltean 12794739b9f3SRussell King (Oracle) static struct dw_xpcs *xpcs_create(struct mdio_device *mdiodev, 12802cac15daSVladimir Oltean phy_interface_t interface) 12812fa4e4b7SAndrew Lunn { 12825673ef86SVladimir Oltean struct dw_xpcs *xpcs; 12832cac15daSVladimir Oltean u32 xpcs_id; 12842cac15daSVladimir Oltean int i, ret; 12852cac15daSVladimir Oltean 12862cac15daSVladimir Oltean xpcs = kzalloc(sizeof(*xpcs), GFP_KERNEL); 12872cac15daSVladimir Oltean if (!xpcs) 12882cad5d2eSWong Vee Khee return ERR_PTR(-ENOMEM); 12892cac15daSVladimir Oltean 12909a5d500cSRussell King (Oracle) mdio_device_get(mdiodev); 12912cac15daSVladimir Oltean xpcs->mdiodev = mdiodev; 12922cac15daSVladimir Oltean 12932cac15daSVladimir Oltean xpcs_id = xpcs_get_id(xpcs); 12942fa4e4b7SAndrew Lunn 12952fa4e4b7SAndrew Lunn for (i = 0; i < ARRAY_SIZE(xpcs_id_list); i++) { 1296a54a8b71SVladimir Oltean const struct xpcs_id *entry = &xpcs_id_list[i]; 12979900074eSVladimir Oltean const struct xpcs_compat *compat; 12982fa4e4b7SAndrew Lunn 12999900074eSVladimir Oltean if ((xpcs_id & entry->mask) != entry->id) 13009900074eSVladimir Oltean continue; 13012fa4e4b7SAndrew Lunn 13029900074eSVladimir Oltean xpcs->id = entry; 13039900074eSVladimir Oltean 13049900074eSVladimir Oltean compat = xpcs_find_compat(entry, interface); 13052cac15daSVladimir Oltean if (!compat) { 13062cac15daSVladimir Oltean ret = -ENODEV; 13072cac15daSVladimir Oltean goto out; 13082fa4e4b7SAndrew Lunn } 13092fa4e4b7SAndrew Lunn 1310*d55595f0SJiawen Wu ret = xpcs_dev_flag(xpcs); 1311*d55595f0SJiawen Wu if (ret) 1312*d55595f0SJiawen Wu goto out; 1313*d55595f0SJiawen Wu 131411059740SVladimir Oltean xpcs->pcs.ops = &xpcs_phylink_ops; 1315a3a47cfbSRussell King (Oracle) xpcs->pcs.neg_mode = true; 1316af8de1e3SJiawen Wu if (compat->an_mode == DW_10GBASER) 1317af8de1e3SJiawen Wu return xpcs; 1318af8de1e3SJiawen Wu 131911059740SVladimir Oltean xpcs->pcs.poll = true; 132011059740SVladimir Oltean 1321*d55595f0SJiawen Wu if (xpcs->dev_flag != DW_DEV_TXGBE) { 13222cac15daSVladimir Oltean ret = xpcs_soft_reset(xpcs, compat); 13232cac15daSVladimir Oltean if (ret) 13242cac15daSVladimir Oltean goto out; 1325*d55595f0SJiawen Wu } 13262cac15daSVladimir Oltean 13272cac15daSVladimir Oltean return xpcs; 13282fa4e4b7SAndrew Lunn } 13292cac15daSVladimir Oltean 13302cac15daSVladimir Oltean ret = -ENODEV; 13312cac15daSVladimir Oltean 13322cac15daSVladimir Oltean out: 13339a5d500cSRussell King (Oracle) mdio_device_put(mdiodev); 13342cac15daSVladimir Oltean kfree(xpcs); 13352cac15daSVladimir Oltean 13362cac15daSVladimir Oltean return ERR_PTR(ret); 13372cac15daSVladimir Oltean } 13382cac15daSVladimir Oltean 13395673ef86SVladimir Oltean void xpcs_destroy(struct dw_xpcs *xpcs) 13402cac15daSVladimir Oltean { 13419a5d500cSRussell King (Oracle) if (xpcs) 13429a5d500cSRussell King (Oracle) mdio_device_put(xpcs->mdiodev); 13432cac15daSVladimir Oltean kfree(xpcs); 13442cac15daSVladimir Oltean } 13452cac15daSVladimir Oltean EXPORT_SYMBOL_GPL(xpcs_destroy); 13462fa4e4b7SAndrew Lunn 13479a5d500cSRussell King (Oracle) struct dw_xpcs *xpcs_create_mdiodev(struct mii_bus *bus, int addr, 13489a5d500cSRussell King (Oracle) phy_interface_t interface) 13499a5d500cSRussell King (Oracle) { 13509a5d500cSRussell King (Oracle) struct mdio_device *mdiodev; 13519a5d500cSRussell King (Oracle) struct dw_xpcs *xpcs; 13529a5d500cSRussell King (Oracle) 13539a5d500cSRussell King (Oracle) mdiodev = mdio_device_create(bus, addr); 13549a5d500cSRussell King (Oracle) if (IS_ERR(mdiodev)) 13559a5d500cSRussell King (Oracle) return ERR_CAST(mdiodev); 13569a5d500cSRussell King (Oracle) 13579a5d500cSRussell King (Oracle) xpcs = xpcs_create(mdiodev, interface); 13589a5d500cSRussell King (Oracle) 13599a5d500cSRussell King (Oracle) /* xpcs_create() has taken a refcount on the mdiodev if it was 13609a5d500cSRussell King (Oracle) * successful. If xpcs_create() fails, this will free the mdio 13619a5d500cSRussell King (Oracle) * device here. In any case, we don't need to hold our reference 13629a5d500cSRussell King (Oracle) * anymore, and putting it here will allow mdio_device_put() in 13639a5d500cSRussell King (Oracle) * xpcs_destroy() to automatically free the mdio device. 13649a5d500cSRussell King (Oracle) */ 13659a5d500cSRussell King (Oracle) mdio_device_put(mdiodev); 13669a5d500cSRussell King (Oracle) 13679a5d500cSRussell King (Oracle) return xpcs; 13689a5d500cSRussell King (Oracle) } 13699a5d500cSRussell King (Oracle) EXPORT_SYMBOL_GPL(xpcs_create_mdiodev); 13709a5d500cSRussell King (Oracle) 13712fa4e4b7SAndrew Lunn MODULE_LICENSE("GPL v2"); 1372