162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Marvell 10G 88x3310 PHY driver 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Based upon the ID registers, this PHY appears to be a mixture of IPs 662306a36Sopenharmony_ci * from two different companies. 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * There appears to be several different data paths through the PHY which 962306a36Sopenharmony_ci * are automatically managed by the PHY. The following has been determined 1062306a36Sopenharmony_ci * via observation and experimentation for a setup using single-lane Serdes: 1162306a36Sopenharmony_ci * 1262306a36Sopenharmony_ci * SGMII PHYXS -- BASE-T PCS -- 10G PMA -- AN -- Copper (for <= 1G) 1362306a36Sopenharmony_ci * 10GBASE-KR PHYXS -- BASE-T PCS -- 10G PMA -- AN -- Copper (for 10G) 1462306a36Sopenharmony_ci * 10GBASE-KR PHYXS -- BASE-R PCS -- Fiber 1562306a36Sopenharmony_ci * 1662306a36Sopenharmony_ci * With XAUI, observation shows: 1762306a36Sopenharmony_ci * 1862306a36Sopenharmony_ci * XAUI PHYXS -- <appropriate PCS as above> 1962306a36Sopenharmony_ci * 2062306a36Sopenharmony_ci * and no switching of the host interface mode occurs. 2162306a36Sopenharmony_ci * 2262306a36Sopenharmony_ci * If both the fiber and copper ports are connected, the first to gain 2362306a36Sopenharmony_ci * link takes priority and the other port is completely locked out. 2462306a36Sopenharmony_ci */ 2562306a36Sopenharmony_ci#include <linux/bitfield.h> 2662306a36Sopenharmony_ci#include <linux/ctype.h> 2762306a36Sopenharmony_ci#include <linux/delay.h> 2862306a36Sopenharmony_ci#include <linux/hwmon.h> 2962306a36Sopenharmony_ci#include <linux/marvell_phy.h> 3062306a36Sopenharmony_ci#include <linux/phy.h> 3162306a36Sopenharmony_ci#include <linux/sfp.h> 3262306a36Sopenharmony_ci#include <linux/netdevice.h> 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci#define MV_PHY_ALASKA_NBT_QUIRK_MASK 0xfffffffe 3562306a36Sopenharmony_ci#define MV_PHY_ALASKA_NBT_QUIRK_REV (MARVELL_PHY_ID_88X3310 | 0xa) 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci#define MV_VERSION(a,b,c,d) ((a) << 24 | (b) << 16 | (c) << 8 | (d)) 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_cienum { 4062306a36Sopenharmony_ci MV_PMA_FW_VER0 = 0xc011, 4162306a36Sopenharmony_ci MV_PMA_FW_VER1 = 0xc012, 4262306a36Sopenharmony_ci MV_PMA_21X0_PORT_CTRL = 0xc04a, 4362306a36Sopenharmony_ci MV_PMA_21X0_PORT_CTRL_SWRST = BIT(15), 4462306a36Sopenharmony_ci MV_PMA_21X0_PORT_CTRL_MACTYPE_MASK = 0x7, 4562306a36Sopenharmony_ci MV_PMA_21X0_PORT_CTRL_MACTYPE_USXGMII = 0x0, 4662306a36Sopenharmony_ci MV_PMA_2180_PORT_CTRL_MACTYPE_DXGMII = 0x1, 4762306a36Sopenharmony_ci MV_PMA_2180_PORT_CTRL_MACTYPE_QXGMII = 0x2, 4862306a36Sopenharmony_ci MV_PMA_21X0_PORT_CTRL_MACTYPE_5GBASER = 0x4, 4962306a36Sopenharmony_ci MV_PMA_21X0_PORT_CTRL_MACTYPE_5GBASER_NO_SGMII_AN = 0x5, 5062306a36Sopenharmony_ci MV_PMA_21X0_PORT_CTRL_MACTYPE_10GBASER_RATE_MATCH = 0x6, 5162306a36Sopenharmony_ci MV_PMA_BOOT = 0xc050, 5262306a36Sopenharmony_ci MV_PMA_BOOT_FATAL = BIT(0), 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci MV_PCS_BASE_T = 0x0000, 5562306a36Sopenharmony_ci MV_PCS_BASE_R = 0x1000, 5662306a36Sopenharmony_ci MV_PCS_1000BASEX = 0x2000, 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci MV_PCS_CSCR1 = 0x8000, 5962306a36Sopenharmony_ci MV_PCS_CSCR1_ED_MASK = 0x0300, 6062306a36Sopenharmony_ci MV_PCS_CSCR1_ED_OFF = 0x0000, 6162306a36Sopenharmony_ci MV_PCS_CSCR1_ED_RX = 0x0200, 6262306a36Sopenharmony_ci MV_PCS_CSCR1_ED_NLP = 0x0300, 6362306a36Sopenharmony_ci MV_PCS_CSCR1_MDIX_MASK = 0x0060, 6462306a36Sopenharmony_ci MV_PCS_CSCR1_MDIX_MDI = 0x0000, 6562306a36Sopenharmony_ci MV_PCS_CSCR1_MDIX_MDIX = 0x0020, 6662306a36Sopenharmony_ci MV_PCS_CSCR1_MDIX_AUTO = 0x0060, 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci MV_PCS_DSC1 = 0x8003, 6962306a36Sopenharmony_ci MV_PCS_DSC1_ENABLE = BIT(9), 7062306a36Sopenharmony_ci MV_PCS_DSC1_10GBT = 0x01c0, 7162306a36Sopenharmony_ci MV_PCS_DSC1_1GBR = 0x0038, 7262306a36Sopenharmony_ci MV_PCS_DSC1_100BTX = 0x0007, 7362306a36Sopenharmony_ci MV_PCS_DSC2 = 0x8004, 7462306a36Sopenharmony_ci MV_PCS_DSC2_2P5G = 0xf000, 7562306a36Sopenharmony_ci MV_PCS_DSC2_5G = 0x0f00, 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci MV_PCS_CSSR1 = 0x8008, 7862306a36Sopenharmony_ci MV_PCS_CSSR1_SPD1_MASK = 0xc000, 7962306a36Sopenharmony_ci MV_PCS_CSSR1_SPD1_SPD2 = 0xc000, 8062306a36Sopenharmony_ci MV_PCS_CSSR1_SPD1_1000 = 0x8000, 8162306a36Sopenharmony_ci MV_PCS_CSSR1_SPD1_100 = 0x4000, 8262306a36Sopenharmony_ci MV_PCS_CSSR1_SPD1_10 = 0x0000, 8362306a36Sopenharmony_ci MV_PCS_CSSR1_DUPLEX_FULL= BIT(13), 8462306a36Sopenharmony_ci MV_PCS_CSSR1_RESOLVED = BIT(11), 8562306a36Sopenharmony_ci MV_PCS_CSSR1_MDIX = BIT(6), 8662306a36Sopenharmony_ci MV_PCS_CSSR1_SPD2_MASK = 0x000c, 8762306a36Sopenharmony_ci MV_PCS_CSSR1_SPD2_5000 = 0x0008, 8862306a36Sopenharmony_ci MV_PCS_CSSR1_SPD2_2500 = 0x0004, 8962306a36Sopenharmony_ci MV_PCS_CSSR1_SPD2_10000 = 0x0000, 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci /* Temperature read register (88E2110 only) */ 9262306a36Sopenharmony_ci MV_PCS_TEMP = 0x8042, 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci /* Number of ports on the device */ 9562306a36Sopenharmony_ci MV_PCS_PORT_INFO = 0xd00d, 9662306a36Sopenharmony_ci MV_PCS_PORT_INFO_NPORTS_MASK = 0x0380, 9762306a36Sopenharmony_ci MV_PCS_PORT_INFO_NPORTS_SHIFT = 7, 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci /* SerDes reinitialization 88E21X0 */ 10062306a36Sopenharmony_ci MV_AN_21X0_SERDES_CTRL2 = 0x800f, 10162306a36Sopenharmony_ci MV_AN_21X0_SERDES_CTRL2_AUTO_INIT_DIS = BIT(13), 10262306a36Sopenharmony_ci MV_AN_21X0_SERDES_CTRL2_RUN_INIT = BIT(15), 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci /* These registers appear at 0x800X and 0xa00X - the 0xa00X control 10562306a36Sopenharmony_ci * registers appear to set themselves to the 0x800X when AN is 10662306a36Sopenharmony_ci * restarted, but status registers appear readable from either. 10762306a36Sopenharmony_ci */ 10862306a36Sopenharmony_ci MV_AN_CTRL1000 = 0x8000, /* 1000base-T control register */ 10962306a36Sopenharmony_ci MV_AN_STAT1000 = 0x8001, /* 1000base-T status register */ 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci /* Vendor2 MMD registers */ 11262306a36Sopenharmony_ci MV_V2_PORT_CTRL = 0xf001, 11362306a36Sopenharmony_ci MV_V2_PORT_CTRL_PWRDOWN = BIT(11), 11462306a36Sopenharmony_ci MV_V2_33X0_PORT_CTRL_SWRST = BIT(15), 11562306a36Sopenharmony_ci MV_V2_33X0_PORT_CTRL_MACTYPE_MASK = 0x7, 11662306a36Sopenharmony_ci MV_V2_33X0_PORT_CTRL_MACTYPE_RXAUI = 0x0, 11762306a36Sopenharmony_ci MV_V2_3310_PORT_CTRL_MACTYPE_XAUI_RATE_MATCH = 0x1, 11862306a36Sopenharmony_ci MV_V2_3340_PORT_CTRL_MACTYPE_RXAUI_NO_SGMII_AN = 0x1, 11962306a36Sopenharmony_ci MV_V2_33X0_PORT_CTRL_MACTYPE_RXAUI_RATE_MATCH = 0x2, 12062306a36Sopenharmony_ci MV_V2_3310_PORT_CTRL_MACTYPE_XAUI = 0x3, 12162306a36Sopenharmony_ci MV_V2_33X0_PORT_CTRL_MACTYPE_10GBASER = 0x4, 12262306a36Sopenharmony_ci MV_V2_33X0_PORT_CTRL_MACTYPE_10GBASER_NO_SGMII_AN = 0x5, 12362306a36Sopenharmony_ci MV_V2_33X0_PORT_CTRL_MACTYPE_10GBASER_RATE_MATCH = 0x6, 12462306a36Sopenharmony_ci MV_V2_33X0_PORT_CTRL_MACTYPE_USXGMII = 0x7, 12562306a36Sopenharmony_ci MV_V2_PORT_INTR_STS = 0xf040, 12662306a36Sopenharmony_ci MV_V2_PORT_INTR_MASK = 0xf043, 12762306a36Sopenharmony_ci MV_V2_PORT_INTR_STS_WOL_EN = BIT(8), 12862306a36Sopenharmony_ci MV_V2_MAGIC_PKT_WORD0 = 0xf06b, 12962306a36Sopenharmony_ci MV_V2_MAGIC_PKT_WORD1 = 0xf06c, 13062306a36Sopenharmony_ci MV_V2_MAGIC_PKT_WORD2 = 0xf06d, 13162306a36Sopenharmony_ci /* Wake on LAN registers */ 13262306a36Sopenharmony_ci MV_V2_WOL_CTRL = 0xf06e, 13362306a36Sopenharmony_ci MV_V2_WOL_CTRL_CLEAR_STS = BIT(15), 13462306a36Sopenharmony_ci MV_V2_WOL_CTRL_MAGIC_PKT_EN = BIT(0), 13562306a36Sopenharmony_ci /* Temperature control/read registers (88X3310 only) */ 13662306a36Sopenharmony_ci MV_V2_TEMP_CTRL = 0xf08a, 13762306a36Sopenharmony_ci MV_V2_TEMP_CTRL_MASK = 0xc000, 13862306a36Sopenharmony_ci MV_V2_TEMP_CTRL_SAMPLE = 0x0000, 13962306a36Sopenharmony_ci MV_V2_TEMP_CTRL_DISABLE = 0xc000, 14062306a36Sopenharmony_ci MV_V2_TEMP = 0xf08c, 14162306a36Sopenharmony_ci MV_V2_TEMP_UNKNOWN = 0x9600, /* unknown function */ 14262306a36Sopenharmony_ci}; 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_cistruct mv3310_chip { 14562306a36Sopenharmony_ci bool (*has_downshift)(struct phy_device *phydev); 14662306a36Sopenharmony_ci void (*init_supported_interfaces)(unsigned long *mask); 14762306a36Sopenharmony_ci int (*get_mactype)(struct phy_device *phydev); 14862306a36Sopenharmony_ci int (*set_mactype)(struct phy_device *phydev, int mactype); 14962306a36Sopenharmony_ci int (*select_mactype)(unsigned long *interfaces); 15062306a36Sopenharmony_ci int (*init_interface)(struct phy_device *phydev, int mactype); 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci#ifdef CONFIG_HWMON 15362306a36Sopenharmony_ci int (*hwmon_read_temp_reg)(struct phy_device *phydev); 15462306a36Sopenharmony_ci#endif 15562306a36Sopenharmony_ci}; 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_cistruct mv3310_priv { 15862306a36Sopenharmony_ci DECLARE_BITMAP(supported_interfaces, PHY_INTERFACE_MODE_MAX); 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci u32 firmware_ver; 16162306a36Sopenharmony_ci bool has_downshift; 16262306a36Sopenharmony_ci bool rate_match; 16362306a36Sopenharmony_ci phy_interface_t const_interface; 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci struct device *hwmon_dev; 16662306a36Sopenharmony_ci char *hwmon_name; 16762306a36Sopenharmony_ci}; 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_cistatic const struct mv3310_chip *to_mv3310_chip(struct phy_device *phydev) 17062306a36Sopenharmony_ci{ 17162306a36Sopenharmony_ci return phydev->drv->driver_data; 17262306a36Sopenharmony_ci} 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci#ifdef CONFIG_HWMON 17562306a36Sopenharmony_cistatic umode_t mv3310_hwmon_is_visible(const void *data, 17662306a36Sopenharmony_ci enum hwmon_sensor_types type, 17762306a36Sopenharmony_ci u32 attr, int channel) 17862306a36Sopenharmony_ci{ 17962306a36Sopenharmony_ci if (type == hwmon_chip && attr == hwmon_chip_update_interval) 18062306a36Sopenharmony_ci return 0444; 18162306a36Sopenharmony_ci if (type == hwmon_temp && attr == hwmon_temp_input) 18262306a36Sopenharmony_ci return 0444; 18362306a36Sopenharmony_ci return 0; 18462306a36Sopenharmony_ci} 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_cistatic int mv3310_hwmon_read_temp_reg(struct phy_device *phydev) 18762306a36Sopenharmony_ci{ 18862306a36Sopenharmony_ci return phy_read_mmd(phydev, MDIO_MMD_VEND2, MV_V2_TEMP); 18962306a36Sopenharmony_ci} 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_cistatic int mv2110_hwmon_read_temp_reg(struct phy_device *phydev) 19262306a36Sopenharmony_ci{ 19362306a36Sopenharmony_ci return phy_read_mmd(phydev, MDIO_MMD_PCS, MV_PCS_TEMP); 19462306a36Sopenharmony_ci} 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_cistatic int mv3310_hwmon_read(struct device *dev, enum hwmon_sensor_types type, 19762306a36Sopenharmony_ci u32 attr, int channel, long *value) 19862306a36Sopenharmony_ci{ 19962306a36Sopenharmony_ci struct phy_device *phydev = dev_get_drvdata(dev); 20062306a36Sopenharmony_ci const struct mv3310_chip *chip = to_mv3310_chip(phydev); 20162306a36Sopenharmony_ci int temp; 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci if (type == hwmon_chip && attr == hwmon_chip_update_interval) { 20462306a36Sopenharmony_ci *value = MSEC_PER_SEC; 20562306a36Sopenharmony_ci return 0; 20662306a36Sopenharmony_ci } 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci if (type == hwmon_temp && attr == hwmon_temp_input) { 20962306a36Sopenharmony_ci temp = chip->hwmon_read_temp_reg(phydev); 21062306a36Sopenharmony_ci if (temp < 0) 21162306a36Sopenharmony_ci return temp; 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci *value = ((temp & 0xff) - 75) * 1000; 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci return 0; 21662306a36Sopenharmony_ci } 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci return -EOPNOTSUPP; 21962306a36Sopenharmony_ci} 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_cistatic const struct hwmon_ops mv3310_hwmon_ops = { 22262306a36Sopenharmony_ci .is_visible = mv3310_hwmon_is_visible, 22362306a36Sopenharmony_ci .read = mv3310_hwmon_read, 22462306a36Sopenharmony_ci}; 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_cistatic u32 mv3310_hwmon_chip_config[] = { 22762306a36Sopenharmony_ci HWMON_C_REGISTER_TZ | HWMON_C_UPDATE_INTERVAL, 22862306a36Sopenharmony_ci 0, 22962306a36Sopenharmony_ci}; 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_cistatic const struct hwmon_channel_info mv3310_hwmon_chip = { 23262306a36Sopenharmony_ci .type = hwmon_chip, 23362306a36Sopenharmony_ci .config = mv3310_hwmon_chip_config, 23462306a36Sopenharmony_ci}; 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_cistatic u32 mv3310_hwmon_temp_config[] = { 23762306a36Sopenharmony_ci HWMON_T_INPUT, 23862306a36Sopenharmony_ci 0, 23962306a36Sopenharmony_ci}; 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_cistatic const struct hwmon_channel_info mv3310_hwmon_temp = { 24262306a36Sopenharmony_ci .type = hwmon_temp, 24362306a36Sopenharmony_ci .config = mv3310_hwmon_temp_config, 24462306a36Sopenharmony_ci}; 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_cistatic const struct hwmon_channel_info * const mv3310_hwmon_info[] = { 24762306a36Sopenharmony_ci &mv3310_hwmon_chip, 24862306a36Sopenharmony_ci &mv3310_hwmon_temp, 24962306a36Sopenharmony_ci NULL, 25062306a36Sopenharmony_ci}; 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_cistatic const struct hwmon_chip_info mv3310_hwmon_chip_info = { 25362306a36Sopenharmony_ci .ops = &mv3310_hwmon_ops, 25462306a36Sopenharmony_ci .info = mv3310_hwmon_info, 25562306a36Sopenharmony_ci}; 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_cistatic int mv3310_hwmon_config(struct phy_device *phydev, bool enable) 25862306a36Sopenharmony_ci{ 25962306a36Sopenharmony_ci u16 val; 26062306a36Sopenharmony_ci int ret; 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci if (phydev->drv->phy_id != MARVELL_PHY_ID_88X3310) 26362306a36Sopenharmony_ci return 0; 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci ret = phy_write_mmd(phydev, MDIO_MMD_VEND2, MV_V2_TEMP, 26662306a36Sopenharmony_ci MV_V2_TEMP_UNKNOWN); 26762306a36Sopenharmony_ci if (ret < 0) 26862306a36Sopenharmony_ci return ret; 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci val = enable ? MV_V2_TEMP_CTRL_SAMPLE : MV_V2_TEMP_CTRL_DISABLE; 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci return phy_modify_mmd(phydev, MDIO_MMD_VEND2, MV_V2_TEMP_CTRL, 27362306a36Sopenharmony_ci MV_V2_TEMP_CTRL_MASK, val); 27462306a36Sopenharmony_ci} 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_cistatic int mv3310_hwmon_probe(struct phy_device *phydev) 27762306a36Sopenharmony_ci{ 27862306a36Sopenharmony_ci struct device *dev = &phydev->mdio.dev; 27962306a36Sopenharmony_ci struct mv3310_priv *priv = dev_get_drvdata(&phydev->mdio.dev); 28062306a36Sopenharmony_ci int i, j, ret; 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci priv->hwmon_name = devm_kstrdup(dev, dev_name(dev), GFP_KERNEL); 28362306a36Sopenharmony_ci if (!priv->hwmon_name) 28462306a36Sopenharmony_ci return -ENODEV; 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ci for (i = j = 0; priv->hwmon_name[i]; i++) { 28762306a36Sopenharmony_ci if (isalnum(priv->hwmon_name[i])) { 28862306a36Sopenharmony_ci if (i != j) 28962306a36Sopenharmony_ci priv->hwmon_name[j] = priv->hwmon_name[i]; 29062306a36Sopenharmony_ci j++; 29162306a36Sopenharmony_ci } 29262306a36Sopenharmony_ci } 29362306a36Sopenharmony_ci priv->hwmon_name[j] = '\0'; 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_ci ret = mv3310_hwmon_config(phydev, true); 29662306a36Sopenharmony_ci if (ret) 29762306a36Sopenharmony_ci return ret; 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci priv->hwmon_dev = devm_hwmon_device_register_with_info(dev, 30062306a36Sopenharmony_ci priv->hwmon_name, phydev, 30162306a36Sopenharmony_ci &mv3310_hwmon_chip_info, NULL); 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ci return PTR_ERR_OR_ZERO(priv->hwmon_dev); 30462306a36Sopenharmony_ci} 30562306a36Sopenharmony_ci#else 30662306a36Sopenharmony_cistatic inline int mv3310_hwmon_config(struct phy_device *phydev, bool enable) 30762306a36Sopenharmony_ci{ 30862306a36Sopenharmony_ci return 0; 30962306a36Sopenharmony_ci} 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_cistatic int mv3310_hwmon_probe(struct phy_device *phydev) 31262306a36Sopenharmony_ci{ 31362306a36Sopenharmony_ci return 0; 31462306a36Sopenharmony_ci} 31562306a36Sopenharmony_ci#endif 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_cistatic int mv3310_power_down(struct phy_device *phydev) 31862306a36Sopenharmony_ci{ 31962306a36Sopenharmony_ci return phy_set_bits_mmd(phydev, MDIO_MMD_VEND2, MV_V2_PORT_CTRL, 32062306a36Sopenharmony_ci MV_V2_PORT_CTRL_PWRDOWN); 32162306a36Sopenharmony_ci} 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_cistatic int mv3310_power_up(struct phy_device *phydev) 32462306a36Sopenharmony_ci{ 32562306a36Sopenharmony_ci struct mv3310_priv *priv = dev_get_drvdata(&phydev->mdio.dev); 32662306a36Sopenharmony_ci int ret; 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_ci ret = phy_clear_bits_mmd(phydev, MDIO_MMD_VEND2, MV_V2_PORT_CTRL, 32962306a36Sopenharmony_ci MV_V2_PORT_CTRL_PWRDOWN); 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ci /* Sometimes, the power down bit doesn't clear immediately, and 33262306a36Sopenharmony_ci * a read of this register causes the bit not to clear. Delay 33362306a36Sopenharmony_ci * 100us to allow the PHY to come out of power down mode before 33462306a36Sopenharmony_ci * the next access. 33562306a36Sopenharmony_ci */ 33662306a36Sopenharmony_ci udelay(100); 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_ci if (phydev->drv->phy_id != MARVELL_PHY_ID_88X3310 || 33962306a36Sopenharmony_ci priv->firmware_ver < 0x00030000) 34062306a36Sopenharmony_ci return ret; 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_ci return phy_set_bits_mmd(phydev, MDIO_MMD_VEND2, MV_V2_PORT_CTRL, 34362306a36Sopenharmony_ci MV_V2_33X0_PORT_CTRL_SWRST); 34462306a36Sopenharmony_ci} 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_cistatic int mv3310_reset(struct phy_device *phydev, u32 unit) 34762306a36Sopenharmony_ci{ 34862306a36Sopenharmony_ci int val, err; 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_ci err = phy_modify_mmd(phydev, MDIO_MMD_PCS, unit + MDIO_CTRL1, 35162306a36Sopenharmony_ci MDIO_CTRL1_RESET, MDIO_CTRL1_RESET); 35262306a36Sopenharmony_ci if (err < 0) 35362306a36Sopenharmony_ci return err; 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ci return phy_read_mmd_poll_timeout(phydev, MDIO_MMD_PCS, 35662306a36Sopenharmony_ci unit + MDIO_CTRL1, val, 35762306a36Sopenharmony_ci !(val & MDIO_CTRL1_RESET), 35862306a36Sopenharmony_ci 5000, 100000, true); 35962306a36Sopenharmony_ci} 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_cistatic int mv3310_get_downshift(struct phy_device *phydev, u8 *ds) 36262306a36Sopenharmony_ci{ 36362306a36Sopenharmony_ci struct mv3310_priv *priv = dev_get_drvdata(&phydev->mdio.dev); 36462306a36Sopenharmony_ci int val; 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_ci if (!priv->has_downshift) 36762306a36Sopenharmony_ci return -EOPNOTSUPP; 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ci val = phy_read_mmd(phydev, MDIO_MMD_PCS, MV_PCS_DSC1); 37062306a36Sopenharmony_ci if (val < 0) 37162306a36Sopenharmony_ci return val; 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_ci if (val & MV_PCS_DSC1_ENABLE) 37462306a36Sopenharmony_ci /* assume that all fields are the same */ 37562306a36Sopenharmony_ci *ds = 1 + FIELD_GET(MV_PCS_DSC1_10GBT, (u16)val); 37662306a36Sopenharmony_ci else 37762306a36Sopenharmony_ci *ds = DOWNSHIFT_DEV_DISABLE; 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_ci return 0; 38062306a36Sopenharmony_ci} 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_cistatic int mv3310_set_downshift(struct phy_device *phydev, u8 ds) 38362306a36Sopenharmony_ci{ 38462306a36Sopenharmony_ci struct mv3310_priv *priv = dev_get_drvdata(&phydev->mdio.dev); 38562306a36Sopenharmony_ci u16 val; 38662306a36Sopenharmony_ci int err; 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_ci if (!priv->has_downshift) 38962306a36Sopenharmony_ci return -EOPNOTSUPP; 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_ci if (ds == DOWNSHIFT_DEV_DISABLE) 39262306a36Sopenharmony_ci return phy_clear_bits_mmd(phydev, MDIO_MMD_PCS, MV_PCS_DSC1, 39362306a36Sopenharmony_ci MV_PCS_DSC1_ENABLE); 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_ci /* DOWNSHIFT_DEV_DEFAULT_COUNT is confusing. It looks like it should 39662306a36Sopenharmony_ci * set the default settings for the PHY. However, it is used for 39762306a36Sopenharmony_ci * "ethtool --set-phy-tunable ethN downshift on". The intention is 39862306a36Sopenharmony_ci * to enable downshift at a default number of retries. The default 39962306a36Sopenharmony_ci * settings for 88x3310 are for two retries with downshift disabled. 40062306a36Sopenharmony_ci * So let's use two retries with downshift enabled. 40162306a36Sopenharmony_ci */ 40262306a36Sopenharmony_ci if (ds == DOWNSHIFT_DEV_DEFAULT_COUNT) 40362306a36Sopenharmony_ci ds = 2; 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_ci if (ds > 8) 40662306a36Sopenharmony_ci return -E2BIG; 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_ci ds -= 1; 40962306a36Sopenharmony_ci val = FIELD_PREP(MV_PCS_DSC2_2P5G, ds); 41062306a36Sopenharmony_ci val |= FIELD_PREP(MV_PCS_DSC2_5G, ds); 41162306a36Sopenharmony_ci err = phy_modify_mmd(phydev, MDIO_MMD_PCS, MV_PCS_DSC2, 41262306a36Sopenharmony_ci MV_PCS_DSC2_2P5G | MV_PCS_DSC2_5G, val); 41362306a36Sopenharmony_ci if (err < 0) 41462306a36Sopenharmony_ci return err; 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_ci val = MV_PCS_DSC1_ENABLE; 41762306a36Sopenharmony_ci val |= FIELD_PREP(MV_PCS_DSC1_10GBT, ds); 41862306a36Sopenharmony_ci val |= FIELD_PREP(MV_PCS_DSC1_1GBR, ds); 41962306a36Sopenharmony_ci val |= FIELD_PREP(MV_PCS_DSC1_100BTX, ds); 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_ci return phy_modify_mmd(phydev, MDIO_MMD_PCS, MV_PCS_DSC1, 42262306a36Sopenharmony_ci MV_PCS_DSC1_ENABLE | MV_PCS_DSC1_10GBT | 42362306a36Sopenharmony_ci MV_PCS_DSC1_1GBR | MV_PCS_DSC1_100BTX, val); 42462306a36Sopenharmony_ci} 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_cistatic int mv3310_get_edpd(struct phy_device *phydev, u16 *edpd) 42762306a36Sopenharmony_ci{ 42862306a36Sopenharmony_ci int val; 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_ci val = phy_read_mmd(phydev, MDIO_MMD_PCS, MV_PCS_CSCR1); 43162306a36Sopenharmony_ci if (val < 0) 43262306a36Sopenharmony_ci return val; 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_ci switch (val & MV_PCS_CSCR1_ED_MASK) { 43562306a36Sopenharmony_ci case MV_PCS_CSCR1_ED_NLP: 43662306a36Sopenharmony_ci *edpd = 1000; 43762306a36Sopenharmony_ci break; 43862306a36Sopenharmony_ci case MV_PCS_CSCR1_ED_RX: 43962306a36Sopenharmony_ci *edpd = ETHTOOL_PHY_EDPD_NO_TX; 44062306a36Sopenharmony_ci break; 44162306a36Sopenharmony_ci default: 44262306a36Sopenharmony_ci *edpd = ETHTOOL_PHY_EDPD_DISABLE; 44362306a36Sopenharmony_ci break; 44462306a36Sopenharmony_ci } 44562306a36Sopenharmony_ci return 0; 44662306a36Sopenharmony_ci} 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_cistatic int mv3310_set_edpd(struct phy_device *phydev, u16 edpd) 44962306a36Sopenharmony_ci{ 45062306a36Sopenharmony_ci u16 val; 45162306a36Sopenharmony_ci int err; 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_ci switch (edpd) { 45462306a36Sopenharmony_ci case 1000: 45562306a36Sopenharmony_ci case ETHTOOL_PHY_EDPD_DFLT_TX_MSECS: 45662306a36Sopenharmony_ci val = MV_PCS_CSCR1_ED_NLP; 45762306a36Sopenharmony_ci break; 45862306a36Sopenharmony_ci 45962306a36Sopenharmony_ci case ETHTOOL_PHY_EDPD_NO_TX: 46062306a36Sopenharmony_ci val = MV_PCS_CSCR1_ED_RX; 46162306a36Sopenharmony_ci break; 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_ci case ETHTOOL_PHY_EDPD_DISABLE: 46462306a36Sopenharmony_ci val = MV_PCS_CSCR1_ED_OFF; 46562306a36Sopenharmony_ci break; 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_ci default: 46862306a36Sopenharmony_ci return -EINVAL; 46962306a36Sopenharmony_ci } 47062306a36Sopenharmony_ci 47162306a36Sopenharmony_ci err = phy_modify_mmd_changed(phydev, MDIO_MMD_PCS, MV_PCS_CSCR1, 47262306a36Sopenharmony_ci MV_PCS_CSCR1_ED_MASK, val); 47362306a36Sopenharmony_ci if (err > 0) 47462306a36Sopenharmony_ci err = mv3310_reset(phydev, MV_PCS_BASE_T); 47562306a36Sopenharmony_ci 47662306a36Sopenharmony_ci return err; 47762306a36Sopenharmony_ci} 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_cistatic int mv3310_sfp_insert(void *upstream, const struct sfp_eeprom_id *id) 48062306a36Sopenharmony_ci{ 48162306a36Sopenharmony_ci struct phy_device *phydev = upstream; 48262306a36Sopenharmony_ci __ETHTOOL_DECLARE_LINK_MODE_MASK(support) = { 0, }; 48362306a36Sopenharmony_ci DECLARE_PHY_INTERFACE_MASK(interfaces); 48462306a36Sopenharmony_ci phy_interface_t iface; 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_ci sfp_parse_support(phydev->sfp_bus, id, support, interfaces); 48762306a36Sopenharmony_ci iface = sfp_select_interface(phydev->sfp_bus, support); 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_ci if (iface != PHY_INTERFACE_MODE_10GBASER) { 49062306a36Sopenharmony_ci dev_err(&phydev->mdio.dev, "incompatible SFP module inserted\n"); 49162306a36Sopenharmony_ci return -EINVAL; 49262306a36Sopenharmony_ci } 49362306a36Sopenharmony_ci return 0; 49462306a36Sopenharmony_ci} 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_cistatic const struct sfp_upstream_ops mv3310_sfp_ops = { 49762306a36Sopenharmony_ci .attach = phy_sfp_attach, 49862306a36Sopenharmony_ci .detach = phy_sfp_detach, 49962306a36Sopenharmony_ci .module_insert = mv3310_sfp_insert, 50062306a36Sopenharmony_ci}; 50162306a36Sopenharmony_ci 50262306a36Sopenharmony_cistatic int mv3310_probe(struct phy_device *phydev) 50362306a36Sopenharmony_ci{ 50462306a36Sopenharmony_ci const struct mv3310_chip *chip = to_mv3310_chip(phydev); 50562306a36Sopenharmony_ci struct mv3310_priv *priv; 50662306a36Sopenharmony_ci u32 mmd_mask = MDIO_DEVS_PMAPMD | MDIO_DEVS_AN; 50762306a36Sopenharmony_ci int ret; 50862306a36Sopenharmony_ci 50962306a36Sopenharmony_ci if (!phydev->is_c45 || 51062306a36Sopenharmony_ci (phydev->c45_ids.devices_in_package & mmd_mask) != mmd_mask) 51162306a36Sopenharmony_ci return -ENODEV; 51262306a36Sopenharmony_ci 51362306a36Sopenharmony_ci ret = phy_read_mmd(phydev, MDIO_MMD_PMAPMD, MV_PMA_BOOT); 51462306a36Sopenharmony_ci if (ret < 0) 51562306a36Sopenharmony_ci return ret; 51662306a36Sopenharmony_ci 51762306a36Sopenharmony_ci if (ret & MV_PMA_BOOT_FATAL) { 51862306a36Sopenharmony_ci dev_warn(&phydev->mdio.dev, 51962306a36Sopenharmony_ci "PHY failed to boot firmware, status=%04x\n", ret); 52062306a36Sopenharmony_ci return -ENODEV; 52162306a36Sopenharmony_ci } 52262306a36Sopenharmony_ci 52362306a36Sopenharmony_ci priv = devm_kzalloc(&phydev->mdio.dev, sizeof(*priv), GFP_KERNEL); 52462306a36Sopenharmony_ci if (!priv) 52562306a36Sopenharmony_ci return -ENOMEM; 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_ci dev_set_drvdata(&phydev->mdio.dev, priv); 52862306a36Sopenharmony_ci 52962306a36Sopenharmony_ci ret = phy_read_mmd(phydev, MDIO_MMD_PMAPMD, MV_PMA_FW_VER0); 53062306a36Sopenharmony_ci if (ret < 0) 53162306a36Sopenharmony_ci return ret; 53262306a36Sopenharmony_ci 53362306a36Sopenharmony_ci priv->firmware_ver = ret << 16; 53462306a36Sopenharmony_ci 53562306a36Sopenharmony_ci ret = phy_read_mmd(phydev, MDIO_MMD_PMAPMD, MV_PMA_FW_VER1); 53662306a36Sopenharmony_ci if (ret < 0) 53762306a36Sopenharmony_ci return ret; 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_ci priv->firmware_ver |= ret; 54062306a36Sopenharmony_ci 54162306a36Sopenharmony_ci phydev_info(phydev, "Firmware version %u.%u.%u.%u\n", 54262306a36Sopenharmony_ci priv->firmware_ver >> 24, (priv->firmware_ver >> 16) & 255, 54362306a36Sopenharmony_ci (priv->firmware_ver >> 8) & 255, priv->firmware_ver & 255); 54462306a36Sopenharmony_ci 54562306a36Sopenharmony_ci if (chip->has_downshift) 54662306a36Sopenharmony_ci priv->has_downshift = chip->has_downshift(phydev); 54762306a36Sopenharmony_ci 54862306a36Sopenharmony_ci /* Powering down the port when not in use saves about 600mW */ 54962306a36Sopenharmony_ci ret = mv3310_power_down(phydev); 55062306a36Sopenharmony_ci if (ret) 55162306a36Sopenharmony_ci return ret; 55262306a36Sopenharmony_ci 55362306a36Sopenharmony_ci ret = mv3310_hwmon_probe(phydev); 55462306a36Sopenharmony_ci if (ret) 55562306a36Sopenharmony_ci return ret; 55662306a36Sopenharmony_ci 55762306a36Sopenharmony_ci chip->init_supported_interfaces(priv->supported_interfaces); 55862306a36Sopenharmony_ci 55962306a36Sopenharmony_ci return phy_sfp_probe(phydev, &mv3310_sfp_ops); 56062306a36Sopenharmony_ci} 56162306a36Sopenharmony_ci 56262306a36Sopenharmony_cistatic void mv3310_remove(struct phy_device *phydev) 56362306a36Sopenharmony_ci{ 56462306a36Sopenharmony_ci mv3310_hwmon_config(phydev, false); 56562306a36Sopenharmony_ci} 56662306a36Sopenharmony_ci 56762306a36Sopenharmony_cistatic int mv3310_suspend(struct phy_device *phydev) 56862306a36Sopenharmony_ci{ 56962306a36Sopenharmony_ci return mv3310_power_down(phydev); 57062306a36Sopenharmony_ci} 57162306a36Sopenharmony_ci 57262306a36Sopenharmony_cistatic int mv3310_resume(struct phy_device *phydev) 57362306a36Sopenharmony_ci{ 57462306a36Sopenharmony_ci int ret; 57562306a36Sopenharmony_ci 57662306a36Sopenharmony_ci ret = mv3310_power_up(phydev); 57762306a36Sopenharmony_ci if (ret) 57862306a36Sopenharmony_ci return ret; 57962306a36Sopenharmony_ci 58062306a36Sopenharmony_ci return mv3310_hwmon_config(phydev, true); 58162306a36Sopenharmony_ci} 58262306a36Sopenharmony_ci 58362306a36Sopenharmony_ci/* Some PHYs in the Alaska family such as the 88X3310 and the 88E2010 58462306a36Sopenharmony_ci * don't set bit 14 in PMA Extended Abilities (1.11), although they do 58562306a36Sopenharmony_ci * support 2.5GBASET and 5GBASET. For these models, we can still read their 58662306a36Sopenharmony_ci * 2.5G/5G extended abilities register (1.21). We detect these models based on 58762306a36Sopenharmony_ci * the PMA device identifier, with a mask matching models known to have this 58862306a36Sopenharmony_ci * issue 58962306a36Sopenharmony_ci */ 59062306a36Sopenharmony_cistatic bool mv3310_has_pma_ngbaset_quirk(struct phy_device *phydev) 59162306a36Sopenharmony_ci{ 59262306a36Sopenharmony_ci if (!(phydev->c45_ids.devices_in_package & MDIO_DEVS_PMAPMD)) 59362306a36Sopenharmony_ci return false; 59462306a36Sopenharmony_ci 59562306a36Sopenharmony_ci /* Only some revisions of the 88X3310 family PMA seem to be impacted */ 59662306a36Sopenharmony_ci return (phydev->c45_ids.device_ids[MDIO_MMD_PMAPMD] & 59762306a36Sopenharmony_ci MV_PHY_ALASKA_NBT_QUIRK_MASK) == MV_PHY_ALASKA_NBT_QUIRK_REV; 59862306a36Sopenharmony_ci} 59962306a36Sopenharmony_ci 60062306a36Sopenharmony_cistatic int mv2110_get_mactype(struct phy_device *phydev) 60162306a36Sopenharmony_ci{ 60262306a36Sopenharmony_ci int mactype; 60362306a36Sopenharmony_ci 60462306a36Sopenharmony_ci mactype = phy_read_mmd(phydev, MDIO_MMD_PMAPMD, MV_PMA_21X0_PORT_CTRL); 60562306a36Sopenharmony_ci if (mactype < 0) 60662306a36Sopenharmony_ci return mactype; 60762306a36Sopenharmony_ci 60862306a36Sopenharmony_ci return mactype & MV_PMA_21X0_PORT_CTRL_MACTYPE_MASK; 60962306a36Sopenharmony_ci} 61062306a36Sopenharmony_ci 61162306a36Sopenharmony_cistatic int mv2110_set_mactype(struct phy_device *phydev, int mactype) 61262306a36Sopenharmony_ci{ 61362306a36Sopenharmony_ci int err, val; 61462306a36Sopenharmony_ci 61562306a36Sopenharmony_ci mactype &= MV_PMA_21X0_PORT_CTRL_MACTYPE_MASK; 61662306a36Sopenharmony_ci err = phy_modify_mmd(phydev, MDIO_MMD_PMAPMD, MV_PMA_21X0_PORT_CTRL, 61762306a36Sopenharmony_ci MV_PMA_21X0_PORT_CTRL_SWRST | 61862306a36Sopenharmony_ci MV_PMA_21X0_PORT_CTRL_MACTYPE_MASK, 61962306a36Sopenharmony_ci MV_PMA_21X0_PORT_CTRL_SWRST | mactype); 62062306a36Sopenharmony_ci if (err) 62162306a36Sopenharmony_ci return err; 62262306a36Sopenharmony_ci 62362306a36Sopenharmony_ci err = phy_set_bits_mmd(phydev, MDIO_MMD_AN, MV_AN_21X0_SERDES_CTRL2, 62462306a36Sopenharmony_ci MV_AN_21X0_SERDES_CTRL2_AUTO_INIT_DIS | 62562306a36Sopenharmony_ci MV_AN_21X0_SERDES_CTRL2_RUN_INIT); 62662306a36Sopenharmony_ci if (err) 62762306a36Sopenharmony_ci return err; 62862306a36Sopenharmony_ci 62962306a36Sopenharmony_ci err = phy_read_mmd_poll_timeout(phydev, MDIO_MMD_AN, 63062306a36Sopenharmony_ci MV_AN_21X0_SERDES_CTRL2, val, 63162306a36Sopenharmony_ci !(val & 63262306a36Sopenharmony_ci MV_AN_21X0_SERDES_CTRL2_RUN_INIT), 63362306a36Sopenharmony_ci 5000, 100000, true); 63462306a36Sopenharmony_ci if (err) 63562306a36Sopenharmony_ci return err; 63662306a36Sopenharmony_ci 63762306a36Sopenharmony_ci return phy_clear_bits_mmd(phydev, MDIO_MMD_AN, MV_AN_21X0_SERDES_CTRL2, 63862306a36Sopenharmony_ci MV_AN_21X0_SERDES_CTRL2_AUTO_INIT_DIS); 63962306a36Sopenharmony_ci} 64062306a36Sopenharmony_ci 64162306a36Sopenharmony_cistatic int mv2110_select_mactype(unsigned long *interfaces) 64262306a36Sopenharmony_ci{ 64362306a36Sopenharmony_ci if (test_bit(PHY_INTERFACE_MODE_USXGMII, interfaces)) 64462306a36Sopenharmony_ci return MV_PMA_21X0_PORT_CTRL_MACTYPE_USXGMII; 64562306a36Sopenharmony_ci else if (test_bit(PHY_INTERFACE_MODE_SGMII, interfaces) && 64662306a36Sopenharmony_ci !test_bit(PHY_INTERFACE_MODE_10GBASER, interfaces)) 64762306a36Sopenharmony_ci return MV_PMA_21X0_PORT_CTRL_MACTYPE_5GBASER; 64862306a36Sopenharmony_ci else if (test_bit(PHY_INTERFACE_MODE_10GBASER, interfaces)) 64962306a36Sopenharmony_ci return MV_PMA_21X0_PORT_CTRL_MACTYPE_10GBASER_RATE_MATCH; 65062306a36Sopenharmony_ci else 65162306a36Sopenharmony_ci return -1; 65262306a36Sopenharmony_ci} 65362306a36Sopenharmony_ci 65462306a36Sopenharmony_cistatic int mv3310_get_mactype(struct phy_device *phydev) 65562306a36Sopenharmony_ci{ 65662306a36Sopenharmony_ci int mactype; 65762306a36Sopenharmony_ci 65862306a36Sopenharmony_ci mactype = phy_read_mmd(phydev, MDIO_MMD_VEND2, MV_V2_PORT_CTRL); 65962306a36Sopenharmony_ci if (mactype < 0) 66062306a36Sopenharmony_ci return mactype; 66162306a36Sopenharmony_ci 66262306a36Sopenharmony_ci return mactype & MV_V2_33X0_PORT_CTRL_MACTYPE_MASK; 66362306a36Sopenharmony_ci} 66462306a36Sopenharmony_ci 66562306a36Sopenharmony_cistatic int mv3310_set_mactype(struct phy_device *phydev, int mactype) 66662306a36Sopenharmony_ci{ 66762306a36Sopenharmony_ci int ret; 66862306a36Sopenharmony_ci 66962306a36Sopenharmony_ci mactype &= MV_V2_33X0_PORT_CTRL_MACTYPE_MASK; 67062306a36Sopenharmony_ci ret = phy_modify_mmd_changed(phydev, MDIO_MMD_VEND2, MV_V2_PORT_CTRL, 67162306a36Sopenharmony_ci MV_V2_33X0_PORT_CTRL_MACTYPE_MASK, 67262306a36Sopenharmony_ci mactype); 67362306a36Sopenharmony_ci if (ret <= 0) 67462306a36Sopenharmony_ci return ret; 67562306a36Sopenharmony_ci 67662306a36Sopenharmony_ci return phy_set_bits_mmd(phydev, MDIO_MMD_VEND2, MV_V2_PORT_CTRL, 67762306a36Sopenharmony_ci MV_V2_33X0_PORT_CTRL_SWRST); 67862306a36Sopenharmony_ci} 67962306a36Sopenharmony_ci 68062306a36Sopenharmony_cistatic int mv3310_select_mactype(unsigned long *interfaces) 68162306a36Sopenharmony_ci{ 68262306a36Sopenharmony_ci if (test_bit(PHY_INTERFACE_MODE_USXGMII, interfaces)) 68362306a36Sopenharmony_ci return MV_V2_33X0_PORT_CTRL_MACTYPE_USXGMII; 68462306a36Sopenharmony_ci else if (test_bit(PHY_INTERFACE_MODE_SGMII, interfaces) && 68562306a36Sopenharmony_ci test_bit(PHY_INTERFACE_MODE_10GBASER, interfaces)) 68662306a36Sopenharmony_ci return MV_V2_33X0_PORT_CTRL_MACTYPE_10GBASER; 68762306a36Sopenharmony_ci else if (test_bit(PHY_INTERFACE_MODE_SGMII, interfaces) && 68862306a36Sopenharmony_ci test_bit(PHY_INTERFACE_MODE_RXAUI, interfaces)) 68962306a36Sopenharmony_ci return MV_V2_33X0_PORT_CTRL_MACTYPE_RXAUI; 69062306a36Sopenharmony_ci else if (test_bit(PHY_INTERFACE_MODE_SGMII, interfaces) && 69162306a36Sopenharmony_ci test_bit(PHY_INTERFACE_MODE_XAUI, interfaces)) 69262306a36Sopenharmony_ci return MV_V2_3310_PORT_CTRL_MACTYPE_XAUI; 69362306a36Sopenharmony_ci else if (test_bit(PHY_INTERFACE_MODE_10GBASER, interfaces)) 69462306a36Sopenharmony_ci return MV_V2_33X0_PORT_CTRL_MACTYPE_10GBASER_RATE_MATCH; 69562306a36Sopenharmony_ci else if (test_bit(PHY_INTERFACE_MODE_RXAUI, interfaces)) 69662306a36Sopenharmony_ci return MV_V2_33X0_PORT_CTRL_MACTYPE_RXAUI_RATE_MATCH; 69762306a36Sopenharmony_ci else if (test_bit(PHY_INTERFACE_MODE_XAUI, interfaces)) 69862306a36Sopenharmony_ci return MV_V2_3310_PORT_CTRL_MACTYPE_XAUI_RATE_MATCH; 69962306a36Sopenharmony_ci else if (test_bit(PHY_INTERFACE_MODE_SGMII, interfaces)) 70062306a36Sopenharmony_ci return MV_V2_33X0_PORT_CTRL_MACTYPE_10GBASER; 70162306a36Sopenharmony_ci else 70262306a36Sopenharmony_ci return -1; 70362306a36Sopenharmony_ci} 70462306a36Sopenharmony_ci 70562306a36Sopenharmony_cistatic int mv2110_init_interface(struct phy_device *phydev, int mactype) 70662306a36Sopenharmony_ci{ 70762306a36Sopenharmony_ci struct mv3310_priv *priv = dev_get_drvdata(&phydev->mdio.dev); 70862306a36Sopenharmony_ci 70962306a36Sopenharmony_ci priv->rate_match = false; 71062306a36Sopenharmony_ci 71162306a36Sopenharmony_ci if (mactype == MV_PMA_21X0_PORT_CTRL_MACTYPE_10GBASER_RATE_MATCH) 71262306a36Sopenharmony_ci priv->rate_match = true; 71362306a36Sopenharmony_ci 71462306a36Sopenharmony_ci if (mactype == MV_PMA_21X0_PORT_CTRL_MACTYPE_USXGMII) 71562306a36Sopenharmony_ci priv->const_interface = PHY_INTERFACE_MODE_USXGMII; 71662306a36Sopenharmony_ci else if (mactype == MV_PMA_21X0_PORT_CTRL_MACTYPE_10GBASER_RATE_MATCH) 71762306a36Sopenharmony_ci priv->const_interface = PHY_INTERFACE_MODE_10GBASER; 71862306a36Sopenharmony_ci else if (mactype == MV_PMA_21X0_PORT_CTRL_MACTYPE_5GBASER || 71962306a36Sopenharmony_ci mactype == MV_PMA_21X0_PORT_CTRL_MACTYPE_5GBASER_NO_SGMII_AN) 72062306a36Sopenharmony_ci priv->const_interface = PHY_INTERFACE_MODE_NA; 72162306a36Sopenharmony_ci else 72262306a36Sopenharmony_ci return -EINVAL; 72362306a36Sopenharmony_ci 72462306a36Sopenharmony_ci return 0; 72562306a36Sopenharmony_ci} 72662306a36Sopenharmony_ci 72762306a36Sopenharmony_cistatic int mv3310_init_interface(struct phy_device *phydev, int mactype) 72862306a36Sopenharmony_ci{ 72962306a36Sopenharmony_ci struct mv3310_priv *priv = dev_get_drvdata(&phydev->mdio.dev); 73062306a36Sopenharmony_ci 73162306a36Sopenharmony_ci priv->rate_match = false; 73262306a36Sopenharmony_ci 73362306a36Sopenharmony_ci if (mactype == MV_V2_33X0_PORT_CTRL_MACTYPE_10GBASER_RATE_MATCH || 73462306a36Sopenharmony_ci mactype == MV_V2_33X0_PORT_CTRL_MACTYPE_RXAUI_RATE_MATCH || 73562306a36Sopenharmony_ci mactype == MV_V2_3310_PORT_CTRL_MACTYPE_XAUI_RATE_MATCH) 73662306a36Sopenharmony_ci priv->rate_match = true; 73762306a36Sopenharmony_ci 73862306a36Sopenharmony_ci if (mactype == MV_V2_33X0_PORT_CTRL_MACTYPE_USXGMII) 73962306a36Sopenharmony_ci priv->const_interface = PHY_INTERFACE_MODE_USXGMII; 74062306a36Sopenharmony_ci else if (mactype == MV_V2_33X0_PORT_CTRL_MACTYPE_10GBASER_RATE_MATCH || 74162306a36Sopenharmony_ci mactype == MV_V2_33X0_PORT_CTRL_MACTYPE_10GBASER_NO_SGMII_AN || 74262306a36Sopenharmony_ci mactype == MV_V2_33X0_PORT_CTRL_MACTYPE_10GBASER) 74362306a36Sopenharmony_ci priv->const_interface = PHY_INTERFACE_MODE_10GBASER; 74462306a36Sopenharmony_ci else if (mactype == MV_V2_33X0_PORT_CTRL_MACTYPE_RXAUI_RATE_MATCH || 74562306a36Sopenharmony_ci mactype == MV_V2_33X0_PORT_CTRL_MACTYPE_RXAUI) 74662306a36Sopenharmony_ci priv->const_interface = PHY_INTERFACE_MODE_RXAUI; 74762306a36Sopenharmony_ci else if (mactype == MV_V2_3310_PORT_CTRL_MACTYPE_XAUI_RATE_MATCH || 74862306a36Sopenharmony_ci mactype == MV_V2_3310_PORT_CTRL_MACTYPE_XAUI) 74962306a36Sopenharmony_ci priv->const_interface = PHY_INTERFACE_MODE_XAUI; 75062306a36Sopenharmony_ci else 75162306a36Sopenharmony_ci return -EINVAL; 75262306a36Sopenharmony_ci 75362306a36Sopenharmony_ci return 0; 75462306a36Sopenharmony_ci} 75562306a36Sopenharmony_ci 75662306a36Sopenharmony_cistatic int mv3340_init_interface(struct phy_device *phydev, int mactype) 75762306a36Sopenharmony_ci{ 75862306a36Sopenharmony_ci struct mv3310_priv *priv = dev_get_drvdata(&phydev->mdio.dev); 75962306a36Sopenharmony_ci int err = 0; 76062306a36Sopenharmony_ci 76162306a36Sopenharmony_ci priv->rate_match = false; 76262306a36Sopenharmony_ci 76362306a36Sopenharmony_ci if (mactype == MV_V2_3340_PORT_CTRL_MACTYPE_RXAUI_NO_SGMII_AN) 76462306a36Sopenharmony_ci priv->const_interface = PHY_INTERFACE_MODE_RXAUI; 76562306a36Sopenharmony_ci else 76662306a36Sopenharmony_ci err = mv3310_init_interface(phydev, mactype); 76762306a36Sopenharmony_ci 76862306a36Sopenharmony_ci return err; 76962306a36Sopenharmony_ci} 77062306a36Sopenharmony_ci 77162306a36Sopenharmony_cistatic int mv3310_config_init(struct phy_device *phydev) 77262306a36Sopenharmony_ci{ 77362306a36Sopenharmony_ci struct mv3310_priv *priv = dev_get_drvdata(&phydev->mdio.dev); 77462306a36Sopenharmony_ci const struct mv3310_chip *chip = to_mv3310_chip(phydev); 77562306a36Sopenharmony_ci int err, mactype; 77662306a36Sopenharmony_ci 77762306a36Sopenharmony_ci /* Check that the PHY interface type is compatible */ 77862306a36Sopenharmony_ci if (!test_bit(phydev->interface, priv->supported_interfaces)) 77962306a36Sopenharmony_ci return -ENODEV; 78062306a36Sopenharmony_ci 78162306a36Sopenharmony_ci phydev->mdix_ctrl = ETH_TP_MDI_AUTO; 78262306a36Sopenharmony_ci 78362306a36Sopenharmony_ci /* Power up so reset works */ 78462306a36Sopenharmony_ci err = mv3310_power_up(phydev); 78562306a36Sopenharmony_ci if (err) 78662306a36Sopenharmony_ci return err; 78762306a36Sopenharmony_ci 78862306a36Sopenharmony_ci /* If host provided host supported interface modes, try to select the 78962306a36Sopenharmony_ci * best one 79062306a36Sopenharmony_ci */ 79162306a36Sopenharmony_ci if (!phy_interface_empty(phydev->host_interfaces)) { 79262306a36Sopenharmony_ci mactype = chip->select_mactype(phydev->host_interfaces); 79362306a36Sopenharmony_ci if (mactype >= 0) { 79462306a36Sopenharmony_ci phydev_info(phydev, "Changing MACTYPE to %i\n", 79562306a36Sopenharmony_ci mactype); 79662306a36Sopenharmony_ci err = chip->set_mactype(phydev, mactype); 79762306a36Sopenharmony_ci if (err) 79862306a36Sopenharmony_ci return err; 79962306a36Sopenharmony_ci } 80062306a36Sopenharmony_ci } 80162306a36Sopenharmony_ci 80262306a36Sopenharmony_ci mactype = chip->get_mactype(phydev); 80362306a36Sopenharmony_ci if (mactype < 0) 80462306a36Sopenharmony_ci return mactype; 80562306a36Sopenharmony_ci 80662306a36Sopenharmony_ci err = chip->init_interface(phydev, mactype); 80762306a36Sopenharmony_ci if (err) { 80862306a36Sopenharmony_ci phydev_err(phydev, "MACTYPE configuration invalid\n"); 80962306a36Sopenharmony_ci return err; 81062306a36Sopenharmony_ci } 81162306a36Sopenharmony_ci 81262306a36Sopenharmony_ci /* Enable EDPD mode - saving 600mW */ 81362306a36Sopenharmony_ci err = mv3310_set_edpd(phydev, ETHTOOL_PHY_EDPD_DFLT_TX_MSECS); 81462306a36Sopenharmony_ci if (err) 81562306a36Sopenharmony_ci return err; 81662306a36Sopenharmony_ci 81762306a36Sopenharmony_ci /* Allow downshift */ 81862306a36Sopenharmony_ci err = mv3310_set_downshift(phydev, DOWNSHIFT_DEV_DEFAULT_COUNT); 81962306a36Sopenharmony_ci if (err && err != -EOPNOTSUPP) 82062306a36Sopenharmony_ci return err; 82162306a36Sopenharmony_ci 82262306a36Sopenharmony_ci return 0; 82362306a36Sopenharmony_ci} 82462306a36Sopenharmony_ci 82562306a36Sopenharmony_cistatic int mv3310_get_features(struct phy_device *phydev) 82662306a36Sopenharmony_ci{ 82762306a36Sopenharmony_ci int ret, val; 82862306a36Sopenharmony_ci 82962306a36Sopenharmony_ci ret = genphy_c45_pma_read_abilities(phydev); 83062306a36Sopenharmony_ci if (ret) 83162306a36Sopenharmony_ci return ret; 83262306a36Sopenharmony_ci 83362306a36Sopenharmony_ci if (mv3310_has_pma_ngbaset_quirk(phydev)) { 83462306a36Sopenharmony_ci val = phy_read_mmd(phydev, MDIO_MMD_PMAPMD, 83562306a36Sopenharmony_ci MDIO_PMA_NG_EXTABLE); 83662306a36Sopenharmony_ci if (val < 0) 83762306a36Sopenharmony_ci return val; 83862306a36Sopenharmony_ci 83962306a36Sopenharmony_ci linkmode_mod_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT, 84062306a36Sopenharmony_ci phydev->supported, 84162306a36Sopenharmony_ci val & MDIO_PMA_NG_EXTABLE_2_5GBT); 84262306a36Sopenharmony_ci 84362306a36Sopenharmony_ci linkmode_mod_bit(ETHTOOL_LINK_MODE_5000baseT_Full_BIT, 84462306a36Sopenharmony_ci phydev->supported, 84562306a36Sopenharmony_ci val & MDIO_PMA_NG_EXTABLE_5GBT); 84662306a36Sopenharmony_ci } 84762306a36Sopenharmony_ci 84862306a36Sopenharmony_ci return 0; 84962306a36Sopenharmony_ci} 85062306a36Sopenharmony_ci 85162306a36Sopenharmony_cistatic int mv3310_config_mdix(struct phy_device *phydev) 85262306a36Sopenharmony_ci{ 85362306a36Sopenharmony_ci u16 val; 85462306a36Sopenharmony_ci int err; 85562306a36Sopenharmony_ci 85662306a36Sopenharmony_ci switch (phydev->mdix_ctrl) { 85762306a36Sopenharmony_ci case ETH_TP_MDI_AUTO: 85862306a36Sopenharmony_ci val = MV_PCS_CSCR1_MDIX_AUTO; 85962306a36Sopenharmony_ci break; 86062306a36Sopenharmony_ci case ETH_TP_MDI_X: 86162306a36Sopenharmony_ci val = MV_PCS_CSCR1_MDIX_MDIX; 86262306a36Sopenharmony_ci break; 86362306a36Sopenharmony_ci case ETH_TP_MDI: 86462306a36Sopenharmony_ci val = MV_PCS_CSCR1_MDIX_MDI; 86562306a36Sopenharmony_ci break; 86662306a36Sopenharmony_ci default: 86762306a36Sopenharmony_ci return -EINVAL; 86862306a36Sopenharmony_ci } 86962306a36Sopenharmony_ci 87062306a36Sopenharmony_ci err = phy_modify_mmd_changed(phydev, MDIO_MMD_PCS, MV_PCS_CSCR1, 87162306a36Sopenharmony_ci MV_PCS_CSCR1_MDIX_MASK, val); 87262306a36Sopenharmony_ci if (err > 0) 87362306a36Sopenharmony_ci err = mv3310_reset(phydev, MV_PCS_BASE_T); 87462306a36Sopenharmony_ci 87562306a36Sopenharmony_ci return err; 87662306a36Sopenharmony_ci} 87762306a36Sopenharmony_ci 87862306a36Sopenharmony_cistatic int mv3310_config_aneg(struct phy_device *phydev) 87962306a36Sopenharmony_ci{ 88062306a36Sopenharmony_ci bool changed = false; 88162306a36Sopenharmony_ci u16 reg; 88262306a36Sopenharmony_ci int ret; 88362306a36Sopenharmony_ci 88462306a36Sopenharmony_ci ret = mv3310_config_mdix(phydev); 88562306a36Sopenharmony_ci if (ret < 0) 88662306a36Sopenharmony_ci return ret; 88762306a36Sopenharmony_ci 88862306a36Sopenharmony_ci if (phydev->autoneg == AUTONEG_DISABLE) 88962306a36Sopenharmony_ci return genphy_c45_pma_setup_forced(phydev); 89062306a36Sopenharmony_ci 89162306a36Sopenharmony_ci ret = genphy_c45_an_config_aneg(phydev); 89262306a36Sopenharmony_ci if (ret < 0) 89362306a36Sopenharmony_ci return ret; 89462306a36Sopenharmony_ci if (ret > 0) 89562306a36Sopenharmony_ci changed = true; 89662306a36Sopenharmony_ci 89762306a36Sopenharmony_ci /* Clause 45 has no standardized support for 1000BaseT, therefore 89862306a36Sopenharmony_ci * use vendor registers for this mode. 89962306a36Sopenharmony_ci */ 90062306a36Sopenharmony_ci reg = linkmode_adv_to_mii_ctrl1000_t(phydev->advertising); 90162306a36Sopenharmony_ci ret = phy_modify_mmd_changed(phydev, MDIO_MMD_AN, MV_AN_CTRL1000, 90262306a36Sopenharmony_ci ADVERTISE_1000FULL | ADVERTISE_1000HALF, reg); 90362306a36Sopenharmony_ci if (ret < 0) 90462306a36Sopenharmony_ci return ret; 90562306a36Sopenharmony_ci if (ret > 0) 90662306a36Sopenharmony_ci changed = true; 90762306a36Sopenharmony_ci 90862306a36Sopenharmony_ci return genphy_c45_check_and_restart_aneg(phydev, changed); 90962306a36Sopenharmony_ci} 91062306a36Sopenharmony_ci 91162306a36Sopenharmony_cistatic int mv3310_aneg_done(struct phy_device *phydev) 91262306a36Sopenharmony_ci{ 91362306a36Sopenharmony_ci int val; 91462306a36Sopenharmony_ci 91562306a36Sopenharmony_ci val = phy_read_mmd(phydev, MDIO_MMD_PCS, MV_PCS_BASE_R + MDIO_STAT1); 91662306a36Sopenharmony_ci if (val < 0) 91762306a36Sopenharmony_ci return val; 91862306a36Sopenharmony_ci 91962306a36Sopenharmony_ci if (val & MDIO_STAT1_LSTATUS) 92062306a36Sopenharmony_ci return 1; 92162306a36Sopenharmony_ci 92262306a36Sopenharmony_ci return genphy_c45_aneg_done(phydev); 92362306a36Sopenharmony_ci} 92462306a36Sopenharmony_ci 92562306a36Sopenharmony_cistatic void mv3310_update_interface(struct phy_device *phydev) 92662306a36Sopenharmony_ci{ 92762306a36Sopenharmony_ci struct mv3310_priv *priv = dev_get_drvdata(&phydev->mdio.dev); 92862306a36Sopenharmony_ci 92962306a36Sopenharmony_ci if (!phydev->link) 93062306a36Sopenharmony_ci return; 93162306a36Sopenharmony_ci 93262306a36Sopenharmony_ci /* In all of the "* with Rate Matching" modes the PHY interface is fixed 93362306a36Sopenharmony_ci * at 10Gb. The PHY adapts the rate to actual wire speed with help of 93462306a36Sopenharmony_ci * internal 16KB buffer. 93562306a36Sopenharmony_ci * 93662306a36Sopenharmony_ci * In USXGMII mode the PHY interface mode is also fixed. 93762306a36Sopenharmony_ci */ 93862306a36Sopenharmony_ci if (priv->rate_match || 93962306a36Sopenharmony_ci priv->const_interface == PHY_INTERFACE_MODE_USXGMII) { 94062306a36Sopenharmony_ci phydev->interface = priv->const_interface; 94162306a36Sopenharmony_ci return; 94262306a36Sopenharmony_ci } 94362306a36Sopenharmony_ci 94462306a36Sopenharmony_ci /* The PHY automatically switches its serdes interface (and active PHYXS 94562306a36Sopenharmony_ci * instance) between Cisco SGMII, 2500BaseX, 5GBase-R and 10GBase-R / 94662306a36Sopenharmony_ci * xaui / rxaui modes according to the speed. 94762306a36Sopenharmony_ci * Florian suggests setting phydev->interface to communicate this to the 94862306a36Sopenharmony_ci * MAC. Only do this if we are already in one of the above modes. 94962306a36Sopenharmony_ci */ 95062306a36Sopenharmony_ci switch (phydev->speed) { 95162306a36Sopenharmony_ci case SPEED_10000: 95262306a36Sopenharmony_ci phydev->interface = priv->const_interface; 95362306a36Sopenharmony_ci break; 95462306a36Sopenharmony_ci case SPEED_5000: 95562306a36Sopenharmony_ci phydev->interface = PHY_INTERFACE_MODE_5GBASER; 95662306a36Sopenharmony_ci break; 95762306a36Sopenharmony_ci case SPEED_2500: 95862306a36Sopenharmony_ci phydev->interface = PHY_INTERFACE_MODE_2500BASEX; 95962306a36Sopenharmony_ci break; 96062306a36Sopenharmony_ci case SPEED_1000: 96162306a36Sopenharmony_ci case SPEED_100: 96262306a36Sopenharmony_ci case SPEED_10: 96362306a36Sopenharmony_ci phydev->interface = PHY_INTERFACE_MODE_SGMII; 96462306a36Sopenharmony_ci break; 96562306a36Sopenharmony_ci default: 96662306a36Sopenharmony_ci break; 96762306a36Sopenharmony_ci } 96862306a36Sopenharmony_ci} 96962306a36Sopenharmony_ci 97062306a36Sopenharmony_ci/* 10GBASE-ER,LR,LRM,SR do not support autonegotiation. */ 97162306a36Sopenharmony_cistatic int mv3310_read_status_10gbaser(struct phy_device *phydev) 97262306a36Sopenharmony_ci{ 97362306a36Sopenharmony_ci phydev->link = 1; 97462306a36Sopenharmony_ci phydev->speed = SPEED_10000; 97562306a36Sopenharmony_ci phydev->duplex = DUPLEX_FULL; 97662306a36Sopenharmony_ci phydev->port = PORT_FIBRE; 97762306a36Sopenharmony_ci 97862306a36Sopenharmony_ci return 0; 97962306a36Sopenharmony_ci} 98062306a36Sopenharmony_ci 98162306a36Sopenharmony_cistatic int mv3310_read_status_copper(struct phy_device *phydev) 98262306a36Sopenharmony_ci{ 98362306a36Sopenharmony_ci int cssr1, speed, val; 98462306a36Sopenharmony_ci 98562306a36Sopenharmony_ci val = genphy_c45_read_link(phydev); 98662306a36Sopenharmony_ci if (val < 0) 98762306a36Sopenharmony_ci return val; 98862306a36Sopenharmony_ci 98962306a36Sopenharmony_ci val = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_STAT1); 99062306a36Sopenharmony_ci if (val < 0) 99162306a36Sopenharmony_ci return val; 99262306a36Sopenharmony_ci 99362306a36Sopenharmony_ci cssr1 = phy_read_mmd(phydev, MDIO_MMD_PCS, MV_PCS_CSSR1); 99462306a36Sopenharmony_ci if (cssr1 < 0) 99562306a36Sopenharmony_ci return cssr1; 99662306a36Sopenharmony_ci 99762306a36Sopenharmony_ci /* If the link settings are not resolved, mark the link down */ 99862306a36Sopenharmony_ci if (!(cssr1 & MV_PCS_CSSR1_RESOLVED)) { 99962306a36Sopenharmony_ci phydev->link = 0; 100062306a36Sopenharmony_ci return 0; 100162306a36Sopenharmony_ci } 100262306a36Sopenharmony_ci 100362306a36Sopenharmony_ci /* Read the copper link settings */ 100462306a36Sopenharmony_ci speed = cssr1 & MV_PCS_CSSR1_SPD1_MASK; 100562306a36Sopenharmony_ci if (speed == MV_PCS_CSSR1_SPD1_SPD2) 100662306a36Sopenharmony_ci speed |= cssr1 & MV_PCS_CSSR1_SPD2_MASK; 100762306a36Sopenharmony_ci 100862306a36Sopenharmony_ci switch (speed) { 100962306a36Sopenharmony_ci case MV_PCS_CSSR1_SPD1_SPD2 | MV_PCS_CSSR1_SPD2_10000: 101062306a36Sopenharmony_ci phydev->speed = SPEED_10000; 101162306a36Sopenharmony_ci break; 101262306a36Sopenharmony_ci 101362306a36Sopenharmony_ci case MV_PCS_CSSR1_SPD1_SPD2 | MV_PCS_CSSR1_SPD2_5000: 101462306a36Sopenharmony_ci phydev->speed = SPEED_5000; 101562306a36Sopenharmony_ci break; 101662306a36Sopenharmony_ci 101762306a36Sopenharmony_ci case MV_PCS_CSSR1_SPD1_SPD2 | MV_PCS_CSSR1_SPD2_2500: 101862306a36Sopenharmony_ci phydev->speed = SPEED_2500; 101962306a36Sopenharmony_ci break; 102062306a36Sopenharmony_ci 102162306a36Sopenharmony_ci case MV_PCS_CSSR1_SPD1_1000: 102262306a36Sopenharmony_ci phydev->speed = SPEED_1000; 102362306a36Sopenharmony_ci break; 102462306a36Sopenharmony_ci 102562306a36Sopenharmony_ci case MV_PCS_CSSR1_SPD1_100: 102662306a36Sopenharmony_ci phydev->speed = SPEED_100; 102762306a36Sopenharmony_ci break; 102862306a36Sopenharmony_ci 102962306a36Sopenharmony_ci case MV_PCS_CSSR1_SPD1_10: 103062306a36Sopenharmony_ci phydev->speed = SPEED_10; 103162306a36Sopenharmony_ci break; 103262306a36Sopenharmony_ci } 103362306a36Sopenharmony_ci 103462306a36Sopenharmony_ci phydev->duplex = cssr1 & MV_PCS_CSSR1_DUPLEX_FULL ? 103562306a36Sopenharmony_ci DUPLEX_FULL : DUPLEX_HALF; 103662306a36Sopenharmony_ci phydev->port = PORT_TP; 103762306a36Sopenharmony_ci phydev->mdix = cssr1 & MV_PCS_CSSR1_MDIX ? 103862306a36Sopenharmony_ci ETH_TP_MDI_X : ETH_TP_MDI; 103962306a36Sopenharmony_ci 104062306a36Sopenharmony_ci if (val & MDIO_AN_STAT1_COMPLETE) { 104162306a36Sopenharmony_ci val = genphy_c45_read_lpa(phydev); 104262306a36Sopenharmony_ci if (val < 0) 104362306a36Sopenharmony_ci return val; 104462306a36Sopenharmony_ci 104562306a36Sopenharmony_ci /* Read the link partner's 1G advertisement */ 104662306a36Sopenharmony_ci val = phy_read_mmd(phydev, MDIO_MMD_AN, MV_AN_STAT1000); 104762306a36Sopenharmony_ci if (val < 0) 104862306a36Sopenharmony_ci return val; 104962306a36Sopenharmony_ci 105062306a36Sopenharmony_ci mii_stat1000_mod_linkmode_lpa_t(phydev->lp_advertising, val); 105162306a36Sopenharmony_ci 105262306a36Sopenharmony_ci /* Update the pause status */ 105362306a36Sopenharmony_ci phy_resolve_aneg_pause(phydev); 105462306a36Sopenharmony_ci } 105562306a36Sopenharmony_ci 105662306a36Sopenharmony_ci return 0; 105762306a36Sopenharmony_ci} 105862306a36Sopenharmony_ci 105962306a36Sopenharmony_cistatic int mv3310_read_status(struct phy_device *phydev) 106062306a36Sopenharmony_ci{ 106162306a36Sopenharmony_ci int err, val; 106262306a36Sopenharmony_ci 106362306a36Sopenharmony_ci phydev->speed = SPEED_UNKNOWN; 106462306a36Sopenharmony_ci phydev->duplex = DUPLEX_UNKNOWN; 106562306a36Sopenharmony_ci linkmode_zero(phydev->lp_advertising); 106662306a36Sopenharmony_ci phydev->link = 0; 106762306a36Sopenharmony_ci phydev->pause = 0; 106862306a36Sopenharmony_ci phydev->asym_pause = 0; 106962306a36Sopenharmony_ci phydev->mdix = ETH_TP_MDI_INVALID; 107062306a36Sopenharmony_ci 107162306a36Sopenharmony_ci val = phy_read_mmd(phydev, MDIO_MMD_PCS, MV_PCS_BASE_R + MDIO_STAT1); 107262306a36Sopenharmony_ci if (val < 0) 107362306a36Sopenharmony_ci return val; 107462306a36Sopenharmony_ci 107562306a36Sopenharmony_ci if (val & MDIO_STAT1_LSTATUS) 107662306a36Sopenharmony_ci err = mv3310_read_status_10gbaser(phydev); 107762306a36Sopenharmony_ci else 107862306a36Sopenharmony_ci err = mv3310_read_status_copper(phydev); 107962306a36Sopenharmony_ci if (err < 0) 108062306a36Sopenharmony_ci return err; 108162306a36Sopenharmony_ci 108262306a36Sopenharmony_ci if (phydev->link) 108362306a36Sopenharmony_ci mv3310_update_interface(phydev); 108462306a36Sopenharmony_ci 108562306a36Sopenharmony_ci return 0; 108662306a36Sopenharmony_ci} 108762306a36Sopenharmony_ci 108862306a36Sopenharmony_cistatic int mv3310_get_tunable(struct phy_device *phydev, 108962306a36Sopenharmony_ci struct ethtool_tunable *tuna, void *data) 109062306a36Sopenharmony_ci{ 109162306a36Sopenharmony_ci switch (tuna->id) { 109262306a36Sopenharmony_ci case ETHTOOL_PHY_DOWNSHIFT: 109362306a36Sopenharmony_ci return mv3310_get_downshift(phydev, data); 109462306a36Sopenharmony_ci case ETHTOOL_PHY_EDPD: 109562306a36Sopenharmony_ci return mv3310_get_edpd(phydev, data); 109662306a36Sopenharmony_ci default: 109762306a36Sopenharmony_ci return -EOPNOTSUPP; 109862306a36Sopenharmony_ci } 109962306a36Sopenharmony_ci} 110062306a36Sopenharmony_ci 110162306a36Sopenharmony_cistatic int mv3310_set_tunable(struct phy_device *phydev, 110262306a36Sopenharmony_ci struct ethtool_tunable *tuna, const void *data) 110362306a36Sopenharmony_ci{ 110462306a36Sopenharmony_ci switch (tuna->id) { 110562306a36Sopenharmony_ci case ETHTOOL_PHY_DOWNSHIFT: 110662306a36Sopenharmony_ci return mv3310_set_downshift(phydev, *(u8 *)data); 110762306a36Sopenharmony_ci case ETHTOOL_PHY_EDPD: 110862306a36Sopenharmony_ci return mv3310_set_edpd(phydev, *(u16 *)data); 110962306a36Sopenharmony_ci default: 111062306a36Sopenharmony_ci return -EOPNOTSUPP; 111162306a36Sopenharmony_ci } 111262306a36Sopenharmony_ci} 111362306a36Sopenharmony_ci 111462306a36Sopenharmony_cistatic bool mv3310_has_downshift(struct phy_device *phydev) 111562306a36Sopenharmony_ci{ 111662306a36Sopenharmony_ci struct mv3310_priv *priv = dev_get_drvdata(&phydev->mdio.dev); 111762306a36Sopenharmony_ci 111862306a36Sopenharmony_ci /* Fails to downshift with firmware older than v0.3.5.0 */ 111962306a36Sopenharmony_ci return priv->firmware_ver >= MV_VERSION(0,3,5,0); 112062306a36Sopenharmony_ci} 112162306a36Sopenharmony_ci 112262306a36Sopenharmony_cistatic void mv3310_init_supported_interfaces(unsigned long *mask) 112362306a36Sopenharmony_ci{ 112462306a36Sopenharmony_ci __set_bit(PHY_INTERFACE_MODE_SGMII, mask); 112562306a36Sopenharmony_ci __set_bit(PHY_INTERFACE_MODE_2500BASEX, mask); 112662306a36Sopenharmony_ci __set_bit(PHY_INTERFACE_MODE_5GBASER, mask); 112762306a36Sopenharmony_ci __set_bit(PHY_INTERFACE_MODE_XAUI, mask); 112862306a36Sopenharmony_ci __set_bit(PHY_INTERFACE_MODE_RXAUI, mask); 112962306a36Sopenharmony_ci __set_bit(PHY_INTERFACE_MODE_10GBASER, mask); 113062306a36Sopenharmony_ci __set_bit(PHY_INTERFACE_MODE_USXGMII, mask); 113162306a36Sopenharmony_ci} 113262306a36Sopenharmony_ci 113362306a36Sopenharmony_cistatic void mv3340_init_supported_interfaces(unsigned long *mask) 113462306a36Sopenharmony_ci{ 113562306a36Sopenharmony_ci __set_bit(PHY_INTERFACE_MODE_SGMII, mask); 113662306a36Sopenharmony_ci __set_bit(PHY_INTERFACE_MODE_2500BASEX, mask); 113762306a36Sopenharmony_ci __set_bit(PHY_INTERFACE_MODE_5GBASER, mask); 113862306a36Sopenharmony_ci __set_bit(PHY_INTERFACE_MODE_RXAUI, mask); 113962306a36Sopenharmony_ci __set_bit(PHY_INTERFACE_MODE_10GBASER, mask); 114062306a36Sopenharmony_ci __set_bit(PHY_INTERFACE_MODE_USXGMII, mask); 114162306a36Sopenharmony_ci} 114262306a36Sopenharmony_ci 114362306a36Sopenharmony_cistatic void mv2110_init_supported_interfaces(unsigned long *mask) 114462306a36Sopenharmony_ci{ 114562306a36Sopenharmony_ci __set_bit(PHY_INTERFACE_MODE_SGMII, mask); 114662306a36Sopenharmony_ci __set_bit(PHY_INTERFACE_MODE_2500BASEX, mask); 114762306a36Sopenharmony_ci __set_bit(PHY_INTERFACE_MODE_5GBASER, mask); 114862306a36Sopenharmony_ci __set_bit(PHY_INTERFACE_MODE_10GBASER, mask); 114962306a36Sopenharmony_ci __set_bit(PHY_INTERFACE_MODE_USXGMII, mask); 115062306a36Sopenharmony_ci} 115162306a36Sopenharmony_ci 115262306a36Sopenharmony_cistatic void mv2111_init_supported_interfaces(unsigned long *mask) 115362306a36Sopenharmony_ci{ 115462306a36Sopenharmony_ci __set_bit(PHY_INTERFACE_MODE_SGMII, mask); 115562306a36Sopenharmony_ci __set_bit(PHY_INTERFACE_MODE_2500BASEX, mask); 115662306a36Sopenharmony_ci __set_bit(PHY_INTERFACE_MODE_10GBASER, mask); 115762306a36Sopenharmony_ci __set_bit(PHY_INTERFACE_MODE_USXGMII, mask); 115862306a36Sopenharmony_ci} 115962306a36Sopenharmony_ci 116062306a36Sopenharmony_cistatic const struct mv3310_chip mv3310_type = { 116162306a36Sopenharmony_ci .has_downshift = mv3310_has_downshift, 116262306a36Sopenharmony_ci .init_supported_interfaces = mv3310_init_supported_interfaces, 116362306a36Sopenharmony_ci .get_mactype = mv3310_get_mactype, 116462306a36Sopenharmony_ci .set_mactype = mv3310_set_mactype, 116562306a36Sopenharmony_ci .select_mactype = mv3310_select_mactype, 116662306a36Sopenharmony_ci .init_interface = mv3310_init_interface, 116762306a36Sopenharmony_ci 116862306a36Sopenharmony_ci#ifdef CONFIG_HWMON 116962306a36Sopenharmony_ci .hwmon_read_temp_reg = mv3310_hwmon_read_temp_reg, 117062306a36Sopenharmony_ci#endif 117162306a36Sopenharmony_ci}; 117262306a36Sopenharmony_ci 117362306a36Sopenharmony_cistatic const struct mv3310_chip mv3340_type = { 117462306a36Sopenharmony_ci .has_downshift = mv3310_has_downshift, 117562306a36Sopenharmony_ci .init_supported_interfaces = mv3340_init_supported_interfaces, 117662306a36Sopenharmony_ci .get_mactype = mv3310_get_mactype, 117762306a36Sopenharmony_ci .set_mactype = mv3310_set_mactype, 117862306a36Sopenharmony_ci .select_mactype = mv3310_select_mactype, 117962306a36Sopenharmony_ci .init_interface = mv3340_init_interface, 118062306a36Sopenharmony_ci 118162306a36Sopenharmony_ci#ifdef CONFIG_HWMON 118262306a36Sopenharmony_ci .hwmon_read_temp_reg = mv3310_hwmon_read_temp_reg, 118362306a36Sopenharmony_ci#endif 118462306a36Sopenharmony_ci}; 118562306a36Sopenharmony_ci 118662306a36Sopenharmony_cistatic const struct mv3310_chip mv2110_type = { 118762306a36Sopenharmony_ci .init_supported_interfaces = mv2110_init_supported_interfaces, 118862306a36Sopenharmony_ci .get_mactype = mv2110_get_mactype, 118962306a36Sopenharmony_ci .set_mactype = mv2110_set_mactype, 119062306a36Sopenharmony_ci .select_mactype = mv2110_select_mactype, 119162306a36Sopenharmony_ci .init_interface = mv2110_init_interface, 119262306a36Sopenharmony_ci 119362306a36Sopenharmony_ci#ifdef CONFIG_HWMON 119462306a36Sopenharmony_ci .hwmon_read_temp_reg = mv2110_hwmon_read_temp_reg, 119562306a36Sopenharmony_ci#endif 119662306a36Sopenharmony_ci}; 119762306a36Sopenharmony_ci 119862306a36Sopenharmony_cistatic const struct mv3310_chip mv2111_type = { 119962306a36Sopenharmony_ci .init_supported_interfaces = mv2111_init_supported_interfaces, 120062306a36Sopenharmony_ci .get_mactype = mv2110_get_mactype, 120162306a36Sopenharmony_ci .set_mactype = mv2110_set_mactype, 120262306a36Sopenharmony_ci .select_mactype = mv2110_select_mactype, 120362306a36Sopenharmony_ci .init_interface = mv2110_init_interface, 120462306a36Sopenharmony_ci 120562306a36Sopenharmony_ci#ifdef CONFIG_HWMON 120662306a36Sopenharmony_ci .hwmon_read_temp_reg = mv2110_hwmon_read_temp_reg, 120762306a36Sopenharmony_ci#endif 120862306a36Sopenharmony_ci}; 120962306a36Sopenharmony_ci 121062306a36Sopenharmony_cistatic int mv3310_get_number_of_ports(struct phy_device *phydev) 121162306a36Sopenharmony_ci{ 121262306a36Sopenharmony_ci int ret; 121362306a36Sopenharmony_ci 121462306a36Sopenharmony_ci ret = phy_read_mmd(phydev, MDIO_MMD_PCS, MV_PCS_PORT_INFO); 121562306a36Sopenharmony_ci if (ret < 0) 121662306a36Sopenharmony_ci return ret; 121762306a36Sopenharmony_ci 121862306a36Sopenharmony_ci ret &= MV_PCS_PORT_INFO_NPORTS_MASK; 121962306a36Sopenharmony_ci ret >>= MV_PCS_PORT_INFO_NPORTS_SHIFT; 122062306a36Sopenharmony_ci 122162306a36Sopenharmony_ci return ret + 1; 122262306a36Sopenharmony_ci} 122362306a36Sopenharmony_ci 122462306a36Sopenharmony_cistatic int mv3310_match_phy_device(struct phy_device *phydev) 122562306a36Sopenharmony_ci{ 122662306a36Sopenharmony_ci if ((phydev->c45_ids.device_ids[MDIO_MMD_PMAPMD] & 122762306a36Sopenharmony_ci MARVELL_PHY_ID_MASK) != MARVELL_PHY_ID_88X3310) 122862306a36Sopenharmony_ci return 0; 122962306a36Sopenharmony_ci 123062306a36Sopenharmony_ci return mv3310_get_number_of_ports(phydev) == 1; 123162306a36Sopenharmony_ci} 123262306a36Sopenharmony_ci 123362306a36Sopenharmony_cistatic int mv3340_match_phy_device(struct phy_device *phydev) 123462306a36Sopenharmony_ci{ 123562306a36Sopenharmony_ci if ((phydev->c45_ids.device_ids[MDIO_MMD_PMAPMD] & 123662306a36Sopenharmony_ci MARVELL_PHY_ID_MASK) != MARVELL_PHY_ID_88X3310) 123762306a36Sopenharmony_ci return 0; 123862306a36Sopenharmony_ci 123962306a36Sopenharmony_ci return mv3310_get_number_of_ports(phydev) == 4; 124062306a36Sopenharmony_ci} 124162306a36Sopenharmony_ci 124262306a36Sopenharmony_cistatic int mv211x_match_phy_device(struct phy_device *phydev, bool has_5g) 124362306a36Sopenharmony_ci{ 124462306a36Sopenharmony_ci int val; 124562306a36Sopenharmony_ci 124662306a36Sopenharmony_ci if ((phydev->c45_ids.device_ids[MDIO_MMD_PMAPMD] & 124762306a36Sopenharmony_ci MARVELL_PHY_ID_MASK) != MARVELL_PHY_ID_88E2110) 124862306a36Sopenharmony_ci return 0; 124962306a36Sopenharmony_ci 125062306a36Sopenharmony_ci val = phy_read_mmd(phydev, MDIO_MMD_PCS, MDIO_SPEED); 125162306a36Sopenharmony_ci if (val < 0) 125262306a36Sopenharmony_ci return val; 125362306a36Sopenharmony_ci 125462306a36Sopenharmony_ci return !!(val & MDIO_PCS_SPEED_5G) == has_5g; 125562306a36Sopenharmony_ci} 125662306a36Sopenharmony_ci 125762306a36Sopenharmony_cistatic int mv2110_match_phy_device(struct phy_device *phydev) 125862306a36Sopenharmony_ci{ 125962306a36Sopenharmony_ci return mv211x_match_phy_device(phydev, true); 126062306a36Sopenharmony_ci} 126162306a36Sopenharmony_ci 126262306a36Sopenharmony_cistatic int mv2111_match_phy_device(struct phy_device *phydev) 126362306a36Sopenharmony_ci{ 126462306a36Sopenharmony_ci return mv211x_match_phy_device(phydev, false); 126562306a36Sopenharmony_ci} 126662306a36Sopenharmony_ci 126762306a36Sopenharmony_cistatic void mv3110_get_wol(struct phy_device *phydev, 126862306a36Sopenharmony_ci struct ethtool_wolinfo *wol) 126962306a36Sopenharmony_ci{ 127062306a36Sopenharmony_ci int ret; 127162306a36Sopenharmony_ci 127262306a36Sopenharmony_ci wol->supported = WAKE_MAGIC; 127362306a36Sopenharmony_ci wol->wolopts = 0; 127462306a36Sopenharmony_ci 127562306a36Sopenharmony_ci ret = phy_read_mmd(phydev, MDIO_MMD_VEND2, MV_V2_WOL_CTRL); 127662306a36Sopenharmony_ci if (ret < 0) 127762306a36Sopenharmony_ci return; 127862306a36Sopenharmony_ci 127962306a36Sopenharmony_ci if (ret & MV_V2_WOL_CTRL_MAGIC_PKT_EN) 128062306a36Sopenharmony_ci wol->wolopts |= WAKE_MAGIC; 128162306a36Sopenharmony_ci} 128262306a36Sopenharmony_ci 128362306a36Sopenharmony_cistatic int mv3110_set_wol(struct phy_device *phydev, 128462306a36Sopenharmony_ci struct ethtool_wolinfo *wol) 128562306a36Sopenharmony_ci{ 128662306a36Sopenharmony_ci int ret; 128762306a36Sopenharmony_ci 128862306a36Sopenharmony_ci if (wol->wolopts & WAKE_MAGIC) { 128962306a36Sopenharmony_ci /* Enable the WOL interrupt */ 129062306a36Sopenharmony_ci ret = phy_set_bits_mmd(phydev, MDIO_MMD_VEND2, 129162306a36Sopenharmony_ci MV_V2_PORT_INTR_MASK, 129262306a36Sopenharmony_ci MV_V2_PORT_INTR_STS_WOL_EN); 129362306a36Sopenharmony_ci if (ret < 0) 129462306a36Sopenharmony_ci return ret; 129562306a36Sopenharmony_ci 129662306a36Sopenharmony_ci /* Store the device address for the magic packet */ 129762306a36Sopenharmony_ci ret = phy_write_mmd(phydev, MDIO_MMD_VEND2, 129862306a36Sopenharmony_ci MV_V2_MAGIC_PKT_WORD2, 129962306a36Sopenharmony_ci ((phydev->attached_dev->dev_addr[5] << 8) | 130062306a36Sopenharmony_ci phydev->attached_dev->dev_addr[4])); 130162306a36Sopenharmony_ci if (ret < 0) 130262306a36Sopenharmony_ci return ret; 130362306a36Sopenharmony_ci 130462306a36Sopenharmony_ci ret = phy_write_mmd(phydev, MDIO_MMD_VEND2, 130562306a36Sopenharmony_ci MV_V2_MAGIC_PKT_WORD1, 130662306a36Sopenharmony_ci ((phydev->attached_dev->dev_addr[3] << 8) | 130762306a36Sopenharmony_ci phydev->attached_dev->dev_addr[2])); 130862306a36Sopenharmony_ci if (ret < 0) 130962306a36Sopenharmony_ci return ret; 131062306a36Sopenharmony_ci 131162306a36Sopenharmony_ci ret = phy_write_mmd(phydev, MDIO_MMD_VEND2, 131262306a36Sopenharmony_ci MV_V2_MAGIC_PKT_WORD0, 131362306a36Sopenharmony_ci ((phydev->attached_dev->dev_addr[1] << 8) | 131462306a36Sopenharmony_ci phydev->attached_dev->dev_addr[0])); 131562306a36Sopenharmony_ci if (ret < 0) 131662306a36Sopenharmony_ci return ret; 131762306a36Sopenharmony_ci 131862306a36Sopenharmony_ci /* Clear WOL status and enable magic packet matching */ 131962306a36Sopenharmony_ci ret = phy_set_bits_mmd(phydev, MDIO_MMD_VEND2, 132062306a36Sopenharmony_ci MV_V2_WOL_CTRL, 132162306a36Sopenharmony_ci MV_V2_WOL_CTRL_MAGIC_PKT_EN | 132262306a36Sopenharmony_ci MV_V2_WOL_CTRL_CLEAR_STS); 132362306a36Sopenharmony_ci if (ret < 0) 132462306a36Sopenharmony_ci return ret; 132562306a36Sopenharmony_ci } else { 132662306a36Sopenharmony_ci /* Disable magic packet matching & reset WOL status bit */ 132762306a36Sopenharmony_ci ret = phy_modify_mmd(phydev, MDIO_MMD_VEND2, 132862306a36Sopenharmony_ci MV_V2_WOL_CTRL, 132962306a36Sopenharmony_ci MV_V2_WOL_CTRL_MAGIC_PKT_EN, 133062306a36Sopenharmony_ci MV_V2_WOL_CTRL_CLEAR_STS); 133162306a36Sopenharmony_ci if (ret < 0) 133262306a36Sopenharmony_ci return ret; 133362306a36Sopenharmony_ci } 133462306a36Sopenharmony_ci 133562306a36Sopenharmony_ci /* Reset the clear WOL status bit as it does not self-clear */ 133662306a36Sopenharmony_ci return phy_clear_bits_mmd(phydev, MDIO_MMD_VEND2, 133762306a36Sopenharmony_ci MV_V2_WOL_CTRL, 133862306a36Sopenharmony_ci MV_V2_WOL_CTRL_CLEAR_STS); 133962306a36Sopenharmony_ci} 134062306a36Sopenharmony_ci 134162306a36Sopenharmony_cistatic struct phy_driver mv3310_drivers[] = { 134262306a36Sopenharmony_ci { 134362306a36Sopenharmony_ci .phy_id = MARVELL_PHY_ID_88X3310, 134462306a36Sopenharmony_ci .phy_id_mask = MARVELL_PHY_ID_MASK, 134562306a36Sopenharmony_ci .match_phy_device = mv3310_match_phy_device, 134662306a36Sopenharmony_ci .name = "mv88x3310", 134762306a36Sopenharmony_ci .driver_data = &mv3310_type, 134862306a36Sopenharmony_ci .get_features = mv3310_get_features, 134962306a36Sopenharmony_ci .config_init = mv3310_config_init, 135062306a36Sopenharmony_ci .probe = mv3310_probe, 135162306a36Sopenharmony_ci .suspend = mv3310_suspend, 135262306a36Sopenharmony_ci .resume = mv3310_resume, 135362306a36Sopenharmony_ci .config_aneg = mv3310_config_aneg, 135462306a36Sopenharmony_ci .aneg_done = mv3310_aneg_done, 135562306a36Sopenharmony_ci .read_status = mv3310_read_status, 135662306a36Sopenharmony_ci .get_tunable = mv3310_get_tunable, 135762306a36Sopenharmony_ci .set_tunable = mv3310_set_tunable, 135862306a36Sopenharmony_ci .remove = mv3310_remove, 135962306a36Sopenharmony_ci .set_loopback = genphy_c45_loopback, 136062306a36Sopenharmony_ci .get_wol = mv3110_get_wol, 136162306a36Sopenharmony_ci .set_wol = mv3110_set_wol, 136262306a36Sopenharmony_ci }, 136362306a36Sopenharmony_ci { 136462306a36Sopenharmony_ci .phy_id = MARVELL_PHY_ID_88X3310, 136562306a36Sopenharmony_ci .phy_id_mask = MARVELL_PHY_ID_MASK, 136662306a36Sopenharmony_ci .match_phy_device = mv3340_match_phy_device, 136762306a36Sopenharmony_ci .name = "mv88x3340", 136862306a36Sopenharmony_ci .driver_data = &mv3340_type, 136962306a36Sopenharmony_ci .get_features = mv3310_get_features, 137062306a36Sopenharmony_ci .config_init = mv3310_config_init, 137162306a36Sopenharmony_ci .probe = mv3310_probe, 137262306a36Sopenharmony_ci .suspend = mv3310_suspend, 137362306a36Sopenharmony_ci .resume = mv3310_resume, 137462306a36Sopenharmony_ci .config_aneg = mv3310_config_aneg, 137562306a36Sopenharmony_ci .aneg_done = mv3310_aneg_done, 137662306a36Sopenharmony_ci .read_status = mv3310_read_status, 137762306a36Sopenharmony_ci .get_tunable = mv3310_get_tunable, 137862306a36Sopenharmony_ci .set_tunable = mv3310_set_tunable, 137962306a36Sopenharmony_ci .remove = mv3310_remove, 138062306a36Sopenharmony_ci .set_loopback = genphy_c45_loopback, 138162306a36Sopenharmony_ci }, 138262306a36Sopenharmony_ci { 138362306a36Sopenharmony_ci .phy_id = MARVELL_PHY_ID_88E2110, 138462306a36Sopenharmony_ci .phy_id_mask = MARVELL_PHY_ID_MASK, 138562306a36Sopenharmony_ci .match_phy_device = mv2110_match_phy_device, 138662306a36Sopenharmony_ci .name = "mv88e2110", 138762306a36Sopenharmony_ci .driver_data = &mv2110_type, 138862306a36Sopenharmony_ci .probe = mv3310_probe, 138962306a36Sopenharmony_ci .suspend = mv3310_suspend, 139062306a36Sopenharmony_ci .resume = mv3310_resume, 139162306a36Sopenharmony_ci .config_init = mv3310_config_init, 139262306a36Sopenharmony_ci .config_aneg = mv3310_config_aneg, 139362306a36Sopenharmony_ci .aneg_done = mv3310_aneg_done, 139462306a36Sopenharmony_ci .read_status = mv3310_read_status, 139562306a36Sopenharmony_ci .get_tunable = mv3310_get_tunable, 139662306a36Sopenharmony_ci .set_tunable = mv3310_set_tunable, 139762306a36Sopenharmony_ci .remove = mv3310_remove, 139862306a36Sopenharmony_ci .set_loopback = genphy_c45_loopback, 139962306a36Sopenharmony_ci .get_wol = mv3110_get_wol, 140062306a36Sopenharmony_ci .set_wol = mv3110_set_wol, 140162306a36Sopenharmony_ci }, 140262306a36Sopenharmony_ci { 140362306a36Sopenharmony_ci .phy_id = MARVELL_PHY_ID_88E2110, 140462306a36Sopenharmony_ci .phy_id_mask = MARVELL_PHY_ID_MASK, 140562306a36Sopenharmony_ci .match_phy_device = mv2111_match_phy_device, 140662306a36Sopenharmony_ci .name = "mv88e2111", 140762306a36Sopenharmony_ci .driver_data = &mv2111_type, 140862306a36Sopenharmony_ci .probe = mv3310_probe, 140962306a36Sopenharmony_ci .suspend = mv3310_suspend, 141062306a36Sopenharmony_ci .resume = mv3310_resume, 141162306a36Sopenharmony_ci .config_init = mv3310_config_init, 141262306a36Sopenharmony_ci .config_aneg = mv3310_config_aneg, 141362306a36Sopenharmony_ci .aneg_done = mv3310_aneg_done, 141462306a36Sopenharmony_ci .read_status = mv3310_read_status, 141562306a36Sopenharmony_ci .get_tunable = mv3310_get_tunable, 141662306a36Sopenharmony_ci .set_tunable = mv3310_set_tunable, 141762306a36Sopenharmony_ci .remove = mv3310_remove, 141862306a36Sopenharmony_ci .set_loopback = genphy_c45_loopback, 141962306a36Sopenharmony_ci }, 142062306a36Sopenharmony_ci}; 142162306a36Sopenharmony_ci 142262306a36Sopenharmony_cimodule_phy_driver(mv3310_drivers); 142362306a36Sopenharmony_ci 142462306a36Sopenharmony_cistatic struct mdio_device_id __maybe_unused mv3310_tbl[] = { 142562306a36Sopenharmony_ci { MARVELL_PHY_ID_88X3310, MARVELL_PHY_ID_MASK }, 142662306a36Sopenharmony_ci { MARVELL_PHY_ID_88E2110, MARVELL_PHY_ID_MASK }, 142762306a36Sopenharmony_ci { }, 142862306a36Sopenharmony_ci}; 142962306a36Sopenharmony_ciMODULE_DEVICE_TABLE(mdio, mv3310_tbl); 143062306a36Sopenharmony_ciMODULE_DESCRIPTION("Marvell Alaska X/M multi-gigabit Ethernet PHY driver"); 143162306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 1432