diff options
| -rw-r--r-- | Documentation/devicetree/bindings/regulator/cprh-kbss-regulator.txt | 18 | ||||
| -rw-r--r-- | drivers/regulator/cpr3-regulator.h | 11 | ||||
| -rw-r--r-- | drivers/regulator/cpr3-util.c | 70 | ||||
| -rw-r--r-- | drivers/regulator/cprh-kbss-regulator.c | 73 |
4 files changed, 169 insertions, 3 deletions
diff --git a/Documentation/devicetree/bindings/regulator/cprh-kbss-regulator.txt b/Documentation/devicetree/bindings/regulator/cprh-kbss-regulator.txt index 8b806f4828bd..5b0770785dbe 100644 --- a/Documentation/devicetree/bindings/regulator/cprh-kbss-regulator.txt +++ b/Documentation/devicetree/bindings/regulator/cprh-kbss-regulator.txt @@ -73,6 +73,24 @@ KBSS specific properties: switching. If this property is not specified, then a value of 0 is assumed. +- qcom,mem-acc-threshold-voltage + Usage: optional + Value type: <u32> + Definition: Specifies the highest memory accelerator (MEM ACC) threshold + voltage in microvolts. The floor to ceiling voltage range + for every corner is adjusted to ensure that it does not + intersect this voltage. The value of this property must + match with the MEM ACC threshold voltage defined in the OSM + device to ensure that MEM ACC settings are switched + appropriately. + +- qcom,mem-acc-crossover-voltage + Usage: required if qcom,mem-acc-threshold-voltage is specified + Value type: <u32> + Definition: Specifies the MEM ACC crossover voltage in microvolts which + corresponds to the voltage the VDD supply must be set to + when switching the MEM ACC configuration. + - qcom,voltage-base Usage: required Value type: <u32> diff --git a/drivers/regulator/cpr3-regulator.h b/drivers/regulator/cpr3-regulator.h index ac571271b0d5..3c652ca8d489 100644 --- a/drivers/regulator/cpr3-regulator.h +++ b/drivers/regulator/cpr3-regulator.h @@ -565,6 +565,11 @@ struct cpr3_panic_regs_info { * @mem_acc_corner_map: mem-acc regulator corners mapping to low and high * voltage mem-acc settings for the memories powered by * this CPR3 controller and its associated CPR3 regulators + * @mem_acc_crossover_volt: Voltage in microvolts corresponding to the voltage + * that the VDD supply must be set to while a MEM ACC + * switch is in progress. This element must be initialized + * for CPRh controllers when a MEM ACC threshold voltage is + * defined. * @core_clk: Pointer to the CPR3 controller core clock * @iface_clk: Pointer to the CPR3 interface clock (platform specific) * @bus_clk: Pointer to the CPR3 bus clock (platform specific) @@ -744,6 +749,7 @@ struct cpr3_controller { int system_supply_max_volt; int mem_acc_threshold_volt; int mem_acc_corner_map[CPR3_MEM_ACC_CORNERS]; + int mem_acc_crossover_volt; struct clk *core_clk; struct clk *iface_clk; struct clk *bus_clk; @@ -876,6 +882,7 @@ int cpr4_parse_core_count_temp_voltage_adj(struct cpr3_regulator *vreg, int cpr3_apm_init(struct cpr3_controller *ctrl); int cpr3_mem_acc_init(struct cpr3_regulator *vreg); void cprh_adjust_voltages_for_apm(struct cpr3_regulator *vreg); +void cprh_adjust_voltages_for_mem_acc(struct cpr3_regulator *vreg); #else @@ -1052,6 +1059,10 @@ static inline void cprh_adjust_voltages_for_apm(struct cpr3_regulator *vreg) { } +static inline void cprh_adjust_voltages_for_mem_acc(struct cpr3_regulator *vreg) +{ +} + #endif /* CONFIG_REGULATOR_CPR3 */ #endif /* __REGULATOR_CPR_REGULATOR_H__ */ diff --git a/drivers/regulator/cpr3-util.c b/drivers/regulator/cpr3-util.c index c377a65a6393..60fe825ca013 100644 --- a/drivers/regulator/cpr3-util.c +++ b/drivers/regulator/cpr3-util.c @@ -680,12 +680,13 @@ int cpr3_parse_common_corner_data(struct cpr3_regulator *vreg) } /* - * In CPRh compliant controllers an additional corner is - * allocated to correspond to the APM crossover voltage + * For CPRh compliant controllers two additional corners are + * allocated to correspond to the APM crossover voltage and the MEM ACC + * crossover voltage. */ vreg->corner = devm_kcalloc(ctrl->dev, ctrl->ctrl_type == CPR_CTRL_TYPE_CPRH ? - vreg->corner_count + 1 : + vreg->corner_count + 2 : vreg->corner_count, sizeof(*vreg->corner), GFP_KERNEL); temp = kcalloc(vreg->corner_count, sizeof(*temp), GFP_KERNEL); @@ -2083,3 +2084,66 @@ void cprh_adjust_voltages_for_apm(struct cpr3_regulator *vreg) corner->ceiling_volt, corner->open_loop_volt); } } + +/** + * cprh_adjust_voltages_for_mem_acc() - adjust per-corner floor and ceiling + * voltages so that they do not intersect the MEM ACC threshold + * voltage + * @vreg: Pointer to the CPR3 regulator + * + * The following algorithm is applied: + * if floor < threshold <= ceiling: + * if open_loop >= threshold, then floor = threshold + * else ceiling = threshold - step + * where: + * step = voltage in microvolts of a single step of the VDD supply + * + * The open-loop voltage is also bounded by the new floor or ceiling value as + * needed. + * + * Return: none + */ +void cprh_adjust_voltages_for_mem_acc(struct cpr3_regulator *vreg) +{ + struct cpr3_controller *ctrl = vreg->thread->ctrl; + struct cpr3_corner *corner; + int i, threshold, prev_ceiling, prev_floor, prev_open_loop; + + if (!ctrl->mem_acc_threshold_volt) { + /* MEM ACC not being used. */ + return; + } + + ctrl->mem_acc_threshold_volt = CPR3_ROUND(ctrl->mem_acc_threshold_volt, + ctrl->step_volt); + + threshold = ctrl->mem_acc_threshold_volt; + + for (i = 0; i < vreg->corner_count; i++) { + corner = &vreg->corner[i]; + + if (threshold <= corner->floor_volt + || threshold > corner->ceiling_volt) + continue; + + prev_floor = corner->floor_volt; + prev_ceiling = corner->ceiling_volt; + prev_open_loop = corner->open_loop_volt; + + if (corner->open_loop_volt >= threshold) { + corner->floor_volt = max(corner->floor_volt, threshold); + if (corner->open_loop_volt < corner->floor_volt) + corner->open_loop_volt = corner->floor_volt; + } else { + corner->ceiling_volt = threshold - ctrl->step_volt; + } + + if (corner->floor_volt != prev_floor + || corner->ceiling_volt != prev_ceiling + || corner->open_loop_volt != prev_open_loop) + cpr3_debug(vreg, "MEM ACC threshold=%d changed corner %d voltages; prev: floor=%d, ceiling=%d, open-loop=%d; new: floor=%d, ceiling=%d, open-loop=%d\n", + threshold, i, prev_floor, prev_ceiling, + prev_open_loop, corner->floor_volt, + corner->ceiling_volt, corner->open_loop_volt); + } +} diff --git a/drivers/regulator/cprh-kbss-regulator.c b/drivers/regulator/cprh-kbss-regulator.c index f5ba9aef632f..9cbd1ee18ec3 100644 --- a/drivers/regulator/cprh-kbss-regulator.c +++ b/drivers/regulator/cprh-kbss-regulator.c @@ -868,6 +868,44 @@ static int cprh_kbss_apm_crossover_as_corner(struct cpr3_regulator *vreg) } /** + * cprh_kbss_mem_acc_crossover_as_corner() - introduce a corner whose floor, + * open-loop, and ceiling voltages correspond to the MEM ACC + * crossover voltage. + * @vreg: Pointer to the CPR3 regulator + * + * The MEM ACC corner is utilized as a crossover corner by OSM and CPRh + * hardware to set the VDD supply voltage during the MEM ACC switch + * routine. + * + * Return: 0 on success, errno on failure + */ +static int cprh_kbss_mem_acc_crossover_as_corner(struct cpr3_regulator *vreg) +{ + struct cpr3_controller *ctrl = vreg->thread->ctrl; + struct cpr3_corner *corner; + + if (!ctrl->mem_acc_crossover_volt) { + /* MEM ACC voltage crossover corner not required. */ + return 0; + } + + corner = &vreg->corner[vreg->corner_count]; + /* + * 0 MHz indicates this corner is not to be + * used as active DCVS set point. + */ + corner->proc_freq = 0; + corner->floor_volt = ctrl->mem_acc_crossover_volt; + corner->ceiling_volt = ctrl->mem_acc_crossover_volt; + corner->open_loop_volt = ctrl->mem_acc_crossover_volt; + corner->abs_ceiling_volt = ctrl->mem_acc_crossover_volt; + corner->use_open_loop = true; + vreg->corner_count++; + + return 0; +} + +/** * cprh_msm8998_kbss_set_no_interpolation_quotients() - use the fused target * quotient values for lower frequencies. * @vreg: Pointer to the CPR3 regulator @@ -1198,6 +1236,7 @@ static int cprh_kbss_init_regulator(struct cpr3_regulator *vreg) } cprh_adjust_voltages_for_apm(vreg); + cprh_adjust_voltages_for_mem_acc(vreg); cpr3_open_loop_voltage_as_ceiling(vreg); @@ -1250,6 +1289,13 @@ static int cprh_kbss_init_regulator(struct cpr3_regulator *vreg) return rc; } + rc = cprh_kbss_mem_acc_crossover_as_corner(vreg); + if (rc) { + cpr3_err(vreg, "unable to introduce MEM ACC voltage crossover corner, rc=%d\n", + rc); + return rc; + } + cprh_kbss_print_settings(vreg); return 0; @@ -1422,6 +1468,25 @@ static int cprh_kbss_init_controller(struct cpr3_controller *ctrl) ctrl->saw_use_unit_mV = of_property_read_bool(ctrl->dev->of_node, "qcom,cpr-saw-use-unit-mV"); + rc = of_property_read_u32(ctrl->dev->of_node, + "qcom,mem-acc-threshold-voltage", + &ctrl->mem_acc_threshold_volt); + if (!rc) { + ctrl->mem_acc_threshold_volt + = CPR3_ROUND(ctrl->mem_acc_threshold_volt, ctrl->step_volt); + + rc = of_property_read_u32(ctrl->dev->of_node, + "qcom,mem-acc-crossover-voltage", + &ctrl->mem_acc_crossover_volt); + if (rc) { + cpr3_err(ctrl, "error reading property qcom,mem-acc-crossover-voltage, rc=%d\n", + rc); + return rc; + } + ctrl->mem_acc_crossover_volt + = CPR3_ROUND(ctrl->mem_acc_crossover_volt, ctrl->step_volt); + } + /* * Use fixed step quotient if specified otherwise use dynamically * calculated per RO step quotient @@ -1477,6 +1542,14 @@ static int cprh_kbss_populate_opp_table(struct cpr3_controller *ctrl) for (i = 0; i < vreg->corner_count; i++) { corner = &vreg->corner[i]; + if (!corner->proc_freq) { + /* + * 0 MHz indicates this corner is not to be + * used as active DCVS set point. Don't add it + * to the OPP table. + */ + continue; + } rc = dev_pm_opp_add(dev, corner->proc_freq, i + 1); if (rc) { cpr3_err(ctrl, "could not add OPP for corner %d with frequency %u MHz, rc=%d\n", |
