summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Collins <collinsd@codeaurora.org>2016-02-24 15:13:09 -0800
committerDavid Keitel <dkeitel@codeaurora.org>2016-03-23 21:21:43 -0700
commit2bcf1c1105658a8d4b9e376b1cd5f08ba4e8b52a (patch)
treebb4e999cd3aa8a1446c685b317a55f5dbaaf81bf
parent003e30879c20e0c79765fbef6106055f175e0a47 (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.c49
-rw-r--r--drivers/regulator/cpr3-regulator.h9
-rw-r--r--drivers/regulator/cpr3-util.c30
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,