|
@@ -0,0 +1,105 @@
|
|
|
+From: David Bauer <[email protected]>
|
|
|
+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 <[email protected]>
|
|
|
+
|
|
|
+--- 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,28 @@ 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(1);
|
|
|
++ __ath79_reset_update_unlocked(ath79_reset, ETH_SWITCH_ARESET, true);
|
|
|
++ mdelay(1);
|
|
|
++ __ath79_reset_update_unlocked(ath79_reset, FULL_CHIP_RESET, true);
|
|
|
++ mdelay(10);
|
|
|
++ } else {
|
|
|
++ __ath79_reset_update_unlocked(ath79_reset, FULL_CHIP_RESET, true);
|
|
|
++ }
|
|
|
++
|
|
|
++ spin_unlock_irqrestore(&ath79_reset->lock, flags);
|
|
|
+
|
|
|
+ return NOTIFY_DONE;
|
|
|
+ }
|
|
|
+@@ -96,6 +127,8 @@ static int ath79_reset_probe(struct plat
|
|
|
+ if (!ath79_reset)
|
|
|
+ return -ENOMEM;
|
|
|
+
|
|
|
++ ath79_reset->pdev = pdev;
|
|
|
++
|
|
|
+ platform_set_drvdata(pdev, ath79_reset);
|
|
|
+
|
|
|
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|