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

Commit 92a67aec authored by Tirupathi Reddy's avatar Tirupathi Reddy
Browse files

regulator: cpr3: Add voltage boost support for CPR4 controllers



Voltage boost is a CPR4 hardware feature which raises the VDD supply
voltage when the number of active cores reaches a certain threshold.
It then reduces the voltage back down when the active core count
condition is no longer met. Add support to enable and configure this
feature.

Change-Id: Iccfc2ddddb6621a150235cb2c46adfd1b884dbc2
Signed-off-by: default avatarTirupathi Reddy <tirupath@codeaurora.org>
parent 8562c3ab
Loading
Loading
Loading
Loading
+64 −0
Original line number Diff line number Diff line
@@ -291,6 +291,63 @@ APSS specific properties:
		    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.

- qcom,cpr-num-boost-cores
	Usage:      required if qcom,allow-boost specified for this CPR3
		    regulator.
	Value type: <u32>
	Definition: Integer value indicates that voltage boost will be applied
		    when the number of online cores become this value.

- qcom,cpr-boost-temp-adjustment
	Usage:      optional
	Value type: <prop-encoded-array>
	Definition: A list of integer tuples which each define the temperature
		    based voltage adjustment to boost voltage in microvolts
		    for each temperature band in order from lowest to highest.

		    The number of elements in each tuple should be 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.

		    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.

- qcom,allow-boost
	Usage:      optional
	Value type: <prop-encoded-array>
	Definition: A list of integers which specifies if the voltage boost
		    feature should be enabled for each fuse combination.
		    Supported per-combo element values:
			0 - voltage boost feature disabled
			1 - voltage boost feature enabled

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

- qcom,cpr-boost-voltage-fuse-adjustment
	Usage:      optional
	Value type: <u32>
	Definition: A list of integers which defines the voltage adjustment
		    in microvolts for the fused boost voltage. This adjustment
		    is applied to the values read from boost fuses.

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

=======
Example
=======
@@ -420,6 +477,13 @@ apc_cpr: cpr4-ctrl@b018000 {
				<(-30000) (-20000) (-10000)  (-5000)>,
				<(-20000) (-10000)  (-5000)       0 >,
				<(-20000) (-10000)  (-5000)       0 >;

			qcom,cpr-num-boost-cores = <4>;
			qcom,cpr-boost-voltage-fuse-adjustment = <(-10000)>;
			qcom,cpr-boost-temp-adjustment =
				<(-20000) (-15000) (-10000) 0>;
			qcom,allow-boost =
				<1>;
		};
	};
};
+153 −34
Original line number Diff line number Diff line
@@ -205,6 +205,7 @@
#define CPR4_MARGIN_TEMP_POINT2_SHIFT		0

#define CPR4_REG_MARGIN_ADJ_CTL					0x7F8
#define CPR4_MARGIN_ADJ_CTL_BOOST_EN				BIT(0)
#define CPR4_MARGIN_ADJ_CTL_CORE_ADJ_EN				BIT(1)
#define CPR4_MARGIN_ADJ_CTL_TEMP_ADJ_EN				BIT(2)
#define CPR4_MARGIN_ADJ_CTL_TIMER_SETTLE_VOLTAGE_EN		BIT(3)
@@ -335,7 +336,8 @@ static inline void cpr3_masked_write(struct cpr3_controller *ctrl, u32 offset,
 */
static inline void cpr3_ctrl_loop_enable(struct cpr3_controller *ctrl)
{
	if (ctrl->cpr_enabled)
	if (ctrl->cpr_enabled && !(ctrl->aggr_corner.sdelta
		&& ctrl->aggr_corner.sdelta->allow_boost))
		cpr3_masked_write(ctrl, CPR3_REG_CPR_CTL,
			CPR3_CPR_CTL_LOOP_EN_MASK, CPR3_CPR_CTL_LOOP_ENABLE);
}
@@ -417,7 +419,7 @@ static inline int cpr3_ctrl_clear_cpr4_config(struct cpr3_controller *ctrl)
	int i, rc = 0;

	if (!aggr_sdelta || !(aggr_sdelta->allow_core_count_adj
		|| aggr_sdelta->allow_temp_adj))
		|| aggr_sdelta->allow_temp_adj || aggr_sdelta->allow_boost))
		/* cpr4 features are not enabled */
		return 0;

