regulator: cpr4: Add miscellaneous fuse based voltage adjustments

Apply voltage adjustments for required voltage corners based on
different values of selected miscellaneous fuse bits. Apply the
adjustments to both open-loop voltages and closed-loop target
quotients.

CRs-Fixed: 982984
Change-Id: Ic45949afc8445d35c245434a7f51e4859a5978ad
Signed-off-by: Tirupathi Reddy <tirupath@codeaurora.org>
This commit is contained in:
Tirupathi Reddy 2016-02-11 18:00:22 +05:30 committed by David Keitel
parent 7f3712ee81
commit 0dc93b15a8
2 changed files with 178 additions and 1 deletions

View file

@ -348,6 +348,27 @@ APSS specific properties:
regardless of the fuse combination and speed bin found
on a given chip.
- qcom,cpr-misc-fuse-voltage-adjustment
Usage: optional
Value type: <prop-encoded-array>
Definition: A grouping of integer tuple lists where each tuple defines
the voltage adjustments in microvolts for each voltage
corner in order from lowest to highest. This adjustment is
applied to both open-loop and closed-loop voltages.
Each tuple list must contain a number of tuples equal to
2 to the power of the number of bits selected for misc
voltage adj fuse definition. For MSMTITANIUM the tuple
list must contain 2 tuples for the 1-bit misc fuse.
Tuples in a list should be specified in ascending order
according to the misc fuse value assuming that the fuse
is treated like an unsigned integer.
The tuple list grouping must contain qcom,cpr-speed-bins
number of tuple lists in which case the lists are matched to
speed bins 1-to-1 or exactly 1 list which is used regardless
of the speed bin found on a given chip.
=======
Example
=======
@ -422,6 +443,13 @@ apc_cpr: cpr4-ctrl@b018000 {
1689600000 1843200000 1958400000
2150400000 2208000000>;
qcom,cpr-misc-fuse-voltage-adjustment =
/* Speed bin 0; misc fuse 0..1 */
< 0 0 0 0
0 0 0 0>,
< 0 0 30000 0
0 0 0 0>;
qcom,allow-voltage-interpolation;
qcom,allow-quotient-interpolation;
qcom,cpr-scaled-open-loop-voltage-as-ceiling;

View file

@ -66,6 +66,7 @@ struct cpr4_msmtitanium_apss_fuses {
u64 cpr_fusing_rev;
u64 boost_cfg;
u64 boost_voltage;
u64 misc;
};
/*
@ -155,6 +156,17 @@ static const struct cpr3_fuse_param msmtitanium_apss_boost_fuse_volt_param[] = {
{},
};
static const struct cpr3_fuse_param msmtitanium_misc_fuse_volt_adj_param[] = {
{36, 54, 54},
{},
};
/*
* The number of possible values for misc fuse is
* 2^(#bits defined for misc fuse)
*/
#define MSMTITANIUM_MISC_FUSE_VAL_COUNT BIT(1)
/*
* Open loop voltage fuse reference voltages in microvolts for MSMTITANIUM
*/
@ -231,6 +243,20 @@ static int cpr4_msmtitanium_apss_read_fuse_data(struct cpr3_regulator *vreg)
}
cpr3_info(vreg, "CPR fusing revision = %llu\n", fuse->cpr_fusing_rev);
rc = cpr3_read_fuse_param(base, msmtitanium_misc_fuse_volt_adj_param,
&fuse->misc);
if (rc) {
cpr3_err(vreg, "Unable to read misc voltage adjustment fuse, rc=%d\n",
rc);
return rc;
}
cpr3_info(vreg, "CPR misc fuse value = %llu\n", fuse->misc);
if (fuse->misc >= MSMTITANIUM_MISC_FUSE_VAL_COUNT) {
cpr3_err(vreg, "CPR misc fuse value = %llu, should be < %lu\n",
fuse->misc, MSMTITANIUM_MISC_FUSE_VAL_COUNT);
return -EINVAL;
}
for (i = 0; i < MSMTITANIUM_APSS_FUSE_CORNERS; i++) {
rc = cpr3_read_fuse_param(base,
msmtitanium_apss_init_voltage_param[i],
@ -324,6 +350,78 @@ static int cpr4_apss_parse_corner_data(struct cpr3_regulator *vreg)
return rc;
}
/**
* cpr4_apss_parse_misc_fuse_voltage_adjustments() - fill an array from a
* portion of the voltage adjustments specified based on
* miscellaneous fuse bits.
* @vreg: Pointer to the CPR3 regulator
* @volt_adjust: Voltage adjustment output data array which must be
* of size vreg->corner_count
*
* cpr3_parse_common_corner_data() must be called for vreg before this function
* is called so that speed bin size elements are initialized.
*
* Two formats are supported for the device tree property:
* 1. Length == tuple_list_size * vreg->corner_count
* (reading begins at index 0)
* 2. Length == tuple_list_size * vreg->speed_bin_corner_sum
* (reading begins at index tuple_list_size * vreg->speed_bin_offset)
*
* Here, tuple_list_size is the number of possible values for misc fuse.
* All other property lengths are treated as errors.
*
* Return: 0 on success, errno on failure
*/
static int cpr4_apss_parse_misc_fuse_voltage_adjustments(
struct cpr3_regulator *vreg, u32 *volt_adjust)
{
struct device_node *node = vreg->of_node;
struct cpr4_msmtitanium_apss_fuses *fuse = vreg->platform_fuses;
int tuple_list_size = MSMTITANIUM_MISC_FUSE_VAL_COUNT;
int i, offset, rc, len = 0;
const char *prop_name = "qcom,cpr-misc-fuse-voltage-adjustment";
if (!of_find_property(node, prop_name, &len)) {
cpr3_err(vreg, "property %s is missing\n", prop_name);
return -EINVAL;
}
if (len == tuple_list_size * vreg->corner_count * sizeof(u32)) {
offset = 0;
} else if (vreg->speed_bin_corner_sum > 0 &&
len == tuple_list_size * vreg->speed_bin_corner_sum
* sizeof(u32)) {
offset = tuple_list_size * vreg->speed_bin_offset
+ fuse->misc * vreg->corner_count;
} else {
if (vreg->speed_bin_corner_sum > 0)
cpr3_err(vreg, "property %s has invalid length=%d, should be %zu or %zu\n",
prop_name, len,
tuple_list_size * vreg->corner_count
* sizeof(u32),
tuple_list_size * vreg->speed_bin_corner_sum
* sizeof(u32));
else
cpr3_err(vreg, "property %s has invalid length=%d, should be %zu\n",
prop_name, len,
tuple_list_size * vreg->corner_count
* sizeof(u32));
return -EINVAL;
}
for (i = 0; i < vreg->corner_count; i++) {
rc = of_property_read_u32_index(node, prop_name, offset + i,
&volt_adjust[i]);
if (rc) {
cpr3_err(vreg, "error reading property %s, rc=%d\n",
prop_name, rc);
return rc;
}
}
return 0;
}
/**
* cpr4_msmtitanium_apss_calculate_open_loop_voltages() - calculate the open-loop
* voltage for each corner of a CPR3 regulator
@ -349,7 +447,7 @@ static int cpr4_msmtitanium_apss_calculate_open_loop_voltages(
int i, j, rc = 0;
bool allow_interpolation;
u64 freq_low, volt_low, freq_high, volt_high;
int *fuse_volt;
int *fuse_volt, *misc_adj_volt;
int *fmax_corner;
fuse_volt = kcalloc(vreg->fuse_corner_count, sizeof(*fuse_volt),
@ -445,8 +543,34 @@ done:
if (rc)
cpr3_err(vreg, "open-loop voltage adjustment failed, rc=%d\n",
rc);
if (of_find_property(node,
"qcom,cpr-misc-fuse-voltage-adjustment",
NULL)) {
misc_adj_volt = kcalloc(vreg->corner_count,
sizeof(*misc_adj_volt), GFP_KERNEL);
if (!misc_adj_volt) {
rc = -ENOMEM;
goto _exit;
}
rc = cpr4_apss_parse_misc_fuse_voltage_adjustments(vreg,
misc_adj_volt);
if (rc) {
cpr3_err(vreg, "qcom,cpr-misc-fuse-voltage-adjustment reading failed, rc=%d\n",
rc);
kfree(misc_adj_volt);
goto _exit;
}
for (i = 0; i < vreg->corner_count; i++)
vreg->corner[i].open_loop_volt
+= misc_adj_volt[i];
kfree(misc_adj_volt);
}
}
_exit:
kfree(fuse_volt);
kfree(fmax_corner);
return rc;
@ -525,6 +649,7 @@ static int cpr4_msmtitanium_apss_calculate_target_quotients(
int i, j, fuse_corner, quot_adjust;
int *fmax_corner;
int *volt_adjust, *volt_adjust_fuse, *ro_scale;
int *voltage_adj_misc;
/* Log fused quotient values for debugging purposes. */
cpr3_info(vreg, "fused LowSVS: quot[%2llu]=%4llu\n",
@ -567,6 +692,30 @@ static int cpr4_msmtitanium_apss_calculate_target_quotients(
goto done;
}
if (of_find_property(vreg->of_node,
"qcom,cpr-misc-fuse-voltage-adjustment", NULL)) {
voltage_adj_misc = kcalloc(vreg->corner_count,
sizeof(*voltage_adj_misc), GFP_KERNEL);
if (!voltage_adj_misc) {
rc = -ENOMEM;
goto done;
}
rc = cpr4_apss_parse_misc_fuse_voltage_adjustments(vreg,
voltage_adj_misc);
if (rc) {
cpr3_err(vreg, "qcom,cpr-misc-fuse-voltage-adjustment reading failed, rc=%d\n",
rc);
kfree(voltage_adj_misc);
goto done;
}
for (i = 0; i < vreg->corner_count; i++)
volt_adjust[i] += voltage_adj_misc[i];
kfree(voltage_adj_misc);
}
if (!allow_interpolation) {
/* Use fused target quotients for lower frequencies. */
return cpr4_msmtitanium_apss_set_no_interpolation_quotients(