summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTirupathi Reddy <tirupath@codeaurora.org>2016-01-18 16:06:20 +0530
committerDavid Keitel <dkeitel@codeaurora.org>2016-03-23 21:23:13 -0700
commit86afb76cfdb674ee1b235dbf7151aeb104248a29 (patch)
tree9f3a87c0d2ce9ce024bab02b4aec0684fec653d7
parent352c8726286162d3c7fb585649bf88e60539dbc6 (diff)
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: Tirupathi Reddy <tirupath@codeaurora.org>
-rw-r--r--Documentation/devicetree/bindings/regulator/cpr4-apss-regulator.txt64
-rw-r--r--drivers/regulator/cpr3-regulator.c187
-rw-r--r--drivers/regulator/cpr3-regulator.h15
-rw-r--r--drivers/regulator/cpr4-apss-regulator.c217
4 files changed, 449 insertions, 34 deletions
diff --git a/Documentation/devicetree/bindings/regulator/cpr4-apss-regulator.txt b/Documentation/devicetree/bindings/regulator/cpr4-apss-regulator.txt
index 5b081119a944..61382b562676 100644
--- a/Documentation/devicetree/bindings/regulator/cpr4-apss-regulator.txt
+++ b/Documentation/devicetree/bindings/regulator/cpr4-apss-regulator.txt
@@ -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>;
};
};
};
diff --git a/drivers/regulator/cpr3-regulator.c b/drivers/regulator/cpr3-regulator.c
index b4d79772ad89..ca09c8d78c28 100644
--- a/drivers/regulator/cpr3-regulator.c
+++ b/drivers/regulator/cpr3-regulator.c
@@ -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) {
- /* Program TEMP_CORE0 to same margins as TEMP_CORE1 */
- cpr3_write_temp_core_margin(ctrl,
- CPR4_REG_MARGIN_TEMP_CORE(0), &sdelta->table[0]);
- }
+ 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]);
+ }
- 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.
- */
- cpr3_write_temp_core_margin(ctrl,
- CPR4_REG_MARGIN_TEMP_CORE(sdelta->allow_core_count_adj
+ 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.
+ */
+ cpr3_write_temp_core_margin(ctrl,
+ 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);
@@ -5135,7 +5234,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");
@@ -5162,6 +5263,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;
}
diff --git a/drivers/regulator/cpr3-regulator.h b/drivers/regulator/cpr3-regulator.h
index a5ba05b5ace4..4e098e7d7df1 100644
--- a/drivers/regulator/cpr3-regulator.h
+++ b/drivers/regulator/cpr3-regulator.h
@@ -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;
};
/**
@@ -241,6 +252,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,
@@ -294,6 +306,7 @@ struct cpr3_regulator {
bool allow_core_count_adj;
bool allow_temp_adj;
int max_core_count;
+ bool allow_boost;
};
/**
@@ -553,6 +566,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
@@ -649,6 +663,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;
diff --git a/drivers/regulator/cpr4-apss-regulator.c b/drivers/regulator/cpr4-apss-regulator.c
index 02242160f3f9..5b8104da7c10 100644
--- a/drivers/regulator/cpr4-apss-regulator.c
+++ b/drivers/regulator/cpr4-apss-regulator.c
@@ -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;
}
@@ -945,6 +1003,157 @@ done:
}
/**
+ * 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
* @vreg: Pointer to the 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;