diff mbox series

[09/18] cpufreq: qcom-nvmem: Add support for voltage scaling

Message ID 20230612053922.3284394-10-dmitry.baryshkov@linaro.org
State New
Headers show
Series ARM: qcom: apq8064: support CPU frequency scaling | expand

Commit Message

Dmitry Baryshkov June 12, 2023, 5:39 a.m. UTC
If requested by the platform, scale voltages according to data specified
in the OPP tables.

Signed-off-by: Dmitry Baryshkov <dmitry.baryshkov@linaro.org>
---
 drivers/cpufreq/qcom-cpufreq-nvmem.c | 115 ++++++++++++++++++++++++++-
 1 file changed, 114 insertions(+), 1 deletion(-)
diff mbox series

Patch

diff --git a/drivers/cpufreq/qcom-cpufreq-nvmem.c b/drivers/cpufreq/qcom-cpufreq-nvmem.c
index fee9736f7326..18d6e6ed1bd0 100644
--- a/drivers/cpufreq/qcom-cpufreq-nvmem.c
+++ b/drivers/cpufreq/qcom-cpufreq-nvmem.c
@@ -26,6 +26,7 @@ 
 #include <linux/platform_device.h>
 #include <linux/pm_domain.h>
 #include <linux/pm_opp.h>
+#include <linux/regulator/consumer.h>
 #include <linux/slab.h>
 #include <linux/soc/qcom/smem.h>
 
@@ -39,6 +40,7 @@  struct qcom_cpufreq_match_data {
 			   char **pvs_name,
 			   struct qcom_cpufreq_drv *drv);
 	const char **genpd_names;
+	const char * const *regulator_names;
 };
 
 struct qcom_cpufreq_drv {
@@ -218,6 +220,110 @@  static const struct qcom_cpufreq_match_data match_data_qcs404 = {
 	.genpd_names = qcs404_genpd_names,
 };
 
+#define NUM_SUPPLIES 2
+static int qcom_cpufreq_config_regulators(struct device *dev,
+					  struct dev_pm_opp *old_opp,
+					  struct dev_pm_opp *new_opp,
+					  struct regulator **regulators,
+					  unsigned int count)
+{
+	struct dev_pm_opp_supply supplies[NUM_SUPPLIES];
+	unsigned long old_freq, freq;
+	unsigned int i;
+	int ret;
+
+	if (WARN_ON_ONCE(count != NUM_SUPPLIES))
+		return -EINVAL;
+
+	ret = dev_pm_opp_get_supplies(new_opp, supplies);
+	if (WARN_ON(ret))
+		return ret;
+
+	old_freq = dev_pm_opp_get_freq(old_opp);
+	freq = dev_pm_opp_get_freq(new_opp);
+
+	WARN_ON(!old_freq || !freq);
+	if (freq > old_freq) {
+		for (i = 0; i < count; i++) {
+			struct regulator *reg = regulators[i];
+			struct dev_pm_opp_supply *supply = &supplies[i];
+
+			dev_dbg(dev, "%s: voltages (mV): %lu %lu %lu\n", __func__,
+				supply->u_volt_min, supply->u_volt, supply->u_volt_max);
+
+			ret = regulator_set_voltage_triplet(reg,
+							    supply->u_volt_min,
+							    supply->u_volt,
+							    supply->u_volt_max);
+			if (ret) {
+				dev_err(dev, "%s: failed to set voltage (%lu %lu %lu mV): %d\n",
+					__func__, supply->u_volt_min, supply->u_volt,
+					supply->u_volt_max, ret);
+				goto restore_backwards;
+			}
+		}
+	} else {
+		for (i = count; i > 0; i--) {
+			struct regulator *reg = regulators[i - 1];
+			struct dev_pm_opp_supply *supply = &supplies[i - 1];
+
+			dev_dbg(dev, "%s: voltages (mV): %lu %lu %lu\n", __func__,
+				supply->u_volt_min, supply->u_volt, supply->u_volt_max);
+
+			ret = regulator_set_voltage_triplet(reg,
+							    supply->u_volt_min,
+							    supply->u_volt,
+							    supply->u_volt_max);
+			if (ret) {
+				dev_err(dev, "%s: failed to set voltage (%lu %lu %lu mV): %d\n",
+					__func__, supply->u_volt_min, supply->u_volt,
+					supply->u_volt_max, ret);
+				goto restore_forward;
+			}
+		}
+	}
+
+	return 0;
+
+restore_backwards:
+
+	dev_pm_opp_get_supplies(old_opp, supplies);
+
+	for (; i > 0; i--) {
+		struct regulator *reg = regulators[i - 1];
+		struct dev_pm_opp_supply *supply = &supplies[i - 1];
+
+		dev_dbg(dev, "%s: voltages (mV): %lu %lu %lu\n", __func__,
+			supply->u_volt_min, supply->u_volt, supply->u_volt_max);
+
+		regulator_set_voltage_triplet(reg,
+					      supply->u_volt_min,
+					      supply->u_volt,
+					      supply->u_volt_max);
+	}
+
+	return ret;
+
+restore_forward:
+
+	dev_pm_opp_get_supplies(old_opp, supplies);
+
+	for ( ; i < count; i++) {
+		struct regulator *reg = regulators[i];
+		struct dev_pm_opp_supply *supply = &supplies[i];
+
+		dev_dbg(dev, "%s: voltages (mV): %lu %lu %lu\n", __func__,
+			supply->u_volt_min, supply->u_volt, supply->u_volt_max);
+
+		regulator_set_voltage_triplet(reg,
+					      supply->u_volt_min,
+					      supply->u_volt,
+					      supply->u_volt_max);
+	}
+
+	return ret;
+}
+
 static int qcom_cpufreq_probe(struct platform_device *pdev)
 {
 	struct qcom_cpufreq_drv *drv;
@@ -305,7 +411,14 @@  static int qcom_cpufreq_probe(struct platform_device *pdev)
 			config.virt_devs = NULL;
 		}
 
-		if (config.supported_hw || config.genpd_names) {
+		if (drv->data->regulator_names) {
+			config.config_regulators = qcom_cpufreq_config_regulators;
+			config.regulator_names = drv->data->regulator_names;
+		}
+
+		if (config.supported_hw ||
+		    config.genpd_names ||
+		    config.regulator_names) {
 			drv->opp_tokens[cpu] = dev_pm_opp_set_config(cpu_dev, &config);
 			if (drv->opp_tokens[cpu] < 0) {
 				ret = drv->opp_tokens[cpu];