diff options
Diffstat (limited to 'drivers/net/phy/qcom')
| -rw-r--r-- | drivers/net/phy/qcom/at803x.c | 98 | ||||
| -rw-r--r-- | drivers/net/phy/qcom/qca808x.c | 15 | ||||
| -rw-r--r-- | drivers/net/phy/qcom/qca83xx.c | 58 |
3 files changed, 138 insertions, 33 deletions
diff --git a/drivers/net/phy/qcom/at803x.c b/drivers/net/phy/qcom/at803x.c index cacbadf1f484..09105f1e3737 100644 --- a/drivers/net/phy/qcom/at803x.c +++ b/drivers/net/phy/qcom/at803x.c @@ -1019,10 +1019,10 @@ static int ipq5018_cable_test_start(struct phy_device *phydev) return 0; } -static int ipq5018_config_init(struct phy_device *phydev) +static int ipq5018_analog_init(struct phy_device *phydev) { struct ipq5018_priv *priv = phydev->priv; - u16 val; + int val, ret; /* * set LDO efuse: first temporarily store ANA_DAC_FILTER value from @@ -1030,39 +1030,66 @@ static int ipq5018_config_init(struct phy_device *phydev) * is written to */ val = at803x_debug_reg_read(phydev, IPQ5018_PHY_DEBUG_ANA_DAC_FILTER); - at803x_debug_reg_mask(phydev, IPQ5018_PHY_DEBUG_ANA_LDO_EFUSE, - IPQ5018_PHY_DEBUG_ANA_LDO_EFUSE_MASK, - IPQ5018_PHY_DEBUG_ANA_LDO_EFUSE_DEFAULT); - at803x_debug_reg_write(phydev, IPQ5018_PHY_DEBUG_ANA_DAC_FILTER, val); + if (val < 0) + return val; + + ret = at803x_debug_reg_mask(phydev, IPQ5018_PHY_DEBUG_ANA_LDO_EFUSE, + IPQ5018_PHY_DEBUG_ANA_LDO_EFUSE_MASK, + IPQ5018_PHY_DEBUG_ANA_LDO_EFUSE_DEFAULT); + if (ret) + return ret; + + ret = at803x_debug_reg_write(phydev, IPQ5018_PHY_DEBUG_ANA_DAC_FILTER, + val); + if (ret) + return ret; /* set 8023AZ EEE TX and RX timer values */ - phy_write_mmd(phydev, MDIO_MMD_PCS, IPQ5018_PHY_PCS_EEE_TX_TIMER, - IPQ5018_PHY_PCS_EEE_TX_TIMER_VAL); - phy_write_mmd(phydev, MDIO_MMD_PCS, IPQ5018_PHY_PCS_EEE_RX_TIMER, - IPQ5018_PHY_PCS_EEE_RX_TIMER_VAL); + ret = phy_write_mmd(phydev, MDIO_MMD_PCS, IPQ5018_PHY_PCS_EEE_TX_TIMER, + IPQ5018_PHY_PCS_EEE_TX_TIMER_VAL); + if (ret) + return ret; + + ret = phy_write_mmd(phydev, MDIO_MMD_PCS, IPQ5018_PHY_PCS_EEE_RX_TIMER, + IPQ5018_PHY_PCS_EEE_RX_TIMER_VAL); + if (ret) + return ret; /* set MSE threshold values */ - phy_write_mmd(phydev, MDIO_MMD_PMAPMD, IPQ5018_PHY_MMD1_MSE_THRESH1, - IPQ5018_PHY_MMD1_MSE_THRESH1_VAL); - phy_write_mmd(phydev, MDIO_MMD_PMAPMD, IPQ5018_PHY_MMD1_MSE_THRESH2, - IPQ5018_PHY_MMD1_MSE_THRESH2_VAL); + ret = phy_write_mmd(phydev, MDIO_MMD_PMAPMD, + IPQ5018_PHY_MMD1_MSE_THRESH1, + IPQ5018_PHY_MMD1_MSE_THRESH1_VAL); + if (ret) + return ret; + + ret = phy_write_mmd(phydev, MDIO_MMD_PMAPMD, + IPQ5018_PHY_MMD1_MSE_THRESH2, + IPQ5018_PHY_MMD1_MSE_THRESH2_VAL); + if (ret) + return ret; /* PHY DAC values are optional and only set in a PHY to PHY link architecture */ - if (priv->set_short_cable_dac) { - /* setting MDAC (Multi-level Digital-to-Analog Converter) in MMD1 */ - phy_modify_mmd(phydev, MDIO_MMD_PMAPMD, IPQ5018_PHY_MMD1_MDAC, - IPQ5018_PHY_DAC_MASK, - FIELD_PREP(IPQ5018_PHY_DAC_MASK, - IPQ5018_PHY_MMD1_MDAC_VAL)); - - /* setting EDAC (Error-detection and Correction) in debug register */ - at803x_debug_reg_mask(phydev, IPQ5018_PHY_DEBUG_EDAC, - IPQ5018_PHY_DAC_MASK, - FIELD_PREP(IPQ5018_PHY_DAC_MASK, - IPQ5018_PHY_DEBUG_EDAC_VAL)); - } + if (!priv->set_short_cable_dac) + return 0; - return 0; + /* setting MDAC (Multi-level Digital-to-Analog Converter) in MMD1 */ + ret = phy_modify_mmd(phydev, MDIO_MMD_PMAPMD, IPQ5018_PHY_MMD1_MDAC, + IPQ5018_PHY_DAC_MASK, + FIELD_PREP(IPQ5018_PHY_DAC_MASK, + IPQ5018_PHY_MMD1_MDAC_VAL)); + if (ret) + return ret; + + /* setting EDAC (Error-detection and Correction) in debug register */ + return at803x_debug_reg_mask(phydev, IPQ5018_PHY_DEBUG_EDAC, + IPQ5018_PHY_DAC_MASK, + FIELD_PREP(IPQ5018_PHY_DAC_MASK, + IPQ5018_PHY_DEBUG_EDAC_VAL)); +} + +static int ipq5018_config_init(struct phy_device *phydev) +{ + return ipq5018_analog_init(phydev); } static void ipq5018_link_change_notify(struct phy_device *phydev) @@ -1111,6 +1138,21 @@ static int ipq5018_probe(struct phy_device *phydev) phydev->priv = priv; + /* + * The PHY starts autonegotiation as soon as it leaves reset. Apply the + * analog settings now instead of waiting for config_init() at attach + * time, and restart autonegotiation so that it uses them. + */ + ret = ipq5018_analog_init(phydev); + if (ret) + return dev_err_probe(dev, ret, + "failed to apply analog settings\n"); + + ret = genphy_restart_aneg(phydev); + if (ret) + return dev_err_probe(dev, ret, + "failed to restart autonegotiation\n"); + return 0; } diff --git a/drivers/net/phy/qcom/qca808x.c b/drivers/net/phy/qcom/qca808x.c index 8eb51b1a006c..bab26e4c6140 100644 --- a/drivers/net/phy/qcom/qca808x.c +++ b/drivers/net/phy/qcom/qca808x.c @@ -87,6 +87,9 @@ #define QCA8081_PHY_ID 0x004dd101 +/* led_polarity_mode otherwise holds a PHY_LED_ACTIVE_* value */ +#define QCA808X_PHY_LED_UNSET -1 + MODULE_DESCRIPTION("Qualcomm Atheros QCA808X PHY driver"); MODULE_AUTHOR("Matus Ujhelyi"); MODULE_LICENSE("GPL"); @@ -187,8 +190,7 @@ static int qca808x_probe(struct phy_device *phydev) if (!priv) return -ENOMEM; - /* Init LED polarity mode to -1 */ - priv->led_polarity_mode = -1; + priv->led_polarity_mode = QCA808X_PHY_LED_UNSET; phydev->priv = priv; @@ -200,8 +202,8 @@ static int qca808x_config_init(struct phy_device *phydev) struct qca808x_priv *priv = phydev->priv; int ret; - /* Default to LED Active High if active-low not in DT */ - if (priv->led_polarity_mode == -1) { + /* Set LED Active High unless active-low was requested in DT */ + if (priv->led_polarity_mode != PHY_LED_ACTIVE_LOW) { ret = phy_set_bits_mmd(phydev, MDIO_MMD_AN, QCA808X_MMD7_LED_POLARITY_CTRL, QCA808X_LED_ACTIVE_HIGH); @@ -603,6 +605,9 @@ static int qca808x_led_polarity_set(struct phy_device *phydev, int index, case PHY_LED_ACTIVE_LOW: active_low = true; break; + case PHY_LED_ACTIVE_HIGH: + active_low = false; + break; default: return -EINVAL; } @@ -612,7 +617,7 @@ static int qca808x_led_polarity_set(struct phy_device *phydev, int index, * To detect this, check if last requested polarity mode * match the new one. */ - if (priv->led_polarity_mode >= 0 && + if (priv->led_polarity_mode != QCA808X_PHY_LED_UNSET && priv->led_polarity_mode != active_low) { phydev_err(phydev, "PHY polarity is global. Mismatched polarity on different LED\n"); return -EINVAL; diff --git a/drivers/net/phy/qcom/qca83xx.c b/drivers/net/phy/qcom/qca83xx.c index bc70ed8efd86..2c4d085bacdb 100644 --- a/drivers/net/phy/qcom/qca83xx.c +++ b/drivers/net/phy/qcom/qca83xx.c @@ -92,6 +92,63 @@ static int qca83xx_probe(struct phy_device *phydev) return 0; } +static int qca8337_read_status(struct phy_device *phydev) +{ + struct at803x_ss_mask ss_mask = { + .speed_mask = AT803X_SS_SPEED_MASK, + .speed_shift = __bf_shf(AT803X_SS_SPEED_MASK), + }; + int ret, old_link = phydev->link; + + ret = genphy_read_status(phydev); + if (ret) + return ret; + + /* Keep the state resolved at link-up while the link stays up */ + if (phydev->autoneg == AUTONEG_ENABLE && old_link && phydev->link) + goto out; + + phydev->mdix = ETH_TP_MDI_INVALID; + + /* genphy_read_status() derives speed and duplex from the + * advertisements, which is wrong after a SmartSpeed downshift. + * Use what the PHY resolved instead. + */ + if (phydev->autoneg == AUTONEG_ENABLE && phydev->autoneg_complete && + phydev->link) { + phydev->speed = SPEED_UNKNOWN; + phydev->duplex = DUPLEX_UNKNOWN; + phydev->pause = false; + phydev->asym_pause = false; + + ret = at803x_read_specific_status(phydev, ss_mask); + if (ret) + return ret; + + /* Report the link down until speed and duplex are resolved, + * so that the next poll reads them again. Also clear what + * genphy_read_status() read for an up link, as the + * PHY_HALTED path of the state machine does. + */ + if (phydev->speed == SPEED_UNKNOWN || + phydev->duplex == DUPLEX_UNKNOWN) { + phydev->link = 0; + phydev->duplex = DUPLEX_UNKNOWN; + phydev->mdix = ETH_TP_MDI_INVALID; + phydev->master_slave_state = MASTER_SLAVE_STATE_UNKNOWN; + linkmode_zero(phydev->lp_advertising); + } else { + phy_resolve_aneg_pause(phydev); + } + } + +out: + /* MDI-X configuration is not implemented */ + phydev->mdix_ctrl = ETH_TP_MDI_INVALID; + + return 0; +} + static int qca83xx_config_init(struct phy_device *phydev) { u8 switch_revision; @@ -220,6 +277,7 @@ static struct phy_driver qca83xx_driver[] = { .flags = PHY_IS_INTERNAL, .config_init = qca83xx_config_init, .soft_reset = genphy_soft_reset, + .read_status = qca8337_read_status, .get_sset_count = qca83xx_get_sset_count, .get_strings = qca83xx_get_strings, .get_stats = qca83xx_get_stats, |
