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

Commit 3d278ad8 authored by Tirupathi Reddy's avatar Tirupathi Reddy Committed by Gerrit - the friendly Code Review server
Browse files

regulator: cpr4: add online-core and temp based voltage adjustment support



Add support for hardware managed per-online-core and per-temperature
voltage adjustment. These adjustment methods may be used together or
independently. The configuration data for these adjustments including
the amount of adjustment for a given corner should be parsed from device
tree. Update the cpr3-regulator driver so that it writes the adjustment
values when enabled into the SDELTA hardware registers.

Change-Id: Idae9018f6a185202d38d210834ca337991fe83d9
Signed-off-by: default avatarTirupathi Reddy <tirupath@codeaurora.org>
parent 1fdcf773
Loading
Loading
Loading
Loading
+167 −0
Original line number Diff line number Diff line
@@ -112,6 +112,40 @@ APSS specific properties:
		    do not use PMIC voltage control register LSBs per actually
		    unique PMIC regulator output voltage.

- qcom,cpr-temp-point-map
	Usage:      required if qcom,corner-allow-temp-adjustment is specified
		    for at least one of the CPR3 regulators.
	Value type: <prop-encoded-array>
	Definition: The temperature points in decidegrees Celsius which indicate
		    the range of temperature bands supported. If t1, t2, and t3
		    are the temperature points, then the temperature bands are:
		    (-inf, t1], (t1, t2], (t2, t3], and (t3, inf).

- qcom,cpr-initial-temp-band
	Usage:      required if qcom,corner-allow-temp-adjustment is specified
		    for at least one of the CPR3 regulators.
	Value type: <u32>
	Definition: The initial temp band considering 0-based index at which
		    the baseline target quotients are derived and fused.

- qcom,cpr-step-quot-fixed
	Usage:      optional
	Value type: <u32>
	Definition: Fixed step quotient value used by controller for applying
		    the SDELTA margin adjustments on the programmed target
		    quotient values. The step quotient is the number of
		    additional ring oscillator ticks observed for each
		    qcom,voltage-step increase in vdd-supply output voltage.
		    Supported values: 0 - 63.

- qcom,cpr-voltage-settling-time
	Usage:      optional
	Value type: <u32>
		    The time in nanoseconds that it takes for the vdd-supply
		    voltage to settle after being increased or decreased by
		    qcom,voltage-step microvolts. This is used as the wait
		    time after applying SDELTA voltage margin adjustments.

=================================================
Second Level Nodes - CPR Threads for a Controller
=================================================
@@ -181,6 +215,82 @@ APSS specific properties:
		    interpolated CPR target quotient values.  These values are
		    interpolated between the target quotient Fmax fuse values.

- qcom,corner-allow-core-count-adjustment
	Usage:      optional
	Value type: <prop-encoded-array>
	Definition: A list of integer tuples which each define the CPR core
		    count adjustment feature enable state for each voltage
		    corner in order from lowest to highest. Each element in
		    the tuple should be either 0 (per-core-count adjustment
		    not allowed) or 1 (per-core-count adjustment allowed).

		    The list must contain qcom,cpr-fuse-combos number of tuples
		    in which case the tuples are matched to fuse combinations
		    1-to-1 or qcom,cpr-speed-bins number of tuples in which case
		    the tuples are matched to speed bins 1-to-1 or exactly 1
		    tuple which is used regardless of the fuse combination and
		    speed bin found on a given chip.

		    Each tuple must be of the length defined in the
		    corresponding element of the qcom,cpr-corners property or
		    the qcom,cpr-speed-bins property.  A single tuple may only
		    be specified if all of the corner counts in qcom,cpr-corners
		    are the same.

- qcom,max-core-count
	Usage:      required if qcom,corner-allow-core-count-adjustment is
		    specified for this CPR3 regulator.
	Value type: <u32>
	Definition: The maximum number of cores considered for core-count vmin
		    adjustments specified for this regulator voltage corners.

