summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Collins <collinsd@codeaurora.org>2016-11-08 17:05:09 -0800
committerOsvaldo Banuelos <osvaldob@codeaurora.org>2016-11-28 14:51:18 -0800
commit1a9d62db8ec97df09feb30d03395a7d38ff2bf0b (patch)
tree7a4fef7e4b67cfb5dbb51e33a85aee5a8360dad3
parentfcb69346a4bb89439433c7f824efb0e690961d93 (diff)
regulator: cprh-regulator: add support for MEM ACC threshold voltage
Add support for configuring the memory accelerator (MEM ACC) threshold voltage and the MEM ACC crossover voltage. The threshold voltage is used to restrict the floor to ceiling voltage range of all corners so that they cannot cross the the MEM ACC threshold voltage due to CPR operation. The crossover voltage is set when switching the MEM ACC configuration. If specified, the APM and MEM ACC crossover voltages are added to the array of corners after all true corners. If both are specified, then the APM crossover corner is added before the MEM ACC crossover corner (i.e. last corner = MEM ACC crossover and second to last corner = APM crossover). CRs-Fixed: 1088429 Change-Id: I2b9b746071579ba9d4bcdcfb6cb755ca08a73182 Signed-off-by: David Collins <collinsd@codeaurora.org>
-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",