Add the capability to detect a resume on J7200, J784s4, J721e, j722s,
j721s2 SoCs.

To detect the resume, SPL searches a magic value (0xBA) in a register
of PMIC.
This value is set by DM-Firmware during the suspend sequence.

NB: As this is called in board_init_f(), there's no BSS, so we can't
use global/static variables.

NB2: For j722s HS-* devices, we need an extra call to ctrl_mmr_unlock(),
and we have to use I2C API instead of PMIC API for this to work.
This is because bind is called before anything comes up, in between either
bootrom or TIFS messing up, clearing pad config and pd config.

Signed-off-by: Richard Genoud (TI) <[email protected]>
---
 arch/arm/mach-k3/common.c                |  6 ++
 arch/arm/mach-k3/include/mach/hardware.h |  1 +
 arch/arm/mach-k3/r5/lpm-common.c         | 78 +++++++++++++++++++++++-
 3 files changed, 83 insertions(+), 2 deletions(-)

diff --git a/arch/arm/mach-k3/common.c b/arch/arm/mach-k3/common.c
index 6e382efdb3be..d934f732b330 100644
--- a/arch/arm/mach-k3/common.c
+++ b/arch/arm/mach-k3/common.c
@@ -713,3 +713,9 @@ int spl_start_uboot(void)
        return 0;
 }
 #endif
+
+/* if r5/lpm-common.c is compiled, this will be overridden */
+__weak bool j7xx_board_is_resuming(void)
+{
+       return false;
+}
diff --git a/arch/arm/mach-k3/include/mach/hardware.h 
b/arch/arm/mach-k3/include/mach/hardware.h
index 2c771a1195f5..c45bd3747163 100644
--- a/arch/arm/mach-k3/include/mach/hardware.h
+++ b/arch/arm/mach-k3/include/mach/hardware.h
@@ -137,6 +137,7 @@ u32 get_boot_device(void);
 const char *get_reset_reason(void);
 enum k3_device_type get_device_type(void);
 int k3_fit_config_match_security_state(const char *name);
+bool j7xx_board_is_resuming(void);
 
 #define writel_verify(val, addr) \
 do { \
diff --git a/arch/arm/mach-k3/r5/lpm-common.c b/arch/arm/mach-k3/r5/lpm-common.c
index 9b91081e4f0f..c1a8c8e940de 100644
--- a/arch/arm/mach-k3/r5/lpm-common.c
+++ b/arch/arm/mach-k3/r5/lpm-common.c
@@ -6,18 +6,28 @@
  * Copyright (C) 2026 Bootlin
  */
 
+#include <asm/global_data.h>
 #include <clk.h>
+#include <dm/device.h>
 #include <dm/read.h>
 #include <elf.h>
+#include <i2c.h>
 #include <linux/printk.h>
 #include <linux/soc/ti/ti_sci_protocol.h>
 #include <power-domain.h>
+#include <power/pmic.h>
 #include <remoteproc.h>
 #include <mach/security.h>
 
 #include "../common.h"
 #include "../lpm-common.h"
 
+/* Magic value in PMIC register to indicate the suspend state (SOC_OFF) */
+#define K3_LPM_MAGIC_SUSPEND 0xba
+
+/* PMIC register where the magic value resides */
+#define K3_LPM_SCRATCH_PAD_REG_3 0xcb
+
 #define FW_IMAGE_SIZE 0x80000
 
 struct lpm_addr_info {
@@ -28,9 +38,73 @@ struct lpm_addr_info {
        u32 size;
 };
 
-__weak bool j7xx_board_is_resuming(void)
+/* This is used by J722s */
+__weak void ctrl_mmr_unlock(void) { }
+
+/* in board_init_f(), there's no BSS, so we can't use global/static variables 
*/
+bool j7xx_board_is_resuming(void)
 {
-       return false;
+       struct udevice *pmic, *i2c;
+       int ret, magic;
+
+       if (gd_k3_resuming() != K3_RESUME_STATE_UNKNOWN)
+               goto end;
+
+       if (IS_ENABLED(CONFIG_SOC_K3_J722S)) {
+               /*
+                * On J722S devices, i2c access fails unless MMR
+                * registers are unlocked.
+                * Moreover, it fails also if we use PMIC API instead of I2C 
API.
+                */
+               ctrl_mmr_unlock();
+               ret = uclass_get_device_by_name(UCLASS_I2C,
+                                               "i2c@2b200000", &i2c);
+               if (ret) {
+                       printf("Getting I2C failed: %d\n", ret);
+                       goto end;
+               }
+               ret = dm_i2c_probe(i2c, 0x48, 0, &pmic);
+               if (ret) {
+                       printf("Getting PMIC failed: %d\n", ret);
+                       goto end;
+               }
+       } else {
+               ret = uclass_get_device_by_name(UCLASS_PMIC,
+                                               "pmic@48", &pmic);
+               if (ret) {
+                       printf("Getting PMIC init failed: %d\n", ret);
+                       goto end;
+               }
+       }
+       debug("%s: PMIC is detected (%s)\n", __func__, pmic->name);
+
+       if (IS_ENABLED(CONFIG_SOC_K3_J722S))
+               magic = dm_i2c_reg_read(pmic, K3_LPM_SCRATCH_PAD_REG_3);
+       else
+               magic = pmic_reg_read(pmic, K3_LPM_SCRATCH_PAD_REG_3);
+
+       if (magic == K3_LPM_MAGIC_SUSPEND) {
+               debug("%s: board is resuming\n", __func__);
+               gd_set_k3_resuming(K3_RESUME_STATE_RESUMING);
+
+               /* clean magic suspend */
+               if (IS_ENABLED(CONFIG_SOC_K3_J722S))
+                       ret = dm_i2c_reg_write(pmic, K3_LPM_SCRATCH_PAD_REG_3, 
0);
+               else
+                       ret = pmic_reg_write(pmic, K3_LPM_SCRATCH_PAD_REG_3, 0);
+
+               if (ret)
+                       printf("Failed to clean magic value for suspend 
detection in PMIC\n");
+               /*
+                * For robustness, the DM should also clean the magic value at
+                * startup.
+                */
+       } else {
+               debug("%s: board is booting (no resume detected)\n", __func__);
+               gd_set_k3_resuming(K3_RESUME_STATE_BOOTING);
+       }
+end:
+       return gd_k3_resuming() == K3_RESUME_STATE_RESUMING;
 }
 
 static const char *lpm_core_compatible;

Reply via email to