regulator: cpr3-regulator: add support for CPRh aging adjustment

Add support to perform a CPR aging measurement and subsequent
target quotient and open-loop voltage adjustments for CPRh
controllers at registration time.

Change-Id: I3a48a912fb9dea37a6197c1a13c2b41454d2dcff
CRs-Fixed: 1048890
Signed-off-by: David Collins <collinsd@codeaurora.org>
This commit is contained in:
David Collins 2016-09-02 18:09:57 -07:00
parent 0b3886773c
commit f49fa83627
2 changed files with 160 additions and 12 deletions

View file

@ -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
@ -3866,6 +3886,126 @@ cleanup:
return rc ? rc : rc2;
}
/**
* 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

View file

@ -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)) {