diff options
| author | David Collins <collinsd@codeaurora.org> | 2016-02-24 15:13:09 -0800 |
|---|---|---|
| committer | David Keitel <dkeitel@codeaurora.org> | 2016-03-23 21:21:43 -0700 |
| commit | 2bcf1c1105658a8d4b9e376b1cd5f08ba4e8b52a (patch) | |
| tree | bb4e999cd3aa8a1446c685b317a55f5dbaaf81bf | |
| parent | 003e30879c20e0c79765fbef6106055f175e0a47 (diff) | |
regulator: cpr3-regulator: add support for corners above max fuse corner
Add support for fuse corner to corner mappings in which the
highest fuse corner is mapped to a corner lower than the
highest corner. This can be used to define a corner which has
a constant voltage offset above the highest fuse corner but which
does not impact interpolation for the lower corners.
Change-Id: Ie41522cb06ca81898f58458694067a567f608df0
CRs-Fixed: 981475
Signed-off-by: David Collins <collinsd@codeaurora.org>
| -rw-r--r-- | drivers/regulator/cpr3-hmss-regulator.c | 49 | ||||
| -rw-r--r-- | drivers/regulator/cpr3-regulator.h | 9 | ||||
| -rw-r--r-- | drivers/regulator/cpr3-util.c | 30 |
3 files changed, 54 insertions, 34 deletions
diff --git a/drivers/regulator/cpr3-hmss-regulator.c b/drivers/regulator/cpr3-hmss-regulator.c index f907a71d4a95..73a0529b3dc6 100644 --- a/drivers/regulator/cpr3-hmss-regulator.c +++ b/drivers/regulator/cpr3-hmss-regulator.c @@ -690,19 +690,8 @@ static int cpr3_msm8996_hmss_calculate_open_loop_voltages( goto done; } - /* Determine highest corner mapped to each fuse corner */ - j = vreg->fuse_corner_count - 1; - for (i = vreg->corner_count - 1; i >= 0; i--) { - if (vreg->corner[i].cpr_fuse_corner == j) { - fmax_corner[j] = i; - j--; - } - } - if (j >= 0) { - cpr3_err(vreg, "invalid fuse corner mapping\n"); - rc = -EINVAL; - goto done; - } + for (i = 0; i < vreg->fuse_corner_count; i++) + fmax_corner[i] = vreg->fuse_corner_map[i]; /* * Interpolation is not possible for corners mapped to the lowest fuse @@ -712,6 +701,14 @@ static int cpr3_msm8996_hmss_calculate_open_loop_voltages( vreg->corner[i].open_loop_volt = fuse_volt[0]; /* + * Interpolation is not possible for corners mapped above the highest + * fuse corner so use the fuse corner value directly. + */ + j = vreg->fuse_corner_count - 1; + for (i = fmax_corner[j] + 1; i < vreg->corner_count; i++) + vreg->corner[i].open_loop_volt = fuse_volt[j]; + + /* * Corner LowSVS should be skipped for voltage interpolation * since no fuse exists for it. Instead, the lowest interpolation * should be between MinSVS and SVS. @@ -871,19 +868,8 @@ static int cpr3_msm8996_hmss_calculate_target_quotients( volt_adjust, volt_adjust_fuse, ro_scale); } - /* Determine highest corner mapped to each fuse corner */ - j = vreg->fuse_corner_count - 1; - for (i = vreg->corner_count - 1; i >= 0; i--) { - if (vreg->corner[i].cpr_fuse_corner == j) { - fmax_corner[j] = i; - j--; - } - } - if (j >= 0) { - cpr3_err(vreg, "invalid fuse corner mapping\n"); - rc = -EINVAL; - goto done; - } + for (i = 0; i < vreg->fuse_corner_count; i++) + fmax_corner[i] = vreg->fuse_corner_map[i]; /* * Interpolation is not possible for corners mapped to the lowest fuse @@ -901,6 +887,17 @@ static int cpr3_msm8996_hmss_calculate_target_quotients( vreg->corner[i].target_quot[ro] = quot; /* + * Interpolation is not possible for corners mapped above the highest + * fuse corner so use the fuse corner value directly. + */ + j = vreg->fuse_corner_count - 1; + quot_adjust = cpr3_quot_adjustment(ro_scale[j], volt_adjust_fuse[j]); + quot = fuse->target_quot[j] + quot_adjust; + ro = fuse->ro_sel[j]; + for (i = fmax_corner[j] + 1; i < vreg->corner_count; i++) + vreg->corner[i].target_quot[ro] = quot; + + /* * The LowSVS target quotient is defined as: * (SVS target quotient) - (the unpacked SVS quotient offset) * MinSVS, LowSVS, and SVS fuse corners all share the same RO so it is diff --git a/drivers/regulator/cpr3-regulator.h b/drivers/regulator/cpr3-regulator.h index 150104c11fc1..919034cfb0fd 100644 --- a/drivers/regulator/cpr3-regulator.h +++ b/drivers/regulator/cpr3-regulator.h @@ -140,6 +140,14 @@ struct cpr3_corner { * @fuse_combos_supported: The number of fuse combinations supported by the * device tree configuration for this CPR3 regulator * @fuse_corner_count: Number of corners defined by fuse parameters + * @fuse_corner_map: Array of length fuse_corner_count which specifies the + * highest corner associated with each fuse corner. Note + * that each element must correspond to a valid corner + * and that element values must be strictly increasing. + * Also, it is acceptable for the lowest fuse corner to map + * to a corner other than the lowest. Likewise, it is + * acceptable for the highest fuse corner to map to a + * corner other than the highest. * @fuse_combo_corner_sum: The sum of the corner counts across all fuse combos * @fuse_combo_offset: The device tree property array offset for the selected * fuse combo @@ -219,6 +227,7 @@ struct cpr3_regulator { int fuse_combo; int fuse_combos_supported; int fuse_corner_count; + int *fuse_corner_map; int fuse_combo_corner_sum; int fuse_combo_offset; int speed_bin_corner_sum; diff --git a/drivers/regulator/cpr3-util.c b/drivers/regulator/cpr3-util.c index fcd876e823d0..390436faf91d 100644 --- a/drivers/regulator/cpr3-util.c +++ b/drivers/regulator/cpr3-util.c @@ -445,9 +445,9 @@ int cpr3_parse_corner_array_property(struct cpr3_regulator *vreg, * and qcom,cpr-corner-fmax-map. * * It initializes these CPR3 regulator elements: corner, corner_count, - * fuse_combos_supported, and speed_bins_supported. It initializes these - * elements for each corner: ceiling_volt, floor_volt, proc_freq, and - * cpr_fuse_corner. + * fuse_combos_supported, fuse_corner_map, and speed_bins_supported. It + * initializes these elements for each corner: ceiling_volt, floor_volt, + * proc_freq, and cpr_fuse_corner. * * It requires that the following CPR3 regulator elements be initialized before * being called: fuse_corner_count, fuse_combo, and speed_bin_fuse. @@ -658,11 +658,19 @@ int cpr3_parse_common_corner_data(struct cpr3_regulator *vreg) } } + vreg->fuse_corner_map = devm_kcalloc(ctrl->dev, vreg->fuse_corner_count, + sizeof(*vreg->fuse_corner_map), GFP_KERNEL); + if (!vreg->fuse_corner_map) { + rc = -ENOMEM; + goto free_temp; + } + rc = cpr3_parse_array_property(vreg, "qcom,cpr-corner-fmax-map", vreg->fuse_corner_count, temp); if (rc) goto free_temp; for (i = 0; i < vreg->fuse_corner_count; i++) { + vreg->fuse_corner_map[i] = temp[i] - CPR3_CORNER_OFFSET; if (temp[i] < CPR3_CORNER_OFFSET || temp[i] > vreg->corner_count + CPR3_CORNER_OFFSET) { cpr3_err(vreg, "invalid corner value specified in qcom,cpr-corner-fmax-map: %u\n", @@ -676,13 +684,11 @@ int cpr3_parse_common_corner_data(struct cpr3_regulator *vreg) goto free_temp; } } - if (temp[vreg->fuse_corner_count - 1] != vreg->corner_count) { - cpr3_err(vreg, "highest Fmax corner %u in qcom,cpr-corner-fmax-map does not match highest supported corner %d\n", + if (temp[vreg->fuse_corner_count - 1] != vreg->corner_count) + cpr3_debug(vreg, "Note: highest Fmax corner %u in qcom,cpr-corner-fmax-map does not match highest supported corner %d\n", temp[vreg->fuse_corner_count - 1], vreg->corner_count); - rc = -EINVAL; - goto free_temp; - } + for (i = 0; i < vreg->corner_count; i++) { for (j = 0; j < vreg->fuse_corner_count; j++) { if (i + CPR3_CORNER_OFFSET <= temp[j]) { @@ -690,6 +696,14 @@ int cpr3_parse_common_corner_data(struct cpr3_regulator *vreg) break; } } + if (j == vreg->fuse_corner_count) { + /* + * Handle the case where the highest fuse corner maps + * to a corner below the highest corner. + */ + vreg->corner[i].cpr_fuse_corner + = vreg->fuse_corner_count - 1; + } } if (of_find_property(vreg->of_node, |