- qcom,corner-allow-temp-adjustment
	Usage:      optional
	Value type: <prop-encoded-array>
	Definition: A list of integer tuples which each define the CPR
		    temperature adjustment feature enable state for each voltage
		    corner in order from lowest to highest. Each element in the
		    tuple should be either 0 (temperature adjustment not
		    allowed) or 1 (temperature adjustment allowed).

		    The list must contain qcom,cpr-fuse-combos number of tuples
		    in which case the tuples are matched to fuse combinations
		    1-to-1 or qcom,cpr-speed-bins number of tuples in which case
		    the tuples are matched to speed bins 1-to-1 or exactly 1
		    tuple which is used regardless of the fuse combination and
		    speed bin found on a given chip.

		    Each tuple must be of the length defined in the
		    corresponding element of the qcom,cpr-corners property or
		    the qcom,cpr-speed-bins property.  A single tuple may only
		    be specified if all of the corner counts in qcom,cpr-corners
		    are the same.

- qcom,cpr-cornerX-temp-core-voltage-adjustment
	Usage:      required if qcom,corner-allow-core-count-adjustment
		    specified for this CPR3 regulator.
	Value type: <prop-encoded-array>
	Definition: A grouping of integer tuple lists for cornerX. The possible
		    values for X are 1 to the max value specified in
		    qcom,cpr-corners. Each tuple defines the temperature based
		    voltage adjustment in microvolts for each temperature band
		    from lowest to highest for a given number of online cores.
		    Each tuple must have a number of elements equal to either
		    (the number of elements in qcom,cpr-ctrl-temp-point-map
		    + 1), if qcom,cpr-ctrl-temp-point-map is specified, or 1.

		    Each tuple list must contain a number of tuples equal to
		    either qcom,max-core-count, if qcom,max-core-count is
		    specified, or 1. The tuples should be ordered from lowest
		    to highest core count.

		    The tuple list grouping must contain qcom,cpr-fuse-combos
		    number of tuple lists in which case the lists are matched to
		    fuse combinations 1-to-1 or qcom,cpr-speed-bins number of
		    tuple lists in which case the lists are matched to
		    speed bins 1-to-1 or exactly 1 list which is used regardless
		    of the fuse combination and speed bin found on a given chip.

=======
Example
=======
@@ -216,6 +326,11 @@ apc_cpr: cpr4-ctrl@b018000 {
	qcom,cpr-enable;
	qcom,cpr-hw-closed-loop;

	qcom,cpr-temp-point-map = <0 250 850>;
	qcom,cpr-initial-temp-band = <3>;
	qcom,cpr-step-quot-fixed = <16>;
	qcom,cpr-voltage-settling-time = <1600>;

	thread@0 {
		qcom,cpr-thread-id = <0>;
		qcom,cpr-consecutive-up = <1>;
@@ -253,6 +368,58 @@ apc_cpr: cpr4-ctrl@b018000 {
			qcom,allow-voltage-interpolation;
			qcom,allow-quotient-interpolation;
			qcom,cpr-scaled-open-loop-voltage-as-ceiling;

			qcom,corner-allow-temp-adjustment =
					<0 0 0 1 0 1 1 1>;

			qcom,corner-allow-core-count-adjustment =
					<0 0 0 0 1 1 1 1>;
			qcom,max-core-count = <8>;
			qcom,cpr-corner4-temp-core-voltage-adjustment =
				<(-20000) (-10000) (-5000) 0>,
				<(-20000) (-10000) (-5000) 0>,
				<(-20000) (-10000) (-5000) 0>,
				<(-20000) (-10000) (-5000) 0>,
				<(-20000) (-10000) (-5000) 0>,
				<(-20000) (-10000) (-5000) 0>,
				<(-20000) (-10000) (-5000) 0>,
				<(-20000) (-10000) (-5000) 0>;
			qcom,cpr-corner5-temp-core-voltage-adjustment =
				<(-50000) (-50000) (-50000) (-50000)>,
				<(-50000) (-50000) (-50000) (-50000)>,
				<(-40000) (-40000) (-40000) (-40000)>,
				<(-40000) (-40000) (-40000) (-40000)>,
				<(-30000) (-30000) (-30000) (-30000)>,
				<(-30000) (-30000) (-30000) (-30000)>,
				<(-20000) (-20000) (-20000) (-20000)>,
				<(-20000) (-20000) (-20000) (-20000)>;
			qcom,cpr-corner6-temp-core-voltage-adjustment =
				<(-50000) (-40000) (-30000) (-20000)>,
				<(-50000) (-40000) (-30000) (-20000)>,
				<(-40000) (-30000) (-20000) (-10000)>,
				<(-40000) (-30000) (-20000) (-10000)>,
				<(-30000) (-20000) (-10000)  (-5000)>,
				<(-30000) (-20000) (-10000)  (-5000)>,
				<(-20000) (-10000)  (-5000)       0 >,
				<(-20000) (-10000)  (-5000)       0 >;
			qcom,cpr-corner7-temp-core-voltage-adjustment =
				<(-50000) (-40000) (-30000) (-20000)>,
				<(-50000) (-40000) (-30000) (-20000)>,
				<(-40000) (-30000) (-20000) (-10000)>,
				<(-40000) (-30000) (-20000) (-10000)>,
				<(-30000) (-20000) (-10000)  (-5000)>,
				<(-30000) (-20000) (-10000)  (-5000)>,
				<(-20000) (-10000)  (-5000)       0 >,
				<(-20000) (-10000)  (-5000)       0 >;
			qcom,cpr-corner8-temp-core-voltage-adjustment =
				<(-50000) (-40000) (-30000) (-20000)>,
				<(-50000) (-40000) (-30000) (-20000)>,
				<(-40000) (-30000) (-20000) (-10000)>,
				<(-40000) (-30000) (-20000) (-10000)>,
				<(-30000) (-20000) (-10000)  (-5000)>,
				<(-30000) (-20000) (-10000)  (-5000)>,
				<(-20000) (-10000)  (-5000)       0 >,
				<(-20000) (-10000)  (-5000)       0 >;
		};
	};
};
+684 −22

