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