Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions arch/arm/dts/lemans-evk-u-boot.dtsi
Original file line number Diff line number Diff line change
Expand Up @@ -64,3 +64,15 @@
mode-edl = <0x80000000 0x00000001>;
};
};

&soc {
restart@c264000 {
compatible = "qcom,pshold";
reg = <0x0 0x0c264000 0x0 0x4>;
qcom,pon = <&pmm8654au_0_pon>;
qcom,pbs-pshold-sw-ctl = <0x52>;
qcom,pbs-pshold-reset-ctl2 = <0x53>;

bootph-all;
};
};
1 change: 1 addition & 0 deletions configs/qcom-snagboot.config
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ CONFIG_QCOM_SNAGBOOT_MODE=y
# CONFIG_SAVE_PREV_BL_INITRAMFS_START_ADDR is not set
# CONFIG_IOMMU is not set
# CONFIG_REBOOT_MODE_ENV_UPDATE is not set
# CONFIG_CMD_POWEROFF is not set

# Use default environment instead of loading from storage
# This ensures snagboot mode works even with corrupted environment
Expand Down
137 changes: 130 additions & 7 deletions drivers/sysreset/sysreset_qcom-pshold.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand All @@ -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[] = {
Expand All @@ -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,
};