[][kernel][common][eth][Update gangload timeout mechanism for the Aquantia PHY driver]

[Description]
Refactor gangload timeout mechanism for the Aquantia PHY driver.

In the previous commit [0], we added a timeout mechanism to
the gangload mode, it will force download firmware into the 10G PHY
when there is only one 10G PHY IC on the board.
However, we found that previous commit might experience a race
condition between the heartbeat thread and the gangload function.

[0] https://git01.mediatek.com/plugins/gitiles/openwrt/feeds/
mtk-openwrt-feeds/+/9451bcd2b4bd78b1c9fee5303a793d5077e60149

Without this patch, the gangload mode may fail to download firmware
on the board without WiFi.

[Release-log]
N/A


Change-Id: I62ad1aa25fe467dc5c121822a278d4ceca217f56
Reviewed-on: https://gerrit.mediatek.inc/c/openwrt/feeds/mtk_openwrt_feeds/+/7951514
diff --git a/target/linux/mediatek/patches-5.4/999-2719-net-phy-aquantia-add-firmware-download.patch b/target/linux/mediatek/patches-5.4/999-2719-net-phy-aquantia-add-firmware-download.patch
index 170b841..407896c 100644
--- a/target/linux/mediatek/patches-5.4/999-2719-net-phy-aquantia-add-firmware-download.patch
+++ b/target/linux/mediatek/patches-5.4/999-2719-net-phy-aquantia-add-firmware-download.patch
@@ -140,7 +140,7 @@
  #endif
 +
 +#ifdef CONFIG_AQUANTIA_PHY_FW_DOWNLOAD
-+enum {
++enum aqr_fw_dl_mode {
 +	FW_DL_SINGLE = 0,
 +	FW_DL_GNAGLOAD,
 +};
@@ -153,7 +153,7 @@
 index 000000000..d2828aad4
 --- /dev/null
 +++ b/drivers/net/phy/aquantia_firmware.c
-@@ -0,0 +1,1100 @@
+@@ -0,0 +1,1109 @@
 +// SPDX-License-Identifier: GPL-2.0
 +/* FW download driver for Aquantia PHY
 + */
@@ -301,8 +301,9 @@
 +struct phy_device *gangload_phydevs[MAX_GANGLOAD_DEVICES];
 +static unsigned long gangload_timeout = 0;
 +static int gangload = 0;
++static DEFINE_SPINLOCK(gangload_lock);
 +
-+static int aqr_firmware_download_single(struct phy_device *phydev);
++static int aqr_firmware_download_single(struct phy_device *phydev, bool force_reload);
 +
 +void AQ_API_EnableMDIO_BootLoadMode
 +(
@@ -1068,9 +1069,7 @@
 +		if (priv->fw_initialized == true &&
 +		    aqr_firmware_check_heartbeat(phydev) == 1) {
 +			dev_err(dev, "Detect heartbeat stopped, start to realod firmware...\n");
-+			priv->fw_initialized = false;
-+			priv->fw_dl_mode = FW_DL_SINGLE;
-+			aqr_firmware_download_single(phydev);
++			aqr_firmware_download_single(phydev, true);
 +		}
 +
 +		set_current_state(TASK_INTERRUPTIBLE);
@@ -1092,12 +1091,14 @@
 +	if (!fw)
 +		return;
 +
++	spin_lock(&gangload_lock);
++
 +	num_phydevs = priv->fw_dl_mode == FW_DL_GNAGLOAD ?
 +		      gangload : 1;
 +
 +retry:
-+	if (gandload_phydev->state == PHY_HALTED) {
-+		dev_info(dev, "Detect PHY power down, stop to reload firmware...\n");
++	if (gandload_phydev->state == PHY_HALTED && priv->fw_initialized == true) {
++		dev_info(dev, "Detect PHY power down, stop to download firmware...\n");
 +		goto out;
 +	}
 +
@@ -1139,23 +1140,28 @@
 +			}
 +		}
 +	}
++
 +out:
 +	release_firmware(fw);
++
++	spin_unlock(&gangload_lock);
 +}
 +
-+static int aqr_firmware_download_single(struct phy_device *phydev)
++static int aqr_firmware_download_single(struct phy_device *phydev, bool force_reload)
 +{
 +	struct aqr107_priv *priv = phydev->priv;
 +	struct device *dev = &phydev->mdio.dev;
 +	const struct firmware *fw;
 +	int ret = 0;
 +
-+	if (priv->fw_initialized == true)
++	if (priv->fw_initialized == true && force_reload == false)
 +		return 0;
 +
-+	spin_lock_init(&priv->lock);
++	if (priv->phydevs[0] != phydev) {
++		priv->phydevs[0] = phydev;
++		spin_lock_init(&priv->lock);
++	}
 +	priv->fw_dl_mode = FW_DL_SINGLE;
-+	priv->phydevs[0] = phydev;
 +	priv->heartbeat = -1;
 +
 +	ret = request_firmware(&fw, AQR_FIRMWARE, dev);
@@ -1224,18 +1230,21 @@
 +		wake_up_process(gangload_kthread);
 +	}
 +
