The family of PCA9450 PMICs have the ability to perform system resets.
Restarting via PMIC is preferred method of restarting the system as all
the peripherals are brought to a know state after a power-cycle. The
PCA9450 features a cold restart procedure which is initiated by an I2C
command 0x14 to the SW_RST register.

Support in Linux for restarting via PCA9450 PMIC has been added by
Linux commit 6157e62b07d9 ("regulator: pca9450: Add restart handler").

Now add support for it also in the U-Boot via sysreset framework.

Signed-off-by: Primoz Fiser <primoz.fi...@norik.com>
---
Change in v2:
- no change

Link to v1: 
https://lore.kernel.org/all/20250716114547.1343797-1-primoz.fi...@norik.com/

 drivers/power/pmic/pca9450.c | 42 ++++++++++++++++++++++++++++++++++++
 include/power/pca9450.h      |  2 ++
 2 files changed, 44 insertions(+)

diff --git a/drivers/power/pmic/pca9450.c b/drivers/power/pmic/pca9450.c
index 9d875f8bdbe8..abc39d0a1946 100644
--- a/drivers/power/pmic/pca9450.c
+++ b/drivers/power/pmic/pca9450.c
@@ -7,15 +7,18 @@
 #include <errno.h>
 #include <dm.h>
 #include <dm/device_compat.h>
+#include <dm/lists.h>
 #include <i2c.h>
 #include <linux/err.h>
 #include <log.h>
 #include <asm/global_data.h>
 #include <asm-generic/gpio.h>
+#include <linux/delay.h>
 #include <linux/printk.h>
 #include <power/pmic.h>
 #include <power/regulator.h>
 #include <power/pca9450.h>
+#include <sysreset.h>
 
 DECLARE_GLOBAL_DATA_PTR;
 
@@ -101,6 +104,14 @@ static int pca9450_probe(struct udevice *dev)
                }
        }
 
+       if (CONFIG_IS_ENABLED(SYSRESET)) {
+               ret = device_bind_driver_to_node(dev, "pca9450_sysreset",
+                                                "pca9450_sysreset",
+                                                dev_ofnode(dev), NULL);
+               if (ret)
+                       return ret;
+       }
+
        if (ofnode_read_bool(dev_ofnode(dev), "nxp,wdog_b-warm-reset"))
                reset_ctrl = PCA9450_PMIC_RESET_WDOG_B_CFG_WARM;
        else
@@ -134,3 +145,34 @@ U_BOOT_DRIVER(pmic_pca9450) = {
        .ops = &pca9450_ops,
        .priv_auto = sizeof(struct pca9450_priv),
 };
+
+#ifdef CONFIG_SYSRESET
+static int pca9450_sysreset_request(struct udevice *dev, enum sysreset_t type)
+{
+       u8 cmd = PCA9450_SW_RST_COLD_RST;
+
+       if (type != SYSRESET_COLD)
+               return -EPROTONOSUPPORT;
+
+       if (pmic_write(dev->parent, PCA9450_SW_RST, &cmd, 1)) {
+               dev_err(dev, "reset command failed\n");
+       } else {
+               /* tRESTART is 250ms, delay 300ms just to be sure */
+               mdelay(300);
+               /* Should not get here, warn if we do */
+               dev_warn(dev, "didn't respond to reset command\n");
+       }
+
+       return -EINPROGRESS;
+}
+
+static struct sysreset_ops pca9450_sysreset_ops = {
+       .request        = pca9450_sysreset_request,
+};
+
+U_BOOT_DRIVER(pca9450_sysreset) = {
+       .name           = "pca9450_sysreset",
+       .id             = UCLASS_SYSRESET,
+       .ops            = &pca9450_sysreset_ops,
+};
+#endif /* CONFIG_SYSRESET */
diff --git a/include/power/pca9450.h b/include/power/pca9450.h
index e5ab09fb8c83..9119ef793b1f 100644
--- a/include/power/pca9450.h
+++ b/include/power/pca9450.h
@@ -75,4 +75,6 @@ enum {
 #define PCA9450_PMIC_RESET_WDOG_B_CFG_WARM             0x40
 #define PCA9450_PMIC_RESET_WDOG_B_CFG_COLD_LDO12       0x80
 
+#define PCA9450_SW_RST_COLD_RST                0x14
+
 #endif
-- 
2.34.1

Reply via email to