pwm: sysfs: Add suspend/resume support
authorYoshihiro Shimoda <yoshihiro.shimoda.uh@renesas.com>
Fri, 31 May 2019 09:55:00 +0000 (18:55 +0900)
committerThierry Reding <thierry.reding@gmail.com>
Wed, 26 Jun 2019 09:39:11 +0000 (11:39 +0200)
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>
drivers/pwm/sysfs.c

index 719f8fa..8158a43 100644 (file)
@@ -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)