summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--drivers/regulator/cpr3-regulator.c10
-rw-r--r--drivers/regulator/cpr3-regulator.h5
-rw-r--r--drivers/regulator/cpr3-util.c75
-rw-r--r--drivers/regulator/cprh-kbss-regulator.c194
4 files changed, 201 insertions, 83 deletions
diff --git a/drivers/regulator/cpr3-regulator.c b/drivers/regulator/cpr3-regulator.c
index 23ac83eb30df..0df2b80ceca5 100644
--- a/drivers/regulator/cpr3-regulator.c
+++ b/drivers/regulator/cpr3-regulator.c
@@ -3691,14 +3691,16 @@ static void cpr3_regulator_readjust_volt_and_quot(struct cpr3_regulator *vreg,
static void cpr3_regulator_set_aging_ref_adjustment(
struct cpr3_controller *ctrl, int ref_adjust_volt)
{
+ struct cpr3_regulator *vreg;
int i, j;
for (i = 0; i < ctrl->thread_count; i++) {
for (j = 0; j < ctrl->thread[i].vreg_count; j++) {
- cpr3_regulator_readjust_volt_and_quot(
- &ctrl->thread[i].vreg[j],
- ctrl->aging_ref_adjust_volt,
- ref_adjust_volt);
+ vreg = &ctrl->thread[i].vreg[j];
+ cpr3_regulator_readjust_volt_and_quot(vreg,
+ ctrl->aging_ref_adjust_volt, ref_adjust_volt);
+ if (ctrl->ctrl_type == CPR_CTRL_TYPE_CPRH)
+ cprh_adjust_voltages_for_apm(vreg);
}
}
diff --git a/drivers/regulator/cpr3-regulator.h b/drivers/regulator/cpr3-regulator.h
index 8897def3ef76..ac571271b0d5 100644
--- a/drivers/regulator/cpr3-regulator.h
+++ b/drivers/regulator/cpr3-regulator.h
@@ -875,6 +875,7 @@ int cpr4_parse_core_count_temp_voltage_adj(struct cpr3_regulator *vreg,
bool use_corner_band);
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);
#else
@@ -1047,6 +1048,10 @@ static inline int cpr3_mem_acc_init(struct cpr3_regulator *vreg)
return 0;
}
+static inline void cprh_adjust_voltages_for_apm(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 e3e05b6ad352..c377a65a6393 100644
--- a/drivers/regulator/cpr3-util.c
+++ b/drivers/regulator/cpr3-util.c
@@ -2008,3 +2008,78 @@ done:
return rc;
}
+
+/**
+ * cprh_adjust_voltages_for_apm() - adjust per-corner floor and ceiling voltages
+ * so that they do not overlap the APM threshold voltage.
+ * @vreg: Pointer to the CPR3 regulator
+ *
+ * The memory array power mux (APM) must be configured for a specific supply
+ * based upon where the VDD voltage lies with respect to the APM threshold
+ * voltage. When using CPR hardware closed-loop, the voltage may vary anywhere
+ * between the floor and ceiling voltage without software notification.
+ * Therefore, it is required that the floor to ceiling range for every corner
+ * not intersect the APM threshold voltage. This function adjusts the floor to
+ * ceiling range for each corner which violates this requirement.
+ *
+ * The following algorithm is applied:
+ * if floor < threshold <= ceiling:
+ * if open_loop >= threshold, then floor = threshold - adj
+ * else ceiling = threshold - step
+ * where:
+ * adj = APM hysteresis voltage established to minimize the number of
+ * corners with artificially increased floor voltages
+ * 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_apm(struct cpr3_regulator *vreg)
+{
+ struct cpr3_controller *ctrl = vreg->thread->ctrl;
+ struct cpr3_corner *corner;
+ int i, adj, threshold, prev_ceiling, prev_floor, prev_open_loop;
+
+ if (!ctrl->apm_threshold_volt) {
+ /* APM not being used. */
+ return;
+ }
+
+ ctrl->apm_threshold_volt = CPR3_ROUND(ctrl->apm_threshold_volt,
+ ctrl->step_volt);
+ ctrl->apm_adj_volt = CPR3_ROUND(ctrl->apm_adj_volt, ctrl->step_volt);
+
+ threshold = ctrl->apm_threshold_volt;
+ adj = ctrl->apm_adj_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 - adj);
+ 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, "APM threshold=%d, APM adj=%d changed corner %d voltages; prev: floor=%d, ceiling=%d, open-loop=%d; new: floor=%d, ceiling=%d, open-loop=%d\n",
+ threshold, adj, 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 284180b0e72f..953ea5f33f40 100644
--- a/drivers/regulator/cprh-kbss-regulator.c
+++ b/drivers/regulator/cprh-kbss-regulator.c
@@ -54,6 +54,8 @@
* @force_highest_corner: Flag indicating that all corners must operate
* at the voltage of the highest corner. This is
* applicable to MSMCOBALT only.
+ * @aging_init_quot_diff: Initial quotient difference between CPR aging
+ * min and max sensors measured at time of manufacturing
*
* This struct holds the values for all of the fuses read from memory.
*/
@@ -65,6 +67,7 @@ struct cprh_msmcobalt_kbss_fuses {
u64 speed_bin;
u64 cpr_fusing_rev;
u64 force_highest_corner;
+ u64 aging_init_quot_diff;
};
/*
@@ -192,6 +195,18 @@ msmcobalt_cpr_force_highest_corner_param[] = {
{},
};
+static const struct cpr3_fuse_param
+msmcobalt_kbss_aging_init_quot_diff_param[2][2] = {
+ [MSMCOBALT_KBSS_POWER_CLUSTER_ID] = {
+ {69, 6, 13},
+ {},
+ },
+ [MSMCOBALT_KBSS_PERFORMANCE_CLUSTER_ID] = {
+ {71, 25, 32},
+ {},
+ },
+};
+
/*
* Open loop voltage fuse reference voltages in microvolts for MSMCOBALT v1
*/
@@ -225,6 +240,8 @@ msmcobalt_v2_kbss_fuse_ref_volt[2][MSMCOBALT_KBSS_FUSE_CORNERS] = {
#define MSMCOBALT_KBSS_FUSE_STEP_VOLT 10000
#define MSMCOBALT_KBSS_VOLTAGE_FUSE_SIZE 6
#define MSMCOBALT_KBSS_QUOT_OFFSET_SCALE 5
+#define MSMCOBALT_KBSS_AGING_INIT_QUOT_DIFF_SIZE 8
+#define MSMCOBALT_KBSS_AGING_INIT_QUOT_DIFF_SCALE 1
#define MSMCOBALT_KBSS_POWER_CPR_SENSOR_COUNT 6
#define MSMCOBALT_KBSS_PERFORMANCE_CPR_SENSOR_COUNT 9
@@ -242,6 +259,12 @@ msmcobalt_v2_kbss_fuse_ref_volt[2][MSMCOBALT_KBSS_FUSE_CORNERS] = {
#define MSMCOBALT_KBSS_PERFORMANCE_TEMP_SENSOR_ID_START 6
#define MSMCOBALT_KBSS_PERFORMANCE_TEMP_SENSOR_ID_END 11
+#define MSMCOBALT_KBSS_POWER_AGING_SENSOR_ID 0
+#define MSMCOBALT_KBSS_POWER_AGING_BYPASS_MASK0 0
+
+#define MSMCOBALT_KBSS_PERFORMANCE_AGING_SENSOR_ID 0
+#define MSMCOBALT_KBSS_PERFORMANCE_AGING_BYPASS_MASK0 0
+
/**
* cprh_msmcobalt_kbss_read_fuse_data() - load KBSS specific fuse parameter values
* @vreg: Pointer to the CPR3 regulator
@@ -321,6 +344,15 @@ static int cprh_msmcobalt_kbss_read_fuse_data(struct cpr3_regulator *vreg)
}
rc = cpr3_read_fuse_param(base,
+ msmcobalt_kbss_aging_init_quot_diff_param[id],
+ &fuse->aging_init_quot_diff);
+ if (rc) {
+ cpr3_err(vreg, "Unable to read aging initial quotient difference fuse, rc=%d\n",
+ rc);
+ return rc;
+ }
+
+ rc = cpr3_read_fuse_param(base,
msmcobalt_cpr_force_highest_corner_param,
&fuse->force_highest_corner);
if (rc) {
@@ -826,6 +858,7 @@ static int cprh_kbss_apm_crossover_as_corner(struct cpr3_regulator *vreg)
corner->floor_volt = ctrl->apm_crossover_volt;
corner->ceiling_volt = ctrl->apm_crossover_volt;
corner->open_loop_volt = ctrl->apm_crossover_volt;
+ corner->abs_ceiling_volt = ctrl->apm_crossover_volt;
corner->use_open_loop = true;
vreg->corner_count++;
@@ -833,79 +866,6 @@ static int cprh_kbss_apm_crossover_as_corner(struct cpr3_regulator *vreg)
}
/**
- * cprh_kbss_adjust_voltages_for_apm() - adjust per-corner floor and ceiling
- * voltages so that they do not overlap the APM threshold voltage.
- * @vreg: Pointer to the CPR3 regulator
- *
- * The KBSS memory array power mux (APM) must be configured for a specific
- * supply based upon where the VDD voltage lies with respect to the APM
- * threshold voltage. When using CPR hardware closed-loop, the voltage may vary
- * anywhere between the floor and ceiling voltage without software notification.
- * Therefore, it is required that the floor to ceiling range for every corner
- * not intersect the APM threshold voltage. This function adjusts the floor to
- * ceiling range for each corner which violates this requirement.
- *
- * The following algorithm is applied in the case that
- * floor < threshold <= ceiling:
- * if open_loop >= threshold, then floor = threshold - adj
- * else ceiling = threshold - step
- * where adj = APM hysteresis voltage established to minimize number
- * of corners with artificially increased floor voltages
- * and 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: 0 on success, errno on failure
- */
-static int cprh_kbss_adjust_voltages_for_apm(struct cpr3_regulator *vreg)
-{
- struct cpr3_controller *ctrl = vreg->thread->ctrl;
- struct cpr3_corner *corner;
- int i, adj, threshold, prev_ceiling, prev_floor, prev_open_loop;
-
- if (!ctrl->apm_threshold_volt) {
- /* APM not being used. */
- return 0;
- }
-
- ctrl->apm_threshold_volt = CPR3_ROUND(ctrl->apm_threshold_volt,
- ctrl->step_volt);
- ctrl->apm_adj_volt = CPR3_ROUND(ctrl->apm_adj_volt, ctrl->step_volt);
-
- threshold = ctrl->apm_threshold_volt;
- adj = ctrl->apm_adj_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 - adj);
- if (corner->open_loop_volt < corner->floor_volt)
- corner->open_loop_volt = corner->floor_volt;
- } else {
- corner->ceiling_volt = threshold - ctrl->step_volt;
- }
-
- cpr3_debug(vreg, "APM threshold=%d, APM adj=%d changed corner %d voltages; prev: floor=%d, ceiling=%d, open-loop=%d; new: floor=%d, ceiling=%d, open-loop=%d\n",
- threshold, adj, i, prev_floor, prev_ceiling,
- prev_open_loop, corner->floor_volt,
- corner->ceiling_volt, corner->open_loop_volt);
- }
-
- return 0;
-}
-
-/**
* cprh_msmcobalt_kbss_set_no_interpolation_quotients() - use the fused target
* quotient values for lower frequencies.
* @vreg: Pointer to the CPR3 regulator
@@ -1235,12 +1195,7 @@ static int cprh_kbss_init_regulator(struct cpr3_regulator *vreg)
return rc;
}
- rc = cprh_kbss_adjust_voltages_for_apm(vreg);
- if (rc) {
- cpr3_err(vreg, "unable to adjust voltages for APM\n, rc=%d\n",
- rc);
- return rc;
- }
+ cprh_adjust_voltages_for_apm(vreg);
cpr3_open_loop_voltage_as_ceiling(vreg);
@@ -1299,6 +1254,80 @@ static int cprh_kbss_init_regulator(struct cpr3_regulator *vreg)
}
/**
+ * cprh_kbss_init_aging() - perform KBSS CPRh controller specific aging
+ * initializations
+ * @ctrl: Pointer to the CPR3 controller
+ *
+ * Return: 0 on success, errno on failure
+ */
+static int cprh_kbss_init_aging(struct cpr3_controller *ctrl)
+{
+ struct cprh_msmcobalt_kbss_fuses *fuse = NULL;
+ struct cpr3_regulator *vreg;
+ u32 aging_ro_scale;
+ int i, j, rc;
+
+ for (i = 0; i < ctrl->thread_count; i++) {
+ for (j = 0; j < ctrl->thread[i].vreg_count; j++) {
+ if (ctrl->thread[i].vreg[j].aging_allowed) {
+ ctrl->aging_required = true;
+ vreg = &ctrl->thread[i].vreg[j];
+ fuse = vreg->platform_fuses;
+ break;
+ }
+ }
+ }
+
+ if (!ctrl->aging_required || !fuse || !vreg)
+ return 0;
+
+ rc = cpr3_parse_array_property(vreg, "qcom,cpr-aging-ro-scaling-factor",
+ 1, &aging_ro_scale);
+ if (rc)
+ return rc;
+
+ if (aging_ro_scale == 0) {
+ cpr3_err(ctrl, "aging RO scaling factor is invalid: %u\n",
+ aging_ro_scale);
+ return -EINVAL;
+ }
+
+ ctrl->aging_vdd_mode = REGULATOR_MODE_NORMAL;
+ ctrl->aging_complete_vdd_mode = REGULATOR_MODE_IDLE;
+
+ ctrl->aging_sensor_count = 1;
+ ctrl->aging_sensor = kzalloc(sizeof(*ctrl->aging_sensor), GFP_KERNEL);
+ if (!ctrl->aging_sensor)
+ return -ENOMEM;
+
+ if (ctrl->ctrl_id == MSMCOBALT_KBSS_POWER_CLUSTER_ID) {
+ ctrl->aging_sensor->sensor_id
+ = MSMCOBALT_KBSS_POWER_AGING_SENSOR_ID;
+ ctrl->aging_sensor->bypass_mask[0]
+ = MSMCOBALT_KBSS_POWER_AGING_BYPASS_MASK0;
+ } else {
+ ctrl->aging_sensor->sensor_id
+ = MSMCOBALT_KBSS_PERFORMANCE_AGING_SENSOR_ID;
+ ctrl->aging_sensor->bypass_mask[0]
+ = MSMCOBALT_KBSS_PERFORMANCE_AGING_BYPASS_MASK0;
+ }
+ ctrl->aging_sensor->ro_scale = aging_ro_scale;
+
+ ctrl->aging_sensor->init_quot_diff
+ = cpr3_convert_open_loop_voltage_fuse(0,
+ MSMCOBALT_KBSS_AGING_INIT_QUOT_DIFF_SCALE,
+ fuse->aging_init_quot_diff,
+ MSMCOBALT_KBSS_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,
+ ctrl->aging_sensor->init_quot_diff,
+ ctrl->aging_sensor->ro_scale);
+
+ return 0;
+}
+
+/**
* cprh_kbss_init_controller() - perform KBSS CPRh controller specific
* initializations
* @ctrl: Pointer to the CPR3 controller
@@ -1566,6 +1595,13 @@ static int cprh_kbss_regulator_probe(struct platform_device *pdev)
return rc;
}
+ rc = cprh_kbss_init_aging(ctrl);
+ if (rc) {
+ cpr3_err(ctrl, "failed to initialize aging configurations, rc=%d\n",
+ rc);
+ return rc;
+ }
+
platform_set_drvdata(pdev, ctrl);
rc = cprh_kbss_populate_opp_table(ctrl);