net: phy: qcom: move additional functions to shared library
authorChristian Marangi <ansuelsmth@gmail.com>
Mon, 29 Jan 2024 14:15:22 +0000 (15:15 +0100)
committerJakub Kicinski <kuba@kernel.org>
Thu, 1 Feb 2024 00:25:17 +0000 (16:25 -0800)
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@gmail.com>
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
Link: https://lore.kernel.org/r/20240129141600.2592-5-ansuelsmth@gmail.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
drivers/net/phy/qcom/at803x.c
drivers/net/phy/qcom/qcom-phy-lib.c
drivers/net/phy/qcom/qcom.h

index 638babc50df12a792788a762be12b3a8560a529a..6a753498b994f9620bc24270ab2fea7759fef231 100644 (file)
 
 #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
-
-#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
 
 #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
@@ -366,11 +308,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 +407,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 +679,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 +704,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 +739,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 +754,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 +785,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[] = {
index 7192184429b7fae390a0a14dd3d5fc0ccd151259..e0295d4b4a51fa7432c735e154ad32561e2aac00 100644 (file)
@@ -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);
index 8eb476d7c2821b3c78d81532b4dd408d302dd125..c127d8f50f0f4053250b3b0c68ef48ecef20d1a9 100644 (file)
@@ -1,5 +1,63 @@
 /* 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_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
+
+#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
 
 #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 +90,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);