summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--drivers/regulator/qpnp-regulator.c99
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,
+ &reg[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,
+ &reg[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,
+ &reg[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);