Skip to content

Commit

Permalink
ath79: reset ETH switch for AR9344
Browse files Browse the repository at this point in the history
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: freifunk-gluon/gluon#2904

Signed-off-by: David Bauer <[email protected]>
  • Loading branch information
blocktrron committed Jan 5, 2025
1 parent 39b61ee commit 916af73
Showing 1 changed file with 104 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
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,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);

0 comments on commit 916af73

Please sign in to comment.