pwm: sysfs: Add suspend/resume support

According to the Documentation/pwm.txt, all PWM consumers should have
power management. Since this sysfs interface is one of consumers so that
this patch adds suspend/resume support.

Signed-off-by: Yoshihiro Shimoda <yoshihiro.shimoda.uh@renesas.com>
Reviewed-by: Geert Uytterhoeven <geert+renesas@glider.be>
Reviewed-by: Simon Horman <horms+renesas@verge.net.au>
[thierry.reding@gmail.com: fix build warnings for !PM]
Signed-off-by: Thierry Reding <thierry.reding@gmail.com>
This commit is contained in:
Yoshihiro Shimoda 2019-05-31 18:55:00 +09:00 коммит произвёл Thierry Reding
Родитель 321a7cea97
Коммит 7fd4edc57b
1 изменённых файлов: 102 добавлений и 0 удалений

Просмотреть файл

@ -27,6 +27,7 @@ struct pwm_export {
struct device child;
struct pwm_device *pwm;
struct mutex lock;
struct pwm_state suspend;
};
static struct pwm_export *child_to_pwm_export(struct device *child)
@ -381,10 +382,111 @@ static struct attribute *pwm_chip_attrs[] = {
};
ATTRIBUTE_GROUPS(pwm_chip);
/* takes export->lock on success */
static struct pwm_export *pwm_class_get_state(struct device *parent,
struct pwm_device *pwm,
struct pwm_state *state)
{
struct device *child;
struct pwm_export *export;
if (!test_bit(PWMF_EXPORTED, &pwm->flags))
return NULL;
child = device_find_child(parent, pwm, pwm_unexport_match);
if (!child)
return NULL;
export = child_to_pwm_export(child);
put_device(child); /* for device_find_child() */
mutex_lock(&export->lock);
pwm_get_state(pwm, state);
return export;
}
static int pwm_class_apply_state(struct pwm_export *export,
struct pwm_device *pwm,
struct pwm_state *state)
{
int ret = pwm_apply_state(pwm, state);
/* release lock taken in pwm_class_get_state */
mutex_unlock(&export->lock);
return ret;
}
static int pwm_class_resume_npwm(struct device *parent, unsigned int npwm)
{
struct pwm_chip *chip = dev_get_drvdata(parent);
unsigned int i;
int ret = 0;
for (i = 0; i < npwm; i++) {
struct pwm_device *pwm = &chip->pwms[i];
struct pwm_state state;
struct pwm_export *export;
export = pwm_class_get_state(parent, pwm, &state);
if (!export)
continue;
state.enabled = export->suspend.enabled;
ret = pwm_class_apply_state(export, pwm, &state);
if (ret < 0)
break;
}
return ret;
}
static int __maybe_unused pwm_class_suspend(struct device *parent)
{
struct pwm_chip *chip = dev_get_drvdata(parent);
unsigned int i;
int ret = 0;
for (i = 0; i < chip->npwm; i++) {
struct pwm_device *pwm = &chip->pwms[i];
struct pwm_state state;
struct pwm_export *export;
export = pwm_class_get_state(parent, pwm, &state);
if (!export)
continue;
export->suspend = state;
state.enabled = false;
ret = pwm_class_apply_state(export, pwm, &state);
if (ret < 0) {
/*
* roll back the PWM devices that were disabled by
* this suspend function.
*/
pwm_class_resume_npwm(parent, i);
break;
}
}
return ret;
}
static int __maybe_unused pwm_class_resume(struct device *parent)
{
struct pwm_chip *chip = dev_get_drvdata(parent);
return pwm_class_resume_npwm(parent, chip->npwm);
}
static SIMPLE_DEV_PM_OPS(pwm_class_pm_ops, pwm_class_suspend, pwm_class_resume);
static struct class pwm_class = {
.name = "pwm",
.owner = THIS_MODULE,
.dev_groups = pwm_chip_groups,
.pm = &pwm_class_pm_ops,
};
static int pwmchip_sysfs_match(struct device *parent, const void *data)