From: David Bauer 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 --- a/drivers/reset/reset-ath79.c +++ b/drivers/reset/reset-ath79.c @@ -12,8 +12,11 @@ #include #include #include +#include +#include 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);