File changed.

Preview size limit exceeded, changes collapsed.

+86 −1
Original line number Diff line number Diff line
/*
 * Copyright (c) 2015, The Linux Foundation. All rights reserved.
 * Copyright (c) 2015-2016, 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
@@ -50,6 +50,37 @@ struct cpr3_fuse_param {
/* This constant is used when allocating array printing buffers. */
#define MAX_CHARS_PER_INT	10

/**
 * struct cpr4_sdelta - CPR4 controller specific data structure for the sdelta
 *			adjustment table which is used to adjust the VDD supply
 *			voltage automatically based upon the temperature and/or
 *			the number of online CPU cores.
 * @allow_core_count_adj: Core count adjustments are allowed.
 * @allow_temp_adj:	Temperature based adjustments are allowed.
 * @max_core_count:	Maximum number of cores considered for core count
 *			adjustment logic.
 * @temp_band_count:	Number of temperature bands considered for temperature
 *			based adjustment logic.
 * @cap_volt:		CAP in uV to apply to SDELTA margins with multiple
 *			cpr3-regulators defined for single controller.
 * @table:		SDELTA table with per-online-core and temperature based
 *			adjustments of size (max_core_count * temp_band_count)
 *			Outer: core count
 *			Inner: temperature band
 *			Each element has units of VDD supply steps. Positive
 *			values correspond to a reduction in voltage and negative
 *			value correspond to an increase (this follows the SDELTA
 *			register semantics).
 */
struct cpr4_sdelta {
	bool	allow_core_count_adj;
	bool	allow_temp_adj;
	int	max_core_count;
	int	temp_band_count;
	int	cap_volt;
	int	*table;
};

