From 26efcc23eb8274e3f2bef3e28f630b3710a194a8 Mon Sep 17 00:00:00 2001 From: Tirupathi Reddy Date: Wed, 17 May 2017 17:16:12 +0530 Subject: regulator: spm: check min/max voltages against HW configuration Regulator HW has the programmable voltage upper and lower limit registers and doesn't allow voltage set points outside the pre- configured voltage limits. Add a check in driver that throws an error if the SW specified voltage range is not within the pre- configured limits in HW. CRs-Fixed: 2044789 Change-Id: I3593ebf8e88824a0cfcb068686673a00051a11f0 Signed-off-by: Tirupathi Reddy --- drivers/regulator/spm-regulator.c | 106 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 106 insertions(+) (limited to 'drivers') diff --git a/drivers/regulator/spm-regulator.c b/drivers/regulator/spm-regulator.c index be59947ba6f2..d469463a0b00 100644 --- a/drivers/regulator/spm-regulator.c +++ b/drivers/regulator/spm-regulator.c @@ -95,6 +95,7 @@ static const struct voltage_range hf_range1 = {1550000, 1550000, 3125000, #define QPNP_SMPS_REG_VOLTAGE_SETPOINT 0x41 #define QPNP_SMPS_REG_MODE 0x45 #define QPNP_SMPS_REG_STEP_CTRL 0x61 +#define QPNP_SMPS_REG_UL_LL_CTRL 0x68 /* FTS426 voltage control registers */ #define QPNP_FTS426_REG_VOLTAGE_LB 0x40 @@ -102,6 +103,22 @@ static const struct voltage_range hf_range1 = {1550000, 1550000, 3125000, #define QPNP_FTS426_REG_VOLTAGE_VALID_LB 0x42 #define QPNP_FTS426_REG_VOLTAGE_VALID_UB 0x43 +/* HF voltage limit registers */ +#define QPNP_HF_REG_VOLTAGE_ULS 0x69 +#define QPNP_HF_REG_VOLTAGE_LLS 0x6B + +/* FTS voltage limit registers */ +#define QPNP_FTS_REG_VOLTAGE_ULS_VALID 0x6A +#define QPNP_FTS_REG_VOLTAGE_LLS_VALID 0x6C + +/* FTS426 voltage limit registers */ +#define QPNP_FTS426_REG_VOLTAGE_ULS_LB 0x68 +#define QPNP_FTS426_REG_VOLTAGE_ULS_UB 0x69 + +/* Common regulator UL & LL limits control register layout */ +#define QPNP_COMMON_UL_EN_MASK 0x80 +#define QPNP_COMMON_LL_EN_MASK 0x40 + #define QPNP_SMPS_MODE_PWM 0x80 #define QPNP_SMPS_MODE_AUTO 0x40 #define QPNP_FTS426_MODE_PWM 0x07 @@ -924,6 +941,88 @@ static int qpnp_smps_init_step_rate(struct spm_vreg *vreg) return rc; } +static int qpnp_smps_check_constraints(struct spm_vreg *vreg, + struct regulator_init_data *init_data) +{ + int rc = 0, limit_min_uV, limit_max_uV; + u16 ul_reg, ll_reg; + u8 reg[2]; + + limit_min_uV = 0; + limit_max_uV = INT_MAX; + + ul_reg = QPNP_FTS_REG_VOLTAGE_ULS_VALID; + ll_reg = QPNP_FTS_REG_VOLTAGE_LLS_VALID; + + switch (vreg->regulator_type) { + case QPNP_TYPE_HF: + ul_reg = QPNP_HF_REG_VOLTAGE_ULS; + ll_reg = QPNP_HF_REG_VOLTAGE_LLS; + case QPNP_TYPE_FTS2: + case QPNP_TYPE_FTS2p5: + rc = regmap_bulk_read(vreg->regmap, vreg->spmi_base_addr + + QPNP_SMPS_REG_UL_LL_CTRL, reg, 1); + if (rc) { + dev_err(&vreg->pdev->dev, "%s: UL_LL register read failed, rc=%d\n", + __func__, rc); + return rc; + } + + if (reg[0] & QPNP_COMMON_UL_EN_MASK) { + rc = regmap_bulk_read(vreg->regmap, vreg->spmi_base_addr + + ul_reg, ®[1], 1); + if (rc) { + dev_err(&vreg->pdev->dev, "%s: ULS register read failed, rc=%d\n", + __func__, rc); + return rc; + } + + limit_max_uV = spm_regulator_vlevel_to_uv(vreg, reg[1]); + } + + if (reg[0] & QPNP_COMMON_LL_EN_MASK) { + rc = regmap_bulk_read(vreg->regmap, vreg->spmi_base_addr + + ll_reg, ®[1], 1); + if (rc) { + dev_err(&vreg->pdev->dev, "%s: LLS register read failed, rc=%d\n", + __func__, rc); + return rc; + } + + limit_min_uV = spm_regulator_vlevel_to_uv(vreg, reg[1]); + } + + break; + case QPNP_TYPE_FTS426: + rc = regmap_bulk_read(vreg->regmap, vreg->spmi_base_addr + + QPNP_FTS426_REG_VOLTAGE_ULS_LB, + reg, 2); + if (rc) { + dev_err(&vreg->pdev->dev, "%s: could not read voltage limit registers, rc=%d\n", + __func__, rc); + return rc; + } + + limit_max_uV = spm_regulator_vlevel_to_uv(vreg, + ((unsigned)reg[1] << 8) | reg[0]); + break; + case QPNP_TYPE_ULT_HF: + /* no HW voltage limit configuration */ + break; + } + + if (init_data->constraints.min_uV < limit_min_uV + || init_data->constraints.max_uV > limit_max_uV) { + dev_err(&vreg->pdev->dev, "regulator min/max(%d/%d) constraints do not fit within HW configured min/max(%d/%d) constraints\n", + init_data->constraints.min_uV, + init_data->constraints.max_uV, limit_min_uV, + limit_max_uV); + return -EINVAL; + } + + return rc; +} + static bool spm_regulator_using_range0(struct spm_vreg *vreg) { return vreg->range == &fts2_range0 || vreg->range == &fts2p5_range0 @@ -1105,6 +1204,13 @@ static int spm_regulator_probe(struct platform_device *pdev) return -EINVAL; } + rc = qpnp_smps_check_constraints(vreg, init_data); + if (rc) { + dev_err(&pdev->dev, "%s: regulator constraints check failed, rc=%d\n", + __func__, rc); + return rc; + } + vreg->rdesc.name = init_data->constraints.name; vreg->rdesc.type = REGULATOR_VOLTAGE; vreg->rdesc.owner = THIS_MODULE; -- cgit v1.2.3