diff options
| author | Tirupathi Reddy <tirupath@codeaurora.org> | 2016-01-18 16:06:20 +0530 |
|---|---|---|
| committer | David Keitel <dkeitel@codeaurora.org> | 2016-03-23 21:23:13 -0700 |
| commit | 86afb76cfdb674ee1b235dbf7151aeb104248a29 (patch) | |
| tree | 9f3a87c0d2ce9ce024bab02b4aec0684fec653d7 | |
| parent | 352c8726286162d3c7fb585649bf88e60539dbc6 (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.txt | 64 | ||||
| -rw-r--r-- | drivers/regulator/cpr3-regulator.c | 187 | ||||
| -rw-r--r-- | drivers/regulator/cpr3-regulator.h | 15 | ||||
| -rw-r--r-- | drivers/regulator/cpr4-apss-regulator.c | 217 |
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; |