/**
 * struct cpr3_corner - CPR3 virtual voltage corner data structure
 * @floor_volt:		CPR closed-loop floor voltage in microvolts
@@ -83,6 +114,8 @@ struct cpr3_fuse_param {
 *			E.g. a value of 900 would imply that the adjustment for
 *			this corner should be 90% (900/1000) of that for the
 *			reference corner.
 * @sdelta:		The CPR4 controller specific data for this corner. This
 *			field is applicable for CPR4 controllers.
 *
 * The value of last_volt is initialized inside of the cpr3_regulator_register()
 * call with the open_loop_volt value.  It can later be updated to the settled
@@ -105,6 +138,7 @@ struct cpr3_corner {
	u32			ro_mask;
	u32			irq_en;
	int			aging_derate;
	struct cpr4_sdelta	*sdelta;
};

/**
@@ -194,6 +228,11 @@ struct cpr3_corner {
 *			may be added to the target quotients of this regulator.
 *			A value of 0 may be specified if this regulator does not
 *			require any aging adjustment.
 * @allow_core_count_adj: Core count adjustments are allowed for this regulator.
 * @allow_temp_adj:	Temperature based adjustments are allowed for this
 *			regulator.
 * @max_core_count:	Maximum number of cores considered for core count
 *			adjustment logic.
 *
 * This structure contains both configuration and runtime state data.  The
 * elements current_corner, last_closed_loop_corner, aggregated, debug_corner,
@@ -242,6 +281,10 @@ struct cpr3_regulator {
	bool			aging_allowed;
	int			aging_corner;
	int			aging_max_adjust_volt;

	bool			allow_core_count_adj;
	bool			allow_temp_adj;
	int			max_core_count;
};

/**
@@ -488,6 +531,33 @@ struct cpr3_aging_sensor_info {
 * @aging_sensor:	Array of CPR3 aging sensors which are used to perform
 *			aging measurements at a runtime.
 * @aging_sensor_count:	Number of elements in the aging_sensor array
 * @step_quot_fixed:	Fixed step quotient value used for target quotient
 *			adjustment if use_dynamic_step_quot is not set.
 *			This parameter is only relevant for CPR4 controllers
 *			when using the per-online-core or per-temperature
 *			adjustments.
 * @initial_temp_band:	Temperature band used for calculation of base-line
 *			target quotients (fused).
 * @use_dynamic_step_quot: Boolean value which indicates that margin adjustment
 *			of target quotient will be based on the step quotient
 *			calculated dynamically in hardware for each RO.
 * @allow_core_count_adj: Core count adjustments are allowed for this controller
 * @allow_temp_adj:	Temperature based adjustments are allowed for
 *			this controller
 * @temp_band_count:	Number of temperature bands used for temperature based
 *			adjustment logic
 * @temp_points:	Array of temperature points in decidegrees Celsius used
 *			to specify the ranges for selected temperature bands.
 *			The array must have (temp_band_count - 1) elements
 *			allocated.
 * @temp_sensor_id_start: Start ID of temperature sensors used for temperature
 *			based adjustments.
 * @temp_sensor_id_end:	End ID of temperature sensors used for temperature
 *			based adjustments.
 * @voltage_settling_time: The time in nanoseconds that it takes for the
 *			VDD supply voltage to settle after being increased or
 *			decreased by step_volt microvolts which is used when
 *			SDELTA voltage margin adjustments are applied.
 *
 * This structure contains both configuration and runtime state data.  The
 * elements cpr_allowed_sw, use_hw_closed_loop, aggr_corner, cpr_enabled,
@@ -496,6 +566,10 @@ struct cpr3_aging_sensor_info {
 *
 * The apm* elements do not need to be initialized if the VDD supply managed by
 * the CPR3 controller does not utilize an APM.
 *
 * The elements step_quot_fixed, initial_temp_band, allow_core_count_adj,
 * allow_temp_adj and temp* need to be initialized for CPR4 controllers which
 * are using per-online-core or per-temperature adjustments.
 */
