git: 6325f105aad2 - main - ipq4018: toggle ps-hold to allow SoC reset

From: Adrian Chadd <adrian_at_FreeBSD.org>
Date: Thu, 04 Nov 2021 16:02:50 UTC
The branch main has been updated by adrian:

URL: https://cgit.FreeBSD.org/src/commit/?id=6325f105aad2d6d8ba08efe5aff1c860cc7df198

commit 6325f105aad2d6d8ba08efe5aff1c860cc7df198
Author:     Adrian Chadd <adrian@FreeBSD.org>
AuthorDate: 2021-10-20 05:33:27 +0000
Commit:     Adrian Chadd <adrian@FreeBSD.org>
CommitDate: 2021-11-04 16:02:21 +0000

    ipq4018: toggle ps-hold to allow SoC reset
    
    This is enough to allow this ASUS router to reboot successfully.
    I tried the watchdog path and although it fires, it isn't rebooting!
    It's just hanging, likely somewhere in TZ.
    
    Tested:
    
    * ASUS RT-AC58U router, IPQ4019
    
    Reviewed by: andrew, manu, imp
    Differential Revision: https://reviews.freebsd.org/D32723
---
 sys/arm/qualcomm/ipq4018_machdep.c | 42 ++++++++++++++++++++++++++++++++++++++
 sys/arm/qualcomm/ipq4018_reg.h     |  3 +++
 2 files changed, 45 insertions(+)

diff --git a/sys/arm/qualcomm/ipq4018_machdep.c b/sys/arm/qualcomm/ipq4018_machdep.c
index b3f841575ebb..39510c8294ae 100644
--- a/sys/arm/qualcomm/ipq4018_machdep.c
+++ b/sys/arm/qualcomm/ipq4018_machdep.c
@@ -36,10 +36,12 @@ __FBSDID("$FreeBSD$");
 #include <sys/reboot.h>
 #include <sys/devmap.h>
 #include <sys/physmem.h>
+#include <sys/lock.h>
 
 #include <vm/vm.h>
 
 #include <machine/bus.h>
+#include <machine/fdt.h>
 #include <machine/intr.h>
 #include <machine/machdep.h>
 #include <machine/platformvar.h>
@@ -94,12 +96,52 @@ ipq4018_devmap_init(platform_t plat)
 	 * a call to pmap_mapdev() when the bus space code is doing its thing.
 	 */
 	devmap_add_entry(IPQ4018_MEM_UART1_START, IPQ4018_MEM_UART1_SIZE);
+
+	/*
+	 * This covers a bunch of the reset block, which includes the PS-HOLD
+	 * register for dropping power.
+	 */
+	devmap_add_entry(IPQ4018_MEM_PSHOLD_START, IPQ4018_MEM_PSHOLD_SIZE);
+
 	return (0);
 }
 
+/*
+ * This toggles the PS-HOLD register which on most IPQ devices will toggle
+ * the power control block and reset the SoC.
+ *
+ * However, there are apparently some units out there where this is not
+ * appropriate and instead the watchdog needs to be used.
+ *
+ * For now since there's only going to be one or two initial supported boards
+ * this will be fine.  But if this doesn't reboot cleanly, now you know.
+ */
+static void
+ipq4018_cpu_reset_pshold(void)
+{
+	bus_space_handle_t pshold;
+
+	printf("%s: called\n", __func__);
+
+	bus_space_map(fdtbus_bs_tag, IPQ4018_MEM_PSHOLD_START,
+	    IPQ4018_MEM_PSHOLD_SIZE, 0, &pshold);
+	bus_space_write_4(fdtbus_bs_tag, pshold, 0, 0);
+	bus_space_barrier(fdtbus_bs_tag, pshold, 0, 0x4,
+	    BUS_SPACE_BARRIER_WRITE);
+}
+
 static void
 ipq4018_cpu_reset(platform_t plat)
 {
+	spinlock_enter();
+	dsb();
+
+	ipq4018_cpu_reset_pshold();
+
+	/* Spin */
+	printf("%s: spinning\n", __func__);
+	while(1)
+		;
 }
 
 /*
diff --git a/sys/arm/qualcomm/ipq4018_reg.h b/sys/arm/qualcomm/ipq4018_reg.h
index 945d650dcfd6..9c09605eab1a 100644
--- a/sys/arm/qualcomm/ipq4018_reg.h
+++ b/sys/arm/qualcomm/ipq4018_reg.h
@@ -39,4 +39,7 @@
 #define	IPQ4018_MEM_UART1_START		0x078af000
 #define	IPQ4018_MEM_UART1_SIZE		0x00001000
 
+#define	IPQ4018_MEM_PSHOLD_START	0x004ab000
+#define	IPQ4018_MEM_PSHOLD_SIZE		0x00001000
+
 #endif