summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Documentation/devicetree/bindings/regulator/cprh-kbss-regulator.txt18
-rw-r--r--drivers/regulator/cpr3-regulator.h11
-rw-r--r--drivers/regulator/cpr3-util.c70
-rw-r--r--drivers/regulator/cprh-kbss-regulator.c73
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",