struct cpr3_controller {
	struct device		*dev;
@@ -560,6 +634,17 @@ struct cpr3_controller {
	bool			aging_failed;
	struct cpr3_aging_sensor_info *aging_sensor;
	int			aging_sensor_count;

	u32			step_quot_fixed;
	u32			initial_temp_band;
	bool			use_dynamic_step_quot;
	bool			allow_core_count_adj;
	bool			allow_temp_adj;
	int			temp_band_count;
	int			*temp_points;
	u32			temp_sensor_id_start;
	u32			temp_sensor_id_end;
	u32			voltage_settling_time;
};

/* Used for rounding voltages to the closest physically available set point. */
+279 −1
Original line number Diff line number Diff line
@@ -159,6 +159,10 @@ static const int msmtitanium_apss_fuse_ref_volt

#define MSMTITANIUM_APSS_CPR_CLOCK_RATE		19200000

#define MSMTITANIUM_APSS_MAX_TEMP_POINTS	3
#define MSMTITANIUM_APSS_TEMP_SENSOR_ID_START	4
#define MSMTITANIUM_APSS_TEMP_SENSOR_ID_END	13

/**
 * cpr4_msmtitanium_apss_read_fuse_data() - load APSS specific fuse parameter values
 * @vreg:		Pointer to the CPR3 regulator
@@ -691,6 +695,255 @@ static int cpr4_apss_init_thread(struct cpr3_thread *thread)
	return 0;
}

/**
 * cpr4_apss_parse_temp_adj_properties() - parse temperature based
 *		adjustment properties from device tree.
 * @ctrl:	Pointer to the CPR3 controller
 *
 * Return: 0 on success, errno on failure
 */
static int cpr4_apss_parse_temp_adj_properties(struct cpr3_controller *ctrl)
{
	struct device_node *of_node = ctrl->dev->of_node;
	int rc, i, len, temp_point_count;

	if (!of_find_property(of_node, "qcom,cpr-temp-point-map", &len)) {
		/*
		 * Temperature based adjustments are not defined. Single
		 * temperature band is still valid for per-online-core
		 * adjustments.
		 */
		ctrl->temp_band_count = 1;
		return 0;
	}

	temp_point_count = len / sizeof(u32);
	if (temp_point_count <= 0
		|| temp_point_count > MSMTITANIUM_APSS_MAX_TEMP_POINTS) {
		cpr3_err(ctrl, "invalid number of temperature points %d > %d (max)\n",
			 temp_point_count, MSMTITANIUM_APSS_MAX_TEMP_POINTS);
		return -EINVAL;
	}

	ctrl->temp_points = devm_kcalloc(ctrl->dev, temp_point_count,
					sizeof(*ctrl->temp_points), GFP_KERNEL);
	if (!ctrl->temp_points)
		return -ENOMEM;

	rc = of_property_read_u32_array(of_node, "qcom,cpr-temp-point-map",
					ctrl->temp_points, temp_point_count);
	if (rc) {
		cpr3_err(ctrl, "error reading property qcom,cpr-temp-point-map, rc=%d\n",
			 rc);
		return rc;
	}

	for (i = 0; i < temp_point_count; i++)
		cpr3_debug(ctrl, "Temperature Point %d=%d\n", i,
				   ctrl->temp_points[i]);

	/*
	 * If t1, t2, and t3 are the temperature points, then the temperature
	 * bands are: (-inf, t1], (t1, t2], (t2, t3], and (t3, inf).
	 */
	ctrl->temp_band_count = temp_point_count + 1;
	cpr3_debug(ctrl, "Number of temp bands =%d\n", ctrl->temp_band_count);

	rc = of_property_read_u32(of_node, "qcom,cpr-initial-temp-band",
				  &ctrl->initial_temp_band);
	if (rc) {
		cpr3_err(ctrl, "error reading qcom,cpr-initial-temp-band, rc=%d\n",
			rc);
		return rc;
	}

	if (ctrl->initial_temp_band >= ctrl->temp_band_count) {
		cpr3_err(ctrl, "Initial temperature band value %d should be in range [0 - %d]\n",
			ctrl->initial_temp_band, ctrl->temp_band_count - 1);
		return -EINVAL;
	}

	ctrl->temp_sensor_id_start = MSMTITANIUM_APSS_TEMP_SENSOR_ID_START;
	ctrl->temp_sensor_id_end = MSMTITANIUM_APSS_TEMP_SENSOR_ID_END;
	ctrl->allow_temp_adj = true;
	return rc;
}

/**
 * cpr4_load_corner_core_and_temp_adj() - parse amount of voltage adjustment for
 *		per-online-core and per-temperature voltage adjustment for a
 *		given corner from device tree.
 * @vreg:	Pointer to the CPR3 regulator
 *
 * Return: 0 on success, errno on failure
 */
static int cpr4_load_corner_core_and_temp_adj(struct cpr3_regulator *vreg,
					int corner_num)
{
	struct cpr3_controller *ctrl = vreg->thread->ctrl;
	struct cpr3_corner *corner = &vreg->corner[corner_num];
	struct cpr4_sdelta *sdelta = corner->sdelta;
	int sdelta_size, i, rc = 0;
	char prop_str[60];

	if (!sdelta->allow_core_count_adj && !sdelta->allow_temp_adj) {
		/* corner doesn't need sdelta table */
		sdelta->max_core_count = 0;
		sdelta->temp_band_count = 0;
		return rc;
	}

	sdelta_size = sdelta->max_core_count * sdelta->temp_band_count;
	cpr3_debug(vreg, "corner=%d core_config_count=%d temp_band_count=%d sdelta_size=%d\n",
			corner_num, sdelta->max_core_count,
			sdelta->temp_band_count, sdelta_size);

	sdelta->table = devm_kcalloc(ctrl->dev, sdelta_size,
				sizeof(*sdelta->table), GFP_KERNEL);
	if (!sdelta->table)
		return -ENOMEM;

	snprintf(prop_str, sizeof(prop_str),
		"qcom,cpr-corner%d-temp-core-voltage-adjustment",
		corner_num + CPR3_CORNER_OFFSET);

	rc = cpr3_parse_array_property(vreg, prop_str, sdelta_size,
				sdelta->table);
	if (rc) {
		cpr3_err(vreg, "could not load %s, rc=%d\n", prop_str, rc);
		return rc;
	}

	/*
	 * Convert sdelta margins from uV to PMIC steps and apply negation to
	 * follow the SDELTA register semantics.
	 */
	for (i = 0; i < sdelta_size; i++)
		sdelta->table[i] = -(sdelta->table[i] / ctrl->step_volt);

	return rc;
}

/**
 * cpr4_apss_parse_core_count_temp_voltage_adj() - parse configuration data for
 *		per-online-core and per-temperature voltage adjustment for CPR3
 *		regulator from device tree.
 * @vreg:	Pointer to the CPR3 regulator
 *
 * Return: 0 on success, errno on failure
 */
static int cpr4_apss_parse_core_count_temp_voltage_adj(
			struct cpr3_regulator *vreg)
{
	struct cpr3_controller *ctrl = vreg->thread->ctrl;
	struct device_node *node = vreg->of_node;
	struct cpr3_corner *corner;
	struct cpr4_sdelta *sdelta;
	int i, rc = 0;
	int *allow_core_count_adj = NULL, *allow_temp_adj = NULL;