@@ -439,13 +441,15 @@ static inline int cpr3_ctrl_clear_cpr4_config(struct cpr3_controller *ctrl)
		CPR4_MARGIN_ADJ_CTL_MAX_NUM_CORES_MASK
		| CPR4_MARGIN_ADJ_CTL_CORE_ADJ_EN
		| CPR4_MARGIN_ADJ_CTL_TEMP_ADJ_EN
		| CPR4_MARGIN_ADJ_CTL_KV_MARGIN_ADJ_EN, 0);
		| CPR4_MARGIN_ADJ_CTL_KV_MARGIN_ADJ_EN
		| CPR4_MARGIN_ADJ_CTL_BOOST_EN
		| CPR4_MARGIN_ADJ_CTL_HW_CLOSED_LOOP_EN_MASK, 0);

	cpr3_masked_write(ctrl, CPR4_REG_MISC,
			CPR4_MISC_MARGIN_TABLE_ROW_SELECT_MASK,
			0 << CPR4_MISC_MARGIN_TABLE_ROW_SELECT_SHIFT);

	for (i = 0; i < aggr_sdelta->max_core_count; i++) {
	for (i = 0; i <= aggr_sdelta->max_core_count; i++) {
		/* Clear voltage margin adjustments programmed in TEMP_COREi */
		cpr3_write(ctrl, CPR4_REG_MARGIN_TEMP_CORE(i), 0);
	}
@@ -689,7 +693,8 @@ static int cpr3_regulator_init_cpr4(struct cpr3_controller *ctrl)
		break;
	}

	if (!ctrl->allow_core_count_adj && !ctrl->allow_temp_adj) {
	if (!ctrl->allow_core_count_adj && !ctrl->allow_temp_adj
		&& !ctrl->allow_boost) {
		/*
		 * Skip below configuration as none of the features
		 * are enabled.
@@ -758,7 +763,8 @@ static int cpr3_regulator_init_cpr4(struct cpr3_controller *ctrl)
			thread_max_core_count = max(thread_max_core_count,
							vreg->max_core_count);
			thread_valid_sdelta |= (vreg->allow_core_count_adj
							| vreg->allow_temp_adj);
							| vreg->allow_temp_adj
							| vreg->allow_boost);
		}
		if (thread_valid_sdelta) {
			sdelta = devm_kzalloc(ctrl->dev, sizeof(*sdelta),
@@ -774,6 +780,13 @@ static int cpr3_regulator_init_cpr4(struct cpr3_controller *ctrl)
			if (!sdelta->table)
				return -ENOMEM;

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

			thread->aggr_corner.sdelta = sdelta;
		}

@@ -793,6 +806,13 @@ static int cpr3_regulator_init_cpr4(struct cpr3_controller *ctrl)
		if (!sdelta->table)
			return -ENOMEM;

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

		ctrl->aggr_corner.sdelta = sdelta;
	}

@@ -852,10 +872,18 @@ static int cpr3_controller_program_sdelta(struct cpr3_controller *ctrl)
		/* cpr4_sdelta not defined for current aggregated corner */
		return 0;

	if (!sdelta->allow_core_count_adj && !sdelta->allow_temp_adj) {
	if (ctrl->supports_hw_closed_loop && ctrl->cpr_enabled) {
		cpr3_masked_write(ctrl, CPR4_REG_MARGIN_ADJ_CTL,
			CPR4_MARGIN_ADJ_CTL_HW_CLOSED_LOOP_EN_MASK,
			(ctrl->use_hw_closed_loop && !sdelta->allow_boost)
			? CPR4_MARGIN_ADJ_CTL_HW_CLOSED_LOOP_ENABLE : 0);
	}

	if (!sdelta->allow_core_count_adj && !sdelta->allow_temp_adj
		&& !sdelta->allow_boost) {
		/*
		 * Both per-online-core and per-temperature adjustments
		 * are disabled for this aggregation corner
		 * Per-online-core, per-temperature and voltage boost
		 * adjustments are disabled for this aggregation corner.
		 */
		return 0;
	}
@@ -872,25 +900,37 @@ static int cpr3_controller_program_sdelta(struct cpr3_controller *ctrl)

	max_core_count = sdelta->max_core_count;

	if (sdelta->allow_core_count_adj || sdelta->allow_temp_adj) {
		if (sdelta->allow_core_count_adj) {
			/* Program TEMP_CORE0 to same margins as TEMP_CORE1 */
			cpr3_write_temp_core_margin(ctrl,
			CPR4_REG_MARGIN_TEMP_CORE(0), &sdelta->table[0]);
				CPR4_REG_MARGIN_TEMP_CORE(0),
				&sdelta->table[0]);
		}

		for (i = 0; i < max_core_count; i++) {
			index = i * sdelta->temp_band_count;
			/*
		 * Program TEMP_COREi with voltage margin adjustments that need
		 * to be applied when the number of cores becomes i.
			 * Program TEMP_COREi with voltage margin adjustments
			 * that need to be applied when the number of cores
			 * becomes i.
			 */
			cpr3_write_temp_core_margin(ctrl,
			CPR4_REG_MARGIN_TEMP_CORE(sdelta->allow_core_count_adj
				CPR4_REG_MARGIN_TEMP_CORE(
						sdelta->allow_core_count_adj
						? i + 1 : max_core_count),
						&sdelta->table[index]);
		}
	}

	if (!sdelta->allow_core_count_adj) {
	if (sdelta->allow_boost) {
		/* Program only boost_num_cores row of SDELTA */
		cpr3_write_temp_core_margin(ctrl,
			CPR4_REG_MARGIN_TEMP_CORE(sdelta->boost_num_cores),
					&sdelta->boost_table[0]);
	}

	if (!sdelta->allow_core_count_adj && !sdelta->allow_boost) {
		cpr3_masked_write(ctrl, CPR4_REG_MISC,
			CPR4_MISC_MARGIN_TABLE_ROW_SELECT_MASK,
			max_core_count
@@ -901,13 +941,16 @@ static int cpr3_controller_program_sdelta(struct cpr3_controller *ctrl)
		CPR4_MARGIN_ADJ_CTL_MAX_NUM_CORES_MASK
		| CPR4_MARGIN_ADJ_CTL_CORE_ADJ_EN
		| CPR4_MARGIN_ADJ_CTL_TEMP_ADJ_EN
		| CPR4_MARGIN_ADJ_CTL_KV_MARGIN_ADJ_EN,
		| CPR4_MARGIN_ADJ_CTL_KV_MARGIN_ADJ_EN
		| CPR4_MARGIN_ADJ_CTL_BOOST_EN,
		max_core_count << CPR4_MARGIN_ADJ_CTL_MAX_NUM_CORES_SHIFT
		| (sdelta->allow_core_count_adj
		| ((sdelta->allow_core_count_adj || sdelta->allow_boost)
		? CPR4_MARGIN_ADJ_CTL_CORE_ADJ_EN : 0)
		| (sdelta->allow_temp_adj ? CPR4_MARGIN_ADJ_CTL_TEMP_ADJ_EN : 0)
		| (ctrl->use_hw_closed_loop
		? CPR4_MARGIN_ADJ_CTL_KV_MARGIN_ADJ_EN : 0));
		| ((ctrl->use_hw_closed_loop && !sdelta->allow_boost)
		? CPR4_MARGIN_ADJ_CTL_KV_MARGIN_ADJ_EN : 0)
		| (sdelta->allow_boost
		?  CPR4_MARGIN_ADJ_CTL_BOOST_EN : 0));

	/*
	 * Ensure that all previous CPR register writes have completed before
@@ -2219,7 +2262,7 @@ static void cpr3_regulator_aggregate_sdelta(
	struct cpr4_sdelta *aggr_sdelta, *sdelta;
	int aggr_core_count, core_count, temp_band_count;
	u32 aggr_index, index;
	int i, j, sdelta_size, cap_steps;
	int i, j, sdelta_size, cap_steps, adjust_sdelta;

	aggr_sdelta = aggr_corner->sdelta;
	sdelta = corner->sdelta;
@@ -2236,7 +2279,6 @@ static void cpr3_regulator_aggregate_sdelta(
		aggr_sdelta->cap_volt = min(aggr_sdelta->cap_volt,
						(corner->open_loop_volt -
						aggr_corner->open_loop_volt));
		aggr_corner->open_loop_volt = corner->open_loop_volt;

		/* Clear old data in the sdelta table */
		sdelta_size = aggr_sdelta->max_core_count
@@ -2256,6 +2298,23 @@ static void cpr3_regulator_aggregate_sdelta(
					sdelta_size * sizeof(*sdelta->table));
		}

		if (sdelta->allow_boost) {
			memcpy(aggr_sdelta->boost_table, sdelta->boost_table,
				sdelta->temp_band_count
				* sizeof(*sdelta->boost_table));
			aggr_sdelta->boost_num_cores = sdelta->boost_num_cores;
		} else if (aggr_sdelta->allow_boost) {
			for (i = 0; i < aggr_sdelta->temp_band_count; i++) {
				adjust_sdelta = (corner->open_loop_volt
						- aggr_corner->open_loop_volt)
						/ step_volt;
				aggr_sdelta->boost_table[i] += adjust_sdelta;
				aggr_sdelta->boost_table[i]
					= min(aggr_sdelta->boost_table[i], 0);
			}
		}

		aggr_corner->open_loop_volt = corner->open_loop_volt;
		aggr_sdelta->allow_temp_adj = sdelta->allow_temp_adj;
		aggr_sdelta->allow_core_count_adj
					= sdelta->allow_core_count_adj;
@@ -2269,6 +2328,19 @@ static void cpr3_regulator_aggregate_sdelta(
		aggr_sdelta->cap_volt = min(aggr_sdelta->cap_volt,
						(aggr_corner->open_loop_volt
						- corner->open_loop_volt));

		if (sdelta->allow_boost) {
			for (i = 0; i < aggr_sdelta->temp_band_count; i++) {
				adjust_sdelta = (aggr_corner->open_loop_volt
						- corner->open_loop_volt)
						/ step_volt;
				aggr_sdelta->boost_table[i] =
					sdelta->boost_table[i] + adjust_sdelta;
				aggr_sdelta->boost_table[i]
					= min(aggr_sdelta->boost_table[i], 0);
			}
			aggr_sdelta->boost_num_cores = sdelta->boost_num_cores;
		}
	} else {
		/*
		 * Found another dominant regulator with same open-loop
@@ -2280,6 +2352,7 @@ static void cpr3_regulator_aggregate_sdelta(
		 */
		aggr_sdelta->cap_volt = 0;
		aggr_sdelta->allow_core_count_adj = false;

		if (aggr_sdelta->allow_temp_adj
					&& sdelta->allow_temp_adj) {
			aggr_core_count = aggr_sdelta->max_core_count - 1;
@@ -2296,9 +2369,21 @@ static void cpr3_regulator_aggregate_sdelta(
		} else {
			aggr_sdelta->allow_temp_adj = false;
		}

		if (sdelta->allow_boost) {
			memcpy(aggr_sdelta->boost_table, sdelta->boost_table,
				sdelta->temp_band_count
				* sizeof(*sdelta->boost_table));
			aggr_sdelta->boost_num_cores = sdelta->boost_num_cores;
		}
	}

	if (aggr_sdelta->cap_volt && !aggr_sdelta->cap_volt == INT_MAX) {
	/* Keep non-dominant clients boost enable state */
	aggr_sdelta->allow_boost |= sdelta->allow_boost;
	if (aggr_sdelta->allow_boost)
		aggr_sdelta->allow_core_count_adj = false;

	if (aggr_sdelta->cap_volt && !(aggr_sdelta->cap_volt == INT_MAX)) {
		core_count = aggr_sdelta->max_core_count;
		temp_band_count = aggr_sdelta->temp_band_count;
		/*
@@ -2357,7 +2442,8 @@ static void cpr3_regulator_aggregate_corners(struct cpr3_corner *aggr_corner,
	}

	if (aggr_corner->sdelta && corner->sdelta
		&& aggr_corner->sdelta->table) {
		&& (aggr_corner->sdelta->table
		|| aggr_corner->sdelta->boost_table)) {
		cpr3_regulator_aggregate_sdelta(aggr_corner, corner, step_volt);
	} else {
		aggr_corner->open_loop_volt
@@ -2398,7 +2484,7 @@ static int _cpr3_regulator_update_ctrl_state(struct cpr3_controller *ctrl)
	bool thread_valid;
	int i, j, rc, new_volt, vdd_volt, dynamic_floor_volt, last_corner_volt;
	u32 reg_last_measurement = 0, sdelta_size;
	int *sdelta_table;
	int *sdelta_table, *boost_table;

	if (ctrl->ctrl_type == CPR_CTRL_TYPE_CPR4) {
		rc = cpr3_ctrl_clear_cpr4_config(ctrl);
@@ -2443,9 +2529,15 @@ static int _cpr3_regulator_update_ctrl_state(struct cpr3_controller *ctrl)
					* sizeof(*sdelta_table));
		}

		boost_table = sdelta->boost_table;
		if (boost_table)
			memset(boost_table, 0, sdelta->temp_band_count
					* sizeof(*boost_table));

		memset(sdelta, 0, sizeof(*sdelta));
		sdelta->table = sdelta_table;
		sdelta->cap_volt = INT_MAX;
		sdelta->boost_table = boost_table;
	}

	/* Aggregate the requests of all threads */
@@ -2463,9 +2555,15 @@ static int _cpr3_regulator_update_ctrl_state(struct cpr3_controller *ctrl)
						* sizeof(*sdelta_table));
			}

			boost_table = sdelta->boost_table;
			if (boost_table)
				memset(boost_table, 0, sdelta->temp_band_count
						* sizeof(*boost_table));

			memset(sdelta, 0, sizeof(*sdelta));
			sdelta->table = sdelta_table;
			sdelta->cap_volt = INT_MAX;
			sdelta->boost_table = boost_table;
		}

		memset(&thread->aggr_corner, 0, sizeof(thread->aggr_corner));
@@ -2689,7 +2787,8 @@ static int _cpr3_regulator_update_ctrl_state(struct cpr3_controller *ctrl)

	ctrl->aggr_corner = aggr_corner;

	if (ctrl->allow_core_count_adj || ctrl->allow_temp_adj) {
	if (ctrl->allow_core_count_adj || ctrl->allow_temp_adj
		|| ctrl->allow_boost) {
		rc = cpr3_controller_program_sdelta(ctrl);
		if (rc) {
			cpr3_err(ctrl, "failed to program sdelta, rc=%d\n", rc);
@@ -5136,7 +5235,9 @@ int cpr3_regulator_resume(struct cpr3_controller *ctrl)
 */
static int cpr3_regulator_validate_controller(struct cpr3_controller *ctrl)
{
	int i;
	struct cpr3_thread *thread;
	struct cpr3_regulator *vreg;
	int i, j, allow_boost_vreg_count = 0;

	if (!ctrl->vdd_regulator) {
		cpr3_err(ctrl, "vdd regulator missing\n");
@@ -5163,6 +5264,24 @@ static int cpr3_regulator_validate_controller(struct cpr3_controller *ctrl)
		}
	}

	for (i = 0; i < ctrl->thread_count; i++) {
		thread = &ctrl->thread[i];
		for (j = 0; j < thread->vreg_count; j++) {
			vreg = &thread->vreg[j];
			if (vreg->allow_boost)
				allow_boost_vreg_count++;
		}
	}

	if (allow_boost_vreg_count > 1) {
		/*
		 * Boost feature is not allowed to be used for more
		 * than one CPR3 regulator of a CPR3 controller.
		 */
		cpr3_err(ctrl, "Boost feature is enabled for more than one regulator\n");
		return -EINVAL;
	}

	return 0;
}

+15 −0
Original line number Diff line number Diff line
@@ -71,6 +71,14 @@ struct cpr3_fuse_param {
 *			values correspond to a reduction in voltage and negative
 *			value correspond to an increase (this follows the SDELTA
 *			register semantics).
 * @allow_boost:	Voltage boost allowed.
 * @boost_num_cores:	The number of online cores at which the boost voltage
 *			adjustments will be applied
 * @boost_table:	SDELTA table with boost voltage adjustments of size
 *			temp_band_count. 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;
@@ -79,6 +87,9 @@ struct cpr4_sdelta {
	int	temp_band_count;
	int	cap_volt;
	int	*table;
	bool	allow_boost;
	int	boost_num_cores;
	int	*boost_table;
};

/**
@@ -233,6 +244,7 @@ struct cpr3_corner {
 *			regulator.
 * @max_core_count:	Maximum number of cores considered for core count
 *			adjustment logic.
 * @allow_boost:	Voltage boost allowed for this regulator.
 *
 * This structure contains both configuration and runtime state data.  The
 * elements current_corner, last_closed_loop_corner, aggregated, debug_corner,
@@ -285,6 +297,7 @@ struct cpr3_regulator {
	bool			allow_core_count_adj;
	bool			allow_temp_adj;
	int			max_core_count;
	bool			allow_boost;
};

/**
@@ -544,6 +557,7 @@ struct cpr3_aging_sensor_info {
 * @allow_core_count_adj: Core count adjustments are allowed for this controller
 * @allow_temp_adj:	Temperature based adjustments are allowed for
 *			this controller
 * @allow_boost:	Voltage boost 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
@@ -640,6 +654,7 @@ struct cpr3_controller {
	bool			use_dynamic_step_quot;
	bool			allow_core_count_adj;
	bool			allow_temp_adj;
	bool			allow_boost;
	int			temp_band_count;
	int			*temp_points;
	u32			temp_sensor_id_start;
+217 −0
Original line number Diff line number Diff line
@@ -51,6 +51,9 @@
 * @speed_bin:		Application processor speed bin fuse parameter value for
 *			the given chip
 * @cpr_fusing_rev:	CPR fusing revision fuse parameter value
 * @boost_cfg:		CPR boost configuration fuse parameter value
 * @boost_voltage:	CPR boost voltage fuse parameter value (raw, not
 *			converted to a voltage)
 *
 * This struct holds the values for all of the fuses read from memory.
 */
@@ -61,6 +64,8 @@ struct cpr4_msmtitanium_apss_fuses {
	u64	quot_offset[MSMTITANIUM_APSS_FUSE_CORNERS];
	u64	speed_bin;
	u64	cpr_fusing_rev;
	u64	boost_cfg;
	u64	boost_voltage;
};

/*
@@ -140,6 +145,16 @@ static const struct cpr3_fuse_param msmtitanium_apss_speed_bin_param[] = {
	{},
};

static const struct cpr3_fuse_param msmtitanium_cpr_boost_fuse_cfg_param[] = {
	{36, 43, 45},
	{},
};

static const struct cpr3_fuse_param msmtitanium_apss_boost_fuse_volt_param[] = {
	{71, 0, 5},
	{},
};

/*
 * Open loop voltage fuse reference voltages in microvolts for MSMTITANIUM
 */
@@ -162,6 +177,22 @@ static const int msmtitanium_apss_fuse_ref_volt
#define MSMTITANIUM_APSS_MAX_TEMP_POINTS	3
#define MSMTITANIUM_APSS_TEMP_SENSOR_ID_START	4
#define MSMTITANIUM_APSS_TEMP_SENSOR_ID_END	13
/*
 * Boost voltage fuse reference and ceiling voltages in microvolts for
 * MSMTITANIUM.
 */
#define MSMTITANIUM_APSS_BOOST_FUSE_REF_VOLT	1140000
#define MSMTITANIUM_APSS_BOOST_CEILING_VOLT	1140000
#define MSMTITANIUM_APSS_BOOST_FLOOR_VOLT	900000
#define MAX_BOOST_CONFIG_FUSE_VALUE		8

#define MSMTITANIUM_APSS_CPR_SDELTA_CORE_COUNT	15

/*
 * Array of integer values mapped to each of the boost config fuse values to
 * indicate boost enable/disable status.
 */
static bool boost_fuse[MAX_BOOST_CONFIG_FUSE_VALUE] = {0, 1, 1, 1, 1, 1, 1, 1};

/**
 * cpr4_msmtitanium_apss_read_fuse_data() - load APSS specific fuse parameter values
@@ -238,6 +269,26 @@ static int cpr4_msmtitanium_apss_read_fuse_data(struct cpr3_regulator *vreg)
		}
	}

	rc = cpr3_read_fuse_param(base, msmtitanium_cpr_boost_fuse_cfg_param,
				&fuse->boost_cfg);
	if (rc) {
		cpr3_err(vreg, "Unable to read CPR boost config fuse, rc=%d\n",
			rc);
		return rc;
	}
	cpr3_info(vreg, "Voltage boost fuse config = %llu boost = %s\n",
			fuse->boost_cfg, boost_fuse[fuse->boost_cfg]
			? "enable" : "disable");

	rc = cpr3_read_fuse_param(base,
				msmtitanium_apss_boost_fuse_volt_param,
				&fuse->boost_voltage);
	if (rc) {
		cpr3_err(vreg, "failed to read boost fuse voltage, rc=%d\n",
			rc);
		return rc;
	}

	vreg->fuse_combo = fuse->cpr_fusing_rev + 8 * fuse->speed_bin;
	if (vreg->fuse_combo >= CPR4_MSMTITANIUM_APSS_FUSE_COMBO_COUNT) {
		cpr3_err(vreg, "invalid CPR fuse combo = %d found\n",
@@ -861,6 +912,13 @@ static int cpr4_apss_parse_core_count_temp_voltage_adj(
			return -EINVAL;
		}

		if (vreg->max_core_count <= 0 || vreg->max_core_count
				> MSMTITANIUM_APSS_CPR_SDELTA_CORE_COUNT) {
			cpr3_err(vreg, "qcom,max-core-count has invalid value = %d\n",
				vreg->max_core_count);
			return -EINVAL;
		}

		vreg->allow_core_count_adj = true;
		ctrl->allow_core_count_adj = true;
	}
@@ -944,6 +1002,157 @@ done:
	return rc;
}

/**
 * cpr4_apss_parse_boost_properties() - parse configuration data for boost
 *		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_boost_properties(struct cpr3_regulator *vreg)
{
	struct cpr3_controller *ctrl = vreg->thread->ctrl;
	struct cpr4_msmtitanium_apss_fuses *fuse = vreg->platform_fuses;
	struct cpr3_corner *corner;
	int i, boost_voltage, final_boost_volt, rc = 0;
	int *boost_table = NULL, *boost_temp_adj = NULL;
	int boost_voltage_adjust = 0, boost_num_cores = 0;
	u32 boost_allowed = 0;

	if (!boost_fuse[fuse->boost_cfg])
		/* Voltage boost is disabled in fuse */
		return 0;

	if (of_find_property(vreg->of_node, "qcom,allow-boost", NULL)) {
		rc = cpr3_parse_array_property(vreg, "qcom,allow-boost", 1,
				&boost_allowed);
		if (rc)
			return rc;
	}

	if (!boost_allowed) {
		/* Voltage boost is not enabled for this regulator */
		return 0;
	}

	boost_voltage = cpr3_convert_open_loop_voltage_fuse(
				MSMTITANIUM_APSS_BOOST_FUSE_REF_VOLT,
				MSMTITANIUM_APSS_FUSE_STEP_VOLT,
				fuse->boost_voltage,
				MSMTITANIUM_APSS_VOLTAGE_FUSE_SIZE);

	/* Log boost voltage value for debugging purposes. */
	cpr3_info(vreg, "Boost open-loop=%7d uV\n", boost_voltage);

	if (of_find_property(vreg->of_node,
			"qcom,cpr-boost-voltage-fuse-adjustment", NULL)) {
		rc = cpr3_parse_array_property(vreg,
			"qcom,cpr-boost-voltage-fuse-adjustment",
			1, &boost_voltage_adjust);
		if (rc) {
			cpr3_err(vreg, "qcom,cpr-boost-voltage-fuse-adjustment reading failed, rc=%d\n",
				rc);
			return rc;
		}

		boost_voltage += boost_voltage_adjust;
		/* Log boost voltage value for debugging purposes. */
		cpr3_info(vreg, "Adjusted boost open-loop=%7d uV\n",
			boost_voltage);
	}

	/* Limit boost voltage value between ceiling and floor voltage limits */
	boost_voltage = min(boost_voltage, MSMTITANIUM_APSS_BOOST_CEILING_VOLT);
	boost_voltage = max(boost_voltage, MSMTITANIUM_APSS_BOOST_FLOOR_VOLT);

	/*
	 * The boost feature can only be used for the highest voltage corner.
	 * Also, keep core-count adjustments disabled when the boost feature
	 * is enabled.
	 */
	corner = &vreg->corner[vreg->corner_count - 1];
	if (!corner->sdelta) {
		/*
		 * If core-count/temp adjustments are not defined, the cpr4
		 * sdelta for this corner will not be allocated. Allocate it
		 * here for boost configuration.
		 */
		corner->sdelta = devm_kzalloc(ctrl->dev,
					sizeof(*corner->sdelta), GFP_KERNEL);
		if (!corner->sdelta)
			return -ENOMEM;
	}
	corner->sdelta->temp_band_count = ctrl->temp_band_count;

	rc = of_property_read_u32(vreg->of_node, "qcom,cpr-num-boost-cores",
				&boost_num_cores);
	if (rc) {
		cpr3_err(vreg, "qcom,cpr-num-boost-cores reading failed, rc=%d\n",
			rc);
		return rc;
	}

	if (boost_num_cores <= 0
		|| boost_num_cores > MSMTITANIUM_APSS_CPR_SDELTA_CORE_COUNT) {
		cpr3_err(vreg, "Invalid boost number of cores = %d\n",
			boost_num_cores);
		return -EINVAL;
	}
	corner->sdelta->boost_num_cores = boost_num_cores;

	boost_table = devm_kcalloc(ctrl->dev, corner->sdelta->temp_band_count,
					sizeof(*boost_table), GFP_KERNEL);
	if (!boost_table)
		return -ENOMEM;

	if (of_find_property(vreg->of_node,
				"qcom,cpr-boost-temp-adjustment", NULL)) {
		boost_temp_adj = kcalloc(corner->sdelta->temp_band_count,
					sizeof(*boost_temp_adj), GFP_KERNEL);
		if (!boost_temp_adj)
			return -ENOMEM;

		rc = cpr3_parse_array_property(vreg,
				"qcom,cpr-boost-temp-adjustment",
				corner->sdelta->temp_band_count,
				boost_temp_adj);
		if (rc) {
			cpr3_err(vreg, "qcom,cpr-boost-temp-adjustment reading failed, rc=%d\n",
				rc);
			goto done;
		}
	}

	for (i = 0; i < corner->sdelta->temp_band_count; i++) {
		/* Apply static adjustments to boost voltage */
		final_boost_volt = boost_voltage + (boost_temp_adj == NULL
						? 0 : boost_temp_adj[i]);
		/*
		 * Limit final adjusted boost voltage value between ceiling
		 * and floor voltage limits
		 */
		final_boost_volt = min(final_boost_volt,
					MSMTITANIUM_APSS_BOOST_CEILING_VOLT);
		final_boost_volt = max(final_boost_volt,
					MSMTITANIUM_APSS_BOOST_FLOOR_VOLT);

		boost_table[i] = (corner->open_loop_volt - final_boost_volt)
					/ ctrl->step_volt;
		cpr3_debug(vreg, "Adjusted boost voltage margin for temp band %d = %d steps\n",
			i, boost_table[i]);
	}

	corner->ceiling_volt = MSMTITANIUM_APSS_BOOST_CEILING_VOLT;
	corner->sdelta->boost_table = boost_table;
	corner->sdelta->allow_boost = true;
	corner->sdelta->allow_core_count_adj = false;
	vreg->allow_boost = true;
	ctrl->allow_boost = true;
done:
	kfree(boost_temp_adj);
	return rc;
}

/**
 * cpr4_apss_init_regulator() - perform all steps necessary to initialize the
 *		configuration data for a CPR3 regulator
@@ -1014,6 +1223,14 @@ static int cpr4_apss_init_regulator(struct cpr3_regulator *vreg)
			 rc);
		return rc;
	}

	rc = cpr4_apss_parse_boost_properties(vreg);
	if (rc) {
		cpr3_err(vreg, "unable to parse boost adjustments, rc=%d\n",
			 rc);
		return rc;
	}

	cpr4_apss_print_settings(vreg);

	return rc;