162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * A simple sysfs interface for the generic PWM framework 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2013 H Hartley Sweeten <hsweeten@visionengravers.com> 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Based on previous work by Lars Poeschel <poeschel@lemonage.de> 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include <linux/device.h> 1162306a36Sopenharmony_ci#include <linux/mutex.h> 1262306a36Sopenharmony_ci#include <linux/err.h> 1362306a36Sopenharmony_ci#include <linux/slab.h> 1462306a36Sopenharmony_ci#include <linux/kdev_t.h> 1562306a36Sopenharmony_ci#include <linux/pwm.h> 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_cistruct pwm_export { 1862306a36Sopenharmony_ci struct device child; 1962306a36Sopenharmony_ci struct pwm_device *pwm; 2062306a36Sopenharmony_ci struct mutex lock; 2162306a36Sopenharmony_ci struct pwm_state suspend; 2262306a36Sopenharmony_ci}; 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_cistatic struct pwm_export *child_to_pwm_export(struct device *child) 2562306a36Sopenharmony_ci{ 2662306a36Sopenharmony_ci return container_of(child, struct pwm_export, child); 2762306a36Sopenharmony_ci} 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_cistatic struct pwm_device *child_to_pwm_device(struct device *child) 3062306a36Sopenharmony_ci{ 3162306a36Sopenharmony_ci struct pwm_export *export = child_to_pwm_export(child); 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci return export->pwm; 3462306a36Sopenharmony_ci} 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_cistatic ssize_t period_show(struct device *child, 3762306a36Sopenharmony_ci struct device_attribute *attr, 3862306a36Sopenharmony_ci char *buf) 3962306a36Sopenharmony_ci{ 4062306a36Sopenharmony_ci const struct pwm_device *pwm = child_to_pwm_device(child); 4162306a36Sopenharmony_ci struct pwm_state state; 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci pwm_get_state(pwm, &state); 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci return sysfs_emit(buf, "%llu\n", state.period); 4662306a36Sopenharmony_ci} 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_cistatic ssize_t period_store(struct device *child, 4962306a36Sopenharmony_ci struct device_attribute *attr, 5062306a36Sopenharmony_ci const char *buf, size_t size) 5162306a36Sopenharmony_ci{ 5262306a36Sopenharmony_ci struct pwm_export *export = child_to_pwm_export(child); 5362306a36Sopenharmony_ci struct pwm_device *pwm = export->pwm; 5462306a36Sopenharmony_ci struct pwm_state state; 5562306a36Sopenharmony_ci u64 val; 5662306a36Sopenharmony_ci int ret; 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci ret = kstrtou64(buf, 0, &val); 5962306a36Sopenharmony_ci if (ret) 6062306a36Sopenharmony_ci return ret; 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci mutex_lock(&export->lock); 6362306a36Sopenharmony_ci pwm_get_state(pwm, &state); 6462306a36Sopenharmony_ci state.period = val; 6562306a36Sopenharmony_ci ret = pwm_apply_state(pwm, &state); 6662306a36Sopenharmony_ci mutex_unlock(&export->lock); 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci return ret ? : size; 6962306a36Sopenharmony_ci} 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_cistatic ssize_t duty_cycle_show(struct device *child, 7262306a36Sopenharmony_ci struct device_attribute *attr, 7362306a36Sopenharmony_ci char *buf) 7462306a36Sopenharmony_ci{ 7562306a36Sopenharmony_ci const struct pwm_device *pwm = child_to_pwm_device(child); 7662306a36Sopenharmony_ci struct pwm_state state; 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci pwm_get_state(pwm, &state); 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci return sysfs_emit(buf, "%llu\n", state.duty_cycle); 8162306a36Sopenharmony_ci} 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_cistatic ssize_t duty_cycle_store(struct device *child, 8462306a36Sopenharmony_ci struct device_attribute *attr, 8562306a36Sopenharmony_ci const char *buf, size_t size) 8662306a36Sopenharmony_ci{ 8762306a36Sopenharmony_ci struct pwm_export *export = child_to_pwm_export(child); 8862306a36Sopenharmony_ci struct pwm_device *pwm = export->pwm; 8962306a36Sopenharmony_ci struct pwm_state state; 9062306a36Sopenharmony_ci u64 val; 9162306a36Sopenharmony_ci int ret; 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci ret = kstrtou64(buf, 0, &val); 9462306a36Sopenharmony_ci if (ret) 9562306a36Sopenharmony_ci return ret; 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci mutex_lock(&export->lock); 9862306a36Sopenharmony_ci pwm_get_state(pwm, &state); 9962306a36Sopenharmony_ci state.duty_cycle = val; 10062306a36Sopenharmony_ci ret = pwm_apply_state(pwm, &state); 10162306a36Sopenharmony_ci mutex_unlock(&export->lock); 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci return ret ? : size; 10462306a36Sopenharmony_ci} 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_cistatic ssize_t enable_show(struct device *child, 10762306a36Sopenharmony_ci struct device_attribute *attr, 10862306a36Sopenharmony_ci char *buf) 10962306a36Sopenharmony_ci{ 11062306a36Sopenharmony_ci const struct pwm_device *pwm = child_to_pwm_device(child); 11162306a36Sopenharmony_ci struct pwm_state state; 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci pwm_get_state(pwm, &state); 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci return sysfs_emit(buf, "%d\n", state.enabled); 11662306a36Sopenharmony_ci} 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_cistatic ssize_t enable_store(struct device *child, 11962306a36Sopenharmony_ci struct device_attribute *attr, 12062306a36Sopenharmony_ci const char *buf, size_t size) 12162306a36Sopenharmony_ci{ 12262306a36Sopenharmony_ci struct pwm_export *export = child_to_pwm_export(child); 12362306a36Sopenharmony_ci struct pwm_device *pwm = export->pwm; 12462306a36Sopenharmony_ci struct pwm_state state; 12562306a36Sopenharmony_ci int val, ret; 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci ret = kstrtoint(buf, 0, &val); 12862306a36Sopenharmony_ci if (ret) 12962306a36Sopenharmony_ci return ret; 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci mutex_lock(&export->lock); 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci pwm_get_state(pwm, &state); 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci switch (val) { 13662306a36Sopenharmony_ci case 0: 13762306a36Sopenharmony_ci state.enabled = false; 13862306a36Sopenharmony_ci break; 13962306a36Sopenharmony_ci case 1: 14062306a36Sopenharmony_ci state.enabled = true; 14162306a36Sopenharmony_ci break; 14262306a36Sopenharmony_ci default: 14362306a36Sopenharmony_ci ret = -EINVAL; 14462306a36Sopenharmony_ci goto unlock; 14562306a36Sopenharmony_ci } 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci ret = pwm_apply_state(pwm, &state); 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ciunlock: 15062306a36Sopenharmony_ci mutex_unlock(&export->lock); 15162306a36Sopenharmony_ci return ret ? : size; 15262306a36Sopenharmony_ci} 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_cistatic ssize_t polarity_show(struct device *child, 15562306a36Sopenharmony_ci struct device_attribute *attr, 15662306a36Sopenharmony_ci char *buf) 15762306a36Sopenharmony_ci{ 15862306a36Sopenharmony_ci const struct pwm_device *pwm = child_to_pwm_device(child); 15962306a36Sopenharmony_ci const char *polarity = "unknown"; 16062306a36Sopenharmony_ci struct pwm_state state; 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci pwm_get_state(pwm, &state); 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci switch (state.polarity) { 16562306a36Sopenharmony_ci case PWM_POLARITY_NORMAL: 16662306a36Sopenharmony_ci polarity = "normal"; 16762306a36Sopenharmony_ci break; 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci case PWM_POLARITY_INVERSED: 17062306a36Sopenharmony_ci polarity = "inversed"; 17162306a36Sopenharmony_ci break; 17262306a36Sopenharmony_ci } 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci return sysfs_emit(buf, "%s\n", polarity); 17562306a36Sopenharmony_ci} 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_cistatic ssize_t polarity_store(struct device *child, 17862306a36Sopenharmony_ci struct device_attribute *attr, 17962306a36Sopenharmony_ci const char *buf, size_t size) 18062306a36Sopenharmony_ci{ 18162306a36Sopenharmony_ci struct pwm_export *export = child_to_pwm_export(child); 18262306a36Sopenharmony_ci struct pwm_device *pwm = export->pwm; 18362306a36Sopenharmony_ci enum pwm_polarity polarity; 18462306a36Sopenharmony_ci struct pwm_state state; 18562306a36Sopenharmony_ci int ret; 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci if (sysfs_streq(buf, "normal")) 18862306a36Sopenharmony_ci polarity = PWM_POLARITY_NORMAL; 18962306a36Sopenharmony_ci else if (sysfs_streq(buf, "inversed")) 19062306a36Sopenharmony_ci polarity = PWM_POLARITY_INVERSED; 19162306a36Sopenharmony_ci else 19262306a36Sopenharmony_ci return -EINVAL; 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci mutex_lock(&export->lock); 19562306a36Sopenharmony_ci pwm_get_state(pwm, &state); 19662306a36Sopenharmony_ci state.polarity = polarity; 19762306a36Sopenharmony_ci ret = pwm_apply_state(pwm, &state); 19862306a36Sopenharmony_ci mutex_unlock(&export->lock); 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci return ret ? : size; 20162306a36Sopenharmony_ci} 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_cistatic ssize_t capture_show(struct device *child, 20462306a36Sopenharmony_ci struct device_attribute *attr, 20562306a36Sopenharmony_ci char *buf) 20662306a36Sopenharmony_ci{ 20762306a36Sopenharmony_ci struct pwm_device *pwm = child_to_pwm_device(child); 20862306a36Sopenharmony_ci struct pwm_capture result; 20962306a36Sopenharmony_ci int ret; 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci ret = pwm_capture(pwm, &result, jiffies_to_msecs(HZ)); 21262306a36Sopenharmony_ci if (ret) 21362306a36Sopenharmony_ci return ret; 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci return sysfs_emit(buf, "%u %u\n", result.period, result.duty_cycle); 21662306a36Sopenharmony_ci} 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_cistatic DEVICE_ATTR_RW(period); 21962306a36Sopenharmony_cistatic DEVICE_ATTR_RW(duty_cycle); 22062306a36Sopenharmony_cistatic DEVICE_ATTR_RW(enable); 22162306a36Sopenharmony_cistatic DEVICE_ATTR_RW(polarity); 22262306a36Sopenharmony_cistatic DEVICE_ATTR_RO(capture); 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_cistatic struct attribute *pwm_attrs[] = { 22562306a36Sopenharmony_ci &dev_attr_period.attr, 22662306a36Sopenharmony_ci &dev_attr_duty_cycle.attr, 22762306a36Sopenharmony_ci &dev_attr_enable.attr, 22862306a36Sopenharmony_ci &dev_attr_polarity.attr, 22962306a36Sopenharmony_ci &dev_attr_capture.attr, 23062306a36Sopenharmony_ci NULL 23162306a36Sopenharmony_ci}; 23262306a36Sopenharmony_ciATTRIBUTE_GROUPS(pwm); 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_cistatic void pwm_export_release(struct device *child) 23562306a36Sopenharmony_ci{ 23662306a36Sopenharmony_ci struct pwm_export *export = child_to_pwm_export(child); 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci kfree(export); 23962306a36Sopenharmony_ci} 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_cistatic int pwm_export_child(struct device *parent, struct pwm_device *pwm) 24262306a36Sopenharmony_ci{ 24362306a36Sopenharmony_ci struct pwm_export *export; 24462306a36Sopenharmony_ci char *pwm_prop[2]; 24562306a36Sopenharmony_ci int ret; 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci if (test_and_set_bit(PWMF_EXPORTED, &pwm->flags)) 24862306a36Sopenharmony_ci return -EBUSY; 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_ci export = kzalloc(sizeof(*export), GFP_KERNEL); 25162306a36Sopenharmony_ci if (!export) { 25262306a36Sopenharmony_ci clear_bit(PWMF_EXPORTED, &pwm->flags); 25362306a36Sopenharmony_ci return -ENOMEM; 25462306a36Sopenharmony_ci } 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci export->pwm = pwm; 25762306a36Sopenharmony_ci mutex_init(&export->lock); 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_ci export->child.release = pwm_export_release; 26062306a36Sopenharmony_ci export->child.parent = parent; 26162306a36Sopenharmony_ci export->child.devt = MKDEV(0, 0); 26262306a36Sopenharmony_ci export->child.groups = pwm_groups; 26362306a36Sopenharmony_ci dev_set_name(&export->child, "pwm%u", pwm->hwpwm); 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci ret = device_register(&export->child); 26662306a36Sopenharmony_ci if (ret) { 26762306a36Sopenharmony_ci clear_bit(PWMF_EXPORTED, &pwm->flags); 26862306a36Sopenharmony_ci put_device(&export->child); 26962306a36Sopenharmony_ci export = NULL; 27062306a36Sopenharmony_ci return ret; 27162306a36Sopenharmony_ci } 27262306a36Sopenharmony_ci pwm_prop[0] = kasprintf(GFP_KERNEL, "EXPORT=pwm%u", pwm->hwpwm); 27362306a36Sopenharmony_ci pwm_prop[1] = NULL; 27462306a36Sopenharmony_ci kobject_uevent_env(&parent->kobj, KOBJ_CHANGE, pwm_prop); 27562306a36Sopenharmony_ci kfree(pwm_prop[0]); 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci return 0; 27862306a36Sopenharmony_ci} 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_cistatic int pwm_unexport_match(struct device *child, void *data) 28162306a36Sopenharmony_ci{ 28262306a36Sopenharmony_ci return child_to_pwm_device(child) == data; 28362306a36Sopenharmony_ci} 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_cistatic int pwm_unexport_child(struct device *parent, struct pwm_device *pwm) 28662306a36Sopenharmony_ci{ 28762306a36Sopenharmony_ci struct device *child; 28862306a36Sopenharmony_ci char *pwm_prop[2]; 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_ci if (!test_and_clear_bit(PWMF_EXPORTED, &pwm->flags)) 29162306a36Sopenharmony_ci return -ENODEV; 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci child = device_find_child(parent, pwm, pwm_unexport_match); 29462306a36Sopenharmony_ci if (!child) 29562306a36Sopenharmony_ci return -ENODEV; 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci pwm_prop[0] = kasprintf(GFP_KERNEL, "UNEXPORT=pwm%u", pwm->hwpwm); 29862306a36Sopenharmony_ci pwm_prop[1] = NULL; 29962306a36Sopenharmony_ci kobject_uevent_env(&parent->kobj, KOBJ_CHANGE, pwm_prop); 30062306a36Sopenharmony_ci kfree(pwm_prop[0]); 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_ci /* for device_find_child() */ 30362306a36Sopenharmony_ci put_device(child); 30462306a36Sopenharmony_ci device_unregister(child); 30562306a36Sopenharmony_ci pwm_put(pwm); 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ci return 0; 30862306a36Sopenharmony_ci} 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_cistatic ssize_t export_store(struct device *parent, 31162306a36Sopenharmony_ci struct device_attribute *attr, 31262306a36Sopenharmony_ci const char *buf, size_t len) 31362306a36Sopenharmony_ci{ 31462306a36Sopenharmony_ci struct pwm_chip *chip = dev_get_drvdata(parent); 31562306a36Sopenharmony_ci struct pwm_device *pwm; 31662306a36Sopenharmony_ci unsigned int hwpwm; 31762306a36Sopenharmony_ci int ret; 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ci ret = kstrtouint(buf, 0, &hwpwm); 32062306a36Sopenharmony_ci if (ret < 0) 32162306a36Sopenharmony_ci return ret; 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_ci if (hwpwm >= chip->npwm) 32462306a36Sopenharmony_ci return -ENODEV; 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_ci pwm = pwm_request_from_chip(chip, hwpwm, "sysfs"); 32762306a36Sopenharmony_ci if (IS_ERR(pwm)) 32862306a36Sopenharmony_ci return PTR_ERR(pwm); 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_ci ret = pwm_export_child(parent, pwm); 33162306a36Sopenharmony_ci if (ret < 0) 33262306a36Sopenharmony_ci pwm_put(pwm); 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_ci return ret ? : len; 33562306a36Sopenharmony_ci} 33662306a36Sopenharmony_cistatic DEVICE_ATTR_WO(export); 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_cistatic ssize_t unexport_store(struct device *parent, 33962306a36Sopenharmony_ci struct device_attribute *attr, 34062306a36Sopenharmony_ci const char *buf, size_t len) 34162306a36Sopenharmony_ci{ 34262306a36Sopenharmony_ci struct pwm_chip *chip = dev_get_drvdata(parent); 34362306a36Sopenharmony_ci unsigned int hwpwm; 34462306a36Sopenharmony_ci int ret; 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_ci ret = kstrtouint(buf, 0, &hwpwm); 34762306a36Sopenharmony_ci if (ret < 0) 34862306a36Sopenharmony_ci return ret; 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_ci if (hwpwm >= chip->npwm) 35162306a36Sopenharmony_ci return -ENODEV; 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_ci ret = pwm_unexport_child(parent, &chip->pwms[hwpwm]); 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ci return ret ? : len; 35662306a36Sopenharmony_ci} 35762306a36Sopenharmony_cistatic DEVICE_ATTR_WO(unexport); 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_cistatic ssize_t npwm_show(struct device *parent, struct device_attribute *attr, 36062306a36Sopenharmony_ci char *buf) 36162306a36Sopenharmony_ci{ 36262306a36Sopenharmony_ci const struct pwm_chip *chip = dev_get_drvdata(parent); 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_ci return sysfs_emit(buf, "%u\n", chip->npwm); 36562306a36Sopenharmony_ci} 36662306a36Sopenharmony_cistatic DEVICE_ATTR_RO(npwm); 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_cistatic struct attribute *pwm_chip_attrs[] = { 36962306a36Sopenharmony_ci &dev_attr_export.attr, 37062306a36Sopenharmony_ci &dev_attr_unexport.attr, 37162306a36Sopenharmony_ci &dev_attr_npwm.attr, 37262306a36Sopenharmony_ci NULL, 37362306a36Sopenharmony_ci}; 37462306a36Sopenharmony_ciATTRIBUTE_GROUPS(pwm_chip); 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_ci/* takes export->lock on success */ 37762306a36Sopenharmony_cistatic struct pwm_export *pwm_class_get_state(struct device *parent, 37862306a36Sopenharmony_ci struct pwm_device *pwm, 37962306a36Sopenharmony_ci struct pwm_state *state) 38062306a36Sopenharmony_ci{ 38162306a36Sopenharmony_ci struct device *child; 38262306a36Sopenharmony_ci struct pwm_export *export; 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_ci if (!test_bit(PWMF_EXPORTED, &pwm->flags)) 38562306a36Sopenharmony_ci return NULL; 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_ci child = device_find_child(parent, pwm, pwm_unexport_match); 38862306a36Sopenharmony_ci if (!child) 38962306a36Sopenharmony_ci return NULL; 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_ci export = child_to_pwm_export(child); 39262306a36Sopenharmony_ci put_device(child); /* for device_find_child() */ 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_ci mutex_lock(&export->lock); 39562306a36Sopenharmony_ci pwm_get_state(pwm, state); 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_ci return export; 39862306a36Sopenharmony_ci} 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_cistatic int pwm_class_apply_state(struct pwm_export *export, 40162306a36Sopenharmony_ci struct pwm_device *pwm, 40262306a36Sopenharmony_ci struct pwm_state *state) 40362306a36Sopenharmony_ci{ 40462306a36Sopenharmony_ci int ret = pwm_apply_state(pwm, state); 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_ci /* release lock taken in pwm_class_get_state */ 40762306a36Sopenharmony_ci mutex_unlock(&export->lock); 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_ci return ret; 41062306a36Sopenharmony_ci} 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_cistatic int pwm_class_resume_npwm(struct device *parent, unsigned int npwm) 41362306a36Sopenharmony_ci{ 41462306a36Sopenharmony_ci struct pwm_chip *chip = dev_get_drvdata(parent); 41562306a36Sopenharmony_ci unsigned int i; 41662306a36Sopenharmony_ci int ret = 0; 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_ci for (i = 0; i < npwm; i++) { 41962306a36Sopenharmony_ci struct pwm_device *pwm = &chip->pwms[i]; 42062306a36Sopenharmony_ci struct pwm_state state; 42162306a36Sopenharmony_ci struct pwm_export *export; 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_ci export = pwm_class_get_state(parent, pwm, &state); 42462306a36Sopenharmony_ci if (!export) 42562306a36Sopenharmony_ci continue; 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_ci /* If pwmchip was not enabled before suspend, do nothing. */ 42862306a36Sopenharmony_ci if (!export->suspend.enabled) { 42962306a36Sopenharmony_ci /* release lock taken in pwm_class_get_state */ 43062306a36Sopenharmony_ci mutex_unlock(&export->lock); 43162306a36Sopenharmony_ci continue; 43262306a36Sopenharmony_ci } 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_ci state.enabled = export->suspend.enabled; 43562306a36Sopenharmony_ci ret = pwm_class_apply_state(export, pwm, &state); 43662306a36Sopenharmony_ci if (ret < 0) 43762306a36Sopenharmony_ci break; 43862306a36Sopenharmony_ci } 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_ci return ret; 44162306a36Sopenharmony_ci} 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_cistatic int pwm_class_suspend(struct device *parent) 44462306a36Sopenharmony_ci{ 44562306a36Sopenharmony_ci struct pwm_chip *chip = dev_get_drvdata(parent); 44662306a36Sopenharmony_ci unsigned int i; 44762306a36Sopenharmony_ci int ret = 0; 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_ci for (i = 0; i < chip->npwm; i++) { 45062306a36Sopenharmony_ci struct pwm_device *pwm = &chip->pwms[i]; 45162306a36Sopenharmony_ci struct pwm_state state; 45262306a36Sopenharmony_ci struct pwm_export *export; 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_ci export = pwm_class_get_state(parent, pwm, &state); 45562306a36Sopenharmony_ci if (!export) 45662306a36Sopenharmony_ci continue; 45762306a36Sopenharmony_ci 45862306a36Sopenharmony_ci /* 45962306a36Sopenharmony_ci * If pwmchip was not enabled before suspend, save 46062306a36Sopenharmony_ci * state for resume time and do nothing else. 46162306a36Sopenharmony_ci */ 46262306a36Sopenharmony_ci export->suspend = state; 46362306a36Sopenharmony_ci if (!state.enabled) { 46462306a36Sopenharmony_ci /* release lock taken in pwm_class_get_state */ 46562306a36Sopenharmony_ci mutex_unlock(&export->lock); 46662306a36Sopenharmony_ci continue; 46762306a36Sopenharmony_ci } 46862306a36Sopenharmony_ci 46962306a36Sopenharmony_ci state.enabled = false; 47062306a36Sopenharmony_ci ret = pwm_class_apply_state(export, pwm, &state); 47162306a36Sopenharmony_ci if (ret < 0) { 47262306a36Sopenharmony_ci /* 47362306a36Sopenharmony_ci * roll back the PWM devices that were disabled by 47462306a36Sopenharmony_ci * this suspend function. 47562306a36Sopenharmony_ci */ 47662306a36Sopenharmony_ci pwm_class_resume_npwm(parent, i); 47762306a36Sopenharmony_ci break; 47862306a36Sopenharmony_ci } 47962306a36Sopenharmony_ci } 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_ci return ret; 48262306a36Sopenharmony_ci} 48362306a36Sopenharmony_ci 48462306a36Sopenharmony_cistatic int pwm_class_resume(struct device *parent) 48562306a36Sopenharmony_ci{ 48662306a36Sopenharmony_ci struct pwm_chip *chip = dev_get_drvdata(parent); 48762306a36Sopenharmony_ci 48862306a36Sopenharmony_ci return pwm_class_resume_npwm(parent, chip->npwm); 48962306a36Sopenharmony_ci} 49062306a36Sopenharmony_ci 49162306a36Sopenharmony_cistatic DEFINE_SIMPLE_DEV_PM_OPS(pwm_class_pm_ops, pwm_class_suspend, pwm_class_resume); 49262306a36Sopenharmony_ci 49362306a36Sopenharmony_cistatic struct class pwm_class = { 49462306a36Sopenharmony_ci .name = "pwm", 49562306a36Sopenharmony_ci .dev_groups = pwm_chip_groups, 49662306a36Sopenharmony_ci .pm = pm_sleep_ptr(&pwm_class_pm_ops), 49762306a36Sopenharmony_ci}; 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_cistatic int pwmchip_sysfs_match(struct device *parent, const void *data) 50062306a36Sopenharmony_ci{ 50162306a36Sopenharmony_ci return dev_get_drvdata(parent) == data; 50262306a36Sopenharmony_ci} 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_civoid pwmchip_sysfs_export(struct pwm_chip *chip) 50562306a36Sopenharmony_ci{ 50662306a36Sopenharmony_ci struct device *parent; 50762306a36Sopenharmony_ci 50862306a36Sopenharmony_ci /* 50962306a36Sopenharmony_ci * If device_create() fails the pwm_chip is still usable by 51062306a36Sopenharmony_ci * the kernel it's just not exported. 51162306a36Sopenharmony_ci */ 51262306a36Sopenharmony_ci parent = device_create(&pwm_class, chip->dev, MKDEV(0, 0), chip, 51362306a36Sopenharmony_ci "pwmchip%d", chip->base); 51462306a36Sopenharmony_ci if (IS_ERR(parent)) { 51562306a36Sopenharmony_ci dev_warn(chip->dev, 51662306a36Sopenharmony_ci "device_create failed for pwm_chip sysfs export\n"); 51762306a36Sopenharmony_ci } 51862306a36Sopenharmony_ci} 51962306a36Sopenharmony_ci 52062306a36Sopenharmony_civoid pwmchip_sysfs_unexport(struct pwm_chip *chip) 52162306a36Sopenharmony_ci{ 52262306a36Sopenharmony_ci struct device *parent; 52362306a36Sopenharmony_ci unsigned int i; 52462306a36Sopenharmony_ci 52562306a36Sopenharmony_ci parent = class_find_device(&pwm_class, NULL, chip, 52662306a36Sopenharmony_ci pwmchip_sysfs_match); 52762306a36Sopenharmony_ci if (!parent) 52862306a36Sopenharmony_ci return; 52962306a36Sopenharmony_ci 53062306a36Sopenharmony_ci for (i = 0; i < chip->npwm; i++) { 53162306a36Sopenharmony_ci struct pwm_device *pwm = &chip->pwms[i]; 53262306a36Sopenharmony_ci 53362306a36Sopenharmony_ci if (test_bit(PWMF_EXPORTED, &pwm->flags)) 53462306a36Sopenharmony_ci pwm_unexport_child(parent, pwm); 53562306a36Sopenharmony_ci } 53662306a36Sopenharmony_ci 53762306a36Sopenharmony_ci put_device(parent); 53862306a36Sopenharmony_ci device_unregister(parent); 53962306a36Sopenharmony_ci} 54062306a36Sopenharmony_ci 54162306a36Sopenharmony_cistatic int __init pwm_sysfs_init(void) 54262306a36Sopenharmony_ci{ 54362306a36Sopenharmony_ci return class_register(&pwm_class); 54462306a36Sopenharmony_ci} 54562306a36Sopenharmony_cisubsys_initcall(pwm_sysfs_init); 546