On Sat, Jan 27, 2024 at 03:42:44PM +0100, Christian Marangi wrote: > Move additional functions to shared library in preparation for qca808x > PHY Family to be detached from at803x driver. > > Only the shared defines are moved to the shared qcom.h header. > > Signed-off-by: Christian Marangi <ansuelsmth@xxxxxxxxx> > --- > drivers/net/phy/qcom/at803x.c | 428 +--------------------------- > drivers/net/phy/qcom/qcom-phy-lib.c | 376 ++++++++++++++++++++++++ > drivers/net/phy/qcom/qcom.h | 84 ++++++ > 3 files changed, 463 insertions(+), 425 deletions(-) > > diff --git a/drivers/net/phy/qcom/at803x.c b/drivers/net/phy/qcom/at803x.c > index 638babc50df1..442060793854 100644 > --- a/drivers/net/phy/qcom/at803x.c > +++ b/drivers/net/phy/qcom/at803x.c > @@ -24,65 +24,11 @@ > > #include "qcom.h" > > -#define AT803X_SPECIFIC_FUNCTION_CONTROL 0x10 > -#define AT803X_SFC_ASSERT_CRS BIT(11) > -#define AT803X_SFC_FORCE_LINK BIT(10) > -#define AT803X_SFC_MDI_CROSSOVER_MODE_M GENMASK(6, 5) > -#define AT803X_SFC_AUTOMATIC_CROSSOVER 0x3 > -#define AT803X_SFC_MANUAL_MDIX 0x1 > -#define AT803X_SFC_MANUAL_MDI 0x0 > -#define AT803X_SFC_SQE_TEST BIT(2) > -#define AT803X_SFC_POLARITY_REVERSAL BIT(1) > -#define AT803X_SFC_DISABLE_JABBER BIT(0) > - > -#define AT803X_SPECIFIC_STATUS 0x11 > -#define AT803X_SS_SPEED_MASK GENMASK(15, 14) > -#define AT803X_SS_SPEED_1000 2 > -#define AT803X_SS_SPEED_100 1 > -#define AT803X_SS_SPEED_10 0 > -#define AT803X_SS_DUPLEX BIT(13) > -#define AT803X_SS_SPEED_DUPLEX_RESOLVED BIT(11) > -#define AT803X_SS_MDIX BIT(6) > - > -#define QCA808X_SS_SPEED_MASK GENMASK(9, 7) > -#define QCA808X_SS_SPEED_2500 4 This was an intended remove and should be removed in patch 5 instead and cause build fail on testing this single patch. Will fix in v3, sorry for the noise! > - > -#define AT803X_INTR_ENABLE 0x12 > -#define AT803X_INTR_ENABLE_AUTONEG_ERR BIT(15) > -#define AT803X_INTR_ENABLE_SPEED_CHANGED BIT(14) > -#define AT803X_INTR_ENABLE_DUPLEX_CHANGED BIT(13) > -#define AT803X_INTR_ENABLE_PAGE_RECEIVED BIT(12) > -#define AT803X_INTR_ENABLE_LINK_FAIL BIT(11) > -#define AT803X_INTR_ENABLE_LINK_SUCCESS BIT(10) > -#define AT803X_INTR_ENABLE_LINK_FAIL_BX BIT(8) > -#define AT803X_INTR_ENABLE_LINK_SUCCESS_BX BIT(7) > -#define AT803X_INTR_ENABLE_WIRESPEED_DOWNGRADE BIT(5) > -#define AT803X_INTR_ENABLE_POLARITY_CHANGED BIT(1) > -#define AT803X_INTR_ENABLE_WOL BIT(0) > - > -#define AT803X_INTR_STATUS 0x13 > - > -#define AT803X_SMART_SPEED 0x14 > -#define AT803X_SMART_SPEED_ENABLE BIT(5) > -#define AT803X_SMART_SPEED_RETRY_LIMIT_MASK GENMASK(4, 2) > -#define AT803X_SMART_SPEED_BYPASS_TIMER BIT(1) > -#define AT803X_CDT 0x16 > -#define AT803X_CDT_MDI_PAIR_MASK GENMASK(9, 8) > -#define AT803X_CDT_ENABLE_TEST BIT(0) > -#define AT803X_CDT_STATUS 0x1c > -#define AT803X_CDT_STATUS_STAT_NORMAL 0 > -#define AT803X_CDT_STATUS_STAT_SHORT 1 > -#define AT803X_CDT_STATUS_STAT_OPEN 2 > -#define AT803X_CDT_STATUS_STAT_FAIL 3 > -#define AT803X_CDT_STATUS_STAT_MASK GENMASK(9, 8) > -#define AT803X_CDT_STATUS_DELTA_TIME_MASK GENMASK(7, 0) > #define AT803X_LED_CONTROL 0x18 > > #define AT803X_PHY_MMD3_WOL_CTRL 0x8012 > #define AT803X_WOL_EN BIT(5) > -#define AT803X_LOC_MAC_ADDR_0_15_OFFSET 0x804C > -#define AT803X_LOC_MAC_ADDR_16_31_OFFSET 0x804B > -#define AT803X_LOC_MAC_ADDR_32_47_OFFSET 0x804A > + > #define AT803X_REG_CHIP_CONFIG 0x1f > #define AT803X_BT_BX_REG_SEL 0x8000 > > @@ -138,10 +84,6 @@ > #define AT803X_CLK_OUT_STRENGTH_HALF 1 > #define AT803X_CLK_OUT_STRENGTH_QUARTER 2 > > -#define AT803X_DEFAULT_DOWNSHIFT 5 > -#define AT803X_MIN_DOWNSHIFT 2 > -#define AT803X_MAX_DOWNSHIFT 9 > - > #define AT803X_MMD3_SMARTEEE_CTL1 0x805b > #define AT803X_MMD3_SMARTEEE_CTL2 0x805c > #define AT803X_MMD3_SMARTEEE_CTL3 0x805d > @@ -158,6 +100,8 @@ > > #define QCA9561_PHY_ID 0x004dd042 > > +#define AT803X_SS_SPEED_MASK GENMASK(15, 14) > + > #define AT803X_PAGE_FIBER 0 > #define AT803X_PAGE_COPPER 1 > > @@ -366,11 +310,6 @@ MODULE_DESCRIPTION("Qualcomm Atheros AR803x and QCA808X PHY driver"); > MODULE_AUTHOR("Matus Ujhelyi"); > MODULE_LICENSE("GPL"); > > -struct at803x_ss_mask { > - u16 speed_mask; > - u8 speed_shift; > -}; > - > struct at803x_priv { > int flags; > u16 clk_25m_reg; > @@ -470,80 +409,6 @@ static void at803x_context_restore(struct phy_device *phydev, > phy_write(phydev, AT803X_LED_CONTROL, context->led_control); > } > > -static int at803x_set_wol(struct phy_device *phydev, > - struct ethtool_wolinfo *wol) > -{ > - int ret, irq_enabled; > - > - if (wol->wolopts & WAKE_MAGIC) { > - struct net_device *ndev = phydev->attached_dev; > - const u8 *mac; > - unsigned int i; > - static const unsigned int offsets[] = { > - AT803X_LOC_MAC_ADDR_32_47_OFFSET, > - AT803X_LOC_MAC_ADDR_16_31_OFFSET, > - AT803X_LOC_MAC_ADDR_0_15_OFFSET, > - }; > - > - if (!ndev) > - return -ENODEV; > - > - mac = (const u8 *)ndev->dev_addr; > - > - if (!is_valid_ether_addr(mac)) > - return -EINVAL; > - > - for (i = 0; i < 3; i++) > - phy_write_mmd(phydev, MDIO_MMD_PCS, offsets[i], > - mac[(i * 2) + 1] | (mac[(i * 2)] << 8)); > - > - /* Enable WOL interrupt */ > - ret = phy_modify(phydev, AT803X_INTR_ENABLE, 0, AT803X_INTR_ENABLE_WOL); > - if (ret) > - return ret; > - } else { > - /* Disable WOL interrupt */ > - ret = phy_modify(phydev, AT803X_INTR_ENABLE, AT803X_INTR_ENABLE_WOL, 0); > - if (ret) > - return ret; > - } > - > - /* Clear WOL status */ > - ret = phy_read(phydev, AT803X_INTR_STATUS); > - if (ret < 0) > - return ret; > - > - /* Check if there are other interrupts except for WOL triggered when PHY is > - * in interrupt mode, only the interrupts enabled by AT803X_INTR_ENABLE can > - * be passed up to the interrupt PIN. > - */ > - irq_enabled = phy_read(phydev, AT803X_INTR_ENABLE); > - if (irq_enabled < 0) > - return irq_enabled; > - > - irq_enabled &= ~AT803X_INTR_ENABLE_WOL; > - if (ret & irq_enabled && !phy_polling_mode(phydev)) > - phy_trigger_machine(phydev); > - > - return 0; > -} > - > -static void at803x_get_wol(struct phy_device *phydev, > - struct ethtool_wolinfo *wol) > -{ > - int value; > - > - wol->supported = WAKE_MAGIC; > - wol->wolopts = 0; > - > - value = phy_read(phydev, AT803X_INTR_ENABLE); > - if (value < 0) > - return; > - > - if (value & AT803X_INTR_ENABLE_WOL) > - wol->wolopts |= WAKE_MAGIC; > -} > - > static int at803x_suspend(struct phy_device *phydev) > { > int value; > @@ -816,73 +681,6 @@ static int at803x_config_init(struct phy_device *phydev) > return phy_modify(phydev, MII_ADVERTISE, MDIO_AN_CTRL1_XNP, 0); > } > > -static int at803x_ack_interrupt(struct phy_device *phydev) > -{ > - int err; > - > - err = phy_read(phydev, AT803X_INTR_STATUS); > - > - return (err < 0) ? err : 0; > -} > - > -static int at803x_config_intr(struct phy_device *phydev) > -{ > - int err; > - int value; > - > - value = phy_read(phydev, AT803X_INTR_ENABLE); > - > - if (phydev->interrupts == PHY_INTERRUPT_ENABLED) { > - /* Clear any pending interrupts */ > - err = at803x_ack_interrupt(phydev); > - if (err) > - return err; > - > - value |= AT803X_INTR_ENABLE_AUTONEG_ERR; > - value |= AT803X_INTR_ENABLE_SPEED_CHANGED; > - value |= AT803X_INTR_ENABLE_DUPLEX_CHANGED; > - value |= AT803X_INTR_ENABLE_LINK_FAIL; > - value |= AT803X_INTR_ENABLE_LINK_SUCCESS; > - > - err = phy_write(phydev, AT803X_INTR_ENABLE, value); > - } else { > - err = phy_write(phydev, AT803X_INTR_ENABLE, 0); > - if (err) > - return err; > - > - /* Clear any pending interrupts */ > - err = at803x_ack_interrupt(phydev); > - } > - > - return err; > -} > - > -static irqreturn_t at803x_handle_interrupt(struct phy_device *phydev) > -{ > - int irq_status, int_enabled; > - > - irq_status = phy_read(phydev, AT803X_INTR_STATUS); > - if (irq_status < 0) { > - phy_error(phydev); > - return IRQ_NONE; > - } > - > - /* Read the current enabled interrupts */ > - int_enabled = phy_read(phydev, AT803X_INTR_ENABLE); > - if (int_enabled < 0) { > - phy_error(phydev); > - return IRQ_NONE; > - } > - > - /* See if this was one of our enabled interrupts */ > - if (!(irq_status & int_enabled)) > - return IRQ_NONE; > - > - phy_trigger_machine(phydev); > - > - return IRQ_HANDLED; > -} > - > static void at803x_link_change_notify(struct phy_device *phydev) > { > /* > @@ -908,69 +706,6 @@ static void at803x_link_change_notify(struct phy_device *phydev) > } > } > > -static int at803x_read_specific_status(struct phy_device *phydev, > - struct at803x_ss_mask ss_mask) > -{ > - int ss; > - > - /* Read the AT8035 PHY-Specific Status register, which indicates the > - * speed and duplex that the PHY is actually using, irrespective of > - * whether we are in autoneg mode or not. > - */ > - ss = phy_read(phydev, AT803X_SPECIFIC_STATUS); > - if (ss < 0) > - return ss; > - > - if (ss & AT803X_SS_SPEED_DUPLEX_RESOLVED) { > - int sfc, speed; > - > - sfc = phy_read(phydev, AT803X_SPECIFIC_FUNCTION_CONTROL); > - if (sfc < 0) > - return sfc; > - > - speed = ss & ss_mask.speed_mask; > - speed >>= ss_mask.speed_shift; > - > - switch (speed) { > - case AT803X_SS_SPEED_10: > - phydev->speed = SPEED_10; > - break; > - case AT803X_SS_SPEED_100: > - phydev->speed = SPEED_100; > - break; > - case AT803X_SS_SPEED_1000: > - phydev->speed = SPEED_1000; > - break; > - case QCA808X_SS_SPEED_2500: > - phydev->speed = SPEED_2500; > - break; > - } > - if (ss & AT803X_SS_DUPLEX) > - phydev->duplex = DUPLEX_FULL; > - else > - phydev->duplex = DUPLEX_HALF; > - > - if (ss & AT803X_SS_MDIX) > - phydev->mdix = ETH_TP_MDI_X; > - else > - phydev->mdix = ETH_TP_MDI; > - > - switch (FIELD_GET(AT803X_SFC_MDI_CROSSOVER_MODE_M, sfc)) { > - case AT803X_SFC_MANUAL_MDI: > - phydev->mdix_ctrl = ETH_TP_MDI; > - break; > - case AT803X_SFC_MANUAL_MDIX: > - phydev->mdix_ctrl = ETH_TP_MDI_X; > - break; > - case AT803X_SFC_AUTOMATIC_CROSSOVER: > - phydev->mdix_ctrl = ETH_TP_MDI_AUTO; > - break; > - } > - } > - > - return 0; > -} > - > static int at803x_read_status(struct phy_device *phydev) > { > struct at803x_ss_mask ss_mask = { 0 }; > @@ -1006,50 +741,6 @@ static int at803x_read_status(struct phy_device *phydev) > return 0; > } > > -static int at803x_config_mdix(struct phy_device *phydev, u8 ctrl) > -{ > - u16 val; > - > - switch (ctrl) { > - case ETH_TP_MDI: > - val = AT803X_SFC_MANUAL_MDI; > - break; > - case ETH_TP_MDI_X: > - val = AT803X_SFC_MANUAL_MDIX; > - break; > - case ETH_TP_MDI_AUTO: > - val = AT803X_SFC_AUTOMATIC_CROSSOVER; > - break; > - default: > - return 0; > - } > - > - return phy_modify_changed(phydev, AT803X_SPECIFIC_FUNCTION_CONTROL, > - AT803X_SFC_MDI_CROSSOVER_MODE_M, > - FIELD_PREP(AT803X_SFC_MDI_CROSSOVER_MODE_M, val)); > -} > - > -static int at803x_prepare_config_aneg(struct phy_device *phydev) > -{ > - int ret; > - > - ret = at803x_config_mdix(phydev, phydev->mdix_ctrl); > - if (ret < 0) > - return ret; > - > - /* Changes of the midx bits are disruptive to the normal operation; > - * therefore any changes to these registers must be followed by a > - * software reset to take effect. > - */ > - if (ret == 1) { > - ret = genphy_soft_reset(phydev); > - if (ret < 0) > - return ret; > - } > - > - return 0; > -} > - > static int at803x_config_aneg(struct phy_device *phydev) > { > struct at803x_priv *priv = phydev->priv; > @@ -1065,80 +756,6 @@ static int at803x_config_aneg(struct phy_device *phydev) > return genphy_config_aneg(phydev); > } > > -static int at803x_get_downshift(struct phy_device *phydev, u8 *d) > -{ > - int val; > - > - val = phy_read(phydev, AT803X_SMART_SPEED); > - if (val < 0) > - return val; > - > - if (val & AT803X_SMART_SPEED_ENABLE) > - *d = FIELD_GET(AT803X_SMART_SPEED_RETRY_LIMIT_MASK, val) + 2; > - else > - *d = DOWNSHIFT_DEV_DISABLE; > - > - return 0; > -} > - > -static int at803x_set_downshift(struct phy_device *phydev, u8 cnt) > -{ > - u16 mask, set; > - int ret; > - > - switch (cnt) { > - case DOWNSHIFT_DEV_DEFAULT_COUNT: > - cnt = AT803X_DEFAULT_DOWNSHIFT; > - fallthrough; > - case AT803X_MIN_DOWNSHIFT ... AT803X_MAX_DOWNSHIFT: > - set = AT803X_SMART_SPEED_ENABLE | > - AT803X_SMART_SPEED_BYPASS_TIMER | > - FIELD_PREP(AT803X_SMART_SPEED_RETRY_LIMIT_MASK, cnt - 2); > - mask = AT803X_SMART_SPEED_RETRY_LIMIT_MASK; > - break; > - case DOWNSHIFT_DEV_DISABLE: > - set = 0; > - mask = AT803X_SMART_SPEED_ENABLE | > - AT803X_SMART_SPEED_BYPASS_TIMER; > - break; > - default: > - return -EINVAL; > - } > - > - ret = phy_modify_changed(phydev, AT803X_SMART_SPEED, mask, set); > - > - /* After changing the smart speed settings, we need to perform a > - * software reset, use phy_init_hw() to make sure we set the > - * reapply any values which might got lost during software reset. > - */ > - if (ret == 1) > - ret = phy_init_hw(phydev); > - > - return ret; > -} > - > -static int at803x_get_tunable(struct phy_device *phydev, > - struct ethtool_tunable *tuna, void *data) > -{ > - switch (tuna->id) { > - case ETHTOOL_PHY_DOWNSHIFT: > - return at803x_get_downshift(phydev, data); > - default: > - return -EOPNOTSUPP; > - } > -} > - > -static int at803x_set_tunable(struct phy_device *phydev, > - struct ethtool_tunable *tuna, const void *data) > -{ > - switch (tuna->id) { > - case ETHTOOL_PHY_DOWNSHIFT: > - return at803x_set_downshift(phydev, *(const u8 *)data); > - default: > - return -EOPNOTSUPP; > - } > -} > - > static int at803x_cable_test_result_trans(u16 status) > { > switch (FIELD_GET(AT803X_CDT_STATUS_STAT_MASK, status)) { > @@ -1170,45 +787,6 @@ static bool at803x_cdt_fault_length_valid(u16 status) > return false; > } > > -static int at803x_cdt_fault_length(int dt) > -{ > - /* According to the datasheet the distance to the fault is > - * DELTA_TIME * 0.824 meters. > - * > - * The author suspect the correct formula is: > - * > - * fault_distance = DELTA_TIME * (c * VF) / 125MHz / 2 > - * > - * where c is the speed of light, VF is the velocity factor of > - * the twisted pair cable, 125MHz the counter frequency and > - * we need to divide by 2 because the hardware will measure the > - * round trip time to the fault and back to the PHY. > - * > - * With a VF of 0.69 we get the factor 0.824 mentioned in the > - * datasheet. > - */ > - return (dt * 824) / 10; > -} > - > -static int at803x_cdt_start(struct phy_device *phydev, > - u32 cdt_start) > -{ > - return phy_write(phydev, AT803X_CDT, cdt_start); > -} > - > -static int at803x_cdt_wait_for_completion(struct phy_device *phydev, > - u32 cdt_en) > -{ > - int val, ret; > - > - /* One test run takes about 25ms */ > - ret = phy_read_poll_timeout(phydev, AT803X_CDT, val, > - !(val & cdt_en), > - 30000, 100000, true); > - > - return ret < 0 ? ret : 0; > -} > - > static int at803x_cable_test_one_pair(struct phy_device *phydev, int pair) > { > static const int ethtool_pair[] = { > diff --git a/drivers/net/phy/qcom/qcom-phy-lib.c b/drivers/net/phy/qcom/qcom-phy-lib.c > index 7192184429b7..e0295d4b4a51 100644 > --- a/drivers/net/phy/qcom/qcom-phy-lib.c > +++ b/drivers/net/phy/qcom/qcom-phy-lib.c > @@ -3,6 +3,9 @@ > #include <linux/phy.h> > #include <linux/module.h> > > +#include <linux/netdevice.h> > +#include <linux/etherdevice.h> > + > #include "qcom.h" > > MODULE_DESCRIPTION("Qualcomm PHY driver Common Functions"); > @@ -51,3 +54,376 @@ int at803x_debug_reg_write(struct phy_device *phydev, u16 reg, u16 data) > return phy_write(phydev, AT803X_DEBUG_DATA, data); > } > EXPORT_SYMBOL_GPL(at803x_debug_reg_write); > + > +int at803x_set_wol(struct phy_device *phydev, > + struct ethtool_wolinfo *wol) > +{ > + int ret, irq_enabled; > + > + if (wol->wolopts & WAKE_MAGIC) { > + struct net_device *ndev = phydev->attached_dev; > + const u8 *mac; > + unsigned int i; > + static const unsigned int offsets[] = { > + AT803X_LOC_MAC_ADDR_32_47_OFFSET, > + AT803X_LOC_MAC_ADDR_16_31_OFFSET, > + AT803X_LOC_MAC_ADDR_0_15_OFFSET, > + }; > + > + if (!ndev) > + return -ENODEV; > + > + mac = (const u8 *)ndev->dev_addr; > + > + if (!is_valid_ether_addr(mac)) > + return -EINVAL; > + > + for (i = 0; i < 3; i++) > + phy_write_mmd(phydev, MDIO_MMD_PCS, offsets[i], > + mac[(i * 2) + 1] | (mac[(i * 2)] << 8)); > + > + /* Enable WOL interrupt */ > + ret = phy_modify(phydev, AT803X_INTR_ENABLE, 0, AT803X_INTR_ENABLE_WOL); > + if (ret) > + return ret; > + } else { > + /* Disable WOL interrupt */ > + ret = phy_modify(phydev, AT803X_INTR_ENABLE, AT803X_INTR_ENABLE_WOL, 0); > + if (ret) > + return ret; > + } > + > + /* Clear WOL status */ > + ret = phy_read(phydev, AT803X_INTR_STATUS); > + if (ret < 0) > + return ret; > + > + /* Check if there are other interrupts except for WOL triggered when PHY is > + * in interrupt mode, only the interrupts enabled by AT803X_INTR_ENABLE can > + * be passed up to the interrupt PIN. > + */ > + irq_enabled = phy_read(phydev, AT803X_INTR_ENABLE); > + if (irq_enabled < 0) > + return irq_enabled; > + > + irq_enabled &= ~AT803X_INTR_ENABLE_WOL; > + if (ret & irq_enabled && !phy_polling_mode(phydev)) > + phy_trigger_machine(phydev); > + > + return 0; > +} > +EXPORT_SYMBOL_GPL(at803x_set_wol); > + > +void at803x_get_wol(struct phy_device *phydev, > + struct ethtool_wolinfo *wol) > +{ > + int value; > + > + wol->supported = WAKE_MAGIC; > + wol->wolopts = 0; > + > + value = phy_read(phydev, AT803X_INTR_ENABLE); > + if (value < 0) > + return; > + > + if (value & AT803X_INTR_ENABLE_WOL) > + wol->wolopts |= WAKE_MAGIC; > +} > +EXPORT_SYMBOL_GPL(at803x_get_wol); > + > +int at803x_ack_interrupt(struct phy_device *phydev) > +{ > + int err; > + > + err = phy_read(phydev, AT803X_INTR_STATUS); > + > + return (err < 0) ? err : 0; > +} > +EXPORT_SYMBOL_GPL(at803x_ack_interrupt); > + > +int at803x_config_intr(struct phy_device *phydev) > +{ > + int err; > + int value; > + > + value = phy_read(phydev, AT803X_INTR_ENABLE); > + > + if (phydev->interrupts == PHY_INTERRUPT_ENABLED) { > + /* Clear any pending interrupts */ > + err = at803x_ack_interrupt(phydev); > + if (err) > + return err; > + > + value |= AT803X_INTR_ENABLE_AUTONEG_ERR; > + value |= AT803X_INTR_ENABLE_SPEED_CHANGED; > + value |= AT803X_INTR_ENABLE_DUPLEX_CHANGED; > + value |= AT803X_INTR_ENABLE_LINK_FAIL; > + value |= AT803X_INTR_ENABLE_LINK_SUCCESS; > + > + err = phy_write(phydev, AT803X_INTR_ENABLE, value); > + } else { > + err = phy_write(phydev, AT803X_INTR_ENABLE, 0); > + if (err) > + return err; > + > + /* Clear any pending interrupts */ > + err = at803x_ack_interrupt(phydev); > + } > + > + return err; > +} > +EXPORT_SYMBOL_GPL(at803x_config_intr); > + > +irqreturn_t at803x_handle_interrupt(struct phy_device *phydev) > +{ > + int irq_status, int_enabled; > + > + irq_status = phy_read(phydev, AT803X_INTR_STATUS); > + if (irq_status < 0) { > + phy_error(phydev); > + return IRQ_NONE; > + } > + > + /* Read the current enabled interrupts */ > + int_enabled = phy_read(phydev, AT803X_INTR_ENABLE); > + if (int_enabled < 0) { > + phy_error(phydev); > + return IRQ_NONE; > + } > + > + /* See if this was one of our enabled interrupts */ > + if (!(irq_status & int_enabled)) > + return IRQ_NONE; > + > + phy_trigger_machine(phydev); > + > + return IRQ_HANDLED; > +} > +EXPORT_SYMBOL_GPL(at803x_handle_interrupt); > + > +int at803x_read_specific_status(struct phy_device *phydev, > + struct at803x_ss_mask ss_mask) > +{ > + int ss; > + > + /* Read the AT8035 PHY-Specific Status register, which indicates the > + * speed and duplex that the PHY is actually using, irrespective of > + * whether we are in autoneg mode or not. > + */ > + ss = phy_read(phydev, AT803X_SPECIFIC_STATUS); > + if (ss < 0) > + return ss; > + > + if (ss & AT803X_SS_SPEED_DUPLEX_RESOLVED) { > + int sfc, speed; > + > + sfc = phy_read(phydev, AT803X_SPECIFIC_FUNCTION_CONTROL); > + if (sfc < 0) > + return sfc; > + > + speed = ss & ss_mask.speed_mask; > + speed >>= ss_mask.speed_shift; > + > + switch (speed) { > + case AT803X_SS_SPEED_10: > + phydev->speed = SPEED_10; > + break; > + case AT803X_SS_SPEED_100: > + phydev->speed = SPEED_100; > + break; > + case AT803X_SS_SPEED_1000: > + phydev->speed = SPEED_1000; > + break; > + case QCA808X_SS_SPEED_2500: > + phydev->speed = SPEED_2500; > + break; > + } > + if (ss & AT803X_SS_DUPLEX) > + phydev->duplex = DUPLEX_FULL; > + else > + phydev->duplex = DUPLEX_HALF; > + > + if (ss & AT803X_SS_MDIX) > + phydev->mdix = ETH_TP_MDI_X; > + else > + phydev->mdix = ETH_TP_MDI; > + > + switch (FIELD_GET(AT803X_SFC_MDI_CROSSOVER_MODE_M, sfc)) { > + case AT803X_SFC_MANUAL_MDI: > + phydev->mdix_ctrl = ETH_TP_MDI; > + break; > + case AT803X_SFC_MANUAL_MDIX: > + phydev->mdix_ctrl = ETH_TP_MDI_X; > + break; > + case AT803X_SFC_AUTOMATIC_CROSSOVER: > + phydev->mdix_ctrl = ETH_TP_MDI_AUTO; > + break; > + } > + } > + > + return 0; > +} > +EXPORT_SYMBOL_GPL(at803x_read_specific_status); > + > +int at803x_config_mdix(struct phy_device *phydev, u8 ctrl) > +{ > + u16 val; > + > + switch (ctrl) { > + case ETH_TP_MDI: > + val = AT803X_SFC_MANUAL_MDI; > + break; > + case ETH_TP_MDI_X: > + val = AT803X_SFC_MANUAL_MDIX; > + break; > + case ETH_TP_MDI_AUTO: > + val = AT803X_SFC_AUTOMATIC_CROSSOVER; > + break; > + default: > + return 0; > + } > + > + return phy_modify_changed(phydev, AT803X_SPECIFIC_FUNCTION_CONTROL, > + AT803X_SFC_MDI_CROSSOVER_MODE_M, > + FIELD_PREP(AT803X_SFC_MDI_CROSSOVER_MODE_M, val)); > +} > +EXPORT_SYMBOL_GPL(at803x_config_mdix); > + > +int at803x_prepare_config_aneg(struct phy_device *phydev) > +{ > + int ret; > + > + ret = at803x_config_mdix(phydev, phydev->mdix_ctrl); > + if (ret < 0) > + return ret; > + > + /* Changes of the midx bits are disruptive to the normal operation; > + * therefore any changes to these registers must be followed by a > + * software reset to take effect. > + */ > + if (ret == 1) { > + ret = genphy_soft_reset(phydev); > + if (ret < 0) > + return ret; > + } > + > + return 0; > +} > +EXPORT_SYMBOL_GPL(at803x_prepare_config_aneg); > + > +static int at803x_get_downshift(struct phy_device *phydev, u8 *d) > +{ > + int val; > + > + val = phy_read(phydev, AT803X_SMART_SPEED); > + if (val < 0) > + return val; > + > + if (val & AT803X_SMART_SPEED_ENABLE) > + *d = FIELD_GET(AT803X_SMART_SPEED_RETRY_LIMIT_MASK, val) + 2; > + else > + *d = DOWNSHIFT_DEV_DISABLE; > + > + return 0; > +} > + > +static int at803x_set_downshift(struct phy_device *phydev, u8 cnt) > +{ > + u16 mask, set; > + int ret; > + > + switch (cnt) { > + case DOWNSHIFT_DEV_DEFAULT_COUNT: > + cnt = AT803X_DEFAULT_DOWNSHIFT; > + fallthrough; > + case AT803X_MIN_DOWNSHIFT ... AT803X_MAX_DOWNSHIFT: > + set = AT803X_SMART_SPEED_ENABLE | > + AT803X_SMART_SPEED_BYPASS_TIMER | > + FIELD_PREP(AT803X_SMART_SPEED_RETRY_LIMIT_MASK, cnt - 2); > + mask = AT803X_SMART_SPEED_RETRY_LIMIT_MASK; > + break; > + case DOWNSHIFT_DEV_DISABLE: > + set = 0; > + mask = AT803X_SMART_SPEED_ENABLE | > + AT803X_SMART_SPEED_BYPASS_TIMER; > + break; > + default: > + return -EINVAL; > + } > + > + ret = phy_modify_changed(phydev, AT803X_SMART_SPEED, mask, set); > + > + /* After changing the smart speed settings, we need to perform a > + * software reset, use phy_init_hw() to make sure we set the > + * reapply any values which might got lost during software reset. > + */ > + if (ret == 1) > + ret = phy_init_hw(phydev); > + > + return ret; > +} > + > +int at803x_get_tunable(struct phy_device *phydev, > + struct ethtool_tunable *tuna, void *data) > +{ > + switch (tuna->id) { > + case ETHTOOL_PHY_DOWNSHIFT: > + return at803x_get_downshift(phydev, data); > + default: > + return -EOPNOTSUPP; > + } > +} > +EXPORT_SYMBOL_GPL(at803x_get_tunable); > + > +int at803x_set_tunable(struct phy_device *phydev, > + struct ethtool_tunable *tuna, const void *data) > +{ > + switch (tuna->id) { > + case ETHTOOL_PHY_DOWNSHIFT: > + return at803x_set_downshift(phydev, *(const u8 *)data); > + default: > + return -EOPNOTSUPP; > + } > +} > +EXPORT_SYMBOL_GPL(at803x_set_tunable); > + > +int at803x_cdt_fault_length(int dt) > +{ > + /* According to the datasheet the distance to the fault is > + * DELTA_TIME * 0.824 meters. > + * > + * The author suspect the correct formula is: > + * > + * fault_distance = DELTA_TIME * (c * VF) / 125MHz / 2 > + * > + * where c is the speed of light, VF is the velocity factor of > + * the twisted pair cable, 125MHz the counter frequency and > + * we need to divide by 2 because the hardware will measure the > + * round trip time to the fault and back to the PHY. > + * > + * With a VF of 0.69 we get the factor 0.824 mentioned in the > + * datasheet. > + */ > + return (dt * 824) / 10; > +} > +EXPORT_SYMBOL_GPL(at803x_cdt_fault_length); > + > +int at803x_cdt_start(struct phy_device *phydev, u32 cdt_start) > +{ > + return phy_write(phydev, AT803X_CDT, cdt_start); > +} > +EXPORT_SYMBOL_GPL(at803x_cdt_start); > + > +int at803x_cdt_wait_for_completion(struct phy_device *phydev, > + u32 cdt_en) > +{ > + int val, ret; > + > + /* One test run takes about 25ms */ > + ret = phy_read_poll_timeout(phydev, AT803X_CDT, val, > + !(val & cdt_en), > + 30000, 100000, true); > + > + return ret < 0 ? ret : 0; > +} > +EXPORT_SYMBOL_GPL(at803x_cdt_wait_for_completion); > diff --git a/drivers/net/phy/qcom/qcom.h b/drivers/net/phy/qcom/qcom.h > index 8eb476d7c282..344f8c01d5b8 100644 > --- a/drivers/net/phy/qcom/qcom.h > +++ b/drivers/net/phy/qcom/qcom.h > @@ -1,5 +1,61 @@ > /* SPDX-License-Identifier: GPL-2.0 */ > > +#define AT803X_SPECIFIC_FUNCTION_CONTROL 0x10 > +#define AT803X_SFC_ASSERT_CRS BIT(11) > +#define AT803X_SFC_FORCE_LINK BIT(10) > +#define AT803X_SFC_MDI_CROSSOVER_MODE_M GENMASK(6, 5) > +#define AT803X_SFC_AUTOMATIC_CROSSOVER 0x3 > +#define AT803X_SFC_MANUAL_MDIX 0x1 > +#define AT803X_SFC_MANUAL_MDI 0x0 > +#define AT803X_SFC_SQE_TEST BIT(2) > +#define AT803X_SFC_POLARITY_REVERSAL BIT(1) > +#define AT803X_SFC_DISABLE_JABBER BIT(0) > + > +#define AT803X_SPECIFIC_STATUS 0x11 > +#define AT803X_SS_SPEED_1000 2 > +#define AT803X_SS_SPEED_100 1 > +#define AT803X_SS_SPEED_10 0 > +#define AT803X_SS_DUPLEX BIT(13) > +#define AT803X_SS_SPEED_DUPLEX_RESOLVED BIT(11) > +#define AT803X_SS_MDIX BIT(6) > + > +#define QCA808X_SS_SPEED_2500 4 > + > +#define AT803X_INTR_ENABLE 0x12 > +#define AT803X_INTR_ENABLE_AUTONEG_ERR BIT(15) > +#define AT803X_INTR_ENABLE_SPEED_CHANGED BIT(14) > +#define AT803X_INTR_ENABLE_DUPLEX_CHANGED BIT(13) > +#define AT803X_INTR_ENABLE_PAGE_RECEIVED BIT(12) > +#define AT803X_INTR_ENABLE_LINK_FAIL BIT(11) > +#define AT803X_INTR_ENABLE_LINK_SUCCESS BIT(10) > +#define AT803X_INTR_ENABLE_LINK_FAIL_BX BIT(8) > +#define AT803X_INTR_ENABLE_LINK_SUCCESS_BX BIT(7) > +#define AT803X_INTR_ENABLE_WIRESPEED_DOWNGRADE BIT(5) > +#define AT803X_INTR_ENABLE_POLARITY_CHANGED BIT(1) > +#define AT803X_INTR_ENABLE_WOL BIT(0) > + > +#define AT803X_INTR_STATUS 0x13 > + > +#define AT803X_SMART_SPEED 0x14 > +#define AT803X_SMART_SPEED_ENABLE BIT(5) > +#define AT803X_SMART_SPEED_RETRY_LIMIT_MASK GENMASK(4, 2) > +#define AT803X_SMART_SPEED_BYPASS_TIMER BIT(1) > + > +#define AT803X_CDT 0x16 > +#define AT803X_CDT_MDI_PAIR_MASK GENMASK(9, 8) > +#define AT803X_CDT_ENABLE_TEST BIT(0) > +#define AT803X_CDT_STATUS 0x1c > +#define AT803X_CDT_STATUS_STAT_NORMAL 0 > +#define AT803X_CDT_STATUS_STAT_SHORT 1 > +#define AT803X_CDT_STATUS_STAT_OPEN 2 > +#define AT803X_CDT_STATUS_STAT_FAIL 3 > +#define AT803X_CDT_STATUS_STAT_MASK GENMASK(9, 8) > +#define AT803X_CDT_STATUS_DELTA_TIME_MASK GENMASK(7, 0) > + > +#define AT803X_LOC_MAC_ADDR_0_15_OFFSET 0x804C > +#define AT803X_LOC_MAC_ADDR_16_31_OFFSET 0x804B > +#define AT803X_LOC_MAC_ADDR_32_47_OFFSET 0x804A > + > #define AT803X_DEBUG_ADDR 0x1D > #define AT803X_DEBUG_DATA 0x1E > > @@ -16,6 +72,10 @@ > #define AT803X_DEBUG_HIB_CTRL_EN_ANY_CHANGE BIT(13) > #define AT803X_DEBUG_HIB_CTRL_PS_HIB_EN BIT(15) > > +#define AT803X_DEFAULT_DOWNSHIFT 5 > +#define AT803X_MIN_DOWNSHIFT 2 > +#define AT803X_MAX_DOWNSHIFT 9 > + > enum stat_access_type { > PHY, > MMD > @@ -28,7 +88,31 @@ struct at803x_hw_stat { > enum stat_access_type access_type; > }; > > +struct at803x_ss_mask { > + u16 speed_mask; > + u8 speed_shift; > +}; > + > int at803x_debug_reg_read(struct phy_device *phydev, u16 reg); > int at803x_debug_reg_mask(struct phy_device *phydev, u16 reg, > u16 clear, u16 set); > int at803x_debug_reg_write(struct phy_device *phydev, u16 reg, u16 data); > +int at803x_set_wol(struct phy_device *phydev, > + struct ethtool_wolinfo *wol); > +void at803x_get_wol(struct phy_device *phydev, > + struct ethtool_wolinfo *wol); > +int at803x_ack_interrupt(struct phy_device *phydev); > +int at803x_config_intr(struct phy_device *phydev); > +irqreturn_t at803x_handle_interrupt(struct phy_device *phydev); > +int at803x_read_specific_status(struct phy_device *phydev, > + struct at803x_ss_mask ss_mask); > +int at803x_config_mdix(struct phy_device *phydev, u8 ctrl); > +int at803x_prepare_config_aneg(struct phy_device *phydev); > +int at803x_get_tunable(struct phy_device *phydev, > + struct ethtool_tunable *tuna, void *data); > +int at803x_set_tunable(struct phy_device *phydev, > + struct ethtool_tunable *tuna, const void *data); > +int at803x_cdt_fault_length(int dt); > +int at803x_cdt_start(struct phy_device *phydev, u32 cdt_start); > +int at803x_cdt_wait_for_completion(struct phy_device *phydev, > + u32 cdt_en); > -- > 2.43.0 > -- Ansuel