Loading drivers/regulator/qcom_pm8008-regulator.c +127 −61 Original line number Diff line number Diff line // SPDX-License-Identifier: GPL-2.0-only /* Copyright (c) 2019, The Linux Foundation. All rights reserved. */ /* Copyright (c) 2019-2020, The Linux Foundation. All rights reserved. */ #define pr_fmt(fmt) "PM8008: %s: " fmt, __func__ #include <linux/delay.h> #include <linux/device.h> #include <linux/regmap.h> #include <linux/interrupt.h> Loading @@ -24,6 +25,7 @@ #define STARTUP_DELAY_USEC 20 #define VSET_STEP_SIZE_MV 1 #define VSET_STEP_MV 8 #define VSET_STEP_UV (VSET_STEP_MV * 1000) #define MISC_BASE 0x900 Loading Loading @@ -187,9 +189,16 @@ static int pm8008_regulator_is_enabled(struct regulator_dev *rdev) static int pm8008_regulator_enable(struct regulator_dev *rdev) { struct pm8008_regulator *pm8008_reg = rdev_get_drvdata(rdev); int rc, init_mv, delay_us, delay_ms, retry_count = 10; int rc, rc2, current_uv, delay_us, delay_ms, retry_count = 10; u8 reg; current_uv = pm8008_regulator_get_voltage(rdev); if (current_uv < 0) { pm8008_err(pm8008_reg, "failed to get current voltage rc=%d\n", current_uv); return current_uv; } rc = regulator_enable(pm8008_reg->en_supply); if (rc < 0) { pm8008_err(pm8008_reg, Loading @@ -198,12 +207,22 @@ static int pm8008_regulator_enable(struct regulator_dev *rdev) } if (pm8008_reg->parent_supply) { rc = regulator_set_voltage(pm8008_reg->parent_supply, current_uv + pm8008_reg->min_dropout_uv, INT_MAX); if (rc < 0) { pm8008_err(pm8008_reg, "failed to request parent supply voltage rc=%d\n", rc); goto remove_en; } rc = regulator_enable(pm8008_reg->parent_supply); if (rc < 0) { pm8008_err(pm8008_reg, "failed to enable parent rc=%d\n", rc); regulator_disable(pm8008_reg->en_supply); return rc; regulator_set_voltage(pm8008_reg->parent_supply, 0, INT_MAX); goto remove_en; } } Loading @@ -217,20 +236,14 @@ static int pm8008_regulator_enable(struct regulator_dev *rdev) } /* * wait for VREG_OK * Read voltage and calculate the delay. * Wait for the VREG_READY status bit to be set using a timeout delay * calculated from the current commanded voltage. */ init_mv = pm8008_regulator_get_voltage(rdev) / 1000; if (init_mv < 0) { pm8008_err(pm8008_reg, "failed to get regulator voltage rc=%d\n", rc); goto out; } delay_us = STARTUP_DELAY_USEC + DIV_ROUND_UP(init_mv * 1000, pm8008_reg->step_rate); + DIV_ROUND_UP(current_uv, pm8008_reg->step_rate); delay_ms = DIV_ROUND_UP(delay_us, 1000); /* Retry 10 times for VREG_OK before bailing out */ /* Retry 10 times for VREG_READY before bailing out */ while (retry_count--) { if (delay_ms > 20) msleep(delay_ms); Loading @@ -242,7 +255,7 @@ static int pm8008_regulator_enable(struct regulator_dev *rdev) if (rc < 0) { pm8008_err(pm8008_reg, "failed to read regulator status rc=%d\n", rc); goto out; goto disable_ldo; } if (reg & VREG_READY_BIT) { pm8008_debug(pm8008_reg, "regulator enabled\n"); Loading @@ -250,20 +263,33 @@ static int pm8008_regulator_enable(struct regulator_dev *rdev) } } pm8008_err(pm8008_reg, "failed to enable regulator VREG_READY not set\n"); out: pm8008_err(pm8008_reg, "failed to enable regulator, VREG_READY not set\n"); rc = -ETIME; disable_ldo: pm8008_masked_write(pm8008_reg->regmap, LDO_ENABLE_REG(pm8008_reg->base), ENABLE_BIT, 0); remove_vote: rc = regulator_disable(pm8008_reg->en_supply); if (pm8008_reg->parent_supply) rc |= regulator_disable(pm8008_reg->parent_supply); if (rc < 0) pm8008_err(pm8008_reg, "failed to disable parent regulator rc=%d\n", rc); if (pm8008_reg->parent_supply) { rc2 = regulator_disable(pm8008_reg->parent_supply); if (rc2 < 0) pm8008_err(pm8008_reg, "failed to disable parent supply rc=%d\n", rc2); rc2 = regulator_set_voltage(pm8008_reg->parent_supply, 0, INT_MAX); if (rc2 < 0) pm8008_err(pm8008_reg, "failed to remove voltage vote for parent supply rc=%d\n", rc2); } return -ETIME; remove_en: rc2 = regulator_disable(pm8008_reg->en_supply); if (rc2 < 0) pm8008_err(pm8008_reg, "failed to disable en_supply rc=%d\n", rc2); return rc; } static int pm8008_regulator_disable(struct regulator_dev *rdev) Loading @@ -280,29 +306,30 @@ static int pm8008_regulator_disable(struct regulator_dev *rdev) return rc; } /* remove vote from chip enable regulator */ rc = regulator_disable(pm8008_reg->en_supply); if (rc < 0) { pm8008_err(pm8008_reg, "failed to disable en_supply rc=%d\n", rc); } /* remove voltage vote from parent regulator */ if (pm8008_reg->parent_supply) { rc = regulator_disable(pm8008_reg->parent_supply); if (rc < 0) { pm8008_err(pm8008_reg, "failed to disable parent rc=%d\n", rc); return rc; } rc = regulator_set_voltage(pm8008_reg->parent_supply, 0, INT_MAX); if (rc < 0) { pm8008_err(pm8008_reg, "failed to remove parent voltage rc=%d\n", rc); pm8008_err(pm8008_reg, "failed to remove parent voltage rc=%d\n", rc); return rc; } rc = regulator_disable(pm8008_reg->parent_supply); } /* remove vote from chip enable regulator */ rc = regulator_disable(pm8008_reg->en_supply); if (rc < 0) { pm8008_err(pm8008_reg, "failed to disable parent rc=%d\n", rc); pm8008_err(pm8008_reg, "failed to disable en_supply rc=%d\n", rc); return rc; } } pm8008_debug(pm8008_reg, "regulator disabled\n"); return 0; Loading Loading @@ -341,31 +368,76 @@ static int pm8008_write_voltage(struct pm8008_regulator *pm8008_reg, int min_uv, return 0; } static int pm8008_regulator_set_voltage_time(struct regulator_dev *rdev, int old_uV, int new_uv) { struct pm8008_regulator *pm8008_reg = rdev_get_drvdata(rdev); return DIV_ROUND_UP(abs(new_uv - old_uV), pm8008_reg->step_rate); } static int pm8008_regulator_set_voltage(struct regulator_dev *rdev, int min_uv, int max_uv, unsigned int *selector) { struct pm8008_regulator *pm8008_reg = rdev_get_drvdata(rdev); int rc = 0; int rc = 0, current_uv = 0, rounded_uv = 0, enabled = 0; if (pm8008_reg->parent_supply) { /* request on parent regulator with headroom */ enabled = pm8008_regulator_is_enabled(rdev); if (enabled < 0) { return enabled; } else if (enabled) { current_uv = pm8008_regulator_get_voltage(rdev); if (current_uv < 0) return current_uv; rounded_uv = roundup(min_uv, VSET_STEP_UV); } } /* * Set the parent_supply voltage before changing the LDO voltage when * the LDO voltage is being increased. */ if (pm8008_reg->parent_supply && enabled && rounded_uv >= current_uv) { /* Request parent voltage with headroom */ rc = regulator_set_voltage(pm8008_reg->parent_supply, pm8008_reg->min_dropout_uv + min_uv, rounded_uv + pm8008_reg->min_dropout_uv, INT_MAX); if (rc < 0) { pm8008_err(pm8008_reg, "failed to request parent supply voltage rc=%d\n", pm8008_err(pm8008_reg, "failed to request parent supply voltage rc=%d\n", rc); return rc; } } rc = pm8008_write_voltage(pm8008_reg, min_uv, max_uv); if (rc < 0) return rc; /* * Set the parent_supply voltage after changing the LDO voltage when * the LDO voltage is being reduced. */ if (pm8008_reg->parent_supply && enabled && rounded_uv < current_uv) { /* * Ensure sufficient time for the LDO voltage to slew down * before reducing the parent supply voltage. The regulator * framework will add the same delay after this function returns * in all cases (i.e. enabled/disabled and increasing/decreasing * voltage). */ udelay(pm8008_regulator_set_voltage_time(rdev, rounded_uv, current_uv)); /* Request parent voltage with headroom */ rc = regulator_set_voltage(pm8008_reg->parent_supply, rounded_uv + pm8008_reg->min_dropout_uv, INT_MAX); if (rc < 0) { /* remove parent's voltage vote */ if (pm8008_reg->parent_supply) regulator_set_voltage(pm8008_reg->parent_supply, 0, INT_MAX); pm8008_err(pm8008_reg, "failed to request parent supply voltage rc=%d\n", rc); return rc; } } pm8008_debug(pm8008_reg, "voltage set to %d\n", min_uv); Loading Loading @@ -423,14 +495,6 @@ static int pm8008_regulator_set_load(struct regulator_dev *rdev, int load_uA) return pm8008_regulator_set_mode(rdev, mode); } static int pm8008_regulator_set_voltage_time(struct regulator_dev *rdev, int old_uV, int new_uv) { struct pm8008_regulator *pm8008_reg = rdev_get_drvdata(rdev); return DIV_ROUND_UP(abs(new_uv - old_uV), pm8008_reg->step_rate); } static struct regulator_ops pm8008_regulator_ops = { .enable = pm8008_regulator_enable, .disable = pm8008_regulator_disable, Loading Loading @@ -506,6 +570,7 @@ static int pm8008_register_ldo(struct pm8008_regulator *pm8008_reg, struct device_node *reg_node = pm8008_reg->of_node; char buff[MAX_REG_NAME]; int rc, i, init_voltage; u32 base = 0; u8 reg; /* get regulator data */ Loading @@ -518,11 +583,12 @@ static int pm8008_register_ldo(struct pm8008_regulator *pm8008_reg, return -EINVAL; } rc = of_property_read_u16(reg_node, "reg", &pm8008_reg->base); rc = of_property_read_u32(reg_node, "reg", &base); if (rc < 0) { pr_err("%s: failed to get regulator base rc=%d\n", name, rc); return rc; } pm8008_reg->base = base; pm8008_reg->min_dropout_uv = reg_data[i].min_dropout_uv; of_property_read_u32(reg_node, "qcom,min-dropout-voltage", Loading Loading @@ -709,7 +775,7 @@ static int pm8008_regulator_probe(struct platform_device *pdev) /* PM8008 chip enable regulator callbacks */ static int pm8008_enable_regulator_enable(struct regulator_dev *rdev) { struct pm8008_regulator *chip = rdev_get_drvdata(rdev); struct pm8008_chip *chip = rdev_get_drvdata(rdev); int rc; rc = pm8008_masked_write(chip->regmap, MISC_CHIP_ENABLE_REG, Loading @@ -725,7 +791,7 @@ static int pm8008_enable_regulator_enable(struct regulator_dev *rdev) static int pm8008_enable_regulator_disable(struct regulator_dev *rdev) { struct pm8008_regulator *chip = rdev_get_drvdata(rdev); struct pm8008_chip *chip = rdev_get_drvdata(rdev); int rc; rc = pm8008_masked_write(chip->regmap, MISC_CHIP_ENABLE_REG, Loading @@ -741,7 +807,7 @@ static int pm8008_enable_regulator_disable(struct regulator_dev *rdev) static int pm8008_enable_regulator_is_enabled(struct regulator_dev *rdev) { struct pm8008_regulator *chip = rdev_get_drvdata(rdev); struct pm8008_chip *chip = rdev_get_drvdata(rdev); int rc; u8 reg; Loading Loading
drivers/regulator/qcom_pm8008-regulator.c +127 −61 Original line number Diff line number Diff line // SPDX-License-Identifier: GPL-2.0-only /* Copyright (c) 2019, The Linux Foundation. All rights reserved. */ /* Copyright (c) 2019-2020, The Linux Foundation. All rights reserved. */ #define pr_fmt(fmt) "PM8008: %s: " fmt, __func__ #include <linux/delay.h> #include <linux/device.h> #include <linux/regmap.h> #include <linux/interrupt.h> Loading @@ -24,6 +25,7 @@ #define STARTUP_DELAY_USEC 20 #define VSET_STEP_SIZE_MV 1 #define VSET_STEP_MV 8 #define VSET_STEP_UV (VSET_STEP_MV * 1000) #define MISC_BASE 0x900 Loading Loading @@ -187,9 +189,16 @@ static int pm8008_regulator_is_enabled(struct regulator_dev *rdev) static int pm8008_regulator_enable(struct regulator_dev *rdev) { struct pm8008_regulator *pm8008_reg = rdev_get_drvdata(rdev); int rc, init_mv, delay_us, delay_ms, retry_count = 10; int rc, rc2, current_uv, delay_us, delay_ms, retry_count = 10; u8 reg; current_uv = pm8008_regulator_get_voltage(rdev); if (current_uv < 0) { pm8008_err(pm8008_reg, "failed to get current voltage rc=%d\n", current_uv); return current_uv; } rc = regulator_enable(pm8008_reg->en_supply); if (rc < 0) { pm8008_err(pm8008_reg, Loading @@ -198,12 +207,22 @@ static int pm8008_regulator_enable(struct regulator_dev *rdev) } if (pm8008_reg->parent_supply) { rc = regulator_set_voltage(pm8008_reg->parent_supply, current_uv + pm8008_reg->min_dropout_uv, INT_MAX); if (rc < 0) { pm8008_err(pm8008_reg, "failed to request parent supply voltage rc=%d\n", rc); goto remove_en; } rc = regulator_enable(pm8008_reg->parent_supply); if (rc < 0) { pm8008_err(pm8008_reg, "failed to enable parent rc=%d\n", rc); regulator_disable(pm8008_reg->en_supply); return rc; regulator_set_voltage(pm8008_reg->parent_supply, 0, INT_MAX); goto remove_en; } } Loading @@ -217,20 +236,14 @@ static int pm8008_regulator_enable(struct regulator_dev *rdev) } /* * wait for VREG_OK * Read voltage and calculate the delay. * Wait for the VREG_READY status bit to be set using a timeout delay * calculated from the current commanded voltage. */ init_mv = pm8008_regulator_get_voltage(rdev) / 1000; if (init_mv < 0) { pm8008_err(pm8008_reg, "failed to get regulator voltage rc=%d\n", rc); goto out; } delay_us = STARTUP_DELAY_USEC + DIV_ROUND_UP(init_mv * 1000, pm8008_reg->step_rate); + DIV_ROUND_UP(current_uv, pm8008_reg->step_rate); delay_ms = DIV_ROUND_UP(delay_us, 1000); /* Retry 10 times for VREG_OK before bailing out */ /* Retry 10 times for VREG_READY before bailing out */ while (retry_count--) { if (delay_ms > 20) msleep(delay_ms); Loading @@ -242,7 +255,7 @@ static int pm8008_regulator_enable(struct regulator_dev *rdev) if (rc < 0) { pm8008_err(pm8008_reg, "failed to read regulator status rc=%d\n", rc); goto out; goto disable_ldo; } if (reg & VREG_READY_BIT) { pm8008_debug(pm8008_reg, "regulator enabled\n"); Loading @@ -250,20 +263,33 @@ static int pm8008_regulator_enable(struct regulator_dev *rdev) } } pm8008_err(pm8008_reg, "failed to enable regulator VREG_READY not set\n"); out: pm8008_err(pm8008_reg, "failed to enable regulator, VREG_READY not set\n"); rc = -ETIME; disable_ldo: pm8008_masked_write(pm8008_reg->regmap, LDO_ENABLE_REG(pm8008_reg->base), ENABLE_BIT, 0); remove_vote: rc = regulator_disable(pm8008_reg->en_supply); if (pm8008_reg->parent_supply) rc |= regulator_disable(pm8008_reg->parent_supply); if (rc < 0) pm8008_err(pm8008_reg, "failed to disable parent regulator rc=%d\n", rc); if (pm8008_reg->parent_supply) { rc2 = regulator_disable(pm8008_reg->parent_supply); if (rc2 < 0) pm8008_err(pm8008_reg, "failed to disable parent supply rc=%d\n", rc2); rc2 = regulator_set_voltage(pm8008_reg->parent_supply, 0, INT_MAX); if (rc2 < 0) pm8008_err(pm8008_reg, "failed to remove voltage vote for parent supply rc=%d\n", rc2); } return -ETIME; remove_en: rc2 = regulator_disable(pm8008_reg->en_supply); if (rc2 < 0) pm8008_err(pm8008_reg, "failed to disable en_supply rc=%d\n", rc2); return rc; } static int pm8008_regulator_disable(struct regulator_dev *rdev) Loading @@ -280,29 +306,30 @@ static int pm8008_regulator_disable(struct regulator_dev *rdev) return rc; } /* remove vote from chip enable regulator */ rc = regulator_disable(pm8008_reg->en_supply); if (rc < 0) { pm8008_err(pm8008_reg, "failed to disable en_supply rc=%d\n", rc); } /* remove voltage vote from parent regulator */ if (pm8008_reg->parent_supply) { rc = regulator_disable(pm8008_reg->parent_supply); if (rc < 0) { pm8008_err(pm8008_reg, "failed to disable parent rc=%d\n", rc); return rc; } rc = regulator_set_voltage(pm8008_reg->parent_supply, 0, INT_MAX); if (rc < 0) { pm8008_err(pm8008_reg, "failed to remove parent voltage rc=%d\n", rc); pm8008_err(pm8008_reg, "failed to remove parent voltage rc=%d\n", rc); return rc; } rc = regulator_disable(pm8008_reg->parent_supply); } /* remove vote from chip enable regulator */ rc = regulator_disable(pm8008_reg->en_supply); if (rc < 0) { pm8008_err(pm8008_reg, "failed to disable parent rc=%d\n", rc); pm8008_err(pm8008_reg, "failed to disable en_supply rc=%d\n", rc); return rc; } } pm8008_debug(pm8008_reg, "regulator disabled\n"); return 0; Loading Loading @@ -341,31 +368,76 @@ static int pm8008_write_voltage(struct pm8008_regulator *pm8008_reg, int min_uv, return 0; } static int pm8008_regulator_set_voltage_time(struct regulator_dev *rdev, int old_uV, int new_uv) { struct pm8008_regulator *pm8008_reg = rdev_get_drvdata(rdev); return DIV_ROUND_UP(abs(new_uv - old_uV), pm8008_reg->step_rate); } static int pm8008_regulator_set_voltage(struct regulator_dev *rdev, int min_uv, int max_uv, unsigned int *selector) { struct pm8008_regulator *pm8008_reg = rdev_get_drvdata(rdev); int rc = 0; int rc = 0, current_uv = 0, rounded_uv = 0, enabled = 0; if (pm8008_reg->parent_supply) { /* request on parent regulator with headroom */ enabled = pm8008_regulator_is_enabled(rdev); if (enabled < 0) { return enabled; } else if (enabled) { current_uv = pm8008_regulator_get_voltage(rdev); if (current_uv < 0) return current_uv; rounded_uv = roundup(min_uv, VSET_STEP_UV); } } /* * Set the parent_supply voltage before changing the LDO voltage when * the LDO voltage is being increased. */ if (pm8008_reg->parent_supply && enabled && rounded_uv >= current_uv) { /* Request parent voltage with headroom */ rc = regulator_set_voltage(pm8008_reg->parent_supply, pm8008_reg->min_dropout_uv + min_uv, rounded_uv + pm8008_reg->min_dropout_uv, INT_MAX); if (rc < 0) { pm8008_err(pm8008_reg, "failed to request parent supply voltage rc=%d\n", pm8008_err(pm8008_reg, "failed to request parent supply voltage rc=%d\n", rc); return rc; } } rc = pm8008_write_voltage(pm8008_reg, min_uv, max_uv); if (rc < 0) return rc; /* * Set the parent_supply voltage after changing the LDO voltage when * the LDO voltage is being reduced. */ if (pm8008_reg->parent_supply && enabled && rounded_uv < current_uv) { /* * Ensure sufficient time for the LDO voltage to slew down * before reducing the parent supply voltage. The regulator * framework will add the same delay after this function returns * in all cases (i.e. enabled/disabled and increasing/decreasing * voltage). */ udelay(pm8008_regulator_set_voltage_time(rdev, rounded_uv, current_uv)); /* Request parent voltage with headroom */ rc = regulator_set_voltage(pm8008_reg->parent_supply, rounded_uv + pm8008_reg->min_dropout_uv, INT_MAX); if (rc < 0) { /* remove parent's voltage vote */ if (pm8008_reg->parent_supply) regulator_set_voltage(pm8008_reg->parent_supply, 0, INT_MAX); pm8008_err(pm8008_reg, "failed to request parent supply voltage rc=%d\n", rc); return rc; } } pm8008_debug(pm8008_reg, "voltage set to %d\n", min_uv); Loading Loading @@ -423,14 +495,6 @@ static int pm8008_regulator_set_load(struct regulator_dev *rdev, int load_uA) return pm8008_regulator_set_mode(rdev, mode); } static int pm8008_regulator_set_voltage_time(struct regulator_dev *rdev, int old_uV, int new_uv) { struct pm8008_regulator *pm8008_reg = rdev_get_drvdata(rdev); return DIV_ROUND_UP(abs(new_uv - old_uV), pm8008_reg->step_rate); } static struct regulator_ops pm8008_regulator_ops = { .enable = pm8008_regulator_enable, .disable = pm8008_regulator_disable, Loading Loading @@ -506,6 +570,7 @@ static int pm8008_register_ldo(struct pm8008_regulator *pm8008_reg, struct device_node *reg_node = pm8008_reg->of_node; char buff[MAX_REG_NAME]; int rc, i, init_voltage; u32 base = 0; u8 reg; /* get regulator data */ Loading @@ -518,11 +583,12 @@ static int pm8008_register_ldo(struct pm8008_regulator *pm8008_reg, return -EINVAL; } rc = of_property_read_u16(reg_node, "reg", &pm8008_reg->base); rc = of_property_read_u32(reg_node, "reg", &base); if (rc < 0) { pr_err("%s: failed to get regulator base rc=%d\n", name, rc); return rc; } pm8008_reg->base = base; pm8008_reg->min_dropout_uv = reg_data[i].min_dropout_uv; of_property_read_u32(reg_node, "qcom,min-dropout-voltage", Loading Loading @@ -709,7 +775,7 @@ static int pm8008_regulator_probe(struct platform_device *pdev) /* PM8008 chip enable regulator callbacks */ static int pm8008_enable_regulator_enable(struct regulator_dev *rdev) { struct pm8008_regulator *chip = rdev_get_drvdata(rdev); struct pm8008_chip *chip = rdev_get_drvdata(rdev); int rc; rc = pm8008_masked_write(chip->regmap, MISC_CHIP_ENABLE_REG, Loading @@ -725,7 +791,7 @@ static int pm8008_enable_regulator_enable(struct regulator_dev *rdev) static int pm8008_enable_regulator_disable(struct regulator_dev *rdev) { struct pm8008_regulator *chip = rdev_get_drvdata(rdev); struct pm8008_chip *chip = rdev_get_drvdata(rdev); int rc; rc = pm8008_masked_write(chip->regmap, MISC_CHIP_ENABLE_REG, Loading @@ -741,7 +807,7 @@ static int pm8008_enable_regulator_disable(struct regulator_dev *rdev) static int pm8008_enable_regulator_is_enabled(struct regulator_dev *rdev) { struct pm8008_regulator *chip = rdev_get_drvdata(rdev); struct pm8008_chip *chip = rdev_get_drvdata(rdev); int rc; u8 reg; Loading