forked from mirrors/linux
		
	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:
		
							parent
							
								
									321a7cea97
								
							
						
					
					
						commit
						7fd4edc57b
					
				
					 1 changed files with 102 additions and 0 deletions
				
			
		| 
						 | 
				
			
			@ -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)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in a new issue