162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (c) 2020 Synopsys, Inc. and/or its affiliates. 462306a36Sopenharmony_ci * Synopsys DesignWare XPCS helpers 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * Author: Jose Abreu <Jose.Abreu@synopsys.com> 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include <linux/delay.h> 1062306a36Sopenharmony_ci#include <linux/pcs/pcs-xpcs.h> 1162306a36Sopenharmony_ci#include <linux/mdio.h> 1262306a36Sopenharmony_ci#include <linux/phylink.h> 1362306a36Sopenharmony_ci#include <linux/workqueue.h> 1462306a36Sopenharmony_ci#include "pcs-xpcs.h" 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci#define phylink_pcs_to_xpcs(pl_pcs) \ 1762306a36Sopenharmony_ci container_of((pl_pcs), struct dw_xpcs, pcs) 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_cistatic const int xpcs_usxgmii_features[] = { 2062306a36Sopenharmony_ci ETHTOOL_LINK_MODE_Pause_BIT, 2162306a36Sopenharmony_ci ETHTOOL_LINK_MODE_Asym_Pause_BIT, 2262306a36Sopenharmony_ci ETHTOOL_LINK_MODE_Autoneg_BIT, 2362306a36Sopenharmony_ci ETHTOOL_LINK_MODE_1000baseKX_Full_BIT, 2462306a36Sopenharmony_ci ETHTOOL_LINK_MODE_10000baseKX4_Full_BIT, 2562306a36Sopenharmony_ci ETHTOOL_LINK_MODE_10000baseKR_Full_BIT, 2662306a36Sopenharmony_ci ETHTOOL_LINK_MODE_2500baseX_Full_BIT, 2762306a36Sopenharmony_ci __ETHTOOL_LINK_MODE_MASK_NBITS, 2862306a36Sopenharmony_ci}; 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_cistatic const int xpcs_10gkr_features[] = { 3162306a36Sopenharmony_ci ETHTOOL_LINK_MODE_Pause_BIT, 3262306a36Sopenharmony_ci ETHTOOL_LINK_MODE_Asym_Pause_BIT, 3362306a36Sopenharmony_ci ETHTOOL_LINK_MODE_10000baseKR_Full_BIT, 3462306a36Sopenharmony_ci __ETHTOOL_LINK_MODE_MASK_NBITS, 3562306a36Sopenharmony_ci}; 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_cistatic const int xpcs_xlgmii_features[] = { 3862306a36Sopenharmony_ci ETHTOOL_LINK_MODE_Pause_BIT, 3962306a36Sopenharmony_ci ETHTOOL_LINK_MODE_Asym_Pause_BIT, 4062306a36Sopenharmony_ci ETHTOOL_LINK_MODE_25000baseCR_Full_BIT, 4162306a36Sopenharmony_ci ETHTOOL_LINK_MODE_25000baseKR_Full_BIT, 4262306a36Sopenharmony_ci ETHTOOL_LINK_MODE_25000baseSR_Full_BIT, 4362306a36Sopenharmony_ci ETHTOOL_LINK_MODE_40000baseKR4_Full_BIT, 4462306a36Sopenharmony_ci ETHTOOL_LINK_MODE_40000baseCR4_Full_BIT, 4562306a36Sopenharmony_ci ETHTOOL_LINK_MODE_40000baseSR4_Full_BIT, 4662306a36Sopenharmony_ci ETHTOOL_LINK_MODE_40000baseLR4_Full_BIT, 4762306a36Sopenharmony_ci ETHTOOL_LINK_MODE_50000baseCR2_Full_BIT, 4862306a36Sopenharmony_ci ETHTOOL_LINK_MODE_50000baseKR2_Full_BIT, 4962306a36Sopenharmony_ci ETHTOOL_LINK_MODE_50000baseSR2_Full_BIT, 5062306a36Sopenharmony_ci ETHTOOL_LINK_MODE_50000baseKR_Full_BIT, 5162306a36Sopenharmony_ci ETHTOOL_LINK_MODE_50000baseSR_Full_BIT, 5262306a36Sopenharmony_ci ETHTOOL_LINK_MODE_50000baseCR_Full_BIT, 5362306a36Sopenharmony_ci ETHTOOL_LINK_MODE_50000baseLR_ER_FR_Full_BIT, 5462306a36Sopenharmony_ci ETHTOOL_LINK_MODE_50000baseDR_Full_BIT, 5562306a36Sopenharmony_ci ETHTOOL_LINK_MODE_100000baseKR4_Full_BIT, 5662306a36Sopenharmony_ci ETHTOOL_LINK_MODE_100000baseSR4_Full_BIT, 5762306a36Sopenharmony_ci ETHTOOL_LINK_MODE_100000baseCR4_Full_BIT, 5862306a36Sopenharmony_ci ETHTOOL_LINK_MODE_100000baseLR4_ER4_Full_BIT, 5962306a36Sopenharmony_ci ETHTOOL_LINK_MODE_100000baseKR2_Full_BIT, 6062306a36Sopenharmony_ci ETHTOOL_LINK_MODE_100000baseSR2_Full_BIT, 6162306a36Sopenharmony_ci ETHTOOL_LINK_MODE_100000baseCR2_Full_BIT, 6262306a36Sopenharmony_ci ETHTOOL_LINK_MODE_100000baseLR2_ER2_FR2_Full_BIT, 6362306a36Sopenharmony_ci ETHTOOL_LINK_MODE_100000baseDR2_Full_BIT, 6462306a36Sopenharmony_ci __ETHTOOL_LINK_MODE_MASK_NBITS, 6562306a36Sopenharmony_ci}; 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_cistatic const int xpcs_10gbaser_features[] = { 6862306a36Sopenharmony_ci ETHTOOL_LINK_MODE_Pause_BIT, 6962306a36Sopenharmony_ci ETHTOOL_LINK_MODE_Asym_Pause_BIT, 7062306a36Sopenharmony_ci ETHTOOL_LINK_MODE_10000baseSR_Full_BIT, 7162306a36Sopenharmony_ci ETHTOOL_LINK_MODE_10000baseLR_Full_BIT, 7262306a36Sopenharmony_ci ETHTOOL_LINK_MODE_10000baseLRM_Full_BIT, 7362306a36Sopenharmony_ci ETHTOOL_LINK_MODE_10000baseER_Full_BIT, 7462306a36Sopenharmony_ci __ETHTOOL_LINK_MODE_MASK_NBITS, 7562306a36Sopenharmony_ci}; 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_cistatic const int xpcs_sgmii_features[] = { 7862306a36Sopenharmony_ci ETHTOOL_LINK_MODE_Pause_BIT, 7962306a36Sopenharmony_ci ETHTOOL_LINK_MODE_Asym_Pause_BIT, 8062306a36Sopenharmony_ci ETHTOOL_LINK_MODE_Autoneg_BIT, 8162306a36Sopenharmony_ci ETHTOOL_LINK_MODE_10baseT_Half_BIT, 8262306a36Sopenharmony_ci ETHTOOL_LINK_MODE_10baseT_Full_BIT, 8362306a36Sopenharmony_ci ETHTOOL_LINK_MODE_100baseT_Half_BIT, 8462306a36Sopenharmony_ci ETHTOOL_LINK_MODE_100baseT_Full_BIT, 8562306a36Sopenharmony_ci ETHTOOL_LINK_MODE_1000baseT_Half_BIT, 8662306a36Sopenharmony_ci ETHTOOL_LINK_MODE_1000baseT_Full_BIT, 8762306a36Sopenharmony_ci __ETHTOOL_LINK_MODE_MASK_NBITS, 8862306a36Sopenharmony_ci}; 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_cistatic const int xpcs_1000basex_features[] = { 9162306a36Sopenharmony_ci ETHTOOL_LINK_MODE_Pause_BIT, 9262306a36Sopenharmony_ci ETHTOOL_LINK_MODE_Asym_Pause_BIT, 9362306a36Sopenharmony_ci ETHTOOL_LINK_MODE_Autoneg_BIT, 9462306a36Sopenharmony_ci ETHTOOL_LINK_MODE_1000baseX_Full_BIT, 9562306a36Sopenharmony_ci __ETHTOOL_LINK_MODE_MASK_NBITS, 9662306a36Sopenharmony_ci}; 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_cistatic const int xpcs_2500basex_features[] = { 9962306a36Sopenharmony_ci ETHTOOL_LINK_MODE_Pause_BIT, 10062306a36Sopenharmony_ci ETHTOOL_LINK_MODE_Asym_Pause_BIT, 10162306a36Sopenharmony_ci ETHTOOL_LINK_MODE_Autoneg_BIT, 10262306a36Sopenharmony_ci ETHTOOL_LINK_MODE_2500baseX_Full_BIT, 10362306a36Sopenharmony_ci ETHTOOL_LINK_MODE_2500baseT_Full_BIT, 10462306a36Sopenharmony_ci __ETHTOOL_LINK_MODE_MASK_NBITS, 10562306a36Sopenharmony_ci}; 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_cistatic const phy_interface_t xpcs_usxgmii_interfaces[] = { 10862306a36Sopenharmony_ci PHY_INTERFACE_MODE_USXGMII, 10962306a36Sopenharmony_ci}; 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_cistatic const phy_interface_t xpcs_10gkr_interfaces[] = { 11262306a36Sopenharmony_ci PHY_INTERFACE_MODE_10GKR, 11362306a36Sopenharmony_ci}; 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_cistatic const phy_interface_t xpcs_xlgmii_interfaces[] = { 11662306a36Sopenharmony_ci PHY_INTERFACE_MODE_XLGMII, 11762306a36Sopenharmony_ci}; 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_cistatic const phy_interface_t xpcs_10gbaser_interfaces[] = { 12062306a36Sopenharmony_ci PHY_INTERFACE_MODE_10GBASER, 12162306a36Sopenharmony_ci}; 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_cistatic const phy_interface_t xpcs_sgmii_interfaces[] = { 12462306a36Sopenharmony_ci PHY_INTERFACE_MODE_SGMII, 12562306a36Sopenharmony_ci}; 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_cistatic const phy_interface_t xpcs_1000basex_interfaces[] = { 12862306a36Sopenharmony_ci PHY_INTERFACE_MODE_1000BASEX, 12962306a36Sopenharmony_ci}; 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_cistatic const phy_interface_t xpcs_2500basex_interfaces[] = { 13262306a36Sopenharmony_ci PHY_INTERFACE_MODE_2500BASEX, 13362306a36Sopenharmony_ci PHY_INTERFACE_MODE_MAX, 13462306a36Sopenharmony_ci}; 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_cienum { 13762306a36Sopenharmony_ci DW_XPCS_USXGMII, 13862306a36Sopenharmony_ci DW_XPCS_10GKR, 13962306a36Sopenharmony_ci DW_XPCS_XLGMII, 14062306a36Sopenharmony_ci DW_XPCS_10GBASER, 14162306a36Sopenharmony_ci DW_XPCS_SGMII, 14262306a36Sopenharmony_ci DW_XPCS_1000BASEX, 14362306a36Sopenharmony_ci DW_XPCS_2500BASEX, 14462306a36Sopenharmony_ci DW_XPCS_INTERFACE_MAX, 14562306a36Sopenharmony_ci}; 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_cistruct xpcs_compat { 14862306a36Sopenharmony_ci const int *supported; 14962306a36Sopenharmony_ci const phy_interface_t *interface; 15062306a36Sopenharmony_ci int num_interfaces; 15162306a36Sopenharmony_ci int an_mode; 15262306a36Sopenharmony_ci int (*pma_config)(struct dw_xpcs *xpcs); 15362306a36Sopenharmony_ci}; 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_cistruct xpcs_id { 15662306a36Sopenharmony_ci u32 id; 15762306a36Sopenharmony_ci u32 mask; 15862306a36Sopenharmony_ci const struct xpcs_compat *compat; 15962306a36Sopenharmony_ci}; 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_cistatic const struct xpcs_compat *xpcs_find_compat(const struct xpcs_id *id, 16262306a36Sopenharmony_ci phy_interface_t interface) 16362306a36Sopenharmony_ci{ 16462306a36Sopenharmony_ci int i, j; 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci for (i = 0; i < DW_XPCS_INTERFACE_MAX; i++) { 16762306a36Sopenharmony_ci const struct xpcs_compat *compat = &id->compat[i]; 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci for (j = 0; j < compat->num_interfaces; j++) 17062306a36Sopenharmony_ci if (compat->interface[j] == interface) 17162306a36Sopenharmony_ci return compat; 17262306a36Sopenharmony_ci } 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci return NULL; 17562306a36Sopenharmony_ci} 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ciint xpcs_get_an_mode(struct dw_xpcs *xpcs, phy_interface_t interface) 17862306a36Sopenharmony_ci{ 17962306a36Sopenharmony_ci const struct xpcs_compat *compat; 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci compat = xpcs_find_compat(xpcs->id, interface); 18262306a36Sopenharmony_ci if (!compat) 18362306a36Sopenharmony_ci return -ENODEV; 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci return compat->an_mode; 18662306a36Sopenharmony_ci} 18762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(xpcs_get_an_mode); 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_cistatic bool __xpcs_linkmode_supported(const struct xpcs_compat *compat, 19062306a36Sopenharmony_ci enum ethtool_link_mode_bit_indices linkmode) 19162306a36Sopenharmony_ci{ 19262306a36Sopenharmony_ci int i; 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci for (i = 0; compat->supported[i] != __ETHTOOL_LINK_MODE_MASK_NBITS; i++) 19562306a36Sopenharmony_ci if (compat->supported[i] == linkmode) 19662306a36Sopenharmony_ci return true; 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci return false; 19962306a36Sopenharmony_ci} 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci#define xpcs_linkmode_supported(compat, mode) \ 20262306a36Sopenharmony_ci __xpcs_linkmode_supported(compat, ETHTOOL_LINK_MODE_ ## mode ## _BIT) 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ciint xpcs_read(struct dw_xpcs *xpcs, int dev, u32 reg) 20562306a36Sopenharmony_ci{ 20662306a36Sopenharmony_ci return mdiodev_c45_read(xpcs->mdiodev, dev, reg); 20762306a36Sopenharmony_ci} 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ciint xpcs_write(struct dw_xpcs *xpcs, int dev, u32 reg, u16 val) 21062306a36Sopenharmony_ci{ 21162306a36Sopenharmony_ci return mdiodev_c45_write(xpcs->mdiodev, dev, reg, val); 21262306a36Sopenharmony_ci} 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_cistatic int xpcs_modify_changed(struct dw_xpcs *xpcs, int dev, u32 reg, 21562306a36Sopenharmony_ci u16 mask, u16 set) 21662306a36Sopenharmony_ci{ 21762306a36Sopenharmony_ci return mdiodev_c45_modify_changed(xpcs->mdiodev, dev, reg, mask, set); 21862306a36Sopenharmony_ci} 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_cistatic int xpcs_read_vendor(struct dw_xpcs *xpcs, int dev, u32 reg) 22162306a36Sopenharmony_ci{ 22262306a36Sopenharmony_ci return xpcs_read(xpcs, dev, DW_VENDOR | reg); 22362306a36Sopenharmony_ci} 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_cistatic int xpcs_write_vendor(struct dw_xpcs *xpcs, int dev, int reg, 22662306a36Sopenharmony_ci u16 val) 22762306a36Sopenharmony_ci{ 22862306a36Sopenharmony_ci return xpcs_write(xpcs, dev, DW_VENDOR | reg, val); 22962306a36Sopenharmony_ci} 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ciint xpcs_read_vpcs(struct dw_xpcs *xpcs, int reg) 23262306a36Sopenharmony_ci{ 23362306a36Sopenharmony_ci return xpcs_read_vendor(xpcs, MDIO_MMD_PCS, reg); 23462306a36Sopenharmony_ci} 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ciint xpcs_write_vpcs(struct dw_xpcs *xpcs, int reg, u16 val) 23762306a36Sopenharmony_ci{ 23862306a36Sopenharmony_ci return xpcs_write_vendor(xpcs, MDIO_MMD_PCS, reg, val); 23962306a36Sopenharmony_ci} 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_cistatic int xpcs_dev_flag(struct dw_xpcs *xpcs) 24262306a36Sopenharmony_ci{ 24362306a36Sopenharmony_ci int ret, oui; 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci ret = xpcs_read(xpcs, MDIO_MMD_PMAPMD, MDIO_DEVID1); 24662306a36Sopenharmony_ci if (ret < 0) 24762306a36Sopenharmony_ci return ret; 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci oui = ret; 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ci ret = xpcs_read(xpcs, MDIO_MMD_PMAPMD, MDIO_DEVID2); 25262306a36Sopenharmony_ci if (ret < 0) 25362306a36Sopenharmony_ci return ret; 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci ret = (ret >> 10) & 0x3F; 25662306a36Sopenharmony_ci oui |= ret << 16; 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci if (oui == DW_OUI_WX) 25962306a36Sopenharmony_ci xpcs->dev_flag = DW_DEV_TXGBE; 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci return 0; 26262306a36Sopenharmony_ci} 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_cistatic int xpcs_poll_reset(struct dw_xpcs *xpcs, int dev) 26562306a36Sopenharmony_ci{ 26662306a36Sopenharmony_ci /* Poll until the reset bit clears (50ms per retry == 0.6 sec) */ 26762306a36Sopenharmony_ci unsigned int retries = 12; 26862306a36Sopenharmony_ci int ret; 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci do { 27162306a36Sopenharmony_ci msleep(50); 27262306a36Sopenharmony_ci ret = xpcs_read(xpcs, dev, MDIO_CTRL1); 27362306a36Sopenharmony_ci if (ret < 0) 27462306a36Sopenharmony_ci return ret; 27562306a36Sopenharmony_ci } while (ret & MDIO_CTRL1_RESET && --retries); 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci return (ret & MDIO_CTRL1_RESET) ? -ETIMEDOUT : 0; 27862306a36Sopenharmony_ci} 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_cistatic int xpcs_soft_reset(struct dw_xpcs *xpcs, 28162306a36Sopenharmony_ci const struct xpcs_compat *compat) 28262306a36Sopenharmony_ci{ 28362306a36Sopenharmony_ci int ret, dev; 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci switch (compat->an_mode) { 28662306a36Sopenharmony_ci case DW_AN_C73: 28762306a36Sopenharmony_ci case DW_10GBASER: 28862306a36Sopenharmony_ci dev = MDIO_MMD_PCS; 28962306a36Sopenharmony_ci break; 29062306a36Sopenharmony_ci case DW_AN_C37_SGMII: 29162306a36Sopenharmony_ci case DW_2500BASEX: 29262306a36Sopenharmony_ci case DW_AN_C37_1000BASEX: 29362306a36Sopenharmony_ci dev = MDIO_MMD_VEND2; 29462306a36Sopenharmony_ci break; 29562306a36Sopenharmony_ci default: 29662306a36Sopenharmony_ci return -1; 29762306a36Sopenharmony_ci } 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci ret = xpcs_write(xpcs, dev, MDIO_CTRL1, MDIO_CTRL1_RESET); 30062306a36Sopenharmony_ci if (ret < 0) 30162306a36Sopenharmony_ci return ret; 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ci return xpcs_poll_reset(xpcs, dev); 30462306a36Sopenharmony_ci} 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci#define xpcs_warn(__xpcs, __state, __args...) \ 30762306a36Sopenharmony_ci({ \ 30862306a36Sopenharmony_ci if ((__state)->link) \ 30962306a36Sopenharmony_ci dev_warn(&(__xpcs)->mdiodev->dev, ##__args); \ 31062306a36Sopenharmony_ci}) 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_cistatic int xpcs_read_fault_c73(struct dw_xpcs *xpcs, 31362306a36Sopenharmony_ci struct phylink_link_state *state, 31462306a36Sopenharmony_ci u16 pcs_stat1) 31562306a36Sopenharmony_ci{ 31662306a36Sopenharmony_ci int ret; 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_ci if (pcs_stat1 & MDIO_STAT1_FAULT) { 31962306a36Sopenharmony_ci xpcs_warn(xpcs, state, "Link fault condition detected!\n"); 32062306a36Sopenharmony_ci return -EFAULT; 32162306a36Sopenharmony_ci } 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_ci ret = xpcs_read(xpcs, MDIO_MMD_PCS, MDIO_STAT2); 32462306a36Sopenharmony_ci if (ret < 0) 32562306a36Sopenharmony_ci return ret; 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci if (ret & MDIO_STAT2_RXFAULT) 32862306a36Sopenharmony_ci xpcs_warn(xpcs, state, "Receiver fault detected!\n"); 32962306a36Sopenharmony_ci if (ret & MDIO_STAT2_TXFAULT) 33062306a36Sopenharmony_ci xpcs_warn(xpcs, state, "Transmitter fault detected!\n"); 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_ci ret = xpcs_read_vendor(xpcs, MDIO_MMD_PCS, DW_VR_XS_PCS_DIG_STS); 33362306a36Sopenharmony_ci if (ret < 0) 33462306a36Sopenharmony_ci return ret; 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_ci if (ret & DW_RXFIFO_ERR) { 33762306a36Sopenharmony_ci xpcs_warn(xpcs, state, "FIFO fault condition detected!\n"); 33862306a36Sopenharmony_ci return -EFAULT; 33962306a36Sopenharmony_ci } 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci ret = xpcs_read(xpcs, MDIO_MMD_PCS, MDIO_PCS_10GBRT_STAT1); 34262306a36Sopenharmony_ci if (ret < 0) 34362306a36Sopenharmony_ci return ret; 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci if (!(ret & MDIO_PCS_10GBRT_STAT1_BLKLK)) 34662306a36Sopenharmony_ci xpcs_warn(xpcs, state, "Link is not locked!\n"); 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci ret = xpcs_read(xpcs, MDIO_MMD_PCS, MDIO_PCS_10GBRT_STAT2); 34962306a36Sopenharmony_ci if (ret < 0) 35062306a36Sopenharmony_ci return ret; 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_ci if (ret & MDIO_PCS_10GBRT_STAT2_ERR) { 35362306a36Sopenharmony_ci xpcs_warn(xpcs, state, "Link has errors!\n"); 35462306a36Sopenharmony_ci return -EFAULT; 35562306a36Sopenharmony_ci } 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_ci return 0; 35862306a36Sopenharmony_ci} 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_cistatic void xpcs_config_usxgmii(struct dw_xpcs *xpcs, int speed) 36162306a36Sopenharmony_ci{ 36262306a36Sopenharmony_ci int ret, speed_sel; 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_ci switch (speed) { 36562306a36Sopenharmony_ci case SPEED_10: 36662306a36Sopenharmony_ci speed_sel = DW_USXGMII_10; 36762306a36Sopenharmony_ci break; 36862306a36Sopenharmony_ci case SPEED_100: 36962306a36Sopenharmony_ci speed_sel = DW_USXGMII_100; 37062306a36Sopenharmony_ci break; 37162306a36Sopenharmony_ci case SPEED_1000: 37262306a36Sopenharmony_ci speed_sel = DW_USXGMII_1000; 37362306a36Sopenharmony_ci break; 37462306a36Sopenharmony_ci case SPEED_2500: 37562306a36Sopenharmony_ci speed_sel = DW_USXGMII_2500; 37662306a36Sopenharmony_ci break; 37762306a36Sopenharmony_ci case SPEED_5000: 37862306a36Sopenharmony_ci speed_sel = DW_USXGMII_5000; 37962306a36Sopenharmony_ci break; 38062306a36Sopenharmony_ci case SPEED_10000: 38162306a36Sopenharmony_ci speed_sel = DW_USXGMII_10000; 38262306a36Sopenharmony_ci break; 38362306a36Sopenharmony_ci default: 38462306a36Sopenharmony_ci /* Nothing to do here */ 38562306a36Sopenharmony_ci return; 38662306a36Sopenharmony_ci } 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_ci ret = xpcs_read_vpcs(xpcs, MDIO_CTRL1); 38962306a36Sopenharmony_ci if (ret < 0) 39062306a36Sopenharmony_ci goto out; 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ci ret = xpcs_write_vpcs(xpcs, MDIO_CTRL1, ret | DW_USXGMII_EN); 39362306a36Sopenharmony_ci if (ret < 0) 39462306a36Sopenharmony_ci goto out; 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_ci ret = xpcs_read(xpcs, MDIO_MMD_VEND2, MDIO_CTRL1); 39762306a36Sopenharmony_ci if (ret < 0) 39862306a36Sopenharmony_ci goto out; 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_ci ret &= ~DW_USXGMII_SS_MASK; 40162306a36Sopenharmony_ci ret |= speed_sel | DW_USXGMII_FULL; 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_ci ret = xpcs_write(xpcs, MDIO_MMD_VEND2, MDIO_CTRL1, ret); 40462306a36Sopenharmony_ci if (ret < 0) 40562306a36Sopenharmony_ci goto out; 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_ci ret = xpcs_read_vpcs(xpcs, MDIO_CTRL1); 40862306a36Sopenharmony_ci if (ret < 0) 40962306a36Sopenharmony_ci goto out; 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_ci ret = xpcs_write_vpcs(xpcs, MDIO_CTRL1, ret | DW_USXGMII_RST); 41262306a36Sopenharmony_ci if (ret < 0) 41362306a36Sopenharmony_ci goto out; 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_ci return; 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_ciout: 41862306a36Sopenharmony_ci pr_err("%s: XPCS access returned %pe\n", __func__, ERR_PTR(ret)); 41962306a36Sopenharmony_ci} 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_cistatic int _xpcs_config_aneg_c73(struct dw_xpcs *xpcs, 42262306a36Sopenharmony_ci const struct xpcs_compat *compat) 42362306a36Sopenharmony_ci{ 42462306a36Sopenharmony_ci int ret, adv; 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_ci /* By default, in USXGMII mode XPCS operates at 10G baud and 42762306a36Sopenharmony_ci * replicates data to achieve lower speeds. Hereby, in this 42862306a36Sopenharmony_ci * default configuration we need to advertise all supported 42962306a36Sopenharmony_ci * modes and not only the ones we want to use. 43062306a36Sopenharmony_ci */ 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_ci /* SR_AN_ADV3 */ 43362306a36Sopenharmony_ci adv = 0; 43462306a36Sopenharmony_ci if (xpcs_linkmode_supported(compat, 2500baseX_Full)) 43562306a36Sopenharmony_ci adv |= DW_C73_2500KX; 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_ci /* TODO: 5000baseKR */ 43862306a36Sopenharmony_ci 43962306a36Sopenharmony_ci ret = xpcs_write(xpcs, MDIO_MMD_AN, DW_SR_AN_ADV3, adv); 44062306a36Sopenharmony_ci if (ret < 0) 44162306a36Sopenharmony_ci return ret; 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_ci /* SR_AN_ADV2 */ 44462306a36Sopenharmony_ci adv = 0; 44562306a36Sopenharmony_ci if (xpcs_linkmode_supported(compat, 1000baseKX_Full)) 44662306a36Sopenharmony_ci adv |= DW_C73_1000KX; 44762306a36Sopenharmony_ci if (xpcs_linkmode_supported(compat, 10000baseKX4_Full)) 44862306a36Sopenharmony_ci adv |= DW_C73_10000KX4; 44962306a36Sopenharmony_ci if (xpcs_linkmode_supported(compat, 10000baseKR_Full)) 45062306a36Sopenharmony_ci adv |= DW_C73_10000KR; 45162306a36Sopenharmony_ci 45262306a36Sopenharmony_ci ret = xpcs_write(xpcs, MDIO_MMD_AN, DW_SR_AN_ADV2, adv); 45362306a36Sopenharmony_ci if (ret < 0) 45462306a36Sopenharmony_ci return ret; 45562306a36Sopenharmony_ci 45662306a36Sopenharmony_ci /* SR_AN_ADV1 */ 45762306a36Sopenharmony_ci adv = DW_C73_AN_ADV_SF; 45862306a36Sopenharmony_ci if (xpcs_linkmode_supported(compat, Pause)) 45962306a36Sopenharmony_ci adv |= DW_C73_PAUSE; 46062306a36Sopenharmony_ci if (xpcs_linkmode_supported(compat, Asym_Pause)) 46162306a36Sopenharmony_ci adv |= DW_C73_ASYM_PAUSE; 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_ci return xpcs_write(xpcs, MDIO_MMD_AN, DW_SR_AN_ADV1, adv); 46462306a36Sopenharmony_ci} 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_cistatic int xpcs_config_aneg_c73(struct dw_xpcs *xpcs, 46762306a36Sopenharmony_ci const struct xpcs_compat *compat) 46862306a36Sopenharmony_ci{ 46962306a36Sopenharmony_ci int ret; 47062306a36Sopenharmony_ci 47162306a36Sopenharmony_ci ret = _xpcs_config_aneg_c73(xpcs, compat); 47262306a36Sopenharmony_ci if (ret < 0) 47362306a36Sopenharmony_ci return ret; 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_ci ret = xpcs_read(xpcs, MDIO_MMD_AN, MDIO_CTRL1); 47662306a36Sopenharmony_ci if (ret < 0) 47762306a36Sopenharmony_ci return ret; 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_ci ret |= MDIO_AN_CTRL1_ENABLE | MDIO_AN_CTRL1_RESTART; 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_ci return xpcs_write(xpcs, MDIO_MMD_AN, MDIO_CTRL1, ret); 48262306a36Sopenharmony_ci} 48362306a36Sopenharmony_ci 48462306a36Sopenharmony_cistatic int xpcs_aneg_done_c73(struct dw_xpcs *xpcs, 48562306a36Sopenharmony_ci struct phylink_link_state *state, 48662306a36Sopenharmony_ci const struct xpcs_compat *compat, u16 an_stat1) 48762306a36Sopenharmony_ci{ 48862306a36Sopenharmony_ci int ret; 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_ci if (an_stat1 & MDIO_AN_STAT1_COMPLETE) { 49162306a36Sopenharmony_ci ret = xpcs_read(xpcs, MDIO_MMD_AN, MDIO_AN_LPA); 49262306a36Sopenharmony_ci if (ret < 0) 49362306a36Sopenharmony_ci return ret; 49462306a36Sopenharmony_ci 49562306a36Sopenharmony_ci /* Check if Aneg outcome is valid */ 49662306a36Sopenharmony_ci if (!(ret & DW_C73_AN_ADV_SF)) { 49762306a36Sopenharmony_ci xpcs_config_aneg_c73(xpcs, compat); 49862306a36Sopenharmony_ci return 0; 49962306a36Sopenharmony_ci } 50062306a36Sopenharmony_ci 50162306a36Sopenharmony_ci return 1; 50262306a36Sopenharmony_ci } 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_ci return 0; 50562306a36Sopenharmony_ci} 50662306a36Sopenharmony_ci 50762306a36Sopenharmony_cistatic int xpcs_read_lpa_c73(struct dw_xpcs *xpcs, 50862306a36Sopenharmony_ci struct phylink_link_state *state, u16 an_stat1) 50962306a36Sopenharmony_ci{ 51062306a36Sopenharmony_ci u16 lpa[3]; 51162306a36Sopenharmony_ci int i, ret; 51262306a36Sopenharmony_ci 51362306a36Sopenharmony_ci if (!(an_stat1 & MDIO_AN_STAT1_LPABLE)) { 51462306a36Sopenharmony_ci phylink_clear(state->lp_advertising, Autoneg); 51562306a36Sopenharmony_ci return 0; 51662306a36Sopenharmony_ci } 51762306a36Sopenharmony_ci 51862306a36Sopenharmony_ci phylink_set(state->lp_advertising, Autoneg); 51962306a36Sopenharmony_ci 52062306a36Sopenharmony_ci /* Read Clause 73 link partner advertisement */ 52162306a36Sopenharmony_ci for (i = ARRAY_SIZE(lpa); --i >= 0; ) { 52262306a36Sopenharmony_ci ret = xpcs_read(xpcs, MDIO_MMD_AN, MDIO_AN_LPA + i); 52362306a36Sopenharmony_ci if (ret < 0) 52462306a36Sopenharmony_ci return ret; 52562306a36Sopenharmony_ci 52662306a36Sopenharmony_ci lpa[i] = ret; 52762306a36Sopenharmony_ci } 52862306a36Sopenharmony_ci 52962306a36Sopenharmony_ci mii_c73_mod_linkmode(state->lp_advertising, lpa); 53062306a36Sopenharmony_ci 53162306a36Sopenharmony_ci return 0; 53262306a36Sopenharmony_ci} 53362306a36Sopenharmony_ci 53462306a36Sopenharmony_cistatic int xpcs_get_max_xlgmii_speed(struct dw_xpcs *xpcs, 53562306a36Sopenharmony_ci struct phylink_link_state *state) 53662306a36Sopenharmony_ci{ 53762306a36Sopenharmony_ci unsigned long *adv = state->advertising; 53862306a36Sopenharmony_ci int speed = SPEED_UNKNOWN; 53962306a36Sopenharmony_ci int bit; 54062306a36Sopenharmony_ci 54162306a36Sopenharmony_ci for_each_set_bit(bit, adv, __ETHTOOL_LINK_MODE_MASK_NBITS) { 54262306a36Sopenharmony_ci int new_speed = SPEED_UNKNOWN; 54362306a36Sopenharmony_ci 54462306a36Sopenharmony_ci switch (bit) { 54562306a36Sopenharmony_ci case ETHTOOL_LINK_MODE_25000baseCR_Full_BIT: 54662306a36Sopenharmony_ci case ETHTOOL_LINK_MODE_25000baseKR_Full_BIT: 54762306a36Sopenharmony_ci case ETHTOOL_LINK_MODE_25000baseSR_Full_BIT: 54862306a36Sopenharmony_ci new_speed = SPEED_25000; 54962306a36Sopenharmony_ci break; 55062306a36Sopenharmony_ci case ETHTOOL_LINK_MODE_40000baseKR4_Full_BIT: 55162306a36Sopenharmony_ci case ETHTOOL_LINK_MODE_40000baseCR4_Full_BIT: 55262306a36Sopenharmony_ci case ETHTOOL_LINK_MODE_40000baseSR4_Full_BIT: 55362306a36Sopenharmony_ci case ETHTOOL_LINK_MODE_40000baseLR4_Full_BIT: 55462306a36Sopenharmony_ci new_speed = SPEED_40000; 55562306a36Sopenharmony_ci break; 55662306a36Sopenharmony_ci case ETHTOOL_LINK_MODE_50000baseCR2_Full_BIT: 55762306a36Sopenharmony_ci case ETHTOOL_LINK_MODE_50000baseKR2_Full_BIT: 55862306a36Sopenharmony_ci case ETHTOOL_LINK_MODE_50000baseSR2_Full_BIT: 55962306a36Sopenharmony_ci case ETHTOOL_LINK_MODE_50000baseKR_Full_BIT: 56062306a36Sopenharmony_ci case ETHTOOL_LINK_MODE_50000baseSR_Full_BIT: 56162306a36Sopenharmony_ci case ETHTOOL_LINK_MODE_50000baseCR_Full_BIT: 56262306a36Sopenharmony_ci case ETHTOOL_LINK_MODE_50000baseLR_ER_FR_Full_BIT: 56362306a36Sopenharmony_ci case ETHTOOL_LINK_MODE_50000baseDR_Full_BIT: 56462306a36Sopenharmony_ci new_speed = SPEED_50000; 56562306a36Sopenharmony_ci break; 56662306a36Sopenharmony_ci case ETHTOOL_LINK_MODE_100000baseKR4_Full_BIT: 56762306a36Sopenharmony_ci case ETHTOOL_LINK_MODE_100000baseSR4_Full_BIT: 56862306a36Sopenharmony_ci case ETHTOOL_LINK_MODE_100000baseCR4_Full_BIT: 56962306a36Sopenharmony_ci case ETHTOOL_LINK_MODE_100000baseLR4_ER4_Full_BIT: 57062306a36Sopenharmony_ci case ETHTOOL_LINK_MODE_100000baseKR2_Full_BIT: 57162306a36Sopenharmony_ci case ETHTOOL_LINK_MODE_100000baseSR2_Full_BIT: 57262306a36Sopenharmony_ci case ETHTOOL_LINK_MODE_100000baseCR2_Full_BIT: 57362306a36Sopenharmony_ci case ETHTOOL_LINK_MODE_100000baseLR2_ER2_FR2_Full_BIT: 57462306a36Sopenharmony_ci case ETHTOOL_LINK_MODE_100000baseDR2_Full_BIT: 57562306a36Sopenharmony_ci new_speed = SPEED_100000; 57662306a36Sopenharmony_ci break; 57762306a36Sopenharmony_ci default: 57862306a36Sopenharmony_ci continue; 57962306a36Sopenharmony_ci } 58062306a36Sopenharmony_ci 58162306a36Sopenharmony_ci if (new_speed > speed) 58262306a36Sopenharmony_ci speed = new_speed; 58362306a36Sopenharmony_ci } 58462306a36Sopenharmony_ci 58562306a36Sopenharmony_ci return speed; 58662306a36Sopenharmony_ci} 58762306a36Sopenharmony_ci 58862306a36Sopenharmony_cistatic void xpcs_resolve_pma(struct dw_xpcs *xpcs, 58962306a36Sopenharmony_ci struct phylink_link_state *state) 59062306a36Sopenharmony_ci{ 59162306a36Sopenharmony_ci state->pause = MLO_PAUSE_TX | MLO_PAUSE_RX; 59262306a36Sopenharmony_ci state->duplex = DUPLEX_FULL; 59362306a36Sopenharmony_ci 59462306a36Sopenharmony_ci switch (state->interface) { 59562306a36Sopenharmony_ci case PHY_INTERFACE_MODE_10GKR: 59662306a36Sopenharmony_ci state->speed = SPEED_10000; 59762306a36Sopenharmony_ci break; 59862306a36Sopenharmony_ci case PHY_INTERFACE_MODE_XLGMII: 59962306a36Sopenharmony_ci state->speed = xpcs_get_max_xlgmii_speed(xpcs, state); 60062306a36Sopenharmony_ci break; 60162306a36Sopenharmony_ci default: 60262306a36Sopenharmony_ci state->speed = SPEED_UNKNOWN; 60362306a36Sopenharmony_ci break; 60462306a36Sopenharmony_ci } 60562306a36Sopenharmony_ci} 60662306a36Sopenharmony_ci 60762306a36Sopenharmony_cistatic int xpcs_validate(struct phylink_pcs *pcs, unsigned long *supported, 60862306a36Sopenharmony_ci const struct phylink_link_state *state) 60962306a36Sopenharmony_ci{ 61062306a36Sopenharmony_ci __ETHTOOL_DECLARE_LINK_MODE_MASK(xpcs_supported) = { 0, }; 61162306a36Sopenharmony_ci const struct xpcs_compat *compat; 61262306a36Sopenharmony_ci struct dw_xpcs *xpcs; 61362306a36Sopenharmony_ci int i; 61462306a36Sopenharmony_ci 61562306a36Sopenharmony_ci xpcs = phylink_pcs_to_xpcs(pcs); 61662306a36Sopenharmony_ci compat = xpcs_find_compat(xpcs->id, state->interface); 61762306a36Sopenharmony_ci 61862306a36Sopenharmony_ci /* Populate the supported link modes for this PHY interface type. 61962306a36Sopenharmony_ci * FIXME: what about the port modes and autoneg bit? This masks 62062306a36Sopenharmony_ci * all those away. 62162306a36Sopenharmony_ci */ 62262306a36Sopenharmony_ci if (compat) 62362306a36Sopenharmony_ci for (i = 0; compat->supported[i] != __ETHTOOL_LINK_MODE_MASK_NBITS; i++) 62462306a36Sopenharmony_ci set_bit(compat->supported[i], xpcs_supported); 62562306a36Sopenharmony_ci 62662306a36Sopenharmony_ci linkmode_and(supported, supported, xpcs_supported); 62762306a36Sopenharmony_ci 62862306a36Sopenharmony_ci return 0; 62962306a36Sopenharmony_ci} 63062306a36Sopenharmony_ci 63162306a36Sopenharmony_civoid xpcs_get_interfaces(struct dw_xpcs *xpcs, unsigned long *interfaces) 63262306a36Sopenharmony_ci{ 63362306a36Sopenharmony_ci int i, j; 63462306a36Sopenharmony_ci 63562306a36Sopenharmony_ci for (i = 0; i < DW_XPCS_INTERFACE_MAX; i++) { 63662306a36Sopenharmony_ci const struct xpcs_compat *compat = &xpcs->id->compat[i]; 63762306a36Sopenharmony_ci 63862306a36Sopenharmony_ci for (j = 0; j < compat->num_interfaces; j++) 63962306a36Sopenharmony_ci if (compat->interface[j] < PHY_INTERFACE_MODE_MAX) 64062306a36Sopenharmony_ci __set_bit(compat->interface[j], interfaces); 64162306a36Sopenharmony_ci } 64262306a36Sopenharmony_ci} 64362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(xpcs_get_interfaces); 64462306a36Sopenharmony_ci 64562306a36Sopenharmony_ciint xpcs_config_eee(struct dw_xpcs *xpcs, int mult_fact_100ns, int enable) 64662306a36Sopenharmony_ci{ 64762306a36Sopenharmony_ci int ret; 64862306a36Sopenharmony_ci 64962306a36Sopenharmony_ci ret = xpcs_read(xpcs, MDIO_MMD_VEND2, DW_VR_MII_EEE_MCTRL0); 65062306a36Sopenharmony_ci if (ret < 0) 65162306a36Sopenharmony_ci return ret; 65262306a36Sopenharmony_ci 65362306a36Sopenharmony_ci if (enable) { 65462306a36Sopenharmony_ci /* Enable EEE */ 65562306a36Sopenharmony_ci ret = DW_VR_MII_EEE_LTX_EN | DW_VR_MII_EEE_LRX_EN | 65662306a36Sopenharmony_ci DW_VR_MII_EEE_TX_QUIET_EN | DW_VR_MII_EEE_RX_QUIET_EN | 65762306a36Sopenharmony_ci DW_VR_MII_EEE_TX_EN_CTRL | DW_VR_MII_EEE_RX_EN_CTRL | 65862306a36Sopenharmony_ci mult_fact_100ns << DW_VR_MII_EEE_MULT_FACT_100NS_SHIFT; 65962306a36Sopenharmony_ci } else { 66062306a36Sopenharmony_ci ret &= ~(DW_VR_MII_EEE_LTX_EN | DW_VR_MII_EEE_LRX_EN | 66162306a36Sopenharmony_ci DW_VR_MII_EEE_TX_QUIET_EN | DW_VR_MII_EEE_RX_QUIET_EN | 66262306a36Sopenharmony_ci DW_VR_MII_EEE_TX_EN_CTRL | DW_VR_MII_EEE_RX_EN_CTRL | 66362306a36Sopenharmony_ci DW_VR_MII_EEE_MULT_FACT_100NS); 66462306a36Sopenharmony_ci } 66562306a36Sopenharmony_ci 66662306a36Sopenharmony_ci ret = xpcs_write(xpcs, MDIO_MMD_VEND2, DW_VR_MII_EEE_MCTRL0, ret); 66762306a36Sopenharmony_ci if (ret < 0) 66862306a36Sopenharmony_ci return ret; 66962306a36Sopenharmony_ci 67062306a36Sopenharmony_ci ret = xpcs_read(xpcs, MDIO_MMD_VEND2, DW_VR_MII_EEE_MCTRL1); 67162306a36Sopenharmony_ci if (ret < 0) 67262306a36Sopenharmony_ci return ret; 67362306a36Sopenharmony_ci 67462306a36Sopenharmony_ci if (enable) 67562306a36Sopenharmony_ci ret |= DW_VR_MII_EEE_TRN_LPI; 67662306a36Sopenharmony_ci else 67762306a36Sopenharmony_ci ret &= ~DW_VR_MII_EEE_TRN_LPI; 67862306a36Sopenharmony_ci 67962306a36Sopenharmony_ci return xpcs_write(xpcs, MDIO_MMD_VEND2, DW_VR_MII_EEE_MCTRL1, ret); 68062306a36Sopenharmony_ci} 68162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(xpcs_config_eee); 68262306a36Sopenharmony_ci 68362306a36Sopenharmony_cistatic int xpcs_config_aneg_c37_sgmii(struct dw_xpcs *xpcs, 68462306a36Sopenharmony_ci unsigned int neg_mode) 68562306a36Sopenharmony_ci{ 68662306a36Sopenharmony_ci int ret, mdio_ctrl, tx_conf; 68762306a36Sopenharmony_ci 68862306a36Sopenharmony_ci if (xpcs->dev_flag == DW_DEV_TXGBE) 68962306a36Sopenharmony_ci xpcs_write_vpcs(xpcs, DW_VR_XS_PCS_DIG_CTRL1, DW_CL37_BP | DW_EN_VSMMD1); 69062306a36Sopenharmony_ci 69162306a36Sopenharmony_ci /* For AN for C37 SGMII mode, the settings are :- 69262306a36Sopenharmony_ci * 1) VR_MII_MMD_CTRL Bit(12) [AN_ENABLE] = 0b (Disable SGMII AN in case 69362306a36Sopenharmony_ci it is already enabled) 69462306a36Sopenharmony_ci * 2) VR_MII_AN_CTRL Bit(2:1)[PCS_MODE] = 10b (SGMII AN) 69562306a36Sopenharmony_ci * 3) VR_MII_AN_CTRL Bit(3) [TX_CONFIG] = 0b (MAC side SGMII) 69662306a36Sopenharmony_ci * DW xPCS used with DW EQoS MAC is always MAC side SGMII. 69762306a36Sopenharmony_ci * 4) VR_MII_DIG_CTRL1 Bit(9) [MAC_AUTO_SW] = 1b (Automatic 69862306a36Sopenharmony_ci * speed/duplex mode change by HW after SGMII AN complete) 69962306a36Sopenharmony_ci * 5) VR_MII_MMD_CTRL Bit(12) [AN_ENABLE] = 1b (Enable SGMII AN) 70062306a36Sopenharmony_ci * 70162306a36Sopenharmony_ci * Note: Since it is MAC side SGMII, there is no need to set 70262306a36Sopenharmony_ci * SR_MII_AN_ADV. MAC side SGMII receives AN Tx Config from 70362306a36Sopenharmony_ci * PHY about the link state change after C28 AN is completed 70462306a36Sopenharmony_ci * between PHY and Link Partner. There is also no need to 70562306a36Sopenharmony_ci * trigger AN restart for MAC-side SGMII. 70662306a36Sopenharmony_ci */ 70762306a36Sopenharmony_ci mdio_ctrl = xpcs_read(xpcs, MDIO_MMD_VEND2, DW_VR_MII_MMD_CTRL); 70862306a36Sopenharmony_ci if (mdio_ctrl < 0) 70962306a36Sopenharmony_ci return mdio_ctrl; 71062306a36Sopenharmony_ci 71162306a36Sopenharmony_ci if (mdio_ctrl & AN_CL37_EN) { 71262306a36Sopenharmony_ci ret = xpcs_write(xpcs, MDIO_MMD_VEND2, DW_VR_MII_MMD_CTRL, 71362306a36Sopenharmony_ci mdio_ctrl & ~AN_CL37_EN); 71462306a36Sopenharmony_ci if (ret < 0) 71562306a36Sopenharmony_ci return ret; 71662306a36Sopenharmony_ci } 71762306a36Sopenharmony_ci 71862306a36Sopenharmony_ci ret = xpcs_read(xpcs, MDIO_MMD_VEND2, DW_VR_MII_AN_CTRL); 71962306a36Sopenharmony_ci if (ret < 0) 72062306a36Sopenharmony_ci return ret; 72162306a36Sopenharmony_ci 72262306a36Sopenharmony_ci ret &= ~(DW_VR_MII_PCS_MODE_MASK | DW_VR_MII_TX_CONFIG_MASK); 72362306a36Sopenharmony_ci ret |= (DW_VR_MII_PCS_MODE_C37_SGMII << 72462306a36Sopenharmony_ci DW_VR_MII_AN_CTRL_PCS_MODE_SHIFT & 72562306a36Sopenharmony_ci DW_VR_MII_PCS_MODE_MASK); 72662306a36Sopenharmony_ci if (xpcs->dev_flag == DW_DEV_TXGBE) { 72762306a36Sopenharmony_ci ret |= DW_VR_MII_AN_CTRL_8BIT; 72862306a36Sopenharmony_ci /* Hardware requires it to be PHY side SGMII */ 72962306a36Sopenharmony_ci tx_conf = DW_VR_MII_TX_CONFIG_PHY_SIDE_SGMII; 73062306a36Sopenharmony_ci } else { 73162306a36Sopenharmony_ci tx_conf = DW_VR_MII_TX_CONFIG_MAC_SIDE_SGMII; 73262306a36Sopenharmony_ci } 73362306a36Sopenharmony_ci ret |= tx_conf << DW_VR_MII_AN_CTRL_TX_CONFIG_SHIFT & 73462306a36Sopenharmony_ci DW_VR_MII_TX_CONFIG_MASK; 73562306a36Sopenharmony_ci ret = xpcs_write(xpcs, MDIO_MMD_VEND2, DW_VR_MII_AN_CTRL, ret); 73662306a36Sopenharmony_ci if (ret < 0) 73762306a36Sopenharmony_ci return ret; 73862306a36Sopenharmony_ci 73962306a36Sopenharmony_ci ret = xpcs_read(xpcs, MDIO_MMD_VEND2, DW_VR_MII_DIG_CTRL1); 74062306a36Sopenharmony_ci if (ret < 0) 74162306a36Sopenharmony_ci return ret; 74262306a36Sopenharmony_ci 74362306a36Sopenharmony_ci if (neg_mode == PHYLINK_PCS_NEG_INBAND_ENABLED) 74462306a36Sopenharmony_ci ret |= DW_VR_MII_DIG_CTRL1_MAC_AUTO_SW; 74562306a36Sopenharmony_ci else 74662306a36Sopenharmony_ci ret &= ~DW_VR_MII_DIG_CTRL1_MAC_AUTO_SW; 74762306a36Sopenharmony_ci 74862306a36Sopenharmony_ci if (xpcs->dev_flag == DW_DEV_TXGBE) 74962306a36Sopenharmony_ci ret |= DW_VR_MII_DIG_CTRL1_PHY_MODE_CTRL; 75062306a36Sopenharmony_ci 75162306a36Sopenharmony_ci ret = xpcs_write(xpcs, MDIO_MMD_VEND2, DW_VR_MII_DIG_CTRL1, ret); 75262306a36Sopenharmony_ci if (ret < 0) 75362306a36Sopenharmony_ci return ret; 75462306a36Sopenharmony_ci 75562306a36Sopenharmony_ci if (neg_mode == PHYLINK_PCS_NEG_INBAND_ENABLED) 75662306a36Sopenharmony_ci ret = xpcs_write(xpcs, MDIO_MMD_VEND2, DW_VR_MII_MMD_CTRL, 75762306a36Sopenharmony_ci mdio_ctrl | AN_CL37_EN); 75862306a36Sopenharmony_ci 75962306a36Sopenharmony_ci return ret; 76062306a36Sopenharmony_ci} 76162306a36Sopenharmony_ci 76262306a36Sopenharmony_cistatic int xpcs_config_aneg_c37_1000basex(struct dw_xpcs *xpcs, 76362306a36Sopenharmony_ci unsigned int neg_mode, 76462306a36Sopenharmony_ci const unsigned long *advertising) 76562306a36Sopenharmony_ci{ 76662306a36Sopenharmony_ci phy_interface_t interface = PHY_INTERFACE_MODE_1000BASEX; 76762306a36Sopenharmony_ci int ret, mdio_ctrl, adv; 76862306a36Sopenharmony_ci bool changed = 0; 76962306a36Sopenharmony_ci 77062306a36Sopenharmony_ci if (xpcs->dev_flag == DW_DEV_TXGBE) 77162306a36Sopenharmony_ci xpcs_write_vpcs(xpcs, DW_VR_XS_PCS_DIG_CTRL1, DW_CL37_BP | DW_EN_VSMMD1); 77262306a36Sopenharmony_ci 77362306a36Sopenharmony_ci /* According to Chap 7.12, to set 1000BASE-X C37 AN, AN must 77462306a36Sopenharmony_ci * be disabled first:- 77562306a36Sopenharmony_ci * 1) VR_MII_MMD_CTRL Bit(12)[AN_ENABLE] = 0b 77662306a36Sopenharmony_ci * 2) VR_MII_AN_CTRL Bit(2:1)[PCS_MODE] = 00b (1000BASE-X C37) 77762306a36Sopenharmony_ci */ 77862306a36Sopenharmony_ci mdio_ctrl = xpcs_read(xpcs, MDIO_MMD_VEND2, DW_VR_MII_MMD_CTRL); 77962306a36Sopenharmony_ci if (mdio_ctrl < 0) 78062306a36Sopenharmony_ci return mdio_ctrl; 78162306a36Sopenharmony_ci 78262306a36Sopenharmony_ci if (mdio_ctrl & AN_CL37_EN) { 78362306a36Sopenharmony_ci ret = xpcs_write(xpcs, MDIO_MMD_VEND2, DW_VR_MII_MMD_CTRL, 78462306a36Sopenharmony_ci mdio_ctrl & ~AN_CL37_EN); 78562306a36Sopenharmony_ci if (ret < 0) 78662306a36Sopenharmony_ci return ret; 78762306a36Sopenharmony_ci } 78862306a36Sopenharmony_ci 78962306a36Sopenharmony_ci ret = xpcs_read(xpcs, MDIO_MMD_VEND2, DW_VR_MII_AN_CTRL); 79062306a36Sopenharmony_ci if (ret < 0) 79162306a36Sopenharmony_ci return ret; 79262306a36Sopenharmony_ci 79362306a36Sopenharmony_ci ret &= ~DW_VR_MII_PCS_MODE_MASK; 79462306a36Sopenharmony_ci if (!xpcs->pcs.poll) 79562306a36Sopenharmony_ci ret |= DW_VR_MII_AN_INTR_EN; 79662306a36Sopenharmony_ci ret = xpcs_write(xpcs, MDIO_MMD_VEND2, DW_VR_MII_AN_CTRL, ret); 79762306a36Sopenharmony_ci if (ret < 0) 79862306a36Sopenharmony_ci return ret; 79962306a36Sopenharmony_ci 80062306a36Sopenharmony_ci /* Check for advertising changes and update the C45 MII ADV 80162306a36Sopenharmony_ci * register accordingly. 80262306a36Sopenharmony_ci */ 80362306a36Sopenharmony_ci adv = phylink_mii_c22_pcs_encode_advertisement(interface, 80462306a36Sopenharmony_ci advertising); 80562306a36Sopenharmony_ci if (adv >= 0) { 80662306a36Sopenharmony_ci ret = xpcs_modify_changed(xpcs, MDIO_MMD_VEND2, 80762306a36Sopenharmony_ci MII_ADVERTISE, 0xffff, adv); 80862306a36Sopenharmony_ci if (ret < 0) 80962306a36Sopenharmony_ci return ret; 81062306a36Sopenharmony_ci 81162306a36Sopenharmony_ci changed = ret; 81262306a36Sopenharmony_ci } 81362306a36Sopenharmony_ci 81462306a36Sopenharmony_ci /* Clear CL37 AN complete status */ 81562306a36Sopenharmony_ci ret = xpcs_write(xpcs, MDIO_MMD_VEND2, DW_VR_MII_AN_INTR_STS, 0); 81662306a36Sopenharmony_ci if (ret < 0) 81762306a36Sopenharmony_ci return ret; 81862306a36Sopenharmony_ci 81962306a36Sopenharmony_ci if (neg_mode == PHYLINK_PCS_NEG_INBAND_ENABLED) { 82062306a36Sopenharmony_ci ret = xpcs_write(xpcs, MDIO_MMD_VEND2, DW_VR_MII_MMD_CTRL, 82162306a36Sopenharmony_ci mdio_ctrl | AN_CL37_EN); 82262306a36Sopenharmony_ci if (ret < 0) 82362306a36Sopenharmony_ci return ret; 82462306a36Sopenharmony_ci } 82562306a36Sopenharmony_ci 82662306a36Sopenharmony_ci return changed; 82762306a36Sopenharmony_ci} 82862306a36Sopenharmony_ci 82962306a36Sopenharmony_cistatic int xpcs_config_2500basex(struct dw_xpcs *xpcs) 83062306a36Sopenharmony_ci{ 83162306a36Sopenharmony_ci int ret; 83262306a36Sopenharmony_ci 83362306a36Sopenharmony_ci ret = xpcs_read(xpcs, MDIO_MMD_VEND2, DW_VR_MII_DIG_CTRL1); 83462306a36Sopenharmony_ci if (ret < 0) 83562306a36Sopenharmony_ci return ret; 83662306a36Sopenharmony_ci ret |= DW_VR_MII_DIG_CTRL1_2G5_EN; 83762306a36Sopenharmony_ci ret &= ~DW_VR_MII_DIG_CTRL1_MAC_AUTO_SW; 83862306a36Sopenharmony_ci ret = xpcs_write(xpcs, MDIO_MMD_VEND2, DW_VR_MII_DIG_CTRL1, ret); 83962306a36Sopenharmony_ci if (ret < 0) 84062306a36Sopenharmony_ci return ret; 84162306a36Sopenharmony_ci 84262306a36Sopenharmony_ci ret = xpcs_read(xpcs, MDIO_MMD_VEND2, DW_VR_MII_MMD_CTRL); 84362306a36Sopenharmony_ci if (ret < 0) 84462306a36Sopenharmony_ci return ret; 84562306a36Sopenharmony_ci ret &= ~AN_CL37_EN; 84662306a36Sopenharmony_ci ret |= SGMII_SPEED_SS6; 84762306a36Sopenharmony_ci ret &= ~SGMII_SPEED_SS13; 84862306a36Sopenharmony_ci return xpcs_write(xpcs, MDIO_MMD_VEND2, DW_VR_MII_MMD_CTRL, ret); 84962306a36Sopenharmony_ci} 85062306a36Sopenharmony_ci 85162306a36Sopenharmony_ciint xpcs_do_config(struct dw_xpcs *xpcs, phy_interface_t interface, 85262306a36Sopenharmony_ci const unsigned long *advertising, unsigned int neg_mode) 85362306a36Sopenharmony_ci{ 85462306a36Sopenharmony_ci const struct xpcs_compat *compat; 85562306a36Sopenharmony_ci int ret; 85662306a36Sopenharmony_ci 85762306a36Sopenharmony_ci compat = xpcs_find_compat(xpcs->id, interface); 85862306a36Sopenharmony_ci if (!compat) 85962306a36Sopenharmony_ci return -ENODEV; 86062306a36Sopenharmony_ci 86162306a36Sopenharmony_ci if (xpcs->dev_flag == DW_DEV_TXGBE) { 86262306a36Sopenharmony_ci ret = txgbe_xpcs_switch_mode(xpcs, interface); 86362306a36Sopenharmony_ci if (ret) 86462306a36Sopenharmony_ci return ret; 86562306a36Sopenharmony_ci } 86662306a36Sopenharmony_ci 86762306a36Sopenharmony_ci switch (compat->an_mode) { 86862306a36Sopenharmony_ci case DW_10GBASER: 86962306a36Sopenharmony_ci break; 87062306a36Sopenharmony_ci case DW_AN_C73: 87162306a36Sopenharmony_ci if (neg_mode == PHYLINK_PCS_NEG_INBAND_ENABLED) { 87262306a36Sopenharmony_ci ret = xpcs_config_aneg_c73(xpcs, compat); 87362306a36Sopenharmony_ci if (ret) 87462306a36Sopenharmony_ci return ret; 87562306a36Sopenharmony_ci } 87662306a36Sopenharmony_ci break; 87762306a36Sopenharmony_ci case DW_AN_C37_SGMII: 87862306a36Sopenharmony_ci ret = xpcs_config_aneg_c37_sgmii(xpcs, neg_mode); 87962306a36Sopenharmony_ci if (ret) 88062306a36Sopenharmony_ci return ret; 88162306a36Sopenharmony_ci break; 88262306a36Sopenharmony_ci case DW_AN_C37_1000BASEX: 88362306a36Sopenharmony_ci ret = xpcs_config_aneg_c37_1000basex(xpcs, neg_mode, 88462306a36Sopenharmony_ci advertising); 88562306a36Sopenharmony_ci if (ret) 88662306a36Sopenharmony_ci return ret; 88762306a36Sopenharmony_ci break; 88862306a36Sopenharmony_ci case DW_2500BASEX: 88962306a36Sopenharmony_ci ret = xpcs_config_2500basex(xpcs); 89062306a36Sopenharmony_ci if (ret) 89162306a36Sopenharmony_ci return ret; 89262306a36Sopenharmony_ci break; 89362306a36Sopenharmony_ci default: 89462306a36Sopenharmony_ci return -1; 89562306a36Sopenharmony_ci } 89662306a36Sopenharmony_ci 89762306a36Sopenharmony_ci if (compat->pma_config) { 89862306a36Sopenharmony_ci ret = compat->pma_config(xpcs); 89962306a36Sopenharmony_ci if (ret) 90062306a36Sopenharmony_ci return ret; 90162306a36Sopenharmony_ci } 90262306a36Sopenharmony_ci 90362306a36Sopenharmony_ci return 0; 90462306a36Sopenharmony_ci} 90562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(xpcs_do_config); 90662306a36Sopenharmony_ci 90762306a36Sopenharmony_cistatic int xpcs_config(struct phylink_pcs *pcs, unsigned int neg_mode, 90862306a36Sopenharmony_ci phy_interface_t interface, 90962306a36Sopenharmony_ci const unsigned long *advertising, 91062306a36Sopenharmony_ci bool permit_pause_to_mac) 91162306a36Sopenharmony_ci{ 91262306a36Sopenharmony_ci struct dw_xpcs *xpcs = phylink_pcs_to_xpcs(pcs); 91362306a36Sopenharmony_ci 91462306a36Sopenharmony_ci return xpcs_do_config(xpcs, interface, advertising, neg_mode); 91562306a36Sopenharmony_ci} 91662306a36Sopenharmony_ci 91762306a36Sopenharmony_cistatic int xpcs_get_state_c73(struct dw_xpcs *xpcs, 91862306a36Sopenharmony_ci struct phylink_link_state *state, 91962306a36Sopenharmony_ci const struct xpcs_compat *compat) 92062306a36Sopenharmony_ci{ 92162306a36Sopenharmony_ci bool an_enabled; 92262306a36Sopenharmony_ci int pcs_stat1; 92362306a36Sopenharmony_ci int an_stat1; 92462306a36Sopenharmony_ci int ret; 92562306a36Sopenharmony_ci 92662306a36Sopenharmony_ci /* The link status bit is latching-low, so it is important to 92762306a36Sopenharmony_ci * avoid unnecessary re-reads of this register to avoid missing 92862306a36Sopenharmony_ci * a link-down event. 92962306a36Sopenharmony_ci */ 93062306a36Sopenharmony_ci pcs_stat1 = xpcs_read(xpcs, MDIO_MMD_PCS, MDIO_STAT1); 93162306a36Sopenharmony_ci if (pcs_stat1 < 0) { 93262306a36Sopenharmony_ci state->link = false; 93362306a36Sopenharmony_ci return pcs_stat1; 93462306a36Sopenharmony_ci } 93562306a36Sopenharmony_ci 93662306a36Sopenharmony_ci /* Link needs to be read first ... */ 93762306a36Sopenharmony_ci state->link = !!(pcs_stat1 & MDIO_STAT1_LSTATUS); 93862306a36Sopenharmony_ci 93962306a36Sopenharmony_ci /* ... and then we check the faults. */ 94062306a36Sopenharmony_ci ret = xpcs_read_fault_c73(xpcs, state, pcs_stat1); 94162306a36Sopenharmony_ci if (ret) { 94262306a36Sopenharmony_ci ret = xpcs_soft_reset(xpcs, compat); 94362306a36Sopenharmony_ci if (ret) 94462306a36Sopenharmony_ci return ret; 94562306a36Sopenharmony_ci 94662306a36Sopenharmony_ci state->link = 0; 94762306a36Sopenharmony_ci 94862306a36Sopenharmony_ci return xpcs_do_config(xpcs, state->interface, NULL, 94962306a36Sopenharmony_ci PHYLINK_PCS_NEG_INBAND_ENABLED); 95062306a36Sopenharmony_ci } 95162306a36Sopenharmony_ci 95262306a36Sopenharmony_ci /* There is no point doing anything else if the link is down. */ 95362306a36Sopenharmony_ci if (!state->link) 95462306a36Sopenharmony_ci return 0; 95562306a36Sopenharmony_ci 95662306a36Sopenharmony_ci an_enabled = linkmode_test_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, 95762306a36Sopenharmony_ci state->advertising); 95862306a36Sopenharmony_ci if (an_enabled) { 95962306a36Sopenharmony_ci /* The link status bit is latching-low, so it is important to 96062306a36Sopenharmony_ci * avoid unnecessary re-reads of this register to avoid missing 96162306a36Sopenharmony_ci * a link-down event. 96262306a36Sopenharmony_ci */ 96362306a36Sopenharmony_ci an_stat1 = xpcs_read(xpcs, MDIO_MMD_AN, MDIO_STAT1); 96462306a36Sopenharmony_ci if (an_stat1 < 0) { 96562306a36Sopenharmony_ci state->link = false; 96662306a36Sopenharmony_ci return an_stat1; 96762306a36Sopenharmony_ci } 96862306a36Sopenharmony_ci 96962306a36Sopenharmony_ci state->an_complete = xpcs_aneg_done_c73(xpcs, state, compat, 97062306a36Sopenharmony_ci an_stat1); 97162306a36Sopenharmony_ci if (!state->an_complete) { 97262306a36Sopenharmony_ci state->link = false; 97362306a36Sopenharmony_ci return 0; 97462306a36Sopenharmony_ci } 97562306a36Sopenharmony_ci 97662306a36Sopenharmony_ci ret = xpcs_read_lpa_c73(xpcs, state, an_stat1); 97762306a36Sopenharmony_ci if (ret < 0) { 97862306a36Sopenharmony_ci state->link = false; 97962306a36Sopenharmony_ci return ret; 98062306a36Sopenharmony_ci } 98162306a36Sopenharmony_ci 98262306a36Sopenharmony_ci phylink_resolve_c73(state); 98362306a36Sopenharmony_ci } else { 98462306a36Sopenharmony_ci xpcs_resolve_pma(xpcs, state); 98562306a36Sopenharmony_ci } 98662306a36Sopenharmony_ci 98762306a36Sopenharmony_ci return 0; 98862306a36Sopenharmony_ci} 98962306a36Sopenharmony_ci 99062306a36Sopenharmony_cistatic int xpcs_get_state_c37_sgmii(struct dw_xpcs *xpcs, 99162306a36Sopenharmony_ci struct phylink_link_state *state) 99262306a36Sopenharmony_ci{ 99362306a36Sopenharmony_ci int ret; 99462306a36Sopenharmony_ci 99562306a36Sopenharmony_ci /* Reset link_state */ 99662306a36Sopenharmony_ci state->link = false; 99762306a36Sopenharmony_ci state->speed = SPEED_UNKNOWN; 99862306a36Sopenharmony_ci state->duplex = DUPLEX_UNKNOWN; 99962306a36Sopenharmony_ci state->pause = 0; 100062306a36Sopenharmony_ci 100162306a36Sopenharmony_ci /* For C37 SGMII mode, we check DW_VR_MII_AN_INTR_STS for link 100262306a36Sopenharmony_ci * status, speed and duplex. 100362306a36Sopenharmony_ci */ 100462306a36Sopenharmony_ci ret = xpcs_read(xpcs, MDIO_MMD_VEND2, DW_VR_MII_AN_INTR_STS); 100562306a36Sopenharmony_ci if (ret < 0) 100662306a36Sopenharmony_ci return ret; 100762306a36Sopenharmony_ci 100862306a36Sopenharmony_ci if (ret & DW_VR_MII_C37_ANSGM_SP_LNKSTS) { 100962306a36Sopenharmony_ci int speed_value; 101062306a36Sopenharmony_ci 101162306a36Sopenharmony_ci state->link = true; 101262306a36Sopenharmony_ci 101362306a36Sopenharmony_ci speed_value = (ret & DW_VR_MII_AN_STS_C37_ANSGM_SP) >> 101462306a36Sopenharmony_ci DW_VR_MII_AN_STS_C37_ANSGM_SP_SHIFT; 101562306a36Sopenharmony_ci if (speed_value == DW_VR_MII_C37_ANSGM_SP_1000) 101662306a36Sopenharmony_ci state->speed = SPEED_1000; 101762306a36Sopenharmony_ci else if (speed_value == DW_VR_MII_C37_ANSGM_SP_100) 101862306a36Sopenharmony_ci state->speed = SPEED_100; 101962306a36Sopenharmony_ci else 102062306a36Sopenharmony_ci state->speed = SPEED_10; 102162306a36Sopenharmony_ci 102262306a36Sopenharmony_ci if (ret & DW_VR_MII_AN_STS_C37_ANSGM_FD) 102362306a36Sopenharmony_ci state->duplex = DUPLEX_FULL; 102462306a36Sopenharmony_ci else 102562306a36Sopenharmony_ci state->duplex = DUPLEX_HALF; 102662306a36Sopenharmony_ci } else if (ret == DW_VR_MII_AN_STS_C37_ANCMPLT_INTR) { 102762306a36Sopenharmony_ci int speed, duplex; 102862306a36Sopenharmony_ci 102962306a36Sopenharmony_ci state->link = true; 103062306a36Sopenharmony_ci 103162306a36Sopenharmony_ci speed = xpcs_read(xpcs, MDIO_MMD_VEND2, MDIO_CTRL1); 103262306a36Sopenharmony_ci if (speed < 0) 103362306a36Sopenharmony_ci return speed; 103462306a36Sopenharmony_ci 103562306a36Sopenharmony_ci speed &= SGMII_SPEED_SS13 | SGMII_SPEED_SS6; 103662306a36Sopenharmony_ci if (speed == SGMII_SPEED_SS6) 103762306a36Sopenharmony_ci state->speed = SPEED_1000; 103862306a36Sopenharmony_ci else if (speed == SGMII_SPEED_SS13) 103962306a36Sopenharmony_ci state->speed = SPEED_100; 104062306a36Sopenharmony_ci else if (speed == 0) 104162306a36Sopenharmony_ci state->speed = SPEED_10; 104262306a36Sopenharmony_ci 104362306a36Sopenharmony_ci duplex = xpcs_read(xpcs, MDIO_MMD_VEND2, MII_ADVERTISE); 104462306a36Sopenharmony_ci if (duplex < 0) 104562306a36Sopenharmony_ci return duplex; 104662306a36Sopenharmony_ci 104762306a36Sopenharmony_ci if (duplex & DW_FULL_DUPLEX) 104862306a36Sopenharmony_ci state->duplex = DUPLEX_FULL; 104962306a36Sopenharmony_ci else if (duplex & DW_HALF_DUPLEX) 105062306a36Sopenharmony_ci state->duplex = DUPLEX_HALF; 105162306a36Sopenharmony_ci 105262306a36Sopenharmony_ci xpcs_write(xpcs, MDIO_MMD_VEND2, DW_VR_MII_AN_INTR_STS, 0); 105362306a36Sopenharmony_ci } 105462306a36Sopenharmony_ci 105562306a36Sopenharmony_ci return 0; 105662306a36Sopenharmony_ci} 105762306a36Sopenharmony_ci 105862306a36Sopenharmony_cistatic int xpcs_get_state_c37_1000basex(struct dw_xpcs *xpcs, 105962306a36Sopenharmony_ci struct phylink_link_state *state) 106062306a36Sopenharmony_ci{ 106162306a36Sopenharmony_ci int lpa, bmsr; 106262306a36Sopenharmony_ci 106362306a36Sopenharmony_ci if (linkmode_test_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, 106462306a36Sopenharmony_ci state->advertising)) { 106562306a36Sopenharmony_ci /* Reset link state */ 106662306a36Sopenharmony_ci state->link = false; 106762306a36Sopenharmony_ci 106862306a36Sopenharmony_ci lpa = xpcs_read(xpcs, MDIO_MMD_VEND2, MII_LPA); 106962306a36Sopenharmony_ci if (lpa < 0 || lpa & LPA_RFAULT) 107062306a36Sopenharmony_ci return lpa; 107162306a36Sopenharmony_ci 107262306a36Sopenharmony_ci bmsr = xpcs_read(xpcs, MDIO_MMD_VEND2, MII_BMSR); 107362306a36Sopenharmony_ci if (bmsr < 0) 107462306a36Sopenharmony_ci return bmsr; 107562306a36Sopenharmony_ci 107662306a36Sopenharmony_ci /* Clear AN complete interrupt */ 107762306a36Sopenharmony_ci if (!xpcs->pcs.poll) { 107862306a36Sopenharmony_ci int an_intr; 107962306a36Sopenharmony_ci 108062306a36Sopenharmony_ci an_intr = xpcs_read(xpcs, MDIO_MMD_VEND2, DW_VR_MII_AN_INTR_STS); 108162306a36Sopenharmony_ci if (an_intr & DW_VR_MII_AN_STS_C37_ANCMPLT_INTR) { 108262306a36Sopenharmony_ci an_intr &= ~DW_VR_MII_AN_STS_C37_ANCMPLT_INTR; 108362306a36Sopenharmony_ci xpcs_write(xpcs, MDIO_MMD_VEND2, DW_VR_MII_AN_INTR_STS, an_intr); 108462306a36Sopenharmony_ci } 108562306a36Sopenharmony_ci } 108662306a36Sopenharmony_ci 108762306a36Sopenharmony_ci phylink_mii_c22_pcs_decode_state(state, bmsr, lpa); 108862306a36Sopenharmony_ci } 108962306a36Sopenharmony_ci 109062306a36Sopenharmony_ci return 0; 109162306a36Sopenharmony_ci} 109262306a36Sopenharmony_ci 109362306a36Sopenharmony_cistatic void xpcs_get_state(struct phylink_pcs *pcs, 109462306a36Sopenharmony_ci struct phylink_link_state *state) 109562306a36Sopenharmony_ci{ 109662306a36Sopenharmony_ci struct dw_xpcs *xpcs = phylink_pcs_to_xpcs(pcs); 109762306a36Sopenharmony_ci const struct xpcs_compat *compat; 109862306a36Sopenharmony_ci int ret; 109962306a36Sopenharmony_ci 110062306a36Sopenharmony_ci compat = xpcs_find_compat(xpcs->id, state->interface); 110162306a36Sopenharmony_ci if (!compat) 110262306a36Sopenharmony_ci return; 110362306a36Sopenharmony_ci 110462306a36Sopenharmony_ci switch (compat->an_mode) { 110562306a36Sopenharmony_ci case DW_10GBASER: 110662306a36Sopenharmony_ci phylink_mii_c45_pcs_get_state(xpcs->mdiodev, state); 110762306a36Sopenharmony_ci break; 110862306a36Sopenharmony_ci case DW_AN_C73: 110962306a36Sopenharmony_ci ret = xpcs_get_state_c73(xpcs, state, compat); 111062306a36Sopenharmony_ci if (ret) { 111162306a36Sopenharmony_ci pr_err("xpcs_get_state_c73 returned %pe\n", 111262306a36Sopenharmony_ci ERR_PTR(ret)); 111362306a36Sopenharmony_ci return; 111462306a36Sopenharmony_ci } 111562306a36Sopenharmony_ci break; 111662306a36Sopenharmony_ci case DW_AN_C37_SGMII: 111762306a36Sopenharmony_ci ret = xpcs_get_state_c37_sgmii(xpcs, state); 111862306a36Sopenharmony_ci if (ret) { 111962306a36Sopenharmony_ci pr_err("xpcs_get_state_c37_sgmii returned %pe\n", 112062306a36Sopenharmony_ci ERR_PTR(ret)); 112162306a36Sopenharmony_ci } 112262306a36Sopenharmony_ci break; 112362306a36Sopenharmony_ci case DW_AN_C37_1000BASEX: 112462306a36Sopenharmony_ci ret = xpcs_get_state_c37_1000basex(xpcs, state); 112562306a36Sopenharmony_ci if (ret) { 112662306a36Sopenharmony_ci pr_err("xpcs_get_state_c37_1000basex returned %pe\n", 112762306a36Sopenharmony_ci ERR_PTR(ret)); 112862306a36Sopenharmony_ci } 112962306a36Sopenharmony_ci break; 113062306a36Sopenharmony_ci default: 113162306a36Sopenharmony_ci return; 113262306a36Sopenharmony_ci } 113362306a36Sopenharmony_ci} 113462306a36Sopenharmony_ci 113562306a36Sopenharmony_cistatic void xpcs_link_up_sgmii(struct dw_xpcs *xpcs, unsigned int neg_mode, 113662306a36Sopenharmony_ci int speed, int duplex) 113762306a36Sopenharmony_ci{ 113862306a36Sopenharmony_ci int val, ret; 113962306a36Sopenharmony_ci 114062306a36Sopenharmony_ci if (neg_mode == PHYLINK_PCS_NEG_INBAND_ENABLED) 114162306a36Sopenharmony_ci return; 114262306a36Sopenharmony_ci 114362306a36Sopenharmony_ci val = mii_bmcr_encode_fixed(speed, duplex); 114462306a36Sopenharmony_ci ret = xpcs_write(xpcs, MDIO_MMD_VEND2, MDIO_CTRL1, val); 114562306a36Sopenharmony_ci if (ret) 114662306a36Sopenharmony_ci pr_err("%s: xpcs_write returned %pe\n", __func__, ERR_PTR(ret)); 114762306a36Sopenharmony_ci} 114862306a36Sopenharmony_ci 114962306a36Sopenharmony_cistatic void xpcs_link_up_1000basex(struct dw_xpcs *xpcs, unsigned int neg_mode, 115062306a36Sopenharmony_ci int speed, int duplex) 115162306a36Sopenharmony_ci{ 115262306a36Sopenharmony_ci int val, ret; 115362306a36Sopenharmony_ci 115462306a36Sopenharmony_ci if (neg_mode == PHYLINK_PCS_NEG_INBAND_ENABLED) 115562306a36Sopenharmony_ci return; 115662306a36Sopenharmony_ci 115762306a36Sopenharmony_ci switch (speed) { 115862306a36Sopenharmony_ci case SPEED_1000: 115962306a36Sopenharmony_ci val = BMCR_SPEED1000; 116062306a36Sopenharmony_ci break; 116162306a36Sopenharmony_ci case SPEED_100: 116262306a36Sopenharmony_ci case SPEED_10: 116362306a36Sopenharmony_ci default: 116462306a36Sopenharmony_ci pr_err("%s: speed = %d\n", __func__, speed); 116562306a36Sopenharmony_ci return; 116662306a36Sopenharmony_ci } 116762306a36Sopenharmony_ci 116862306a36Sopenharmony_ci if (duplex == DUPLEX_FULL) 116962306a36Sopenharmony_ci val |= BMCR_FULLDPLX; 117062306a36Sopenharmony_ci else 117162306a36Sopenharmony_ci pr_err("%s: half duplex not supported\n", __func__); 117262306a36Sopenharmony_ci 117362306a36Sopenharmony_ci ret = xpcs_write(xpcs, MDIO_MMD_VEND2, MDIO_CTRL1, val); 117462306a36Sopenharmony_ci if (ret) 117562306a36Sopenharmony_ci pr_err("%s: xpcs_write returned %pe\n", __func__, ERR_PTR(ret)); 117662306a36Sopenharmony_ci} 117762306a36Sopenharmony_ci 117862306a36Sopenharmony_civoid xpcs_link_up(struct phylink_pcs *pcs, unsigned int neg_mode, 117962306a36Sopenharmony_ci phy_interface_t interface, int speed, int duplex) 118062306a36Sopenharmony_ci{ 118162306a36Sopenharmony_ci struct dw_xpcs *xpcs = phylink_pcs_to_xpcs(pcs); 118262306a36Sopenharmony_ci 118362306a36Sopenharmony_ci if (interface == PHY_INTERFACE_MODE_USXGMII) 118462306a36Sopenharmony_ci return xpcs_config_usxgmii(xpcs, speed); 118562306a36Sopenharmony_ci if (interface == PHY_INTERFACE_MODE_SGMII) 118662306a36Sopenharmony_ci return xpcs_link_up_sgmii(xpcs, neg_mode, speed, duplex); 118762306a36Sopenharmony_ci if (interface == PHY_INTERFACE_MODE_1000BASEX) 118862306a36Sopenharmony_ci return xpcs_link_up_1000basex(xpcs, neg_mode, speed, duplex); 118962306a36Sopenharmony_ci} 119062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(xpcs_link_up); 119162306a36Sopenharmony_ci 119262306a36Sopenharmony_cistatic void xpcs_an_restart(struct phylink_pcs *pcs) 119362306a36Sopenharmony_ci{ 119462306a36Sopenharmony_ci struct dw_xpcs *xpcs = phylink_pcs_to_xpcs(pcs); 119562306a36Sopenharmony_ci int ret; 119662306a36Sopenharmony_ci 119762306a36Sopenharmony_ci ret = xpcs_read(xpcs, MDIO_MMD_VEND2, MDIO_CTRL1); 119862306a36Sopenharmony_ci if (ret >= 0) { 119962306a36Sopenharmony_ci ret |= BMCR_ANRESTART; 120062306a36Sopenharmony_ci xpcs_write(xpcs, MDIO_MMD_VEND2, MDIO_CTRL1, ret); 120162306a36Sopenharmony_ci } 120262306a36Sopenharmony_ci} 120362306a36Sopenharmony_ci 120462306a36Sopenharmony_cistatic u32 xpcs_get_id(struct dw_xpcs *xpcs) 120562306a36Sopenharmony_ci{ 120662306a36Sopenharmony_ci int ret; 120762306a36Sopenharmony_ci u32 id; 120862306a36Sopenharmony_ci 120962306a36Sopenharmony_ci /* First, search C73 PCS using PCS MMD */ 121062306a36Sopenharmony_ci ret = xpcs_read(xpcs, MDIO_MMD_PCS, MII_PHYSID1); 121162306a36Sopenharmony_ci if (ret < 0) 121262306a36Sopenharmony_ci return 0xffffffff; 121362306a36Sopenharmony_ci 121462306a36Sopenharmony_ci id = ret << 16; 121562306a36Sopenharmony_ci 121662306a36Sopenharmony_ci ret = xpcs_read(xpcs, MDIO_MMD_PCS, MII_PHYSID2); 121762306a36Sopenharmony_ci if (ret < 0) 121862306a36Sopenharmony_ci return 0xffffffff; 121962306a36Sopenharmony_ci 122062306a36Sopenharmony_ci /* If Device IDs are not all zeros or all ones, 122162306a36Sopenharmony_ci * we found C73 AN-type device 122262306a36Sopenharmony_ci */ 122362306a36Sopenharmony_ci if ((id | ret) && (id | ret) != 0xffffffff) 122462306a36Sopenharmony_ci return id | ret; 122562306a36Sopenharmony_ci 122662306a36Sopenharmony_ci /* Next, search C37 PCS using Vendor-Specific MII MMD */ 122762306a36Sopenharmony_ci ret = xpcs_read(xpcs, MDIO_MMD_VEND2, MII_PHYSID1); 122862306a36Sopenharmony_ci if (ret < 0) 122962306a36Sopenharmony_ci return 0xffffffff; 123062306a36Sopenharmony_ci 123162306a36Sopenharmony_ci id = ret << 16; 123262306a36Sopenharmony_ci 123362306a36Sopenharmony_ci ret = xpcs_read(xpcs, MDIO_MMD_VEND2, MII_PHYSID2); 123462306a36Sopenharmony_ci if (ret < 0) 123562306a36Sopenharmony_ci return 0xffffffff; 123662306a36Sopenharmony_ci 123762306a36Sopenharmony_ci /* If Device IDs are not all zeros, we found C37 AN-type device */ 123862306a36Sopenharmony_ci if (id | ret) 123962306a36Sopenharmony_ci return id | ret; 124062306a36Sopenharmony_ci 124162306a36Sopenharmony_ci return 0xffffffff; 124262306a36Sopenharmony_ci} 124362306a36Sopenharmony_ci 124462306a36Sopenharmony_cistatic const struct xpcs_compat synopsys_xpcs_compat[DW_XPCS_INTERFACE_MAX] = { 124562306a36Sopenharmony_ci [DW_XPCS_USXGMII] = { 124662306a36Sopenharmony_ci .supported = xpcs_usxgmii_features, 124762306a36Sopenharmony_ci .interface = xpcs_usxgmii_interfaces, 124862306a36Sopenharmony_ci .num_interfaces = ARRAY_SIZE(xpcs_usxgmii_interfaces), 124962306a36Sopenharmony_ci .an_mode = DW_AN_C73, 125062306a36Sopenharmony_ci }, 125162306a36Sopenharmony_ci [DW_XPCS_10GKR] = { 125262306a36Sopenharmony_ci .supported = xpcs_10gkr_features, 125362306a36Sopenharmony_ci .interface = xpcs_10gkr_interfaces, 125462306a36Sopenharmony_ci .num_interfaces = ARRAY_SIZE(xpcs_10gkr_interfaces), 125562306a36Sopenharmony_ci .an_mode = DW_AN_C73, 125662306a36Sopenharmony_ci }, 125762306a36Sopenharmony_ci [DW_XPCS_XLGMII] = { 125862306a36Sopenharmony_ci .supported = xpcs_xlgmii_features, 125962306a36Sopenharmony_ci .interface = xpcs_xlgmii_interfaces, 126062306a36Sopenharmony_ci .num_interfaces = ARRAY_SIZE(xpcs_xlgmii_interfaces), 126162306a36Sopenharmony_ci .an_mode = DW_AN_C73, 126262306a36Sopenharmony_ci }, 126362306a36Sopenharmony_ci [DW_XPCS_10GBASER] = { 126462306a36Sopenharmony_ci .supported = xpcs_10gbaser_features, 126562306a36Sopenharmony_ci .interface = xpcs_10gbaser_interfaces, 126662306a36Sopenharmony_ci .num_interfaces = ARRAY_SIZE(xpcs_10gbaser_interfaces), 126762306a36Sopenharmony_ci .an_mode = DW_10GBASER, 126862306a36Sopenharmony_ci }, 126962306a36Sopenharmony_ci [DW_XPCS_SGMII] = { 127062306a36Sopenharmony_ci .supported = xpcs_sgmii_features, 127162306a36Sopenharmony_ci .interface = xpcs_sgmii_interfaces, 127262306a36Sopenharmony_ci .num_interfaces = ARRAY_SIZE(xpcs_sgmii_interfaces), 127362306a36Sopenharmony_ci .an_mode = DW_AN_C37_SGMII, 127462306a36Sopenharmony_ci }, 127562306a36Sopenharmony_ci [DW_XPCS_1000BASEX] = { 127662306a36Sopenharmony_ci .supported = xpcs_1000basex_features, 127762306a36Sopenharmony_ci .interface = xpcs_1000basex_interfaces, 127862306a36Sopenharmony_ci .num_interfaces = ARRAY_SIZE(xpcs_1000basex_interfaces), 127962306a36Sopenharmony_ci .an_mode = DW_AN_C37_1000BASEX, 128062306a36Sopenharmony_ci }, 128162306a36Sopenharmony_ci [DW_XPCS_2500BASEX] = { 128262306a36Sopenharmony_ci .supported = xpcs_2500basex_features, 128362306a36Sopenharmony_ci .interface = xpcs_2500basex_interfaces, 128462306a36Sopenharmony_ci .num_interfaces = ARRAY_SIZE(xpcs_2500basex_interfaces), 128562306a36Sopenharmony_ci .an_mode = DW_2500BASEX, 128662306a36Sopenharmony_ci }, 128762306a36Sopenharmony_ci}; 128862306a36Sopenharmony_ci 128962306a36Sopenharmony_cistatic const struct xpcs_compat nxp_sja1105_xpcs_compat[DW_XPCS_INTERFACE_MAX] = { 129062306a36Sopenharmony_ci [DW_XPCS_SGMII] = { 129162306a36Sopenharmony_ci .supported = xpcs_sgmii_features, 129262306a36Sopenharmony_ci .interface = xpcs_sgmii_interfaces, 129362306a36Sopenharmony_ci .num_interfaces = ARRAY_SIZE(xpcs_sgmii_interfaces), 129462306a36Sopenharmony_ci .an_mode = DW_AN_C37_SGMII, 129562306a36Sopenharmony_ci .pma_config = nxp_sja1105_sgmii_pma_config, 129662306a36Sopenharmony_ci }, 129762306a36Sopenharmony_ci}; 129862306a36Sopenharmony_ci 129962306a36Sopenharmony_cistatic const struct xpcs_compat nxp_sja1110_xpcs_compat[DW_XPCS_INTERFACE_MAX] = { 130062306a36Sopenharmony_ci [DW_XPCS_SGMII] = { 130162306a36Sopenharmony_ci .supported = xpcs_sgmii_features, 130262306a36Sopenharmony_ci .interface = xpcs_sgmii_interfaces, 130362306a36Sopenharmony_ci .num_interfaces = ARRAY_SIZE(xpcs_sgmii_interfaces), 130462306a36Sopenharmony_ci .an_mode = DW_AN_C37_SGMII, 130562306a36Sopenharmony_ci .pma_config = nxp_sja1110_sgmii_pma_config, 130662306a36Sopenharmony_ci }, 130762306a36Sopenharmony_ci [DW_XPCS_2500BASEX] = { 130862306a36Sopenharmony_ci .supported = xpcs_2500basex_features, 130962306a36Sopenharmony_ci .interface = xpcs_2500basex_interfaces, 131062306a36Sopenharmony_ci .num_interfaces = ARRAY_SIZE(xpcs_2500basex_interfaces), 131162306a36Sopenharmony_ci .an_mode = DW_2500BASEX, 131262306a36Sopenharmony_ci .pma_config = nxp_sja1110_2500basex_pma_config, 131362306a36Sopenharmony_ci }, 131462306a36Sopenharmony_ci}; 131562306a36Sopenharmony_ci 131662306a36Sopenharmony_cistatic const struct xpcs_id xpcs_id_list[] = { 131762306a36Sopenharmony_ci { 131862306a36Sopenharmony_ci .id = SYNOPSYS_XPCS_ID, 131962306a36Sopenharmony_ci .mask = SYNOPSYS_XPCS_MASK, 132062306a36Sopenharmony_ci .compat = synopsys_xpcs_compat, 132162306a36Sopenharmony_ci }, { 132262306a36Sopenharmony_ci .id = NXP_SJA1105_XPCS_ID, 132362306a36Sopenharmony_ci .mask = SYNOPSYS_XPCS_MASK, 132462306a36Sopenharmony_ci .compat = nxp_sja1105_xpcs_compat, 132562306a36Sopenharmony_ci }, { 132662306a36Sopenharmony_ci .id = NXP_SJA1110_XPCS_ID, 132762306a36Sopenharmony_ci .mask = SYNOPSYS_XPCS_MASK, 132862306a36Sopenharmony_ci .compat = nxp_sja1110_xpcs_compat, 132962306a36Sopenharmony_ci }, 133062306a36Sopenharmony_ci}; 133162306a36Sopenharmony_ci 133262306a36Sopenharmony_cistatic const struct phylink_pcs_ops xpcs_phylink_ops = { 133362306a36Sopenharmony_ci .pcs_validate = xpcs_validate, 133462306a36Sopenharmony_ci .pcs_config = xpcs_config, 133562306a36Sopenharmony_ci .pcs_get_state = xpcs_get_state, 133662306a36Sopenharmony_ci .pcs_an_restart = xpcs_an_restart, 133762306a36Sopenharmony_ci .pcs_link_up = xpcs_link_up, 133862306a36Sopenharmony_ci}; 133962306a36Sopenharmony_ci 134062306a36Sopenharmony_cistatic struct dw_xpcs *xpcs_create(struct mdio_device *mdiodev, 134162306a36Sopenharmony_ci phy_interface_t interface) 134262306a36Sopenharmony_ci{ 134362306a36Sopenharmony_ci struct dw_xpcs *xpcs; 134462306a36Sopenharmony_ci u32 xpcs_id; 134562306a36Sopenharmony_ci int i, ret; 134662306a36Sopenharmony_ci 134762306a36Sopenharmony_ci xpcs = kzalloc(sizeof(*xpcs), GFP_KERNEL); 134862306a36Sopenharmony_ci if (!xpcs) 134962306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 135062306a36Sopenharmony_ci 135162306a36Sopenharmony_ci mdio_device_get(mdiodev); 135262306a36Sopenharmony_ci xpcs->mdiodev = mdiodev; 135362306a36Sopenharmony_ci 135462306a36Sopenharmony_ci xpcs_id = xpcs_get_id(xpcs); 135562306a36Sopenharmony_ci 135662306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(xpcs_id_list); i++) { 135762306a36Sopenharmony_ci const struct xpcs_id *entry = &xpcs_id_list[i]; 135862306a36Sopenharmony_ci const struct xpcs_compat *compat; 135962306a36Sopenharmony_ci 136062306a36Sopenharmony_ci if ((xpcs_id & entry->mask) != entry->id) 136162306a36Sopenharmony_ci continue; 136262306a36Sopenharmony_ci 136362306a36Sopenharmony_ci xpcs->id = entry; 136462306a36Sopenharmony_ci 136562306a36Sopenharmony_ci compat = xpcs_find_compat(entry, interface); 136662306a36Sopenharmony_ci if (!compat) { 136762306a36Sopenharmony_ci ret = -ENODEV; 136862306a36Sopenharmony_ci goto out; 136962306a36Sopenharmony_ci } 137062306a36Sopenharmony_ci 137162306a36Sopenharmony_ci ret = xpcs_dev_flag(xpcs); 137262306a36Sopenharmony_ci if (ret) 137362306a36Sopenharmony_ci goto out; 137462306a36Sopenharmony_ci 137562306a36Sopenharmony_ci xpcs->pcs.ops = &xpcs_phylink_ops; 137662306a36Sopenharmony_ci xpcs->pcs.neg_mode = true; 137762306a36Sopenharmony_ci 137862306a36Sopenharmony_ci if (xpcs->dev_flag != DW_DEV_TXGBE) { 137962306a36Sopenharmony_ci xpcs->pcs.poll = true; 138062306a36Sopenharmony_ci 138162306a36Sopenharmony_ci ret = xpcs_soft_reset(xpcs, compat); 138262306a36Sopenharmony_ci if (ret) 138362306a36Sopenharmony_ci goto out; 138462306a36Sopenharmony_ci } 138562306a36Sopenharmony_ci 138662306a36Sopenharmony_ci return xpcs; 138762306a36Sopenharmony_ci } 138862306a36Sopenharmony_ci 138962306a36Sopenharmony_ci ret = -ENODEV; 139062306a36Sopenharmony_ci 139162306a36Sopenharmony_ciout: 139262306a36Sopenharmony_ci mdio_device_put(mdiodev); 139362306a36Sopenharmony_ci kfree(xpcs); 139462306a36Sopenharmony_ci 139562306a36Sopenharmony_ci return ERR_PTR(ret); 139662306a36Sopenharmony_ci} 139762306a36Sopenharmony_ci 139862306a36Sopenharmony_civoid xpcs_destroy(struct dw_xpcs *xpcs) 139962306a36Sopenharmony_ci{ 140062306a36Sopenharmony_ci if (xpcs) 140162306a36Sopenharmony_ci mdio_device_put(xpcs->mdiodev); 140262306a36Sopenharmony_ci kfree(xpcs); 140362306a36Sopenharmony_ci} 140462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(xpcs_destroy); 140562306a36Sopenharmony_ci 140662306a36Sopenharmony_cistruct dw_xpcs *xpcs_create_mdiodev(struct mii_bus *bus, int addr, 140762306a36Sopenharmony_ci phy_interface_t interface) 140862306a36Sopenharmony_ci{ 140962306a36Sopenharmony_ci struct mdio_device *mdiodev; 141062306a36Sopenharmony_ci struct dw_xpcs *xpcs; 141162306a36Sopenharmony_ci 141262306a36Sopenharmony_ci mdiodev = mdio_device_create(bus, addr); 141362306a36Sopenharmony_ci if (IS_ERR(mdiodev)) 141462306a36Sopenharmony_ci return ERR_CAST(mdiodev); 141562306a36Sopenharmony_ci 141662306a36Sopenharmony_ci xpcs = xpcs_create(mdiodev, interface); 141762306a36Sopenharmony_ci 141862306a36Sopenharmony_ci /* xpcs_create() has taken a refcount on the mdiodev if it was 141962306a36Sopenharmony_ci * successful. If xpcs_create() fails, this will free the mdio 142062306a36Sopenharmony_ci * device here. In any case, we don't need to hold our reference 142162306a36Sopenharmony_ci * anymore, and putting it here will allow mdio_device_put() in 142262306a36Sopenharmony_ci * xpcs_destroy() to automatically free the mdio device. 142362306a36Sopenharmony_ci */ 142462306a36Sopenharmony_ci mdio_device_put(mdiodev); 142562306a36Sopenharmony_ci 142662306a36Sopenharmony_ci return xpcs; 142762306a36Sopenharmony_ci} 142862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(xpcs_create_mdiodev); 142962306a36Sopenharmony_ci 143062306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 1431