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

Commit 57235f8a authored by Osvaldo Banuelos's avatar Osvaldo Banuelos Committed by Gerrit - the friendly Code Review server
Browse files

regulator: kryo-regulator: switch regulators to BHS prior to M4M collapse



Due to unexpected interactions with the M3 low power mode, the
clusters must be powered through the BHS before entering the M3 state.
To satisfy this requirement, subscribe to CPU PM notifications and ensure
that the regulators are placed in bypass (BHS) mode when entering such
state. On the resume path, restore the configuration existing prior to
entering M3 in order to guarantee that the consumers' internal state and
the true kryo regulator states are kept in sync.

Change-Id: I65fbbd06e9a8af272fa2f976bfe161b5ec383ce1
Signed-off-by: default avatarOsvaldo Banuelos <osvaldob@codeaurora.org>
parent 526cc3ec
Loading
Loading
Loading
Loading
+8 −0
Original line number Diff line number Diff line
@@ -41,6 +41,12 @@ First Level Nodes
	Definition: The value for retention voltage in microvolts. Must be
		    between 520000 and 865000 uV.

- qcom,ldo-headroom-voltage
	Usage:      required
	Value type: <u32>
	Definition: Voltage in microvolts required between the VDD_APCC voltage supply
		    and the LDO output in order for the LDO to be operational.

- qcom,vref-functional-step-voltage
	Usage:      required
	Value type: <u32>
@@ -94,6 +100,7 @@ Example
		regulator-max-microvolt = <865000>;
		qcom,ldo-default-voltage = <750000>;
		qcom,retention-voltage = <520000>;
		qcom,ldo-headroom-voltage = <150000>;
		qcom,vref-functional-step-voltage = <4100>;
		qcom,vref-functional-min-voltage = <299000>;
		qcom,vref-retention-step-voltage = <4554>;
@@ -116,6 +123,7 @@ Example
		regulator-max-microvolt = <865000>;
		qcom,ldo-default-voltage = <750000>;
		qcom,retention-voltage = <520000>;
		qcom,ldo-headroom-voltage = <150000>;
		qcom,vref-functional-step-voltage = <4063>;
		qcom,vref-functional-min-voltage = <296000>;
		qcom,vref-retention-step-voltage = <4554>;
+2 −0
Original line number Diff line number Diff line
@@ -903,6 +903,7 @@
		regulator-max-microvolt = <865000>;
		qcom,ldo-default-voltage = <750000>;
		qcom,retention-voltage = <520000>;
		qcom,ldo-headroom-voltage = <150000>;
		qcom,vref-functional-step-voltage = <4048>;
		qcom,vref-functional-min-voltage = <294800>;
		qcom,vref-retention-step-voltage = <4462>;
@@ -925,6 +926,7 @@
		regulator-max-microvolt = <865000>;
		qcom,ldo-default-voltage = <750000>;
		qcom,retention-voltage = <520000>;
		qcom,ldo-headroom-voltage = <150000>;
		qcom,vref-functional-step-voltage = <4048>;
		qcom,vref-functional-min-voltage = <294800>;
		qcom,vref-retention-step-voltage = <4462>;
+153 −17
Original line number Diff line number Diff line
@@ -13,6 +13,7 @@

#define pr_fmt(fmt) "%s: " fmt, __func__

#include <linux/cpu_pm.h>
#include <linux/debugfs.h>
#include <linux/delay.h>
#include <linux/init.h>
@@ -29,6 +30,8 @@
#include <linux/regulator/of_regulator.h>
#include <linux/regulator/kryo-regulator.h>

#include <soc/qcom/spm.h>

#define KRYO_REGULATOR_DRIVER_NAME	"kryo-regulator"

#define kvreg_err(kvreg, message, ...) \
@@ -73,9 +76,14 @@
#define MSM8996_CPUSS_VER_1P1	0x10010000

#define LDO_N_VOLTAGES		0x80
#define AFFINITY_LEVEL_M3	2
#define SHARED_CPU_REG_NUM	0
#define VDD_SUPPLY_STEP_UV	5000
#define VDD_SUPPLY_MIN_UV	80000

