From 0c52c9d6fc53ebe0834b3945c5d58277752d9648 Mon Sep 17 00:00:00 2001
From: David Bauer <mail@david-bauer.net>
Date: Fri, 3 Jan 2025 21:46:13 +0100
Subject: [PATCH] ath79: reset ETH switch for AR9344

According to datasheet, on AR9344 the switch and switch analog need to
be reset first before initiating a full reset.

Resetting these systems fixes spurious reset hangs on Atheros AR9344
SoCs.

Link: https://github.com/freifunk-gluon/gluon/issues/2904

Signed-off-by: David Bauer <mail@david-bauer.net>
(cherry picked from commit 916af73fc388d638b1a717a2411792e0680dd8e6)
---
 ...et-ath79-reset-ETH-switch-for-AR9344.patch | 104 ++++++++++++++++++
 1 file changed, 104 insertions(+)
 create mode 100644 target/linux/ath79/patches-6.6/101-reset-ath79-reset-ETH-switch-for-AR9344.patch

diff --git a/target/linux/ath79/patches-6.6/101-reset-ath79-reset-ETH-switch-for-AR9344.patch b/target/linux/ath79/patches-6.6/101-reset-ath79-reset-ETH-switch-for-AR9344.patch
new file mode 100644
index 0000000000..dcac5be58b
--- /dev/null
+++ b/target/linux/ath79/patches-6.6/101-reset-ath79-reset-ETH-switch-for-AR9344.patch
@@ -0,0 +1,104 @@
+From: David Bauer <mail@david-bauer.net>
+Date: Tue, 2 Jan 2025 19:22:40 +0100
+Subject: [PATCH] reset: ath79: reset ETH switch for AR9344
+
+According to datasheet, on AR9344 the switch and switch analog need to
+be reset first before initiating a full reset.
+
+Resetting these systems fixes spurious reset hangs on Atheros AR9344
+SoCs.
+
+Link: https://github.com/freifunk-gluon/gluon/issues/2904
+
+Signed-off-by: David Bauer <mail@david-bauer.net>
+
+--- a/drivers/reset/reset-ath79.c
++++ b/drivers/reset/reset-ath79.c
+@@ -12,8 +12,11 @@
+ #include <linux/platform_device.h>
+ #include <linux/reset-controller.h>
+ #include <linux/reboot.h>
++#include <linux/delay.h>
++#include <linux/of.h>
+ 
+ struct ath79_reset {
++	struct platform_device *pdev;
+ 	struct reset_controller_dev rcdev;
+ 	struct notifier_block restart_nb;
+ 	void __iomem *base;
+@@ -21,16 +24,13 @@ struct ath79_reset {
+ };
+ 
+ #define FULL_CHIP_RESET 24
++#define ETH_SWITCH_RESET 8
++#define ETH_SWITCH_ARESET 12
+ 
+-static int ath79_reset_update(struct reset_controller_dev *rcdev,
++static void __ath79_reset_update_unlocked(struct ath79_reset *ath79_reset,
+ 			unsigned long id, bool assert)
+ {
+-	struct ath79_reset *ath79_reset =
+-		container_of(rcdev, struct ath79_reset, rcdev);
+-	unsigned long flags;
+ 	u32 val;
+-
+-	spin_lock_irqsave(&ath79_reset->lock, flags);
+ 	val = readl(ath79_reset->base);
+ 	if (assert)
+ 		val |= BIT(id);
+@@ -39,6 +39,17 @@ static int ath79_reset_update(struct res
+ 	writel(val, ath79_reset->base);
+ 	/* Flush cache */
+ 	readl(ath79_reset->base);
++}
++
++static int ath79_reset_update(struct reset_controller_dev *rcdev,
++			unsigned long id, bool assert)
++{
++	struct ath79_reset *ath79_reset =
++		container_of(rcdev, struct ath79_reset, rcdev);
++	unsigned long flags;
++
++	spin_lock_irqsave(&ath79_reset->lock, flags);
++	__ath79_reset_update_unlocked(ath79_reset, id, assert);
+ 	spin_unlock_irqrestore(&ath79_reset->lock, flags);
+ 
+ 	return 0;
+@@ -79,8 +90,27 @@ static int ath79_reset_restart_handler(s
+ {
+ 	struct ath79_reset *ath79_reset =
+ 		container_of(nb, struct ath79_reset, restart_nb);
++	unsigned long flags;
+ 
+-	ath79_reset_assert(&ath79_reset->rcdev, FULL_CHIP_RESET);
++	spin_lock_irqsave(&ath79_reset->lock, flags);
++
++	if (of_device_is_compatible(ath79_reset->pdev->dev.of_node, "qca,ar9340-reset")) {
++		/**
++		 * AR9344 has been observed to hang on reboot in rare cases.
++		 *
++		 * Datasheet states to reset the ETH switch systems before asserting
++		 * full chip reset. See page 111 of the AR9344 datasheet.
++		 */
++		__ath79_reset_update_unlocked(ath79_reset, ETH_SWITCH_RESET, true);
++		mdelay(10);
++		__ath79_reset_update_unlocked(ath79_reset, ETH_SWITCH_ARESET, true);
++		mdelay(10);
++	}
++
++	__ath79_reset_update_unlocked(ath79_reset, FULL_CHIP_RESET, true);
++	mdelay(10);
++
++	spin_unlock_irqrestore(&ath79_reset->lock, flags);
+ 
+ 	return NOTIFY_DONE;
+ }
+@@ -95,6 +125,8 @@ static int ath79_reset_probe(struct plat
+ 	if (!ath79_reset)
+ 		return -ENOMEM;
+ 
++	ath79_reset->pdev = pdev;
++
+ 	ath79_reset->base = devm_platform_ioremap_resource(pdev, 0);
+ 	if (IS_ERR(ath79_reset->base))
+ 		return PTR_ERR(ath79_reset->base);
-- 
2.30.2