Loading Documentation/devicetree/bindings/regulator/cpr4-apss-regulator.txt +64 −0 Original line number Diff line number Diff line Loading @@ -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 ======= Loading Loading @@ -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>; }; }; }; drivers/regulator/cpr3-regulator.c +153 −34 Original line number Diff line number Diff line Loading @@ -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) Loading Loading @@ -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); } Loading Loading @@ -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; Loading @@ -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); } Loading Loading @@ -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. Loading Loading @@ -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), Loading @@ -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; } Loading @@ -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; } Loading Loading @@ -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; } Loading @@ -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 Loading @@ -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 Loading Loading @@ -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; Loading @@ -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 Loading @@ -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; Loading @@ -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 Loading @@ -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; Loading @@ -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; /* Loading Loading @@ -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 Loading Loading @@ -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); Loading Loading @@ -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 */ Loading @@ -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)); Loading Loading @@ -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); Loading Loading @@ -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"); Loading @@ -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; } Loading drivers/regulator/cpr3-regulator.h +15 −0 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; }; /** Loading Loading @@ -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, Loading Loading @@ -285,6 +297,7 @@ struct cpr3_regulator { bool allow_core_count_adj; bool allow_temp_adj; int max_core_count; bool allow_boost; }; /** Loading Loading @@ -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 Loading Loading @@ -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; Loading drivers/regulator/cpr4-apss-regulator.c +217 −0 Original line number Diff line number Diff line Loading @@ -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. */ Loading @@ -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; }; /* Loading Loading @@ -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 */ Loading @@ -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 Loading Loading @@ -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", Loading Loading @@ -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; } Loading Loading @@ -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 Loading Loading @@ -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; Loading Loading
Documentation/devicetree/bindings/regulator/cpr4-apss-regulator.txt +64 −0 Original line number Diff line number Diff line Loading @@ -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 ======= Loading Loading @@ -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>; }; }; };
drivers/regulator/cpr3-regulator.c +153 −34 Original line number Diff line number Diff line Loading @@ -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) Loading Loading @@ -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); } Loading Loading @@ -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; Loading @@ -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); } Loading Loading @@ -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. Loading Loading @@ -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), Loading @@ -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; } Loading @@ -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; } Loading Loading @@ -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; } Loading @@ -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 Loading @@ -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 Loading Loading @@ -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; Loading @@ -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 Loading @@ -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; Loading @@ -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 Loading @@ -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; Loading @@ -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; /* Loading Loading @@ -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 Loading Loading @@ -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); Loading Loading @@ -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 */ Loading @@ -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)); Loading Loading @@ -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); Loading Loading @@ -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"); Loading @@ -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; } Loading
drivers/regulator/cpr3-regulator.h +15 −0 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; }; /** Loading Loading @@ -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, Loading Loading @@ -285,6 +297,7 @@ struct cpr3_regulator { bool allow_core_count_adj; bool allow_temp_adj; int max_core_count; bool allow_boost; }; /** Loading Loading @@ -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 Loading Loading @@ -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; Loading
drivers/regulator/cpr4-apss-regulator.c +217 −0 Original line number Diff line number Diff line Loading @@ -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. */ Loading @@ -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; }; /* Loading Loading @@ -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 */ Loading @@ -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 Loading Loading @@ -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", Loading Loading @@ -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; } Loading Loading @@ -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 Loading Loading @@ -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; Loading