struct kryo_regulator {
	struct list_head		link;
	spinlock_t			slock;
	struct regulator_desc		desc;
	struct regulator_dev		*rdev;
	struct regulator_dev		*retention_rdev;
@@ -83,12 +91,17 @@ struct kryo_regulator {
	const char			*name;
	enum kryo_supply_mode		mode;
	enum kryo_supply_mode		retention_mode;
	enum kryo_supply_mode		pre_lpm_state_mode;
	void __iomem			*reg_base;
	void __iomem			*pm_apcc_base;
	struct dentry			*debugfs;
	struct mutex			lock;
	struct notifier_block		cpu_pm_notifier;
	unsigned long			lpm_enter_count;
	unsigned long			lpm_exit_count;
	int				volt;
	int				retention_volt;
	int				headroom_volt;
	int				pre_lpm_state_volt;
	int				vref_func_step_volt;
	int				vref_func_min_volt;
	int				vref_func_max_volt;
@@ -182,6 +195,7 @@ static inline int kryo_encode_functional_volt(struct kryo_regulator *kvreg,
		return encoded_volt;
}

/* Locks must be held by the caller */
static int kryo_set_retention_volt(struct kryo_regulator *kvreg, int volt)
{
	int reg_val;
@@ -203,6 +217,7 @@ static int kryo_set_retention_volt(struct kryo_regulator *kvreg, int volt)
	return 0;
}

/* Locks must be held by the caller */
static int kryo_set_ldo_volt(struct kryo_regulator *kvreg, int volt)
{
	int reg_val;
@@ -229,6 +244,7 @@ static int kryo_set_ldo_volt(struct kryo_regulator *kvreg, int volt)
	return 0;
}

/* Locks must be held by the caller */
static int kryo_configure_mode(struct kryo_regulator *kvreg,
				enum kryo_supply_mode mode)
{
@@ -269,11 +285,12 @@ static int kryo_regulator_enable(struct regulator_dev *rdev)
{
	struct kryo_regulator *kvreg = rdev_get_drvdata(rdev);
	int rc;
	unsigned long flags;

	if (kvreg->vreg_en == true)
		return 0;

	mutex_lock(&kvreg->lock);
	spin_lock_irqsave(&kvreg->slock, flags);
	rc = kryo_set_ldo_volt(kvreg, kvreg->volt);
	if (rc) {
		kvreg_err(kvreg, "set voltage failed, rc=%d\n", rc);
@@ -284,7 +301,7 @@ static int kryo_regulator_enable(struct regulator_dev *rdev)
	kvreg_debug(kvreg, "enabled\n");

done:
	mutex_unlock(&kvreg->lock);
	spin_unlock_irqrestore(&kvreg->slock, flags);

	return rc;
}
@@ -293,14 +310,15 @@ static int kryo_regulator_disable(struct regulator_dev *rdev)
{
	struct kryo_regulator *kvreg = rdev_get_drvdata(rdev);
	int rc;
	unsigned long flags;

	if (kvreg->vreg_en == false)
		return 0;

	mutex_lock(&kvreg->lock);
	spin_lock_irqsave(&kvreg->slock, flags);
	kvreg->vreg_en = false;
	kvreg_debug(kvreg, "disabled\n");
	mutex_unlock(&kvreg->lock);
	spin_unlock_irqrestore(&kvreg->slock, flags);

	return rc;
}
@@ -317,11 +335,13 @@ static int kryo_regulator_set_voltage(struct regulator_dev *rdev,
{
	struct kryo_regulator *kvreg = rdev_get_drvdata(rdev);
	int rc;
	unsigned long flags;

	spin_lock_irqsave(&kvreg->slock, flags);

	mutex_lock(&kvreg->lock);
	if (!kvreg->vreg_en) {
		kvreg->volt = min_volt;
		mutex_unlock(&kvreg->lock);
		spin_unlock_irqrestore(&kvreg->slock, flags);
		return 0;
	}

@@ -329,7 +349,7 @@ static int kryo_regulator_set_voltage(struct regulator_dev *rdev,
	if (rc)
		kvreg_err(kvreg, "set voltage failed, rc=%d\n", rc);

	mutex_unlock(&kvreg->lock);
	spin_unlock_irqrestore(&kvreg->slock, flags);

	return rc;
}
@@ -346,8 +366,9 @@ static int kryo_regulator_set_bypass(struct regulator_dev *rdev,
{
	struct kryo_regulator *kvreg = rdev_get_drvdata(rdev);
	int rc;
	unsigned long flags;

	mutex_lock(&kvreg->lock);
	spin_lock_irqsave(&kvreg->slock, flags);

	/*
	 * LDO Vref voltage must be programmed before switching
@@ -361,7 +382,7 @@ static int kryo_regulator_set_bypass(struct regulator_dev *rdev,
	if (rc)
		kvreg_err(kvreg, "could not configure to %s mode\n",
			  enable == LDO_MODE ? "LDO" : "BHS");
	mutex_unlock(&kvreg->lock);
	spin_unlock_irqrestore(&kvreg->slock, flags);

	return rc;
}
@@ -392,13 +413,14 @@ static int kryo_regulator_retention_set_voltage(struct regulator_dev *rdev,
{
	struct kryo_regulator *kvreg = rdev_get_drvdata(rdev);
	int rc;
	unsigned long flags;

	mutex_lock(&kvreg->lock);
	spin_lock_irqsave(&kvreg->slock, flags);
	rc = kryo_set_retention_volt(kvreg, min_volt);
	if (rc)
		kvreg_err(kvreg, "set voltage failed, rc=%d\n", rc);

	mutex_unlock(&kvreg->lock);
	spin_unlock_irqrestore(&kvreg->slock, flags);

	return rc;
}
@@ -417,8 +439,10 @@ static int kryo_regulator_retention_set_bypass(struct regulator_dev *rdev,
	int timeout = PWR_GATE_SWITCH_TIMEOUT_US;
	int rc = 0;
	u32 reg_val;
	unsigned long flags;

	spin_lock_irqsave(&kvreg->slock, flags);

	mutex_lock(&kvreg->lock);
	kryo_pm_apcc_masked_write(kvreg,
				  APCC_PWR_CTL_OVERRIDE,
				  APCC_PGS_MASK(kvreg->cluster_num),
@@ -457,7 +481,8 @@ static int kryo_regulator_retention_set_bypass(struct regulator_dev *rdev,
		: BHS_MODE;

done:
	mutex_unlock(&kvreg->lock);
	spin_unlock_irqrestore(&kvreg->slock, flags);

	return rc;
}

@@ -544,12 +569,13 @@ static ssize_t kryo_dbg_mode_read(struct file *file, char __user *buff,
	char buf[10];
	int len = 0;
	u32 reg_val;
	unsigned long flags;

	if (!kvreg)
		return -ENODEV;

	/* Confirm HW state matches Kryo regulator device state */
	mutex_lock(&kvreg->lock);
	spin_lock_irqsave(&kvreg->slock, flags);
	reg_val = readl_relaxed(kvreg->reg_base + APC_PWR_GATE_MODE);
	if (((reg_val & PWR_GATE_SWITCH_MODE_MASK) == PWR_GATE_SWITCH_MODE_LDO
	     && kvreg->mode != LDO_MODE) ||
@@ -563,7 +589,7 @@ static ssize_t kryo_dbg_mode_read(struct file *file, char __user *buff,
			       kvreg->mode == LDO_MODE ?
			       "LDO" : "BHS");
	}
	mutex_unlock(&kvreg->lock);
	spin_unlock_irqrestore(&kvreg->slock, flags);

	return simple_read_from_buffer(buff, count, ppos, buf, len);
}
@@ -747,6 +773,13 @@ static int kryo_regulator_init_data(struct platform_device *pdev,
		return -EINVAL;
	}

	rc = of_property_read_u32(dev->of_node, "qcom,ldo-headroom-voltage",
				  &kvreg->headroom_volt);
	if (rc < 0) {
		dev_err(dev, "qcom,ldo-headroom-voltage missing rc=%d\n", rc);
		return rc;
	}

	rc = of_property_read_u32(dev->of_node, "qcom,cluster-num",
				  &kvreg->cluster_num);
	if (rc < 0) {
@@ -802,6 +835,104 @@ static int kryo_regulator_retention_init(struct kryo_regulator *kvreg,
	return rc;
}

static int kryo_regulator_lpm_prepare(struct kryo_regulator *kvreg)
{
	int vdd_volt_uv, vdd_vlvl = 0;
	unsigned long flags;

	spin_lock_irqsave(&kvreg->slock, flags);

	kvreg->pre_lpm_state_mode = kvreg->mode;
	kvreg->pre_lpm_state_volt = kvreg->volt;

	if (kvreg->mode == LDO_MODE) {
		if (!vdd_vlvl) {
			vdd_vlvl = msm_spm_get_vdd(SHARED_CPU_REG_NUM);
			if (vdd_vlvl < 0) {
				kvreg_err(kvreg, "could not get vdd supply voltage level, rc=%d\n",
					  vdd_vlvl);
				spin_unlock_irqrestore(&kvreg->slock, flags);
				return NOTIFY_BAD;
			}

			vdd_volt_uv = vdd_vlvl * VDD_SUPPLY_STEP_UV
				+ VDD_SUPPLY_MIN_UV;
		}
		kvreg_debug(kvreg, "switching to BHS mode, vdd_apcc=%d uV, current LDO Vref=%d, LPM enter count=%lx\n",
			    vdd_volt_uv, kvreg->volt, kvreg->lpm_enter_count);

		/* Program vdd supply minus LDO headroom as voltage */
		kryo_set_ldo_volt(kvreg, vdd_volt_uv
				  - kvreg->headroom_volt);

		/* Switch Power Gate Mode */
		kryo_configure_mode(kvreg, BHS_MODE);
	}

	kvreg->lpm_enter_count++;
	spin_unlock_irqrestore(&kvreg->slock, flags);

	return NOTIFY_OK;
}

static int kryo_regulator_lpm_resume(struct kryo_regulator *kvreg)
{
	unsigned long flags;

	spin_lock_irqsave(&kvreg->slock, flags);

	if (kvreg->mode == BHS_MODE &&
	    kvreg->pre_lpm_state_mode == LDO_MODE) {
		kvreg_debug(kvreg, "switching to LDO mode, cached LDO Vref=%d, LPM exit count=%lx\n",
			    kvreg->pre_lpm_state_volt, kvreg->lpm_exit_count);

		/*
		 * Cached voltage value corresponds to vdd supply minus
		 * LDO headroom, reprogram it.
		 */
		kryo_set_ldo_volt(kvreg, kvreg->volt);

		/* Switch Power Gate Mode */
		kryo_configure_mode(kvreg, LDO_MODE);

		/* Request final LDO output voltage */
		kryo_set_ldo_volt(kvreg, kvreg->pre_lpm_state_volt);
	}

	kvreg->lpm_exit_count++;
	spin_unlock_irqrestore(&kvreg->slock, flags);

	if (kvreg->lpm_exit_count != kvreg->lpm_enter_count) {
		kvreg_err(kvreg, "LPM entry/exit counter mismatch, this is not expected: enter=%lx exit=%lx\n",
			  kvreg->lpm_enter_count, kvreg->lpm_exit_count);
		BUG_ON(1);
	}

	return NOTIFY_OK;
}

static int kryo_regulator_cpu_pm_callback(struct notifier_block *self,
					 unsigned long cmd, void *v)
{
	struct kryo_regulator *kvreg = container_of(self, struct kryo_regulator,
						    cpu_pm_notifier);
	unsigned long aff_level = (unsigned long) v;
	int rc;

	switch (cmd) {
	case CPU_CLUSTER_PM_ENTER:
		if (aff_level == AFFINITY_LEVEL_M3)
			rc = kryo_regulator_lpm_prepare(kvreg);
		break;
	case CPU_CLUSTER_PM_EXIT:
		if (aff_level == AFFINITY_LEVEL_M3)
			rc = kryo_regulator_lpm_resume(kvreg);
		break;
	}

	return rc;
}

static int kryo_regulator_probe(struct platform_device *pdev)
{
	struct device *dev = &pdev->dev;
@@ -838,13 +969,13 @@ static int kryo_regulator_probe(struct platform_device *pdev)
		return -ENOMEM;
	}

	mutex_init(&kvreg->lock);
	rc = kryo_regulator_init_data(pdev, kvreg);
	if (rc) {
		dev_err(dev, "could not parse and ioremap all device tree properties\n");
		return rc;
	}

	spin_lock_init(&kvreg->slock);
	kvreg->name		= init_data->constraints.name;
	kvreg->desc.name	= kvreg->name;
	kvreg->desc.n_voltages	= LDO_N_VOLTAGES;
@@ -888,6 +1019,10 @@ static int kryo_regulator_probe(struct platform_device *pdev)
	list_add_tail(&kvreg->link, &kryo_regulator_list);
	mutex_unlock(&kryo_regulator_list_mutex);

	kvreg->cpu_pm_notifier.notifier_call = kryo_regulator_cpu_pm_callback;
	cpu_pm_register_notifier(&kvreg->cpu_pm_notifier);
	kvreg_debug(kvreg, "registered cpu pm notifier\n");

	kvreg_info(kvreg, "default LDO functional volt=%d uV, LDO retention volt=%d uV, Vref func=%d + %d*(val), cluster-num=%d\n",
		   kvreg->volt, kvreg->retention_volt,
		   kvreg->vref_func_min_volt,
@@ -905,6 +1040,7 @@ static int kryo_regulator_remove(struct platform_device *pdev)
	list_del(&kvreg->link);
	mutex_unlock(&kryo_regulator_list_mutex);

	cpu_pm_unregister_notifier(&kvreg->cpu_pm_notifier);
	regulator_unregister(kvreg->rdev);
	platform_set_drvdata(pdev, NULL);
	kryo_debugfs_deinit(kvreg);