-+	/* fall back to single mode if timeout is reached */
-+	if (time_after(jiffies, gangload_timeout))
-+		return aqr_firmware_download_single(phydev);
-+
 +	for (i = 0; i < gangload; i++) {
 +		if (gangload_phydevs[i] == phydev) {
 +			dev_warn(dev, "Detect duplicate gangload phydev\n");
-+			return -EINVAL;
++			return 0;
 +		}
 +	}
 +
-+	spin_lock_init(&priv->lock);
++	/* fall back to single mode if timeout is reached */
++	if (time_after(jiffies, gangload_timeout))
++		return aqr_firmware_download_single(phydev, false);
++
++	if (priv->phydevs[0] != phydev) {
++		priv->phydevs[0] = phydev;
++		spin_lock_init(&priv->lock);
++	}
 +	priv->fw_dl_mode = FW_DL_GNAGLOAD;
 +	priv->heartbeat = -1;
 +	gangload_phydevs[gangload] = phydev;
@@ -1248,7 +1257,7 @@
 +{
 +	int ret = 0;
 +#ifdef CONFIG_AQUANTIA_PHY_FW_DOWNLOAD_SINGLE
-+	ret = aqr_firmware_download_single(phydev);
++	ret = aqr_firmware_download_single(phydev, false);
 +#elif CONFIG_AQUANTIA_PHY_FW_DOWNLOAD_GANG
 +	ret = aqr_firmware_download_gang(phydev);
 +#endif
@@ -1383,20 +1392,21 @@
  
  	return aqr107_set_downshift(phydev, MDIO_AN_VEND_PROV_DOWNSHIFT_DFLT);
  }
-@@ -574,12 +559,41 @@ static void aqr107_link_change_notify(struct phy_device *phydev)
+@@ -574,12 +559,43 @@ static void aqr107_link_change_notify(struct phy_device *phydev)
  
  static int aqr107_suspend(struct phy_device *phydev)
  {
 +#ifdef CONFIG_AQUANTIA_PHY_FW_DOWNLOAD
 +	struct aqr107_priv *priv = phydev->priv;
 +
-+	spin_lock(&priv->lock);
-+	if (priv->heartbeat_thread) {
-+		kthread_stop(priv->heartbeat_thread);
-+		priv->heartbeat_thread = NULL;
-+		priv->heartbeat = -1;
++	if (spin_trylock(&priv->lock)) {
++		if (priv->heartbeat_thread) {
++			kthread_stop(priv->heartbeat_thread);
++			priv->heartbeat_thread = NULL;
++			priv->heartbeat = -1;
++		}
++		spin_unlock(&priv->lock);
 +	}
-+	spin_unlock(&priv->lock);
 +#endif
  	return phy_set_bits_mmd(phydev, MDIO_MMD_VEND1, MDIO_CTRL1,
  				MDIO_CTRL1_LPOWER);
@@ -1407,20 +1417,21 @@
 +#ifdef CONFIG_AQUANTIA_PHY_FW_DOWNLOAD
 +	struct aqr107_priv *priv = phydev->priv;
 +
-+	spin_lock(&priv->lock);
-+	if (!priv->heartbeat_thread) {
-+		priv->heartbeat_thread = kthread_create(aqr_firmware_heartbeat_thread,
-+							phydev,
-+							"aqr_firmware_heartbeat_thread");
-+		if (IS_ERR(priv->heartbeat_thread)) {
-+			phydev_err(phydev,
-+				   "Failed to create aqr_firmware_heartbeat_thread(%ld)\n",
-+				    PTR_ERR(priv->heartbeat_thread));
-+			priv->heartbeat_thread = NULL;
-+		} else
-+			wake_up_process(priv->heartbeat_thread);
++	if (spin_trylock(&priv->lock)) {
++		if (!priv->heartbeat_thread) {
++			priv->heartbeat_thread = kthread_create(aqr_firmware_heartbeat_thread,
++								phydev,
++								"aqr_firmware_heartbeat_thread");
++			if (IS_ERR(priv->heartbeat_thread)) {
++				phydev_err(phydev,
++					   "Failed to create aqr_firmware_heartbeat_thread(%ld)\n",
++					    PTR_ERR(priv->heartbeat_thread));
++				priv->heartbeat_thread = NULL;
++			} else
++				wake_up_process(priv->heartbeat_thread);
++		}
++		spin_unlock(&priv->lock);
 +	}
-+	spin_unlock(&priv->lock);
 +#endif
  	return phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1, MDIO_CTRL1,
  				  MDIO_CTRL1_LPOWER);