diff --git a/Documentation/devicetree/bindings/regulator/cpr3-regulator.txt b/Documentation/devicetree/bindings/regulator/cpr3-regulator.txt index 71d3738d03a6..58bee1c2cba8 100644 --- a/Documentation/devicetree/bindings/regulator/cpr3-regulator.txt +++ b/Documentation/devicetree/bindings/regulator/cpr3-regulator.txt @@ -517,6 +517,21 @@ Platform independent properties: regardless of the fuse combination and speed bin found on a given chip. +- qcom,allow-aging-open-loop-voltage-adjustment + Usage: optional + Value type: + Definition: A list of integers which specifies if CPR aging adjustment + should be applied to open-loop voltages for each fuse + combination. Note that aging adjustment must be allowed via + qcom,allow-aging-voltage-adjustment in order for this + property to have an effect. + Supported per-combo element values: + 0 - do not perform CPR aging adjustment + 1 - perform CPR aging adjustment + + The list must meet the same size requirements as those + specified for qcom,allow-aging-voltage-adjustment above. + - qcom,cpr-aging-max-voltage-adjustment Usage: required if qcom,allow-aging-voltage-adjustment is specified Value type: diff --git a/drivers/regulator/cpr3-regulator.c b/drivers/regulator/cpr3-regulator.c index 5c028d51fdc0..309ec4c1e745 100644 --- a/drivers/regulator/cpr3-regulator.c +++ b/drivers/regulator/cpr3-regulator.c @@ -3505,7 +3505,8 @@ cleanup: } /** - * cpr3_regulator_readjust_quotients() - readjust the target quotients for the + * cpr3_regulator_readjust_volt_and_quot() - readjust the target quotients as + * well as the floor, ceiling, and open-loop voltages for the * regulator by removing the old adjustment and adding the new one * @vreg: Pointer to the CPR3 regulator * @old_adjust_volt: Old aging adjustment voltage in microvolts @@ -3513,12 +3514,14 @@ cleanup: * * Also reset the cached closed loop voltage (last_volt) to equal the open-loop * voltage for each corner. + * + * Return: None */ -static void cpr3_regulator_readjust_quotients(struct cpr3_regulator *vreg, +static void cpr3_regulator_readjust_volt_and_quot(struct cpr3_regulator *vreg, int old_adjust_volt, int new_adjust_volt) { unsigned long long temp; - int i, j, old_volt, new_volt; + int i, j, old_volt, new_volt, rounded_volt; if (!vreg->aging_allowed) return; @@ -3548,14 +3551,34 @@ static void cpr3_regulator_readjust_quotients(struct cpr3_regulator *vreg, old_volt); } } + + rounded_volt = CPR3_ROUND(new_volt, + vreg->thread->ctrl->step_volt); + + if (!vreg->aging_allow_open_loop_adj) + rounded_volt = 0; + + vreg->corner[i].ceiling_volt + = vreg->corner[i].unaged_ceiling_volt + rounded_volt; + vreg->corner[i].ceiling_volt = min(vreg->corner[i].ceiling_volt, + vreg->corner[i].abs_ceiling_volt); + vreg->corner[i].floor_volt + = vreg->corner[i].unaged_floor_volt + rounded_volt; + vreg->corner[i].floor_volt = min(vreg->corner[i].floor_volt, + vreg->corner[i].ceiling_volt); + vreg->corner[i].open_loop_volt + = vreg->corner[i].unaged_open_loop_volt + rounded_volt; + vreg->corner[i].open_loop_volt + = min(vreg->corner[i].open_loop_volt, + vreg->corner[i].ceiling_volt); + vreg->corner[i].last_volt = vreg->corner[i].open_loop_volt; - cpr3_debug(vreg, "corner %d: applying %d uV closed-loop voltage margin adjustment\n", - i, new_volt); + cpr3_debug(vreg, "corner %d: applying %d uV closed-loop and %d uV open-loop voltage margin adjustment\n", + i, new_volt, rounded_volt); } } - /** * cpr3_regulator_set_aging_ref_adjustment() - adjust target quotients for the * regulators managed by this CPR controller to account for aging @@ -3574,7 +3597,7 @@ static void cpr3_regulator_set_aging_ref_adjustment( for (i = 0; i < ctrl->thread_count; i++) { for (j = 0; j < ctrl->thread[i].vreg_count; j++) { - cpr3_regulator_readjust_quotients( + cpr3_regulator_readjust_volt_and_quot( &ctrl->thread[i].vreg[j], ctrl->aging_ref_adjust_volt, ref_adjust_volt); @@ -5586,10 +5609,13 @@ static int cpr3_regulator_init_ctrl_data(struct cpr3_controller *ctrl) static int cpr3_regulator_init_vreg_data(struct cpr3_regulator *vreg) { int i, j; + bool init_aging; vreg->current_corner = CPR3_REGULATOR_CORNER_INVALID; vreg->last_closed_loop_corner = CPR3_REGULATOR_CORNER_INVALID; + init_aging = vreg->aging_allowed && vreg->thread->ctrl->aging_required; + for (i = 0; i < vreg->corner_count; i++) { vreg->corner[i].last_volt = vreg->corner[i].open_loop_volt; vreg->corner[i].irq_en = CPR3_IRQ_UP | CPR3_IRQ_DOWN; @@ -5599,6 +5625,33 @@ static int cpr3_regulator_init_vreg_data(struct cpr3_regulator *vreg) if (vreg->corner[i].target_quot[j] == 0) vreg->corner[i].ro_mask |= BIT(j); } + + if (init_aging) { + vreg->corner[i].unaged_floor_volt + = vreg->corner[i].floor_volt; + vreg->corner[i].unaged_ceiling_volt + = vreg->corner[i].ceiling_volt; + vreg->corner[i].unaged_open_loop_volt + = vreg->corner[i].open_loop_volt; + } + + if (vreg->aging_allowed) { + if (vreg->corner[i].unaged_floor_volt <= 0) { + cpr3_err(vreg, "invalid unaged_floor_volt[%d] = %d\n", + i, vreg->corner[i].unaged_floor_volt); + return -EINVAL; + } + if (vreg->corner[i].unaged_ceiling_volt <= 0) { + cpr3_err(vreg, "invalid unaged_ceiling_volt[%d] = %d\n", + i, vreg->corner[i].unaged_ceiling_volt); + return -EINVAL; + } + if (vreg->corner[i].unaged_open_loop_volt <= 0) { + cpr3_err(vreg, "invalid unaged_open_loop_volt[%d] = %d\n", + i, vreg->corner[i].unaged_open_loop_volt); + return -EINVAL; + } + } } if (vreg->aging_allowed && vreg->corner[vreg->aging_corner].ceiling_volt diff --git a/drivers/regulator/cpr3-regulator.h b/drivers/regulator/cpr3-regulator.h index d7729641fcf6..111d362a9384 100644 --- a/drivers/regulator/cpr3-regulator.h +++ b/drivers/regulator/cpr3-regulator.h @@ -100,6 +100,16 @@ struct cpr4_sdelta { * microvolts * @last_volt: Last known settled CPR closed-loop voltage which is used * when switching to a new corner + * @abs_ceiling_volt: The absolute CPR closed-loop ceiling voltage in + * microvolts. This is used to limit the ceiling_volt + * value when it is increased as a result of aging + * adjustment. + * @unaged_floor_volt: The CPR closed-loop floor voltage in microvolts before + * any aging adjustment is performed + * @unaged_ceiling_volt: The CPR closed-loop ceiling voltage in microvolts + * before any aging adjustment is performed + * @unaged_open_loop_volt: The CPR open-loop voltage (i.e. initial voltage) in + * microvolts before any aging adjusment is performed * @system_volt: The system-supply voltage in microvolts or corners or * levels * @mem_acc_volt: The mem-acc-supply voltage in corners @@ -136,7 +146,11 @@ struct cpr4_sdelta { * * The value of last_volt is initialized inside of the cpr3_regulator_register() * call with the open_loop_volt value. It can later be updated to the settled - * VDD supply voltage. + * VDD supply voltage. The values for unaged_floor_volt, unaged_ceiling_volt, + * and unaged_open_loop_volt are initialized inside of cpr3_regulator_register() + * if ctrl->aging_required == true. These three values must be pre-initialized + * if cpr3_regulator_register() is called with ctrl->aging_required == false and + * ctrl->aging_succeeded == true. * * The values of ro_mask and irq_en are initialized inside of the * cpr3_regulator_register() call. @@ -146,6 +160,10 @@ struct cpr3_corner { int ceiling_volt; int open_loop_volt; int last_volt; + int abs_ceiling_volt; + int unaged_floor_volt; + int unaged_ceiling_volt; + int unaged_open_loop_volt; int system_volt; int mem_acc_volt; u32 proc_freq; @@ -277,6 +295,13 @@ struct cprh_corner_band { * @aging_allowed: Boolean defining if CPR aging adjustments are allowed * for this CPR3 regulator given the fuse combo of the * device + * @aging_allow_open_loop_adj: Boolean defining if the open-loop voltage of each + * corner of this regulator should be adjusted as a result + * of an aging measurement. This flag can be set to false + * when the open-loop voltage adjustments have been + * specified such that they include the maximum possible + * aging adjustment. This flag is only used if + * aging_allowed == true. * @aging_corner: The corner that should be configured for this regulator * when an aging measurement is performed. * @aging_max_adjust_volt: The maximum aging voltage margin in microvolts that @@ -342,6 +367,7 @@ struct cpr3_regulator { bool vreg_enabled; bool aging_allowed; + bool aging_allow_open_loop_adj; int aging_corner; int aging_max_adjust_volt; diff --git a/drivers/regulator/cpr3-util.c b/drivers/regulator/cpr3-util.c index d208e8a10ced..255e585f02a3 100644 --- a/drivers/regulator/cpr3-util.c +++ b/drivers/regulator/cpr3-util.c @@ -695,9 +695,11 @@ int cpr3_parse_common_corner_data(struct cpr3_regulator *vreg) 1, temp); if (rc) goto free_temp; - for (i = 0; i < vreg->corner_count; i++) + for (i = 0; i < vreg->corner_count; i++) { vreg->corner[i].ceiling_volt = CPR3_ROUND(temp[i], ctrl->step_volt); + vreg->corner[i].abs_ceiling_volt = vreg->corner[i].ceiling_volt; + } rc = cpr3_parse_corner_array_property(vreg, "qcom,cpr-voltage-floor", 1, temp); @@ -807,6 +809,17 @@ int cpr3_parse_common_corner_data(struct cpr3_regulator *vreg) vreg->aging_allowed = aging_allowed; } + if (of_find_property(vreg->of_node, + "qcom,allow-aging-open-loop-voltage-adjustment", NULL)) { + rc = cpr3_parse_array_property(vreg, + "qcom,allow-aging-open-loop-voltage-adjustment", + 1, &aging_allowed); + if (rc) + goto free_temp; + + vreg->aging_allow_open_loop_adj = aging_allowed; + } + if (vreg->aging_allowed) { if (ctrl->aging_ref_volt <= 0) { cpr3_err(ctrl, "qcom,cpr-aging-ref-voltage must be specified\n");