From: Sam Day <[email protected]> The PM8916 provides a PON_SOFT_RB_SPARE register that survives across reboots.
Signed-off-by: Sam Day <[email protected]> --- drivers/misc/pm8916_pon.c | 7 +++ drivers/reboot-mode/Makefile | 1 + drivers/reboot-mode/reboot-mode-pm8916-pon.c | 88 ++++++++++++++++++++++++++++ 3 files changed, 96 insertions(+) diff --git a/drivers/misc/pm8916_pon.c b/drivers/misc/pm8916_pon.c index 4cd8921a08a..c889d2d4e6e 100644 --- a/drivers/misc/pm8916_pon.c +++ b/drivers/misc/pm8916_pon.c @@ -142,6 +142,13 @@ static int pm8916_pon_bind(struct udevice *dev) dev_warn(dev, "failed to bind qcom_pwrkey: %d\n", ret); } + if (CONFIG_IS_ENABLED(DM_REBOOT_MODE)) { + ret = device_bind_driver_to_node(dev, "pm8916_pon_reboot_mode", + "reboot_mode", dev_ofnode(dev), NULL); + if (ret) + dev_warn(dev, "failed to bind reboot_mode: %d\n", ret); + } + return 0; } diff --git a/drivers/reboot-mode/Makefile b/drivers/reboot-mode/Makefile index 48c8ab7fe71..467334d010e 100644 --- a/drivers/reboot-mode/Makefile +++ b/drivers/reboot-mode/Makefile @@ -8,3 +8,4 @@ obj-$(CONFIG_DM_REBOOT_MODE) += reboot-mode-uclass.o obj-$(CONFIG_DM_REBOOT_MODE_GPIO) += reboot-mode-gpio.o obj-$(CONFIG_DM_REBOOT_MODE_RTC) += reboot-mode-rtc.o obj-$(CONFIG_REBOOT_MODE_NVMEM) += reboot-mode-nvmem.o +obj-$(CONFIG_PM8916_PON) += reboot-mode-pm8916-pon.o diff --git a/drivers/reboot-mode/reboot-mode-pm8916-pon.c b/drivers/reboot-mode/reboot-mode-pm8916-pon.c new file mode 100644 index 00000000000..fa8d0ecaa77 --- /dev/null +++ b/drivers/reboot-mode/reboot-mode-pm8916-pon.c @@ -0,0 +1,88 @@ +// SPDX-License-Identifier: GPL-2.0+ + +#include <dm.h> +#include <dm/device_compat.h> +#include <dm/read.h> +#include <fdtdec.h> +#include <power/pmic.h> +#include <reboot-mode/reboot-mode.h> + +#define PON_SOFT_RB_SPARE 0x8f + +#define GEN1_REASON_SHIFT 2 + +struct pm8916_pon_reboot_mode_priv { + struct udevice *pmic; + phys_addr_t base; +}; + +static int pm8916_pon_reboot_mode_get(struct udevice *dev, u32 *rebootmode) +{ + struct pm8916_pon_reboot_mode_priv *priv = dev_get_priv(dev); + int reg; + uint mask = GENMASK(7, GEN1_REASON_SHIFT); + + reg = pmic_reg_read(priv->pmic, priv->base + PON_SOFT_RB_SPARE); + if (reg < 0) { + dev_warn(dev, "Failed to read PON_SOFT_RB_SPARE: %d\n", reg); + return reg; + } + + *rebootmode = (reg & mask) >> GEN1_REASON_SHIFT; + + return 0; +} + +static int pm8916_pon_reboot_mode_set(struct udevice *dev, u32 rebootmode) +{ + struct pm8916_pon_reboot_mode_priv *priv = dev_get_priv(dev); + uint mask = GENMASK(7, GEN1_REASON_SHIFT); + int ret; + + ret = pmic_clrsetbits(priv->pmic, priv->base + PON_SOFT_RB_SPARE, mask, + rebootmode << GEN1_REASON_SHIFT); + if (ret) { + dev_warn(dev, "Failed to write PON_SOFT_RB_SPARE: %d\n", ret); + return ret; + } + return 0; +} + +static const struct reboot_mode_ops pm8916_pon_reboot_mode_ops = { + .get = pm8916_pon_reboot_mode_get, + .set = pm8916_pon_reboot_mode_set, +}; + +static int pm8916_pon_reboot_mode_probe(struct udevice *dev) +{ + struct pm8916_pon_reboot_mode_priv *priv = dev_get_priv(dev); + struct udevice *pon = dev_get_parent(dev); + fdt_addr_t base; + + /* this driver only works as a child of pm8916_pon */ + if (!pon || !pon->driver || strcmp(pon->driver->name, "pm8916_pon")) + return -EINVAL; + + priv->pmic = dev_get_parent(pon); + if (!priv->pmic) { + dev_err(dev, "PMIC driver not found\n"); + return -EINVAL; + } + + base = dev_read_addr(pon); + if (base == FDT_ADDR_T_NONE) { + dev_err(dev, "missing PON reg base\n"); + return -EINVAL; + } + priv->base = base; + + return 0; +} + +U_BOOT_DRIVER(pm8916_pon_reboot_mode) = { + .name = "pm8916_pon_reboot_mode", + .id = UCLASS_REBOOT_MODE, + .probe = pm8916_pon_reboot_mode_probe, + .ops = &pm8916_pon_reboot_mode_ops, + .priv_auto = sizeof(struct pm8916_pon_reboot_mode_priv), +}; -- 2.54.0

