--- /dev/null
+From cd4fb0bb4a49aad79adedcee6a2bd8185935cc3f Mon Sep 17 00:00:00 2001
+From: David Bauer <mail@david-bauer.net>
+Date: Fri, 10 Apr 2020 17:57:37 +0200
+Subject: [PATCH 1/4] net: phy: at803x: use phydev phy-mode in aneg_done
+
+at803x_aneg_done currently reads the chip configuration register to
+determine the operation mode of the AR8031/AR8033 PHY.
+
+This read has to be performed every time the link-state changes. It's
+also inconsistent, as some places already use the phy-mode defined for
+the phydev.
+
+Use the phy-mode of the phydev to remove this read and be more
+consistent throughout the driver.
+
+Signed-off-by: David Bauer <mail@david-bauer.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
+@@ -348,8 +348,7 @@ static int at803x_aneg_done(struct phy_d
+ * in SGMII mode, if copper side autoneg is successful,
+ * also check SGMII side autoneg result
+ */
+- ccr = phy_read(phydev, AT803X_REG_CHIP_CONFIG);
+- if ((ccr & AT803X_MODE_CFG_MASK) != AT803X_MODE_CFG_SGMII)
++ if (phydev->interface != PHY_INTERFACE_MODE_SGMII)
+ return aneg_done;
+
+ /* switch to SGMII/fiber page */
--- /dev/null
+From 0c486c4fc5102f73fed4a203018616871a7855b2 Mon Sep 17 00:00:00 2001
+From: David Bauer <mail@david-bauer.net>
+Date: Tue, 17 Mar 2020 21:21:14 +0100
+Subject: [PATCH 2/4] net: phy: at803x: use paged reads
+
+Refactor the page-switching in at803x_aneg_done to use
+paged reads as suggested by Andrew Lunn.
+
+This way, potential race conditions are avoided, as the
+MDIO lock was previously not taken.
+
+Signed-off-by: David Bauer <mail@david-bauer.net>
+---
+ drivers/net/phy/at803x.c | 40 ++++++++++++++++++++++++++++++++++------
+ 1 file changed, 34 insertions(+), 6 deletions(-)
+
+--- a/drivers/net/phy/at803x.c
++++ b/drivers/net/phy/at803x.c
+@@ -62,6 +62,9 @@
+ #define AT803X_DEBUG_REG_5 0x05
+ #define AT803X_DEBUG_TX_CLK_DLY_EN BIT(8)
+
++#define AT803X_PAGE_FIBER 0
++#define AT803X_PAGE_COPPER 1
++
+ #define ATH8030_PHY_ID 0x004dd076
+ #define ATH8031_PHY_ID 0x004dd074
+ #define ATH8035_PHY_ID 0x004dd072
+@@ -112,6 +115,32 @@ static int at803x_debug_reg_mask(struct
+ return phy_write(phydev, AT803X_DEBUG_DATA, val);
+ }
+
++static int at803x_write_page(struct phy_device *phydev, int page)
++{
++ int mask;
++ int set;
++
++ if (page == AT803X_PAGE_COPPER)
++ set = AT803X_BT_BX_REG_SEL;
++ else
++ mask = AT803X_BT_BX_REG_SEL;
++
++ return __phy_modify_changed(phydev, AT803X_REG_CHIP_CONFIG, mask, set);
++}
++
++static int at803x_read_page(struct phy_device *phydev)
++{
++ int ccr = __phy_read(phydev, AT803X_REG_CHIP_CONFIG);
++
++ if (ccr < 0)
++ return ccr;
++
++ if (ccr & AT803X_BT_BX_REG_SEL)
++ return AT803X_PAGE_COPPER;
++
++ return AT803X_PAGE_FIBER;
++}
++
+ static int at803x_enable_rx_delay(struct phy_device *phydev)
+ {
+ return at803x_debug_reg_mask(phydev, AT803X_DEBUG_REG_0, 0,
+@@ -338,7 +367,7 @@ static void at803x_link_change_notify(st
+
+ static int at803x_aneg_done(struct phy_device *phydev)
+ {
+- int ccr;
++ int pssr;
+
+ int aneg_done = genphy_aneg_done(phydev);
+ if (aneg_done != BMSR_ANEGCOMPLETE)
+@@ -351,16 +380,13 @@ static int at803x_aneg_done(struct phy_d
+ if (phydev->interface != PHY_INTERFACE_MODE_SGMII)
+ return aneg_done;
+
+- /* switch to SGMII/fiber page */
+- phy_write(phydev, AT803X_REG_CHIP_CONFIG, ccr & ~AT803X_BT_BX_REG_SEL);
++ pssr = phy_read_paged(phydev, AT803X_PAGE_FIBER, AT803X_PSSR);
+
+ /* check if the SGMII link is OK. */
+- if (!(phy_read(phydev, AT803X_PSSR) & AT803X_PSSR_MR_AN_COMPLETE)) {
++ if (!(pssr & AT803X_PSSR_MR_AN_COMPLETE)) {
+ phydev_warn(phydev, "803x_aneg_done: SGMII link is not ok\n");
+ aneg_done = 0;
+ }
+- /* switch back to copper page */
+- phy_write(phydev, AT803X_REG_CHIP_CONFIG, ccr | AT803X_BT_BX_REG_SEL);
+
+ return aneg_done;
+ }
+@@ -465,6 +491,8 @@ static struct phy_driver at803x_driver[]
+ .get_wol = at803x_get_wol,
+ .suspend = at803x_suspend,
+ .resume = at803x_resume,
++ .read_page = at803x_read_page,
++ .write_page = at803x_write_page,
+ /* PHY_GBIT_FEATURES */
+ .read_status = at803x_read_status,
+ .aneg_done = at803x_aneg_done,
--- /dev/null
+From 0a8c84ba82f9e55cf9c97394a0a83a9318b64a66 Mon Sep 17 00:00:00 2001
+From: David Bauer <mail@david-bauer.net>
+Date: Sun, 5 Apr 2020 02:29:36 +0200
+Subject: [PATCH 3/4] net: phy: at803x: select correct page on config init
+
+The Atheros AR8031 and AR8033 expose different registers for SGMII/Fiber
+as well as the copper side of the PHY depending on the BT_BX_REG_SEL bit
+in the chip configure register.
+
+The driver assumes the copper side is selected on probe, but this might
+not be the case depending which page was last selected by the
+bootloader.
+
+Select the copper page when initializing the configuration to circumvent
+this.
+
+Signed-off-by: David Bauer <mail@david-bauer.net>
+---
+ drivers/net/phy/at803x.c | 7 +++++++
+ 1 file changed, 7 insertions(+)
+
+--- a/drivers/net/phy/at803x.c
++++ b/drivers/net/phy/at803x.c
+@@ -287,6 +287,13 @@ static int at803x_config_init(struct phy
+ {
+ int ret;
+
++ if (phydev->drv->phy_id == ATH8031_PHY_ID) {
++ ret = phy_select_page(phydev, AT803X_PAGE_COPPER);
++ if (ret < 0)
++ return ret;
++ mutex_unlock(&phydev->mdio.bus->mdio_lock);
++ }
++
+ /* 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
--- /dev/null
+From 84ad131ed1a668cf656cb5f6921db4012b2edef3 Mon Sep 17 00:00:00 2001
+From: David Bauer <mail@david-bauer.net>
+Date: Fri, 10 Apr 2020 17:54:58 +0200
+Subject: [PATCH 4/4] net: phy: at803x: enable SGMII aneg on config init
+
+Some bootloaders disable the fiber-side autonegotiation on the AR8033,
+expecting it to be enabled by the kernel.
+This however is currently not the case with at803x, leaving the SGMII
+side unusable.
+
+Activate the fiber-side autonegotiation to make the SGMII link work.
+
+Signed-off-by: David Bauer <mail@david-bauer.net>
+---
+ drivers/net/phy/at803x.c | 8 ++++++++
+ 1 file changed, 8 insertions(+)
+
+--- a/drivers/net/phy/at803x.c
++++ b/drivers/net/phy/at803x.c
+@@ -288,6 +288,14 @@ static int at803x_config_init(struct phy
+ int ret;
+
+ if (phydev->drv->phy_id == ATH8031_PHY_ID) {
++ if (phydev->interface == PHY_INTERFACE_MODE_SGMII) {
++ ret = phy_modify_paged_changed(phydev,
++ AT803X_PAGE_FIBER,
++ MII_BMCR, 0,
++ BMCR_ANENABLE);
++ if (ret < 0)
++ return ret;
++ }
+ ret = phy_select_page(phydev, AT803X_PAGE_COPPER);
+ if (ret < 0)
+ return ret;
+++ /dev/null
-From: Roman Yeryomin <roman@advem.lv>
-Subject: kernel: add at803x fix for sgmii mode
-
-Some (possibly broken) bootloaders incorreclty initialize at8033
-phy. This patch enables sgmii autonegotiation mode.
-
-[john@phrozen.org: felix added this to his upstream queue]
-
-Signed-off-by: Roman Yeryomin <roman@advem.lv>
----
- drivers/net/phy/at803x.c | 25 +++++++++++++++++++++++++
- 1 file changed, 25 insertions(+)
-
---- a/drivers/net/phy/at803x.c
-+++ b/drivers/net/phy/at803x.c
-@@ -46,6 +46,7 @@
- #define AT803X_LOC_MAC_ADDR_32_47_OFFSET 0x804A
- #define AT803X_REG_CHIP_CONFIG 0x1f
- #define AT803X_BT_BX_REG_SEL 0x8000
-+#define AT803X_SGMII_ANEG_EN 0x1000
-
- #define AT803X_DEBUG_ADDR 0x1D
- #define AT803X_DEBUG_DATA 0x1E
-@@ -259,6 +260,27 @@ static int at803x_probe(struct phy_devic
- static int at803x_config_init(struct phy_device *phydev)
- {
- int ret;
-+ u32 v;
-+
-+ if (phydev->drv->phy_id == ATH8031_PHY_ID &&
-+ phydev->interface == PHY_INTERFACE_MODE_SGMII)
-+ {
-+ v = phy_read(phydev, AT803X_REG_CHIP_CONFIG);
-+ /* select SGMII/fiber page */
-+ ret = phy_write(phydev, AT803X_REG_CHIP_CONFIG,
-+ v & ~AT803X_BT_BX_REG_SEL);
-+ if (ret)
-+ return ret;
-+ /* enable SGMII autonegotiation */
-+ ret = phy_write(phydev, MII_BMCR, AT803X_SGMII_ANEG_EN);
-+ if (ret)
-+ return ret;
-+ /* select copper page */
-+ ret = phy_write(phydev, AT803X_REG_CHIP_CONFIG,
-+ v | AT803X_BT_BX_REG_SEL);
-+ if (ret)
-+ return ret;
-+ }
-
- /* The RX and TX delay default is:
- * after HW reset: RX delay enabled and TX delay disabled