Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 58997453 authored by Rama Krishna Phani A's avatar Rama Krishna Phani A Committed by Veera Vegivada
Browse files

input: qpnp-power-on: Add support for suspend to disk



Add support to pon driver for suspend to disk feature.
Free interrupts during hibernation and re-register them during
resume. Also, re-configure the boot time registers on resume.

Change-Id: I027d0d44e93b0ca20d8ab6498976886fafb117a0
Signed-off-by: default avatarRama Krishna Phani A <rphani@codeaurora.org>
Signed-off-by: default avatarVeera Vegivada <vvegivad@codeaurora.org>
parent 1c79f79f
Loading
Loading
Loading
Loading
+143 −18
Original line number Diff line number Diff line
/* Copyright (c) 2012-2018, The Linux Foundation. All rights reserved.
/* Copyright (c) 2012-2019, The Linux Foundation. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 and
@@ -167,6 +167,12 @@ enum pon_type {
	PON_KPDPWR_RESIN = PON_POWER_ON_TYPE_KPDPWR_RESIN,
};

struct pon_reg {
	unsigned int val;
	u16 addr;
	struct list_head list;
};

struct qpnp_pon_config {
	u32			pon_type;
	u32			support_reset;
@@ -199,7 +205,9 @@ struct qpnp_pon {
	struct input_dev	*pon_input;
	struct qpnp_pon_config	*pon_cfg;
	struct pon_regulator	*pon_reg_cfg;
	struct list_head	restore_regs;
	struct list_head	list;
	struct mutex		restore_lock;
	struct delayed_work	bark_work;
	struct dentry		*debugfs;
	u16			base;
@@ -305,6 +313,42 @@ static const char * const qpnp_poff_reason[] = {
	[39] = "Triggered from S3_RESET_KPDPWR_ANDOR_RESIN",
};

static int qpnp_pon_store_reg(struct qpnp_pon *pon, u16 addr)
{
	int rc;
	unsigned int val;
	struct pon_reg *reg, *pos = NULL;

	mutex_lock(&pon->restore_lock);
	rc = regmap_read(pon->regmap, addr, &val);
	if (rc < 0) {
		dev_info(pon->dev, "Register read failed, addr=0x%04X, rc=%d\n",
			addr, rc);
	} else {
		list_for_each_entry(pos, &pon->restore_regs, list) {
			if (pos->addr == addr) {
				pos->val = val;
				goto done;
			}
		}

		reg = devm_kzalloc(pon->dev, sizeof(*reg), GFP_KERNEL);
		if (!reg) {
			rc = -ENOMEM;
			goto done;
		}

		reg->addr = addr;
		reg->val = val;
		INIT_LIST_HEAD(&reg->list);
		list_add_tail(&reg->list, &pon->restore_regs);
	}

done:
	mutex_unlock(&pon->restore_lock);
	return rc;
}

static int
qpnp_pon_masked_write(struct qpnp_pon *pon, u16 addr, u8 mask, u8 val)
{
@@ -317,6 +361,18 @@ qpnp_pon_masked_write(struct qpnp_pon *pon, u16 addr, u8 mask, u8 val)
	return rc;
}

static int
qpnp_pon_masked_write_backup(struct qpnp_pon *pon, u16 addr, u8 mask, u8 val)
{
	int rc;

	rc = qpnp_pon_masked_write(pon, addr, mask, val);
	if (rc < 0)
		return rc;

	return qpnp_pon_store_reg(pon, addr);
}

static int qpnp_pon_read(struct qpnp_pon *pon, u16 addr, unsigned int *val)
{
	int rc;
@@ -416,7 +472,7 @@ static int qpnp_pon_set_dbc(struct qpnp_pon *pon, u32 delay)
	}

	val = ilog2(val);
	rc = qpnp_pon_masked_write(pon, QPNP_PON_DBC_CTL(pon),
	rc = qpnp_pon_masked_write_backup(pon, QPNP_PON_DBC_CTL(pon),
				   QPNP_PON_DBC_DELAY_MASK(pon), val);
	if (!rc)
		pon->dbc_time_us = delay;
@@ -794,10 +850,10 @@ int qpnp_pon_trigger_config(enum pon_trigger_source pon_src, bool enable)
	}

	if (is_pon_gen2(pon) && pon_src == PON_SMPL)
		rc = qpnp_pon_masked_write(pon, QPNP_PON_SMPL_CTL(pon),
		rc = qpnp_pon_masked_write_backup(pon, QPNP_PON_SMPL_CTL(pon),
			QPNP_PON_SMPL_EN, enable ? QPNP_PON_SMPL_EN : 0);
	else
		rc = qpnp_pon_masked_write(pon, QPNP_PON_TRIGGER_EN(pon),
		rc = qpnp_pon_masked_write_backup(pon, QPNP_PON_TRIGGER_EN(pon),
				BIT(pon_src), enable ? BIT(pon_src) : 0);

	return rc;
@@ -834,6 +890,7 @@ static int qpnp_pon_store_and_clear_warm_reset(struct qpnp_pon *pon)
				QPNP_PON_WARM_RESET_REASON1(pon), rc);
			return rc;
		}
		qpnp_pon_store_reg(pon, QPNP_PON_WARM_RESET_REASON1(pon));
	}

	return 0;
@@ -1122,8 +1179,8 @@ static int qpnp_config_pull(struct qpnp_pon *pon, struct qpnp_pon_config *cfg)
		return -EINVAL;
	}

	return qpnp_pon_masked_write(pon, QPNP_PON_PULL_CTL(pon), pull_bit,
				     cfg->pull_up ? pull_bit : 0);
	return qpnp_pon_masked_write_backup(pon, QPNP_PON_PULL_CTL(pon),
				pull_bit, cfg->pull_up ? pull_bit : 0);
}

static int qpnp_config_reset(struct qpnp_pon *pon, struct qpnp_pon_config *cfg)
@@ -1150,8 +1207,8 @@ static int qpnp_config_reset(struct qpnp_pon *pon, struct qpnp_pon_config *cfg)
	}

	/* Disable S2 reset */
	rc = qpnp_pon_masked_write(pon, cfg->s2_cntl2_addr, QPNP_PON_S2_CNTL_EN,
				   0);
	rc = qpnp_pon_masked_write_backup(pon, cfg->s2_cntl2_addr,
				QPNP_PON_S2_CNTL_EN, 0);
	if (rc)
		return rc;

