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

Commit 6b87f488 authored by Linux Build Service Account's avatar Linux Build Service Account Committed by Gerrit - the friendly Code Review server
Browse files

Merge "regulator: cpr-regulator: add online CPUs based voltage adjustment support"

parents 02073349 7ea105a4
Loading
Loading
Loading
Loading
+77 −0
Original line number Diff line number Diff line
@@ -437,6 +437,41 @@ Optional properties:
				qcom,cpr-fuse-version-map property is not specified, then
				qcom,cpr-virtual-corner-quotient-adjustment must contain a single tuple which is then
				applied unconditionally.
- qcom,cpr-cpus:		Array of CPU phandles which correspond to the cores that this cpr-regulator
				device must monitor when adjusting the voltage and/or target quotient based
				upon the number of online cores. This property must be specified in order to
				utilize the qcom,cpr-online-cpu-virtual-corner-init-voltage-adjustment or
				qcom,cpr-online-cpu-virtual-corner-quotient-adjustment properties.
- qcom,cpr-online-cpu-virtual-corner-init-voltage-adjustment:	Array of tuples where each tuple specifies
				the voltage adjustment for each corner. These adjustments apply to the
				initial voltage of each corner. The size of each tuple must be equal
				to qcom,cpr-fuse-corners if consumers request fuse corners or the length of
				qcom,cpr-corner-map if consumers request virtual corners. In each tuple, the
				value corresponds to the voltage adjustment when running at that corner at
				init, from lowest to highest. The tuples must be organized into 1 group if
				qcom,cpr-fuse-version-map is not specified or the same number of groups as
				the number of tuples in qcom,cpr-fuse-version-map. The i-th group of tuples
				corresponds to the voltage adjustments for i-th fuse version map tuple. In
				each group, there are 1 plus length of qcom,cpr-cpus tuples, each tuple
				corresponds to the number of cores online, from 0 to the number of elements
				in qcom,cpr-cpus.
- qcom,cpr-online-cpu-init-voltage-as-ceiling:	Boolean which indicates that the ceiling voltage used for a
				given virtual corner may be reduced to the per number of cores online,
				per-virtual corner ceiling voltage value. This property takes precedence
				over qcom,cpr-scaled-init-voltage-as-ceiling if both are specified.
- qcom,cpr-online-cpu-virtual-corner-quotient-adjustment:	Array of tuples where each tuple specifies
				the quotient adjustment for each corner. These adjustments will be applied
				to each corner at run time. The size of each tuple must be equal to
				qcom,cpr-fuse-corners if consumers request fuse corners or the length of
				qcom,cpr-corner-map if consumers request virtual corners. In each tuple,
				the value corresponds to the quotient adjustment when running at that corner,
				from lowest to highest. The tuples must be organized into 1 group if
				qcom,cpr-fuse-version-map is not specified or the same number of groups
				as the number of tuples in qcom,cpr-fuse-version-map. The i-th group of
				tuples corresponds to the quotient adjustments for i-th fuse version map
				tuple. In each group, there are 1 plus length of qcom,cpr-cpus tuples,
				each tuple corresponds to the number of cores online, from 0 to the
				number of elements in qcom,cpr-cpus.
- qcom,cpr-init-voltage-as-ceiling: Boolean which indicates that the ceiling voltage used for a given virtual
				corner may be reduced to the per-fuse-corner initial voltage fuse value.
- qcom,cpr-scaled-init-voltage-as-ceiling: Boolean which indicates that the ceiling voltage used for a given
@@ -578,6 +613,9 @@ Optional properties:
				qcom,cpr-min-quot-diff-adjustment must contain a single tuple which is then
				applied unconditionally. The qcom,cpr-min-quot-diff-adjustment property must be specified
				if the qcom,cpr-fuse-min-quot-diff property is specified.
- qcom,cpr-skip-voltage-change-during-suspend: Boolean property which indicates that the CPR voltage
				should not be adjusted based upon the number of online cores while
				entering or exiting system suspend.

