Loading Documentation/devicetree/bindings/regulator/cpr3-mmss-regulator.txt +77 −3 Original line number Diff line number Diff line Loading @@ -27,13 +27,13 @@ MMSS specific properties: - compatible Usage: required Value type: <string> Definition: should be "qcom,cpr3-msm8996-mmss-regulator" Definition: should be one of the following: "qcom,cpr3-msm8996-v1-mmss-regulator", "qcom,cpr3-msm8996-v2-mmss-regulator", "qcom,cpr3-msm8996-v3-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 be the most recent revision of MSM8996, i.e. v3. Loading @@ -50,7 +50,39 @@ MMSS specific properties: Value type: <stringlist> Definition: Clock names. This list must match up 1-to-1 with the clocks 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 Loading Loading @@ -163,6 +195,48 @@ MMSS specific properties: Values 1 to qcom,cpr-fuse-corners denote the specific fuse 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 meaningful for MMSS CPR3 regulator nodes since target quotients are not defined in fuses. Loading drivers/regulator/cpr3-mmss-regulator.c +218 −25 Original line number Diff line number Diff line Loading @@ -71,6 +71,9 @@ struct cpr3_msm8996_mmss_fuses { */ #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: * 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 MSMCOBALT_SOC_ID 5 /* * 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, }; 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_OFFSET_FUSE_STEP_VOLT 10000 #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_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 * @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); } 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); if (rc) { 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); 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); if (rc) { 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 ? "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); if (rc) { 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++) { 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]); if (rc) { 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, 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]); if (rc) { 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; vreg->fuse_combo = fuse->cpr_fusing_rev + 8 * fuse->speed_bin; } 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_msm8996_mmss_fuses *fuse = vreg->platform_fuses; const struct cpr3_fuse_param (*offset_param)[2]; u32 *corner_map; int *volt_offset; int rc = 0, i, fuse_len; Loading @@ -347,9 +421,12 @@ static int cpr3_msm8996_mmss_apply_closed_loop_offset_voltages( if (rc) 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++) { fuse_len = msm8996_mmss_offset_voltage_param[i][0].bit_end + 1 - msm8996_mmss_offset_voltage_param[i][0].bit_start; fuse_len = offset_param[i][0].bit_end + 1 - offset_param[i][0].bit_start; volt_offset[i] = cpr3_convert_open_loop_voltage_fuse( 0, MSM8996_MMSS_OFFSET_FUSE_STEP_VOLT, 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 (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 + 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", Loading Loading @@ -617,7 +694,9 @@ static int cpr3_msm8996_mmss_calculate_open_loop_voltages( 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; else 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) 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; 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 = cpr3_convert_open_loop_voltage_fuse(0, MSM8996_MMSS_AGING_INIT_QUOT_DIFF_SCALE, fuse->aging_init_quot_diff, MSM8996_MMSS_AGING_INIT_QUOT_DIFF_SIZE); } cpr3_debug(ctrl, "sensor %u aging init quotient diff = %d, aging RO scale = %u QUOT/V\n", ctrl->aging_sensor->sensor_id, Loading Loading @@ -862,11 +953,83 @@ static int cpr3_mmss_init_thread(struct cpr3_thread *thread) 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); 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 * initializations Loading @@ -886,7 +1049,15 @@ static int cpr3_mmss_init_controller(struct cpr3_controller *ctrl) 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 Loading @@ -898,16 +1069,34 @@ static int cpr3_mmss_init_controller(struct cpr3_controller *ctrl) return -ENOMEM; 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"); if (IS_ERR(ctrl->iface_clk)) { rc = PTR_ERR(ctrl->iface_clk); if (rc != -EPROBE_DEFER) cpr3_err(ctrl, "unable request interface clock, rc=%d\n", if (ctrl->soc_revision == MSMCOBALT_SOC_ID) { /* 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); return rc; } } ctrl->bus_clk = devm_clk_get(ctrl->dev, "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", .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 Diff line number Diff line Loading @@ -698,6 +698,7 @@ static int cpr3_regulator_init_cpr4(struct cpr3_controller *ctrl) int thread_id = 0; u64 temp; if (ctrl->supports_hw_closed_loop) { if (ctrl->saw_use_unit_mV) pmic_step_size = ctrl->step_volt / 1000; 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)); /* * Enable thread aggregation regardless of which threads are enabled * or disabled. * Enable thread aggregation regardless of which threads are * enabled or disabled. */ cpr3_masked_write(ctrl, CPR4_REG_CPR_TIMER_CLAMP, CPR4_CPR_TIMER_CLAMP_THREAD_AGGREGATION_EN, Loading @@ -741,13 +742,15 @@ static int cpr3_regulator_init_cpr4(struct cpr3_controller *ctrl) case 1: /* Disable unused thread */ 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_RO_MASK4THREAD_MASK, CPR4_CPR_MASK_THREAD_DISABLE_THREAD | CPR4_CPR_MASK_THREAD_RO_MASK4THREAD_MASK); break; } } if (!ctrl->allow_core_count_adj && !ctrl->allow_temp_adj && !ctrl->allow_boost) { Loading @@ -758,6 +761,7 @@ static int cpr3_regulator_init_cpr4(struct cpr3_controller *ctrl) return rc; } if (ctrl->supports_hw_closed_loop) 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); 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 | ((sdelta->allow_core_count_adj || sdelta->allow_boost) ? CPR4_MARGIN_ADJ_CTL_CORE_ADJ_EN : 0) | (sdelta->allow_temp_adj ? CPR4_MARGIN_ADJ_CTL_TEMP_ADJ_EN : 0) | ((ctrl->use_hw_closed_loop && !sdelta->allow_boost) | ((sdelta->allow_temp_adj && ctrl->supports_hw_closed_loop) ? 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) | (sdelta->allow_boost ? 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", ctrl->proc_clock_throttle); } } if ((ctrl->use_hw_closed_loop || 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) { 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) { cpr3_err(ctrl, "could not enable max IRQ, rc=%d\n", rc); Loading @@ -1490,6 +1496,7 @@ static int cpr3_regulator_init_ctrl(struct cpr3_controller *ctrl) } } } } if (ctrl->ctrl_type == CPR_CTRL_TYPE_CPR4) { 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; } if (new_volt == last_volt && ctrl->ctrl_type == CPR_CTRL_TYPE_CPR4) { if (new_volt == last_volt && ctrl->supports_hw_closed_loop && ctrl->ctrl_type == CPR_CTRL_TYPE_CPR4) { /* * CPR4 features enforce voltage reprogramming when the last * 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; } /** * 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 * 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) { /* * Always program open-loop voltage for CPR4 controller. * Storing last closed loop voltage in corner structure would * help debug. * Always program open-loop voltage for CPR4 controllers which * support hardware closed-loop. Storing the last closed loop * voltage in corner structure can still help with debugging. */ new_volt = (ctrl->ctrl_type == CPR_CTRL_TYPE_CPR3 ? aggr_corner.last_volt : aggr_corner.open_loop_volt); if (ctrl->ctrl_type == CPR_CTRL_TYPE_CPR3) new_volt = aggr_corner.last_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 { new_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 * 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. */ 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 */ rc = regulator_set_voltage(ctrl->vdd_limit_regulator, aggr_corner.floor_volt, Loading Loading @@ -6051,9 +6097,7 @@ int cpr3_regulator_unregister(struct cpr3_controller *ctrl) cpr3_closed_loop_disable(ctrl); if ((ctrl->use_hw_closed_loop || ctrl->ctrl_type == CPR_CTRL_TYPE_CPR4) && ctrl->ctrl_type != CPR_CTRL_TYPE_CPRH) { if (ctrl->vdd_limit_regulator) { regulator_disable(ctrl->vdd_limit_regulator); if (ctrl->ctrl_type == CPR_CTRL_TYPE_CPR3) Loading Loading
Documentation/devicetree/bindings/regulator/cpr3-mmss-regulator.txt +77 −3 Original line number Diff line number Diff line Loading @@ -27,13 +27,13 @@ MMSS specific properties: - compatible Usage: required Value type: <string> Definition: should be "qcom,cpr3-msm8996-mmss-regulator" Definition: should be one of the following: "qcom,cpr3-msm8996-v1-mmss-regulator", "qcom,cpr3-msm8996-v2-mmss-regulator", "qcom,cpr3-msm8996-v3-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 be the most recent revision of MSM8996, i.e. v3. Loading @@ -50,7 +50,39 @@ MMSS specific properties: Value type: <stringlist> Definition: Clock names. This list must match up 1-to-1 with the clocks 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 Loading Loading @@ -163,6 +195,48 @@ MMSS specific properties: Values 1 to qcom,cpr-fuse-corners denote the specific fuse 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 meaningful for MMSS CPR3 regulator nodes since target quotients are not defined in fuses. Loading
drivers/regulator/cpr3-mmss-regulator.c +218 −25 Original line number Diff line number Diff line Loading @@ -71,6 +71,9 @@ struct cpr3_msm8996_mmss_fuses { */ #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: * 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 MSMCOBALT_SOC_ID 5 /* * 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, }; 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_OFFSET_FUSE_STEP_VOLT 10000 #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_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 * @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); } 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); if (rc) { 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); 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); if (rc) { 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 ? "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); if (rc) { 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++) { 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]); if (rc) { 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, 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]); if (rc) { 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; vreg->fuse_combo = fuse->cpr_fusing_rev + 8 * fuse->speed_bin; } 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_msm8996_mmss_fuses *fuse = vreg->platform_fuses; const struct cpr3_fuse_param (*offset_param)[2]; u32 *corner_map; int *volt_offset; int rc = 0, i, fuse_len; Loading @@ -347,9 +421,12 @@ static int cpr3_msm8996_mmss_apply_closed_loop_offset_voltages( if (rc) 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++) { fuse_len = msm8996_mmss_offset_voltage_param[i][0].bit_end + 1 - msm8996_mmss_offset_voltage_param[i][0].bit_start; fuse_len = offset_param[i][0].bit_end + 1 - offset_param[i][0].bit_start; volt_offset[i] = cpr3_convert_open_loop_voltage_fuse( 0, MSM8996_MMSS_OFFSET_FUSE_STEP_VOLT, 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 (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 + 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", Loading Loading @@ -617,7 +694,9 @@ static int cpr3_msm8996_mmss_calculate_open_loop_voltages( 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; else 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) 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; 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 = cpr3_convert_open_loop_voltage_fuse(0, MSM8996_MMSS_AGING_INIT_QUOT_DIFF_SCALE, fuse->aging_init_quot_diff, MSM8996_MMSS_AGING_INIT_QUOT_DIFF_SIZE); } cpr3_debug(ctrl, "sensor %u aging init quotient diff = %d, aging RO scale = %u QUOT/V\n", ctrl->aging_sensor->sensor_id, Loading Loading @@ -862,11 +953,83 @@ static int cpr3_mmss_init_thread(struct cpr3_thread *thread) 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); 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 * initializations Loading @@ -886,7 +1049,15 @@ static int cpr3_mmss_init_controller(struct cpr3_controller *ctrl) 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 Loading @@ -898,16 +1069,34 @@ static int cpr3_mmss_init_controller(struct cpr3_controller *ctrl) return -ENOMEM; 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"); if (IS_ERR(ctrl->iface_clk)) { rc = PTR_ERR(ctrl->iface_clk); if (rc != -EPROBE_DEFER) cpr3_err(ctrl, "unable request interface clock, rc=%d\n", if (ctrl->soc_revision == MSMCOBALT_SOC_ID) { /* 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); return rc; } } ctrl->bus_clk = devm_clk_get(ctrl->dev, "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", .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 Diff line number Diff line Loading @@ -698,6 +698,7 @@ static int cpr3_regulator_init_cpr4(struct cpr3_controller *ctrl) int thread_id = 0; u64 temp; if (ctrl->supports_hw_closed_loop) { if (ctrl->saw_use_unit_mV) pmic_step_size = ctrl->step_volt / 1000; 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)); /* * Enable thread aggregation regardless of which threads are enabled * or disabled. * Enable thread aggregation regardless of which threads are * enabled or disabled. */ cpr3_masked_write(ctrl, CPR4_REG_CPR_TIMER_CLAMP, CPR4_CPR_TIMER_CLAMP_THREAD_AGGREGATION_EN, Loading @@ -741,13 +742,15 @@ static int cpr3_regulator_init_cpr4(struct cpr3_controller *ctrl) case 1: /* Disable unused thread */ 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_RO_MASK4THREAD_MASK, CPR4_CPR_MASK_THREAD_DISABLE_THREAD | CPR4_CPR_MASK_THREAD_RO_MASK4THREAD_MASK); break; } } if (!ctrl->allow_core_count_adj && !ctrl->allow_temp_adj && !ctrl->allow_boost) { Loading @@ -758,6 +761,7 @@ static int cpr3_regulator_init_cpr4(struct cpr3_controller *ctrl) return rc; } if (ctrl->supports_hw_closed_loop) 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); 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 | ((sdelta->allow_core_count_adj || sdelta->allow_boost) ? CPR4_MARGIN_ADJ_CTL_CORE_ADJ_EN : 0) | (sdelta->allow_temp_adj ? CPR4_MARGIN_ADJ_CTL_TEMP_ADJ_EN : 0) | ((ctrl->use_hw_closed_loop && !sdelta->allow_boost) | ((sdelta->allow_temp_adj && ctrl->supports_hw_closed_loop) ? 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) | (sdelta->allow_boost ? 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", ctrl->proc_clock_throttle); } } if ((ctrl->use_hw_closed_loop || 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) { 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) { cpr3_err(ctrl, "could not enable max IRQ, rc=%d\n", rc); Loading @@ -1490,6 +1496,7 @@ static int cpr3_regulator_init_ctrl(struct cpr3_controller *ctrl) } } } } if (ctrl->ctrl_type == CPR_CTRL_TYPE_CPR4) { 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; } if (new_volt == last_volt && ctrl->ctrl_type == CPR_CTRL_TYPE_CPR4) { if (new_volt == last_volt && ctrl->supports_hw_closed_loop && ctrl->ctrl_type == CPR_CTRL_TYPE_CPR4) { /* * CPR4 features enforce voltage reprogramming when the last * 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; } /** * 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 * 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) { /* * Always program open-loop voltage for CPR4 controller. * Storing last closed loop voltage in corner structure would * help debug. * Always program open-loop voltage for CPR4 controllers which * support hardware closed-loop. Storing the last closed loop * voltage in corner structure can still help with debugging. */ new_volt = (ctrl->ctrl_type == CPR_CTRL_TYPE_CPR3 ? aggr_corner.last_volt : aggr_corner.open_loop_volt); if (ctrl->ctrl_type == CPR_CTRL_TYPE_CPR3) new_volt = aggr_corner.last_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 { new_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 * 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. */ 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 */ rc = regulator_set_voltage(ctrl->vdd_limit_regulator, aggr_corner.floor_volt, Loading Loading @@ -6051,9 +6097,7 @@ int cpr3_regulator_unregister(struct cpr3_controller *ctrl) cpr3_closed_loop_disable(ctrl); if ((ctrl->use_hw_closed_loop || ctrl->ctrl_type == CPR_CTRL_TYPE_CPR4) && ctrl->ctrl_type != CPR_CTRL_TYPE_CPRH) { if (ctrl->vdd_limit_regulator) { regulator_disable(ctrl->vdd_limit_regulator); if (ctrl->ctrl_type == CPR_CTRL_TYPE_CPR3) Loading