	if (of_find_property(node, "qcom,corner-allow-temp-adjustment", NULL)) {
		if (!ctrl->allow_temp_adj) {
			cpr3_err(ctrl, "Temperature adjustment configurations missing\n");
			return -EINVAL;
		}

		vreg->allow_temp_adj = true;
	}

	if (of_find_property(node, "qcom,corner-allow-core-count-adjustment",
				NULL)) {
		rc = of_property_read_u32(node, "qcom,max-core-count",
				&vreg->max_core_count);
		if (rc) {
			cpr3_err(vreg, "error reading qcom,max-num-cpus, rc=%d\n",
				rc);
			return -EINVAL;
		}

		vreg->allow_core_count_adj = true;
		ctrl->allow_core_count_adj = true;
	}

	if (!vreg->allow_temp_adj && !vreg->allow_core_count_adj) {
		/*
		 * Both per-online-core and temperature based adjustments are
		 * disabled for this regualtor.
		 */
		return 0;
	} else if (!vreg->allow_core_count_adj) {
		/*
		 * Only per-temperature voltage adjusments are allowed.
		 * Keep max core count value as 1 to allocate SDELTA.
		 */
		vreg->max_core_count = 1;
	}

	if (vreg->allow_core_count_adj) {
		allow_core_count_adj = kcalloc(vreg->corner_count,
					sizeof(*allow_core_count_adj),
					GFP_KERNEL);
		if (!allow_core_count_adj)
			return -ENOMEM;

		rc = cpr3_parse_corner_array_property(vreg,
				"qcom,corner-allow-core-count-adjustment",
				1, allow_core_count_adj);
		if (rc) {
			cpr3_err(vreg, "qcom,corner-allow-core-count-adjustment reading failed, rc=%d\n",
				rc);
			goto done;
		}
	}