@@ -1162,7 +1219,7 @@ static int qpnp_config_reset(struct qpnp_pon *pon, struct qpnp_pon_config *cfg)
		if (cfg->s1_timer <= s1_delay[i])
			break;
	}
	rc = qpnp_pon_masked_write(pon, s1_timer_addr,
	rc = qpnp_pon_masked_write_backup(pon, s1_timer_addr,
				QPNP_PON_S1_TIMER_MASK, i);
	if (rc)
		return rc;
@@ -1173,18 +1230,18 @@ static int qpnp_config_reset(struct qpnp_pon *pon, struct qpnp_pon_config *cfg)
		i = ilog2(i + 1);
	}

	rc = qpnp_pon_masked_write(pon, s2_timer_addr, QPNP_PON_S2_TIMER_MASK,
				   i);
	rc = qpnp_pon_masked_write_backup(pon, s2_timer_addr,
				QPNP_PON_S2_TIMER_MASK, i);
	if (rc)
		return rc;

	rc = qpnp_pon_masked_write(pon, cfg->s2_cntl_addr,
	rc = qpnp_pon_masked_write_backup(pon, cfg->s2_cntl_addr,
				QPNP_PON_S2_CNTL_TYPE_MASK, (u8)cfg->s2_type);
	if (rc)
		return rc;

	/* Enable S2 reset */
	return qpnp_pon_masked_write(pon, cfg->s2_cntl2_addr,
	return qpnp_pon_masked_write_backup(pon, cfg->s2_cntl2_addr,
				     QPNP_PON_S2_CNTL_EN, QPNP_PON_S2_CNTL_EN);
}

@@ -1277,6 +1334,18 @@ qpnp_pon_request_irqs(struct qpnp_pon *pon, struct qpnp_pon_config *cfg)
	return 0;
}

static int
qpnp_pon_free_irqs(struct qpnp_pon *pon, struct qpnp_pon_config *cfg)
{
	if (cfg->state_irq > 0)
		devm_free_irq(pon->dev, cfg->state_irq, pon);

	if (cfg->use_bark && cfg->bark_irq > 0)
		devm_free_irq(pon->dev, cfg->bark_irq, pon);

	return 0;
}