Example:
	apc_vreg_corner: regulator@f9018000 {
@@ -752,6 +790,45 @@ Example:
			<0 0 0 100 0 0 0 0 0 0 0 0>,
			<0 0 0 0 0 0 0 0 0 0 0 (-300)>,
			<0 0 0 (-60) 0 0 0 0 0 0 0 0>;
		qcom,cpr-cpus = <&CPU0 &CPU1 &CPU2 &CPU3>;
		qcom,cpr-online-cpu-virtual-corner-init-voltage-adjustment =
			/* 1st fuse version tuple matched */
			<0 0 0 (-10000) (-10000) (-10000) (-15000) (-15000) (-20000) 0 (-20000) (-30000) >, /* 0 CPUs online */
			<0 0 0 (-10000) (-10000) (-10000) (-15000) (-15000) (-20000) 0 (-20000) (-30000) >, /* 1 CPUs online */
			<0 0 0 (-5000) (-5000) (-5000) (-5000) (-5000) (-10000) 0 (-10000) (-10000) >, /* 2 CPUs online */
			<0 0 0 0 0 0 0 0 0 0 0 0>, /* 3 CPUs online */
			<0 0 0 0 0 0 0 0 0 0 0 0>, /* 4 CPUs online */
			/* 2nd fuse version tuple matched */
			<0 0 0 (-10000) (-10000) (-10000) (-15000) (-15000) (-20000) 0 (-20000) (-30000) >, /* 0 CPUs online */
			<0 0 0 (-10000) (-10000) (-10000) (-15000) (-15000) (-20000) 0 (-20000) (-30000) >, /* 1 CPUs online */
			<0 0 0 (-5000) (-5000) (-5000) (-5000) (-5000) (-10000) 0 (-10000) (-10000) >, /* 2 CPUs online */
			<0 0 0 0 0 0 0 0 0 0 0 0>, /* 3 CPUs online */
			<0 0 0 0 0 0 0 0 0 0 0 0>, /* 4 CPUs online */
			/* 3rd fuse version tuple matched */
			<0 0 0 (-10000) (-10000) (-10000) (-15000) (-15000) (-20000) 0 (-20000) (-30000) >, /* 0 CPUs online */
			<0 0 0 (-10000) (-10000) (-10000) (-15000) (-15000) (-20000) 0 (-20000) (-30000) >, /* 1 CPUs online */
			<0 0 0 (-5000) (-5000) (-5000) (-5000) (-5000) (-10000) 0 (-10000) (-10000) >, /* 2 CPUs online */
			<0 0 0 0 0 0 0 0 0 0 0 0>, /* 3 CPUs online */
			<0 0 0 0 0 0 0 0 0 0 0 0>; /* 4 CPUs online */
		qcom,cpr-online-cpu-virtual-corner-quotient-adjustment =
			/* 1st fuse version tuple matched */
			<0 0 0 (-6) (-6) (-6) (-9) (-9) (-12) 0 (-12) (-18)>, /* 0 CPUs online */
			<0 0 0 (-6) (-6) (-6) (-9) (-9) (-12) 0 (-12) (-18)>, /* 1 CPUs online */
			<0 0 0 (-3) (-3) (-3) (-3) (-3) (-6) 0 (-6) (-6)>, /* 2 CPUs online */
			<0 0 0 0 0 0 0 0 0 0 0 0>, /* 3 CPUs online */
			<0 0 0 0 0 0 0 0 0 0 0 0>, /* 4 CPUs online */
			/* 2nd fuse version tuple matched */
			<0 0 0 (-6) (-6) (-6) (-9) (-9) (-12) 0 (-12) (-18)>, /* 0 CPUs online */
			<0 0 0 (-6) (-6) (-6) (-9) (-9) (-12) 0 (-12) (-18)>, /* 1 CPUs online */
			<0 0 0 (-3) (-3) (-3) (-3) (-3) (-6) 0 (-6) (-6)>, /* 2 CPUs online */
			<0 0 0 0 0 0 0 0 0 0 0 0>, /* 3 CPUs online */
			<0 0 0 0 0 0 0 0 0 0 0 0>, /* 4 CPUs online */
			/* 3rd fuse version tuple matched */
			<0 0 0 (-21) (-21) (-21) (-32) (-32) (-42) 0 (-42) (-63)>, /* 0 CPUs online */
			<0 0 0 (-21) (-21) (-21) (-32) (-32) (-42) 0 (-42) (-63)>, /* 1 CPUs online */
			<0 0 0 (-11) (-11) (-11) (-11) (-11) (-21) 0 (-21) (-21)>, /* 2 CPUs online */
			<0 0 0 0 0 0 0 0 0 0 0 0>, /* 3 CPUs online */
			<0 0 0 0 0 0 0 0 0 0 0 0>; /* 4 CPUs online */
		qcom,cpr-allowed =
			<0>,
			<1>,
+479 −18
Original line number Diff line number Diff line
@@ -14,6 +14,7 @@
#define pr_fmt(fmt) "%s: " fmt, __func__

#include <linux/module.h>
#include <linux/cpu.h>
#include <linux/err.h>
#include <linux/string.h>
#include <linux/kernel.h>
@@ -294,7 +295,19 @@ struct cpr_regulator {
	u32		num_corners;
	int		*quot_adjust;

	int			num_adj_cpus;
	int			online_cpus;
	int			*adj_cpus;
	int			**adj_cpus_save_ctl;
	int			**adj_cpus_save_irq;
	int			**adj_cpus_last_volt;
	int			**adj_cpus_quot_adjust;
	int			**adj_cpus_open_loop_volt;
	bool			adj_cpus_open_loop_volt_as_ceiling;
	struct notifier_block	cpu_notifier;

	bool		is_cpr_suspended;
	bool		skip_voltage_change_during_suspend;
};

#define CPR_DEBUG_MASK_IRQ	BIT(0)
@@ -1013,7 +1026,7 @@ static int cpr_regulator_disable(struct regulator_dev *rdev)
}

