Configure the PMIC PON PBS reset type before deasserting MPM PS_HOLD.

The PON PBS reset sequence requires PS_HOLD_RESET_CTL2 to be cleared,
the requested reset type to be written to PS_HOLD_SW_CTL, and
PS_HOLD_RESET_CTL2 to be enabled again before deasserting MPM_PS_HOLD.

Signed-off-by: Sumit Singh Dhami <[email protected]>
---
 drivers/sysreset/sysreset_qcom-pshold.c | 137 ++++++++++++++++++++++--
 1 file changed, 130 insertions(+), 7 deletions(-)

diff --git a/drivers/sysreset/sysreset_qcom-pshold.c 
b/drivers/sysreset/sysreset_qcom-pshold.c
index 45290478536..de7c0b388aa 100644
--- a/drivers/sysreset/sysreset_qcom-pshold.c
+++ b/drivers/sysreset/sysreset_qcom-pshold.c
@@ -13,15 +13,94 @@
 #include <sysreset.h>
 #include <asm/io.h>
 #include <linux/delay.h>
+#include <power/pmic.h>
+
+#define PON_PBS_RESET_TYPE_WARM                0x01
+#define PON_PBS_RESET_TYPE_HARD                0x07
+
+/*
+ * Reset State of PON_PBS_PS_HOLD_RESET_CTL2 register
+ */
+#define PON_PBS_PS_HOLD_RESET_CTL2_S2_RESET_EN_BMSK    0x80
 
 struct qcom_pshold_priv {
        phys_addr_t base;
+
+       struct udevice *pmic;
+       u32 pbs_base;
+       bool have_pon;
+
+       u32 pon_pbs_pshold_sw_ctl_offset;
+       u32 pon_pbs_pshold_reset_ctl2_offset;
 };
 
+static int qcom_pshold_pon_cfg(struct qcom_pshold_priv *priv, u8 reset_type)
+{
+       int ret;
+
+       ret = pmic_reg_write(priv->pmic,
+                            priv->pbs_base +
+                            priv->pon_pbs_pshold_reset_ctl2_offset,
+                            0x00);
+       if (ret)
+               return ret;
+
+       udelay(100);
+
+       ret = pmic_reg_write(priv->pmic,
+                            priv->pbs_base + 
priv->pon_pbs_pshold_sw_ctl_offset,
+                            reset_type);
+       if (ret)
+               return ret;
+
+       udelay(300);
+
+       ret = pmic_reg_write(priv->pmic,
+                            priv->pbs_base + 
priv->pon_pbs_pshold_reset_ctl2_offset,
+                            PON_PBS_PS_HOLD_RESET_CTL2_S2_RESET_EN_BMSK);
+       if (ret)
+               return ret;
+
+       udelay(100);
+
+       return 0;
+}
+
+static int qcom_pshold_read_pon_offset(struct udevice *dev, struct 
qcom_pshold_priv *priv)
+{
+       int ret;
+
+       ret = dev_read_u32(dev,
+                          "qcom,pbs-pshold-sw-ctl",
+                          &priv->pon_pbs_pshold_sw_ctl_offset);
+       if (ret)
+               return ret;
+
+       ret = dev_read_u32(dev,
+                          "qcom,pbs-pshold-reset-ctl2",
+                          &priv->pon_pbs_pshold_reset_ctl2_offset);
+       if (ret)
+               return ret;
+
+       return 0;
+}
+
 static int qcom_pshold_request(struct udevice *dev, enum sysreset_t type)
 {
        struct qcom_pshold_priv *priv = dev_get_priv(dev);
 
+       if (priv->have_pon) {
+               u8 reset_type = (type == SYSRESET_WARM) ?
+                                PON_PBS_RESET_TYPE_WARM :
+                                PON_PBS_RESET_TYPE_HARD;
+               int ret;
+
+               ret = qcom_pshold_pon_cfg(priv, reset_type);
+               if (ret)
+                       debug("%s: failed to configure PON reset type: %d\n",
+                             dev->name, ret);
+       }
+
        writel(0, priv->base);
        mdelay(10000);
 
@@ -35,9 +114,53 @@ static struct sysreset_ops qcom_pshold_ops = {
 static int qcom_pshold_probe(struct udevice *dev)
 {
        struct qcom_pshold_priv *priv = dev_get_priv(dev);
+       struct ofnode_phandle_args args;
+       ofnode pmic_node;
+       int index, ret;
+
+       priv->have_pon = false;
 
        priv->base = dev_read_addr(dev);
-       return priv->base == FDT_ADDR_T_NONE ? -EINVAL : 0;
+       if (priv->base == FDT_ADDR_T_NONE)
+               return -EINVAL;
+
+       ret = dev_read_phandle_with_args(dev, "qcom,pon", NULL, 0, 0, &args);
+       if (ret)
+               return 0;
+
+       index = ofnode_stringlist_search(args.node, "reg-names", "pbs");
+       if (index < 0) {
+               debug("%s: referenced PON node has no 'pbs' reg\n", dev->name);
+               return 0;
+       }
+
+       ret = ofnode_read_u32_index(args.node, "reg", index, &priv->pbs_base);
+       if (ret) {
+               debug("%s: failed to read PON 'pbs' reg: %d\n", dev->name, ret);
+               return 0;
+       }
+
+       ret = qcom_pshold_read_pon_offset(dev, priv);
+       if (ret) {
+               debug("%s: failed to read PON offset configs: %d\n", dev->name, 
ret);
+               return 0;
+       }
+
+       pmic_node = ofnode_get_parent(args.node);
+       if (!ofnode_valid(pmic_node)) {
+               debug("%s: failed to get PON's parent PMIC node\n", dev->name);
+               return 0;
+       }
+
+       ret = uclass_get_device_by_ofnode(UCLASS_PMIC, pmic_node, &priv->pmic);
+       if (ret) {
+               debug("%s: failed to get PMIC device: %d\n", dev->name, ret);
+               return 0;
+       }
+
+       priv->have_pon = true;
+
+       return 0;
 }
 
 static const struct udevice_id qcom_pshold_ids[] = {
@@ -46,10 +169,10 @@ static const struct udevice_id qcom_pshold_ids[] = {
 };
 
 U_BOOT_DRIVER(qcom_pshold) = {
-       .name           = "qcom_pshold",
-       .id             = UCLASS_SYSRESET,
-       .of_match       = qcom_pshold_ids,
-       .probe          = qcom_pshold_probe,
-       .priv_auto      = sizeof(struct qcom_pshold_priv),
-       .ops            = &qcom_pshold_ops,
+       .name       = "qcom_pshold",
+       .id         = UCLASS_SYSRESET,
+       .of_match   = qcom_pshold_ids,
+       .probe      = qcom_pshold_probe,
+       .priv_auto  = sizeof(struct qcom_pshold_priv),
+       .ops        = &qcom_pshold_ops,
 };
-- 
2.55.0

Reply via email to