13e0a4e85SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later 276abbddeSH Hartley Sweeten /* 376abbddeSH Hartley Sweeten * A simple sysfs interface for the generic PWM framework 476abbddeSH Hartley Sweeten * 576abbddeSH Hartley Sweeten * Copyright (C) 2013 H Hartley Sweeten <hsweeten@visionengravers.com> 676abbddeSH Hartley Sweeten * 776abbddeSH Hartley Sweeten * Based on previous work by Lars Poeschel <poeschel@lemonage.de> 876abbddeSH Hartley Sweeten */ 976abbddeSH Hartley Sweeten 1076abbddeSH Hartley Sweeten #include <linux/device.h> 1176abbddeSH Hartley Sweeten #include <linux/mutex.h> 1276abbddeSH Hartley Sweeten #include <linux/err.h> 1376abbddeSH Hartley Sweeten #include <linux/slab.h> 1476abbddeSH Hartley Sweeten #include <linux/kdev_t.h> 1576abbddeSH Hartley Sweeten #include <linux/pwm.h> 1676abbddeSH Hartley Sweeten 1776abbddeSH Hartley Sweeten struct pwm_export { 1876abbddeSH Hartley Sweeten struct device child; 1976abbddeSH Hartley Sweeten struct pwm_device *pwm; 20459a25afSBoris BREZILLON struct mutex lock; 217fd4edc5SYoshihiro Shimoda struct pwm_state suspend; 2276abbddeSH Hartley Sweeten }; 2376abbddeSH Hartley Sweeten 2476abbddeSH Hartley Sweeten static struct pwm_export *child_to_pwm_export(struct device *child) 2576abbddeSH Hartley Sweeten { 2676abbddeSH Hartley Sweeten return container_of(child, struct pwm_export, child); 2776abbddeSH Hartley Sweeten } 2876abbddeSH Hartley Sweeten 2976abbddeSH Hartley Sweeten static struct pwm_device *child_to_pwm_device(struct device *child) 3076abbddeSH Hartley Sweeten { 3176abbddeSH Hartley Sweeten struct pwm_export *export = child_to_pwm_export(child); 3276abbddeSH Hartley Sweeten 3376abbddeSH Hartley Sweeten return export->pwm; 3476abbddeSH Hartley Sweeten } 3576abbddeSH Hartley Sweeten 3665cdc691SOlliver Schinagl static ssize_t period_show(struct device *child, 3776abbddeSH Hartley Sweeten struct device_attribute *attr, 3876abbddeSH Hartley Sweeten char *buf) 3976abbddeSH Hartley Sweeten { 4076abbddeSH Hartley Sweeten const struct pwm_device *pwm = child_to_pwm_device(child); 4139100ceeSBoris Brezillon struct pwm_state state; 4276abbddeSH Hartley Sweeten 4339100ceeSBoris Brezillon pwm_get_state(pwm, &state); 4439100ceeSBoris Brezillon 45a9d887dcSGuru Das Srinagesh return sprintf(buf, "%llu\n", state.period); 4676abbddeSH Hartley Sweeten } 4776abbddeSH Hartley Sweeten 4865cdc691SOlliver Schinagl static ssize_t period_store(struct device *child, 4976abbddeSH Hartley Sweeten struct device_attribute *attr, 5076abbddeSH Hartley Sweeten const char *buf, size_t size) 5176abbddeSH Hartley Sweeten { 52459a25afSBoris BREZILLON struct pwm_export *export = child_to_pwm_export(child); 53459a25afSBoris BREZILLON struct pwm_device *pwm = export->pwm; 5439100ceeSBoris Brezillon struct pwm_state state; 55a9d887dcSGuru Das Srinagesh u64 val; 5676abbddeSH Hartley Sweeten int ret; 5776abbddeSH Hartley Sweeten 58a9d887dcSGuru Das Srinagesh ret = kstrtou64(buf, 0, &val); 5976abbddeSH Hartley Sweeten if (ret) 6076abbddeSH Hartley Sweeten return ret; 6176abbddeSH Hartley Sweeten 62459a25afSBoris BREZILLON mutex_lock(&export->lock); 6339100ceeSBoris Brezillon pwm_get_state(pwm, &state); 6439100ceeSBoris Brezillon state.period = val; 6539100ceeSBoris Brezillon ret = pwm_apply_state(pwm, &state); 66459a25afSBoris BREZILLON mutex_unlock(&export->lock); 6776abbddeSH Hartley Sweeten 6876abbddeSH Hartley Sweeten return ret ? : size; 6976abbddeSH Hartley Sweeten } 7076abbddeSH Hartley Sweeten 7165cdc691SOlliver Schinagl static ssize_t duty_cycle_show(struct device *child, 7276abbddeSH Hartley Sweeten struct device_attribute *attr, 7376abbddeSH Hartley Sweeten char *buf) 7476abbddeSH Hartley Sweeten { 7576abbddeSH Hartley Sweeten const struct pwm_device *pwm = child_to_pwm_device(child); 7639100ceeSBoris Brezillon struct pwm_state state; 7776abbddeSH Hartley Sweeten 7839100ceeSBoris Brezillon pwm_get_state(pwm, &state); 7939100ceeSBoris Brezillon 80a9d887dcSGuru Das Srinagesh return sprintf(buf, "%llu\n", state.duty_cycle); 8176abbddeSH Hartley Sweeten } 8276abbddeSH Hartley Sweeten 8365cdc691SOlliver Schinagl static ssize_t duty_cycle_store(struct device *child, 8476abbddeSH Hartley Sweeten struct device_attribute *attr, 8576abbddeSH Hartley Sweeten const char *buf, size_t size) 8676abbddeSH Hartley Sweeten { 87459a25afSBoris BREZILLON struct pwm_export *export = child_to_pwm_export(child); 88459a25afSBoris BREZILLON struct pwm_device *pwm = export->pwm; 8939100ceeSBoris Brezillon struct pwm_state state; 9076abbddeSH Hartley Sweeten unsigned int val; 9176abbddeSH Hartley Sweeten int ret; 9276abbddeSH Hartley Sweeten 9376abbddeSH Hartley Sweeten ret = kstrtouint(buf, 0, &val); 9476abbddeSH Hartley Sweeten if (ret) 9576abbddeSH Hartley Sweeten return ret; 9676abbddeSH Hartley Sweeten 97459a25afSBoris BREZILLON mutex_lock(&export->lock); 9839100ceeSBoris Brezillon pwm_get_state(pwm, &state); 9939100ceeSBoris Brezillon state.duty_cycle = val; 10039100ceeSBoris Brezillon ret = pwm_apply_state(pwm, &state); 101459a25afSBoris BREZILLON mutex_unlock(&export->lock); 10276abbddeSH Hartley Sweeten 10376abbddeSH Hartley Sweeten return ret ? : size; 10476abbddeSH Hartley Sweeten } 10576abbddeSH Hartley Sweeten 10665cdc691SOlliver Schinagl static ssize_t enable_show(struct device *child, 10776abbddeSH Hartley Sweeten struct device_attribute *attr, 10876abbddeSH Hartley Sweeten char *buf) 10976abbddeSH Hartley Sweeten { 11076abbddeSH Hartley Sweeten const struct pwm_device *pwm = child_to_pwm_device(child); 11139100ceeSBoris Brezillon struct pwm_state state; 11276abbddeSH Hartley Sweeten 11339100ceeSBoris Brezillon pwm_get_state(pwm, &state); 11439100ceeSBoris Brezillon 11539100ceeSBoris Brezillon return sprintf(buf, "%d\n", state.enabled); 11676abbddeSH Hartley Sweeten } 11776abbddeSH Hartley Sweeten 11865cdc691SOlliver Schinagl static ssize_t enable_store(struct device *child, 11976abbddeSH Hartley Sweeten struct device_attribute *attr, 12076abbddeSH Hartley Sweeten const char *buf, size_t size) 12176abbddeSH Hartley Sweeten { 122459a25afSBoris BREZILLON struct pwm_export *export = child_to_pwm_export(child); 123459a25afSBoris BREZILLON struct pwm_device *pwm = export->pwm; 12439100ceeSBoris Brezillon struct pwm_state state; 12576abbddeSH Hartley Sweeten int val, ret; 12676abbddeSH Hartley Sweeten 12776abbddeSH Hartley Sweeten ret = kstrtoint(buf, 0, &val); 12876abbddeSH Hartley Sweeten if (ret) 12976abbddeSH Hartley Sweeten return ret; 13076abbddeSH Hartley Sweeten 131459a25afSBoris BREZILLON mutex_lock(&export->lock); 132459a25afSBoris BREZILLON 13339100ceeSBoris Brezillon pwm_get_state(pwm, &state); 13439100ceeSBoris Brezillon 13576abbddeSH Hartley Sweeten switch (val) { 13676abbddeSH Hartley Sweeten case 0: 13739100ceeSBoris Brezillon state.enabled = false; 13876abbddeSH Hartley Sweeten break; 13976abbddeSH Hartley Sweeten case 1: 14039100ceeSBoris Brezillon state.enabled = true; 14176abbddeSH Hartley Sweeten break; 14276abbddeSH Hartley Sweeten default: 14376abbddeSH Hartley Sweeten ret = -EINVAL; 14439100ceeSBoris Brezillon goto unlock; 14576abbddeSH Hartley Sweeten } 14676abbddeSH Hartley Sweeten 147fe5aa34dSRyo Kodama ret = pwm_apply_state(pwm, &state); 148459a25afSBoris BREZILLON 14939100ceeSBoris Brezillon unlock: 15039100ceeSBoris Brezillon mutex_unlock(&export->lock); 15176abbddeSH Hartley Sweeten return ret ? : size; 15276abbddeSH Hartley Sweeten } 15376abbddeSH Hartley Sweeten 15465cdc691SOlliver Schinagl static ssize_t polarity_show(struct device *child, 15576abbddeSH Hartley Sweeten struct device_attribute *attr, 15676abbddeSH Hartley Sweeten char *buf) 15776abbddeSH Hartley Sweeten { 15876abbddeSH Hartley Sweeten const struct pwm_device *pwm = child_to_pwm_device(child); 1595a063d87SThierry Reding const char *polarity = "unknown"; 16039100ceeSBoris Brezillon struct pwm_state state; 16176abbddeSH Hartley Sweeten 16239100ceeSBoris Brezillon pwm_get_state(pwm, &state); 16339100ceeSBoris Brezillon 16439100ceeSBoris Brezillon switch (state.polarity) { 1655a063d87SThierry Reding case PWM_POLARITY_NORMAL: 1665a063d87SThierry Reding polarity = "normal"; 1675a063d87SThierry Reding break; 1685a063d87SThierry Reding 1695a063d87SThierry Reding case PWM_POLARITY_INVERSED: 1705a063d87SThierry Reding polarity = "inversed"; 1715a063d87SThierry Reding break; 1725a063d87SThierry Reding } 1735a063d87SThierry Reding 1745a063d87SThierry Reding return sprintf(buf, "%s\n", polarity); 17576abbddeSH Hartley Sweeten } 17676abbddeSH Hartley Sweeten 17765cdc691SOlliver Schinagl static ssize_t polarity_store(struct device *child, 17876abbddeSH Hartley Sweeten struct device_attribute *attr, 17976abbddeSH Hartley Sweeten const char *buf, size_t size) 18076abbddeSH Hartley Sweeten { 181459a25afSBoris BREZILLON struct pwm_export *export = child_to_pwm_export(child); 182459a25afSBoris BREZILLON struct pwm_device *pwm = export->pwm; 18376abbddeSH Hartley Sweeten enum pwm_polarity polarity; 18439100ceeSBoris Brezillon struct pwm_state state; 18576abbddeSH Hartley Sweeten int ret; 18676abbddeSH Hartley Sweeten 18776abbddeSH Hartley Sweeten if (sysfs_streq(buf, "normal")) 18876abbddeSH Hartley Sweeten polarity = PWM_POLARITY_NORMAL; 18976abbddeSH Hartley Sweeten else if (sysfs_streq(buf, "inversed")) 19076abbddeSH Hartley Sweeten polarity = PWM_POLARITY_INVERSED; 19176abbddeSH Hartley Sweeten else 19276abbddeSH Hartley Sweeten return -EINVAL; 19376abbddeSH Hartley Sweeten 194459a25afSBoris BREZILLON mutex_lock(&export->lock); 19539100ceeSBoris Brezillon pwm_get_state(pwm, &state); 19639100ceeSBoris Brezillon state.polarity = polarity; 19739100ceeSBoris Brezillon ret = pwm_apply_state(pwm, &state); 198459a25afSBoris BREZILLON mutex_unlock(&export->lock); 19976abbddeSH Hartley Sweeten 20076abbddeSH Hartley Sweeten return ret ? : size; 20176abbddeSH Hartley Sweeten } 20276abbddeSH Hartley Sweeten 2031a366fe9SLee Jones static ssize_t capture_show(struct device *child, 2041a366fe9SLee Jones struct device_attribute *attr, 2051a366fe9SLee Jones char *buf) 2061a366fe9SLee Jones { 2071a366fe9SLee Jones struct pwm_device *pwm = child_to_pwm_device(child); 2081a366fe9SLee Jones struct pwm_capture result; 2091a366fe9SLee Jones int ret; 2101a366fe9SLee Jones 2111a366fe9SLee Jones ret = pwm_capture(pwm, &result, jiffies_to_msecs(HZ)); 2121a366fe9SLee Jones if (ret) 2131a366fe9SLee Jones return ret; 2141a366fe9SLee Jones 2151a366fe9SLee Jones return sprintf(buf, "%u %u\n", result.period, result.duty_cycle); 2161a366fe9SLee Jones } 2171a366fe9SLee Jones 21865cdc691SOlliver Schinagl static DEVICE_ATTR_RW(period); 21965cdc691SOlliver Schinagl static DEVICE_ATTR_RW(duty_cycle); 22065cdc691SOlliver Schinagl static DEVICE_ATTR_RW(enable); 22165cdc691SOlliver Schinagl static DEVICE_ATTR_RW(polarity); 2221a366fe9SLee Jones static DEVICE_ATTR_RO(capture); 22376abbddeSH Hartley Sweeten 22476abbddeSH Hartley Sweeten static struct attribute *pwm_attrs[] = { 22576abbddeSH Hartley Sweeten &dev_attr_period.attr, 22676abbddeSH Hartley Sweeten &dev_attr_duty_cycle.attr, 22776abbddeSH Hartley Sweeten &dev_attr_enable.attr, 22876abbddeSH Hartley Sweeten &dev_attr_polarity.attr, 2291a366fe9SLee Jones &dev_attr_capture.attr, 23076abbddeSH Hartley Sweeten NULL 23176abbddeSH Hartley Sweeten }; 2326ca142adSAxel Lin ATTRIBUTE_GROUPS(pwm); 23376abbddeSH Hartley Sweeten 23476abbddeSH Hartley Sweeten static void pwm_export_release(struct device *child) 23576abbddeSH Hartley Sweeten { 23676abbddeSH Hartley Sweeten struct pwm_export *export = child_to_pwm_export(child); 23776abbddeSH Hartley Sweeten 23876abbddeSH Hartley Sweeten kfree(export); 23976abbddeSH Hartley Sweeten } 24076abbddeSH Hartley Sweeten 24176abbddeSH Hartley Sweeten static int pwm_export_child(struct device *parent, struct pwm_device *pwm) 24276abbddeSH Hartley Sweeten { 24376abbddeSH Hartley Sweeten struct pwm_export *export; 244552c02e3SFabrice Gasnier char *pwm_prop[2]; 24576abbddeSH Hartley Sweeten int ret; 24676abbddeSH Hartley Sweeten 24776abbddeSH Hartley Sweeten if (test_and_set_bit(PWMF_EXPORTED, &pwm->flags)) 24876abbddeSH Hartley Sweeten return -EBUSY; 24976abbddeSH Hartley Sweeten 25076abbddeSH Hartley Sweeten export = kzalloc(sizeof(*export), GFP_KERNEL); 25176abbddeSH Hartley Sweeten if (!export) { 25276abbddeSH Hartley Sweeten clear_bit(PWMF_EXPORTED, &pwm->flags); 25376abbddeSH Hartley Sweeten return -ENOMEM; 25476abbddeSH Hartley Sweeten } 25576abbddeSH Hartley Sweeten 25676abbddeSH Hartley Sweeten export->pwm = pwm; 257459a25afSBoris BREZILLON mutex_init(&export->lock); 25876abbddeSH Hartley Sweeten 25976abbddeSH Hartley Sweeten export->child.release = pwm_export_release; 26076abbddeSH Hartley Sweeten export->child.parent = parent; 26176abbddeSH Hartley Sweeten export->child.devt = MKDEV(0, 0); 2626ca142adSAxel Lin export->child.groups = pwm_groups; 26376abbddeSH Hartley Sweeten dev_set_name(&export->child, "pwm%u", pwm->hwpwm); 26476abbddeSH Hartley Sweeten 26576abbddeSH Hartley Sweeten ret = device_register(&export->child); 26676abbddeSH Hartley Sweeten if (ret) { 26776abbddeSH Hartley Sweeten clear_bit(PWMF_EXPORTED, &pwm->flags); 2688bbf5b42SArvind Yadav put_device(&export->child); 2698bbf5b42SArvind Yadav export = NULL; 27076abbddeSH Hartley Sweeten return ret; 27176abbddeSH Hartley Sweeten } 272552c02e3SFabrice Gasnier pwm_prop[0] = kasprintf(GFP_KERNEL, "EXPORT=pwm%u", pwm->hwpwm); 273552c02e3SFabrice Gasnier pwm_prop[1] = NULL; 274552c02e3SFabrice Gasnier kobject_uevent_env(&parent->kobj, KOBJ_CHANGE, pwm_prop); 275552c02e3SFabrice Gasnier kfree(pwm_prop[0]); 27676abbddeSH Hartley Sweeten 27776abbddeSH Hartley Sweeten return 0; 27876abbddeSH Hartley Sweeten } 27976abbddeSH Hartley Sweeten 28076abbddeSH Hartley Sweeten static int pwm_unexport_match(struct device *child, void *data) 28176abbddeSH Hartley Sweeten { 28276abbddeSH Hartley Sweeten return child_to_pwm_device(child) == data; 28376abbddeSH Hartley Sweeten } 28476abbddeSH Hartley Sweeten 28576abbddeSH Hartley Sweeten static int pwm_unexport_child(struct device *parent, struct pwm_device *pwm) 28676abbddeSH Hartley Sweeten { 28776abbddeSH Hartley Sweeten struct device *child; 288552c02e3SFabrice Gasnier char *pwm_prop[2]; 28976abbddeSH Hartley Sweeten 29076abbddeSH Hartley Sweeten if (!test_and_clear_bit(PWMF_EXPORTED, &pwm->flags)) 29176abbddeSH Hartley Sweeten return -ENODEV; 29276abbddeSH Hartley Sweeten 29376abbddeSH Hartley Sweeten child = device_find_child(parent, pwm, pwm_unexport_match); 29476abbddeSH Hartley Sweeten if (!child) 29576abbddeSH Hartley Sweeten return -ENODEV; 29676abbddeSH Hartley Sweeten 297552c02e3SFabrice Gasnier pwm_prop[0] = kasprintf(GFP_KERNEL, "UNEXPORT=pwm%u", pwm->hwpwm); 298552c02e3SFabrice Gasnier pwm_prop[1] = NULL; 299552c02e3SFabrice Gasnier kobject_uevent_env(&parent->kobj, KOBJ_CHANGE, pwm_prop); 300552c02e3SFabrice Gasnier kfree(pwm_prop[0]); 301552c02e3SFabrice Gasnier 30276abbddeSH Hartley Sweeten /* for device_find_child() */ 30376abbddeSH Hartley Sweeten put_device(child); 30476abbddeSH Hartley Sweeten device_unregister(child); 30576abbddeSH Hartley Sweeten pwm_put(pwm); 30676abbddeSH Hartley Sweeten 30776abbddeSH Hartley Sweeten return 0; 30876abbddeSH Hartley Sweeten } 30976abbddeSH Hartley Sweeten 31065cdc691SOlliver Schinagl static ssize_t export_store(struct device *parent, 31176abbddeSH Hartley Sweeten struct device_attribute *attr, 31276abbddeSH Hartley Sweeten const char *buf, size_t len) 31376abbddeSH Hartley Sweeten { 31476abbddeSH Hartley Sweeten struct pwm_chip *chip = dev_get_drvdata(parent); 31576abbddeSH Hartley Sweeten struct pwm_device *pwm; 31676abbddeSH Hartley Sweeten unsigned int hwpwm; 31776abbddeSH Hartley Sweeten int ret; 31876abbddeSH Hartley Sweeten 31976abbddeSH Hartley Sweeten ret = kstrtouint(buf, 0, &hwpwm); 32076abbddeSH Hartley Sweeten if (ret < 0) 32176abbddeSH Hartley Sweeten return ret; 32276abbddeSH Hartley Sweeten 32376abbddeSH Hartley Sweeten if (hwpwm >= chip->npwm) 32476abbddeSH Hartley Sweeten return -ENODEV; 32576abbddeSH Hartley Sweeten 32676abbddeSH Hartley Sweeten pwm = pwm_request_from_chip(chip, hwpwm, "sysfs"); 32776abbddeSH Hartley Sweeten if (IS_ERR(pwm)) 32876abbddeSH Hartley Sweeten return PTR_ERR(pwm); 32976abbddeSH Hartley Sweeten 33076abbddeSH Hartley Sweeten ret = pwm_export_child(parent, pwm); 33176abbddeSH Hartley Sweeten if (ret < 0) 33276abbddeSH Hartley Sweeten pwm_put(pwm); 33376abbddeSH Hartley Sweeten 33476abbddeSH Hartley Sweeten return ret ? : len; 33576abbddeSH Hartley Sweeten } 33665cdc691SOlliver Schinagl static DEVICE_ATTR_WO(export); 33776abbddeSH Hartley Sweeten 33865cdc691SOlliver Schinagl static ssize_t unexport_store(struct device *parent, 33976abbddeSH Hartley Sweeten struct device_attribute *attr, 34076abbddeSH Hartley Sweeten const char *buf, size_t len) 34176abbddeSH Hartley Sweeten { 34276abbddeSH Hartley Sweeten struct pwm_chip *chip = dev_get_drvdata(parent); 34376abbddeSH Hartley Sweeten unsigned int hwpwm; 34476abbddeSH Hartley Sweeten int ret; 34576abbddeSH Hartley Sweeten 34676abbddeSH Hartley Sweeten ret = kstrtouint(buf, 0, &hwpwm); 34776abbddeSH Hartley Sweeten if (ret < 0) 34876abbddeSH Hartley Sweeten return ret; 34976abbddeSH Hartley Sweeten 35076abbddeSH Hartley Sweeten if (hwpwm >= chip->npwm) 35176abbddeSH Hartley Sweeten return -ENODEV; 35276abbddeSH Hartley Sweeten 35376abbddeSH Hartley Sweeten ret = pwm_unexport_child(parent, &chip->pwms[hwpwm]); 35476abbddeSH Hartley Sweeten 35576abbddeSH Hartley Sweeten return ret ? : len; 35676abbddeSH Hartley Sweeten } 35765cdc691SOlliver Schinagl static DEVICE_ATTR_WO(unexport); 35876abbddeSH Hartley Sweeten 3599da01759SGreg Kroah-Hartman static ssize_t npwm_show(struct device *parent, struct device_attribute *attr, 36076abbddeSH Hartley Sweeten char *buf) 36176abbddeSH Hartley Sweeten { 36276abbddeSH Hartley Sweeten const struct pwm_chip *chip = dev_get_drvdata(parent); 36376abbddeSH Hartley Sweeten 36476abbddeSH Hartley Sweeten return sprintf(buf, "%u\n", chip->npwm); 36576abbddeSH Hartley Sweeten } 3669da01759SGreg Kroah-Hartman static DEVICE_ATTR_RO(npwm); 36776abbddeSH Hartley Sweeten 3689da01759SGreg Kroah-Hartman static struct attribute *pwm_chip_attrs[] = { 3699da01759SGreg Kroah-Hartman &dev_attr_export.attr, 3709da01759SGreg Kroah-Hartman &dev_attr_unexport.attr, 3719da01759SGreg Kroah-Hartman &dev_attr_npwm.attr, 3729da01759SGreg Kroah-Hartman NULL, 37376abbddeSH Hartley Sweeten }; 3749da01759SGreg Kroah-Hartman ATTRIBUTE_GROUPS(pwm_chip); 37576abbddeSH Hartley Sweeten 3767fd4edc5SYoshihiro Shimoda /* takes export->lock on success */ 3777fd4edc5SYoshihiro Shimoda static struct pwm_export *pwm_class_get_state(struct device *parent, 3787fd4edc5SYoshihiro Shimoda struct pwm_device *pwm, 3797fd4edc5SYoshihiro Shimoda struct pwm_state *state) 3807fd4edc5SYoshihiro Shimoda { 3817fd4edc5SYoshihiro Shimoda struct device *child; 3827fd4edc5SYoshihiro Shimoda struct pwm_export *export; 3837fd4edc5SYoshihiro Shimoda 3847fd4edc5SYoshihiro Shimoda if (!test_bit(PWMF_EXPORTED, &pwm->flags)) 3857fd4edc5SYoshihiro Shimoda return NULL; 3867fd4edc5SYoshihiro Shimoda 3877fd4edc5SYoshihiro Shimoda child = device_find_child(parent, pwm, pwm_unexport_match); 3887fd4edc5SYoshihiro Shimoda if (!child) 3897fd4edc5SYoshihiro Shimoda return NULL; 3907fd4edc5SYoshihiro Shimoda 3917fd4edc5SYoshihiro Shimoda export = child_to_pwm_export(child); 3927fd4edc5SYoshihiro Shimoda put_device(child); /* for device_find_child() */ 3937fd4edc5SYoshihiro Shimoda 3947fd4edc5SYoshihiro Shimoda mutex_lock(&export->lock); 3957fd4edc5SYoshihiro Shimoda pwm_get_state(pwm, state); 3967fd4edc5SYoshihiro Shimoda 3977fd4edc5SYoshihiro Shimoda return export; 3987fd4edc5SYoshihiro Shimoda } 3997fd4edc5SYoshihiro Shimoda 4007fd4edc5SYoshihiro Shimoda static int pwm_class_apply_state(struct pwm_export *export, 4017fd4edc5SYoshihiro Shimoda struct pwm_device *pwm, 4027fd4edc5SYoshihiro Shimoda struct pwm_state *state) 4037fd4edc5SYoshihiro Shimoda { 4047fd4edc5SYoshihiro Shimoda int ret = pwm_apply_state(pwm, state); 4057fd4edc5SYoshihiro Shimoda 4067fd4edc5SYoshihiro Shimoda /* release lock taken in pwm_class_get_state */ 4077fd4edc5SYoshihiro Shimoda mutex_unlock(&export->lock); 4087fd4edc5SYoshihiro Shimoda 4097fd4edc5SYoshihiro Shimoda return ret; 4107fd4edc5SYoshihiro Shimoda } 4117fd4edc5SYoshihiro Shimoda 4127fd4edc5SYoshihiro Shimoda static int pwm_class_resume_npwm(struct device *parent, unsigned int npwm) 4137fd4edc5SYoshihiro Shimoda { 4147fd4edc5SYoshihiro Shimoda struct pwm_chip *chip = dev_get_drvdata(parent); 4157fd4edc5SYoshihiro Shimoda unsigned int i; 4167fd4edc5SYoshihiro Shimoda int ret = 0; 4177fd4edc5SYoshihiro Shimoda 4187fd4edc5SYoshihiro Shimoda for (i = 0; i < npwm; i++) { 4197fd4edc5SYoshihiro Shimoda struct pwm_device *pwm = &chip->pwms[i]; 4207fd4edc5SYoshihiro Shimoda struct pwm_state state; 4217fd4edc5SYoshihiro Shimoda struct pwm_export *export; 4227fd4edc5SYoshihiro Shimoda 4237fd4edc5SYoshihiro Shimoda export = pwm_class_get_state(parent, pwm, &state); 4247fd4edc5SYoshihiro Shimoda if (!export) 4257fd4edc5SYoshihiro Shimoda continue; 4267fd4edc5SYoshihiro Shimoda 4277fd4edc5SYoshihiro Shimoda state.enabled = export->suspend.enabled; 4287fd4edc5SYoshihiro Shimoda ret = pwm_class_apply_state(export, pwm, &state); 4297fd4edc5SYoshihiro Shimoda if (ret < 0) 4307fd4edc5SYoshihiro Shimoda break; 4317fd4edc5SYoshihiro Shimoda } 4327fd4edc5SYoshihiro Shimoda 4337fd4edc5SYoshihiro Shimoda return ret; 4347fd4edc5SYoshihiro Shimoda } 4357fd4edc5SYoshihiro Shimoda 4367fd4edc5SYoshihiro Shimoda static int __maybe_unused pwm_class_suspend(struct device *parent) 4377fd4edc5SYoshihiro Shimoda { 4387fd4edc5SYoshihiro Shimoda struct pwm_chip *chip = dev_get_drvdata(parent); 4397fd4edc5SYoshihiro Shimoda unsigned int i; 4407fd4edc5SYoshihiro Shimoda int ret = 0; 4417fd4edc5SYoshihiro Shimoda 4427fd4edc5SYoshihiro Shimoda for (i = 0; i < chip->npwm; i++) { 4437fd4edc5SYoshihiro Shimoda struct pwm_device *pwm = &chip->pwms[i]; 4447fd4edc5SYoshihiro Shimoda struct pwm_state state; 4457fd4edc5SYoshihiro Shimoda struct pwm_export *export; 4467fd4edc5SYoshihiro Shimoda 4477fd4edc5SYoshihiro Shimoda export = pwm_class_get_state(parent, pwm, &state); 4487fd4edc5SYoshihiro Shimoda if (!export) 4497fd4edc5SYoshihiro Shimoda continue; 4507fd4edc5SYoshihiro Shimoda 4517fd4edc5SYoshihiro Shimoda export->suspend = state; 4527fd4edc5SYoshihiro Shimoda state.enabled = false; 4537fd4edc5SYoshihiro Shimoda ret = pwm_class_apply_state(export, pwm, &state); 4547fd4edc5SYoshihiro Shimoda if (ret < 0) { 4557fd4edc5SYoshihiro Shimoda /* 4567fd4edc5SYoshihiro Shimoda * roll back the PWM devices that were disabled by 4577fd4edc5SYoshihiro Shimoda * this suspend function. 4587fd4edc5SYoshihiro Shimoda */ 4597fd4edc5SYoshihiro Shimoda pwm_class_resume_npwm(parent, i); 4607fd4edc5SYoshihiro Shimoda break; 4617fd4edc5SYoshihiro Shimoda } 4627fd4edc5SYoshihiro Shimoda } 4637fd4edc5SYoshihiro Shimoda 4647fd4edc5SYoshihiro Shimoda return ret; 4657fd4edc5SYoshihiro Shimoda } 4667fd4edc5SYoshihiro Shimoda 4677fd4edc5SYoshihiro Shimoda static int __maybe_unused pwm_class_resume(struct device *parent) 4687fd4edc5SYoshihiro Shimoda { 4697fd4edc5SYoshihiro Shimoda struct pwm_chip *chip = dev_get_drvdata(parent); 4707fd4edc5SYoshihiro Shimoda 4717fd4edc5SYoshihiro Shimoda return pwm_class_resume_npwm(parent, chip->npwm); 4727fd4edc5SYoshihiro Shimoda } 4737fd4edc5SYoshihiro Shimoda 4747fd4edc5SYoshihiro Shimoda static SIMPLE_DEV_PM_OPS(pwm_class_pm_ops, pwm_class_suspend, pwm_class_resume); 4757fd4edc5SYoshihiro Shimoda 47676abbddeSH Hartley Sweeten static struct class pwm_class = { 47776abbddeSH Hartley Sweeten .name = "pwm", 47876abbddeSH Hartley Sweeten .owner = THIS_MODULE, 4799da01759SGreg Kroah-Hartman .dev_groups = pwm_chip_groups, 4807fd4edc5SYoshihiro Shimoda .pm = &pwm_class_pm_ops, 48176abbddeSH Hartley Sweeten }; 48276abbddeSH Hartley Sweeten 48376abbddeSH Hartley Sweeten static int pwmchip_sysfs_match(struct device *parent, const void *data) 48476abbddeSH Hartley Sweeten { 48576abbddeSH Hartley Sweeten return dev_get_drvdata(parent) == data; 48676abbddeSH Hartley Sweeten } 48776abbddeSH Hartley Sweeten 48876abbddeSH Hartley Sweeten void pwmchip_sysfs_export(struct pwm_chip *chip) 48976abbddeSH Hartley Sweeten { 49076abbddeSH Hartley Sweeten struct device *parent; 49176abbddeSH Hartley Sweeten 49276abbddeSH Hartley Sweeten /* 49376abbddeSH Hartley Sweeten * If device_create() fails the pwm_chip is still usable by 4949ff06679SUwe Kleine-König * the kernel it's just not exported. 49576abbddeSH Hartley Sweeten */ 49676abbddeSH Hartley Sweeten parent = device_create(&pwm_class, chip->dev, MKDEV(0, 0), chip, 49776abbddeSH Hartley Sweeten "pwmchip%d", chip->base); 49876abbddeSH Hartley Sweeten if (IS_ERR(parent)) { 49976abbddeSH Hartley Sweeten dev_warn(chip->dev, 50076abbddeSH Hartley Sweeten "device_create failed for pwm_chip sysfs export\n"); 50176abbddeSH Hartley Sweeten } 50276abbddeSH Hartley Sweeten } 50376abbddeSH Hartley Sweeten 50476abbddeSH Hartley Sweeten void pwmchip_sysfs_unexport(struct pwm_chip *chip) 50576abbddeSH Hartley Sweeten { 50676abbddeSH Hartley Sweeten struct device *parent; 5070733424cSDavid Hsu unsigned int i; 5080733424cSDavid Hsu 5090733424cSDavid Hsu parent = class_find_device(&pwm_class, NULL, chip, 5100733424cSDavid Hsu pwmchip_sysfs_match); 5110733424cSDavid Hsu if (!parent) 5120733424cSDavid Hsu return; 5130733424cSDavid Hsu 5140733424cSDavid Hsu for (i = 0; i < chip->npwm; i++) { 5150733424cSDavid Hsu struct pwm_device *pwm = &chip->pwms[i]; 5160733424cSDavid Hsu 5170733424cSDavid Hsu if (test_bit(PWMF_EXPORTED, &pwm->flags)) 5180733424cSDavid Hsu pwm_unexport_child(parent, pwm); 5190733424cSDavid Hsu } 5200e1614acSJohan Hovold 5210e1614acSJohan Hovold put_device(parent); 522347ab948SPhong Hoang device_unregister(parent); 5230733424cSDavid Hsu } 5240733424cSDavid Hsu 52576abbddeSH Hartley Sweeten static int __init pwm_sysfs_init(void) 52676abbddeSH Hartley Sweeten { 52776abbddeSH Hartley Sweeten return class_register(&pwm_class); 52876abbddeSH Hartley Sweeten } 52976abbddeSH Hartley Sweeten subsys_initcall(pwm_sysfs_init); 530