Loading Documentation/devicetree/bindings/regulator/cpr3-mmss-regulator.txt +29 −0 Original line number Diff line number Diff line Loading @@ -119,6 +119,32 @@ MMSS specific properties: or the qcom,cpr-speed-bins property as opposed to the value of the qcom,cpr-fuse-corners property. - qcom,cpr-fused-closed-loop-voltage-adjustment-map Usage: optional Value type: <prop-encoded-array> Definition: A list of integer tuples which each define the CPR fused corner closed-loop offset adjustment fuse to utilize for each voltage corner in order from lowest to highest. 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. Each tuple must be of the length defined in the corresponding element of the qcom,cpr-corners property or the qcom,cpr-speed-bins property. A single tuple may only be specified if all of the corner counts in qcom,cpr-corners are the same. Each tuple element must be either 0 or in the range 1 to qcom,cpr-fuse-corners. A value of 0 signifies that no fuse based adjustment should be applied to the fuse corner. Values 1 to qcom,cpr-fuse-corners denote the specific fuse corner that should be used by a given voltage corner. Note that the qcom,cpr-closed-loop-voltage-fuse-adjustment property is not meaningful for MMSS CPR3 regulator nodes since target quotients are not defined in fuses. Loading Loading @@ -205,6 +231,9 @@ gfx_cpr: cpr3-ctrl@838000 { qcom,cpr-closed-loop-voltage-adjustment = <45000 0 0 0>; qcom,cpr-fused-closed-loop-voltage-adjustment-map = <2 2 0 4>; qcom,allow-voltage-interpolation; qcom,cpr-scaled-open-loop-voltage-as-ceiling; Loading drivers/regulator/cpr3-hmss-regulator.c +8 −8 Original line number Diff line number Diff line Loading @@ -656,7 +656,7 @@ static int cpr3_msm8996_hmss_calculate_open_loop_voltages( for (i = 1; i < vreg->fuse_corner_count; i++) { if (fuse_volt[i] < fuse_volt[i - 1]) { cpr3_info(vreg, "fuse corner %d voltage=%d uV < fuse corner %d voltage=%d uV; overriding: fuse corner %d voltage=%d\n", cpr3_debug(vreg, "fuse corner %d voltage=%d uV < fuse corner %d voltage=%d uV; overriding: fuse corner %d voltage=%d\n", i, fuse_volt[i], i - 1, fuse_volt[i - 1], i, fuse_volt[i - 1]); fuse_volt[i] = fuse_volt[i - 1]; Loading Loading @@ -923,7 +923,7 @@ static int cpr3_msm8996_hmss_set_no_interpolation_quotients( ro = fuse->ro_sel[fuse_corner]; vreg->corner[i].target_quot[ro] = quot + quot_adjust; if (quot_adjust) cpr3_info(vreg, "adjusted corner %d RO%u target quot: %u --> %u (%d uV)\n", cpr3_debug(vreg, "adjusted corner %d RO%u target quot: %u --> %u (%d uV)\n", i, ro, quot, vreg->corner[i].target_quot[ro], volt_adjust_fuse[fuse_corner] + volt_adjust[i]); } Loading Loading @@ -1035,7 +1035,7 @@ static int cpr3_msm8996_hmss_calculate_target_quotients( quot_high[i] = quot; ro = fuse->ro_sel[i]; if (quot_adjust) cpr3_info(vreg, "adjusted fuse corner %d RO%u target quot: %llu --> %u (%d uV)\n", cpr3_debug(vreg, "adjusted fuse corner %d RO%u target quot: %llu --> %u (%d uV)\n", i, ro, fuse->target_quot[i], quot, volt_adjust_fuse[i]); for (i = 0; i <= fmax_corner[CPR3_MSM8996_HMSS_FUSE_CORNER_MINSVS]; i++) vreg->corner[i].target_quot[ro] = quot; Loading Loading @@ -1074,7 +1074,7 @@ static int cpr3_msm8996_hmss_calculate_target_quotients( - fuse->quot_offset[i] * MSM8996_HMSS_QUOT_OFFSET_SCALE; if (quot_high[i] < quot_low[i]) { cpr3_info(vreg, "quot_high[%d]=%llu < quot_low[%d]=%llu; overriding: quot_high[%d]=%llu\n", cpr3_debug(vreg, "quot_high[%d]=%llu < quot_low[%d]=%llu; overriding: quot_high[%d]=%llu\n", i, quot_high[i], i, quot_low[i], i, quot_low[i]); quot_high[i] = quot_low[i]; Loading @@ -1088,7 +1088,7 @@ static int cpr3_msm8996_hmss_calculate_target_quotients( if (quot_adjust) { prev_quot = quot_high[i]; quot_high[i] += quot_adjust; cpr3_info(vreg, "adjusted fuse corner %d RO%llu target quot: %llu --> %llu (%d uV)\n", cpr3_debug(vreg, "adjusted fuse corner %d RO%llu target quot: %llu --> %llu (%d uV)\n", i, fuse->ro_sel[i], prev_quot, quot_high[i], volt_adjust_fuse[i]); } Loading @@ -1100,7 +1100,7 @@ static int cpr3_msm8996_hmss_calculate_target_quotients( volt_adjust_fuse[i - 1]); if (quot_high[i] < quot_low[i]) { cpr3_info(vreg, "quot_high[%d]=%llu < quot_low[%d]=%llu after adjustment; overriding: quot_high[%d]=%llu\n", cpr3_debug(vreg, "quot_high[%d]=%llu < quot_low[%d]=%llu after adjustment; overriding: quot_high[%d]=%llu\n", i, quot_high[i], i, quot_low[i], i, quot_low[i]); quot_high[i] = quot_low[i]; Loading Loading @@ -1128,7 +1128,7 @@ static int cpr3_msm8996_hmss_calculate_target_quotients( if (quot_adjust) { prev_quot = vreg->corner[i].target_quot[ro]; vreg->corner[i].target_quot[ro] += quot_adjust; cpr3_info(vreg, "adjusted corner %d RO%u target quot: %llu --> %u (%d uV)\n", cpr3_debug(vreg, "adjusted corner %d RO%u target quot: %llu --> %u (%d uV)\n", i, ro, prev_quot, vreg->corner[i].target_quot[ro], volt_adjust[i]); Loading @@ -1141,7 +1141,7 @@ static int cpr3_msm8996_hmss_calculate_target_quotients( if (fuse->ro_sel[vreg->corner[i - 1].cpr_fuse_corner] == ro && vreg->corner[i].target_quot[ro] < vreg->corner[i - 1].target_quot[ro]) { cpr3_info(vreg, "adjusted corner %d RO%u target quot=%u < adjusted corner %d RO%u target quot=%u; overriding: corner %d RO%u target quot=%u\n", cpr3_debug(vreg, "adjusted corner %d RO%u target quot=%u < adjusted corner %d RO%u target quot=%u; overriding: corner %d RO%u target quot=%u\n", i, ro, vreg->corner[i].target_quot[ro], i - 1, ro, vreg->corner[i - 1].target_quot[ro], i, ro, vreg->corner[i - 1].target_quot[ro]); Loading drivers/regulator/cpr3-mmss-regulator.c +234 −54 Original line number Diff line number Diff line Loading @@ -41,6 +41,9 @@ * struct cpr3_msm8996_mmss_fuses - MMSS specific fuse data for MSM8996 * @init_voltage: Initial (i.e. open-loop) voltage fuse parameter value * for each fuse corner (raw, not converted to a voltage) * @offset_voltage: The closed-loop voltage margin adjustment fuse parameter * value for each fuse corner (raw, not converted to a * voltage) * @cpr_fusing_rev: CPR fusing revision fuse parameter value * @limitation: CPR limitation select fuse parameter value * @aging_init_quot_diff: Initial quotient difference between CPR aging Loading @@ -50,6 +53,7 @@ */ struct cpr3_msm8996_mmss_fuses { u64 init_voltage[MSM8996_MMSS_FUSE_CORNERS]; u64 offset_voltage[MSM8996_MMSS_FUSE_CORNERS]; u64 cpr_fusing_rev; u64 limitation; u64 aging_init_quot_diff; Loading Loading @@ -119,6 +123,15 @@ msm8996_mmss_aging_init_quot_diff_param[] = { {}, }; /* Offset voltages are defined for SVS and Turbo fuse corners only */ static const struct cpr3_fuse_param msm8996_mmss_offset_voltage_param[MSM8996_MMSS_FUSE_CORNERS][2] = { {{} }, {{66, 42, 44}, {} }, {{} }, {{64, 58, 61}, {} }, }; /* * Some initial msm8996 parts cannot be used in a meaningful way by software. * Other parts can only be used when operating with CPR disabled (i.e. at the Loading @@ -142,6 +155,7 @@ static const int msm8996_mmss_fuse_ref_volt[MSM8996_MMSS_FUSE_CORNERS] = { }; #define MSM8996_MMSS_FUSE_STEP_VOLT 10000 #define MSM8996_MMSS_OFFSET_FUSE_STEP_VOLT 10000 #define MSM8996_MMSS_VOLTAGE_FUSE_SIZE 5 #define MSM8996_MMSS_AGING_INIT_QUOT_DIFF_SCALE 2 #define MSM8996_MMSS_AGING_INIT_QUOT_DIFF_SIZE 6 Loading Loading @@ -212,6 +226,15 @@ static int cpr3_msm8996_mmss_read_fuse_data(struct cpr3_regulator *vreg) i, rc); return rc; } rc = cpr3_read_fuse_param(base, msm8996_mmss_offset_voltage_param[i], &fuse->offset_voltage[i]); if (rc) { cpr3_err(vreg, "Unable to read fuse-corner %d offset voltage fuse, rc=%d\n", i, rc); return rc; } } vreg->fuse_combo = fuse->cpr_fusing_rev; Loading Loading @@ -267,24 +290,207 @@ done: return rc; } /** * cpr3_msm8996_mmss_apply_closed_loop_offset_voltages() - modify the * closed-loop voltage adjustments by the amounts that are needed * for this fuse combo * @vreg: Pointer to the CPR3 regulator * @volt_adjust: Array of closed-loop voltage adjustment values of length * vreg->corner_count which is further adjusted based upon * offset voltage fuse values. * * Return: 0 on success, errno on failure */ static int cpr3_msm8996_mmss_apply_closed_loop_offset_voltages( struct cpr3_regulator *vreg, int *volt_adjust) { struct cpr3_msm8996_mmss_fuses *fuse = vreg->platform_fuses; u32 *corner_map; int *volt_offset; int rc = 0, i, fuse_len; if (!of_find_property(vreg->of_node, "qcom,cpr-fused-closed-loop-voltage-adjustment-map", NULL)) { /* No closed-loop offset required. */ return 0; } corner_map = kcalloc(vreg->corner_count, sizeof(*corner_map), GFP_KERNEL); volt_offset = kcalloc(vreg->fuse_corner_count, sizeof(*volt_offset), GFP_KERNEL); if (!corner_map || !volt_offset) { rc = -ENOMEM; goto done; } rc = cpr3_parse_corner_array_property(vreg, "qcom,cpr-fused-closed-loop-voltage-adjustment-map", 1, corner_map); if (rc) goto done; for (i = 0; i < vreg->fuse_corner_count; i++) { fuse_len = msm8996_mmss_offset_voltage_param[i][0].bit_end + 1 - msm8996_mmss_offset_voltage_param[i][0].bit_start; volt_offset[i] = cpr3_convert_open_loop_voltage_fuse( 0, MSM8996_MMSS_OFFSET_FUSE_STEP_VOLT, fuse->offset_voltage[i], fuse_len); if (volt_offset[i]) cpr3_info(vreg, "fuse_corner[%d] offset=%7d uV\n", i, volt_offset[i]); } for (i = 0; i < vreg->corner_count; i++) { if (corner_map[i] == 0) { continue; } else if (corner_map[i] > vreg->fuse_corner_count) { cpr3_err(vreg, "corner %d mapped to invalid fuse corner: %u\n", i, corner_map[i]); rc = -EINVAL; goto done; } volt_adjust[i] += volt_offset[corner_map[i] - 1]; } done: kfree(corner_map); kfree(volt_offset); return rc; } /** * cpr3_mmss_enforce_inc_quotient_monotonicity() - Ensure that target quotients * increase monotonically from lower to higher corners * @vreg: Pointer to the CPR3 regulator * * Return: 0 on success, errno on failure */ static void cpr3_mmss_enforce_inc_quotient_monotonicity( struct cpr3_regulator *vreg) { int i, j; for (i = 1; i < vreg->corner_count; i++) { for (j = 0; j < CPR3_RO_COUNT; j++) { if (vreg->corner[i].target_quot[j] && vreg->corner[i].target_quot[j] < vreg->corner[i - 1].target_quot[j]) { cpr3_debug(vreg, "corner %d RO%u target quot=%u < corner %d RO%u target quot=%u; overriding: corner %d RO%u target quot=%u\n", i, j, vreg->corner[i].target_quot[j], i - 1, j, vreg->corner[i - 1].target_quot[j], i, j, vreg->corner[i - 1].target_quot[j]); vreg->corner[i].target_quot[j] = vreg->corner[i - 1].target_quot[j]; } } } } /** * cpr3_mmss_enforce_dec_quotient_monotonicity() - Ensure that target quotients * decrease monotonically from higher to lower corners * @vreg: Pointer to the CPR3 regulator * * Return: 0 on success, errno on failure */ static void cpr3_mmss_enforce_dec_quotient_monotonicity( struct cpr3_regulator *vreg) { int i, j; for (i = vreg->corner_count - 2; i >= 0; i--) { for (j = 0; j < CPR3_RO_COUNT; j++) { if (vreg->corner[i].target_quot[j] && vreg->corner[i].target_quot[j] > vreg->corner[i + 1].target_quot[j]) { cpr3_debug(vreg, "corner %d RO%u target quot=%u > corner %d RO%u target quot=%u; overriding: corner %d RO%u target quot=%u\n", i, j, vreg->corner[i].target_quot[j], i + 1, j, vreg->corner[i + 1].target_quot[j], i, j, vreg->corner[i + 1].target_quot[j]); vreg->corner[i].target_quot[j] = vreg->corner[i + 1].target_quot[j]; } } } } /** * _cpr3_mmss_adjust_target_quotients() - adjust the target quotients for each * corner of the regulator according to input adjustment and * scaling arrays * @vreg: Pointer to the CPR3 regulator * @volt_adjust: Pointer to an array of closed-loop voltage adjustments * with units of microvolts. The array must have * vreg->corner_count number of elements. * @ro_scale: Pointer to a flattened 2D array of RO scaling factors. * The array must have an inner dimension of CPR3_RO_COUNT * and an outer dimension of vreg->corner_count * @label: Null terminated string providing a label for the type * of adjustment. * * Return: true if any corners received a positive voltage adjustment (> 0), * else false */ static bool _cpr3_mmss_adjust_target_quotients(struct cpr3_regulator *vreg, const int *volt_adjust, const int *ro_scale, const char *label) { int i, j, quot_adjust; bool is_increasing = false; u32 prev_quot; for (i = 0; i < vreg->corner_count; i++) { for (j = 0; j < CPR3_RO_COUNT; j++) { if (vreg->corner[i].target_quot[j]) { quot_adjust = cpr3_quot_adjustment( ro_scale[i * CPR3_RO_COUNT + j], volt_adjust[i]); if (quot_adjust) { prev_quot = vreg->corner[i]. target_quot[j]; vreg->corner[i].target_quot[j] += quot_adjust; cpr3_debug(vreg, "adjusted corner %d RO%d target quot %s: %u --> %u (%d uV)\n", i, j, label, prev_quot, vreg->corner[i].target_quot[j], volt_adjust[i]); } } } if (volt_adjust[i] > 0) is_increasing = true; } return is_increasing; } /** * cpr3_mmss_adjust_target_quotients() - adjust the target quotients for each * corner according to device tree values * corner according to device tree values and fuse values * @vreg: Pointer to the CPR3 regulator * * Return: 0 on success, errno on failure */ static int cpr3_mmss_adjust_target_quotients(struct cpr3_regulator *vreg) { int i, j, rc, quot_adjust; int i, rc; int *volt_adjust, *ro_scale; bool explicit_adjustment; u32 prev_quot; bool explicit_adjustment, fused_adjustment, is_increasing; explicit_adjustment = of_find_property(vreg->of_node, "qcom,cpr-closed-loop-voltage-adjustment", NULL); fused_adjustment = of_find_property(vreg->of_node, "qcom,cpr-fused-closed-loop-voltage-adjustment-map", NULL); if (!explicit_adjustment && !vreg->aging_allowed) { if (!explicit_adjustment && !fused_adjustment && !vreg->aging_allowed) { /* No adjustment required. */ return 0; } else if (!of_find_property(vreg->of_node, Loading Loading @@ -324,55 +530,29 @@ static int cpr3_mmss_adjust_target_quotients(struct cpr3_regulator *vreg) goto done; } /* * Adjust the target quotients for each corner according to * device tree adjustment values. */ for (i = 0; i < vreg->corner_count; i++) { for (j = 0; j < CPR3_RO_COUNT; j++) { if (vreg->corner[i].target_quot[j]) { quot_adjust = cpr3_quot_adjustment( ro_scale[i * CPR3_RO_COUNT + j], volt_adjust[i]); if (quot_adjust) { prev_quot = vreg->corner[i]. target_quot[j]; vreg->corner[i].target_quot[j] += quot_adjust; cpr3_info(vreg, "adjusted corner %d RO%d target quot: %u --> %u (%d uV)\n", i, j, prev_quot, vreg->corner[i]. target_quot[j], volt_adjust[i]); } } } _cpr3_mmss_adjust_target_quotients(vreg, volt_adjust, ro_scale, "from DT"); cpr3_mmss_enforce_inc_quotient_monotonicity(vreg); } /* * Ensure that target quotients increase monotonically from * lower to higher corners after being adjusted based upon * device tree adjustment values. */ for (i = 1; i < vreg->corner_count; i++) { for (j = 0; j < CPR3_RO_COUNT; j++) { if (vreg->corner[i].target_quot[j] && vreg->corner[i].target_quot[j] < vreg->corner[i - 1].target_quot[j]) { cpr3_info(vreg, "adjusted corner %d RO%u target quot=%u < adjusted corner %d RO%u target quot=%u; overriding: corner %d RO%u target quot=%u\n", i, j, vreg->corner[i].target_quot[j], i - 1, j, vreg->corner[i - 1]. target_quot[j], i, j, vreg->corner[i - 1]. target_quot[j]); vreg->corner[i].target_quot[j] = vreg->corner[i - 1].target_quot[j]; } } if (fused_adjustment) { memset(volt_adjust, 0, sizeof(*volt_adjust) * vreg->corner_count); rc = cpr3_msm8996_mmss_apply_closed_loop_offset_voltages(vreg, volt_adjust); if (rc) { cpr3_err(vreg, "could not apply fused closed-loop voltage reductions, rc=%d\n", rc); goto done; } is_increasing = _cpr3_mmss_adjust_target_quotients(vreg, volt_adjust, ro_scale, "from fuse"); if (is_increasing) cpr3_mmss_enforce_inc_quotient_monotonicity(vreg); else cpr3_mmss_enforce_dec_quotient_monotonicity(vreg); } done: Loading Loading @@ -440,7 +620,7 @@ static int cpr3_msm8996_mmss_calculate_open_loop_voltages( for (i = 1; i < vreg->fuse_corner_count; i++) { if (fuse_volt[i] < fuse_volt[i - 1]) { cpr3_info(vreg, "fuse corner %d voltage=%d uV < fuse corner %d voltage=%d uV; overriding: fuse corner %d voltage=%d\n", cpr3_debug(vreg, "fuse corner %d voltage=%d uV < fuse corner %d voltage=%d uV; overriding: fuse corner %d voltage=%d\n", i, fuse_volt[i], i - 1, fuse_volt[i - 1], i, fuse_volt[i - 1]); fuse_volt[i] = fuse_volt[i - 1]; Loading drivers/regulator/cpr3-util.c +3 −3 Original line number Diff line number Diff line Loading @@ -1166,7 +1166,7 @@ int cpr3_adjust_fused_open_loop_voltages(struct cpr3_regulator *vreg, if (volt_adjust[i]) { prev_volt = fuse_volt[i]; fuse_volt[i] += volt_adjust[i]; cpr3_info(vreg, "adjusted fuse corner %d open-loop voltage: %d --> %d uV\n", cpr3_debug(vreg, "adjusted fuse corner %d open-loop voltage: %d --> %d uV\n", i, prev_volt, fuse_volt[i]); } } Loading Loading @@ -1214,7 +1214,7 @@ int cpr3_adjust_open_loop_voltages(struct cpr3_regulator *vreg) if (volt_adjust[i]) { prev_volt = vreg->corner[i].open_loop_volt; vreg->corner[i].open_loop_volt += volt_adjust[i]; cpr3_info(vreg, "adjusted corner %d open-loop voltage: %d --> %d uV\n", cpr3_debug(vreg, "adjusted corner %d open-loop voltage: %d --> %d uV\n", i, prev_volt, vreg->corner[i].open_loop_volt); } } Loading @@ -1237,7 +1237,7 @@ int cpr3_adjust_open_loop_voltages(struct cpr3_regulator *vreg) for (i = 1; i < vreg->corner_count; i++) { min_volt = vreg->corner[i - 1].open_loop_volt + volt_diff[i]; if (vreg->corner[i].open_loop_volt < min_volt) { cpr3_info(vreg, "adjusted corner %d open-loop voltage=%d uV < corner %d voltage=%d uV + min diff=%d uV; overriding: corner %d voltage=%d\n", cpr3_debug(vreg, "adjusted corner %d open-loop voltage=%d uV < corner %d voltage=%d uV + min diff=%d uV; overriding: corner %d voltage=%d\n", i, vreg->corner[i].open_loop_volt, i - 1, vreg->corner[i - 1].open_loop_volt, volt_diff[i], i, min_volt); Loading Loading
Documentation/devicetree/bindings/regulator/cpr3-mmss-regulator.txt +29 −0 Original line number Diff line number Diff line Loading @@ -119,6 +119,32 @@ MMSS specific properties: or the qcom,cpr-speed-bins property as opposed to the value of the qcom,cpr-fuse-corners property. - qcom,cpr-fused-closed-loop-voltage-adjustment-map Usage: optional Value type: <prop-encoded-array> Definition: A list of integer tuples which each define the CPR fused corner closed-loop offset adjustment fuse to utilize for each voltage corner in order from lowest to highest. 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. Each tuple must be of the length defined in the corresponding element of the qcom,cpr-corners property or the qcom,cpr-speed-bins property. A single tuple may only be specified if all of the corner counts in qcom,cpr-corners are the same. Each tuple element must be either 0 or in the range 1 to qcom,cpr-fuse-corners. A value of 0 signifies that no fuse based adjustment should be applied to the fuse corner. Values 1 to qcom,cpr-fuse-corners denote the specific fuse corner that should be used by a given voltage corner. Note that the qcom,cpr-closed-loop-voltage-fuse-adjustment property is not meaningful for MMSS CPR3 regulator nodes since target quotients are not defined in fuses. Loading Loading @@ -205,6 +231,9 @@ gfx_cpr: cpr3-ctrl@838000 { qcom,cpr-closed-loop-voltage-adjustment = <45000 0 0 0>; qcom,cpr-fused-closed-loop-voltage-adjustment-map = <2 2 0 4>; qcom,allow-voltage-interpolation; qcom,cpr-scaled-open-loop-voltage-as-ceiling; Loading
drivers/regulator/cpr3-hmss-regulator.c +8 −8 Original line number Diff line number Diff line Loading @@ -656,7 +656,7 @@ static int cpr3_msm8996_hmss_calculate_open_loop_voltages( for (i = 1; i < vreg->fuse_corner_count; i++) { if (fuse_volt[i] < fuse_volt[i - 1]) { cpr3_info(vreg, "fuse corner %d voltage=%d uV < fuse corner %d voltage=%d uV; overriding: fuse corner %d voltage=%d\n", cpr3_debug(vreg, "fuse corner %d voltage=%d uV < fuse corner %d voltage=%d uV; overriding: fuse corner %d voltage=%d\n", i, fuse_volt[i], i - 1, fuse_volt[i - 1], i, fuse_volt[i - 1]); fuse_volt[i] = fuse_volt[i - 1]; Loading Loading @@ -923,7 +923,7 @@ static int cpr3_msm8996_hmss_set_no_interpolation_quotients( ro = fuse->ro_sel[fuse_corner]; vreg->corner[i].target_quot[ro] = quot + quot_adjust; if (quot_adjust) cpr3_info(vreg, "adjusted corner %d RO%u target quot: %u --> %u (%d uV)\n", cpr3_debug(vreg, "adjusted corner %d RO%u target quot: %u --> %u (%d uV)\n", i, ro, quot, vreg->corner[i].target_quot[ro], volt_adjust_fuse[fuse_corner] + volt_adjust[i]); } Loading Loading @@ -1035,7 +1035,7 @@ static int cpr3_msm8996_hmss_calculate_target_quotients( quot_high[i] = quot; ro = fuse->ro_sel[i]; if (quot_adjust) cpr3_info(vreg, "adjusted fuse corner %d RO%u target quot: %llu --> %u (%d uV)\n", cpr3_debug(vreg, "adjusted fuse corner %d RO%u target quot: %llu --> %u (%d uV)\n", i, ro, fuse->target_quot[i], quot, volt_adjust_fuse[i]); for (i = 0; i <= fmax_corner[CPR3_MSM8996_HMSS_FUSE_CORNER_MINSVS]; i++) vreg->corner[i].target_quot[ro] = quot; Loading Loading @@ -1074,7 +1074,7 @@ static int cpr3_msm8996_hmss_calculate_target_quotients( - fuse->quot_offset[i] * MSM8996_HMSS_QUOT_OFFSET_SCALE; if (quot_high[i] < quot_low[i]) { cpr3_info(vreg, "quot_high[%d]=%llu < quot_low[%d]=%llu; overriding: quot_high[%d]=%llu\n", cpr3_debug(vreg, "quot_high[%d]=%llu < quot_low[%d]=%llu; overriding: quot_high[%d]=%llu\n", i, quot_high[i], i, quot_low[i], i, quot_low[i]); quot_high[i] = quot_low[i]; Loading @@ -1088,7 +1088,7 @@ static int cpr3_msm8996_hmss_calculate_target_quotients( if (quot_adjust) { prev_quot = quot_high[i]; quot_high[i] += quot_adjust; cpr3_info(vreg, "adjusted fuse corner %d RO%llu target quot: %llu --> %llu (%d uV)\n", cpr3_debug(vreg, "adjusted fuse corner %d RO%llu target quot: %llu --> %llu (%d uV)\n", i, fuse->ro_sel[i], prev_quot, quot_high[i], volt_adjust_fuse[i]); } Loading @@ -1100,7 +1100,7 @@ static int cpr3_msm8996_hmss_calculate_target_quotients( volt_adjust_fuse[i - 1]); if (quot_high[i] < quot_low[i]) { cpr3_info(vreg, "quot_high[%d]=%llu < quot_low[%d]=%llu after adjustment; overriding: quot_high[%d]=%llu\n", cpr3_debug(vreg, "quot_high[%d]=%llu < quot_low[%d]=%llu after adjustment; overriding: quot_high[%d]=%llu\n", i, quot_high[i], i, quot_low[i], i, quot_low[i]); quot_high[i] = quot_low[i]; Loading Loading @@ -1128,7 +1128,7 @@ static int cpr3_msm8996_hmss_calculate_target_quotients( if (quot_adjust) { prev_quot = vreg->corner[i].target_quot[ro]; vreg->corner[i].target_quot[ro] += quot_adjust; cpr3_info(vreg, "adjusted corner %d RO%u target quot: %llu --> %u (%d uV)\n", cpr3_debug(vreg, "adjusted corner %d RO%u target quot: %llu --> %u (%d uV)\n", i, ro, prev_quot, vreg->corner[i].target_quot[ro], volt_adjust[i]); Loading @@ -1141,7 +1141,7 @@ static int cpr3_msm8996_hmss_calculate_target_quotients( if (fuse->ro_sel[vreg->corner[i - 1].cpr_fuse_corner] == ro && vreg->corner[i].target_quot[ro] < vreg->corner[i - 1].target_quot[ro]) { cpr3_info(vreg, "adjusted corner %d RO%u target quot=%u < adjusted corner %d RO%u target quot=%u; overriding: corner %d RO%u target quot=%u\n", cpr3_debug(vreg, "adjusted corner %d RO%u target quot=%u < adjusted corner %d RO%u target quot=%u; overriding: corner %d RO%u target quot=%u\n", i, ro, vreg->corner[i].target_quot[ro], i - 1, ro, vreg->corner[i - 1].target_quot[ro], i, ro, vreg->corner[i - 1].target_quot[ro]); Loading
drivers/regulator/cpr3-mmss-regulator.c +234 −54 Original line number Diff line number Diff line Loading @@ -41,6 +41,9 @@ * struct cpr3_msm8996_mmss_fuses - MMSS specific fuse data for MSM8996 * @init_voltage: Initial (i.e. open-loop) voltage fuse parameter value * for each fuse corner (raw, not converted to a voltage) * @offset_voltage: The closed-loop voltage margin adjustment fuse parameter * value for each fuse corner (raw, not converted to a * voltage) * @cpr_fusing_rev: CPR fusing revision fuse parameter value * @limitation: CPR limitation select fuse parameter value * @aging_init_quot_diff: Initial quotient difference between CPR aging Loading @@ -50,6 +53,7 @@ */ struct cpr3_msm8996_mmss_fuses { u64 init_voltage[MSM8996_MMSS_FUSE_CORNERS]; u64 offset_voltage[MSM8996_MMSS_FUSE_CORNERS]; u64 cpr_fusing_rev; u64 limitation; u64 aging_init_quot_diff; Loading Loading @@ -119,6 +123,15 @@ msm8996_mmss_aging_init_quot_diff_param[] = { {}, }; /* Offset voltages are defined for SVS and Turbo fuse corners only */ static const struct cpr3_fuse_param msm8996_mmss_offset_voltage_param[MSM8996_MMSS_FUSE_CORNERS][2] = { {{} }, {{66, 42, 44}, {} }, {{} }, {{64, 58, 61}, {} }, }; /* * Some initial msm8996 parts cannot be used in a meaningful way by software. * Other parts can only be used when operating with CPR disabled (i.e. at the Loading @@ -142,6 +155,7 @@ static const int msm8996_mmss_fuse_ref_volt[MSM8996_MMSS_FUSE_CORNERS] = { }; #define MSM8996_MMSS_FUSE_STEP_VOLT 10000 #define MSM8996_MMSS_OFFSET_FUSE_STEP_VOLT 10000 #define MSM8996_MMSS_VOLTAGE_FUSE_SIZE 5 #define MSM8996_MMSS_AGING_INIT_QUOT_DIFF_SCALE 2 #define MSM8996_MMSS_AGING_INIT_QUOT_DIFF_SIZE 6 Loading Loading @@ -212,6 +226,15 @@ static int cpr3_msm8996_mmss_read_fuse_data(struct cpr3_regulator *vreg) i, rc); return rc; } rc = cpr3_read_fuse_param(base, msm8996_mmss_offset_voltage_param[i], &fuse->offset_voltage[i]); if (rc) { cpr3_err(vreg, "Unable to read fuse-corner %d offset voltage fuse, rc=%d\n", i, rc); return rc; } } vreg->fuse_combo = fuse->cpr_fusing_rev; Loading Loading @@ -267,24 +290,207 @@ done: return rc; } /** * cpr3_msm8996_mmss_apply_closed_loop_offset_voltages() - modify the * closed-loop voltage adjustments by the amounts that are needed * for this fuse combo * @vreg: Pointer to the CPR3 regulator * @volt_adjust: Array of closed-loop voltage adjustment values of length * vreg->corner_count which is further adjusted based upon * offset voltage fuse values. * * Return: 0 on success, errno on failure */ static int cpr3_msm8996_mmss_apply_closed_loop_offset_voltages( struct cpr3_regulator *vreg, int *volt_adjust) { struct cpr3_msm8996_mmss_fuses *fuse = vreg->platform_fuses; u32 *corner_map; int *volt_offset; int rc = 0, i, fuse_len; if (!of_find_property(vreg->of_node, "qcom,cpr-fused-closed-loop-voltage-adjustment-map", NULL)) { /* No closed-loop offset required. */ return 0; } corner_map = kcalloc(vreg->corner_count, sizeof(*corner_map), GFP_KERNEL); volt_offset = kcalloc(vreg->fuse_corner_count, sizeof(*volt_offset), GFP_KERNEL); if (!corner_map || !volt_offset) { rc = -ENOMEM; goto done; } rc = cpr3_parse_corner_array_property(vreg, "qcom,cpr-fused-closed-loop-voltage-adjustment-map", 1, corner_map); if (rc) goto done; for (i = 0; i < vreg->fuse_corner_count; i++) { fuse_len = msm8996_mmss_offset_voltage_param[i][0].bit_end + 1 - msm8996_mmss_offset_voltage_param[i][0].bit_start; volt_offset[i] = cpr3_convert_open_loop_voltage_fuse( 0, MSM8996_MMSS_OFFSET_FUSE_STEP_VOLT, fuse->offset_voltage[i], fuse_len); if (volt_offset[i]) cpr3_info(vreg, "fuse_corner[%d] offset=%7d uV\n", i, volt_offset[i]); } for (i = 0; i < vreg->corner_count; i++) { if (corner_map[i] == 0) { continue; } else if (corner_map[i] > vreg->fuse_corner_count) { cpr3_err(vreg, "corner %d mapped to invalid fuse corner: %u\n", i, corner_map[i]); rc = -EINVAL; goto done; } volt_adjust[i] += volt_offset[corner_map[i] - 1]; } done: kfree(corner_map); kfree(volt_offset); return rc; } /** * cpr3_mmss_enforce_inc_quotient_monotonicity() - Ensure that target quotients * increase monotonically from lower to higher corners * @vreg: Pointer to the CPR3 regulator * * Return: 0 on success, errno on failure */ static void cpr3_mmss_enforce_inc_quotient_monotonicity( struct cpr3_regulator *vreg) { int i, j; for (i = 1; i < vreg->corner_count; i++) { for (j = 0; j < CPR3_RO_COUNT; j++) { if (vreg->corner[i].target_quot[j] && vreg->corner[i].target_quot[j] < vreg->corner[i - 1].target_quot[j]) { cpr3_debug(vreg, "corner %d RO%u target quot=%u < corner %d RO%u target quot=%u; overriding: corner %d RO%u target quot=%u\n", i, j, vreg->corner[i].target_quot[j], i - 1, j, vreg->corner[i - 1].target_quot[j], i, j, vreg->corner[i - 1].target_quot[j]); vreg->corner[i].target_quot[j] = vreg->corner[i - 1].target_quot[j]; } } } } /** * cpr3_mmss_enforce_dec_quotient_monotonicity() - Ensure that target quotients * decrease monotonically from higher to lower corners * @vreg: Pointer to the CPR3 regulator * * Return: 0 on success, errno on failure */ static void cpr3_mmss_enforce_dec_quotient_monotonicity( struct cpr3_regulator *vreg) { int i, j; for (i = vreg->corner_count - 2; i >= 0; i--) { for (j = 0; j < CPR3_RO_COUNT; j++) { if (vreg->corner[i].target_quot[j] && vreg->corner[i].target_quot[j] > vreg->corner[i + 1].target_quot[j]) { cpr3_debug(vreg, "corner %d RO%u target quot=%u > corner %d RO%u target quot=%u; overriding: corner %d RO%u target quot=%u\n", i, j, vreg->corner[i].target_quot[j], i + 1, j, vreg->corner[i + 1].target_quot[j], i, j, vreg->corner[i + 1].target_quot[j]); vreg->corner[i].target_quot[j] = vreg->corner[i + 1].target_quot[j]; } } } } /** * _cpr3_mmss_adjust_target_quotients() - adjust the target quotients for each * corner of the regulator according to input adjustment and * scaling arrays * @vreg: Pointer to the CPR3 regulator * @volt_adjust: Pointer to an array of closed-loop voltage adjustments * with units of microvolts. The array must have * vreg->corner_count number of elements. * @ro_scale: Pointer to a flattened 2D array of RO scaling factors. * The array must have an inner dimension of CPR3_RO_COUNT * and an outer dimension of vreg->corner_count * @label: Null terminated string providing a label for the type * of adjustment. * * Return: true if any corners received a positive voltage adjustment (> 0), * else false */ static bool _cpr3_mmss_adjust_target_quotients(struct cpr3_regulator *vreg, const int *volt_adjust, const int *ro_scale, const char *label) { int i, j, quot_adjust; bool is_increasing = false; u32 prev_quot; for (i = 0; i < vreg->corner_count; i++) { for (j = 0; j < CPR3_RO_COUNT; j++) { if (vreg->corner[i].target_quot[j]) { quot_adjust = cpr3_quot_adjustment( ro_scale[i * CPR3_RO_COUNT + j], volt_adjust[i]); if (quot_adjust) { prev_quot = vreg->corner[i]. target_quot[j]; vreg->corner[i].target_quot[j] += quot_adjust; cpr3_debug(vreg, "adjusted corner %d RO%d target quot %s: %u --> %u (%d uV)\n", i, j, label, prev_quot, vreg->corner[i].target_quot[j], volt_adjust[i]); } } } if (volt_adjust[i] > 0) is_increasing = true; } return is_increasing; } /** * cpr3_mmss_adjust_target_quotients() - adjust the target quotients for each * corner according to device tree values * corner according to device tree values and fuse values * @vreg: Pointer to the CPR3 regulator * * Return: 0 on success, errno on failure */ static int cpr3_mmss_adjust_target_quotients(struct cpr3_regulator *vreg) { int i, j, rc, quot_adjust; int i, rc; int *volt_adjust, *ro_scale; bool explicit_adjustment; u32 prev_quot; bool explicit_adjustment, fused_adjustment, is_increasing; explicit_adjustment = of_find_property(vreg->of_node, "qcom,cpr-closed-loop-voltage-adjustment", NULL); fused_adjustment = of_find_property(vreg->of_node, "qcom,cpr-fused-closed-loop-voltage-adjustment-map", NULL); if (!explicit_adjustment && !vreg->aging_allowed) { if (!explicit_adjustment && !fused_adjustment && !vreg->aging_allowed) { /* No adjustment required. */ return 0; } else if (!of_find_property(vreg->of_node, Loading Loading @@ -324,55 +530,29 @@ static int cpr3_mmss_adjust_target_quotients(struct cpr3_regulator *vreg) goto done; } /* * Adjust the target quotients for each corner according to * device tree adjustment values. */ for (i = 0; i < vreg->corner_count; i++) { for (j = 0; j < CPR3_RO_COUNT; j++) { if (vreg->corner[i].target_quot[j]) { quot_adjust = cpr3_quot_adjustment( ro_scale[i * CPR3_RO_COUNT + j], volt_adjust[i]); if (quot_adjust) { prev_quot = vreg->corner[i]. target_quot[j]; vreg->corner[i].target_quot[j] += quot_adjust; cpr3_info(vreg, "adjusted corner %d RO%d target quot: %u --> %u (%d uV)\n", i, j, prev_quot, vreg->corner[i]. target_quot[j], volt_adjust[i]); } } } _cpr3_mmss_adjust_target_quotients(vreg, volt_adjust, ro_scale, "from DT"); cpr3_mmss_enforce_inc_quotient_monotonicity(vreg); } /* * Ensure that target quotients increase monotonically from * lower to higher corners after being adjusted based upon * device tree adjustment values. */ for (i = 1; i < vreg->corner_count; i++) { for (j = 0; j < CPR3_RO_COUNT; j++) { if (vreg->corner[i].target_quot[j] && vreg->corner[i].target_quot[j] < vreg->corner[i - 1].target_quot[j]) { cpr3_info(vreg, "adjusted corner %d RO%u target quot=%u < adjusted corner %d RO%u target quot=%u; overriding: corner %d RO%u target quot=%u\n", i, j, vreg->corner[i].target_quot[j], i - 1, j, vreg->corner[i - 1]. target_quot[j], i, j, vreg->corner[i - 1]. target_quot[j]); vreg->corner[i].target_quot[j] = vreg->corner[i - 1].target_quot[j]; } } if (fused_adjustment) { memset(volt_adjust, 0, sizeof(*volt_adjust) * vreg->corner_count); rc = cpr3_msm8996_mmss_apply_closed_loop_offset_voltages(vreg, volt_adjust); if (rc) { cpr3_err(vreg, "could not apply fused closed-loop voltage reductions, rc=%d\n", rc); goto done; } is_increasing = _cpr3_mmss_adjust_target_quotients(vreg, volt_adjust, ro_scale, "from fuse"); if (is_increasing) cpr3_mmss_enforce_inc_quotient_monotonicity(vreg); else cpr3_mmss_enforce_dec_quotient_monotonicity(vreg); } done: Loading Loading @@ -440,7 +620,7 @@ static int cpr3_msm8996_mmss_calculate_open_loop_voltages( for (i = 1; i < vreg->fuse_corner_count; i++) { if (fuse_volt[i] < fuse_volt[i - 1]) { cpr3_info(vreg, "fuse corner %d voltage=%d uV < fuse corner %d voltage=%d uV; overriding: fuse corner %d voltage=%d\n", cpr3_debug(vreg, "fuse corner %d voltage=%d uV < fuse corner %d voltage=%d uV; overriding: fuse corner %d voltage=%d\n", i, fuse_volt[i], i - 1, fuse_volt[i - 1], i, fuse_volt[i - 1]); fuse_volt[i] = fuse_volt[i - 1]; Loading
drivers/regulator/cpr3-util.c +3 −3 Original line number Diff line number Diff line Loading @@ -1166,7 +1166,7 @@ int cpr3_adjust_fused_open_loop_voltages(struct cpr3_regulator *vreg, if (volt_adjust[i]) { prev_volt = fuse_volt[i]; fuse_volt[i] += volt_adjust[i]; cpr3_info(vreg, "adjusted fuse corner %d open-loop voltage: %d --> %d uV\n", cpr3_debug(vreg, "adjusted fuse corner %d open-loop voltage: %d --> %d uV\n", i, prev_volt, fuse_volt[i]); } } Loading Loading @@ -1214,7 +1214,7 @@ int cpr3_adjust_open_loop_voltages(struct cpr3_regulator *vreg) if (volt_adjust[i]) { prev_volt = vreg->corner[i].open_loop_volt; vreg->corner[i].open_loop_volt += volt_adjust[i]; cpr3_info(vreg, "adjusted corner %d open-loop voltage: %d --> %d uV\n", cpr3_debug(vreg, "adjusted corner %d open-loop voltage: %d --> %d uV\n", i, prev_volt, vreg->corner[i].open_loop_volt); } } Loading @@ -1237,7 +1237,7 @@ int cpr3_adjust_open_loop_voltages(struct cpr3_regulator *vreg) for (i = 1; i < vreg->corner_count; i++) { min_volt = vreg->corner[i - 1].open_loop_volt + volt_diff[i]; if (vreg->corner[i].open_loop_volt < min_volt) { cpr3_info(vreg, "adjusted corner %d open-loop voltage=%d uV < corner %d voltage=%d uV + min diff=%d uV; overriding: corner %d voltage=%d\n", cpr3_debug(vreg, "adjusted corner %d open-loop voltage=%d uV < corner %d voltage=%d uV + min diff=%d uV; overriding: corner %d voltage=%d\n", i, vreg->corner[i].open_loop_volt, i - 1, vreg->corner[i - 1].open_loop_volt, volt_diff[i], i, min_volt); Loading