regulator: qpnp-regulator: add support for FTSMPS slew rate delay

PMIC Fast Transient SMPS regulators support voltage scaling with
a slew rate defined to ensure reliable voltage changes during
active operation.  Add support to read the step control register
in order to obtain the slew and use the slew rate to calculate an
appropriate delay during voltage change requests.

CRs-Fixed: 924563
Change-Id: I0aaea78d8ea4f0b7d1ff13eab7100fbc28868092
Signed-off-by: Osvaldo Banuelos <osvaldob@codeaurora.org>
Signed-off-by: David Collins <collinsd@codeaurora.org>
This commit is contained in:
Osvaldo Banuelos 2015-01-09 15:55:15 -08:00 committed by Tirupathi Reddy
parent 37b29b2b1c
commit 1b8888bf82
1 changed files with 104 additions and 4 deletions

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2012-2014, The Linux Foundation. All rights reserved.
* Copyright (c) 2012-2015, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@ -121,6 +121,7 @@ enum qpnp_common_regulator_registers {
QPNP_COMMON_REG_MODE = 0x45,
QPNP_COMMON_REG_ENABLE = 0x46,
QPNP_COMMON_REG_PULL_DOWN = 0x48,
QPNP_COMMON_REG_STEP_CTRL = 0x61,
};
enum qpnp_ldo_registers {
@ -195,6 +196,24 @@ enum qpnp_common_control_register_index {
#define QPNP_VS_OCP_FALL_DELAY_US 90
#define QPNP_VS_OCP_FAULT_DELAY_US 20000
#define QPNP_FTSMPS_STEP_CTRL_STEP_MASK 0x18
#define QPNP_FTSMPS_STEP_CTRL_STEP_SHIFT 3
#define QPNP_FTSMPS_STEP_CTRL_DELAY_MASK 0x07
#define QPNP_FTSMPS_STEP_CTRL_DELAY_SHIFT 0
/* Clock rate in kHz of the FTSMPS regulator reference clock. */
#define QPNP_FTSMPS_CLOCK_RATE 19200
/* Minimum voltage stepper delay for each step. */
#define QPNP_FTSMPS_STEP_DELAY 8
/*
* The ratio QPNP_FTSMPS_STEP_MARGIN_NUM/QPNP_FTSMPS_STEP_MARGIN_DEN is used to
* adjust the step rate in order to account for oscillator variance.
*/
#define QPNP_FTSMPS_STEP_MARGIN_NUM 4
#define QPNP_FTSMPS_STEP_MARGIN_DEN 5
/*
* This voltage in uV is returned by get_voltage functions when there is no way
* to determine the current voltage level. It is needed because the regulator
@ -275,6 +294,7 @@ struct qpnp_regulator {
int ocp_retry_delay_ms;
int system_load;
int hpm_min_load;
int slew_rate;
u32 write_count;
u32 prev_write_count;
ktime_t vs_enable_time;
@ -777,9 +797,18 @@ static int qpnp_regulator_common_set_voltage(struct regulator_dev *rdev,
int min_uV, int max_uV, unsigned *selector)
{
struct qpnp_regulator *vreg = rdev_get_drvdata(rdev);
int rc, range_sel, voltage_sel;
int rc, range_sel, voltage_sel, voltage_old, voltage_new;
u8 buf[2];
if (vreg->slew_rate && vreg->rdesc.ops->get_voltage) {
voltage_old = vreg->rdesc.ops->get_voltage(rdev);
if (voltage_old < 0) {
vreg_err(vreg, "could not get current voltage, rc=%d\n",
voltage_old);
return voltage_old;
}
}
/*
* Favor staying in the current voltage range if possible. This avoids
* voltage spikes that occur when changing the voltage range.
@ -812,10 +841,24 @@ static int qpnp_regulator_common_set_voltage(struct regulator_dev *rdev,
&vreg->ctrl_reg[QPNP_COMMON_IDX_VOLTAGE_RANGE], 2);
}
if (rc)
if (rc) {
vreg_err(vreg, "SPMI write failed, rc=%d\n", rc);
else
} else {
/* Delay for voltage slewing if a step rate is specified. */
if (vreg->slew_rate && vreg->rdesc.ops->get_voltage) {
voltage_new = vreg->rdesc.ops->get_voltage(rdev);
if (voltage_new < 0) {
vreg_err(vreg, "could not get new voltage, rc=%d\n",
voltage_new);
return voltage_new;
}
udelay(DIV_ROUND_UP(abs(voltage_new - voltage_old),
vreg->slew_rate));
}
qpnp_vreg_show_state(rdev, QPNP_REGULATOR_ACTION_VOLTAGE);
}
return rc;
}
@ -1521,6 +1564,53 @@ static int qpnp_regulator_match(struct qpnp_regulator *vreg)
return rc;
}
static int qpnp_regulator_ftsmps_init_slew_rate(struct qpnp_regulator *vreg)
{
int rc;
u8 reg = 0;
int step = 0, delay, i, range_sel;
struct qpnp_voltage_range *range = NULL;
rc = qpnp_vreg_read(vreg, QPNP_COMMON_REG_STEP_CTRL, &reg, 1);
if (rc) {
vreg_err(vreg, "spmi read failed, rc=%d\n", rc);
return rc;
}
range_sel = vreg->ctrl_reg[QPNP_COMMON_IDX_VOLTAGE_RANGE];
for (i = 0; i < vreg->set_points->count; i++) {
if (vreg->set_points->range[i].range_sel == range_sel) {
range = &vreg->set_points->range[i];
break;
}
}
if (!range) {
vreg_err(vreg, "range %d is invalid\n", range_sel);
return -EINVAL;
}
step = (reg & QPNP_FTSMPS_STEP_CTRL_STEP_MASK)
>> QPNP_FTSMPS_STEP_CTRL_STEP_SHIFT;
delay = (reg & QPNP_FTSMPS_STEP_CTRL_DELAY_MASK)
>> QPNP_FTSMPS_STEP_CTRL_DELAY_SHIFT;
/* slew_rate has units of uV/us. */
vreg->slew_rate = QPNP_FTSMPS_CLOCK_RATE * range->step_uV * (1 << step);
vreg->slew_rate /= 1000 * (QPNP_FTSMPS_STEP_DELAY << delay);
vreg->slew_rate = vreg->slew_rate * QPNP_FTSMPS_STEP_MARGIN_NUM
/ QPNP_FTSMPS_STEP_MARGIN_DEN;
/* Ensure that the slew rate is greater than 0. */
vreg->slew_rate = max(vreg->slew_rate, 1);
return rc;
}
static int qpnp_regulator_init_registers(struct qpnp_regulator *vreg,
struct qpnp_regulator_platform_data *pdata)
{
@ -1730,6 +1820,16 @@ static int qpnp_regulator_init_registers(struct qpnp_regulator *vreg,
}
}
/* Calculate the slew rate for FTSMPS regulators. */
if (type == QPNP_REGULATOR_LOGICAL_TYPE_FTSMPS) {
rc = qpnp_regulator_ftsmps_init_slew_rate(vreg);
if (rc) {
vreg_err(vreg, "failed to initialize step rate, rc=%d\n",
rc);
return rc;
}
}
return rc;
}