static int cpr_regulator_set_voltage(struct regulator_dev *rdev,
		int corner, int corner_max, unsigned *selector)
		int corner, bool reset_quot)
{
	struct cpr_regulator *cpr_vreg = rdev_get_drvdata(rdev);
	int rc;
@@ -1044,6 +1057,9 @@ static int cpr_regulator_set_voltage(struct regulator_dev *rdev,

	if (cpr_is_allowed(cpr_vreg) && cpr_vreg->vreg_enabled) {
		cpr_irq_clr(cpr_vreg);
		if (reset_quot)
			cpr_corner_restore(cpr_vreg, corner);
		else
			cpr_corner_switch(cpr_vreg, corner);
		cpr_ctl_enable(cpr_vreg, corner);
	}
@@ -1056,6 +1072,12 @@ _exit:
	return rc;
}

static int cpr_regulator_set_voltage_op(struct regulator_dev *rdev,
		int corner, int corner_max, unsigned *selector)
{
	return cpr_regulator_set_voltage(rdev, corner, false);
}

static int cpr_regulator_get_voltage(struct regulator_dev *rdev)
{
	struct cpr_regulator *cpr_vreg = rdev_get_drvdata(rdev);
@@ -1067,7 +1089,7 @@ static struct regulator_ops cpr_corner_ops = {
	.enable			= cpr_regulator_enable,
	.disable		= cpr_regulator_disable,
	.is_enabled		= cpr_regulator_is_enabled,
	.set_voltage		= cpr_regulator_set_voltage,
	.set_voltage		= cpr_regulator_set_voltage_op,
	.get_voltage		= cpr_regulator_get_voltage,
};

@@ -1076,15 +1098,10 @@ static int cpr_suspend(struct cpr_regulator *cpr_vreg)
{
	cpr_debug(cpr_vreg, "suspend\n");

	mutex_lock(&cpr_vreg->cpr_mutex);

	cpr_ctl_disable(cpr_vreg);

	cpr_irq_clr(cpr_vreg);

	cpr_vreg->is_cpr_suspended = true;

	mutex_unlock(&cpr_vreg->cpr_mutex);
	return 0;
}

@@ -1093,14 +1110,10 @@ static int cpr_resume(struct cpr_regulator *cpr_vreg)
{
	cpr_debug(cpr_vreg, "resume\n");

	mutex_lock(&cpr_vreg->cpr_mutex);

	cpr_vreg->is_cpr_suspended = false;
	cpr_irq_clr(cpr_vreg);

	cpr_ctl_enable(cpr_vreg, cpr_vreg->corner);

	mutex_unlock(&cpr_vreg->cpr_mutex);
	return 0;
}

@@ -1108,21 +1121,35 @@ static int cpr_regulator_suspend(struct platform_device *pdev,
				 pm_message_t state)
{
	struct cpr_regulator *cpr_vreg = platform_get_drvdata(pdev);
	int rc = 0;

	mutex_lock(&cpr_vreg->cpr_mutex);

	if (cpr_is_allowed(cpr_vreg))
		return cpr_suspend(cpr_vreg);
	else
		return 0;
		rc = cpr_suspend(cpr_vreg);

	cpr_vreg->is_cpr_suspended = true;

	mutex_unlock(&cpr_vreg->cpr_mutex);

	return rc;
}

static int cpr_regulator_resume(struct platform_device *pdev)
{
	struct cpr_regulator *cpr_vreg = platform_get_drvdata(pdev);
	int rc = 0;

	mutex_lock(&cpr_vreg->cpr_mutex);

	cpr_vreg->is_cpr_suspended = false;

	if (cpr_is_allowed(cpr_vreg))
		return cpr_resume(cpr_vreg);
	else
		return 0;
		rc = cpr_resume(cpr_vreg);

	mutex_unlock(&cpr_vreg->cpr_mutex);

	return rc;
}
#else
#define cpr_regulator_suspend NULL
@@ -3483,6 +3510,429 @@ static int cpr_init_cpr_parameters(struct platform_device *pdev,
	return 0;
}

static void cpr_regulator_switch_adj_cpus(struct cpr_regulator *cpr_vreg)
{
	cpr_vreg->last_volt = cpr_vreg->adj_cpus_last_volt
					[cpr_vreg->online_cpus];
	cpr_vreg->save_ctl = cpr_vreg->adj_cpus_save_ctl[cpr_vreg->online_cpus];
	cpr_vreg->save_irq = cpr_vreg->adj_cpus_save_irq[cpr_vreg->online_cpus];

	if (cpr_vreg->adj_cpus_quot_adjust)
		cpr_vreg->quot_adjust = cpr_vreg->adj_cpus_quot_adjust
						[cpr_vreg->online_cpus];
	if (cpr_vreg->adj_cpus_open_loop_volt)
		cpr_vreg->open_loop_volt
			= cpr_vreg->adj_cpus_open_loop_volt
				[cpr_vreg->online_cpus];
	if (cpr_vreg->adj_cpus_open_loop_volt_as_ceiling)
		cpr_vreg->ceiling_volt = cpr_vreg->open_loop_volt;
}

static void cpr_regulator_set_online_cpus(struct cpr_regulator *cpr_vreg)
{
	int i, j;

	cpr_vreg->online_cpus = 0;
	get_online_cpus();
	for_each_online_cpu(i)
		for (j = 0; j < cpr_vreg->num_adj_cpus; j++)
			if (i == cpr_vreg->adj_cpus[j])
				cpr_vreg->online_cpus++;
	put_online_cpus();
}

static int cpr_regulator_cpu_callback(struct notifier_block *nb,
					    unsigned long action, void *data)
{
	struct cpr_regulator *cpr_vreg = container_of(nb, struct cpr_regulator,
					cpu_notifier);
	int prev_online_cpus = cpr_vreg->online_cpus;
	int cpu = (long)data;
	int rc, i;

	action &= ~CPU_TASKS_FROZEN;

	if (action != CPU_UP_PREPARE && action != CPU_UP_CANCELED
	    && action != CPU_DEAD)
		return NOTIFY_OK;

	if (cpr_vreg->skip_voltage_change_during_suspend) {
		mutex_lock(&cpr_vreg->cpr_mutex);

		if (cpr_vreg->is_cpr_suspended) {
			/* Do nothing during system suspend/resume */
			mutex_unlock(&cpr_vreg->cpr_mutex);
			return NOTIFY_OK;
		}

		mutex_unlock(&cpr_vreg->cpr_mutex);
	}

	cpr_regulator_set_online_cpus(cpr_vreg);

	if (action == CPU_UP_PREPARE)
		for (i = 0; i < cpr_vreg->num_adj_cpus; i++)
			if (cpu == cpr_vreg->adj_cpus[i]) {
				cpr_vreg->online_cpus++;
				break;
			}

	if (cpr_vreg->online_cpus == prev_online_cpus)
		return NOTIFY_OK;

	cpr_debug(cpr_vreg, "adjusting quotient for %d cpus\n",
			cpr_vreg->online_cpus);

	cpr_regulator_switch_adj_cpus(cpr_vreg);

	if (cpr_vreg->corner) {
		rc = cpr_regulator_set_voltage(cpr_vreg->rdev,
				cpr_vreg->corner, true);
		if (rc)
			cpr_err(cpr_vreg, "could not update quotient, rc=%d\n",
				rc);
	}

	return NOTIFY_OK;
}

static int cpr_parse_adj_cpus_init_voltage(struct cpr_regulator *cpr_vreg,
		struct device *dev)
{
	int rc, i, j, k, tuple_count, tuple_match, len, offset;
	int *temp;

	if (!of_find_property(dev->of_node,
		   "qcom,cpr-online-cpu-virtual-corner-init-voltage-adjustment",
		   NULL))
		return 0;

	if (cpr_vreg->cpr_fuse_map_count) {
		if (cpr_vreg->cpr_fuse_map_match == FUSE_MAP_NO_MATCH) {
			/* No matching index to use for voltage adjustment. */
			return 0;
		}
		tuple_count = cpr_vreg->cpr_fuse_map_count;
		tuple_match = cpr_vreg->cpr_fuse_map_match;
	} else {
		tuple_count = 1;
		tuple_match = 0;
	}

	len = (cpr_vreg->num_adj_cpus + 1) * tuple_count
		* cpr_vreg->num_corners;

	temp = kzalloc(sizeof(int) * len, GFP_KERNEL);
	if (!temp) {
		cpr_err(cpr_vreg, "Could not allocate memory\n");
		return -ENOMEM;
	}

	cpr_vreg->adj_cpus_open_loop_volt = devm_kzalloc(dev,
				sizeof(int *) * (cpr_vreg->num_adj_cpus + 1),
				GFP_KERNEL);
	if (!cpr_vreg->adj_cpus_open_loop_volt) {
		cpr_err(cpr_vreg, "Could not allocate memory\n");
		rc = -ENOMEM;
		goto done;
	}

	cpr_vreg->adj_cpus_open_loop_volt[0] = devm_kzalloc(dev,
				sizeof(int) * (cpr_vreg->num_adj_cpus + 1)
				* (cpr_vreg->num_corners + 1),
				GFP_KERNEL);
	if (!cpr_vreg->adj_cpus_open_loop_volt[0]) {
		cpr_err(cpr_vreg, "Could not allocate memory\n");
		rc = -ENOMEM;
		goto done;
	}
	for (i = 1; i <= cpr_vreg->num_adj_cpus; i++)
		cpr_vreg->adj_cpus_open_loop_volt[i] =
			cpr_vreg->adj_cpus_open_loop_volt[0] +
			i * (cpr_vreg->num_corners + 1);

	rc = of_property_read_u32_array(dev->of_node,
		"qcom,cpr-online-cpu-virtual-corner-init-voltage-adjustment",
		temp, len);
	if (rc) {
		cpr_err(cpr_vreg, "failed to read qcom,cpr-online-cpu-virtual-corner-init-voltage-adjustment, rc=%d\n",
			rc);
		goto done;
	}

	cpr_debug(cpr_vreg, "Open loop voltage based on number of online CPUs:\n");
	offset = tuple_match * cpr_vreg->num_corners *
			(cpr_vreg->num_adj_cpus + 1);

	for (i = 0; i <= cpr_vreg->num_adj_cpus; i++) {
		for (j = CPR_CORNER_MIN; j <= cpr_vreg->num_corners; j++) {
			k = j - 1 + offset;

			cpr_vreg->adj_cpus_open_loop_volt[i][j]
				= cpr_vreg->open_loop_volt[j] + temp[k];
			cpr_vreg->adj_cpus_open_loop_volt[i][j]
			    = DIV_ROUND_UP(cpr_vreg->
					adj_cpus_open_loop_volt[i][j],
				cpr_vreg->step_volt) * cpr_vreg->step_volt;

			if (cpr_vreg->adj_cpus_open_loop_volt[i][j]
					> cpr_vreg->ceiling_volt[j])
				cpr_vreg->adj_cpus_open_loop_volt[i][j]
					= cpr_vreg->ceiling_volt[j];
			if (cpr_vreg->adj_cpus_open_loop_volt[i][j]
					< cpr_vreg->floor_volt[j])
				cpr_vreg->adj_cpus_open_loop_volt[i][j]
					= cpr_vreg->floor_volt[j];

			cpr_debug(cpr_vreg, "cpus=%d, corner=%d, volt=%d\n",
				i, j, cpr_vreg->adj_cpus_open_loop_volt[i][j]);
		}
		offset += cpr_vreg->num_corners;
	}

	cpr_vreg->adj_cpus_open_loop_volt_as_ceiling
		= of_property_read_bool(dev->of_node,
			"qcom,cpr-online-cpu-init-voltage-as-ceiling");
done:
	kfree(temp);
	return rc;
}

static int cpr_parse_adj_cpus_target_quot(struct cpr_regulator *cpr_vreg,
		struct device *dev)
{
	int rc, i, j, k, tuple_count, tuple_match, len, offset;
	int *temp;

	if (!of_find_property(dev->of_node,
		   "qcom,cpr-online-cpu-virtual-corner-quotient-adjustment",
		   NULL))
		return 0;

	if (cpr_vreg->cpr_fuse_map_count) {
		if (cpr_vreg->cpr_fuse_map_match == FUSE_MAP_NO_MATCH) {
			/* No matching index to use for quotient adjustment. */
			return 0;
		}
		tuple_count = cpr_vreg->cpr_fuse_map_count;
		tuple_match = cpr_vreg->cpr_fuse_map_match;
	} else {
		tuple_count = 1;
		tuple_match = 0;
	}

	len = (cpr_vreg->num_adj_cpus + 1) * tuple_count
		* cpr_vreg->num_corners;

	temp = kzalloc(sizeof(int) * len, GFP_KERNEL);
	if (!temp) {
		cpr_err(cpr_vreg, "Could not allocate memory\n");
		return -ENOMEM;
	}

	cpr_vreg->adj_cpus_quot_adjust = devm_kzalloc(dev,
				sizeof(int *) * (cpr_vreg->num_adj_cpus + 1),
				GFP_KERNEL);
	if (!cpr_vreg->adj_cpus_quot_adjust) {
		cpr_err(cpr_vreg, "Could not allocate memory\n");
		rc = -ENOMEM;
		goto done;
	}

	cpr_vreg->adj_cpus_quot_adjust[0] = devm_kzalloc(dev,
				sizeof(int) * (cpr_vreg->num_adj_cpus + 1)
				* (cpr_vreg->num_corners + 1),
				GFP_KERNEL);
	if (!cpr_vreg->adj_cpus_quot_adjust[0]) {
		cpr_err(cpr_vreg, "Could not allocate memory\n");
		rc = -ENOMEM;
		goto done;
	}
	for (i = 1; i <= cpr_vreg->num_adj_cpus; i++)
		cpr_vreg->adj_cpus_quot_adjust[i] =
			cpr_vreg->adj_cpus_quot_adjust[0] +
			i * (cpr_vreg->num_corners + 1);


	rc = of_property_read_u32_array(dev->of_node,
		"qcom,cpr-online-cpu-virtual-corner-quotient-adjustment",
		temp, len);
	if (rc) {
		cpr_err(cpr_vreg, "failed to read qcom,cpr-online-cpu-virtual-corner-quotient-adjustment, rc=%d\n",
			rc);
		goto done;
	}

	cpr_debug(cpr_vreg, "Target quotients based on number of online CPUs:\n");
	offset = tuple_match * cpr_vreg->num_corners *
			(cpr_vreg->num_adj_cpus + 1);

	for (i = 0; i <= cpr_vreg->num_adj_cpus; i++) {
		for (j = CPR_CORNER_MIN; j <= cpr_vreg->num_corners; j++) {
			k = j - 1 + offset;

			cpr_vreg->adj_cpus_quot_adjust[i][j] =
					cpr_vreg->quot_adjust[j] - temp[k];

			cpr_debug(cpr_vreg, "cpus=%d, corner=%d, quot=%d\n",
				i, j,
				cpr_vreg->cpr_fuse_target_quot[
							cpr_vreg->corner_map[j]]
					- cpr_vreg->adj_cpus_quot_adjust[i][j]);
		}
		offset += cpr_vreg->num_corners;
	}

done:
	kfree(temp);
	return rc;
}

static int cpr_init_per_cpu_adjustments(struct cpr_regulator *cpr_vreg,
		struct device *dev)
{
	struct device_node *cpu_node;
	int rc, i, j, cpu;

	if (!of_find_property(dev->of_node, "qcom,cpr-cpus",
				&cpr_vreg->num_adj_cpus)) {
		/* No per-online CPU adjustment needed */
		return 0;
	}
	cpr_vreg->num_adj_cpus /= sizeof(u32);

	if (!of_find_property(dev->of_node,
		   "qcom,cpr-online-cpu-virtual-corner-init-voltage-adjustment",
		   NULL)
	    && !of_find_property(dev->of_node,
		   "qcom,cpr-online-cpu-virtual-corner-quotient-adjustment",
		   NULL)) {
		cpr_err(cpr_vreg, "qcom,cpr-online-cpu-virtual-corner-init-voltage-adjustment and/or qcom,cpr-online-cpu-virtual-corner-quotient-adjustment must be specified\n");
		return -EINVAL;
	}

	cpr_vreg->adj_cpus = devm_kzalloc(dev,
				sizeof(int) * cpr_vreg->num_adj_cpus,
				GFP_KERNEL);
	if (!cpr_vreg->adj_cpus) {
		cpr_err(cpr_vreg, "Could not allocate memory\n");
		return -ENOMEM;
	}

	for (i = 0; i < cpr_vreg->num_adj_cpus; i++) {
		cpu_node = of_parse_phandle(dev->of_node, "qcom,cpr-cpus", i);
		if (!cpu_node) {
			cpr_err(cpr_vreg, "could not find CPU node %d\n", i);
			return -EINVAL;
		}
		cpr_vreg->adj_cpus[i] = -1;
		for_each_possible_cpu(cpu) {
			if (of_get_cpu_node(cpu, NULL) == cpu_node) {
				cpr_vreg->adj_cpus[i] = cpu;
				break;
			}
		}
		of_node_put(cpu_node);
	}

	rc = cpr_parse_adj_cpus_init_voltage(cpr_vreg, dev);
	if (rc) {
		cpr_err(cpr_vreg, "cpr_parse_adj_cpus_init_voltage failed: rc =%d\n",
			rc);
		return rc;
	}

	rc = cpr_parse_adj_cpus_target_quot(cpr_vreg, dev);
	if (rc) {
		cpr_err(cpr_vreg, "cpr_parse_adj_cpus_target_quot failed: rc =%d\n",
			rc);
		return rc;
	}

	cpr_vreg->adj_cpus_last_volt = devm_kzalloc(dev,
				sizeof(int *) * (cpr_vreg->num_adj_cpus + 1),
				GFP_KERNEL);
	cpr_vreg->adj_cpus_save_ctl = devm_kzalloc(dev,
				sizeof(int *) * (cpr_vreg->num_adj_cpus + 1),
				GFP_KERNEL);
	cpr_vreg->adj_cpus_save_irq = devm_kzalloc(dev,
				sizeof(int *) * (cpr_vreg->num_adj_cpus + 1),
				GFP_KERNEL);
	if (!cpr_vreg->adj_cpus_last_volt || !cpr_vreg->adj_cpus_save_ctl ||
		!cpr_vreg->adj_cpus_save_irq) {
		cpr_err(cpr_vreg, "Could not allocate memory\n");
		return -ENOMEM;
	}

	cpr_vreg->adj_cpus_last_volt[0] = devm_kzalloc(dev,
				sizeof(int) * (cpr_vreg->num_adj_cpus + 1)
				* (cpr_vreg->num_corners + 1),
				GFP_KERNEL);
	cpr_vreg->adj_cpus_save_ctl[0] = devm_kzalloc(dev,
				sizeof(int) * (cpr_vreg->num_adj_cpus + 1)
				* (cpr_vreg->num_corners + 1),
				GFP_KERNEL);
	cpr_vreg->adj_cpus_save_irq[0] = devm_kzalloc(dev,
				sizeof(int) * (cpr_vreg->num_adj_cpus + 1)
				* (cpr_vreg->num_corners + 1),
				GFP_KERNEL);
	if (!cpr_vreg->adj_cpus_last_volt[0] ||
		!cpr_vreg->adj_cpus_save_ctl[0] ||
		!cpr_vreg->adj_cpus_save_irq[0]) {
		cpr_err(cpr_vreg, "Could not allocate memory\n");
		return -ENOMEM;
	}
	for (i = 1; i <= cpr_vreg->num_adj_cpus; i++) {
		j = i * (cpr_vreg->num_corners + 1);
		cpr_vreg->adj_cpus_last_volt[i] =
			cpr_vreg->adj_cpus_last_volt[0] + j;
		cpr_vreg->adj_cpus_save_ctl[i] =
			cpr_vreg->adj_cpus_save_ctl[0] + j;
		cpr_vreg->adj_cpus_save_irq[i] =
			cpr_vreg->adj_cpus_save_irq[0] + j;
	}


	for (i = 0; i <= cpr_vreg->num_adj_cpus; i++) {
		for (j = CPR_CORNER_MIN; j <= cpr_vreg->num_corners; j++) {

			cpr_vreg->adj_cpus_save_ctl[i][j] =
				cpr_vreg->save_ctl[j];
			cpr_vreg->adj_cpus_save_irq[i][j] =
				cpr_vreg->save_irq[j];

			cpr_vreg->adj_cpus_last_volt[i][j]
				= cpr_vreg->adj_cpus_open_loop_volt
				? cpr_vreg->adj_cpus_open_loop_volt[i][j]
					: cpr_vreg->open_loop_volt[j];
		}
	}

	cpr_regulator_set_online_cpus(cpr_vreg);
	cpr_debug(cpr_vreg, "%d cpus online\n", cpr_vreg->online_cpus);

	devm_kfree(dev, cpr_vreg->last_volt);
	devm_kfree(dev, cpr_vreg->save_ctl);
	devm_kfree(dev, cpr_vreg->save_irq);
	if (cpr_vreg->adj_cpus_quot_adjust)
		devm_kfree(dev, cpr_vreg->quot_adjust);
	if (cpr_vreg->adj_cpus_open_loop_volt)
		devm_kfree(dev, cpr_vreg->open_loop_volt);
	if (cpr_vreg->adj_cpus_open_loop_volt_as_ceiling)
		devm_kfree(dev, cpr_vreg->ceiling_volt);

	cpr_regulator_switch_adj_cpus(cpr_vreg);

	cpr_vreg->skip_voltage_change_during_suspend
			= of_property_read_bool(dev->of_node,
				"qcom,cpr-skip-voltage-change-during-suspend");

	cpr_vreg->cpu_notifier.notifier_call = cpr_regulator_cpu_callback;
	register_hotcpu_notifier(&cpr_vreg->cpu_notifier);

	return rc;
}

static int cpr_init_cpr(struct platform_device *pdev,
			       struct cpr_regulator *cpr_vreg)
{
@@ -4285,6 +4735,14 @@ static int cpr_regulator_probe(struct platform_device *pdev)
		goto err_out;
	}

	/* Load per-online CPU adjustment data */
	rc = cpr_init_per_cpu_adjustments(cpr_vreg, &pdev->dev);
	if (rc) {
		cpr_err(cpr_vreg, "cpr_init_per_cpu_adjustments failed: rc=%d\n",
			rc);
		goto err_out;
	}

	cpr_efuse_free(cpr_vreg);

	/*
@@ -4343,6 +4801,9 @@ static int cpr_regulator_remove(struct platform_device *pdev)
		list_del(&cpr_vreg->list);
		mutex_unlock(&cpr_regulator_list_mutex);

		if (cpr_vreg->adj_cpus)
			unregister_hotcpu_notifier(&cpr_vreg->cpu_notifier);

		cpr_apc_exit(cpr_vreg);
		cpr_debugfs_remove(cpr_vreg);
		regulator_unregister(cpr_vreg->rdev);