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


Reply via email to