Merge tag 'pwm/for-5.3-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/thierry...
[sfrench/cifs-2.6.git] / drivers / pwm / sysfs.c
index bf6823fe08125510216b9885822a43a7532d3e26..2389b86698468228a54ae50c2bae49b9663e5028 100644 (file)
@@ -18,6 +18,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)
@@ -372,10 +373,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)