summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--drivers/regulator/cpr3-regulator.c146
-rw-r--r--drivers/regulator/cpr3-util.c26
2 files changed, 160 insertions, 12 deletions
diff --git a/drivers/regulator/cpr3-regulator.c b/drivers/regulator/cpr3-regulator.c
index cd02debc37aa..23ac83eb30df 100644
--- a/drivers/regulator/cpr3-regulator.c
+++ b/drivers/regulator/cpr3-regulator.c
@@ -1264,6 +1264,8 @@ static void cprh_controller_program_sdelta(
mb();
}
+static int cprh_regulator_aging_adjust(struct cpr3_controller *ctrl);
+
/**
* cpr3_regulator_init_cprh() - performs hardware initialization at the
* controller and thread level required for CPRh operation.
@@ -1290,6 +1292,16 @@ static int cpr3_regulator_init_cprh(struct cpr3_controller *ctrl)
return -EINVAL;
}
+ rc = cprh_regulator_aging_adjust(ctrl);
+ if (rc && rc != -ETIMEDOUT) {
+ /*
+ * Don't fail initialization if the CPR aging measurement
+ * timed out due to sensors not being available.
+ */
+ cpr3_err(ctrl, "CPR aging adjustment failed, rc=%d\n", rc);
+ return rc;
+ }
+
cprh_controller_program_sdelta(ctrl);
rc = cpr3_regulator_init_cprh_corners(&ctrl->thread[0].vreg[0]);
@@ -3346,7 +3358,7 @@ static int cpr3_regulator_measure_aging(struct cpr3_controller *ctrl,
u32 mask, reg, result, quot_min, quot_max, sel_min, sel_max;
u32 quot_min_scaled, quot_max_scaled;
u32 gcnt, gcnt_ref, gcnt0_restore, gcnt1_restore, irq_restore;
- u32 cont_dly_restore, up_down_dly_restore = 0;
+ u32 ro_mask_restore, cont_dly_restore, up_down_dly_restore = 0;
int quot_delta, quot_delta_scaled, quot_delta_scaled_sum;
int *quot_delta_results;
int rc, rc2, i, aging_measurement_count, filtered_count;
@@ -3379,7 +3391,8 @@ static int cpr3_regulator_measure_aging(struct cpr3_controller *ctrl,
/* Switch from HW to SW closed-loop if necessary */
if (ctrl->supports_hw_closed_loop) {
- if (ctrl->ctrl_type == CPR_CTRL_TYPE_CPR4) {
+ if (ctrl->ctrl_type == CPR_CTRL_TYPE_CPR4 ||
+ ctrl->ctrl_type == CPR_CTRL_TYPE_CPRH) {
cpr3_masked_write(ctrl, CPR4_REG_MARGIN_ADJ_CTL,
CPR4_MARGIN_ADJ_CTL_HW_CLOSED_LOOP_EN_MASK,
CPR4_MARGIN_ADJ_CTL_HW_CLOSED_LOOP_DISABLE);
@@ -3397,6 +3410,10 @@ static int cpr3_regulator_measure_aging(struct cpr3_controller *ctrl,
cpr3_write(ctrl, CPR3_REG_GCNT(0), gcnt);
cpr3_write(ctrl, CPR3_REG_GCNT(1), gcnt);
+ /* Unmask all RO's */
+ ro_mask_restore = cpr3_read(ctrl, CPR3_REG_RO_MASK(0));
+ cpr3_write(ctrl, CPR3_REG_RO_MASK(0), 0);
+
/*
* Mask all sensors except for the one to measure and bypass all
* sensors in collapsible domains.
@@ -3535,6 +3552,8 @@ cleanup:
cpr3_write(ctrl, CPR3_REG_IRQ_EN, irq_restore);
+ cpr3_write(ctrl, CPR3_REG_RO_MASK(0), ro_mask_restore);
+
cpr3_write(ctrl, CPR3_REG_GCNT(0), gcnt0_restore);
cpr3_write(ctrl, CPR3_REG_GCNT(1), gcnt1_restore);
@@ -3565,7 +3584,8 @@ cleanup:
CPR3_IRQ_UP | CPR3_IRQ_DOWN | CPR3_IRQ_MID);
if (ctrl->supports_hw_closed_loop) {
- if (ctrl->ctrl_type == CPR_CTRL_TYPE_CPR4) {
+ if (ctrl->ctrl_type == CPR_CTRL_TYPE_CPR4 ||
+ ctrl->ctrl_type == CPR_CTRL_TYPE_CPRH) {
cpr3_masked_write(ctrl, CPR4_REG_MARGIN_ADJ_CTL,
CPR4_MARGIN_ADJ_CTL_HW_CLOSED_LOOP_EN_MASK,
ctrl->use_hw_closed_loop
@@ -3867,6 +3887,126 @@ cleanup:
}
/**
+ * cprh_regulator_aging_adjust() - adjust the target quotients and open-loop
+ * voltages for CPRh regulators based on the output of CPR aging
+ * sensors
+ * @ctrl: Pointer to the CPR3 controller
+ *
+ * Return: 0 on success, errno on failure
+ */
+static int cprh_regulator_aging_adjust(struct cpr3_controller *ctrl)
+{
+ int i, j, id, rc, rc2, aging_volt, init_volt;
+ int max_aging_volt = 0;
+ u32 reg;
+
+ if (!ctrl->aging_required || !ctrl->cpr_enabled)
+ return 0;
+
+ if (!ctrl->vdd_regulator) {
+ cpr3_err(ctrl, "vdd-supply regulator missing\n");
+ return -ENODEV;
+ }
+
+ init_volt = regulator_get_voltage(ctrl->vdd_regulator);
+ if (init_volt < 0) {
+ cpr3_err(ctrl, "could not get vdd-supply voltage, rc=%d\n",
+ init_volt);
+ return init_volt;
+ }
+
+ if (init_volt > ctrl->aging_ref_volt) {
+ cpr3_info(ctrl, "unable to perform CPR aging measurement as vdd=%d uV > aging voltage=%d uV\n",
+ init_volt, ctrl->aging_ref_volt);
+ return 0;
+ }
+
+ /* Verify that none of the aging sensors are currently masked. */
+ for (i = 0; i < ctrl->aging_sensor_count; i++) {
+ id = ctrl->aging_sensor[i].sensor_id;
+ reg = cpr3_read(ctrl, CPR3_REG_SENSOR_MASK_READ(id));
+ if (reg & BIT(id % 32)) {
+ cpr3_info(ctrl, "unable to perform CPR aging measurement as CPR sensor %d is masked\n",
+ id);
+ return 0;
+ }
+ }
+
+ rc = regulator_set_voltage(ctrl->vdd_regulator, ctrl->aging_ref_volt,
+ INT_MAX);
+ if (rc) {
+ cpr3_err(ctrl, "unable to set vdd-supply to aging voltage=%d uV, rc=%d\n",
+ ctrl->aging_ref_volt, rc);
+ return rc;
+ }
+
+ if (ctrl->aging_vdd_mode) {
+ rc = regulator_set_mode(ctrl->vdd_regulator,
+ ctrl->aging_vdd_mode);
+ if (rc) {
+ cpr3_err(ctrl, "unable to configure vdd-supply for mode=%u, rc=%d\n",
+ ctrl->aging_vdd_mode, rc);
+ goto cleanup;
+ }
+ }
+
+ /* Perform aging measurement on all aging sensors */
+ for (i = 0; i < ctrl->aging_sensor_count; i++) {
+ for (j = 0; j < CPR3_AGING_RETRY_COUNT; j++) {
+ rc = cpr3_regulator_measure_aging(ctrl,
+ &ctrl->aging_sensor[i]);
+ if (!rc)
+ break;
+ }
+
+ if (!rc) {
+ aging_volt =
+ cpr3_voltage_adjustment(
+ ctrl->aging_sensor[i].ro_scale,
+ ctrl->aging_sensor[i].measured_quot_diff
+ - ctrl->aging_sensor[i].init_quot_diff);
+ max_aging_volt = max(max_aging_volt, aging_volt);
+ } else {
+ cpr3_err(ctrl, "CPR aging measurement failed after %d tries, rc=%d\n",
+ j, rc);
+ ctrl->aging_failed = true;
+ ctrl->aging_required = false;
+ goto cleanup;
+ }
+ }
+
+cleanup:
+ /* Adjust the CPR target quotients according to the aging measurement */
+ if (!rc) {
+ cpr3_regulator_set_aging_ref_adjustment(ctrl, max_aging_volt);
+
+ cpr3_info(ctrl, "aging measurement successful; aging reference adjustment voltage=%d uV\n",
+ ctrl->aging_ref_adjust_volt);
+ ctrl->aging_succeeded = true;
+ ctrl->aging_required = false;
+ }
+
+ rc2 = regulator_set_voltage(ctrl->vdd_regulator, init_volt, INT_MAX);
+ if (rc2) {
+ cpr3_err(ctrl, "unable to reset vdd-supply to initial voltage=%d uV, rc=%d\n",
+ init_volt, rc2);
+ return rc2;
+ }
+
+ if (ctrl->aging_complete_vdd_mode) {
+ rc2 = regulator_set_mode(ctrl->vdd_regulator,
+ ctrl->aging_complete_vdd_mode);
+ if (rc2) {
+ cpr3_err(ctrl, "unable to configure vdd-supply for mode=%u, rc=%d\n",
+ ctrl->aging_complete_vdd_mode, rc2);
+ return rc2;
+ }
+ }
+
+ return rc;
+}
+
+/**
* cpr3_regulator_update_ctrl_state() - update the state of the CPR controller
* to reflect the corners used by all CPR3 regulators as well as
* the CPR operating mode and perform aging adjustments if needed
diff --git a/drivers/regulator/cpr3-util.c b/drivers/regulator/cpr3-util.c
index 51179f28fcf5..e3e05b6ad352 100644
--- a/drivers/regulator/cpr3-util.c
+++ b/drivers/regulator/cpr3-util.c
@@ -1202,6 +1202,23 @@ int cpr3_parse_common_ctrl_data(struct cpr3_controller *ctrl)
if (rc)
return rc;
+ ctrl->vdd_regulator = devm_regulator_get(ctrl->dev, "vdd");
+ if (IS_ERR(ctrl->vdd_regulator)) {
+ rc = PTR_ERR(ctrl->vdd_regulator);
+ if (rc != -EPROBE_DEFER) {
+ /* vdd-supply is optional for CPRh controllers. */
+ if (ctrl->ctrl_type == CPR_CTRL_TYPE_CPRH) {
+ cpr3_debug(ctrl, "unable to request vdd regulator, rc=%d\n",
+ rc);
+ ctrl->vdd_regulator = NULL;
+ return 0;
+ }
+ cpr3_err(ctrl, "unable to request vdd regulator, rc=%d\n",
+ rc);
+ }
+ return rc;
+ }
+
/*
* Regulator device handles are not necessary for CPRh controllers
* since communication with the regulators is completely managed
@@ -1210,15 +1227,6 @@ int cpr3_parse_common_ctrl_data(struct cpr3_controller *ctrl)
if (ctrl->ctrl_type == CPR_CTRL_TYPE_CPRH)
return rc;
- ctrl->vdd_regulator = devm_regulator_get(ctrl->dev, "vdd");
- if (IS_ERR(ctrl->vdd_regulator)) {
- rc = PTR_ERR(ctrl->vdd_regulator);
- if (rc != -EPROBE_DEFER)
- cpr3_err(ctrl, "unable request vdd regulator, rc=%d\n",
- rc);
- return rc;
- }
-
ctrl->system_regulator = devm_regulator_get_optional(ctrl->dev,
"system");
if (IS_ERR(ctrl->system_regulator)) {