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 device child;
 | 
				
			||||||
	struct pwm_device *pwm;
 | 
						struct pwm_device *pwm;
 | 
				
			||||||
	struct mutex lock;
 | 
						struct mutex lock;
 | 
				
			||||||
 | 
						struct pwm_state suspend;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static struct pwm_export *child_to_pwm_export(struct device *child)
 | 
					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);
 | 
					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 = {
 | 
					static struct class pwm_class = {
 | 
				
			||||||
	.name = "pwm",
 | 
						.name = "pwm",
 | 
				
			||||||
	.owner = THIS_MODULE,
 | 
						.owner = THIS_MODULE,
 | 
				
			||||||
	.dev_groups = pwm_chip_groups,
 | 
						.dev_groups = pwm_chip_groups,
 | 
				
			||||||
 | 
						.pm = &pwm_class_pm_ops,
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static int pwmchip_sysfs_match(struct device *parent, const void *data)
 | 
					static int pwmchip_sysfs_match(struct device *parent, const void *data)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in a new issue