The regulator-fixed driver is possible to be off by firmware like PSCI while the system is suspended. If a consumer could get such a condition from regulator_is_enabled(), it's useful by consumers. The regulator subsystem already has regulator-state-(standby|mem|disk) sub-nodes and regulator-off-in-suspend property. However, suitable regulator_ops APIs didn't exist. So, add new regulator_ops APIs and prepare()/resume_early() in the regulator_pm_ops to set/clear the condition by new APIs before suspend() functions of consumers are called. Signed-off-by: Yoshihiro Shimoda <yoshihiro.shimoda.uh@xxxxxxxxxxx> --- drivers/regulator/core.c | 42 ++++++++++++++++++++++++++++++++++++++++ include/linux/regulator/driver.h | 6 ++++++ 2 files changed, 48 insertions(+) diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index 03154f5..93eb2a3 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -5286,6 +5286,46 @@ void regulator_unregister(struct regulator_dev *rdev) EXPORT_SYMBOL_GPL(regulator_unregister); #ifdef CONFIG_SUSPEND +static int regulator_prepare(struct device *dev) +{ + struct regulator_dev *rdev = dev_to_rdev(dev); + suspend_state_t state = pm_suspend_target_state; + struct regulator_state *rstate; + int ret = 0; + + rstate = regulator_get_suspend_state(rdev, state); + if (rstate == NULL) + return 0; + + regulator_lock(rdev); + if (rstate->enabled == DISABLE_IN_SUSPEND && + rdev->desc->ops->set_prepare_disable) + ret = rdev->desc->ops->set_prepare_disable(rdev); + regulator_unlock(rdev); + + return ret; +} + +static int regulator_resume_early(struct device *dev) +{ + struct regulator_dev *rdev = dev_to_rdev(dev); + suspend_state_t state = pm_suspend_target_state; + struct regulator_state *rstate; + int ret = 0; + + rstate = regulator_get_suspend_state(rdev, state); + if (rstate == NULL) + return 0; + + regulator_lock(rdev); + if (rstate->enabled == DISABLE_IN_SUSPEND && + rdev->desc->ops->clear_resume_early_disable) + ret = rdev->desc->ops->clear_resume_early_disable(rdev); + regulator_unlock(rdev); + + return ret; +} + /** * regulator_suspend - prepare regulators for system wide suspend * @dev: ``&struct device`` pointer that is passed to _regulator_suspend() @@ -5336,6 +5376,8 @@ static int regulator_resume(struct device *dev) #ifdef CONFIG_PM static const struct dev_pm_ops __maybe_unused regulator_pm_ops = { + .prepare = regulator_prepare, + .resume_early = regulator_resume_early, .suspend = regulator_suspend, .resume = regulator_resume, }; diff --git a/include/linux/regulator/driver.h b/include/linux/regulator/driver.h index 7eb9fea..299a504 100644 --- a/include/linux/regulator/driver.h +++ b/include/linux/regulator/driver.h @@ -115,6 +115,10 @@ enum regulator_status { * suspended. * @set_suspend_disable: Mark the regulator as disabled when the system is * suspended. + * @set_prepare_disable: Mark the regulator as disabled when the system is + * suspending. + * @clear_resume_early_disable: Unmark the regulator as disabled when + * the system is resuming. * @set_suspend_mode: Set the operating mode for the regulator when the * system is suspended. * @@ -195,6 +199,8 @@ struct regulator_ops { /* enable/disable regulator in suspend state */ int (*set_suspend_enable) (struct regulator_dev *); int (*set_suspend_disable) (struct regulator_dev *); + int (*set_prepare_disable) (struct regulator_dev *); + int (*clear_resume_early_disable) (struct regulator_dev *); /* set regulator suspend operating mode (defined in consumer.h) */ int (*set_suspend_mode) (struct regulator_dev *, unsigned int mode); -- 2.7.4