in qcom/cpr.c [461:587]
static int cpr_scale(struct cpr_drv *drv, enum voltage_change_dir dir)
{
u32 val, error_steps, reg_mask;
int last_uV, new_uV, step_uV, ret;
struct corner *corner;
const struct cpr_desc *desc = drv->desc;
if (dir != UP && dir != DOWN)
return 0;
step_uV = regulator_get_linear_step(drv->vdd_apc);
if (!step_uV)
return -EINVAL;
corner = drv->corner;
val = cpr_read(drv, REG_RBCPR_RESULT_0);
error_steps = val >> RBCPR_RESULT0_ERROR_STEPS_SHIFT;
error_steps &= RBCPR_RESULT0_ERROR_STEPS_MASK;
last_uV = corner->last_uV;
if (dir == UP) {
if (desc->clamp_timer_interval &&
error_steps < desc->up_threshold) {
/*
* Handle the case where another measurement started
* after the interrupt was triggered due to a core
* exiting from power collapse.
*/
error_steps = max(desc->up_threshold,
desc->vdd_apc_step_up_limit);
}
if (last_uV >= corner->max_uV) {
cpr_irq_clr_nack(drv);
/* Maximize the UP threshold */
reg_mask = RBCPR_CTL_UP_THRESHOLD_MASK;
reg_mask <<= RBCPR_CTL_UP_THRESHOLD_SHIFT;
val = reg_mask;
cpr_ctl_modify(drv, reg_mask, val);
/* Disable UP interrupt */
cpr_irq_set(drv, CPR_INT_DEFAULT & ~CPR_INT_UP);
return 0;
}
if (error_steps > desc->vdd_apc_step_up_limit)
error_steps = desc->vdd_apc_step_up_limit;
/* Calculate new voltage */
new_uV = last_uV + error_steps * step_uV;
new_uV = min(new_uV, corner->max_uV);
dev_dbg(drv->dev,
"UP: -> new_uV: %d last_uV: %d perf state: %u\n",
new_uV, last_uV, cpr_get_cur_perf_state(drv));
} else {
if (desc->clamp_timer_interval &&
error_steps < desc->down_threshold) {
/*
* Handle the case where another measurement started
* after the interrupt was triggered due to a core
* exiting from power collapse.
*/
error_steps = max(desc->down_threshold,
desc->vdd_apc_step_down_limit);
}
if (last_uV <= corner->min_uV) {
cpr_irq_clr_nack(drv);
/* Enable auto nack down */
reg_mask = RBCPR_CTL_SW_AUTO_CONT_NACK_DN_EN;
val = RBCPR_CTL_SW_AUTO_CONT_NACK_DN_EN;
cpr_ctl_modify(drv, reg_mask, val);
/* Disable DOWN interrupt */
cpr_irq_set(drv, CPR_INT_DEFAULT & ~CPR_INT_DOWN);
return 0;
}
if (error_steps > desc->vdd_apc_step_down_limit)
error_steps = desc->vdd_apc_step_down_limit;
/* Calculate new voltage */
new_uV = last_uV - error_steps * step_uV;
new_uV = max(new_uV, corner->min_uV);
dev_dbg(drv->dev,
"DOWN: -> new_uV: %d last_uV: %d perf state: %u\n",
new_uV, last_uV, cpr_get_cur_perf_state(drv));
}
ret = cpr_scale_voltage(drv, corner, new_uV, dir);
if (ret) {
cpr_irq_clr_nack(drv);
return ret;
}
drv->corner->last_uV = new_uV;
if (dir == UP) {
/* Disable auto nack down */
reg_mask = RBCPR_CTL_SW_AUTO_CONT_NACK_DN_EN;
val = 0;
} else {
/* Restore default threshold for UP */
reg_mask = RBCPR_CTL_UP_THRESHOLD_MASK;
reg_mask <<= RBCPR_CTL_UP_THRESHOLD_SHIFT;
val = desc->up_threshold;
val <<= RBCPR_CTL_UP_THRESHOLD_SHIFT;
}
cpr_ctl_modify(drv, reg_mask, val);
/* Re-enable default interrupts */
cpr_irq_set(drv, CPR_INT_DEFAULT);
/* Ack */
cpr_irq_clr_ack(drv);
return 0;
}