From 0dc93b15a84a8350081acd29592ef873be1918c9 Mon Sep 17 00:00:00 2001 From: Tirupathi Reddy Date: Thu, 11 Feb 2016 18:00:22 +0530 Subject: [PATCH] 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 --- .../regulator/cpr4-apss-regulator.txt | 28 ++++ drivers/regulator/cpr4-apss-regulator.c | 151 +++++++++++++++++- 2 files changed, 178 insertions(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/regulator/cpr4-apss-regulator.txt b/Documentation/devicetree/bindings/regulator/cpr4-apss-regulator.txt index 61382b562676..4864bef72962 100644 --- a/Documentation/devicetree/bindings/regulator/cpr4-apss-regulator.txt +++ b/Documentation/devicetree/bindings/regulator/cpr4-apss-regulator.txt @@ -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: + 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; diff --git a/drivers/regulator/cpr4-apss-regulator.c b/drivers/regulator/cpr4-apss-regulator.c index c5b66718a7dd..5fd6d0ba1824 100644 --- a/drivers/regulator/cpr4-apss-regulator.c +++ b/drivers/regulator/cpr4-apss-regulator.c @@ -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(