static int
qpnp_pon_config_input(struct qpnp_pon *pon, struct qpnp_pon_config *cfg)
{
@@ -1632,7 +1701,7 @@ static int qpnp_pon_config_init(struct qpnp_pon *pon,
					return rc;
			} else if (cfg->pon_type != PON_CBLPWR) {
				/* Disable S2 reset */
				rc = qpnp_pon_masked_write(pon,
				rc = qpnp_pon_masked_write_backup(pon,
							cfg->s2_cntl2_addr,
							QPNP_PON_S2_CNTL_EN, 0);
				if (rc)
@@ -1847,7 +1916,7 @@ qpnp_pon_uvlo_dload_set(const char *val, const struct kernel_param *kp)

	reg = *(bool *)kp->arg ? QPNP_PON_UVLO_DLOAD_EN : 0;

	return qpnp_pon_masked_write(pon, QPNP_PON_XVDD_RB_SPARE(pon),
	return qpnp_pon_masked_write_backup(pon, QPNP_PON_XVDD_RB_SPARE(pon),
				   QPNP_PON_UVLO_DLOAD_EN, reg);
}

@@ -1970,12 +2039,12 @@ static int qpnp_pon_configure_s3_reset(struct qpnp_pon *pon)
			debounce = ilog2(debounce);

		/* S3 debounce is a SEC_ACCESS register */
		rc = qpnp_pon_masked_write(pon, QPNP_PON_SEC_ACCESS(pon),
		rc = qpnp_pon_masked_write_backup(pon, QPNP_PON_SEC_ACCESS(pon),
					0xFF, QPNP_PON_SEC_UNLOCK);
		if (rc)
			return rc;

		rc = qpnp_pon_masked_write(pon, QPNP_PON_S3_DBC_CTL(pon),
		rc = qpnp_pon_masked_write_backup(pon, QPNP_PON_S3_DBC_CTL(pon),
					QPNP_PON_S3_DBC_DELAY_MASK, debounce);
		if (rc)
			return rc;
@@ -2003,7 +2072,7 @@ static int qpnp_pon_configure_s3_reset(struct qpnp_pon *pon)
		 * been configured by the bootloader then this operation will
		 * not have an effect.
		 */
		rc = qpnp_pon_masked_write(pon, QPNP_PON_S3_SRC(pon),
		rc = qpnp_pon_masked_write_backup(pon, QPNP_PON_S3_SRC(pon),
					QPNP_PON_S3_SRC_MASK, src_val);
		if (rc)
			return rc;
@@ -2224,6 +2293,9 @@ static int qpnp_pon_probe(struct platform_device *pdev)
		return -EINVAL;
	}

	INIT_LIST_HEAD(&pon->restore_regs);
	mutex_init(&pon->restore_lock);

	/* Get the total number of pon configurations and regulators */
	for_each_available_child_of_node(dev->of_node, node) {
		if (of_find_property(node, "regulator-name", NULL)) {
@@ -2325,10 +2397,60 @@ static int qpnp_pon_remove(struct platform_device *pdev)
		list_del(&pon->list);
		spin_unlock_irqrestore(&spon_list_slock, flags);
	}
	mutex_destroy(&pon->restore_lock);

	return 0;
}

#ifdef CONFIG_PM
static int qpnp_pon_restore(struct device *dev)
{
	int i, rc = 0;
	struct qpnp_pon_config *cfg;
	struct qpnp_pon *pon = dev_get_drvdata(dev);
	struct pon_reg *pos = NULL;

	list_for_each_entry(pos, &pon->restore_regs, list) {
		rc = regmap_write(pon->regmap, pos->addr, pos->val);
		if (rc < 0) {
			dev_err(dev, "Failed to restore reg addr=0x%04X rc=%d\n",
				pos->addr, rc);
			return rc;
		}
	}

	for (i = 0; i < pon->num_pon_config; i++) {
		cfg = &pon->pon_cfg[i];
		rc = qpnp_pon_request_irqs(pon, cfg);
		if (rc < 0)
			return rc;
	}

	return rc;
}

static int qpnp_pon_freeze(struct device *dev)
{
	int i, rc = 0;
	struct qpnp_pon_config *cfg;
	struct qpnp_pon *pon = dev_get_drvdata(dev);

	for (i = 0; i < pon->num_pon_config; i++) {
		cfg = &pon->pon_cfg[i];
		rc = qpnp_pon_free_irqs(pon, cfg);
		if (rc < 0)
			return rc;
	}

	return rc;
}

static const struct dev_pm_ops qpnp_pon_pm_ops = {
	.freeze = qpnp_pon_freeze,
	.restore = qpnp_pon_restore,
};
#endif

static const struct of_device_id qpnp_pon_match_table[] = {
	{ .compatible = "qcom,qpnp-power-on" },
	{}
@@ -2338,6 +2460,9 @@ static struct platform_driver qpnp_pon_driver = {
	.driver = {
		.name = "qcom,qpnp-power-on",
		.of_match_table = qpnp_pon_match_table,
#ifdef CONFIG_PM
		.pm = &qpnp_pon_pm_ops,
#endif
	},
	.probe = qpnp_pon_probe,
	.remove = qpnp_pon_remove,