	if (vreg->allow_temp_adj) {
		allow_temp_adj = kcalloc(vreg->corner_count,
					sizeof(*allow_temp_adj), GFP_KERNEL);
		if (!allow_temp_adj) {
			rc = -ENOMEM;
			goto done;
		}

		rc = cpr3_parse_corner_array_property(vreg,
					"qcom,corner-allow-temp-adjustment",
					1, allow_temp_adj);
		if (rc) {
			cpr3_err(vreg, "qcom,corner-allow-temp-adjustment reading failed, rc=%d\n",
				rc);
			goto done;
		}
	}

	for (i = 0; i < vreg->corner_count; i++) {
		corner = &vreg->corner[i];
		sdelta = devm_kzalloc(ctrl->dev, sizeof(*corner->sdelta),
					GFP_KERNEL);
		if (!sdelta) {
			rc = -ENOMEM;
			goto done;
		}

		if (allow_core_count_adj)
			sdelta->allow_core_count_adj = allow_core_count_adj[i];
		if (allow_temp_adj)
			sdelta->allow_temp_adj = allow_temp_adj[i];
		sdelta->max_core_count = vreg->max_core_count;
		sdelta->temp_band_count = ctrl->temp_band_count;
		corner->sdelta = sdelta;
		rc = cpr4_load_corner_core_and_temp_adj(vreg, i);
		if (rc) {
			cpr3_err(vreg, "corner %d core and temp adjustment loading failed: rc=%d\n",
				i, rc);
			goto done;
		}
	}

done:
	kfree(allow_core_count_adj);
	kfree(allow_temp_adj);
	return rc;
}

/**
 * cpr4_apss_init_regulator() - perform all steps necessary to initialize the
 *		configuration data for a CPR3 regulator
@@ -755,9 +1008,15 @@ static int cpr4_apss_init_regulator(struct cpr3_regulator *vreg)
		return rc;
	}

	rc = cpr4_apss_parse_core_count_temp_voltage_adj(vreg);
	if (rc) {
		cpr3_err(vreg, "unable to parse temperature and core count voltage adjustments, rc=%d\n",
			 rc);
		return rc;
	}
	cpr4_apss_print_settings(vreg);

	return 0;
	return rc;
}

/**
@@ -797,9 +1056,21 @@ static int cpr4_apss_init_controller(struct cpr3_controller *ctrl)
		return rc;
	}

	/*
	 * Use fixed step quotient if specified otherwise use dynamic
	 * calculated per RO step quotient
	 */
	of_property_read_u32(ctrl->dev->of_node, "qcom,cpr-step-quot-fixed",
			&ctrl->step_quot_fixed);
	ctrl->use_dynamic_step_quot = ctrl->step_quot_fixed ? false : true;

	ctrl->saw_use_unit_mV = of_property_read_bool(ctrl->dev->of_node,
					"qcom,cpr-saw-use-unit-mV");

	of_property_read_u32(ctrl->dev->of_node,
			"qcom,cpr-voltage-settling-time",
			&ctrl->voltage_settling_time);

	ctrl->vdd_limit_regulator = devm_regulator_get(ctrl->dev, "vdd-limit");
	if (IS_ERR(ctrl->vdd_limit_regulator)) {
		rc = PTR_ERR(ctrl->vdd_limit_regulator);
@@ -817,6 +1088,13 @@ static int cpr4_apss_init_controller(struct cpr3_controller *ctrl)
		return rc;
	}

	rc = cpr4_apss_parse_temp_adj_properties(ctrl);
	if (rc) {
		cpr3_err(ctrl, "unable to parse temperature adjustment properties, rc=%d\n",
			 rc);
		return rc;
	}

	ctrl->sensor_count = MSMTITANIUM_APSS_CPR_SENSOR_COUNT;

	/*