Loading Documentation/devicetree/bindings/regulator/cpr3-mmss-regulator.txt +77 −3 Original line number Original line Diff line number Diff line Loading @@ -27,13 +27,13 @@ MMSS specific properties: - compatible - compatible Usage: required Usage: required Value type: <string> Value type: <string> Definition: should be "qcom,cpr3-msm8996-mmss-regulator" Definition: should be one of the following: Definition: should be one of the following: "qcom,cpr3-msm8996-v1-mmss-regulator", "qcom,cpr3-msm8996-v1-mmss-regulator", "qcom,cpr3-msm8996-v2-mmss-regulator", "qcom,cpr3-msm8996-v2-mmss-regulator", "qcom,cpr3-msm8996-v3-mmss-regulator", "qcom,cpr3-msm8996-v3-mmss-regulator", "qcom,cpr3-msm8996-mmss-regulator", "qcom,cpr3-msm8996-mmss-regulator", "qcom,cpr3-msm8996pro-mmss-regulator". "qcom,cpr3-msm8996pro-mmss-regulator", "qcom,cpr4-msmcobalt-mmss-regulator". If the SoC revision is not specified, then it is assumed to If the SoC revision is not specified, then it is assumed to be the most recent revision of MSM8996, i.e. v3. be the most recent revision of MSM8996, i.e. v3. Loading @@ -50,7 +50,39 @@ MMSS specific properties: Value type: <stringlist> Value type: <stringlist> Definition: Clock names. This list must match up 1-to-1 with the clocks Definition: Clock names. This list must match up 1-to-1 with the clocks specified in the 'clocks' property. "core_clk", "iface_clk", specified in the 'clocks' property. "core_clk", "iface_clk", and "bus_clk" must be specified. and "bus_clk" must be specified. Note that "iface_clk" is not required for devices with compatible = "qcom,cpr4-msmcobalt-mmss-regulator". - qcom,cpr-temp-point-map Usage: Required if qcom,corner-allow-temp-adjustment is specified for at least one of the CPR3 regulators. Value type: <prop-encoded-array> Definition: The temperature points in decidegrees Celsius which indicate the range of temperature bands supported. If t1, t2, and t3 are the temperature points, then the temperature bands are: (-inf, t1], (t1, t2], (t2, t3], and (t3, inf). 1 to 3 temperature points should be specified. - qcom,cpr-initial-temp-band Usage: Required if qcom,cpr-temp-point-map is specified. Value type: <u32> Definition: The initial temp band considering 0-based index at which the baseline target quotients are derived and fused. Supported values: 0 to number of elements in qcom,cpr-temp-point-map. - qcom,cpr-step-quot-fixed Usage: Optional for controllers with compatible = "qcom,cpr4-msmcobalt-mmss-regulator"; unsupported for all others. Value type: <u32> Definition: Fixed step quotient value used by controller for applying the SDELTA margin adjustments on the programmed target quotient values. The step quotient is the number of additional ring oscillator ticks observed for each qcom,voltage-step increase in vdd-supply output voltage. Supported values: 0 - 63. ================================================= ================================================= Second Level Nodes - CPR Threads for a Controller Second Level Nodes - CPR Threads for a Controller Loading Loading @@ -163,6 +195,48 @@ MMSS specific properties: Values 1 to qcom,cpr-fuse-corners denote the specific fuse Values 1 to qcom,cpr-fuse-corners denote the specific fuse corner that should be used by a given voltage corner. corner that should be used by a given voltage corner. - qcom,corner-allow-temp-adjustment Usage: Optional for controllers with compatible = "qcom,cpr4-msmcobalt-mmss-regulator"; unsupported for all others. Value type: <prop-encoded-array> Definition: A list of integer tuples which each define the CPR temperature adjustment feature enable state for each voltage corner in order from lowest to highest. Each element in the tuple should be either 0 (temperature adjustment not allowed) or 1 (temperature adjustment allowed). 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-bin-corners property. A single tuple may only be specified if all of the corner counts in qcom,cpr-corners are the same. - qcom,cpr-cornerX-temp-core-voltage-adjustment Usage: Required if qcom,corner-allow-temp-adjustment is specified for this CPR3 regulator. Value type: <prop-encoded-array> Definition: A list of integer tuples for cornerX. The possible values for X are 1 to the max value specified in qcom,cpr-corners. Each tuple defines the temperature based voltage adjustment in microvolts for each temperature band from lowest to highest. Each tuple must have a number of elements equal to (the number of elements in qcom,cpr-ctrl-temp-point-map + 1) The tuple 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 list which is used regardless of the fuse combination and speed bin found on a given chip. Note that the qcom,cpr-closed-loop-voltage-fuse-adjustment property is not 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 meaningful for MMSS CPR3 regulator nodes since target quotients are not defined in fuses. in fuses. Loading drivers/regulator/cpr3-mmss-regulator.c +218 −25 Original line number Original line Diff line number Diff line Loading @@ -71,6 +71,9 @@ struct cpr3_msm8996_mmss_fuses { */ */ #define CPR3_MSM8996PRO_MMSS_FUSE_COMBO_COUNT 16 #define CPR3_MSM8996PRO_MMSS_FUSE_COMBO_COUNT 16 /* Fuse combos 0 - 7 map to CPR fusing revision 0 - 7 */ #define CPR3_MSMCOBALT_MMSS_FUSE_COMBO_COUNT 8 /* /* * MSM8996 MMSS fuse parameter locations: * MSM8996 MMSS fuse parameter locations: * * Loading Loading @@ -121,7 +124,42 @@ static const struct cpr3_fuse_param msm8996pro_mmss_speed_bin_param[] = { {}, {}, }; }; /* MSMCOBALT MMSS fuse parameter locations: */ static const struct cpr3_fuse_param msmcobalt_mmss_init_voltage_param[MSM8996_MMSS_FUSE_CORNERS][2] = { {{65, 39, 43}, {} }, {{65, 34, 38}, {} }, {{65, 29, 33}, {} }, {{65, 24, 28}, {} }, }; static const struct cpr3_fuse_param msmcobalt_cpr_fusing_rev_param[] = { {39, 48, 50}, {}, }; static const struct cpr3_fuse_param msmcobalt_cpr_limitation_param[] = { {41, 46, 47}, {}, }; static const struct cpr3_fuse_param msmcobalt_mmss_aging_init_quot_diff_param[] = { {65, 60, 63}, {66, 0, 3}, {}, }; static const struct cpr3_fuse_param msmcobalt_mmss_offset_voltage_param[MSM8996_MMSS_FUSE_CORNERS][2] = { {{65, 56, 59}, {} }, {{65, 52, 55}, {} }, {{65, 48, 51}, {} }, {{65, 44, 47}, {} }, }; #define MSM8996PRO_SOC_ID 4 #define MSM8996PRO_SOC_ID 4 #define MSMCOBALT_SOC_ID 5 /* /* * Some initial msm8996 parts cannot be used in a meaningful way by software. * Some initial msm8996 parts cannot be used in a meaningful way by software. Loading Loading @@ -152,6 +190,13 @@ static const int msm8996pro_mmss_fuse_ref_volt[MSM8996_MMSS_FUSE_CORNERS] = { 1065000, 1065000, }; }; static const int msmcobalt_mmss_fuse_ref_volt[MSM8996_MMSS_FUSE_CORNERS] = { 632000, 768000, 896000, 1032000, }; #define MSM8996_MMSS_FUSE_STEP_VOLT 10000 #define MSM8996_MMSS_FUSE_STEP_VOLT 10000 #define MSM8996_MMSS_OFFSET_FUSE_STEP_VOLT 10000 #define MSM8996_MMSS_OFFSET_FUSE_STEP_VOLT 10000 #define MSM8996_MMSS_VOLTAGE_FUSE_SIZE 5 #define MSM8996_MMSS_VOLTAGE_FUSE_SIZE 5 Loading @@ -165,6 +210,18 @@ static const int msm8996pro_mmss_fuse_ref_volt[MSM8996_MMSS_FUSE_CORNERS] = { #define MSM8996_MMSS_AGING_SENSOR_ID 29 #define MSM8996_MMSS_AGING_SENSOR_ID 29 #define MSM8996_MMSS_AGING_BYPASS_MASK0 (GENMASK(23, 0)) #define MSM8996_MMSS_AGING_BYPASS_MASK0 (GENMASK(23, 0)) #define MSMCOBALT_MMSS_AGING_INIT_QUOT_DIFF_SCALE 1 #define MSMCOBALT_MMSS_AGING_INIT_QUOT_DIFF_SIZE 8 #define MSMCOBALT_MMSS_CPR_SENSOR_COUNT 35 #define MSMCOBALT_MMSS_AGING_SENSOR_ID 17 #define MSMCOBALT_MMSS_AGING_BYPASS_MASK0 0 #define MSMCOBALT_MMSS_MAX_TEMP_POINTS 3 #define MSMCOBALT_MMSS_TEMP_SENSOR_ID_START 12 #define MSMCOBALT_MMSS_TEMP_SENSOR_ID_END 13 /** /** * cpr3_msm8996_mmss_read_fuse_data() - load MMSS specific fuse parameter values * cpr3_msm8996_mmss_read_fuse_data() - load MMSS specific fuse parameter values * @vreg: Pointer to the CPR3 regulator * @vreg: Pointer to the CPR3 regulator Loading Loading @@ -196,7 +253,10 @@ static int cpr3_msm8996_mmss_read_fuse_data(struct cpr3_regulator *vreg) cpr3_info(vreg, "speed bin = %llu\n", fuse->speed_bin); cpr3_info(vreg, "speed bin = %llu\n", fuse->speed_bin); } } rc = cpr3_read_fuse_param(base, msm8996_cpr_fusing_rev_param, rc = cpr3_read_fuse_param(base, vreg->thread->ctrl->soc_revision == MSMCOBALT_SOC_ID ? msmcobalt_cpr_fusing_rev_param : msm8996_cpr_fusing_rev_param, &fuse->cpr_fusing_rev); &fuse->cpr_fusing_rev); if (rc) { if (rc) { cpr3_err(vreg, "Unable to read CPR fusing revision fuse, rc=%d\n", cpr3_err(vreg, "Unable to read CPR fusing revision fuse, rc=%d\n", Loading @@ -205,7 +265,10 @@ static int cpr3_msm8996_mmss_read_fuse_data(struct cpr3_regulator *vreg) } } cpr3_info(vreg, "CPR fusing revision = %llu\n", fuse->cpr_fusing_rev); cpr3_info(vreg, "CPR fusing revision = %llu\n", fuse->cpr_fusing_rev); rc = cpr3_read_fuse_param(base, msm8996_cpr_limitation_param, rc = cpr3_read_fuse_param(base, vreg->thread->ctrl->soc_revision == MSMCOBALT_SOC_ID ? msmcobalt_cpr_limitation_param : msm8996_cpr_limitation_param, &fuse->limitation); &fuse->limitation); if (rc) { if (rc) { cpr3_err(vreg, "Unable to read CPR limitation fuse, rc=%d\n", cpr3_err(vreg, "Unable to read CPR limitation fuse, rc=%d\n", Loading @@ -218,7 +281,10 @@ static int cpr3_msm8996_mmss_read_fuse_data(struct cpr3_regulator *vreg) == MSM8996_CPR_LIMITATION_NO_CPR_OR_INTERPOLATION == MSM8996_CPR_LIMITATION_NO_CPR_OR_INTERPOLATION ? "CPR disabled and no interpolation" : "none"); ? "CPR disabled and no interpolation" : "none"); rc = cpr3_read_fuse_param(base, msm8996_mmss_aging_init_quot_diff_param, rc = cpr3_read_fuse_param(base, vreg->thread->ctrl->soc_revision == MSMCOBALT_SOC_ID ? msmcobalt_mmss_aging_init_quot_diff_param : msm8996_mmss_aging_init_quot_diff_param, &fuse->aging_init_quot_diff); &fuse->aging_init_quot_diff); if (rc) { if (rc) { cpr3_err(vreg, "Unable to read aging initial quotient difference fuse, rc=%d\n", cpr3_err(vreg, "Unable to read aging initial quotient difference fuse, rc=%d\n", Loading @@ -228,7 +294,9 @@ static int cpr3_msm8996_mmss_read_fuse_data(struct cpr3_regulator *vreg) for (i = 0; i < MSM8996_MMSS_FUSE_CORNERS; i++) { for (i = 0; i < MSM8996_MMSS_FUSE_CORNERS; i++) { rc = cpr3_read_fuse_param(base, rc = cpr3_read_fuse_param(base, msm8996_mmss_init_voltage_param[i], vreg->thread->ctrl->soc_revision == MSMCOBALT_SOC_ID ? msmcobalt_mmss_init_voltage_param[i] : msm8996_mmss_init_voltage_param[i], &fuse->init_voltage[i]); &fuse->init_voltage[i]); if (rc) { if (rc) { cpr3_err(vreg, "Unable to read fuse-corner %d initial voltage fuse, rc=%d\n", cpr3_err(vreg, "Unable to read fuse-corner %d initial voltage fuse, rc=%d\n", Loading @@ -237,7 +305,9 @@ static int cpr3_msm8996_mmss_read_fuse_data(struct cpr3_regulator *vreg) } } rc = cpr3_read_fuse_param(base, rc = cpr3_read_fuse_param(base, msm8996_mmss_offset_voltage_param[i], vreg->thread->ctrl->soc_revision == MSMCOBALT_SOC_ID ? msmcobalt_mmss_offset_voltage_param[i] : msm8996_mmss_offset_voltage_param[i], &fuse->offset_voltage[i]); &fuse->offset_voltage[i]); if (rc) { if (rc) { cpr3_err(vreg, "Unable to read fuse-corner %d offset voltage fuse, rc=%d\n", cpr3_err(vreg, "Unable to read fuse-corner %d offset voltage fuse, rc=%d\n", Loading @@ -246,7 +316,10 @@ static int cpr3_msm8996_mmss_read_fuse_data(struct cpr3_regulator *vreg) } } } } if (vreg->thread->ctrl->soc_revision == MSM8996PRO_SOC_ID) { if (vreg->thread->ctrl->soc_revision == MSMCOBALT_SOC_ID) { combo_max = CPR3_MSMCOBALT_MMSS_FUSE_COMBO_COUNT; vreg->fuse_combo = fuse->cpr_fusing_rev; } else if (vreg->thread->ctrl->soc_revision == MSM8996PRO_SOC_ID) { combo_max = CPR3_MSM8996PRO_MMSS_FUSE_COMBO_COUNT; combo_max = CPR3_MSM8996PRO_MMSS_FUSE_COMBO_COUNT; vreg->fuse_combo = fuse->cpr_fusing_rev + 8 * fuse->speed_bin; vreg->fuse_combo = fuse->cpr_fusing_rev + 8 * fuse->speed_bin; } else { } else { Loading Loading @@ -322,6 +395,7 @@ static int cpr3_msm8996_mmss_apply_closed_loop_offset_voltages( struct cpr3_regulator *vreg, int *volt_adjust) struct cpr3_regulator *vreg, int *volt_adjust) { { struct cpr3_msm8996_mmss_fuses *fuse = vreg->platform_fuses; struct cpr3_msm8996_mmss_fuses *fuse = vreg->platform_fuses; const struct cpr3_fuse_param (*offset_param)[2]; u32 *corner_map; u32 *corner_map; int *volt_offset; int *volt_offset; int rc = 0, i, fuse_len; int rc = 0, i, fuse_len; Loading @@ -347,9 +421,12 @@ static int cpr3_msm8996_mmss_apply_closed_loop_offset_voltages( if (rc) if (rc) goto done; goto done; offset_param = vreg->thread->ctrl->soc_revision == MSMCOBALT_SOC_ID ? msmcobalt_mmss_offset_voltage_param : msm8996_mmss_offset_voltage_param; for (i = 0; i < vreg->fuse_corner_count; i++) { for (i = 0; i < vreg->fuse_corner_count; i++) { fuse_len = msm8996_mmss_offset_voltage_param[i][0].bit_end + 1 fuse_len = offset_param[i][0].bit_end + 1 - msm8996_mmss_offset_voltage_param[i][0].bit_start; - offset_param[i][0].bit_start; volt_offset[i] = cpr3_convert_open_loop_voltage_fuse( volt_offset[i] = cpr3_convert_open_loop_voltage_fuse( 0, MSM8996_MMSS_OFFSET_FUSE_STEP_VOLT, 0, MSM8996_MMSS_OFFSET_FUSE_STEP_VOLT, fuse->offset_voltage[i], fuse_len); fuse->offset_voltage[i], fuse_len); Loading Loading @@ -423,7 +500,7 @@ static void cpr3_mmss_enforce_dec_quotient_monotonicity( for (i = vreg->corner_count - 2; i >= 0; i--) { for (i = vreg->corner_count - 2; i >= 0; i--) { for (j = 0; j < CPR3_RO_COUNT; j++) { for (j = 0; j < CPR3_RO_COUNT; j++) { if (vreg->corner[i].target_quot[j] if (vreg->corner[i + 1].target_quot[j] && vreg->corner[i].target_quot[j] && vreg->corner[i].target_quot[j] > vreg->corner[i + 1].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", 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", Loading Loading @@ -617,7 +694,9 @@ static int cpr3_msm8996_mmss_calculate_open_loop_voltages( goto done; goto done; } } if (vreg->thread->ctrl->soc_revision == MSM8996PRO_SOC_ID) if (vreg->thread->ctrl->soc_revision == MSMCOBALT_SOC_ID) ref_volt = msmcobalt_mmss_fuse_ref_volt; else if (vreg->thread->ctrl->soc_revision == MSM8996PRO_SOC_ID) ref_volt = msm8996pro_mmss_fuse_ref_volt; ref_volt = msm8996pro_mmss_fuse_ref_volt; else else ref_volt = msm8996_mmss_fuse_ref_volt; ref_volt = msm8996_mmss_fuse_ref_volt; Loading Loading @@ -773,15 +852,27 @@ static int cpr3_mmss_init_aging(struct cpr3_controller *ctrl) if (!ctrl->aging_sensor) if (!ctrl->aging_sensor) return -ENOMEM; return -ENOMEM; ctrl->aging_sensor->sensor_id = MSM8996_MMSS_AGING_SENSOR_ID; ctrl->aging_sensor->bypass_mask[0] = MSM8996_MMSS_AGING_BYPASS_MASK0; ctrl->aging_sensor->ro_scale = aging_ro_scale; ctrl->aging_sensor->ro_scale = aging_ro_scale; if (vreg->thread->ctrl->soc_revision == MSMCOBALT_SOC_ID) { ctrl->aging_sensor->sensor_id = MSMCOBALT_MMSS_AGING_SENSOR_ID; ctrl->aging_sensor->bypass_mask[0] = MSMCOBALT_MMSS_AGING_BYPASS_MASK0; ctrl->aging_sensor->init_quot_diff = cpr3_convert_open_loop_voltage_fuse(0, MSMCOBALT_MMSS_AGING_INIT_QUOT_DIFF_SCALE, fuse->aging_init_quot_diff, MSMCOBALT_MMSS_AGING_INIT_QUOT_DIFF_SIZE); } else { ctrl->aging_sensor->sensor_id = MSM8996_MMSS_AGING_SENSOR_ID; ctrl->aging_sensor->bypass_mask[0] = MSM8996_MMSS_AGING_BYPASS_MASK0; ctrl->aging_sensor->init_quot_diff ctrl->aging_sensor->init_quot_diff = cpr3_convert_open_loop_voltage_fuse(0, = cpr3_convert_open_loop_voltage_fuse(0, MSM8996_MMSS_AGING_INIT_QUOT_DIFF_SCALE, MSM8996_MMSS_AGING_INIT_QUOT_DIFF_SCALE, fuse->aging_init_quot_diff, fuse->aging_init_quot_diff, MSM8996_MMSS_AGING_INIT_QUOT_DIFF_SIZE); MSM8996_MMSS_AGING_INIT_QUOT_DIFF_SIZE); } cpr3_debug(ctrl, "sensor %u aging init quotient diff = %d, aging RO scale = %u QUOT/V\n", cpr3_debug(ctrl, "sensor %u aging init quotient diff = %d, aging RO scale = %u QUOT/V\n", ctrl->aging_sensor->sensor_id, ctrl->aging_sensor->sensor_id, Loading Loading @@ -862,11 +953,83 @@ static int cpr3_mmss_init_thread(struct cpr3_thread *thread) return rc; return rc; } } if (thread->ctrl->soc_revision == MSMCOBALT_SOC_ID) { rc = cpr4_parse_core_count_temp_voltage_adj(vreg, false); if (rc) { cpr3_err(vreg, "unable to parse temperature based voltage adjustments, rc=%d\n", rc); return rc; } } cpr3_mmss_print_settings(vreg); cpr3_mmss_print_settings(vreg); return 0; return 0; } } /** * cpr4_mmss_parse_temp_adj_properties() - parse temperature based * adjustment properties from device tree * @ctrl: Pointer to the CPR3 controller * * Return: 0 on success, errno on failure */ static int cpr4_mmss_parse_temp_adj_properties(struct cpr3_controller *ctrl) { struct device_node *of_node = ctrl->dev->of_node; int rc, len, temp_point_count; if (!of_find_property(of_node, "qcom,cpr-temp-point-map", &len)) return 0; temp_point_count = len / sizeof(u32); if (temp_point_count <= 0 || temp_point_count > MSMCOBALT_MMSS_MAX_TEMP_POINTS) { cpr3_err(ctrl, "invalid number of temperature points %d > %d (max)\n", temp_point_count, MSMCOBALT_MMSS_MAX_TEMP_POINTS); return -EINVAL; } ctrl->temp_points = devm_kcalloc(ctrl->dev, temp_point_count, sizeof(*ctrl->temp_points), GFP_KERNEL); if (!ctrl->temp_points) return -ENOMEM; rc = of_property_read_u32_array(of_node, "qcom,cpr-temp-point-map", ctrl->temp_points, temp_point_count); if (rc) { cpr3_err(ctrl, "error reading property qcom,cpr-temp-point-map, rc=%d\n", rc); return rc; } /* * If t1, t2, and t3 are the temperature points, then the temperature * bands are: (-inf, t1], (t1, t2], (t2, t3], and (t3, inf). */ ctrl->temp_band_count = temp_point_count + 1; rc = of_property_read_u32(of_node, "qcom,cpr-initial-temp-band", &ctrl->initial_temp_band); if (rc) { cpr3_err(ctrl, "error reading qcom,cpr-initial-temp-band, rc=%d\n", rc); return rc; } if (ctrl->initial_temp_band >= ctrl->temp_band_count) { cpr3_err(ctrl, "Initial temperature band value %d should be in range [0 - %d]\n", ctrl->initial_temp_band, ctrl->temp_band_count - 1); return -EINVAL; } ctrl->temp_sensor_id_start = MSMCOBALT_MMSS_TEMP_SENSOR_ID_START; ctrl->temp_sensor_id_end = MSMCOBALT_MMSS_TEMP_SENSOR_ID_END; ctrl->allow_temp_adj = true; return rc; } /** /** * cpr3_mmss_init_controller() - perform MMSS CPR3 controller specific * cpr3_mmss_init_controller() - perform MMSS CPR3 controller specific * initializations * initializations Loading @@ -886,7 +1049,15 @@ static int cpr3_mmss_init_controller(struct cpr3_controller *ctrl) return rc; return rc; } } ctrl->sensor_count = MSM8996_MMSS_CPR_SENSOR_COUNT; if (ctrl->soc_revision == MSMCOBALT_SOC_ID) { rc = cpr4_mmss_parse_temp_adj_properties(ctrl); if (rc) return rc; } ctrl->sensor_count = ctrl->soc_revision == MSMCOBALT_SOC_ID ? MSMCOBALT_MMSS_CPR_SENSOR_COUNT : MSM8996_MMSS_CPR_SENSOR_COUNT; /* /* * MMSS only has one thread (0) so the zeroed array does not need * MMSS only has one thread (0) so the zeroed array does not need Loading @@ -898,16 +1069,34 @@ static int cpr3_mmss_init_controller(struct cpr3_controller *ctrl) return -ENOMEM; return -ENOMEM; ctrl->cpr_clock_rate = MSM8996_MMSS_CPR_CLOCK_RATE; ctrl->cpr_clock_rate = MSM8996_MMSS_CPR_CLOCK_RATE; ctrl->ctrl_type = CPR_CTRL_TYPE_CPR3; ctrl->ctrl_type = ctrl->soc_revision == MSMCOBALT_SOC_ID ? CPR_CTRL_TYPE_CPR4 : CPR_CTRL_TYPE_CPR3; if (ctrl->ctrl_type == CPR_CTRL_TYPE_CPR4) { /* * Use fixed step quotient if specified otherwise use dynamic * calculated per RO step quotient */ of_property_read_u32(ctrl->dev->of_node, "qcom,cpr-step-quot-fixed", &ctrl->step_quot_fixed); ctrl->use_dynamic_step_quot = !ctrl->step_quot_fixed; } ctrl->iface_clk = devm_clk_get(ctrl->dev, "iface_clk"); ctrl->iface_clk = devm_clk_get(ctrl->dev, "iface_clk"); if (IS_ERR(ctrl->iface_clk)) { if (IS_ERR(ctrl->iface_clk)) { rc = PTR_ERR(ctrl->iface_clk); rc = PTR_ERR(ctrl->iface_clk); if (rc != -EPROBE_DEFER) if (ctrl->soc_revision == MSMCOBALT_SOC_ID) { cpr3_err(ctrl, "unable request interface clock, rc=%d\n", /* iface_clk is optional for msmcobalt */ ctrl->iface_clk = NULL; } else if (rc == -EPROBE_DEFER) { return rc; } else { cpr3_err(ctrl, "unable to request interface clock, rc=%d\n", rc); rc); return rc; return rc; } } } ctrl->bus_clk = devm_clk_get(ctrl->dev, "bus_clk"); ctrl->bus_clk = devm_clk_get(ctrl->dev, "bus_clk"); if (IS_ERR(ctrl->bus_clk)) { if (IS_ERR(ctrl->bus_clk)) { Loading Loading @@ -958,6 +1147,10 @@ static struct of_device_id cpr_regulator_match_table[] = { .compatible = "qcom,cpr3-msm8996pro-mmss-regulator", .compatible = "qcom,cpr3-msm8996pro-mmss-regulator", .data = (void *)(uintptr_t)MSM8996PRO_SOC_ID, .data = (void *)(uintptr_t)MSM8996PRO_SOC_ID, }, }, { .compatible = "qcom,cpr4-msmcobalt-mmss-regulator", .data = (void *)(uintptr_t)MSMCOBALT_SOC_ID, }, {} {} }; }; Loading drivers/regulator/cpr3-regulator.c +118 −74 Original line number Original line Diff line number Diff line Loading @@ -698,6 +698,7 @@ static int cpr3_regulator_init_cpr4(struct cpr3_controller *ctrl) int thread_id = 0; int thread_id = 0; u64 temp; u64 temp; if (ctrl->supports_hw_closed_loop) { if (ctrl->saw_use_unit_mV) if (ctrl->saw_use_unit_mV) pmic_step_size = ctrl->step_volt / 1000; pmic_step_size = ctrl->step_volt / 1000; cpr3_masked_write(ctrl, CPR4_REG_MARGIN_ADJ_CTL, cpr3_masked_write(ctrl, CPR4_REG_MARGIN_ADJ_CTL, Loading @@ -716,8 +717,8 @@ static int cpr3_regulator_init_cpr4(struct cpr3_controller *ctrl) << CPR4_SAW_ERROR_STEP_LIMIT_UP_SHIFT)); << CPR4_SAW_ERROR_STEP_LIMIT_UP_SHIFT)); /* /* * Enable thread aggregation regardless of which threads are enabled * Enable thread aggregation regardless of which threads are * or disabled. * enabled or disabled. */ */ cpr3_masked_write(ctrl, CPR4_REG_CPR_TIMER_CLAMP, cpr3_masked_write(ctrl, CPR4_REG_CPR_TIMER_CLAMP, CPR4_CPR_TIMER_CLAMP_THREAD_AGGREGATION_EN, CPR4_CPR_TIMER_CLAMP_THREAD_AGGREGATION_EN, Loading @@ -741,13 +742,15 @@ static int cpr3_regulator_init_cpr4(struct cpr3_controller *ctrl) case 1: case 1: /* Disable unused thread */ /* Disable unused thread */ thread_id = ctrl->thread[0].thread_id ? 0 : 1; thread_id = ctrl->thread[0].thread_id ? 0 : 1; cpr3_masked_write(ctrl, CPR4_REG_CPR_MASK_THREAD(thread_id), cpr3_masked_write(ctrl, CPR4_REG_CPR_MASK_THREAD(thread_id), CPR4_CPR_MASK_THREAD_DISABLE_THREAD CPR4_CPR_MASK_THREAD_DISABLE_THREAD | CPR4_CPR_MASK_THREAD_RO_MASK4THREAD_MASK, | CPR4_CPR_MASK_THREAD_RO_MASK4THREAD_MASK, CPR4_CPR_MASK_THREAD_DISABLE_THREAD CPR4_CPR_MASK_THREAD_DISABLE_THREAD | CPR4_CPR_MASK_THREAD_RO_MASK4THREAD_MASK); | CPR4_CPR_MASK_THREAD_RO_MASK4THREAD_MASK); break; break; } } } if (!ctrl->allow_core_count_adj && !ctrl->allow_temp_adj if (!ctrl->allow_core_count_adj && !ctrl->allow_temp_adj && !ctrl->allow_boost) { && !ctrl->allow_boost) { Loading @@ -758,6 +761,7 @@ static int cpr3_regulator_init_cpr4(struct cpr3_controller *ctrl) return rc; return rc; } } if (ctrl->supports_hw_closed_loop) cpr3_masked_write(ctrl, CPR4_REG_MARGIN_ADJ_CTL, cpr3_masked_write(ctrl, CPR4_REG_MARGIN_ADJ_CTL, CPR4_MARGIN_ADJ_CTL_TIMER_SETTLE_VOLTAGE_EN, CPR4_MARGIN_ADJ_CTL_TIMER_SETTLE_VOLTAGE_EN, CPR4_MARGIN_ADJ_CTL_TIMER_SETTLE_VOLTAGE_EN); CPR4_MARGIN_ADJ_CTL_TIMER_SETTLE_VOLTAGE_EN); Loading Loading @@ -1001,8 +1005,10 @@ static int cpr3_controller_program_sdelta(struct cpr3_controller *ctrl) max_core_count << CPR4_MARGIN_ADJ_CTL_MAX_NUM_CORES_SHIFT max_core_count << CPR4_MARGIN_ADJ_CTL_MAX_NUM_CORES_SHIFT | ((sdelta->allow_core_count_adj || sdelta->allow_boost) | ((sdelta->allow_core_count_adj || sdelta->allow_boost) ? CPR4_MARGIN_ADJ_CTL_CORE_ADJ_EN : 0) ? CPR4_MARGIN_ADJ_CTL_CORE_ADJ_EN : 0) | (sdelta->allow_temp_adj ? CPR4_MARGIN_ADJ_CTL_TEMP_ADJ_EN : 0) | ((sdelta->allow_temp_adj && ctrl->supports_hw_closed_loop) | ((ctrl->use_hw_closed_loop && !sdelta->allow_boost) ? CPR4_MARGIN_ADJ_CTL_TEMP_ADJ_EN : 0) | (((ctrl->use_hw_closed_loop && !sdelta->allow_boost) || !ctrl->supports_hw_closed_loop) ? CPR4_MARGIN_ADJ_CTL_KV_MARGIN_ADJ_EN : 0) ? CPR4_MARGIN_ADJ_CTL_KV_MARGIN_ADJ_EN : 0) | (sdelta->allow_boost | (sdelta->allow_boost ? CPR4_MARGIN_ADJ_CTL_BOOST_EN : 0)); ? CPR4_MARGIN_ADJ_CTL_BOOST_EN : 0)); Loading Loading @@ -1469,7 +1475,6 @@ static int cpr3_regulator_init_ctrl(struct cpr3_controller *ctrl) cpr3_debug(ctrl, "PD_THROTTLE=0x%08X\n", cpr3_debug(ctrl, "PD_THROTTLE=0x%08X\n", ctrl->proc_clock_throttle); ctrl->proc_clock_throttle); } } } if ((ctrl->use_hw_closed_loop || if ((ctrl->use_hw_closed_loop || ctrl->ctrl_type == CPR_CTRL_TYPE_CPR4) && ctrl->ctrl_type == CPR_CTRL_TYPE_CPR4) && Loading @@ -1482,7 +1487,8 @@ static int cpr3_regulator_init_ctrl(struct cpr3_controller *ctrl) } } if (ctrl->ctrl_type == CPR_CTRL_TYPE_CPR3) { if (ctrl->ctrl_type == CPR_CTRL_TYPE_CPR3) { rc = msm_spm_avs_enable_irq(0, MSM_SPM_AVS_IRQ_MAX); rc = msm_spm_avs_enable_irq(0, MSM_SPM_AVS_IRQ_MAX); if (rc) { if (rc) { cpr3_err(ctrl, "could not enable max IRQ, rc=%d\n", cpr3_err(ctrl, "could not enable max IRQ, rc=%d\n", rc); rc); Loading @@ -1490,6 +1496,7 @@ static int cpr3_regulator_init_ctrl(struct cpr3_controller *ctrl) } } } } } } } if (ctrl->ctrl_type == CPR_CTRL_TYPE_CPR4) { if (ctrl->ctrl_type == CPR_CTRL_TYPE_CPR4) { rc = cpr3_regulator_init_cpr4(ctrl); rc = cpr3_regulator_init_cpr4(ctrl); Loading Loading @@ -2531,8 +2538,8 @@ static int cpr3_regulator_scale_vdd_voltage(struct cpr3_controller *ctrl, return rc; return rc; } } if (new_volt == last_volt && if (new_volt == last_volt && ctrl->supports_hw_closed_loop ctrl->ctrl_type == CPR_CTRL_TYPE_CPR4) { && ctrl->ctrl_type == CPR_CTRL_TYPE_CPR4) { /* /* * CPR4 features enforce voltage reprogramming when the last * CPR4 features enforce voltage reprogramming when the last * set voltage and new set voltage are same. This way, we can * set voltage and new set voltage are same. This way, we can Loading Loading @@ -2637,6 +2644,36 @@ static int cpr3_regulator_get_dynamic_floor_volt(struct cpr3_controller *ctrl, return dynamic_floor_volt; return dynamic_floor_volt; } } /** * cpr3_regulator_max_sdelta_diff() - returns the maximum voltage difference in * microvolts that can result from different operating conditions * for the specified sdelta struct * @sdelta: Pointer to the sdelta structure * @step_volt: Step size in microvolts between available set * points of the VDD supply. * * Return: voltage difference between the highest and lowest adjustments if * sdelta and sdelta->table are valid, else 0. */ static int cpr3_regulator_max_sdelta_diff(const struct cpr4_sdelta *sdelta, int step_volt) { int i, j, index, sdelta_min = INT_MAX, sdelta_max = INT_MIN; if (!sdelta || !sdelta->table) return 0; for (i = 0; i < sdelta->max_core_count; i++) { for (j = 0; j < sdelta->temp_band_count; j++) { index = i * sdelta->temp_band_count + j; sdelta_min = min(sdelta_min, sdelta->table[index]); sdelta_max = max(sdelta_max, sdelta->table[index]); } } return (sdelta_max - sdelta_min) * step_volt; } /** /** * cpr3_regulator_aggregate_sdelta() - check open-loop voltages of current * cpr3_regulator_aggregate_sdelta() - check open-loop voltages of current * aggregated corner and current corner of a given regulator * aggregated corner and current corner of a given regulator Loading Loading @@ -3080,19 +3117,27 @@ static int _cpr3_regulator_update_ctrl_state(struct cpr3_controller *ctrl) if (ctrl->cpr_enabled && ctrl->last_corner_was_closed_loop) { if (ctrl->cpr_enabled && ctrl->last_corner_was_closed_loop) { /* /* * Always program open-loop voltage for CPR4 controller. * Always program open-loop voltage for CPR4 controllers which * Storing last closed loop voltage in corner structure would * support hardware closed-loop. Storing the last closed loop * help debug. * voltage in corner structure can still help with debugging. */ */ new_volt = (ctrl->ctrl_type == CPR_CTRL_TYPE_CPR3 if (ctrl->ctrl_type == CPR_CTRL_TYPE_CPR3) ? aggr_corner.last_volt new_volt = aggr_corner.last_volt; : aggr_corner.open_loop_volt); else if (ctrl->ctrl_type == CPR_CTRL_TYPE_CPR4 && ctrl->supports_hw_closed_loop) new_volt = aggr_corner.open_loop_volt; else new_volt = min(aggr_corner.last_volt + cpr3_regulator_max_sdelta_diff(aggr_corner.sdelta, ctrl->step_volt), aggr_corner.ceiling_volt); } else { } else { new_volt = aggr_corner.open_loop_volt; new_volt = aggr_corner.open_loop_volt; aggr_corner.last_volt = aggr_corner.open_loop_volt; aggr_corner.last_volt = aggr_corner.open_loop_volt; } } if (ctrl->ctrl_type == CPR_CTRL_TYPE_CPR4) { if (ctrl->ctrl_type == CPR_CTRL_TYPE_CPR4 && ctrl->supports_hw_closed_loop) { /* /* * Store last aggregated corner open-loop voltage in vdd_volt * Store last aggregated corner open-loop voltage in vdd_volt * which is used when programming current aggregated corner * which is used when programming current aggregated corner Loading Loading @@ -3168,7 +3213,8 @@ static int _cpr3_regulator_update_ctrl_state(struct cpr3_controller *ctrl) * re-enabling CPR loop operation. * re-enabling CPR loop operation. */ */ wmb(); wmb(); } else if (ctrl->ctrl_type == CPR_CTRL_TYPE_CPR4) { } else if (ctrl->ctrl_type == CPR_CTRL_TYPE_CPR4 && ctrl->vdd_limit_regulator) { /* Set ceiling and floor limits in hardware */ /* Set ceiling and floor limits in hardware */ rc = regulator_set_voltage(ctrl->vdd_limit_regulator, rc = regulator_set_voltage(ctrl->vdd_limit_regulator, aggr_corner.floor_volt, aggr_corner.floor_volt, Loading Loading @@ -6051,9 +6097,7 @@ int cpr3_regulator_unregister(struct cpr3_controller *ctrl) cpr3_closed_loop_disable(ctrl); cpr3_closed_loop_disable(ctrl); if ((ctrl->use_hw_closed_loop if (ctrl->vdd_limit_regulator) { || ctrl->ctrl_type == CPR_CTRL_TYPE_CPR4) && ctrl->ctrl_type != CPR_CTRL_TYPE_CPRH) { regulator_disable(ctrl->vdd_limit_regulator); regulator_disable(ctrl->vdd_limit_regulator); if (ctrl->ctrl_type == CPR_CTRL_TYPE_CPR3) if (ctrl->ctrl_type == CPR_CTRL_TYPE_CPR3) Loading Loading
Documentation/devicetree/bindings/regulator/cpr3-mmss-regulator.txt +77 −3 Original line number Original line Diff line number Diff line Loading @@ -27,13 +27,13 @@ MMSS specific properties: - compatible - compatible Usage: required Usage: required Value type: <string> Value type: <string> Definition: should be "qcom,cpr3-msm8996-mmss-regulator" Definition: should be one of the following: Definition: should be one of the following: "qcom,cpr3-msm8996-v1-mmss-regulator", "qcom,cpr3-msm8996-v1-mmss-regulator", "qcom,cpr3-msm8996-v2-mmss-regulator", "qcom,cpr3-msm8996-v2-mmss-regulator", "qcom,cpr3-msm8996-v3-mmss-regulator", "qcom,cpr3-msm8996-v3-mmss-regulator", "qcom,cpr3-msm8996-mmss-regulator", "qcom,cpr3-msm8996-mmss-regulator", "qcom,cpr3-msm8996pro-mmss-regulator". "qcom,cpr3-msm8996pro-mmss-regulator", "qcom,cpr4-msmcobalt-mmss-regulator". If the SoC revision is not specified, then it is assumed to If the SoC revision is not specified, then it is assumed to be the most recent revision of MSM8996, i.e. v3. be the most recent revision of MSM8996, i.e. v3. Loading @@ -50,7 +50,39 @@ MMSS specific properties: Value type: <stringlist> Value type: <stringlist> Definition: Clock names. This list must match up 1-to-1 with the clocks Definition: Clock names. This list must match up 1-to-1 with the clocks specified in the 'clocks' property. "core_clk", "iface_clk", specified in the 'clocks' property. "core_clk", "iface_clk", and "bus_clk" must be specified. and "bus_clk" must be specified. Note that "iface_clk" is not required for devices with compatible = "qcom,cpr4-msmcobalt-mmss-regulator". - qcom,cpr-temp-point-map Usage: Required if qcom,corner-allow-temp-adjustment is specified for at least one of the CPR3 regulators. Value type: <prop-encoded-array> Definition: The temperature points in decidegrees Celsius which indicate the range of temperature bands supported. If t1, t2, and t3 are the temperature points, then the temperature bands are: (-inf, t1], (t1, t2], (t2, t3], and (t3, inf). 1 to 3 temperature points should be specified. - qcom,cpr-initial-temp-band Usage: Required if qcom,cpr-temp-point-map is specified. Value type: <u32> Definition: The initial temp band considering 0-based index at which the baseline target quotients are derived and fused. Supported values: 0 to number of elements in qcom,cpr-temp-point-map. - qcom,cpr-step-quot-fixed Usage: Optional for controllers with compatible = "qcom,cpr4-msmcobalt-mmss-regulator"; unsupported for all others. Value type: <u32> Definition: Fixed step quotient value used by controller for applying the SDELTA margin adjustments on the programmed target quotient values. The step quotient is the number of additional ring oscillator ticks observed for each qcom,voltage-step increase in vdd-supply output voltage. Supported values: 0 - 63. ================================================= ================================================= Second Level Nodes - CPR Threads for a Controller Second Level Nodes - CPR Threads for a Controller Loading Loading @@ -163,6 +195,48 @@ MMSS specific properties: Values 1 to qcom,cpr-fuse-corners denote the specific fuse Values 1 to qcom,cpr-fuse-corners denote the specific fuse corner that should be used by a given voltage corner. corner that should be used by a given voltage corner. - qcom,corner-allow-temp-adjustment Usage: Optional for controllers with compatible = "qcom,cpr4-msmcobalt-mmss-regulator"; unsupported for all others. Value type: <prop-encoded-array> Definition: A list of integer tuples which each define the CPR temperature adjustment feature enable state for each voltage corner in order from lowest to highest. Each element in the tuple should be either 0 (temperature adjustment not allowed) or 1 (temperature adjustment allowed). 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-bin-corners property. A single tuple may only be specified if all of the corner counts in qcom,cpr-corners are the same. - qcom,cpr-cornerX-temp-core-voltage-adjustment Usage: Required if qcom,corner-allow-temp-adjustment is specified for this CPR3 regulator. Value type: <prop-encoded-array> Definition: A list of integer tuples for cornerX. The possible values for X are 1 to the max value specified in qcom,cpr-corners. Each tuple defines the temperature based voltage adjustment in microvolts for each temperature band from lowest to highest. Each tuple must have a number of elements equal to (the number of elements in qcom,cpr-ctrl-temp-point-map + 1) The tuple 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 list which is used regardless of the fuse combination and speed bin found on a given chip. Note that the qcom,cpr-closed-loop-voltage-fuse-adjustment property is not 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 meaningful for MMSS CPR3 regulator nodes since target quotients are not defined in fuses. in fuses. Loading
drivers/regulator/cpr3-mmss-regulator.c +218 −25 Original line number Original line Diff line number Diff line Loading @@ -71,6 +71,9 @@ struct cpr3_msm8996_mmss_fuses { */ */ #define CPR3_MSM8996PRO_MMSS_FUSE_COMBO_COUNT 16 #define CPR3_MSM8996PRO_MMSS_FUSE_COMBO_COUNT 16 /* Fuse combos 0 - 7 map to CPR fusing revision 0 - 7 */ #define CPR3_MSMCOBALT_MMSS_FUSE_COMBO_COUNT 8 /* /* * MSM8996 MMSS fuse parameter locations: * MSM8996 MMSS fuse parameter locations: * * Loading Loading @@ -121,7 +124,42 @@ static const struct cpr3_fuse_param msm8996pro_mmss_speed_bin_param[] = { {}, {}, }; }; /* MSMCOBALT MMSS fuse parameter locations: */ static const struct cpr3_fuse_param msmcobalt_mmss_init_voltage_param[MSM8996_MMSS_FUSE_CORNERS][2] = { {{65, 39, 43}, {} }, {{65, 34, 38}, {} }, {{65, 29, 33}, {} }, {{65, 24, 28}, {} }, }; static const struct cpr3_fuse_param msmcobalt_cpr_fusing_rev_param[] = { {39, 48, 50}, {}, }; static const struct cpr3_fuse_param msmcobalt_cpr_limitation_param[] = { {41, 46, 47}, {}, }; static const struct cpr3_fuse_param msmcobalt_mmss_aging_init_quot_diff_param[] = { {65, 60, 63}, {66, 0, 3}, {}, }; static const struct cpr3_fuse_param msmcobalt_mmss_offset_voltage_param[MSM8996_MMSS_FUSE_CORNERS][2] = { {{65, 56, 59}, {} }, {{65, 52, 55}, {} }, {{65, 48, 51}, {} }, {{65, 44, 47}, {} }, }; #define MSM8996PRO_SOC_ID 4 #define MSM8996PRO_SOC_ID 4 #define MSMCOBALT_SOC_ID 5 /* /* * Some initial msm8996 parts cannot be used in a meaningful way by software. * Some initial msm8996 parts cannot be used in a meaningful way by software. Loading Loading @@ -152,6 +190,13 @@ static const int msm8996pro_mmss_fuse_ref_volt[MSM8996_MMSS_FUSE_CORNERS] = { 1065000, 1065000, }; }; static const int msmcobalt_mmss_fuse_ref_volt[MSM8996_MMSS_FUSE_CORNERS] = { 632000, 768000, 896000, 1032000, }; #define MSM8996_MMSS_FUSE_STEP_VOLT 10000 #define MSM8996_MMSS_FUSE_STEP_VOLT 10000 #define MSM8996_MMSS_OFFSET_FUSE_STEP_VOLT 10000 #define MSM8996_MMSS_OFFSET_FUSE_STEP_VOLT 10000 #define MSM8996_MMSS_VOLTAGE_FUSE_SIZE 5 #define MSM8996_MMSS_VOLTAGE_FUSE_SIZE 5 Loading @@ -165,6 +210,18 @@ static const int msm8996pro_mmss_fuse_ref_volt[MSM8996_MMSS_FUSE_CORNERS] = { #define MSM8996_MMSS_AGING_SENSOR_ID 29 #define MSM8996_MMSS_AGING_SENSOR_ID 29 #define MSM8996_MMSS_AGING_BYPASS_MASK0 (GENMASK(23, 0)) #define MSM8996_MMSS_AGING_BYPASS_MASK0 (GENMASK(23, 0)) #define MSMCOBALT_MMSS_AGING_INIT_QUOT_DIFF_SCALE 1 #define MSMCOBALT_MMSS_AGING_INIT_QUOT_DIFF_SIZE 8 #define MSMCOBALT_MMSS_CPR_SENSOR_COUNT 35 #define MSMCOBALT_MMSS_AGING_SENSOR_ID 17 #define MSMCOBALT_MMSS_AGING_BYPASS_MASK0 0 #define MSMCOBALT_MMSS_MAX_TEMP_POINTS 3 #define MSMCOBALT_MMSS_TEMP_SENSOR_ID_START 12 #define MSMCOBALT_MMSS_TEMP_SENSOR_ID_END 13 /** /** * cpr3_msm8996_mmss_read_fuse_data() - load MMSS specific fuse parameter values * cpr3_msm8996_mmss_read_fuse_data() - load MMSS specific fuse parameter values * @vreg: Pointer to the CPR3 regulator * @vreg: Pointer to the CPR3 regulator Loading Loading @@ -196,7 +253,10 @@ static int cpr3_msm8996_mmss_read_fuse_data(struct cpr3_regulator *vreg) cpr3_info(vreg, "speed bin = %llu\n", fuse->speed_bin); cpr3_info(vreg, "speed bin = %llu\n", fuse->speed_bin); } } rc = cpr3_read_fuse_param(base, msm8996_cpr_fusing_rev_param, rc = cpr3_read_fuse_param(base, vreg->thread->ctrl->soc_revision == MSMCOBALT_SOC_ID ? msmcobalt_cpr_fusing_rev_param : msm8996_cpr_fusing_rev_param, &fuse->cpr_fusing_rev); &fuse->cpr_fusing_rev); if (rc) { if (rc) { cpr3_err(vreg, "Unable to read CPR fusing revision fuse, rc=%d\n", cpr3_err(vreg, "Unable to read CPR fusing revision fuse, rc=%d\n", Loading @@ -205,7 +265,10 @@ static int cpr3_msm8996_mmss_read_fuse_data(struct cpr3_regulator *vreg) } } cpr3_info(vreg, "CPR fusing revision = %llu\n", fuse->cpr_fusing_rev); cpr3_info(vreg, "CPR fusing revision = %llu\n", fuse->cpr_fusing_rev); rc = cpr3_read_fuse_param(base, msm8996_cpr_limitation_param, rc = cpr3_read_fuse_param(base, vreg->thread->ctrl->soc_revision == MSMCOBALT_SOC_ID ? msmcobalt_cpr_limitation_param : msm8996_cpr_limitation_param, &fuse->limitation); &fuse->limitation); if (rc) { if (rc) { cpr3_err(vreg, "Unable to read CPR limitation fuse, rc=%d\n", cpr3_err(vreg, "Unable to read CPR limitation fuse, rc=%d\n", Loading @@ -218,7 +281,10 @@ static int cpr3_msm8996_mmss_read_fuse_data(struct cpr3_regulator *vreg) == MSM8996_CPR_LIMITATION_NO_CPR_OR_INTERPOLATION == MSM8996_CPR_LIMITATION_NO_CPR_OR_INTERPOLATION ? "CPR disabled and no interpolation" : "none"); ? "CPR disabled and no interpolation" : "none"); rc = cpr3_read_fuse_param(base, msm8996_mmss_aging_init_quot_diff_param, rc = cpr3_read_fuse_param(base, vreg->thread->ctrl->soc_revision == MSMCOBALT_SOC_ID ? msmcobalt_mmss_aging_init_quot_diff_param : msm8996_mmss_aging_init_quot_diff_param, &fuse->aging_init_quot_diff); &fuse->aging_init_quot_diff); if (rc) { if (rc) { cpr3_err(vreg, "Unable to read aging initial quotient difference fuse, rc=%d\n", cpr3_err(vreg, "Unable to read aging initial quotient difference fuse, rc=%d\n", Loading @@ -228,7 +294,9 @@ static int cpr3_msm8996_mmss_read_fuse_data(struct cpr3_regulator *vreg) for (i = 0; i < MSM8996_MMSS_FUSE_CORNERS; i++) { for (i = 0; i < MSM8996_MMSS_FUSE_CORNERS; i++) { rc = cpr3_read_fuse_param(base, rc = cpr3_read_fuse_param(base, msm8996_mmss_init_voltage_param[i], vreg->thread->ctrl->soc_revision == MSMCOBALT_SOC_ID ? msmcobalt_mmss_init_voltage_param[i] : msm8996_mmss_init_voltage_param[i], &fuse->init_voltage[i]); &fuse->init_voltage[i]); if (rc) { if (rc) { cpr3_err(vreg, "Unable to read fuse-corner %d initial voltage fuse, rc=%d\n", cpr3_err(vreg, "Unable to read fuse-corner %d initial voltage fuse, rc=%d\n", Loading @@ -237,7 +305,9 @@ static int cpr3_msm8996_mmss_read_fuse_data(struct cpr3_regulator *vreg) } } rc = cpr3_read_fuse_param(base, rc = cpr3_read_fuse_param(base, msm8996_mmss_offset_voltage_param[i], vreg->thread->ctrl->soc_revision == MSMCOBALT_SOC_ID ? msmcobalt_mmss_offset_voltage_param[i] : msm8996_mmss_offset_voltage_param[i], &fuse->offset_voltage[i]); &fuse->offset_voltage[i]); if (rc) { if (rc) { cpr3_err(vreg, "Unable to read fuse-corner %d offset voltage fuse, rc=%d\n", cpr3_err(vreg, "Unable to read fuse-corner %d offset voltage fuse, rc=%d\n", Loading @@ -246,7 +316,10 @@ static int cpr3_msm8996_mmss_read_fuse_data(struct cpr3_regulator *vreg) } } } } if (vreg->thread->ctrl->soc_revision == MSM8996PRO_SOC_ID) { if (vreg->thread->ctrl->soc_revision == MSMCOBALT_SOC_ID) { combo_max = CPR3_MSMCOBALT_MMSS_FUSE_COMBO_COUNT; vreg->fuse_combo = fuse->cpr_fusing_rev; } else if (vreg->thread->ctrl->soc_revision == MSM8996PRO_SOC_ID) { combo_max = CPR3_MSM8996PRO_MMSS_FUSE_COMBO_COUNT; combo_max = CPR3_MSM8996PRO_MMSS_FUSE_COMBO_COUNT; vreg->fuse_combo = fuse->cpr_fusing_rev + 8 * fuse->speed_bin; vreg->fuse_combo = fuse->cpr_fusing_rev + 8 * fuse->speed_bin; } else { } else { Loading Loading @@ -322,6 +395,7 @@ static int cpr3_msm8996_mmss_apply_closed_loop_offset_voltages( struct cpr3_regulator *vreg, int *volt_adjust) struct cpr3_regulator *vreg, int *volt_adjust) { { struct cpr3_msm8996_mmss_fuses *fuse = vreg->platform_fuses; struct cpr3_msm8996_mmss_fuses *fuse = vreg->platform_fuses; const struct cpr3_fuse_param (*offset_param)[2]; u32 *corner_map; u32 *corner_map; int *volt_offset; int *volt_offset; int rc = 0, i, fuse_len; int rc = 0, i, fuse_len; Loading @@ -347,9 +421,12 @@ static int cpr3_msm8996_mmss_apply_closed_loop_offset_voltages( if (rc) if (rc) goto done; goto done; offset_param = vreg->thread->ctrl->soc_revision == MSMCOBALT_SOC_ID ? msmcobalt_mmss_offset_voltage_param : msm8996_mmss_offset_voltage_param; for (i = 0; i < vreg->fuse_corner_count; i++) { for (i = 0; i < vreg->fuse_corner_count; i++) { fuse_len = msm8996_mmss_offset_voltage_param[i][0].bit_end + 1 fuse_len = offset_param[i][0].bit_end + 1 - msm8996_mmss_offset_voltage_param[i][0].bit_start; - offset_param[i][0].bit_start; volt_offset[i] = cpr3_convert_open_loop_voltage_fuse( volt_offset[i] = cpr3_convert_open_loop_voltage_fuse( 0, MSM8996_MMSS_OFFSET_FUSE_STEP_VOLT, 0, MSM8996_MMSS_OFFSET_FUSE_STEP_VOLT, fuse->offset_voltage[i], fuse_len); fuse->offset_voltage[i], fuse_len); Loading Loading @@ -423,7 +500,7 @@ static void cpr3_mmss_enforce_dec_quotient_monotonicity( for (i = vreg->corner_count - 2; i >= 0; i--) { for (i = vreg->corner_count - 2; i >= 0; i--) { for (j = 0; j < CPR3_RO_COUNT; j++) { for (j = 0; j < CPR3_RO_COUNT; j++) { if (vreg->corner[i].target_quot[j] if (vreg->corner[i + 1].target_quot[j] && vreg->corner[i].target_quot[j] && vreg->corner[i].target_quot[j] > vreg->corner[i + 1].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", 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", Loading Loading @@ -617,7 +694,9 @@ static int cpr3_msm8996_mmss_calculate_open_loop_voltages( goto done; goto done; } } if (vreg->thread->ctrl->soc_revision == MSM8996PRO_SOC_ID) if (vreg->thread->ctrl->soc_revision == MSMCOBALT_SOC_ID) ref_volt = msmcobalt_mmss_fuse_ref_volt; else if (vreg->thread->ctrl->soc_revision == MSM8996PRO_SOC_ID) ref_volt = msm8996pro_mmss_fuse_ref_volt; ref_volt = msm8996pro_mmss_fuse_ref_volt; else else ref_volt = msm8996_mmss_fuse_ref_volt; ref_volt = msm8996_mmss_fuse_ref_volt; Loading Loading @@ -773,15 +852,27 @@ static int cpr3_mmss_init_aging(struct cpr3_controller *ctrl) if (!ctrl->aging_sensor) if (!ctrl->aging_sensor) return -ENOMEM; return -ENOMEM; ctrl->aging_sensor->sensor_id = MSM8996_MMSS_AGING_SENSOR_ID; ctrl->aging_sensor->bypass_mask[0] = MSM8996_MMSS_AGING_BYPASS_MASK0; ctrl->aging_sensor->ro_scale = aging_ro_scale; ctrl->aging_sensor->ro_scale = aging_ro_scale; if (vreg->thread->ctrl->soc_revision == MSMCOBALT_SOC_ID) { ctrl->aging_sensor->sensor_id = MSMCOBALT_MMSS_AGING_SENSOR_ID; ctrl->aging_sensor->bypass_mask[0] = MSMCOBALT_MMSS_AGING_BYPASS_MASK0; ctrl->aging_sensor->init_quot_diff = cpr3_convert_open_loop_voltage_fuse(0, MSMCOBALT_MMSS_AGING_INIT_QUOT_DIFF_SCALE, fuse->aging_init_quot_diff, MSMCOBALT_MMSS_AGING_INIT_QUOT_DIFF_SIZE); } else { ctrl->aging_sensor->sensor_id = MSM8996_MMSS_AGING_SENSOR_ID; ctrl->aging_sensor->bypass_mask[0] = MSM8996_MMSS_AGING_BYPASS_MASK0; ctrl->aging_sensor->init_quot_diff ctrl->aging_sensor->init_quot_diff = cpr3_convert_open_loop_voltage_fuse(0, = cpr3_convert_open_loop_voltage_fuse(0, MSM8996_MMSS_AGING_INIT_QUOT_DIFF_SCALE, MSM8996_MMSS_AGING_INIT_QUOT_DIFF_SCALE, fuse->aging_init_quot_diff, fuse->aging_init_quot_diff, MSM8996_MMSS_AGING_INIT_QUOT_DIFF_SIZE); MSM8996_MMSS_AGING_INIT_QUOT_DIFF_SIZE); } cpr3_debug(ctrl, "sensor %u aging init quotient diff = %d, aging RO scale = %u QUOT/V\n", cpr3_debug(ctrl, "sensor %u aging init quotient diff = %d, aging RO scale = %u QUOT/V\n", ctrl->aging_sensor->sensor_id, ctrl->aging_sensor->sensor_id, Loading Loading @@ -862,11 +953,83 @@ static int cpr3_mmss_init_thread(struct cpr3_thread *thread) return rc; return rc; } } if (thread->ctrl->soc_revision == MSMCOBALT_SOC_ID) { rc = cpr4_parse_core_count_temp_voltage_adj(vreg, false); if (rc) { cpr3_err(vreg, "unable to parse temperature based voltage adjustments, rc=%d\n", rc); return rc; } } cpr3_mmss_print_settings(vreg); cpr3_mmss_print_settings(vreg); return 0; return 0; } } /** * cpr4_mmss_parse_temp_adj_properties() - parse temperature based * adjustment properties from device tree * @ctrl: Pointer to the CPR3 controller * * Return: 0 on success, errno on failure */ static int cpr4_mmss_parse_temp_adj_properties(struct cpr3_controller *ctrl) { struct device_node *of_node = ctrl->dev->of_node; int rc, len, temp_point_count; if (!of_find_property(of_node, "qcom,cpr-temp-point-map", &len)) return 0; temp_point_count = len / sizeof(u32); if (temp_point_count <= 0 || temp_point_count > MSMCOBALT_MMSS_MAX_TEMP_POINTS) { cpr3_err(ctrl, "invalid number of temperature points %d > %d (max)\n", temp_point_count, MSMCOBALT_MMSS_MAX_TEMP_POINTS); return -EINVAL; } ctrl->temp_points = devm_kcalloc(ctrl->dev, temp_point_count, sizeof(*ctrl->temp_points), GFP_KERNEL); if (!ctrl->temp_points) return -ENOMEM; rc = of_property_read_u32_array(of_node, "qcom,cpr-temp-point-map", ctrl->temp_points, temp_point_count); if (rc) { cpr3_err(ctrl, "error reading property qcom,cpr-temp-point-map, rc=%d\n", rc); return rc; } /* * If t1, t2, and t3 are the temperature points, then the temperature * bands are: (-inf, t1], (t1, t2], (t2, t3], and (t3, inf). */ ctrl->temp_band_count = temp_point_count + 1; rc = of_property_read_u32(of_node, "qcom,cpr-initial-temp-band", &ctrl->initial_temp_band); if (rc) { cpr3_err(ctrl, "error reading qcom,cpr-initial-temp-band, rc=%d\n", rc); return rc; } if (ctrl->initial_temp_band >= ctrl->temp_band_count) { cpr3_err(ctrl, "Initial temperature band value %d should be in range [0 - %d]\n", ctrl->initial_temp_band, ctrl->temp_band_count - 1); return -EINVAL; } ctrl->temp_sensor_id_start = MSMCOBALT_MMSS_TEMP_SENSOR_ID_START; ctrl->temp_sensor_id_end = MSMCOBALT_MMSS_TEMP_SENSOR_ID_END; ctrl->allow_temp_adj = true; return rc; } /** /** * cpr3_mmss_init_controller() - perform MMSS CPR3 controller specific * cpr3_mmss_init_controller() - perform MMSS CPR3 controller specific * initializations * initializations Loading @@ -886,7 +1049,15 @@ static int cpr3_mmss_init_controller(struct cpr3_controller *ctrl) return rc; return rc; } } ctrl->sensor_count = MSM8996_MMSS_CPR_SENSOR_COUNT; if (ctrl->soc_revision == MSMCOBALT_SOC_ID) { rc = cpr4_mmss_parse_temp_adj_properties(ctrl); if (rc) return rc; } ctrl->sensor_count = ctrl->soc_revision == MSMCOBALT_SOC_ID ? MSMCOBALT_MMSS_CPR_SENSOR_COUNT : MSM8996_MMSS_CPR_SENSOR_COUNT; /* /* * MMSS only has one thread (0) so the zeroed array does not need * MMSS only has one thread (0) so the zeroed array does not need Loading @@ -898,16 +1069,34 @@ static int cpr3_mmss_init_controller(struct cpr3_controller *ctrl) return -ENOMEM; return -ENOMEM; ctrl->cpr_clock_rate = MSM8996_MMSS_CPR_CLOCK_RATE; ctrl->cpr_clock_rate = MSM8996_MMSS_CPR_CLOCK_RATE; ctrl->ctrl_type = CPR_CTRL_TYPE_CPR3; ctrl->ctrl_type = ctrl->soc_revision == MSMCOBALT_SOC_ID ? CPR_CTRL_TYPE_CPR4 : CPR_CTRL_TYPE_CPR3; if (ctrl->ctrl_type == CPR_CTRL_TYPE_CPR4) { /* * Use fixed step quotient if specified otherwise use dynamic * calculated per RO step quotient */ of_property_read_u32(ctrl->dev->of_node, "qcom,cpr-step-quot-fixed", &ctrl->step_quot_fixed); ctrl->use_dynamic_step_quot = !ctrl->step_quot_fixed; } ctrl->iface_clk = devm_clk_get(ctrl->dev, "iface_clk"); ctrl->iface_clk = devm_clk_get(ctrl->dev, "iface_clk"); if (IS_ERR(ctrl->iface_clk)) { if (IS_ERR(ctrl->iface_clk)) { rc = PTR_ERR(ctrl->iface_clk); rc = PTR_ERR(ctrl->iface_clk); if (rc != -EPROBE_DEFER) if (ctrl->soc_revision == MSMCOBALT_SOC_ID) { cpr3_err(ctrl, "unable request interface clock, rc=%d\n", /* iface_clk is optional for msmcobalt */ ctrl->iface_clk = NULL; } else if (rc == -EPROBE_DEFER) { return rc; } else { cpr3_err(ctrl, "unable to request interface clock, rc=%d\n", rc); rc); return rc; return rc; } } } ctrl->bus_clk = devm_clk_get(ctrl->dev, "bus_clk"); ctrl->bus_clk = devm_clk_get(ctrl->dev, "bus_clk"); if (IS_ERR(ctrl->bus_clk)) { if (IS_ERR(ctrl->bus_clk)) { Loading Loading @@ -958,6 +1147,10 @@ static struct of_device_id cpr_regulator_match_table[] = { .compatible = "qcom,cpr3-msm8996pro-mmss-regulator", .compatible = "qcom,cpr3-msm8996pro-mmss-regulator", .data = (void *)(uintptr_t)MSM8996PRO_SOC_ID, .data = (void *)(uintptr_t)MSM8996PRO_SOC_ID, }, }, { .compatible = "qcom,cpr4-msmcobalt-mmss-regulator", .data = (void *)(uintptr_t)MSMCOBALT_SOC_ID, }, {} {} }; }; Loading
drivers/regulator/cpr3-regulator.c +118 −74 Original line number Original line Diff line number Diff line Loading @@ -698,6 +698,7 @@ static int cpr3_regulator_init_cpr4(struct cpr3_controller *ctrl) int thread_id = 0; int thread_id = 0; u64 temp; u64 temp; if (ctrl->supports_hw_closed_loop) { if (ctrl->saw_use_unit_mV) if (ctrl->saw_use_unit_mV) pmic_step_size = ctrl->step_volt / 1000; pmic_step_size = ctrl->step_volt / 1000; cpr3_masked_write(ctrl, CPR4_REG_MARGIN_ADJ_CTL, cpr3_masked_write(ctrl, CPR4_REG_MARGIN_ADJ_CTL, Loading @@ -716,8 +717,8 @@ static int cpr3_regulator_init_cpr4(struct cpr3_controller *ctrl) << CPR4_SAW_ERROR_STEP_LIMIT_UP_SHIFT)); << CPR4_SAW_ERROR_STEP_LIMIT_UP_SHIFT)); /* /* * Enable thread aggregation regardless of which threads are enabled * Enable thread aggregation regardless of which threads are * or disabled. * enabled or disabled. */ */ cpr3_masked_write(ctrl, CPR4_REG_CPR_TIMER_CLAMP, cpr3_masked_write(ctrl, CPR4_REG_CPR_TIMER_CLAMP, CPR4_CPR_TIMER_CLAMP_THREAD_AGGREGATION_EN, CPR4_CPR_TIMER_CLAMP_THREAD_AGGREGATION_EN, Loading @@ -741,13 +742,15 @@ static int cpr3_regulator_init_cpr4(struct cpr3_controller *ctrl) case 1: case 1: /* Disable unused thread */ /* Disable unused thread */ thread_id = ctrl->thread[0].thread_id ? 0 : 1; thread_id = ctrl->thread[0].thread_id ? 0 : 1; cpr3_masked_write(ctrl, CPR4_REG_CPR_MASK_THREAD(thread_id), cpr3_masked_write(ctrl, CPR4_REG_CPR_MASK_THREAD(thread_id), CPR4_CPR_MASK_THREAD_DISABLE_THREAD CPR4_CPR_MASK_THREAD_DISABLE_THREAD | CPR4_CPR_MASK_THREAD_RO_MASK4THREAD_MASK, | CPR4_CPR_MASK_THREAD_RO_MASK4THREAD_MASK, CPR4_CPR_MASK_THREAD_DISABLE_THREAD CPR4_CPR_MASK_THREAD_DISABLE_THREAD | CPR4_CPR_MASK_THREAD_RO_MASK4THREAD_MASK); | CPR4_CPR_MASK_THREAD_RO_MASK4THREAD_MASK); break; break; } } } if (!ctrl->allow_core_count_adj && !ctrl->allow_temp_adj if (!ctrl->allow_core_count_adj && !ctrl->allow_temp_adj && !ctrl->allow_boost) { && !ctrl->allow_boost) { Loading @@ -758,6 +761,7 @@ static int cpr3_regulator_init_cpr4(struct cpr3_controller *ctrl) return rc; return rc; } } if (ctrl->supports_hw_closed_loop) cpr3_masked_write(ctrl, CPR4_REG_MARGIN_ADJ_CTL, cpr3_masked_write(ctrl, CPR4_REG_MARGIN_ADJ_CTL, CPR4_MARGIN_ADJ_CTL_TIMER_SETTLE_VOLTAGE_EN, CPR4_MARGIN_ADJ_CTL_TIMER_SETTLE_VOLTAGE_EN, CPR4_MARGIN_ADJ_CTL_TIMER_SETTLE_VOLTAGE_EN); CPR4_MARGIN_ADJ_CTL_TIMER_SETTLE_VOLTAGE_EN); Loading Loading @@ -1001,8 +1005,10 @@ static int cpr3_controller_program_sdelta(struct cpr3_controller *ctrl) max_core_count << CPR4_MARGIN_ADJ_CTL_MAX_NUM_CORES_SHIFT max_core_count << CPR4_MARGIN_ADJ_CTL_MAX_NUM_CORES_SHIFT | ((sdelta->allow_core_count_adj || sdelta->allow_boost) | ((sdelta->allow_core_count_adj || sdelta->allow_boost) ? CPR4_MARGIN_ADJ_CTL_CORE_ADJ_EN : 0) ? CPR4_MARGIN_ADJ_CTL_CORE_ADJ_EN : 0) | (sdelta->allow_temp_adj ? CPR4_MARGIN_ADJ_CTL_TEMP_ADJ_EN : 0) | ((sdelta->allow_temp_adj && ctrl->supports_hw_closed_loop) | ((ctrl->use_hw_closed_loop && !sdelta->allow_boost) ? CPR4_MARGIN_ADJ_CTL_TEMP_ADJ_EN : 0) | (((ctrl->use_hw_closed_loop && !sdelta->allow_boost) || !ctrl->supports_hw_closed_loop) ? CPR4_MARGIN_ADJ_CTL_KV_MARGIN_ADJ_EN : 0) ? CPR4_MARGIN_ADJ_CTL_KV_MARGIN_ADJ_EN : 0) | (sdelta->allow_boost | (sdelta->allow_boost ? CPR4_MARGIN_ADJ_CTL_BOOST_EN : 0)); ? CPR4_MARGIN_ADJ_CTL_BOOST_EN : 0)); Loading Loading @@ -1469,7 +1475,6 @@ static int cpr3_regulator_init_ctrl(struct cpr3_controller *ctrl) cpr3_debug(ctrl, "PD_THROTTLE=0x%08X\n", cpr3_debug(ctrl, "PD_THROTTLE=0x%08X\n", ctrl->proc_clock_throttle); ctrl->proc_clock_throttle); } } } if ((ctrl->use_hw_closed_loop || if ((ctrl->use_hw_closed_loop || ctrl->ctrl_type == CPR_CTRL_TYPE_CPR4) && ctrl->ctrl_type == CPR_CTRL_TYPE_CPR4) && Loading @@ -1482,7 +1487,8 @@ static int cpr3_regulator_init_ctrl(struct cpr3_controller *ctrl) } } if (ctrl->ctrl_type == CPR_CTRL_TYPE_CPR3) { if (ctrl->ctrl_type == CPR_CTRL_TYPE_CPR3) { rc = msm_spm_avs_enable_irq(0, MSM_SPM_AVS_IRQ_MAX); rc = msm_spm_avs_enable_irq(0, MSM_SPM_AVS_IRQ_MAX); if (rc) { if (rc) { cpr3_err(ctrl, "could not enable max IRQ, rc=%d\n", cpr3_err(ctrl, "could not enable max IRQ, rc=%d\n", rc); rc); Loading @@ -1490,6 +1496,7 @@ static int cpr3_regulator_init_ctrl(struct cpr3_controller *ctrl) } } } } } } } if (ctrl->ctrl_type == CPR_CTRL_TYPE_CPR4) { if (ctrl->ctrl_type == CPR_CTRL_TYPE_CPR4) { rc = cpr3_regulator_init_cpr4(ctrl); rc = cpr3_regulator_init_cpr4(ctrl); Loading Loading @@ -2531,8 +2538,8 @@ static int cpr3_regulator_scale_vdd_voltage(struct cpr3_controller *ctrl, return rc; return rc; } } if (new_volt == last_volt && if (new_volt == last_volt && ctrl->supports_hw_closed_loop ctrl->ctrl_type == CPR_CTRL_TYPE_CPR4) { && ctrl->ctrl_type == CPR_CTRL_TYPE_CPR4) { /* /* * CPR4 features enforce voltage reprogramming when the last * CPR4 features enforce voltage reprogramming when the last * set voltage and new set voltage are same. This way, we can * set voltage and new set voltage are same. This way, we can Loading Loading @@ -2637,6 +2644,36 @@ static int cpr3_regulator_get_dynamic_floor_volt(struct cpr3_controller *ctrl, return dynamic_floor_volt; return dynamic_floor_volt; } } /** * cpr3_regulator_max_sdelta_diff() - returns the maximum voltage difference in * microvolts that can result from different operating conditions * for the specified sdelta struct * @sdelta: Pointer to the sdelta structure * @step_volt: Step size in microvolts between available set * points of the VDD supply. * * Return: voltage difference between the highest and lowest adjustments if * sdelta and sdelta->table are valid, else 0. */ static int cpr3_regulator_max_sdelta_diff(const struct cpr4_sdelta *sdelta, int step_volt) { int i, j, index, sdelta_min = INT_MAX, sdelta_max = INT_MIN; if (!sdelta || !sdelta->table) return 0; for (i = 0; i < sdelta->max_core_count; i++) { for (j = 0; j < sdelta->temp_band_count; j++) { index = i * sdelta->temp_band_count + j; sdelta_min = min(sdelta_min, sdelta->table[index]); sdelta_max = max(sdelta_max, sdelta->table[index]); } } return (sdelta_max - sdelta_min) * step_volt; } /** /** * cpr3_regulator_aggregate_sdelta() - check open-loop voltages of current * cpr3_regulator_aggregate_sdelta() - check open-loop voltages of current * aggregated corner and current corner of a given regulator * aggregated corner and current corner of a given regulator Loading Loading @@ -3080,19 +3117,27 @@ static int _cpr3_regulator_update_ctrl_state(struct cpr3_controller *ctrl) if (ctrl->cpr_enabled && ctrl->last_corner_was_closed_loop) { if (ctrl->cpr_enabled && ctrl->last_corner_was_closed_loop) { /* /* * Always program open-loop voltage for CPR4 controller. * Always program open-loop voltage for CPR4 controllers which * Storing last closed loop voltage in corner structure would * support hardware closed-loop. Storing the last closed loop * help debug. * voltage in corner structure can still help with debugging. */ */ new_volt = (ctrl->ctrl_type == CPR_CTRL_TYPE_CPR3 if (ctrl->ctrl_type == CPR_CTRL_TYPE_CPR3) ? aggr_corner.last_volt new_volt = aggr_corner.last_volt; : aggr_corner.open_loop_volt); else if (ctrl->ctrl_type == CPR_CTRL_TYPE_CPR4 && ctrl->supports_hw_closed_loop) new_volt = aggr_corner.open_loop_volt; else new_volt = min(aggr_corner.last_volt + cpr3_regulator_max_sdelta_diff(aggr_corner.sdelta, ctrl->step_volt), aggr_corner.ceiling_volt); } else { } else { new_volt = aggr_corner.open_loop_volt; new_volt = aggr_corner.open_loop_volt; aggr_corner.last_volt = aggr_corner.open_loop_volt; aggr_corner.last_volt = aggr_corner.open_loop_volt; } } if (ctrl->ctrl_type == CPR_CTRL_TYPE_CPR4) { if (ctrl->ctrl_type == CPR_CTRL_TYPE_CPR4 && ctrl->supports_hw_closed_loop) { /* /* * Store last aggregated corner open-loop voltage in vdd_volt * Store last aggregated corner open-loop voltage in vdd_volt * which is used when programming current aggregated corner * which is used when programming current aggregated corner Loading Loading @@ -3168,7 +3213,8 @@ static int _cpr3_regulator_update_ctrl_state(struct cpr3_controller *ctrl) * re-enabling CPR loop operation. * re-enabling CPR loop operation. */ */ wmb(); wmb(); } else if (ctrl->ctrl_type == CPR_CTRL_TYPE_CPR4) { } else if (ctrl->ctrl_type == CPR_CTRL_TYPE_CPR4 && ctrl->vdd_limit_regulator) { /* Set ceiling and floor limits in hardware */ /* Set ceiling and floor limits in hardware */ rc = regulator_set_voltage(ctrl->vdd_limit_regulator, rc = regulator_set_voltage(ctrl->vdd_limit_regulator, aggr_corner.floor_volt, aggr_corner.floor_volt, Loading Loading @@ -6051,9 +6097,7 @@ int cpr3_regulator_unregister(struct cpr3_controller *ctrl) cpr3_closed_loop_disable(ctrl); cpr3_closed_loop_disable(ctrl); if ((ctrl->use_hw_closed_loop if (ctrl->vdd_limit_regulator) { || ctrl->ctrl_type == CPR_CTRL_TYPE_CPR4) && ctrl->ctrl_type != CPR_CTRL_TYPE_CPRH) { regulator_disable(ctrl->vdd_limit_regulator); regulator_disable(ctrl->vdd_limit_regulator); if (ctrl->ctrl_type == CPR_CTRL_TYPE_CPR3) if (ctrl->ctrl_type == CPR_CTRL_TYPE_CPR3) Loading