--- /dev/null
+From 4e4aafcddbbfcdd6eed5780e190fcbfac8b4685a Mon Sep 17 00:00:00 2001
+From: Andrew Lunn <andrew@lunn.ch>
+Date: Mon, 9 Jan 2023 16:30:41 +0100
+Subject: [PATCH] net: mdio: Add dedicated C45 API to MDIO bus drivers
+
+Currently C22 and C45 transactions are mixed over a combined API calls
+which make use of a special bit in the reg address to indicate if a
+C45 transaction should be performed. This makes it impossible to know
+if the bus driver actually supports C45. Additionally, many C22 only
+drivers don't return -EOPNOTSUPP when asked to perform a C45
+transaction, they mistaking perform a C22 transaction.
+
+This is the first step to cleanly separate C22 from C45. To maintain
+backwards compatibility until all drivers which are capable of
+performing C45 are converted to this new API, the helper functions
+will fall back to the older API if the new API is not
+supported. Eventually this fallback will be removed.
+
+Signed-off-by: Andrew Lunn <andrew@lunn.ch>
+Signed-off-by: Michael Walle <michael@walle.cc>
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/phy/mdio_bus.c | 189 +++++++++++++++++++++++++++++++++++++
+ include/linux/mdio.h | 39 ++++----
+ include/linux/phy.h | 5 +
+ 3 files changed, 214 insertions(+), 19 deletions(-)
+
+--- a/drivers/net/phy/mdio_bus.c
++++ b/drivers/net/phy/mdio_bus.c
+@@ -832,6 +832,100 @@ int __mdiobus_modify_changed(struct mii_
+ EXPORT_SYMBOL_GPL(__mdiobus_modify_changed);
+
+ /**
++ * __mdiobus_c45_read - Unlocked version of the mdiobus_c45_read function
++ * @bus: the mii_bus struct
++ * @addr: the phy address
++ * @devad: device address to read
++ * @regnum: register number to read
++ *
++ * Read a MDIO bus register. Caller must hold the mdio bus lock.
++ *
++ * NOTE: MUST NOT be called from interrupt context.
++ */
++int __mdiobus_c45_read(struct mii_bus *bus, int addr, int devad, u32 regnum)
++{
++ int retval;
++
++ lockdep_assert_held_once(&bus->mdio_lock);
++
++ if (bus->read_c45)
++ retval = bus->read_c45(bus, addr, devad, regnum);
++ else
++ retval = bus->read(bus, addr, mdiobus_c45_addr(devad, regnum));
++
++ trace_mdio_access(bus, 1, addr, regnum, retval, retval);
++ mdiobus_stats_acct(&bus->stats[addr], true, retval);
++
++ return retval;
++}
++EXPORT_SYMBOL(__mdiobus_c45_read);
++
++/**
++ * __mdiobus_c45_write - Unlocked version of the mdiobus_write function
++ * @bus: the mii_bus struct
++ * @addr: the phy address
++ * @devad: device address to read
++ * @regnum: register number to write
++ * @val: value to write to @regnum
++ *
++ * Write a MDIO bus register. Caller must hold the mdio bus lock.
++ *
++ * NOTE: MUST NOT be called from interrupt context.
++ */
++int __mdiobus_c45_write(struct mii_bus *bus, int addr, int devad, u32 regnum,
++ u16 val)
++{
++ int err;
++
++ lockdep_assert_held_once(&bus->mdio_lock);
++
++ if (bus->write_c45)
++ err = bus->write_c45(bus, addr, devad, regnum, val);
++ else
++ err = bus->write(bus, addr, mdiobus_c45_addr(devad, regnum),
++ val);
++
++ trace_mdio_access(bus, 0, addr, regnum, val, err);
++ mdiobus_stats_acct(&bus->stats[addr], false, err);
++
++ return err;
++}
++EXPORT_SYMBOL(__mdiobus_c45_write);
++
++/**
++ * __mdiobus_c45_modify_changed - Unlocked version of the mdiobus_modify function
++ * @bus: the mii_bus struct
++ * @addr: the phy address
++ * @devad: device address to read
++ * @regnum: register number to modify
++ * @mask: bit mask of bits to clear
++ * @set: bit mask of bits to set
++ *
++ * Read, modify, and if any change, write the register value back to the
++ * device. Any error returns a negative number.
++ *
++ * NOTE: MUST NOT be called from interrupt context.
++ */
++static int __mdiobus_c45_modify_changed(struct mii_bus *bus, int addr,
++ int devad, u32 regnum, u16 mask,
++ u16 set)
++{
++ int new, ret;
++
++ ret = __mdiobus_c45_read(bus, addr, devad, regnum);
++ if (ret < 0)
++ return ret;
++
++ new = (ret & ~mask) | set;
++ if (new == ret)
++ return 0;
++
++ ret = __mdiobus_c45_write(bus, addr, devad, regnum, new);
++
++ return ret < 0 ? ret : 1;
++}
++
++/**
+ * mdiobus_read_nested - Nested version of the mdiobus_read function
+ * @bus: the mii_bus struct
+ * @addr: the phy address
+@@ -879,6 +973,29 @@ int mdiobus_read(struct mii_bus *bus, in
+ EXPORT_SYMBOL(mdiobus_read);
+
+ /**
++ * mdiobus_c45_read - Convenience function for reading a given MII mgmt register
++ * @bus: the mii_bus struct
++ * @addr: the phy address
++ * @devad: device address to read
++ * @regnum: register number to read
++ *
++ * NOTE: MUST NOT be called from interrupt context,
++ * because the bus read/write functions may wait for an interrupt
++ * to conclude the operation.
++ */
++int mdiobus_c45_read(struct mii_bus *bus, int addr, int devad, u32 regnum)
++{
++ int retval;
++
++ mutex_lock(&bus->mdio_lock);
++ retval = __mdiobus_c45_read(bus, addr, devad, regnum);
++ mutex_unlock(&bus->mdio_lock);
++
++ return retval;
++}
++EXPORT_SYMBOL(mdiobus_c45_read);
++
++/**
+ * mdiobus_write_nested - Nested version of the mdiobus_write function
+ * @bus: the mii_bus struct
+ * @addr: the phy address
+@@ -928,6 +1045,31 @@ int mdiobus_write(struct mii_bus *bus, i
+ EXPORT_SYMBOL(mdiobus_write);
+
+ /**
++ * mdiobus_c45_write - Convenience function for writing a given MII mgmt register
++ * @bus: the mii_bus struct
++ * @addr: the phy address
++ * @devad: device address to read
++ * @regnum: register number to write
++ * @val: value to write to @regnum
++ *
++ * NOTE: MUST NOT be called from interrupt context,
++ * because the bus read/write functions may wait for an interrupt
++ * to conclude the operation.
++ */
++int mdiobus_c45_write(struct mii_bus *bus, int addr, int devad, u32 regnum,
++ u16 val)
++{
++ int err;
++
++ mutex_lock(&bus->mdio_lock);
++ err = __mdiobus_c45_write(bus, addr, devad, regnum, val);
++ mutex_unlock(&bus->mdio_lock);
++
++ return err;
++}
++EXPORT_SYMBOL(mdiobus_c45_write);
++
++/**
+ * mdiobus_modify - Convenience function for modifying a given mdio device
+ * register
+ * @bus: the mii_bus struct
+@@ -949,6 +1091,30 @@ int mdiobus_modify(struct mii_bus *bus,
+ EXPORT_SYMBOL_GPL(mdiobus_modify);
+
+ /**
++ * mdiobus_c45_modify - Convenience function for modifying a given mdio device
++ * register
++ * @bus: the mii_bus struct
++ * @addr: the phy address
++ * @devad: device address to read
++ * @regnum: register number to write
++ * @mask: bit mask of bits to clear
++ * @set: bit mask of bits to set
++ */
++int mdiobus_c45_modify(struct mii_bus *bus, int addr, int devad, u32 regnum,
++ u16 mask, u16 set)
++{
++ int err;
++
++ mutex_lock(&bus->mdio_lock);
++ err = __mdiobus_c45_modify_changed(bus, addr, devad, regnum,
++ mask, set);
++ mutex_unlock(&bus->mdio_lock);
++
++ return err < 0 ? err : 0;
++}
++EXPORT_SYMBOL_GPL(mdiobus_c45_modify);
++
++/**
+ * mdiobus_modify_changed - Convenience function for modifying a given mdio
+ * device register and returning if it changed
+ * @bus: the mii_bus struct
+@@ -971,6 +1137,29 @@ int mdiobus_modify_changed(struct mii_bu
+ EXPORT_SYMBOL_GPL(mdiobus_modify_changed);
+
+ /**
++ * mdiobus_c45_modify_changed - Convenience function for modifying a given mdio
++ * device register and returning if it changed
++ * @bus: the mii_bus struct
++ * @addr: the phy address
++ * @devad: device address to read
++ * @regnum: register number to write
++ * @mask: bit mask of bits to clear
++ * @set: bit mask of bits to set
++ */
++int mdiobus_c45_modify_changed(struct mii_bus *bus, int devad, int addr,
++ u32 regnum, u16 mask, u16 set)
++{
++ int err;
++
++ mutex_lock(&bus->mdio_lock);
++ err = __mdiobus_c45_modify_changed(bus, addr, devad, regnum, mask, set);
++ mutex_unlock(&bus->mdio_lock);
++
++ return err;
++}
++EXPORT_SYMBOL_GPL(mdiobus_c45_modify_changed);
++
++/**
+ * mdio_bus_match - determine if given MDIO driver supports the given
+ * MDIO device
+ * @dev: target MDIO device
+--- a/include/linux/mdio.h
++++ b/include/linux/mdio.h
+@@ -423,6 +423,17 @@ int mdiobus_modify(struct mii_bus *bus,
+ u16 set);
+ int mdiobus_modify_changed(struct mii_bus *bus, int addr, u32 regnum,
+ u16 mask, u16 set);
++int __mdiobus_c45_read(struct mii_bus *bus, int addr, int devad, u32 regnum);
++int mdiobus_c45_read(struct mii_bus *bus, int addr, int devad, u32 regnum);
++int __mdiobus_c45_write(struct mii_bus *bus, int addr, int devad, u32 regnum,
++ u16 val);
++int mdiobus_c45_write(struct mii_bus *bus, int addr, int devad, u32 regnum,
++ u16 val);
++int mdiobus_c45_modify(struct mii_bus *bus, int addr, int devad, u32 regnum,
++ u16 mask, u16 set);
++
++int mdiobus_c45_modify_changed(struct mii_bus *bus, int addr, int devad,
++ u32 regnum, u16 mask, u16 set);
+
+ static inline int mdiodev_read(struct mdio_device *mdiodev, u32 regnum)
+ {
+@@ -463,29 +474,19 @@ static inline u16 mdiobus_c45_devad(u32
+ return FIELD_GET(MII_DEVADDR_C45_MASK, regnum);
+ }
+
+-static inline int __mdiobus_c45_read(struct mii_bus *bus, int prtad, int devad,
+- u16 regnum)
+-{
+- return __mdiobus_read(bus, prtad, mdiobus_c45_addr(devad, regnum));
+-}
+-
+-static inline int __mdiobus_c45_write(struct mii_bus *bus, int prtad, int devad,
+- u16 regnum, u16 val)
+-{
+- return __mdiobus_write(bus, prtad, mdiobus_c45_addr(devad, regnum),
+- val);
+-}
+-
+-static inline int mdiobus_c45_read(struct mii_bus *bus, int prtad, int devad,
+- u16 regnum)
++static inline int mdiodev_c45_modify(struct mdio_device *mdiodev, int devad,
++ u32 regnum, u16 mask, u16 set)
+ {
+- return mdiobus_read(bus, prtad, mdiobus_c45_addr(devad, regnum));
++ return mdiobus_c45_modify(mdiodev->bus, mdiodev->addr, devad, regnum,
++ mask, set);
+ }
+
+-static inline int mdiobus_c45_write(struct mii_bus *bus, int prtad, int devad,
+- u16 regnum, u16 val)
++static inline int mdiodev_c45_modify_changed(struct mdio_device *mdiodev,
++ int devad, u32 regnum, u16 mask,
++ u16 set)
+ {
+- return mdiobus_write(bus, prtad, mdiobus_c45_addr(devad, regnum), val);
++ return mdiobus_c45_modify_changed(mdiodev->bus, mdiodev->addr, devad,
++ regnum, mask, set);
+ }
+
+ int mdiobus_register_device(struct mdio_device *mdiodev);
+--- a/include/linux/phy.h
++++ b/include/linux/phy.h
+@@ -364,6 +364,11 @@ struct mii_bus {
+ int (*read)(struct mii_bus *bus, int addr, int regnum);
+ /** @write: Perform a write transfer on the bus */
+ int (*write)(struct mii_bus *bus, int addr, int regnum, u16 val);
++ /** @read_c45: Perform a C45 read transfer on the bus */
++ int (*read_c45)(struct mii_bus *bus, int addr, int devnum, int regnum);
++ /** @write_c45: Perform a C45 write transfer on the bus */
++ int (*write_c45)(struct mii_bus *bus, int addr, int devnum,
++ int regnum, u16 val);
+ /** @reset: Perform a reset of the bus */
+ int (*reset)(struct mii_bus *bus);
+
--- /dev/null
+From 9a0e95e34e9c0a713ddfd48c3a88a20d2bdfd514 Mon Sep 17 00:00:00 2001
+From: Gabor Juhos <j4g8y7@gmail.com>
+Date: Fri, 11 Aug 2023 13:10:07 +0200
+Subject: [PATCH] net: phy: Introduce PSGMII PHY interface mode
+
+The PSGMII interface is similar to QSGMII. The main difference
+is that the PSGMII interface combines five SGMII lines into a
+single link while in QSGMII only four lines are combined.
+
+Similarly to the QSGMII, this interface mode might also needs
+special handling within the MAC driver.
+
+It is commonly used by Qualcomm with their QCA807x PHY series and
+modern WiSoC-s.
+
+Add definitions for the PHY layer to allow to express this type
+of connection between the MAC and PHY.
+
+Signed-off-by: Gabor Juhos <j4g8y7@gmail.com>
+Signed-off-by: Robert Marko <robert.marko@sartura.hr>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ Documentation/networking/phy.rst | 4 ++++
+ drivers/net/phy/phy-core.c | 2 ++
+ drivers/net/phy/phylink.c | 3 +++
+ include/linux/phy.h | 4 ++++
+ 4 files changed, 13 insertions(+)
+
+--- a/Documentation/networking/phy.rst
++++ b/Documentation/networking/phy.rst
+@@ -323,6 +323,10 @@ Some of the interface modes are describe
+ contrast with the 1000BASE-X phy mode used for Clause 38 and 39 PMDs, this
+ interface mode has different autonegotiation and only supports full duplex.
+
++``PHY_INTERFACE_MODE_PSGMII``
++ This is the Penta SGMII mode, it is similar to QSGMII but it combines 5
++ SGMII lines into a single link compared to 4 on QSGMII.
++
+ Pause frames / flow control
+ ===========================
+
+--- a/drivers/net/phy/phy-core.c
++++ b/drivers/net/phy/phy-core.c
+@@ -140,6 +140,8 @@ int phy_interface_num_ports(phy_interfac
+ case PHY_INTERFACE_MODE_QSGMII:
+ case PHY_INTERFACE_MODE_QUSGMII:
+ return 4;
++ case PHY_INTERFACE_MODE_PSGMII:
++ return 5;
+ case PHY_INTERFACE_MODE_MAX:
+ WARN_ONCE(1, "PHY_INTERFACE_MODE_MAX isn't a valid interface mode");
+ return 0;
+--- a/drivers/net/phy/phylink.c
++++ b/drivers/net/phy/phylink.c
+@@ -187,6 +187,7 @@ static int phylink_interface_max_speed(p
+ case PHY_INTERFACE_MODE_RGMII_RXID:
+ case PHY_INTERFACE_MODE_RGMII_ID:
+ case PHY_INTERFACE_MODE_RGMII:
++ case PHY_INTERFACE_MODE_PSGMII:
+ case PHY_INTERFACE_MODE_QSGMII:
+ case PHY_INTERFACE_MODE_QUSGMII:
+ case PHY_INTERFACE_MODE_SGMII:
+@@ -448,6 +449,7 @@ unsigned long phylink_get_capabilities(p
+ case PHY_INTERFACE_MODE_RGMII_RXID:
+ case PHY_INTERFACE_MODE_RGMII_ID:
+ case PHY_INTERFACE_MODE_RGMII:
++ case PHY_INTERFACE_MODE_PSGMII:
+ case PHY_INTERFACE_MODE_QSGMII:
+ case PHY_INTERFACE_MODE_QUSGMII:
+ case PHY_INTERFACE_MODE_SGMII:
+@@ -814,6 +816,7 @@ static int phylink_parse_mode(struct phy
+
+ switch (pl->link_config.interface) {
+ case PHY_INTERFACE_MODE_SGMII:
++ case PHY_INTERFACE_MODE_PSGMII:
+ case PHY_INTERFACE_MODE_QSGMII:
+ case PHY_INTERFACE_MODE_QUSGMII:
+ case PHY_INTERFACE_MODE_RGMII:
+--- a/include/linux/phy.h
++++ b/include/linux/phy.h
+@@ -103,6 +103,7 @@ extern const int phy_10gbit_features_arr
+ * @PHY_INTERFACE_MODE_XGMII: 10 gigabit media-independent interface
+ * @PHY_INTERFACE_MODE_XLGMII:40 gigabit media-independent interface
+ * @PHY_INTERFACE_MODE_MOCA: Multimedia over Coax
++ * @PHY_INTERFACE_MODE_PSGMII: Penta SGMII
+ * @PHY_INTERFACE_MODE_QSGMII: Quad SGMII
+ * @PHY_INTERFACE_MODE_TRGMII: Turbo RGMII
+ * @PHY_INTERFACE_MODE_100BASEX: 100 BaseX
+@@ -140,6 +141,7 @@ typedef enum {
+ PHY_INTERFACE_MODE_XGMII,
+ PHY_INTERFACE_MODE_XLGMII,
+ PHY_INTERFACE_MODE_MOCA,
++ PHY_INTERFACE_MODE_PSGMII,
+ PHY_INTERFACE_MODE_QSGMII,
+ PHY_INTERFACE_MODE_TRGMII,
+ PHY_INTERFACE_MODE_100BASEX,
+@@ -247,6 +249,8 @@ static inline const char *phy_modes(phy_
+ return "xlgmii";
+ case PHY_INTERFACE_MODE_MOCA:
+ return "moca";
++ case PHY_INTERFACE_MODE_PSGMII:
++ return "psgmii";
+ case PHY_INTERFACE_MODE_QSGMII:
+ return "qsgmii";
+ case PHY_INTERFACE_MODE_TRGMII:
--- /dev/null
+From a593a2fcfdfb92cfd0ffc54bc81b07e6bfaaaf46 Mon Sep 17 00:00:00 2001
+From: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
+Date: Thu, 16 Mar 2023 14:08:26 +0200
+Subject: [PATCH] net: phy: at803x: Replace of_gpio.h with what indeed is used
+
+of_gpio.h in this driver is solely used as a proxy to other headers.
+This is incorrect usage of the of_gpio.h. Replace it .h with what
+indeed is used in the code.
+
+Signed-off-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/phy/at803x.c | 3 +--
+ 1 file changed, 1 insertion(+), 2 deletions(-)
+
+--- a/drivers/net/phy/at803x.c
++++ b/drivers/net/phy/at803x.c
+@@ -13,12 +13,11 @@
+ #include <linux/netdevice.h>
+ #include <linux/etherdevice.h>
+ #include <linux/ethtool_netlink.h>
+-#include <linux/of_gpio.h>
+ #include <linux/bitfield.h>
+-#include <linux/gpio/consumer.h>
+ #include <linux/regulator/of_regulator.h>
+ #include <linux/regulator/driver.h>
+ #include <linux/regulator/consumer.h>
++#include <linux/of.h>
+ #include <linux/phylink.h>
+ #include <linux/sfp.h>
+ #include <dt-bindings/net/qca-ar803x.h>
--- /dev/null
+From 8b8bc13d89a7d23d14b0e041c73f037c9db997b1 Mon Sep 17 00:00:00 2001
+From: Luo Jie <quic_luoj@quicinc.com>
+Date: Sun, 16 Jul 2023 16:49:19 +0800
+Subject: [PATCH 1/6] net: phy: at803x: support qca8081
+ genphy_c45_pma_read_abilities
+
+qca8081 PHY supports to use genphy_c45_pma_read_abilities for
+getting the PHY features supported except for the autoneg ability
+
+but autoneg ability exists in MDIO_STAT1 instead of MMD7.1, add it
+manually after calling genphy_c45_pma_read_abilities.
+
+Signed-off-by: Luo Jie <quic_luoj@quicinc.com>
+Reviewed-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/phy/at803x.c | 28 ++++++++++++++++++----------
+ 1 file changed, 18 insertions(+), 10 deletions(-)
+
+--- a/drivers/net/phy/at803x.c
++++ b/drivers/net/phy/at803x.c
+@@ -902,15 +902,6 @@ static int at803x_get_features(struct ph
+ if (err)
+ return err;
+
+- if (phydev->drv->phy_id == QCA8081_PHY_ID) {
+- err = phy_read_mmd(phydev, MDIO_MMD_PMAPMD, MDIO_PMA_NG_EXTABLE);
+- if (err < 0)
+- return err;
+-
+- linkmode_mod_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT, phydev->supported,
+- err & MDIO_PMA_NG_EXTABLE_2_5GBT);
+- }
+-
+ if (phydev->drv->phy_id != ATH8031_PHY_ID)
+ return 0;
+
+@@ -1996,6 +1987,23 @@ static int qca808x_cable_test_get_status
+ return 0;
+ }
+
++static int qca808x_get_features(struct phy_device *phydev)
++{
++ int ret;
++
++ ret = genphy_c45_pma_read_abilities(phydev);
++ if (ret)
++ return ret;
++
++ /* The autoneg ability is not existed in bit3 of MMD7.1,
++ * but it is supported by qca808x PHY, so we add it here
++ * manually.
++ */
++ linkmode_set_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, phydev->supported);
++
++ return 0;
++}
++
+ static struct phy_driver at803x_driver[] = {
+ {
+ /* Qualcomm Atheros AR8035 */
+@@ -2163,7 +2171,7 @@ static struct phy_driver at803x_driver[]
+ .set_tunable = at803x_set_tunable,
+ .set_wol = at803x_set_wol,
+ .get_wol = at803x_get_wol,
+- .get_features = at803x_get_features,
++ .get_features = qca808x_get_features,
+ .config_aneg = at803x_config_aneg,
+ .suspend = genphy_suspend,
+ .resume = genphy_resume,
--- /dev/null
+From f3db55ae860a82e1224a909072783ef850e5d228 Mon Sep 17 00:00:00 2001
+From: Luo Jie <quic_luoj@quicinc.com>
+Date: Sun, 16 Jul 2023 16:49:20 +0800
+Subject: [PATCH 2/6] net: phy: at803x: merge qca8081 slave seed function
+
+merge the seed enablement and seed value configuration into
+one function, since the random seed value is needed to be
+configured when the seed is enabled.
+
+Signed-off-by: Luo Jie <quic_luoj@quicinc.com>
+Reviewed-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/phy/at803x.c | 29 +++++++++--------------------
+ 1 file changed, 9 insertions(+), 20 deletions(-)
+
+--- a/drivers/net/phy/at803x.c
++++ b/drivers/net/phy/at803x.c
+@@ -1730,24 +1730,19 @@ static int qca808x_phy_fast_retrain_conf
+ return 0;
+ }
+
+-static int qca808x_phy_ms_random_seed_set(struct phy_device *phydev)
+-{
+- u16 seed_value = prandom_u32_max(QCA808X_MASTER_SLAVE_SEED_RANGE);
+-
+- return at803x_debug_reg_mask(phydev, QCA808X_PHY_DEBUG_LOCAL_SEED,
+- QCA808X_MASTER_SLAVE_SEED_CFG,
+- FIELD_PREP(QCA808X_MASTER_SLAVE_SEED_CFG, seed_value));
+-}
+-
+ static int qca808x_phy_ms_seed_enable(struct phy_device *phydev, bool enable)
+ {
+- u16 seed_enable = 0;
++ u16 seed_value;
+
+- if (enable)
+- seed_enable = QCA808X_MASTER_SLAVE_SEED_ENABLE;
++ if (!enable)
++ return at803x_debug_reg_mask(phydev, QCA808X_PHY_DEBUG_LOCAL_SEED,
++ QCA808X_MASTER_SLAVE_SEED_ENABLE, 0);
+
++ seed_value = prandom_u32_max(QCA808X_MASTER_SLAVE_SEED_RANGE);
+ return at803x_debug_reg_mask(phydev, QCA808X_PHY_DEBUG_LOCAL_SEED,
+- QCA808X_MASTER_SLAVE_SEED_ENABLE, seed_enable);
++ QCA808X_MASTER_SLAVE_SEED_CFG | QCA808X_MASTER_SLAVE_SEED_ENABLE,
++ FIELD_PREP(QCA808X_MASTER_SLAVE_SEED_CFG, seed_value) |
++ QCA808X_MASTER_SLAVE_SEED_ENABLE);
+ }
+
+ static int qca808x_config_init(struct phy_device *phydev)
+@@ -1771,12 +1766,7 @@ static int qca808x_config_init(struct ph
+ if (ret)
+ return ret;
+
+- /* Configure lower ramdom seed to make phy linked as slave mode */
+- ret = qca808x_phy_ms_random_seed_set(phydev);
+- if (ret)
+- return ret;
+-
+- /* Enable seed */
++ /* Enable seed and configure lower ramdom seed to make phy linked as slave mode */
+ ret = qca808x_phy_ms_seed_enable(phydev, true);
+ if (ret)
+ return ret;
+@@ -1821,7 +1811,6 @@ static int qca808x_read_status(struct ph
+ if (phydev->master_slave_state == MASTER_SLAVE_STATE_ERR) {
+ qca808x_phy_ms_seed_enable(phydev, false);
+ } else {
+- qca808x_phy_ms_random_seed_set(phydev);
+ qca808x_phy_ms_seed_enable(phydev, true);
+ }
+ }
--- /dev/null
+From 7cc3209558002d95c0d45a1276ba4f5f741eec42 Mon Sep 17 00:00:00 2001
+From: Luo Jie <quic_luoj@quicinc.com>
+Date: Sun, 16 Jul 2023 16:49:21 +0800
+Subject: [PATCH 3/6] net: phy: at803x: enable qca8081 slave seed conditionally
+
+qca8081 is the single port PHY, the slave prefer mode is used
+by default.
+
+if the phy master perfer mode is configured, the slave seed
+configuration should not be enabled, since the slave seed
+enablement is for making PHY linked as slave mode easily.
+
+disable slave seed if the master mode is preferred.
+
+Signed-off-by: Luo Jie <quic_luoj@quicinc.com>
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/phy/at803x.c | 25 ++++++++++++++++++++-----
+ 1 file changed, 20 insertions(+), 5 deletions(-)
+
+--- a/drivers/net/phy/at803x.c
++++ b/drivers/net/phy/at803x.c
+@@ -1745,6 +1745,12 @@ static int qca808x_phy_ms_seed_enable(st
+ QCA808X_MASTER_SLAVE_SEED_ENABLE);
+ }
+
++static bool qca808x_is_prefer_master(struct phy_device *phydev)
++{
++ return (phydev->master_slave_get == MASTER_SLAVE_CFG_MASTER_FORCE) ||
++ (phydev->master_slave_get == MASTER_SLAVE_CFG_MASTER_PREFERRED);
++}
++
+ static int qca808x_config_init(struct phy_device *phydev)
+ {
+ int ret;
+@@ -1766,11 +1772,17 @@ static int qca808x_config_init(struct ph
+ if (ret)
+ return ret;
+
+- /* Enable seed and configure lower ramdom seed to make phy linked as slave mode */
+- ret = qca808x_phy_ms_seed_enable(phydev, true);
+- if (ret)
++ ret = genphy_read_master_slave(phydev);
++ if (ret < 0)
+ return ret;
+
++ if (!qca808x_is_prefer_master(phydev)) {
++ /* Enable seed and configure lower ramdom seed to make phy linked as slave mode */
++ ret = qca808x_phy_ms_seed_enable(phydev, true);
++ if (ret)
++ return ret;
++ }
++
+ /* Configure adc threshold as 100mv for the link 10M */
+ return at803x_debug_reg_mask(phydev, QCA808X_PHY_DEBUG_ADC_THRESHOLD,
+ QCA808X_ADC_THRESHOLD_MASK, QCA808X_ADC_THRESHOLD_100MV);
+@@ -1802,13 +1814,16 @@ static int qca808x_read_status(struct ph
+ phydev->interface = PHY_INTERFACE_MODE_SGMII;
+ } else {
+ /* generate seed as a lower random value to make PHY linked as SLAVE easily,
+- * except for master/slave configuration fault detected.
++ * except for master/slave configuration fault detected or the master mode
++ * preferred.
++ *
+ * the reason for not putting this code into the function link_change_notify is
+ * the corner case where the link partner is also the qca8081 PHY and the seed
+ * value is configured as the same value, the link can't be up and no link change
+ * occurs.
+ */
+- if (phydev->master_slave_state == MASTER_SLAVE_STATE_ERR) {
++ if (phydev->master_slave_state == MASTER_SLAVE_STATE_ERR ||
++ qca808x_is_prefer_master(phydev)) {
+ qca808x_phy_ms_seed_enable(phydev, false);
+ } else {
+ qca808x_phy_ms_seed_enable(phydev, true);
--- /dev/null
+From fea7cfb83d1a2782e39cd101dd44ed2548539de5 Mon Sep 17 00:00:00 2001
+From: Luo Jie <quic_luoj@quicinc.com>
+Date: Sun, 16 Jul 2023 16:49:22 +0800
+Subject: [PATCH 4/6] net: phy: at803x: support qca8081 1G chip type
+
+The qca8081 1G chip version does not support 2.5 capability, which
+is distinguished from qca8081 2.5G chip according to the bit0 of
+register mmd7.0x901d, the 1G version chip also has the same PHY ID
+as the normal qca8081 2.5G chip.
+
+Signed-off-by: Luo Jie <quic_luoj@quicinc.com>
+Reviewed-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/phy/at803x.c | 15 +++++++++++++++
+ 1 file changed, 15 insertions(+)
+
+--- a/drivers/net/phy/at803x.c
++++ b/drivers/net/phy/at803x.c
+@@ -272,6 +272,10 @@
+ #define QCA808X_CDT_STATUS_STAT_OPEN 2
+ #define QCA808X_CDT_STATUS_STAT_SHORT 3
+
++/* QCA808X 1G chip type */
++#define QCA808X_PHY_MMD7_CHIP_TYPE 0x901d
++#define QCA808X_PHY_CHIP_TYPE_1G BIT(0)
++
+ MODULE_DESCRIPTION("Qualcomm Atheros AR803x and QCA808X PHY driver");
+ MODULE_AUTHOR("Matus Ujhelyi");
+ MODULE_LICENSE("GPL");
+@@ -2005,6 +2009,17 @@ static int qca808x_get_features(struct p
+ */
+ linkmode_set_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, phydev->supported);
+
++ /* As for the qca8081 1G version chip, the 2500baseT ability is also
++ * existed in the bit0 of MMD1.21, we need to remove it manually if
++ * it is the qca8081 1G chip according to the bit0 of MMD7.0x901d.
++ */
++ ret = phy_read_mmd(phydev, MDIO_MMD_AN, QCA808X_PHY_MMD7_CHIP_TYPE);
++ if (ret < 0)
++ return ret;
++
++ if (QCA808X_PHY_CHIP_TYPE_1G & ret)
++ linkmode_clear_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT, phydev->supported);
++
+ return 0;
+ }
+
--- /dev/null
+From df9401ff3e6eeaa42bfb06761967f1b71f5afce7 Mon Sep 17 00:00:00 2001
+From: Luo Jie <quic_luoj@quicinc.com>
+Date: Sun, 16 Jul 2023 16:49:23 +0800
+Subject: [PATCH 5/6] net: phy: at803x: remove qca8081 1G fast retrain and
+ slave seed config
+
+The fast retrain and slave seed configs are only applicable when the 2.5G
+ability is supported.
+
+Signed-off-by: Luo Jie <quic_luoj@quicinc.com>
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/phy/at803x.c | 50 +++++++++++++++++++++++++---------------
+ 1 file changed, 32 insertions(+), 18 deletions(-)
+
+--- a/drivers/net/phy/at803x.c
++++ b/drivers/net/phy/at803x.c
+@@ -1755,6 +1755,11 @@ static bool qca808x_is_prefer_master(str
+ (phydev->master_slave_get == MASTER_SLAVE_CFG_MASTER_PREFERRED);
+ }
+
++static bool qca808x_has_fast_retrain_or_slave_seed(struct phy_device *phydev)
++{
++ return linkmode_test_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT, phydev->supported);
++}
++
+ static int qca808x_config_init(struct phy_device *phydev)
+ {
+ int ret;
+@@ -1771,20 +1776,24 @@ static int qca808x_config_init(struct ph
+ if (ret)
+ return ret;
+
+- /* Config the fast retrain for the link 2500M */
+- ret = qca808x_phy_fast_retrain_config(phydev);
+- if (ret)
+- return ret;
+-
+- ret = genphy_read_master_slave(phydev);
+- if (ret < 0)
+- return ret;
+-
+- if (!qca808x_is_prefer_master(phydev)) {
+- /* Enable seed and configure lower ramdom seed to make phy linked as slave mode */
+- ret = qca808x_phy_ms_seed_enable(phydev, true);
++ if (qca808x_has_fast_retrain_or_slave_seed(phydev)) {
++ /* Config the fast retrain for the link 2500M */
++ ret = qca808x_phy_fast_retrain_config(phydev);
+ if (ret)
+ return ret;
++
++ ret = genphy_read_master_slave(phydev);
++ if (ret < 0)
++ return ret;
++
++ if (!qca808x_is_prefer_master(phydev)) {
++ /* Enable seed and configure lower ramdom seed to make phy
++ * linked as slave mode.
++ */
++ ret = qca808x_phy_ms_seed_enable(phydev, true);
++ if (ret)
++ return ret;
++ }
+ }
+
+ /* Configure adc threshold as 100mv for the link 10M */
+@@ -1826,11 +1835,13 @@ static int qca808x_read_status(struct ph
+ * value is configured as the same value, the link can't be up and no link change
+ * occurs.
+ */
+- if (phydev->master_slave_state == MASTER_SLAVE_STATE_ERR ||
+- qca808x_is_prefer_master(phydev)) {
+- qca808x_phy_ms_seed_enable(phydev, false);
+- } else {
+- qca808x_phy_ms_seed_enable(phydev, true);
++ if (qca808x_has_fast_retrain_or_slave_seed(phydev)) {
++ if (phydev->master_slave_state == MASTER_SLAVE_STATE_ERR ||
++ qca808x_is_prefer_master(phydev)) {
++ qca808x_phy_ms_seed_enable(phydev, false);
++ } else {
++ qca808x_phy_ms_seed_enable(phydev, true);
++ }
+ }
+ }
+
+@@ -1845,7 +1856,10 @@ static int qca808x_soft_reset(struct phy
+ if (ret < 0)
+ return ret;
+
+- return qca808x_phy_ms_seed_enable(phydev, true);
++ if (qca808x_has_fast_retrain_or_slave_seed(phydev))
++ ret = qca808x_phy_ms_seed_enable(phydev, true);
++
++ return ret;
+ }
+
+ static bool qca808x_cdt_fault_length_valid(int cdt_code)
--- /dev/null
+From 723970affdd8766fa0d91cd34bf2ffc861538b5f Mon Sep 17 00:00:00 2001
+From: Luo Jie <quic_luoj@quicinc.com>
+Date: Sun, 16 Jul 2023 16:49:24 +0800
+Subject: [PATCH 6/6] net: phy: at803x: add qca8081 fifo reset on the link
+ changed
+
+The qca8081 sgmii fifo needs to be reset on link down and
+released on the link up in case of any abnormal issue
+such as the packet blocked on the PHY.
+
+Signed-off-by: Luo Jie <quic_luoj@quicinc.com>
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Reviewed-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/phy/at803x.c | 14 ++++++++++++++
+ 1 file changed, 14 insertions(+)
+
+--- a/drivers/net/phy/at803x.c
++++ b/drivers/net/phy/at803x.c
+@@ -276,6 +276,9 @@
+ #define QCA808X_PHY_MMD7_CHIP_TYPE 0x901d
+ #define QCA808X_PHY_CHIP_TYPE_1G BIT(0)
+
++#define QCA8081_PHY_SERDES_MMD1_FIFO_CTRL 0x9072
++#define QCA8081_PHY_FIFO_RSTN BIT(11)
++
+ MODULE_DESCRIPTION("Qualcomm Atheros AR803x and QCA808X PHY driver");
+ MODULE_AUTHOR("Matus Ujhelyi");
+ MODULE_LICENSE("GPL");
+@@ -2037,6 +2040,16 @@ static int qca808x_get_features(struct p
+ return 0;
+ }
+
++static void qca808x_link_change_notify(struct phy_device *phydev)
++{
++ /* Assert interface sgmii fifo on link down, deassert it on link up,
++ * the interface device address is always phy address added by 1.
++ */
++ mdiobus_c45_modify_changed(phydev->mdio.bus, phydev->mdio.addr + 1,
++ MDIO_MMD_PMAPMD, QCA8081_PHY_SERDES_MMD1_FIFO_CTRL,
++ QCA8081_PHY_FIFO_RSTN, phydev->link ? QCA8081_PHY_FIFO_RSTN : 0);
++}
++
+ static struct phy_driver at803x_driver[] = {
+ {
+ /* Qualcomm Atheros AR8035 */
+@@ -2213,6 +2226,7 @@ static struct phy_driver at803x_driver[]
+ .soft_reset = qca808x_soft_reset,
+ .cable_test_start = qca808x_cable_test_start,
+ .cable_test_get_status = qca808x_cable_test_get_status,
++ .link_change_notify = qca808x_link_change_notify,
+ }, };
+
+ module_phy_driver(at803x_driver);
--- /dev/null
+From f8fdbf3389f44c7026f16e36cb1f2ff017f7f5b2 Mon Sep 17 00:00:00 2001
+From: Christian Marangi <ansuelsmth@gmail.com>
+Date: Fri, 8 Dec 2023 15:51:48 +0100
+Subject: [PATCH 01/13] net: phy: at803x: fix passing the wrong reference for
+ config_intr
+
+Fix passing the wrong reference for config_initr on passing the function
+pointer, drop the wrong & from at803x_config_intr in the PHY struct.
+
+Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/phy/at803x.c | 6 +++---
+ 1 file changed, 3 insertions(+), 3 deletions(-)
+
+--- a/drivers/net/phy/at803x.c
++++ b/drivers/net/phy/at803x.c
+@@ -2104,7 +2104,7 @@ static struct phy_driver at803x_driver[]
+ .write_page = at803x_write_page,
+ .get_features = at803x_get_features,
+ .read_status = at803x_read_status,
+- .config_intr = &at803x_config_intr,
++ .config_intr = at803x_config_intr,
+ .handle_interrupt = at803x_handle_interrupt,
+ .get_tunable = at803x_get_tunable,
+ .set_tunable = at803x_set_tunable,
+@@ -2134,7 +2134,7 @@ static struct phy_driver at803x_driver[]
+ .resume = at803x_resume,
+ .flags = PHY_POLL_CABLE_TEST,
+ /* PHY_BASIC_FEATURES */
+- .config_intr = &at803x_config_intr,
++ .config_intr = at803x_config_intr,
+ .handle_interrupt = at803x_handle_interrupt,
+ .cable_test_start = at803x_cable_test_start,
+ .cable_test_get_status = at803x_cable_test_get_status,
+@@ -2150,7 +2150,7 @@ static struct phy_driver at803x_driver[]
+ .resume = at803x_resume,
+ .flags = PHY_POLL_CABLE_TEST,
+ /* PHY_BASIC_FEATURES */
+- .config_intr = &at803x_config_intr,
++ .config_intr = at803x_config_intr,
+ .handle_interrupt = at803x_handle_interrupt,
+ .cable_test_start = at803x_cable_test_start,
+ .cable_test_get_status = at803x_cable_test_get_status,
--- /dev/null
+From 6a3b8c573b5a152a6aa7a0b54c5e18b84c6ba6f5 Mon Sep 17 00:00:00 2001
+From: Christian Marangi <ansuelsmth@gmail.com>
+Date: Fri, 8 Dec 2023 15:51:49 +0100
+Subject: [PATCH 02/13] net: phy: at803x: move disable WOL to specific at8031
+ probe
+
+Move the WOL disable call to specific at8031 probe to make at803x_probe
+more generic and drop extra check for PHY ID.
+
+Keep the same previous behaviour by first calling at803x_probe and then
+disabling WOL.
+
+Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/phy/at803x.c | 27 +++++++++++++++++----------
+ 1 file changed, 17 insertions(+), 10 deletions(-)
+
+--- a/drivers/net/phy/at803x.c
++++ b/drivers/net/phy/at803x.c
+@@ -886,15 +886,6 @@ static int at803x_probe(struct phy_devic
+ priv->is_fiber = true;
+ break;
+ }
+-
+- /* Disable WoL in 1588 register which is enabled
+- * by default
+- */
+- ret = phy_modify_mmd(phydev, MDIO_MMD_PCS,
+- AT803X_PHY_MMD3_WOL_CTRL,
+- AT803X_WOL_EN, 0);
+- if (ret)
+- return ret;
+ }
+
+ return 0;
+@@ -1591,6 +1582,22 @@ static int at803x_cable_test_start(struc
+ return 0;
+ }
+
++static int at8031_probe(struct phy_device *phydev)
++{
++ int ret;
++
++ ret = at803x_probe(phydev);
++ if (ret)
++ return ret;
++
++ /* Disable WoL in 1588 register which is enabled
++ * by default
++ */
++ return phy_modify_mmd(phydev, MDIO_MMD_PCS,
++ AT803X_PHY_MMD3_WOL_CTRL,
++ AT803X_WOL_EN, 0);
++}
++
+ static int qca83xx_config_init(struct phy_device *phydev)
+ {
+ u8 switch_revision;
+@@ -2092,7 +2099,7 @@ static struct phy_driver at803x_driver[]
+ PHY_ID_MATCH_EXACT(ATH8031_PHY_ID),
+ .name = "Qualcomm Atheros AR8031/AR8033",
+ .flags = PHY_POLL_CABLE_TEST,
+- .probe = at803x_probe,
++ .probe = at8031_probe,
+ .config_init = at803x_config_init,
+ .config_aneg = at803x_config_aneg,
+ .soft_reset = genphy_soft_reset,
--- /dev/null
+From 07b1ad83b9ed6db1735ba10baf67b7a565ac0cef Mon Sep 17 00:00:00 2001
+From: Christian Marangi <ansuelsmth@gmail.com>
+Date: Fri, 8 Dec 2023 15:51:50 +0100
+Subject: [PATCH 03/13] net: phy: at803x: raname hw_stats functions to qca83xx
+ specific name
+
+The function and the struct related to hw_stats were specific to qca83xx
+PHY but were called following the convention in the driver of calling
+everything with at803x prefix.
+
+To better organize the code, rename these function a more specific name
+to better describe that they are specific to 83xx PHY family.
+
+Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/phy/at803x.c | 44 ++++++++++++++++++++--------------------
+ 1 file changed, 22 insertions(+), 22 deletions(-)
+
+--- a/drivers/net/phy/at803x.c
++++ b/drivers/net/phy/at803x.c
+@@ -295,7 +295,7 @@ struct at803x_hw_stat {
+ enum stat_access_type access_type;
+ };
+
+-static struct at803x_hw_stat at803x_hw_stats[] = {
++static struct at803x_hw_stat qca83xx_hw_stats[] = {
+ { "phy_idle_errors", 0xa, GENMASK(7, 0), PHY},
+ { "phy_receive_errors", 0x15, GENMASK(15, 0), PHY},
+ { "eee_wake_errors", 0x16, GENMASK(15, 0), MMD},
+@@ -311,7 +311,7 @@ struct at803x_priv {
+ bool is_1000basex;
+ struct regulator_dev *vddio_rdev;
+ struct regulator_dev *vddh_rdev;
+- u64 stats[ARRAY_SIZE(at803x_hw_stats)];
++ u64 stats[ARRAY_SIZE(qca83xx_hw_stats)];
+ };
+
+ struct at803x_context {
+@@ -529,24 +529,24 @@ static void at803x_get_wol(struct phy_de
+ wol->wolopts |= WAKE_MAGIC;
+ }
+
+-static int at803x_get_sset_count(struct phy_device *phydev)
++static int qca83xx_get_sset_count(struct phy_device *phydev)
+ {
+- return ARRAY_SIZE(at803x_hw_stats);
++ return ARRAY_SIZE(qca83xx_hw_stats);
+ }
+
+-static void at803x_get_strings(struct phy_device *phydev, u8 *data)
++static void qca83xx_get_strings(struct phy_device *phydev, u8 *data)
+ {
+ int i;
+
+- for (i = 0; i < ARRAY_SIZE(at803x_hw_stats); i++) {
++ for (i = 0; i < ARRAY_SIZE(qca83xx_hw_stats); i++) {
+ strscpy(data + i * ETH_GSTRING_LEN,
+- at803x_hw_stats[i].string, ETH_GSTRING_LEN);
++ qca83xx_hw_stats[i].string, ETH_GSTRING_LEN);
+ }
+ }
+
+-static u64 at803x_get_stat(struct phy_device *phydev, int i)
++static u64 qca83xx_get_stat(struct phy_device *phydev, int i)
+ {
+- struct at803x_hw_stat stat = at803x_hw_stats[i];
++ struct at803x_hw_stat stat = qca83xx_hw_stats[i];
+ struct at803x_priv *priv = phydev->priv;
+ int val;
+ u64 ret;
+@@ -567,13 +567,13 @@ static u64 at803x_get_stat(struct phy_de
+ return ret;
+ }
+
+-static void at803x_get_stats(struct phy_device *phydev,
+- struct ethtool_stats *stats, u64 *data)
++static void qca83xx_get_stats(struct phy_device *phydev,
++ struct ethtool_stats *stats, u64 *data)
+ {
+ int i;
+
+- for (i = 0; i < ARRAY_SIZE(at803x_hw_stats); i++)
+- data[i] = at803x_get_stat(phydev, i);
++ for (i = 0; i < ARRAY_SIZE(qca83xx_hw_stats); i++)
++ data[i] = qca83xx_get_stat(phydev, i);
+ }
+
+ static int at803x_suspend(struct phy_device *phydev)
+@@ -2175,9 +2175,9 @@ static struct phy_driver at803x_driver[]
+ .flags = PHY_IS_INTERNAL,
+ .config_init = qca83xx_config_init,
+ .soft_reset = genphy_soft_reset,
+- .get_sset_count = at803x_get_sset_count,
+- .get_strings = at803x_get_strings,
+- .get_stats = at803x_get_stats,
++ .get_sset_count = qca83xx_get_sset_count,
++ .get_strings = qca83xx_get_strings,
++ .get_stats = qca83xx_get_stats,
+ .suspend = qca83xx_suspend,
+ .resume = qca83xx_resume,
+ }, {
+@@ -2191,9 +2191,9 @@ static struct phy_driver at803x_driver[]
+ .flags = PHY_IS_INTERNAL,
+ .config_init = qca83xx_config_init,
+ .soft_reset = genphy_soft_reset,
+- .get_sset_count = at803x_get_sset_count,
+- .get_strings = at803x_get_strings,
+- .get_stats = at803x_get_stats,
++ .get_sset_count = qca83xx_get_sset_count,
++ .get_strings = qca83xx_get_strings,
++ .get_stats = qca83xx_get_stats,
+ .suspend = qca83xx_suspend,
+ .resume = qca83xx_resume,
+ }, {
+@@ -2207,9 +2207,9 @@ static struct phy_driver at803x_driver[]
+ .flags = PHY_IS_INTERNAL,
+ .config_init = qca83xx_config_init,
+ .soft_reset = genphy_soft_reset,
+- .get_sset_count = at803x_get_sset_count,
+- .get_strings = at803x_get_strings,
+- .get_stats = at803x_get_stats,
++ .get_sset_count = qca83xx_get_sset_count,
++ .get_strings = qca83xx_get_strings,
++ .get_stats = qca83xx_get_stats,
+ .suspend = qca83xx_suspend,
+ .resume = qca83xx_resume,
+ }, {
--- /dev/null
+From d43cff3f82336c0bd965ea552232d9f4ddac71a6 Mon Sep 17 00:00:00 2001
+From: Christian Marangi <ansuelsmth@gmail.com>
+Date: Fri, 8 Dec 2023 15:51:51 +0100
+Subject: [PATCH 04/13] net: phy: at803x: move qca83xx specific check in
+ dedicated functions
+
+Rework qca83xx specific check to dedicated function to tidy things up
+and drop useless phy_id check.
+
+Also drop an useless link_change_notify for QCA8337 as it did nothing an
+returned early.
+
+Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/phy/at803x.c | 68 ++++++++++++++++++++++------------------
+ 1 file changed, 37 insertions(+), 31 deletions(-)
+
+--- a/drivers/net/phy/at803x.c
++++ b/drivers/net/phy/at803x.c
+@@ -1623,27 +1623,26 @@ static int qca83xx_config_init(struct ph
+ break;
+ }
+
++ /* Following original QCA sourcecode set port to prefer master */
++ phy_set_bits(phydev, MII_CTRL1000, CTL1000_PREFER_MASTER);
++
++ return 0;
++}
++
++static int qca8327_config_init(struct phy_device *phydev)
++{
+ /* QCA8327 require DAC amplitude adjustment for 100m set to +6%.
+ * Disable on init and enable only with 100m speed following
+ * qca original source code.
+ */
+- if (phydev->drv->phy_id == QCA8327_A_PHY_ID ||
+- phydev->drv->phy_id == QCA8327_B_PHY_ID)
+- at803x_debug_reg_mask(phydev, AT803X_DEBUG_ANALOG_TEST_CTRL,
+- QCA8327_DEBUG_MANU_CTRL_EN, 0);
++ at803x_debug_reg_mask(phydev, AT803X_DEBUG_ANALOG_TEST_CTRL,
++ QCA8327_DEBUG_MANU_CTRL_EN, 0);
+
+- /* Following original QCA sourcecode set port to prefer master */
+- phy_set_bits(phydev, MII_CTRL1000, CTL1000_PREFER_MASTER);
+-
+- return 0;
++ return qca83xx_config_init(phydev);
+ }
+
+ static void qca83xx_link_change_notify(struct phy_device *phydev)
+ {
+- /* QCA8337 doesn't require DAC Amplitude adjustement */
+- if (phydev->drv->phy_id == QCA8337_PHY_ID)
+- return;
+-
+ /* Set DAC Amplitude adjustment to +6% for 100m on link running */
+ if (phydev->state == PHY_RUNNING) {
+ if (phydev->speed == SPEED_100)
+@@ -1686,19 +1685,6 @@ static int qca83xx_resume(struct phy_dev
+
+ static int qca83xx_suspend(struct phy_device *phydev)
+ {
+- u16 mask = 0;
+-
+- /* Only QCA8337 support actual suspend.
+- * QCA8327 cause port unreliability when phy suspend
+- * is set.
+- */
+- if (phydev->drv->phy_id == QCA8337_PHY_ID) {
+- genphy_suspend(phydev);
+- } else {
+- mask |= ~(BMCR_SPEED1000 | BMCR_FULLDPLX);
+- phy_modify(phydev, MII_BMCR, mask, 0);
+- }
+-
+ at803x_debug_reg_mask(phydev, AT803X_DEBUG_REG_GREEN,
+ AT803X_DEBUG_GATE_CLK_IN1000, 0);
+
+@@ -1709,6 +1695,27 @@ static int qca83xx_suspend(struct phy_de
+ return 0;
+ }
+
++static int qca8337_suspend(struct phy_device *phydev)
++{
++ /* Only QCA8337 support actual suspend. */
++ genphy_suspend(phydev);
++
++ return qca83xx_suspend(phydev);
++}
++
++static int qca8327_suspend(struct phy_device *phydev)
++{
++ u16 mask = 0;
++
++ /* QCA8327 cause port unreliability when phy suspend
++ * is set.
++ */
++ mask |= ~(BMCR_SPEED1000 | BMCR_FULLDPLX);
++ phy_modify(phydev, MII_BMCR, mask, 0);
++
++ return qca83xx_suspend(phydev);
++}
++
+ static int qca808x_phy_fast_retrain_config(struct phy_device *phydev)
+ {
+ int ret;
+@@ -2170,7 +2177,6 @@ static struct phy_driver at803x_driver[]
+ .phy_id_mask = QCA8K_PHY_ID_MASK,
+ .name = "Qualcomm Atheros 8337 internal PHY",
+ /* PHY_GBIT_FEATURES */
+- .link_change_notify = qca83xx_link_change_notify,
+ .probe = at803x_probe,
+ .flags = PHY_IS_INTERNAL,
+ .config_init = qca83xx_config_init,
+@@ -2178,7 +2184,7 @@ static struct phy_driver at803x_driver[]
+ .get_sset_count = qca83xx_get_sset_count,
+ .get_strings = qca83xx_get_strings,
+ .get_stats = qca83xx_get_stats,
+- .suspend = qca83xx_suspend,
++ .suspend = qca8337_suspend,
+ .resume = qca83xx_resume,
+ }, {
+ /* QCA8327-A from switch QCA8327-AL1A */
+@@ -2189,12 +2195,12 @@ static struct phy_driver at803x_driver[]
+ .link_change_notify = qca83xx_link_change_notify,
+ .probe = at803x_probe,
+ .flags = PHY_IS_INTERNAL,
+- .config_init = qca83xx_config_init,
++ .config_init = qca8327_config_init,
+ .soft_reset = genphy_soft_reset,
+ .get_sset_count = qca83xx_get_sset_count,
+ .get_strings = qca83xx_get_strings,
+ .get_stats = qca83xx_get_stats,
+- .suspend = qca83xx_suspend,
++ .suspend = qca8327_suspend,
+ .resume = qca83xx_resume,
+ }, {
+ /* QCA8327-B from switch QCA8327-BL1A */
+@@ -2205,12 +2211,12 @@ static struct phy_driver at803x_driver[]
+ .link_change_notify = qca83xx_link_change_notify,
+ .probe = at803x_probe,
+ .flags = PHY_IS_INTERNAL,
+- .config_init = qca83xx_config_init,
++ .config_init = qca8327_config_init,
+ .soft_reset = genphy_soft_reset,
+ .get_sset_count = qca83xx_get_sset_count,
+ .get_strings = qca83xx_get_strings,
+ .get_stats = qca83xx_get_stats,
+- .suspend = qca83xx_suspend,
++ .suspend = qca8327_suspend,
+ .resume = qca83xx_resume,
+ }, {
+ /* Qualcomm QCA8081 */
--- /dev/null
+From 900eef75cc5018e149c52fe305c9c3fe424c52a7 Mon Sep 17 00:00:00 2001
+From: Christian Marangi <ansuelsmth@gmail.com>
+Date: Fri, 8 Dec 2023 15:51:52 +0100
+Subject: [PATCH 05/13] net: phy: at803x: move specific DT option for at8031 to
+ specific probe
+
+Move specific DT options for at8031 to specific probe to tidy things up
+and make at803x_parse_dt more generic.
+
+Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/phy/at803x.c | 55 ++++++++++++++++++++++------------------
+ 1 file changed, 31 insertions(+), 24 deletions(-)
+
+--- a/drivers/net/phy/at803x.c
++++ b/drivers/net/phy/at803x.c
+@@ -825,30 +825,6 @@ static int at803x_parse_dt(struct phy_de
+ }
+ }
+
+- /* Only supported on AR8031/AR8033, the AR8030/AR8035 use strapping
+- * options.
+- */
+- if (phydev->drv->phy_id == ATH8031_PHY_ID) {
+- if (of_property_read_bool(node, "qca,keep-pll-enabled"))
+- priv->flags |= AT803X_KEEP_PLL_ENABLED;
+-
+- ret = at8031_register_regulators(phydev);
+- if (ret < 0)
+- return ret;
+-
+- ret = devm_regulator_get_enable_optional(&phydev->mdio.dev,
+- "vddio");
+- if (ret) {
+- phydev_err(phydev, "failed to get VDDIO regulator\n");
+- return ret;
+- }
+-
+- /* Only AR8031/8033 support 1000Base-X for SFP modules */
+- ret = phy_sfp_probe(phydev, &at803x_sfp_ops);
+- if (ret < 0)
+- return ret;
+- }
+-
+ return 0;
+ }
+
+@@ -1582,6 +1558,30 @@ static int at803x_cable_test_start(struc
+ return 0;
+ }
+
++static int at8031_parse_dt(struct phy_device *phydev)
++{
++ struct device_node *node = phydev->mdio.dev.of_node;
++ struct at803x_priv *priv = phydev->priv;
++ int ret;
++
++ if (of_property_read_bool(node, "qca,keep-pll-enabled"))
++ priv->flags |= AT803X_KEEP_PLL_ENABLED;
++
++ ret = at8031_register_regulators(phydev);
++ if (ret < 0)
++ return ret;
++
++ ret = devm_regulator_get_enable_optional(&phydev->mdio.dev,
++ "vddio");
++ if (ret) {
++ phydev_err(phydev, "failed to get VDDIO regulator\n");
++ return ret;
++ }
++
++ /* Only AR8031/8033 support 1000Base-X for SFP modules */
++ return phy_sfp_probe(phydev, &at803x_sfp_ops);
++}
++
+ static int at8031_probe(struct phy_device *phydev)
+ {
+ int ret;
+@@ -1590,6 +1590,13 @@ static int at8031_probe(struct phy_devic
+ if (ret)
+ return ret;
+
++ /* Only supported on AR8031/AR8033, the AR8030/AR8035 use strapping
++ * options.
++ */
++ ret = at8031_parse_dt(phydev);
++ if (ret)
++ return ret;
++
+ /* Disable WoL in 1588 register which is enabled
+ * by default
+ */
--- /dev/null
+From 25d2ba94005fac18fe68878cddff59a67e115554 Mon Sep 17 00:00:00 2001
+From: Christian Marangi <ansuelsmth@gmail.com>
+Date: Fri, 8 Dec 2023 15:51:53 +0100
+Subject: [PATCH 06/13] net: phy: at803x: move specific at8031 probe mode check
+ to dedicated probe
+
+Move specific at8031 probe mode check to dedicated probe to make
+at803x_probe more generic and keep code tidy.
+
+Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/phy/at803x.c | 39 +++++++++++++++++++--------------------
+ 1 file changed, 19 insertions(+), 20 deletions(-)
+
+--- a/drivers/net/phy/at803x.c
++++ b/drivers/net/phy/at803x.c
+@@ -844,26 +844,6 @@ static int at803x_probe(struct phy_devic
+ if (ret)
+ return ret;
+
+- if (phydev->drv->phy_id == ATH8031_PHY_ID) {
+- int ccr = phy_read(phydev, AT803X_REG_CHIP_CONFIG);
+- int mode_cfg;
+-
+- if (ccr < 0)
+- return ccr;
+- mode_cfg = ccr & AT803X_MODE_CFG_MASK;
+-
+- switch (mode_cfg) {
+- case AT803X_MODE_CFG_BX1000_RGMII_50OHM:
+- case AT803X_MODE_CFG_BX1000_RGMII_75OHM:
+- priv->is_1000basex = true;
+- fallthrough;
+- case AT803X_MODE_CFG_FX100_RGMII_50OHM:
+- case AT803X_MODE_CFG_FX100_RGMII_75OHM:
+- priv->is_fiber = true;
+- break;
+- }
+- }
+-
+ return 0;
+ }
+
+@@ -1584,6 +1564,9 @@ static int at8031_parse_dt(struct phy_de
+
+ static int at8031_probe(struct phy_device *phydev)
+ {
++ struct at803x_priv *priv = phydev->priv;
++ int mode_cfg;
++ int ccr;
+ int ret;
+
+ ret = at803x_probe(phydev);
+@@ -1597,6 +1580,22 @@ static int at8031_probe(struct phy_devic
+ if (ret)
+ return ret;
+
++ ccr = phy_read(phydev, AT803X_REG_CHIP_CONFIG);
++ if (ccr < 0)
++ return ccr;
++ mode_cfg = ccr & AT803X_MODE_CFG_MASK;
++
++ switch (mode_cfg) {
++ case AT803X_MODE_CFG_BX1000_RGMII_50OHM:
++ case AT803X_MODE_CFG_BX1000_RGMII_75OHM:
++ priv->is_1000basex = true;
++ fallthrough;
++ case AT803X_MODE_CFG_FX100_RGMII_50OHM:
++ case AT803X_MODE_CFG_FX100_RGMII_75OHM:
++ priv->is_fiber = true;
++ break;
++ }
++
+ /* Disable WoL in 1588 register which is enabled
+ * by default
+ */
--- /dev/null
+From 3ae3bc426eaf57ca8f53d75777d9a5ef779bc7b7 Mon Sep 17 00:00:00 2001
+From: Christian Marangi <ansuelsmth@gmail.com>
+Date: Fri, 8 Dec 2023 15:51:54 +0100
+Subject: [PATCH 07/13] net: phy: at803x: move specific at8031 config_init to
+ dedicated function
+
+Move specific at8031 config_init to dedicated function to make
+at803x_config_init more generic and tidy things up.
+
+Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/phy/at803x.c | 45 ++++++++++++++++++++++------------------
+ 1 file changed, 25 insertions(+), 20 deletions(-)
+
+--- a/drivers/net/phy/at803x.c
++++ b/drivers/net/phy/at803x.c
+@@ -951,27 +951,8 @@ static int at803x_hibernation_mode_confi
+
+ static int at803x_config_init(struct phy_device *phydev)
+ {
+- struct at803x_priv *priv = phydev->priv;
+ int ret;
+
+- if (phydev->drv->phy_id == ATH8031_PHY_ID) {
+- /* Some bootloaders leave the fiber page selected.
+- * Switch to the appropriate page (fiber or copper), as otherwise we
+- * read the PHY capabilities from the wrong page.
+- */
+- phy_lock_mdio_bus(phydev);
+- ret = at803x_write_page(phydev,
+- priv->is_fiber ? AT803X_PAGE_FIBER :
+- AT803X_PAGE_COPPER);
+- phy_unlock_mdio_bus(phydev);
+- if (ret)
+- return ret;
+-
+- ret = at8031_pll_config(phydev);
+- if (ret < 0)
+- return ret;
+- }
+-
+ /* The RX and TX delay default is:
+ * after HW reset: RX delay enabled and TX delay disabled
+ * after SW reset: RX delay enabled, while TX delay retains the
+@@ -1604,6 +1585,30 @@ static int at8031_probe(struct phy_devic
+ AT803X_WOL_EN, 0);
+ }
+
++static int at8031_config_init(struct phy_device *phydev)
++{
++ struct at803x_priv *priv = phydev->priv;
++ int ret;
++
++ /* Some bootloaders leave the fiber page selected.
++ * Switch to the appropriate page (fiber or copper), as otherwise we
++ * read the PHY capabilities from the wrong page.
++ */
++ phy_lock_mdio_bus(phydev);
++ ret = at803x_write_page(phydev,
++ priv->is_fiber ? AT803X_PAGE_FIBER :
++ AT803X_PAGE_COPPER);
++ phy_unlock_mdio_bus(phydev);
++ if (ret)
++ return ret;
++
++ ret = at8031_pll_config(phydev);
++ if (ret < 0)
++ return ret;
++
++ return at803x_config_init(phydev);
++}
++
+ static int qca83xx_config_init(struct phy_device *phydev)
+ {
+ u8 switch_revision;
+@@ -2113,7 +2118,7 @@ static struct phy_driver at803x_driver[]
+ .name = "Qualcomm Atheros AR8031/AR8033",
+ .flags = PHY_POLL_CABLE_TEST,
+ .probe = at8031_probe,
+- .config_init = at803x_config_init,
++ .config_init = at8031_config_init,
+ .config_aneg = at803x_config_aneg,
+ .soft_reset = genphy_soft_reset,
+ .set_wol = at803x_set_wol,
--- /dev/null
+From 27b89c9dc1b0393090d68d651b82f30ad2696baa Mon Sep 17 00:00:00 2001
+From: Christian Marangi <ansuelsmth@gmail.com>
+Date: Fri, 8 Dec 2023 15:51:55 +0100
+Subject: [PATCH 08/13] net: phy: at803x: move specific at8031 WOL bits to
+ dedicated function
+
+Move specific at8031 WOL enable/disable to dedicated function to make
+at803x_set_wol more generic.
+
+This is needed in preparation for PHY driver split as qca8081 share the
+same function to toggle WOL settings.
+
+In this new implementation WOL module in at8031 is enabled after the
+generic interrupt is setup. This should not cause any problem as the
+WOL_INT has a separate implementation and only relay on MAC bits.
+
+Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/phy/at803x.c | 42 ++++++++++++++++++++++++----------------
+ 1 file changed, 25 insertions(+), 17 deletions(-)
+
+--- a/drivers/net/phy/at803x.c
++++ b/drivers/net/phy/at803x.c
+@@ -466,27 +466,11 @@ static int at803x_set_wol(struct phy_dev
+ phy_write_mmd(phydev, MDIO_MMD_PCS, offsets[i],
+ mac[(i * 2) + 1] | (mac[(i * 2)] << 8));
+
+- /* Enable WOL function for 1588 */
+- if (phydev->drv->phy_id == ATH8031_PHY_ID) {
+- ret = phy_modify_mmd(phydev, MDIO_MMD_PCS,
+- AT803X_PHY_MMD3_WOL_CTRL,
+- 0, AT803X_WOL_EN);
+- if (ret)
+- return ret;
+- }
+ /* Enable WOL interrupt */
+ ret = phy_modify(phydev, AT803X_INTR_ENABLE, 0, AT803X_INTR_ENABLE_WOL);
+ if (ret)
+ return ret;
+ } else {
+- /* Disable WoL function for 1588 */
+- if (phydev->drv->phy_id == ATH8031_PHY_ID) {
+- ret = phy_modify_mmd(phydev, MDIO_MMD_PCS,
+- AT803X_PHY_MMD3_WOL_CTRL,
+- AT803X_WOL_EN, 0);
+- if (ret)
+- return ret;
+- }
+ /* Disable WOL interrupt */
+ ret = phy_modify(phydev, AT803X_INTR_ENABLE, AT803X_INTR_ENABLE_WOL, 0);
+ if (ret)
+@@ -1609,6 +1593,30 @@ static int at8031_config_init(struct phy
+ return at803x_config_init(phydev);
+ }
+
++static int at8031_set_wol(struct phy_device *phydev,
++ struct ethtool_wolinfo *wol)
++{
++ int ret;
++
++ /* First setup MAC address and enable WOL interrupt */
++ ret = at803x_set_wol(phydev, wol);
++ if (ret)
++ return ret;
++
++ if (wol->wolopts & WAKE_MAGIC)
++ /* Enable WOL function for 1588 */
++ ret = phy_modify_mmd(phydev, MDIO_MMD_PCS,
++ AT803X_PHY_MMD3_WOL_CTRL,
++ 0, AT803X_WOL_EN);
++ else
++ /* Disable WoL function for 1588 */
++ ret = phy_modify_mmd(phydev, MDIO_MMD_PCS,
++ AT803X_PHY_MMD3_WOL_CTRL,
++ AT803X_WOL_EN, 0);
++
++ return ret;
++}
++
+ static int qca83xx_config_init(struct phy_device *phydev)
+ {
+ u8 switch_revision;
+@@ -2121,7 +2129,7 @@ static struct phy_driver at803x_driver[]
+ .config_init = at8031_config_init,
+ .config_aneg = at803x_config_aneg,
+ .soft_reset = genphy_soft_reset,
+- .set_wol = at803x_set_wol,
++ .set_wol = at8031_set_wol,
+ .get_wol = at803x_get_wol,
+ .suspend = at803x_suspend,
+ .resume = at803x_resume,
--- /dev/null
+From 30dd62191d3dd97c08f7f9dc9ce77ffab457e4fb Mon Sep 17 00:00:00 2001
+From: Christian Marangi <ansuelsmth@gmail.com>
+Date: Fri, 8 Dec 2023 15:51:56 +0100
+Subject: [PATCH 09/13] net: phy: at803x: move specific at8031 config_intr to
+ dedicated function
+
+Move specific at8031 config_intr bits to dedicated function to make
+at803x_config_initr more generic.
+
+This is needed in preparation for PHY driver split as qca8081 share the
+same function to setup interrupts.
+
+Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/phy/at803x.c | 30 ++++++++++++++++++++++++------
+ 1 file changed, 24 insertions(+), 6 deletions(-)
+
+--- a/drivers/net/phy/at803x.c
++++ b/drivers/net/phy/at803x.c
+@@ -990,7 +990,6 @@ static int at803x_ack_interrupt(struct p
+
+ static int at803x_config_intr(struct phy_device *phydev)
+ {
+- struct at803x_priv *priv = phydev->priv;
+ int err;
+ int value;
+
+@@ -1007,10 +1006,6 @@ static int at803x_config_intr(struct phy
+ value |= AT803X_INTR_ENABLE_DUPLEX_CHANGED;
+ value |= AT803X_INTR_ENABLE_LINK_FAIL;
+ value |= AT803X_INTR_ENABLE_LINK_SUCCESS;
+- if (priv->is_fiber) {
+- value |= AT803X_INTR_ENABLE_LINK_FAIL_BX;
+- value |= AT803X_INTR_ENABLE_LINK_SUCCESS_BX;
+- }
+
+ err = phy_write(phydev, AT803X_INTR_ENABLE, value);
+ } else {
+@@ -1617,6 +1612,29 @@ static int at8031_set_wol(struct phy_dev
+ return ret;
+ }
+
++static int at8031_config_intr(struct phy_device *phydev)
++{
++ struct at803x_priv *priv = phydev->priv;
++ int err, value = 0;
++
++ if (phydev->interrupts == PHY_INTERRUPT_ENABLED &&
++ priv->is_fiber) {
++ /* Clear any pending interrupts */
++ err = at803x_ack_interrupt(phydev);
++ if (err)
++ return err;
++
++ value |= AT803X_INTR_ENABLE_LINK_FAIL_BX;
++ value |= AT803X_INTR_ENABLE_LINK_SUCCESS_BX;
++
++ err = phy_set_bits(phydev, AT803X_INTR_ENABLE, value);
++ if (err)
++ return err;
++ }
++
++ return at803x_config_intr(phydev);
++}
++
+ static int qca83xx_config_init(struct phy_device *phydev)
+ {
+ u8 switch_revision;
+@@ -2137,7 +2155,7 @@ static struct phy_driver at803x_driver[]
+ .write_page = at803x_write_page,
+ .get_features = at803x_get_features,
+ .read_status = at803x_read_status,
+- .config_intr = at803x_config_intr,
++ .config_intr = at8031_config_intr,
+ .handle_interrupt = at803x_handle_interrupt,
+ .get_tunable = at803x_get_tunable,
+ .set_tunable = at803x_set_tunable,
--- /dev/null
+From a5ab9d8e7ae0da8328ac1637a9755311508dc8ab Mon Sep 17 00:00:00 2001
+From: Christian Marangi <ansuelsmth@gmail.com>
+Date: Fri, 8 Dec 2023 15:51:57 +0100
+Subject: [PATCH 10/13] net: phy: at803x: make at8031 related DT functions name
+ more specific
+
+Rename at8031 related DT function name to a more specific name
+referencing they are only related to at8031 and not to the generic
+at803x PHY family.
+
+Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/phy/at803x.c | 16 ++++++++--------
+ 1 file changed, 8 insertions(+), 8 deletions(-)
+
+--- a/drivers/net/phy/at803x.c
++++ b/drivers/net/phy/at803x.c
+@@ -583,7 +583,7 @@ static int at803x_resume(struct phy_devi
+ return phy_modify(phydev, MII_BMCR, BMCR_PDOWN | BMCR_ISOLATE, 0);
+ }
+
+-static int at803x_rgmii_reg_set_voltage_sel(struct regulator_dev *rdev,
++static int at8031_rgmii_reg_set_voltage_sel(struct regulator_dev *rdev,
+ unsigned int selector)
+ {
+ struct phy_device *phydev = rdev_get_drvdata(rdev);
+@@ -596,7 +596,7 @@ static int at803x_rgmii_reg_set_voltage_
+ AT803X_DEBUG_RGMII_1V8, 0);
+ }
+
+-static int at803x_rgmii_reg_get_voltage_sel(struct regulator_dev *rdev)
++static int at8031_rgmii_reg_get_voltage_sel(struct regulator_dev *rdev)
+ {
+ struct phy_device *phydev = rdev_get_drvdata(rdev);
+ int val;
+@@ -610,8 +610,8 @@ static int at803x_rgmii_reg_get_voltage_
+
+ static const struct regulator_ops vddio_regulator_ops = {
+ .list_voltage = regulator_list_voltage_table,
+- .set_voltage_sel = at803x_rgmii_reg_set_voltage_sel,
+- .get_voltage_sel = at803x_rgmii_reg_get_voltage_sel,
++ .set_voltage_sel = at8031_rgmii_reg_set_voltage_sel,
++ .get_voltage_sel = at8031_rgmii_reg_get_voltage_sel,
+ };
+
+ static const unsigned int vddio_voltage_table[] = {
+@@ -666,7 +666,7 @@ static int at8031_register_regulators(st
+ return 0;
+ }
+
+-static int at803x_sfp_insert(void *upstream, const struct sfp_eeprom_id *id)
++static int at8031_sfp_insert(void *upstream, const struct sfp_eeprom_id *id)
+ {
+ struct phy_device *phydev = upstream;
+ __ETHTOOL_DECLARE_LINK_MODE_MASK(phy_support);
+@@ -710,10 +710,10 @@ static int at803x_sfp_insert(void *upstr
+ return 0;
+ }
+
+-static const struct sfp_upstream_ops at803x_sfp_ops = {
++static const struct sfp_upstream_ops at8031_sfp_ops = {
+ .attach = phy_sfp_attach,
+ .detach = phy_sfp_detach,
+- .module_insert = at803x_sfp_insert,
++ .module_insert = at8031_sfp_insert,
+ };
+
+ static int at803x_parse_dt(struct phy_device *phydev)
+@@ -1519,7 +1519,7 @@ static int at8031_parse_dt(struct phy_de
+ }
+
+ /* Only AR8031/8033 support 1000Base-X for SFP modules */
+- return phy_sfp_probe(phydev, &at803x_sfp_ops);
++ return phy_sfp_probe(phydev, &at8031_sfp_ops);
+ }
+
+ static int at8031_probe(struct phy_device *phydev)
--- /dev/null
+From f932a6dc8bae0dae9645b5b1b4c65aed8a8acb2a Mon Sep 17 00:00:00 2001
+From: Christian Marangi <ansuelsmth@gmail.com>
+Date: Fri, 8 Dec 2023 15:51:58 +0100
+Subject: [PATCH 11/13] net: phy: at803x: move at8031 functions in dedicated
+ section
+
+Move at8031 functions in dedicated section with dedicated at8031
+parse_dt and probe.
+
+Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/phy/at803x.c | 266 +++++++++++++++++++--------------------
+ 1 file changed, 133 insertions(+), 133 deletions(-)
+
+--- a/drivers/net/phy/at803x.c
++++ b/drivers/net/phy/at803x.c
+@@ -583,139 +583,6 @@ static int at803x_resume(struct phy_devi
+ return phy_modify(phydev, MII_BMCR, BMCR_PDOWN | BMCR_ISOLATE, 0);
+ }
+
+-static int at8031_rgmii_reg_set_voltage_sel(struct regulator_dev *rdev,
+- unsigned int selector)
+-{
+- struct phy_device *phydev = rdev_get_drvdata(rdev);
+-
+- if (selector)
+- return at803x_debug_reg_mask(phydev, AT803X_DEBUG_REG_1F,
+- 0, AT803X_DEBUG_RGMII_1V8);
+- else
+- return at803x_debug_reg_mask(phydev, AT803X_DEBUG_REG_1F,
+- AT803X_DEBUG_RGMII_1V8, 0);
+-}
+-
+-static int at8031_rgmii_reg_get_voltage_sel(struct regulator_dev *rdev)
+-{
+- struct phy_device *phydev = rdev_get_drvdata(rdev);
+- int val;
+-
+- val = at803x_debug_reg_read(phydev, AT803X_DEBUG_REG_1F);
+- if (val < 0)
+- return val;
+-
+- return (val & AT803X_DEBUG_RGMII_1V8) ? 1 : 0;
+-}
+-
+-static const struct regulator_ops vddio_regulator_ops = {
+- .list_voltage = regulator_list_voltage_table,
+- .set_voltage_sel = at8031_rgmii_reg_set_voltage_sel,
+- .get_voltage_sel = at8031_rgmii_reg_get_voltage_sel,
+-};
+-
+-static const unsigned int vddio_voltage_table[] = {
+- 1500000,
+- 1800000,
+-};
+-
+-static const struct regulator_desc vddio_desc = {
+- .name = "vddio",
+- .of_match = of_match_ptr("vddio-regulator"),
+- .n_voltages = ARRAY_SIZE(vddio_voltage_table),
+- .volt_table = vddio_voltage_table,
+- .ops = &vddio_regulator_ops,
+- .type = REGULATOR_VOLTAGE,
+- .owner = THIS_MODULE,
+-};
+-
+-static const struct regulator_ops vddh_regulator_ops = {
+-};
+-
+-static const struct regulator_desc vddh_desc = {
+- .name = "vddh",
+- .of_match = of_match_ptr("vddh-regulator"),
+- .n_voltages = 1,
+- .fixed_uV = 2500000,
+- .ops = &vddh_regulator_ops,
+- .type = REGULATOR_VOLTAGE,
+- .owner = THIS_MODULE,
+-};
+-
+-static int at8031_register_regulators(struct phy_device *phydev)
+-{
+- struct at803x_priv *priv = phydev->priv;
+- struct device *dev = &phydev->mdio.dev;
+- struct regulator_config config = { };
+-
+- config.dev = dev;
+- config.driver_data = phydev;
+-
+- priv->vddio_rdev = devm_regulator_register(dev, &vddio_desc, &config);
+- if (IS_ERR(priv->vddio_rdev)) {
+- phydev_err(phydev, "failed to register VDDIO regulator\n");
+- return PTR_ERR(priv->vddio_rdev);
+- }
+-
+- priv->vddh_rdev = devm_regulator_register(dev, &vddh_desc, &config);
+- if (IS_ERR(priv->vddh_rdev)) {
+- phydev_err(phydev, "failed to register VDDH regulator\n");
+- return PTR_ERR(priv->vddh_rdev);
+- }
+-
+- return 0;
+-}
+-
+-static int at8031_sfp_insert(void *upstream, const struct sfp_eeprom_id *id)
+-{
+- struct phy_device *phydev = upstream;
+- __ETHTOOL_DECLARE_LINK_MODE_MASK(phy_support);
+- __ETHTOOL_DECLARE_LINK_MODE_MASK(sfp_support);
+- DECLARE_PHY_INTERFACE_MASK(interfaces);
+- phy_interface_t iface;
+-
+- linkmode_zero(phy_support);
+- phylink_set(phy_support, 1000baseX_Full);
+- phylink_set(phy_support, 1000baseT_Full);
+- phylink_set(phy_support, Autoneg);
+- phylink_set(phy_support, Pause);
+- phylink_set(phy_support, Asym_Pause);
+-
+- linkmode_zero(sfp_support);
+- sfp_parse_support(phydev->sfp_bus, id, sfp_support, interfaces);
+- /* Some modules support 10G modes as well as others we support.
+- * Mask out non-supported modes so the correct interface is picked.
+- */
+- linkmode_and(sfp_support, phy_support, sfp_support);
+-
+- if (linkmode_empty(sfp_support)) {
+- dev_err(&phydev->mdio.dev, "incompatible SFP module inserted\n");
+- return -EINVAL;
+- }
+-
+- iface = sfp_select_interface(phydev->sfp_bus, sfp_support);
+-
+- /* Only 1000Base-X is supported by AR8031/8033 as the downstream SerDes
+- * interface for use with SFP modules.
+- * However, some copper modules detected as having a preferred SGMII
+- * interface do default to and function in 1000Base-X mode, so just
+- * print a warning and allow such modules, as they may have some chance
+- * of working.
+- */
+- if (iface == PHY_INTERFACE_MODE_SGMII)
+- dev_warn(&phydev->mdio.dev, "module may not function if 1000Base-X not supported\n");
+- else if (iface != PHY_INTERFACE_MODE_1000BASEX)
+- return -EINVAL;
+-
+- return 0;
+-}
+-
+-static const struct sfp_upstream_ops at8031_sfp_ops = {
+- .attach = phy_sfp_attach,
+- .detach = phy_sfp_detach,
+- .module_insert = at8031_sfp_insert,
+-};
+-
+ static int at803x_parse_dt(struct phy_device *phydev)
+ {
+ struct device_node *node = phydev->mdio.dev.of_node;
+@@ -1498,6 +1365,139 @@ static int at803x_cable_test_start(struc
+ return 0;
+ }
+
++static int at8031_rgmii_reg_set_voltage_sel(struct regulator_dev *rdev,
++ unsigned int selector)
++{
++ struct phy_device *phydev = rdev_get_drvdata(rdev);
++
++ if (selector)
++ return at803x_debug_reg_mask(phydev, AT803X_DEBUG_REG_1F,
++ 0, AT803X_DEBUG_RGMII_1V8);
++ else
++ return at803x_debug_reg_mask(phydev, AT803X_DEBUG_REG_1F,
++ AT803X_DEBUG_RGMII_1V8, 0);
++}
++
++static int at8031_rgmii_reg_get_voltage_sel(struct regulator_dev *rdev)
++{
++ struct phy_device *phydev = rdev_get_drvdata(rdev);
++ int val;
++
++ val = at803x_debug_reg_read(phydev, AT803X_DEBUG_REG_1F);
++ if (val < 0)
++ return val;
++
++ return (val & AT803X_DEBUG_RGMII_1V8) ? 1 : 0;
++}
++
++static const struct regulator_ops vddio_regulator_ops = {
++ .list_voltage = regulator_list_voltage_table,
++ .set_voltage_sel = at8031_rgmii_reg_set_voltage_sel,
++ .get_voltage_sel = at8031_rgmii_reg_get_voltage_sel,
++};
++
++static const unsigned int vddio_voltage_table[] = {
++ 1500000,
++ 1800000,
++};
++
++static const struct regulator_desc vddio_desc = {
++ .name = "vddio",
++ .of_match = of_match_ptr("vddio-regulator"),
++ .n_voltages = ARRAY_SIZE(vddio_voltage_table),
++ .volt_table = vddio_voltage_table,
++ .ops = &vddio_regulator_ops,
++ .type = REGULATOR_VOLTAGE,
++ .owner = THIS_MODULE,
++};
++
++static const struct regulator_ops vddh_regulator_ops = {
++};
++
++static const struct regulator_desc vddh_desc = {
++ .name = "vddh",
++ .of_match = of_match_ptr("vddh-regulator"),
++ .n_voltages = 1,
++ .fixed_uV = 2500000,
++ .ops = &vddh_regulator_ops,
++ .type = REGULATOR_VOLTAGE,
++ .owner = THIS_MODULE,
++};
++
++static int at8031_register_regulators(struct phy_device *phydev)
++{
++ struct at803x_priv *priv = phydev->priv;
++ struct device *dev = &phydev->mdio.dev;
++ struct regulator_config config = { };
++
++ config.dev = dev;
++ config.driver_data = phydev;
++
++ priv->vddio_rdev = devm_regulator_register(dev, &vddio_desc, &config);
++ if (IS_ERR(priv->vddio_rdev)) {
++ phydev_err(phydev, "failed to register VDDIO regulator\n");
++ return PTR_ERR(priv->vddio_rdev);
++ }
++
++ priv->vddh_rdev = devm_regulator_register(dev, &vddh_desc, &config);
++ if (IS_ERR(priv->vddh_rdev)) {
++ phydev_err(phydev, "failed to register VDDH regulator\n");
++ return PTR_ERR(priv->vddh_rdev);
++ }
++
++ return 0;
++}
++
++static int at8031_sfp_insert(void *upstream, const struct sfp_eeprom_id *id)
++{
++ struct phy_device *phydev = upstream;
++ __ETHTOOL_DECLARE_LINK_MODE_MASK(phy_support);
++ __ETHTOOL_DECLARE_LINK_MODE_MASK(sfp_support);
++ DECLARE_PHY_INTERFACE_MASK(interfaces);
++ phy_interface_t iface;
++
++ linkmode_zero(phy_support);
++ phylink_set(phy_support, 1000baseX_Full);
++ phylink_set(phy_support, 1000baseT_Full);
++ phylink_set(phy_support, Autoneg);
++ phylink_set(phy_support, Pause);
++ phylink_set(phy_support, Asym_Pause);
++
++ linkmode_zero(sfp_support);
++ sfp_parse_support(phydev->sfp_bus, id, sfp_support, interfaces);
++ /* Some modules support 10G modes as well as others we support.
++ * Mask out non-supported modes so the correct interface is picked.
++ */
++ linkmode_and(sfp_support, phy_support, sfp_support);
++
++ if (linkmode_empty(sfp_support)) {
++ dev_err(&phydev->mdio.dev, "incompatible SFP module inserted\n");
++ return -EINVAL;
++ }
++
++ iface = sfp_select_interface(phydev->sfp_bus, sfp_support);
++
++ /* Only 1000Base-X is supported by AR8031/8033 as the downstream SerDes
++ * interface for use with SFP modules.
++ * However, some copper modules detected as having a preferred SGMII
++ * interface do default to and function in 1000Base-X mode, so just
++ * print a warning and allow such modules, as they may have some chance
++ * of working.
++ */
++ if (iface == PHY_INTERFACE_MODE_SGMII)
++ dev_warn(&phydev->mdio.dev, "module may not function if 1000Base-X not supported\n");
++ else if (iface != PHY_INTERFACE_MODE_1000BASEX)
++ return -EINVAL;
++
++ return 0;
++}
++
++static const struct sfp_upstream_ops at8031_sfp_ops = {
++ .attach = phy_sfp_attach,
++ .detach = phy_sfp_detach,
++ .module_insert = at8031_sfp_insert,
++};
++
+ static int at8031_parse_dt(struct phy_device *phydev)
+ {
+ struct device_node *node = phydev->mdio.dev.of_node;
--- /dev/null
+From 21a2802a8365cfa82cc02187c1f95136d85592ad Mon Sep 17 00:00:00 2001
+From: Christian Marangi <ansuelsmth@gmail.com>
+Date: Fri, 8 Dec 2023 15:51:59 +0100
+Subject: [PATCH 12/13] net: phy: at803x: move at8035 specific DT parse to
+ dedicated probe
+
+Move at8035 specific DT parse for clock out frequency to dedicated probe
+to make at803x probe function more generic.
+
+This is to tidy code and no behaviour change are intended.
+
+Detection logic is changed, we check if the clk 25m mask is set and if
+it's not zero, we assume the qca,clk-out-frequency property is set.
+
+The property is checked in the generic at803x_parse_dt called by
+at803x_probe.
+
+Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/phy/at803x.c | 60 +++++++++++++++++++++++++++-------------
+ 1 file changed, 41 insertions(+), 19 deletions(-)
+
+--- a/drivers/net/phy/at803x.c
++++ b/drivers/net/phy/at803x.c
+@@ -638,23 +638,6 @@ static int at803x_parse_dt(struct phy_de
+
+ priv->clk_25m_reg |= FIELD_PREP(AT803X_CLK_OUT_MASK, sel);
+ priv->clk_25m_mask |= AT803X_CLK_OUT_MASK;
+-
+- /* Fixup for the AR8030/AR8035. This chip has another mask and
+- * doesn't support the DSP reference. Eg. the lowest bit of the
+- * mask. The upper two bits select the same frequencies. Mask
+- * the lowest bit here.
+- *
+- * Warning:
+- * There was no datasheet for the AR8030 available so this is
+- * just a guess. But the AR8035 is listed as pin compatible
+- * to the AR8030 so there might be a good chance it works on
+- * the AR8030 too.
+- */
+- if (phydev->drv->phy_id == ATH8030_PHY_ID ||
+- phydev->drv->phy_id == ATH8035_PHY_ID) {
+- priv->clk_25m_reg &= AT8035_CLK_OUT_MASK;
+- priv->clk_25m_mask &= AT8035_CLK_OUT_MASK;
+- }
+ }
+
+ ret = of_property_read_u32(node, "qca,clk-out-strength", &strength);
+@@ -1635,6 +1618,45 @@ static int at8031_config_intr(struct phy
+ return at803x_config_intr(phydev);
+ }
+
++static int at8035_parse_dt(struct phy_device *phydev)
++{
++ struct at803x_priv *priv = phydev->priv;
++
++ /* Mask is set by the generic at803x_parse_dt
++ * if property is set. Assume property is set
++ * with the mask not zero.
++ */
++ if (priv->clk_25m_mask) {
++ /* Fixup for the AR8030/AR8035. This chip has another mask and
++ * doesn't support the DSP reference. Eg. the lowest bit of the
++ * mask. The upper two bits select the same frequencies. Mask
++ * the lowest bit here.
++ *
++ * Warning:
++ * There was no datasheet for the AR8030 available so this is
++ * just a guess. But the AR8035 is listed as pin compatible
++ * to the AR8030 so there might be a good chance it works on
++ * the AR8030 too.
++ */
++ priv->clk_25m_reg &= AT8035_CLK_OUT_MASK;
++ priv->clk_25m_mask &= AT8035_CLK_OUT_MASK;
++ }
++
++ return 0;
++}
++
++/* AR8030 and AR8035 shared the same special mask for clk_25m */
++static int at8035_probe(struct phy_device *phydev)
++{
++ int ret;
++
++ ret = at803x_probe(phydev);
++ if (ret)
++ return ret;
++
++ return at8035_parse_dt(phydev);
++}
++
+ static int qca83xx_config_init(struct phy_device *phydev)
+ {
+ u8 switch_revision;
+@@ -2107,7 +2129,7 @@ static struct phy_driver at803x_driver[]
+ PHY_ID_MATCH_EXACT(ATH8035_PHY_ID),
+ .name = "Qualcomm Atheros AR8035",
+ .flags = PHY_POLL_CABLE_TEST,
+- .probe = at803x_probe,
++ .probe = at8035_probe,
+ .config_aneg = at803x_config_aneg,
+ .config_init = at803x_config_init,
+ .soft_reset = genphy_soft_reset,
+@@ -2128,7 +2150,7 @@ static struct phy_driver at803x_driver[]
+ .phy_id = ATH8030_PHY_ID,
+ .name = "Qualcomm Atheros AR8030",
+ .phy_id_mask = AT8030_PHY_ID_MASK,
+- .probe = at803x_probe,
++ .probe = at8035_probe,
+ .config_init = at803x_config_init,
+ .link_change_notify = at803x_link_change_notify,
+ .set_wol = at803x_set_wol,
--- /dev/null
+From ef9df47b449e32e06501a11272809be49019bdb6 Mon Sep 17 00:00:00 2001
+From: Christian Marangi <ansuelsmth@gmail.com>
+Date: Fri, 8 Dec 2023 15:52:00 +0100
+Subject: [PATCH 13/13] net: phy: at803x: drop specific PHY ID check from cable
+ test functions
+
+Drop specific PHY ID check for cable test functions for at803x. This is
+done to make functions more generic. While at it better describe what
+the functions does by using more symbolic function names.
+
+PHYs that requires to set additional reg are moved to specific function
+calling the more generic one.
+
+cdt_start and cdt_wait_for_completion are changed to take an additional
+arg to pass specific values specific to the PHY.
+
+Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/phy/at803x.c | 95 +++++++++++++++++++++-------------------
+ 1 file changed, 50 insertions(+), 45 deletions(-)
+
+--- a/drivers/net/phy/at803x.c
++++ b/drivers/net/phy/at803x.c
+@@ -1222,31 +1222,16 @@ static int at803x_cdt_fault_length(u16 s
+ return (dt * 824) / 10;
+ }
+
+-static int at803x_cdt_start(struct phy_device *phydev, int pair)
++static int at803x_cdt_start(struct phy_device *phydev,
++ u32 cdt_start)
+ {
+- u16 cdt;
+-
+- /* qca8081 takes the different bit 15 to enable CDT test */
+- if (phydev->drv->phy_id == QCA8081_PHY_ID)
+- cdt = QCA808X_CDT_ENABLE_TEST |
+- QCA808X_CDT_LENGTH_UNIT |
+- QCA808X_CDT_INTER_CHECK_DIS;
+- else
+- cdt = FIELD_PREP(AT803X_CDT_MDI_PAIR_MASK, pair) |
+- AT803X_CDT_ENABLE_TEST;
+-
+- return phy_write(phydev, AT803X_CDT, cdt);
++ return phy_write(phydev, AT803X_CDT, cdt_start);
+ }
+
+-static int at803x_cdt_wait_for_completion(struct phy_device *phydev)
++static int at803x_cdt_wait_for_completion(struct phy_device *phydev,
++ u32 cdt_en)
+ {
+ int val, ret;
+- u16 cdt_en;
+-
+- if (phydev->drv->phy_id == QCA8081_PHY_ID)
+- cdt_en = QCA808X_CDT_ENABLE_TEST;
+- else
+- cdt_en = AT803X_CDT_ENABLE_TEST;
+
+ /* One test run takes about 25ms */
+ ret = phy_read_poll_timeout(phydev, AT803X_CDT, val,
+@@ -1266,11 +1251,13 @@ static int at803x_cable_test_one_pair(st
+ };
+ int ret, val;
+
+- ret = at803x_cdt_start(phydev, pair);
++ val = FIELD_PREP(AT803X_CDT_MDI_PAIR_MASK, pair) |
++ AT803X_CDT_ENABLE_TEST;
++ ret = at803x_cdt_start(phydev, val);
+ if (ret)
+ return ret;
+
+- ret = at803x_cdt_wait_for_completion(phydev);
++ ret = at803x_cdt_wait_for_completion(phydev, AT803X_CDT_ENABLE_TEST);
+ if (ret)
+ return ret;
+
+@@ -1292,19 +1279,11 @@ static int at803x_cable_test_one_pair(st
+ }
+
+ static int at803x_cable_test_get_status(struct phy_device *phydev,
+- bool *finished)
++ bool *finished, unsigned long pair_mask)
+ {
+- unsigned long pair_mask;
+ int retries = 20;
+ int pair, ret;
+
+- if (phydev->phy_id == ATH9331_PHY_ID ||
+- phydev->phy_id == ATH8032_PHY_ID ||
+- phydev->phy_id == QCA9561_PHY_ID)
+- pair_mask = 0x3;
+- else
+- pair_mask = 0xf;
+-
+ *finished = false;
+
+ /* According to the datasheet the CDT can be performed when
+@@ -1331,7 +1310,7 @@ static int at803x_cable_test_get_status(
+ return 0;
+ }
+
+-static int at803x_cable_test_start(struct phy_device *phydev)
++static void at803x_cable_test_autoneg(struct phy_device *phydev)
+ {
+ /* Enable auto-negotiation, but advertise no capabilities, no link
+ * will be established. A restart of the auto-negotiation is not
+@@ -1339,11 +1318,11 @@ static int at803x_cable_test_start(struc
+ */
+ phy_write(phydev, MII_BMCR, BMCR_ANENABLE);
+ phy_write(phydev, MII_ADVERTISE, ADVERTISE_CSMA);
+- if (phydev->phy_id != ATH9331_PHY_ID &&
+- phydev->phy_id != ATH8032_PHY_ID &&
+- phydev->phy_id != QCA9561_PHY_ID)
+- phy_write(phydev, MII_CTRL1000, 0);
++}
+
++static int at803x_cable_test_start(struct phy_device *phydev)
++{
++ at803x_cable_test_autoneg(phydev);
+ /* we do all the (time consuming) work later */
+ return 0;
+ }
+@@ -1618,6 +1597,29 @@ static int at8031_config_intr(struct phy
+ return at803x_config_intr(phydev);
+ }
+
++/* AR8031 and AR8035 share the same cable test get status reg */
++static int at8031_cable_test_get_status(struct phy_device *phydev,
++ bool *finished)
++{
++ return at803x_cable_test_get_status(phydev, finished, 0xf);
++}
++
++/* AR8031 and AR8035 share the same cable test start logic */
++static int at8031_cable_test_start(struct phy_device *phydev)
++{
++ at803x_cable_test_autoneg(phydev);
++ phy_write(phydev, MII_CTRL1000, 0);
++ /* we do all the (time consuming) work later */
++ return 0;
++}
++
++/* AR8032, AR9331 and QCA9561 share the same cable test get status reg */
++static int at8032_cable_test_get_status(struct phy_device *phydev,
++ bool *finished)
++{
++ return at803x_cable_test_get_status(phydev, finished, 0x3);
++}
++
+ static int at8035_parse_dt(struct phy_device *phydev)
+ {
+ struct at803x_priv *priv = phydev->priv;
+@@ -2041,11 +2043,14 @@ static int qca808x_cable_test_get_status
+
+ *finished = false;
+
+- ret = at803x_cdt_start(phydev, 0);
++ val = QCA808X_CDT_ENABLE_TEST |
++ QCA808X_CDT_LENGTH_UNIT |
++ QCA808X_CDT_INTER_CHECK_DIS;
++ ret = at803x_cdt_start(phydev, val);
+ if (ret)
+ return ret;
+
+- ret = at803x_cdt_wait_for_completion(phydev);
++ ret = at803x_cdt_wait_for_completion(phydev, QCA808X_CDT_ENABLE_TEST);
+ if (ret)
+ return ret;
+
+@@ -2143,8 +2148,8 @@ static struct phy_driver at803x_driver[]
+ .handle_interrupt = at803x_handle_interrupt,
+ .get_tunable = at803x_get_tunable,
+ .set_tunable = at803x_set_tunable,
+- .cable_test_start = at803x_cable_test_start,
+- .cable_test_get_status = at803x_cable_test_get_status,
++ .cable_test_start = at8031_cable_test_start,
++ .cable_test_get_status = at8031_cable_test_get_status,
+ }, {
+ /* Qualcomm Atheros AR8030 */
+ .phy_id = ATH8030_PHY_ID,
+@@ -2181,8 +2186,8 @@ static struct phy_driver at803x_driver[]
+ .handle_interrupt = at803x_handle_interrupt,
+ .get_tunable = at803x_get_tunable,
+ .set_tunable = at803x_set_tunable,
+- .cable_test_start = at803x_cable_test_start,
+- .cable_test_get_status = at803x_cable_test_get_status,
++ .cable_test_start = at8031_cable_test_start,
++ .cable_test_get_status = at8031_cable_test_get_status,
+ }, {
+ /* Qualcomm Atheros AR8032 */
+ PHY_ID_MATCH_EXACT(ATH8032_PHY_ID),
+@@ -2197,7 +2202,7 @@ static struct phy_driver at803x_driver[]
+ .config_intr = at803x_config_intr,
+ .handle_interrupt = at803x_handle_interrupt,
+ .cable_test_start = at803x_cable_test_start,
+- .cable_test_get_status = at803x_cable_test_get_status,
++ .cable_test_get_status = at8032_cable_test_get_status,
+ }, {
+ /* ATHEROS AR9331 */
+ PHY_ID_MATCH_EXACT(ATH9331_PHY_ID),
+@@ -2210,7 +2215,7 @@ static struct phy_driver at803x_driver[]
+ .config_intr = at803x_config_intr,
+ .handle_interrupt = at803x_handle_interrupt,
+ .cable_test_start = at803x_cable_test_start,
+- .cable_test_get_status = at803x_cable_test_get_status,
++ .cable_test_get_status = at8032_cable_test_get_status,
+ .read_status = at803x_read_status,
+ .soft_reset = genphy_soft_reset,
+ .config_aneg = at803x_config_aneg,
+@@ -2226,7 +2231,7 @@ static struct phy_driver at803x_driver[]
+ .config_intr = at803x_config_intr,
+ .handle_interrupt = at803x_handle_interrupt,
+ .cable_test_start = at803x_cable_test_start,
+- .cable_test_get_status = at803x_cable_test_get_status,
++ .cable_test_get_status = at8032_cable_test_get_status,
+ .read_status = at803x_read_status,
+ .soft_reset = genphy_soft_reset,
+ .config_aneg = at803x_config_aneg,
--- /dev/null
+From 8e732f1c6f2dc5e18f766d0f1b11df9db2dd044a Mon Sep 17 00:00:00 2001
+From: Christian Marangi <ansuelsmth@gmail.com>
+Date: Thu, 14 Dec 2023 01:44:31 +0100
+Subject: [PATCH 1/2] net: phy: at803x: move specific qca808x config_aneg to
+ dedicated function
+
+Move specific qca808x config_aneg to dedicated function to permit easier
+split of qca808x portion from at803x driver.
+
+Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/phy/at803x.c | 66 ++++++++++++++++++++++++----------------
+ 1 file changed, 40 insertions(+), 26 deletions(-)
+
+--- a/drivers/net/phy/at803x.c
++++ b/drivers/net/phy/at803x.c
+@@ -1045,9 +1045,8 @@ static int at803x_config_mdix(struct phy
+ FIELD_PREP(AT803X_SFC_MDI_CROSSOVER_MODE_M, val));
+ }
+
+-static int at803x_config_aneg(struct phy_device *phydev)
++static int at803x_prepare_config_aneg(struct phy_device *phydev)
+ {
+- struct at803x_priv *priv = phydev->priv;
+ int ret;
+
+ ret = at803x_config_mdix(phydev, phydev->mdix_ctrl);
+@@ -1064,33 +1063,22 @@ static int at803x_config_aneg(struct phy
+ return ret;
+ }
+
+- if (priv->is_1000basex)
+- return genphy_c37_config_aneg(phydev);
+-
+- /* Do not restart auto-negotiation by setting ret to 0 defautly,
+- * when calling __genphy_config_aneg later.
+- */
+- ret = 0;
+-
+- if (phydev->drv->phy_id == QCA8081_PHY_ID) {
+- int phy_ctrl = 0;
++ return 0;
++}
+
+- /* The reg MII_BMCR also needs to be configured for force mode, the
+- * genphy_config_aneg is also needed.
+- */
+- if (phydev->autoneg == AUTONEG_DISABLE)
+- genphy_c45_pma_setup_forced(phydev);
++static int at803x_config_aneg(struct phy_device *phydev)
++{
++ struct at803x_priv *priv = phydev->priv;
++ int ret;
+
+- if (linkmode_test_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT, phydev->advertising))
+- phy_ctrl = MDIO_AN_10GBT_CTRL_ADV2_5G;
++ ret = at803x_prepare_config_aneg(phydev);
++ if (ret)
++ return ret;
+
+- ret = phy_modify_mmd_changed(phydev, MDIO_MMD_AN, MDIO_AN_10GBT_CTRL,
+- MDIO_AN_10GBT_CTRL_ADV2_5G, phy_ctrl);
+- if (ret < 0)
+- return ret;
+- }
++ if (priv->is_1000basex)
++ return genphy_c37_config_aneg(phydev);
+
+- return __genphy_config_aneg(phydev, ret);
++ return genphy_config_aneg(phydev);
+ }
+
+ static int at803x_get_downshift(struct phy_device *phydev, u8 *d)
+@@ -2118,6 +2106,32 @@ static int qca808x_get_features(struct p
+ return 0;
+ }
+
++static int qca808x_config_aneg(struct phy_device *phydev)
++{
++ int phy_ctrl = 0;
++ int ret;
++
++ ret = at803x_prepare_config_aneg(phydev);
++ if (ret)
++ return ret;
++
++ /* The reg MII_BMCR also needs to be configured for force mode, the
++ * genphy_config_aneg is also needed.
++ */
++ if (phydev->autoneg == AUTONEG_DISABLE)
++ genphy_c45_pma_setup_forced(phydev);
++
++ if (linkmode_test_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT, phydev->advertising))
++ phy_ctrl = MDIO_AN_10GBT_CTRL_ADV2_5G;
++
++ ret = phy_modify_mmd_changed(phydev, MDIO_MMD_AN, MDIO_AN_10GBT_CTRL,
++ MDIO_AN_10GBT_CTRL_ADV2_5G, phy_ctrl);
++ if (ret < 0)
++ return ret;
++
++ return __genphy_config_aneg(phydev, ret);
++}
++
+ static void qca808x_link_change_notify(struct phy_device *phydev)
+ {
+ /* Assert interface sgmii fifo on link down, deassert it on link up,
+@@ -2295,7 +2309,7 @@ static struct phy_driver at803x_driver[]
+ .set_wol = at803x_set_wol,
+ .get_wol = at803x_get_wol,
+ .get_features = qca808x_get_features,
+- .config_aneg = at803x_config_aneg,
++ .config_aneg = qca808x_config_aneg,
+ .suspend = genphy_suspend,
+ .resume = genphy_resume,
+ .read_status = qca808x_read_status,
--- /dev/null
+From 38eb804e8458ba181a03a0498ce4bf84eebd1931 Mon Sep 17 00:00:00 2001
+From: Christian Marangi <ansuelsmth@gmail.com>
+Date: Thu, 14 Dec 2023 01:44:32 +0100
+Subject: [PATCH 2/2] net: phy: at803x: make read specific status function more
+ generic
+
+Rework read specific status function to be more generic. The function
+apply different speed mask based on the PHY ID. Make it more generic by
+adding an additional arg to pass the specific speed (ss) mask and use
+the provided mask to parse the speed value.
+
+This is needed to permit an easier deatch of qca808x code from the
+at803x driver.
+
+Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/phy/at803x.c | 26 ++++++++++++++++++--------
+ 1 file changed, 18 insertions(+), 8 deletions(-)
+
+--- a/drivers/net/phy/at803x.c
++++ b/drivers/net/phy/at803x.c
+@@ -301,6 +301,11 @@ static struct at803x_hw_stat qca83xx_hw_
+ { "eee_wake_errors", 0x16, GENMASK(15, 0), MMD},
+ };
+
++struct at803x_ss_mask {
++ u16 speed_mask;
++ u8 speed_shift;
++};
++
+ struct at803x_priv {
+ int flags;
+ u16 clk_25m_reg;
+@@ -921,7 +926,8 @@ static void at803x_link_change_notify(st
+ }
+ }
+
+-static int at803x_read_specific_status(struct phy_device *phydev)
++static int at803x_read_specific_status(struct phy_device *phydev,
++ struct at803x_ss_mask ss_mask)
+ {
+ int ss;
+
+@@ -940,11 +946,8 @@ static int at803x_read_specific_status(s
+ if (sfc < 0)
+ return sfc;
+
+- /* qca8081 takes the different bits for speed value from at803x */
+- if (phydev->drv->phy_id == QCA8081_PHY_ID)
+- speed = FIELD_GET(QCA808X_SS_SPEED_MASK, ss);
+- else
+- speed = FIELD_GET(AT803X_SS_SPEED_MASK, ss);
++ speed = ss & ss_mask.speed_mask;
++ speed >>= ss_mask.speed_shift;
+
+ switch (speed) {
+ case AT803X_SS_SPEED_10:
+@@ -989,6 +992,7 @@ static int at803x_read_specific_status(s
+ static int at803x_read_status(struct phy_device *phydev)
+ {
+ struct at803x_priv *priv = phydev->priv;
++ struct at803x_ss_mask ss_mask = { 0 };
+ int err, old_link = phydev->link;
+
+ if (priv->is_1000basex)
+@@ -1012,7 +1016,9 @@ static int at803x_read_status(struct phy
+ if (err < 0)
+ return err;
+
+- err = at803x_read_specific_status(phydev);
++ ss_mask.speed_mask = AT803X_SS_SPEED_MASK;
++ ss_mask.speed_shift = __bf_shf(AT803X_SS_SPEED_MASK);
++ err = at803x_read_specific_status(phydev, ss_mask);
+ if (err < 0)
+ return err;
+
+@@ -1869,6 +1875,7 @@ static int qca808x_config_init(struct ph
+
+ static int qca808x_read_status(struct phy_device *phydev)
+ {
++ struct at803x_ss_mask ss_mask = { 0 };
+ int ret;
+
+ ret = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_10GBT_STAT);
+@@ -1882,7 +1889,10 @@ static int qca808x_read_status(struct ph
+ if (ret)
+ return ret;
+
+- ret = at803x_read_specific_status(phydev);
++ /* qca8081 takes the different bits for speed value from at803x */
++ ss_mask.speed_mask = QCA808X_SS_SPEED_MASK;
++ ss_mask.speed_shift = __bf_shf(QCA808X_SS_SPEED_MASK);
++ ret = at803x_read_specific_status(phydev, ss_mask);
+ if (ret < 0)
+ return ret;
+
--- /dev/null
+From fc9d7264ddc32eaa647d6bfcdc25cdf9f786fde0 Mon Sep 17 00:00:00 2001
+From: Christian Marangi <ansuelsmth@gmail.com>
+Date: Mon, 18 Dec 2023 00:27:39 +0100
+Subject: [PATCH 1/2] net: phy: at803x: remove extra space after cast
+
+Remove extra space after cast as reported by checkpatch to keep code
+clean.
+
+Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Link: https://lore.kernel.org/r/20231217232739.27065-1-ansuelsmth@gmail.com
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+---
+ drivers/net/phy/at803x.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/drivers/net/phy/at803x.c
++++ b/drivers/net/phy/at803x.c
+@@ -462,7 +462,7 @@ static int at803x_set_wol(struct phy_dev
+ if (!ndev)
+ return -ENODEV;
+
+- mac = (const u8 *) ndev->dev_addr;
++ mac = (const u8 *)ndev->dev_addr;
+
+ if (!is_valid_ether_addr(mac))
+ return -EINVAL;
--- /dev/null
+From 3ab5720881a924fb6405d9e6a3b09f1026467c47 Mon Sep 17 00:00:00 2001
+From: Christian Marangi <ansuelsmth@gmail.com>
+Date: Mon, 18 Dec 2023 00:25:08 +0100
+Subject: [PATCH 2/2] net: phy: at803x: replace msleep(1) with usleep_range
+
+Replace msleep(1) with usleep_range as suggested by timers-howto guide.
+
+Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Link: https://lore.kernel.org/r/20231217232508.26470-1-ansuelsmth@gmail.com
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+---
+ drivers/net/phy/at803x.c | 6 +++---
+ 1 file changed, 3 insertions(+), 3 deletions(-)
+
+--- a/drivers/net/phy/at803x.c
++++ b/drivers/net/phy/at803x.c
+@@ -916,9 +916,9 @@ static void at803x_link_change_notify(st
+ at803x_context_save(phydev, &context);
+
+ phy_device_reset(phydev, 1);
+- msleep(1);
++ usleep_range(1000, 2000);
+ phy_device_reset(phydev, 0);
+- msleep(1);
++ usleep_range(1000, 2000);
+
+ at803x_context_restore(phydev, &context);
+
+@@ -1733,7 +1733,7 @@ static int qca83xx_resume(struct phy_dev
+ if (ret)
+ return ret;
+
+- msleep(1);
++ usleep_range(1000, 2000);
+
+ return 0;
+ }
--- /dev/null
+From 7961ef1fa10ec35ad6923fb5751877116e4b035b Mon Sep 17 00:00:00 2001
+From: Christian Marangi <ansuelsmth@gmail.com>
+Date: Tue, 19 Dec 2023 21:21:24 +0100
+Subject: [PATCH] net: phy: at803x: better align function varibles to open
+ parenthesis
+
+Better align function variables to open parenthesis as suggested by
+checkpatch script for qca808x function to make code cleaner.
+
+For cable_test_get_status function some additional rework was needed to
+handle too long functions.
+
+Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/phy/at803x.c | 67 ++++++++++++++++++++++------------------
+ 1 file changed, 37 insertions(+), 30 deletions(-)
+
+--- a/drivers/net/phy/at803x.c
++++ b/drivers/net/phy/at803x.c
+@@ -1781,27 +1781,27 @@ static int qca808x_phy_fast_retrain_conf
+ return ret;
+
+ phy_write_mmd(phydev, MDIO_MMD_AN, QCA808X_PHY_MMD7_TOP_OPTION1,
+- QCA808X_TOP_OPTION1_DATA);
++ QCA808X_TOP_OPTION1_DATA);
+ phy_write_mmd(phydev, MDIO_MMD_PMAPMD, QCA808X_PHY_MMD1_MSE_THRESHOLD_20DB,
+- QCA808X_MSE_THRESHOLD_20DB_VALUE);
++ QCA808X_MSE_THRESHOLD_20DB_VALUE);
+ phy_write_mmd(phydev, MDIO_MMD_PMAPMD, QCA808X_PHY_MMD1_MSE_THRESHOLD_17DB,
+- QCA808X_MSE_THRESHOLD_17DB_VALUE);
++ QCA808X_MSE_THRESHOLD_17DB_VALUE);
+ phy_write_mmd(phydev, MDIO_MMD_PMAPMD, QCA808X_PHY_MMD1_MSE_THRESHOLD_27DB,
+- QCA808X_MSE_THRESHOLD_27DB_VALUE);
++ QCA808X_MSE_THRESHOLD_27DB_VALUE);
+ phy_write_mmd(phydev, MDIO_MMD_PMAPMD, QCA808X_PHY_MMD1_MSE_THRESHOLD_28DB,
+- QCA808X_MSE_THRESHOLD_28DB_VALUE);
++ QCA808X_MSE_THRESHOLD_28DB_VALUE);
+ phy_write_mmd(phydev, MDIO_MMD_PCS, QCA808X_PHY_MMD3_DEBUG_1,
+- QCA808X_MMD3_DEBUG_1_VALUE);
++ QCA808X_MMD3_DEBUG_1_VALUE);
+ phy_write_mmd(phydev, MDIO_MMD_PCS, QCA808X_PHY_MMD3_DEBUG_4,
+- QCA808X_MMD3_DEBUG_4_VALUE);
++ QCA808X_MMD3_DEBUG_4_VALUE);
+ phy_write_mmd(phydev, MDIO_MMD_PCS, QCA808X_PHY_MMD3_DEBUG_5,
+- QCA808X_MMD3_DEBUG_5_VALUE);
++ QCA808X_MMD3_DEBUG_5_VALUE);
+ phy_write_mmd(phydev, MDIO_MMD_PCS, QCA808X_PHY_MMD3_DEBUG_3,
+- QCA808X_MMD3_DEBUG_3_VALUE);
++ QCA808X_MMD3_DEBUG_3_VALUE);
+ phy_write_mmd(phydev, MDIO_MMD_PCS, QCA808X_PHY_MMD3_DEBUG_6,
+- QCA808X_MMD3_DEBUG_6_VALUE);
++ QCA808X_MMD3_DEBUG_6_VALUE);
+ phy_write_mmd(phydev, MDIO_MMD_PCS, QCA808X_PHY_MMD3_DEBUG_2,
+- QCA808X_MMD3_DEBUG_2_VALUE);
++ QCA808X_MMD3_DEBUG_2_VALUE);
+
+ return 0;
+ }
+@@ -1838,13 +1838,14 @@ static int qca808x_config_init(struct ph
+
+ /* Active adc&vga on 802.3az for the link 1000M and 100M */
+ ret = phy_modify_mmd(phydev, MDIO_MMD_PCS, QCA808X_PHY_MMD3_ADDR_CLD_CTRL7,
+- QCA808X_8023AZ_AFE_CTRL_MASK, QCA808X_8023AZ_AFE_EN);
++ QCA808X_8023AZ_AFE_CTRL_MASK, QCA808X_8023AZ_AFE_EN);
+ if (ret)
+ return ret;
+
+ /* Adjust the threshold on 802.3az for the link 1000M */
+ ret = phy_write_mmd(phydev, MDIO_MMD_PCS,
+- QCA808X_PHY_MMD3_AZ_TRAINING_CTRL, QCA808X_MMD3_AZ_TRAINING_VAL);
++ QCA808X_PHY_MMD3_AZ_TRAINING_CTRL,
++ QCA808X_MMD3_AZ_TRAINING_VAL);
+ if (ret)
+ return ret;
+
+@@ -1870,7 +1871,8 @@ static int qca808x_config_init(struct ph
+
+ /* Configure adc threshold as 100mv for the link 10M */
+ return at803x_debug_reg_mask(phydev, QCA808X_PHY_DEBUG_ADC_THRESHOLD,
+- QCA808X_ADC_THRESHOLD_MASK, QCA808X_ADC_THRESHOLD_100MV);
++ QCA808X_ADC_THRESHOLD_MASK,
++ QCA808X_ADC_THRESHOLD_100MV);
+ }
+
+ static int qca808x_read_status(struct phy_device *phydev)
+@@ -1883,7 +1885,7 @@ static int qca808x_read_status(struct ph
+ return ret;
+
+ linkmode_mod_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT, phydev->lp_advertising,
+- ret & MDIO_AN_10GBT_STAT_LP2_5G);
++ ret & MDIO_AN_10GBT_STAT_LP2_5G);
+
+ ret = genphy_read_status(phydev);
+ if (ret)
+@@ -1913,7 +1915,7 @@ static int qca808x_read_status(struct ph
+ */
+ if (qca808x_has_fast_retrain_or_slave_seed(phydev)) {
+ if (phydev->master_slave_state == MASTER_SLAVE_STATE_ERR ||
+- qca808x_is_prefer_master(phydev)) {
++ qca808x_is_prefer_master(phydev)) {
+ qca808x_phy_ms_seed_enable(phydev, false);
+ } else {
+ qca808x_phy_ms_seed_enable(phydev, true);
+@@ -2070,18 +2072,22 @@ static int qca808x_cable_test_get_status
+ ethnl_cable_test_result(phydev, ETHTOOL_A_CABLE_PAIR_D,
+ qca808x_cable_test_result_trans(pair_d));
+
+- if (qca808x_cdt_fault_length_valid(pair_a))
+- ethnl_cable_test_fault_length(phydev, ETHTOOL_A_CABLE_PAIR_A,
+- qca808x_cdt_fault_length(phydev, ETHTOOL_A_CABLE_PAIR_A));
+- if (qca808x_cdt_fault_length_valid(pair_b))
+- ethnl_cable_test_fault_length(phydev, ETHTOOL_A_CABLE_PAIR_B,
+- qca808x_cdt_fault_length(phydev, ETHTOOL_A_CABLE_PAIR_B));
+- if (qca808x_cdt_fault_length_valid(pair_c))
+- ethnl_cable_test_fault_length(phydev, ETHTOOL_A_CABLE_PAIR_C,
+- qca808x_cdt_fault_length(phydev, ETHTOOL_A_CABLE_PAIR_C));
+- if (qca808x_cdt_fault_length_valid(pair_d))
+- ethnl_cable_test_fault_length(phydev, ETHTOOL_A_CABLE_PAIR_D,
+- qca808x_cdt_fault_length(phydev, ETHTOOL_A_CABLE_PAIR_D));
++ if (qca808x_cdt_fault_length_valid(pair_a)) {
++ val = qca808x_cdt_fault_length(phydev, ETHTOOL_A_CABLE_PAIR_A);
++ ethnl_cable_test_fault_length(phydev, ETHTOOL_A_CABLE_PAIR_A, val);
++ }
++ if (qca808x_cdt_fault_length_valid(pair_b)) {
++ val = qca808x_cdt_fault_length(phydev, ETHTOOL_A_CABLE_PAIR_B);
++ ethnl_cable_test_fault_length(phydev, ETHTOOL_A_CABLE_PAIR_B, val);
++ }
++ if (qca808x_cdt_fault_length_valid(pair_c)) {
++ val = qca808x_cdt_fault_length(phydev, ETHTOOL_A_CABLE_PAIR_C);
++ ethnl_cable_test_fault_length(phydev, ETHTOOL_A_CABLE_PAIR_C, val);
++ }
++ if (qca808x_cdt_fault_length_valid(pair_d)) {
++ val = qca808x_cdt_fault_length(phydev, ETHTOOL_A_CABLE_PAIR_D);
++ ethnl_cable_test_fault_length(phydev, ETHTOOL_A_CABLE_PAIR_D, val);
++ }
+
+ *finished = true;
+
+@@ -2148,8 +2154,9 @@ static void qca808x_link_change_notify(s
+ * the interface device address is always phy address added by 1.
+ */
+ mdiobus_c45_modify_changed(phydev->mdio.bus, phydev->mdio.addr + 1,
+- MDIO_MMD_PMAPMD, QCA8081_PHY_SERDES_MMD1_FIFO_CTRL,
+- QCA8081_PHY_FIFO_RSTN, phydev->link ? QCA8081_PHY_FIFO_RSTN : 0);
++ MDIO_MMD_PMAPMD, QCA8081_PHY_SERDES_MMD1_FIFO_CTRL,
++ QCA8081_PHY_FIFO_RSTN,
++ phydev->link ? QCA8081_PHY_FIFO_RSTN : 0);
+ }
+
+ static struct phy_driver at803x_driver[] = {
--- /dev/null
+From 22eb276098da820d9440fad22901f9b74ed4d659 Mon Sep 17 00:00:00 2001
+From: Christian Marangi <ansuelsmth@gmail.com>
+Date: Thu, 4 Jan 2024 22:30:38 +0100
+Subject: [PATCH 1/4] net: phy: at803x: generalize cdt fault length function
+
+Generalize cable test fault length function since they all base on the
+same magic values (already reverse engineered to understand the meaning
+of it) to have consistenct values on every PHY.
+
+Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
+Reviewed-by: Simon Horman <horms@kernel.org>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/phy/at803x.c | 13 ++++++-------
+ 1 file changed, 6 insertions(+), 7 deletions(-)
+
+--- a/drivers/net/phy/at803x.c
++++ b/drivers/net/phy/at803x.c
+@@ -1192,10 +1192,8 @@ static bool at803x_cdt_fault_length_vali
+ return false;
+ }
+
+-static int at803x_cdt_fault_length(u16 status)
++static int at803x_cdt_fault_length(int dt)
+ {
+- int dt;
+-
+ /* According to the datasheet the distance to the fault is
+ * DELTA_TIME * 0.824 meters.
+ *
+@@ -1211,8 +1209,6 @@ static int at803x_cdt_fault_length(u16 s
+ * With a VF of 0.69 we get the factor 0.824 mentioned in the
+ * datasheet.
+ */
+- dt = FIELD_GET(AT803X_CDT_STATUS_DELTA_TIME_MASK, status);
+-
+ return (dt * 824) / 10;
+ }
+
+@@ -1265,9 +1261,11 @@ static int at803x_cable_test_one_pair(st
+ ethnl_cable_test_result(phydev, ethtool_pair[pair],
+ at803x_cable_test_result_trans(val));
+
+- if (at803x_cdt_fault_length_valid(val))
++ if (at803x_cdt_fault_length_valid(val)) {
++ val = FIELD_GET(AT803X_CDT_STATUS_DELTA_TIME_MASK, val);
+ ethnl_cable_test_fault_length(phydev, ethtool_pair[pair],
+ at803x_cdt_fault_length(val));
++ }
+
+ return 1;
+ }
+@@ -1992,7 +1990,8 @@ static int qca808x_cdt_fault_length(stru
+ if (val < 0)
+ return val;
+
+- return (FIELD_GET(QCA808X_CDT_DIAG_LENGTH, val) * 824) / 10;
++ val = FIELD_GET(QCA808X_CDT_DIAG_LENGTH, val);
++ return at803x_cdt_fault_length(val);
+ }
+
+ static int qca808x_cable_test_start(struct phy_device *phydev)
--- /dev/null
+From e0e9ada1df6133513249861c1d91c1dbefd9383b Mon Sep 17 00:00:00 2001
+From: Christian Marangi <ansuelsmth@gmail.com>
+Date: Thu, 4 Jan 2024 22:30:39 +0100
+Subject: [PATCH 2/4] net: phy: at803x: refactor qca808x cable test get status
+ function
+
+Refactor qca808x cable test get status function to remove code
+duplication and clean things up.
+
+The same logic is applied to each pair hence it can be generalized and
+moved to a common function.
+
+Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
+Reviewed-by: Simon Horman <horms@kernel.org>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/phy/at803x.c | 80 ++++++++++++++++++++++++----------------
+ 1 file changed, 49 insertions(+), 31 deletions(-)
+
+--- a/drivers/net/phy/at803x.c
++++ b/drivers/net/phy/at803x.c
+@@ -2035,10 +2035,43 @@ static int qca808x_cable_test_start(stru
+ return 0;
+ }
+
++static int qca808x_cable_test_get_pair_status(struct phy_device *phydev, u8 pair,
++ u16 status)
++{
++ u16 pair_code;
++ int length;
++
++ switch (pair) {
++ case ETHTOOL_A_CABLE_PAIR_A:
++ pair_code = FIELD_GET(QCA808X_CDT_CODE_PAIR_A, status);
++ break;
++ case ETHTOOL_A_CABLE_PAIR_B:
++ pair_code = FIELD_GET(QCA808X_CDT_CODE_PAIR_B, status);
++ break;
++ case ETHTOOL_A_CABLE_PAIR_C:
++ pair_code = FIELD_GET(QCA808X_CDT_CODE_PAIR_C, status);
++ break;
++ case ETHTOOL_A_CABLE_PAIR_D:
++ pair_code = FIELD_GET(QCA808X_CDT_CODE_PAIR_D, status);
++ break;
++ default:
++ return -EINVAL;
++ }
++
++ ethnl_cable_test_result(phydev, pair,
++ qca808x_cable_test_result_trans(pair_code));
++
++ if (qca808x_cdt_fault_length_valid(pair_code)) {
++ length = qca808x_cdt_fault_length(phydev, pair);
++ ethnl_cable_test_fault_length(phydev, pair, length);
++ }
++
++ return 0;
++}
++
+ static int qca808x_cable_test_get_status(struct phy_device *phydev, bool *finished)
+ {
+ int ret, val;
+- int pair_a, pair_b, pair_c, pair_d;
+
+ *finished = false;
+
+@@ -2057,36 +2090,21 @@ static int qca808x_cable_test_get_status
+ if (val < 0)
+ return val;
+
+- pair_a = FIELD_GET(QCA808X_CDT_CODE_PAIR_A, val);
+- pair_b = FIELD_GET(QCA808X_CDT_CODE_PAIR_B, val);
+- pair_c = FIELD_GET(QCA808X_CDT_CODE_PAIR_C, val);
+- pair_d = FIELD_GET(QCA808X_CDT_CODE_PAIR_D, val);
+-
+- ethnl_cable_test_result(phydev, ETHTOOL_A_CABLE_PAIR_A,
+- qca808x_cable_test_result_trans(pair_a));
+- ethnl_cable_test_result(phydev, ETHTOOL_A_CABLE_PAIR_B,
+- qca808x_cable_test_result_trans(pair_b));
+- ethnl_cable_test_result(phydev, ETHTOOL_A_CABLE_PAIR_C,
+- qca808x_cable_test_result_trans(pair_c));
+- ethnl_cable_test_result(phydev, ETHTOOL_A_CABLE_PAIR_D,
+- qca808x_cable_test_result_trans(pair_d));
+-
+- if (qca808x_cdt_fault_length_valid(pair_a)) {
+- val = qca808x_cdt_fault_length(phydev, ETHTOOL_A_CABLE_PAIR_A);
+- ethnl_cable_test_fault_length(phydev, ETHTOOL_A_CABLE_PAIR_A, val);
+- }
+- if (qca808x_cdt_fault_length_valid(pair_b)) {
+- val = qca808x_cdt_fault_length(phydev, ETHTOOL_A_CABLE_PAIR_B);
+- ethnl_cable_test_fault_length(phydev, ETHTOOL_A_CABLE_PAIR_B, val);
+- }
+- if (qca808x_cdt_fault_length_valid(pair_c)) {
+- val = qca808x_cdt_fault_length(phydev, ETHTOOL_A_CABLE_PAIR_C);
+- ethnl_cable_test_fault_length(phydev, ETHTOOL_A_CABLE_PAIR_C, val);
+- }
+- if (qca808x_cdt_fault_length_valid(pair_d)) {
+- val = qca808x_cdt_fault_length(phydev, ETHTOOL_A_CABLE_PAIR_D);
+- ethnl_cable_test_fault_length(phydev, ETHTOOL_A_CABLE_PAIR_D, val);
+- }
++ ret = qca808x_cable_test_get_pair_status(phydev, ETHTOOL_A_CABLE_PAIR_A, val);
++ if (ret)
++ return ret;
++
++ ret = qca808x_cable_test_get_pair_status(phydev, ETHTOOL_A_CABLE_PAIR_B, val);
++ if (ret)
++ return ret;
++
++ ret = qca808x_cable_test_get_pair_status(phydev, ETHTOOL_A_CABLE_PAIR_C, val);
++ if (ret)
++ return ret;
++
++ ret = qca808x_cable_test_get_pair_status(phydev, ETHTOOL_A_CABLE_PAIR_D, val);
++ if (ret)
++ return ret;
+
+ *finished = true;
+
--- /dev/null
+From ea73e5ea442ee2aade67b1fb1233ccb3cbea2ceb Mon Sep 17 00:00:00 2001
+From: Christian Marangi <ansuelsmth@gmail.com>
+Date: Thu, 4 Jan 2024 22:30:40 +0100
+Subject: [PATCH 3/4] net: phy: at803x: add support for cdt cross short test
+ for qca808x
+
+QCA808x PHY Family supports Cable Diagnostic Test also for Cross Pair
+Short.
+
+Add all the define to make enable and support these additional tests.
+
+Cross Short test was previously disabled by default, this is now changed
+and enabled by default. In this mode, the mask changed a bit and length
+is shifted based on the fault condition.
+
+Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
+Reviewed-by: Simon Horman <horms@kernel.org>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/phy/at803x.c | 86 ++++++++++++++++++++++++++++++++--------
+ 1 file changed, 69 insertions(+), 17 deletions(-)
+
+--- a/drivers/net/phy/at803x.c
++++ b/drivers/net/phy/at803x.c
+@@ -254,6 +254,7 @@
+
+ #define QCA808X_CDT_ENABLE_TEST BIT(15)
+ #define QCA808X_CDT_INTER_CHECK_DIS BIT(13)
++#define QCA808X_CDT_STATUS BIT(11)
+ #define QCA808X_CDT_LENGTH_UNIT BIT(10)
+
+ #define QCA808X_MMD3_CDT_STATUS 0x8064
+@@ -261,16 +262,44 @@
+ #define QCA808X_MMD3_CDT_DIAG_PAIR_B 0x8066
+ #define QCA808X_MMD3_CDT_DIAG_PAIR_C 0x8067
+ #define QCA808X_MMD3_CDT_DIAG_PAIR_D 0x8068
+-#define QCA808X_CDT_DIAG_LENGTH GENMASK(7, 0)
++#define QCA808X_CDT_DIAG_LENGTH_SAME_SHORT GENMASK(15, 8)
++#define QCA808X_CDT_DIAG_LENGTH_CROSS_SHORT GENMASK(7, 0)
+
+ #define QCA808X_CDT_CODE_PAIR_A GENMASK(15, 12)
+ #define QCA808X_CDT_CODE_PAIR_B GENMASK(11, 8)
+ #define QCA808X_CDT_CODE_PAIR_C GENMASK(7, 4)
+ #define QCA808X_CDT_CODE_PAIR_D GENMASK(3, 0)
+-#define QCA808X_CDT_STATUS_STAT_FAIL 0
+-#define QCA808X_CDT_STATUS_STAT_NORMAL 1
+-#define QCA808X_CDT_STATUS_STAT_OPEN 2
+-#define QCA808X_CDT_STATUS_STAT_SHORT 3
++
++#define QCA808X_CDT_STATUS_STAT_TYPE GENMASK(1, 0)
++#define QCA808X_CDT_STATUS_STAT_FAIL FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_TYPE, 0)
++#define QCA808X_CDT_STATUS_STAT_NORMAL FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_TYPE, 1)
++#define QCA808X_CDT_STATUS_STAT_SAME_OPEN FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_TYPE, 2)
++#define QCA808X_CDT_STATUS_STAT_SAME_SHORT FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_TYPE, 3)
++
++#define QCA808X_CDT_STATUS_STAT_MDI GENMASK(3, 2)
++#define QCA808X_CDT_STATUS_STAT_MDI1 FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_MDI, 1)
++#define QCA808X_CDT_STATUS_STAT_MDI2 FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_MDI, 2)
++#define QCA808X_CDT_STATUS_STAT_MDI3 FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_MDI, 3)
++
++/* NORMAL are MDI with type set to 0 */
++#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_NORMAL QCA808X_CDT_STATUS_STAT_MDI1
++#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_OPEN (QCA808X_CDT_STATUS_STAT_SAME_OPEN |\
++ QCA808X_CDT_STATUS_STAT_MDI1)
++#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_SHORT (QCA808X_CDT_STATUS_STAT_SAME_SHORT |\
++ QCA808X_CDT_STATUS_STAT_MDI1)
++#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_NORMAL QCA808X_CDT_STATUS_STAT_MDI2
++#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_OPEN (QCA808X_CDT_STATUS_STAT_SAME_OPEN |\
++ QCA808X_CDT_STATUS_STAT_MDI2)
++#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_SHORT (QCA808X_CDT_STATUS_STAT_SAME_SHORT |\
++ QCA808X_CDT_STATUS_STAT_MDI2)
++#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_NORMAL QCA808X_CDT_STATUS_STAT_MDI3
++#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_OPEN (QCA808X_CDT_STATUS_STAT_SAME_OPEN |\
++ QCA808X_CDT_STATUS_STAT_MDI3)
++#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_SHORT (QCA808X_CDT_STATUS_STAT_SAME_SHORT |\
++ QCA808X_CDT_STATUS_STAT_MDI3)
++
++/* Added for reference of existence but should be handled by wait_for_completion already */
++#define QCA808X_CDT_STATUS_STAT_BUSY (BIT(1) | BIT(3))
+
+ /* QCA808X 1G chip type */
+ #define QCA808X_PHY_MMD7_CHIP_TYPE 0x901d
+@@ -1941,8 +1970,17 @@ static int qca808x_soft_reset(struct phy
+ static bool qca808x_cdt_fault_length_valid(int cdt_code)
+ {
+ switch (cdt_code) {
+- case QCA808X_CDT_STATUS_STAT_SHORT:
+- case QCA808X_CDT_STATUS_STAT_OPEN:
++ case QCA808X_CDT_STATUS_STAT_SAME_SHORT:
++ case QCA808X_CDT_STATUS_STAT_SAME_OPEN:
++ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_NORMAL:
++ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_OPEN:
++ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_SHORT:
++ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_NORMAL:
++ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_OPEN:
++ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_SHORT:
++ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_NORMAL:
++ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_OPEN:
++ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_SHORT:
+ return true;
+ default:
+ return false;
+@@ -1954,17 +1992,28 @@ static int qca808x_cable_test_result_tra
+ switch (cdt_code) {
+ case QCA808X_CDT_STATUS_STAT_NORMAL:
+ return ETHTOOL_A_CABLE_RESULT_CODE_OK;
+- case QCA808X_CDT_STATUS_STAT_SHORT:
++ case QCA808X_CDT_STATUS_STAT_SAME_SHORT:
+ return ETHTOOL_A_CABLE_RESULT_CODE_SAME_SHORT;
+- case QCA808X_CDT_STATUS_STAT_OPEN:
++ case QCA808X_CDT_STATUS_STAT_SAME_OPEN:
+ return ETHTOOL_A_CABLE_RESULT_CODE_OPEN;
++ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_NORMAL:
++ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_OPEN:
++ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_SHORT:
++ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_NORMAL:
++ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_OPEN:
++ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_SHORT:
++ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_NORMAL:
++ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_OPEN:
++ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_SHORT:
++ return ETHTOOL_A_CABLE_RESULT_CODE_CROSS_SHORT;
+ case QCA808X_CDT_STATUS_STAT_FAIL:
+ default:
+ return ETHTOOL_A_CABLE_RESULT_CODE_UNSPEC;
+ }
+ }
+
+-static int qca808x_cdt_fault_length(struct phy_device *phydev, int pair)
++static int qca808x_cdt_fault_length(struct phy_device *phydev, int pair,
++ int result)
+ {
+ int val;
+ u32 cdt_length_reg = 0;
+@@ -1990,7 +2039,11 @@ static int qca808x_cdt_fault_length(stru
+ if (val < 0)
+ return val;
+
+- val = FIELD_GET(QCA808X_CDT_DIAG_LENGTH, val);
++ if (result == ETHTOOL_A_CABLE_RESULT_CODE_SAME_SHORT)
++ val = FIELD_GET(QCA808X_CDT_DIAG_LENGTH_SAME_SHORT, val);
++ else
++ val = FIELD_GET(QCA808X_CDT_DIAG_LENGTH_CROSS_SHORT, val);
++
+ return at803x_cdt_fault_length(val);
+ }
+
+@@ -2038,8 +2091,8 @@ static int qca808x_cable_test_start(stru
+ static int qca808x_cable_test_get_pair_status(struct phy_device *phydev, u8 pair,
+ u16 status)
+ {
++ int length, result;
+ u16 pair_code;
+- int length;
+
+ switch (pair) {
+ case ETHTOOL_A_CABLE_PAIR_A:
+@@ -2058,11 +2111,11 @@ static int qca808x_cable_test_get_pair_s
+ return -EINVAL;
+ }
+
+- ethnl_cable_test_result(phydev, pair,
+- qca808x_cable_test_result_trans(pair_code));
++ result = qca808x_cable_test_result_trans(pair_code);
++ ethnl_cable_test_result(phydev, pair, result);
+
+ if (qca808x_cdt_fault_length_valid(pair_code)) {
+- length = qca808x_cdt_fault_length(phydev, pair);
++ length = qca808x_cdt_fault_length(phydev, pair, result);
+ ethnl_cable_test_fault_length(phydev, pair, length);
+ }
+
+@@ -2076,8 +2129,7 @@ static int qca808x_cable_test_get_status
+ *finished = false;
+
+ val = QCA808X_CDT_ENABLE_TEST |
+- QCA808X_CDT_LENGTH_UNIT |
+- QCA808X_CDT_INTER_CHECK_DIS;
++ QCA808X_CDT_LENGTH_UNIT;
+ ret = at803x_cdt_start(phydev, val);
+ if (ret)
+ return ret;
--- /dev/null
+From c34d9452d4e5d98a655d7b625e85466320885416 Mon Sep 17 00:00:00 2001
+From: Christian Marangi <ansuelsmth@gmail.com>
+Date: Thu, 4 Jan 2024 22:30:41 +0100
+Subject: [PATCH 4/4] net: phy: at803x: make read_status more generic
+
+Make read_status more generic in preparation on moving it to shared
+library as other PHY Family Driver will have the exact same
+implementation.
+
+The only specific part was a check for AR8031/33 if 1000basex was used.
+The check is moved to a dedicated function specific for those PHYs.
+
+Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
+Reviewed-by: Simon Horman <horms@kernel.org>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/phy/at803x.c | 17 ++++++++++++-----
+ 1 file changed, 12 insertions(+), 5 deletions(-)
+
+--- a/drivers/net/phy/at803x.c
++++ b/drivers/net/phy/at803x.c
+@@ -1020,13 +1020,9 @@ static int at803x_read_specific_status(s
+
+ static int at803x_read_status(struct phy_device *phydev)
+ {
+- struct at803x_priv *priv = phydev->priv;
+ struct at803x_ss_mask ss_mask = { 0 };
+ int err, old_link = phydev->link;
+
+- if (priv->is_1000basex)
+- return genphy_c37_read_status(phydev);
+-
+ /* Update the link, but return if there was an error */
+ err = genphy_update_link(phydev);
+ if (err)
+@@ -1618,6 +1614,17 @@ static int at8031_config_intr(struct phy
+ return at803x_config_intr(phydev);
+ }
+
++/* AR8031 and AR8033 share the same read status logic */
++static int at8031_read_status(struct phy_device *phydev)
++{
++ struct at803x_priv *priv = phydev->priv;
++
++ if (priv->is_1000basex)
++ return genphy_c37_read_status(phydev);
++
++ return at803x_read_status(phydev);
++}
++
+ /* AR8031 and AR8035 share the same cable test get status reg */
+ static int at8031_cable_test_get_status(struct phy_device *phydev,
+ bool *finished)
+@@ -2281,7 +2288,7 @@ static struct phy_driver at803x_driver[]
+ .read_page = at803x_read_page,
+ .write_page = at803x_write_page,
+ .get_features = at803x_get_features,
+- .read_status = at803x_read_status,
++ .read_status = at8031_read_status,
+ .config_intr = at8031_config_intr,
+ .handle_interrupt = at803x_handle_interrupt,
+ .get_tunable = at803x_get_tunable,
bool mac_link_dropped;
bool using_mac_select_pcs;
-@@ -987,6 +992,22 @@ static void phylink_mac_pcs_an_restart(s
+@@ -990,6 +995,22 @@ static void phylink_mac_pcs_an_restart(s
}
}
static void phylink_major_config(struct phylink *pl, bool restart,
const struct phylink_link_state *state)
{
-@@ -1023,11 +1044,16 @@ static void phylink_major_config(struct
+@@ -1026,11 +1047,16 @@ static void phylink_major_config(struct
/* If we have a new PCS, switch to the new PCS after preparing the MAC
* for the change.
*/
if (pl->pcs) {
err = pl->pcs->ops->pcs_config(pl->pcs, pl->cur_link_an_mode,
state->interface,
-@@ -1499,6 +1525,7 @@ struct phylink *phylink_create(struct ph
+@@ -1502,6 +1528,7 @@ struct phylink *phylink_create(struct ph
pl->link_config.speed = SPEED_UNKNOWN;
pl->link_config.duplex = DUPLEX_UNKNOWN;
pl->link_config.an_enabled = true;
pl->mac_ops = mac_ops;
__set_bit(PHYLINK_DISABLE_STOPPED, &pl->phylink_disable_state);
timer_setup(&pl->link_poll, phylink_fixed_poll, 0);
-@@ -1900,6 +1927,8 @@ void phylink_start(struct phylink *pl)
+@@ -1903,6 +1930,8 @@ void phylink_start(struct phylink *pl)
if (pl->netdev)
netif_carrier_off(pl->netdev);
/* Apply the link configuration to the MAC when starting. This allows
* a fixed-link to start with the correct parameters, and also
* ensures that we set the appropriate advertisement for Serdes links.
-@@ -1910,6 +1939,8 @@ void phylink_start(struct phylink *pl)
+@@ -1913,6 +1942,8 @@ void phylink_start(struct phylink *pl)
*/
phylink_mac_initial_config(pl, true);
phylink_enable_and_run_resolve(pl, PHYLINK_DISABLE_STOPPED);
if (pl->cfg_link_an_mode == MLO_AN_FIXED && pl->link_gpio) {
-@@ -1928,15 +1959,9 @@ void phylink_start(struct phylink *pl)
+@@ -1931,15 +1962,9 @@ void phylink_start(struct phylink *pl)
poll = true;
}
if (poll)
mod_timer(&pl->link_poll, jiffies + HZ);
if (pl->phydev)
-@@ -1973,6 +1998,10 @@ void phylink_stop(struct phylink *pl)
+@@ -1976,6 +2001,10 @@ void phylink_stop(struct phylink *pl)
}
phylink_run_resolve_and_disable(pl, PHYLINK_DISABLE_STOPPED);
#include <linux/linkmode.h>
#include <linux/netlink.h>
#include <linux/mdio.h>
-@@ -593,6 +594,7 @@ struct macsec_ops;
+@@ -602,6 +603,7 @@ struct macsec_ops;
* @phy_num_led_triggers: Number of triggers in @phy_led_triggers
* @led_link_trigger: LED trigger for link up/down
* @last_triggered: last LED trigger for link speed
* @master_slave_set: User requested master/slave configuration
* @master_slave_get: Current master/slave advertisement
* @master_slave_state: Current master/slave configuration
-@@ -685,6 +687,7 @@ struct phy_device {
+@@ -694,6 +696,7 @@ struct phy_device {
struct phy_led_trigger *led_link_trigger;
#endif
/*
* Interrupt number for this PHY
-@@ -759,6 +762,19 @@ struct phy_tdr_config {
+@@ -768,6 +771,19 @@ struct phy_tdr_config {
#define PHY_PAIR_ALL -1
/**
init_data.fwnode = of_fwnode_handle(led);
--- a/include/linux/phy.h
+++ b/include/linux/phy.h
-@@ -765,15 +765,19 @@ struct phy_tdr_config {
+@@ -774,15 +774,19 @@ struct phy_tdr_config {
* struct phy_led: An LED driven by the PHY
*
* @list: List of LEDs
/**
* struct phy_driver - Driver structure for a particular PHY type
*
-@@ -988,6 +992,15 @@ struct phy_driver {
+@@ -997,6 +1001,15 @@ struct phy_driver {
int (*get_sqi)(struct phy_device *dev);
/** @get_sqi_max: Get the maximum signal quality indication */
int (*get_sqi_max)(struct phy_device *dev);
init_data.fwnode = of_fwnode_handle(led);
--- a/include/linux/phy.h
+++ b/include/linux/phy.h
-@@ -1001,6 +1001,18 @@ struct phy_driver {
+@@ -1010,6 +1010,18 @@ struct phy_driver {
*/
int (*led_brightness_set)(struct phy_device *dev,
u8 index, enum led_brightness value);
init_data.fwnode = of_fwnode_handle(led);
--- a/include/linux/phy.h
+++ b/include/linux/phy.h
-@@ -1013,6 +1013,39 @@ struct phy_driver {
+@@ -1022,6 +1022,39 @@ struct phy_driver {
int (*led_blink_set)(struct phy_device *dev, u8 index,
unsigned long *delay_on,
unsigned long *delay_off);
sysfs_remove_link(&dev->dev.kobj, "phydev");
--- a/include/linux/phy.h
+++ b/include/linux/phy.h
-@@ -878,6 +878,12 @@ struct phy_driver {
+@@ -887,6 +887,12 @@ struct phy_driver {
/** @handle_interrupt: Override default interrupt handling */
irqreturn_t (*handle_interrupt)(struct phy_device *phydev);