Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 7fd4edc5 authored by Yoshihiro Shimoda's avatar Yoshihiro Shimoda Committed by Thierry Reding
Browse files

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: default avatarYoshihiro Shimoda <yoshihiro.shimoda.uh@renesas.com>
Reviewed-by: default avatarGeert Uytterhoeven <geert+renesas@glider.be>
Reviewed-by: default avatarSimon Horman <horms+renesas@verge.net.au>
[thierry.reding@gmail.com: fix build warnings for !PM]
Signed-off-by: default avatarThierry Reding <thierry.reding@gmail.com>
parent 321a7cea
Loading
Loading
Loading
Loading
+102 −0
Original line number Diff line number Diff line
@@ -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)