diff options
| -rw-r--r-- | drivers/regulator/qpnp-regulator.c | 99 |
1 files changed, 99 insertions, 0 deletions
diff --git a/drivers/regulator/qpnp-regulator.c b/drivers/regulator/qpnp-regulator.c index bd706658348d..08e991fa7db3 100644 --- a/drivers/regulator/qpnp-regulator.c +++ b/drivers/regulator/qpnp-regulator.c @@ -127,6 +127,9 @@ enum qpnp_common_regulator_registers { QPNP_COMMON_REG_ENABLE = 0x46, QPNP_COMMON_REG_PULL_DOWN = 0x48, QPNP_COMMON_REG_STEP_CTRL = 0x61, + QPNP_COMMON_REG_UL_LL_CTRL = 0x68, + QPNP_COMMON_REG_VOLTAGE_ULS_VALID = 0x6A, + QPNP_COMMON_REG_VOLTAGE_LLS_VALID = 0x6C, }; /* @@ -139,6 +142,8 @@ enum qpnp_common2_regulator_registers { QPNP_COMMON2_REG_VOLTAGE_MSB = 0x41, QPNP_COMMON2_REG_MODE = 0x45, QPNP_COMMON2_REG_STEP_CTRL = 0x61, + QPNP_COMMON2_REG_VOLTAGE_ULS_LSB = 0x68, + QPNP_COMMON2_REG_VOLTAGE_ULS_MSB = 0x69, }; enum qpnp_ldo_registers { @@ -205,6 +210,10 @@ enum qpnp_common2_control_register_index { /* Common regulator pull down control register layout */ #define QPNP_COMMON_PULL_DOWN_ENABLE_MASK 0x80 +/* Common regulator UL & LL limits control register layout */ +#define QPNP_COMMON_UL_EN_MASK 0x80 +#define QPNP_COMMON_LL_EN_MASK 0x40 + /* LDO regulator current limit control register layout */ #define QPNP_LDO_CURRENT_LIMIT_ENABLE_MASK 0x80 @@ -1749,6 +1758,89 @@ static int qpnp_regulator_match(struct qpnp_regulator *vreg) return rc; } +static int qpnp_regulator_check_constraints(struct qpnp_regulator *vreg, + struct qpnp_regulator_platform_data *pdata) +{ + struct qpnp_voltage_range *range = NULL; + int i, rc = 0, limit_min_uV, limit_max_uV, max_uV; + u8 reg[2]; + + limit_min_uV = 0; + limit_max_uV = INT_MAX; + + if (vreg->logical_type == QPNP_REGULATOR_LOGICAL_TYPE_FTSMPS) { + max_uV = pdata->init_data.constraints.max_uV; + /* Find the range which max_uV is inside of. */ + for (i = vreg->set_points->count - 1; i > 0; i--) { + range = &vreg->set_points->range[i]; + if (range->set_point_max_uV > 0 + && max_uV >= range->set_point_min_uV + && max_uV <= range->set_point_max_uV) + break; + } + + if (i < 0 || range == NULL) { + vreg_err(vreg, "max_uV doesn't fit in any voltage range\n"); + return -EINVAL; + } + + rc = qpnp_vreg_read(vreg, QPNP_COMMON_REG_UL_LL_CTRL, + ®[0], 1); + if (rc) { + vreg_err(vreg, "UL_LL register read failed, rc=%d\n", + rc); + return rc; + } + + if (reg[0] & QPNP_COMMON_UL_EN_MASK) { + rc = qpnp_vreg_read(vreg, + QPNP_COMMON_REG_VOLTAGE_ULS_VALID, + ®[1], 1); + if (rc) { + vreg_err(vreg, "ULS_VALID register read failed, rc=%d\n", + rc); + return rc; + } + + limit_max_uV = range->step_uV * reg[1] + range->min_uV; + } + + if (reg[0] & QPNP_COMMON_LL_EN_MASK) { + rc = qpnp_vreg_read(vreg, + QPNP_COMMON_REG_VOLTAGE_LLS_VALID, + ®[1], 1); + if (rc) { + vreg_err(vreg, "LLS_VALID register read failed, rc=%d\n", + rc); + return rc; + } + + limit_min_uV = range->step_uV * reg[1] + range->min_uV; + } + } else if (vreg->logical_type == QPNP_REGULATOR_LOGICAL_TYPE_FTSMPS2) { + rc = qpnp_vreg_read(vreg, QPNP_COMMON2_REG_VOLTAGE_ULS_LSB, + reg, 2); + if (rc) { + vreg_err(vreg, "ULS registers read failed, rc=%d\n", + rc); + return rc; + } + + limit_max_uV = (((int)reg[1] << 8) | (int)reg[0]) * 1000; + } + + if (pdata->init_data.constraints.min_uV < limit_min_uV + || pdata->init_data.constraints.max_uV > limit_max_uV) { + vreg_err(vreg, "regulator min/max(%d/%d) constraints do not fit within HW configured min/max(%d/%d) constraints\n", + pdata->init_data.constraints.min_uV, + pdata->init_data.constraints.max_uV, + limit_min_uV, limit_max_uV); + return -EINVAL; + } + + return 0; +} + static int qpnp_regulator_ftsmps_init_slew_rate(struct qpnp_regulator *vreg) { int rc; @@ -2282,6 +2374,13 @@ static int qpnp_regulator_probe(struct platform_device *pdev) } } + rc = qpnp_regulator_check_constraints(vreg, pdata); + if (rc) { + vreg_err(vreg, "regulator constraints check failed, rc=%d\n", + rc); + goto bail; + } + rc = qpnp_regulator_init_registers(vreg, pdata); if (rc) { vreg_err(vreg, "common initialization failed, rc=%d\n", rc); |
