summaryrefslogtreecommitdiff
path: root/drivers/pwm/core.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2024-05-14 15:03:19 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2024-05-14 15:03:19 -0700
commit9d81e2d5a9e4befa119e40742a60c366e15d76ce (patch)
treef16cc19e854074d8a9844129e24396031b317906 /drivers/pwm/core.c
parent00fddaf58854717a075f3690c828b61290701e7e (diff)
parent4817118f257e49b043f3d80f021a327b7e1d796f (diff)
Merge tag 'pwm/for-6.10-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/ukleinek/linux
Pull pwm updates from Uwe Kleine-König: "Apart for the normal updates for dt bindings, cleanups and support for new device variants to existing drivers this completes the conversion to pwmchip_alloc() which was started in the v6.9 development cycle. Using pwmchip_alloc() is a precondition to the character device support which allows easier and faster access to PWM devices. However there are some issues I want to clean up before including it in mainline, so this isn't contained here despite it was in next for some time. Thanks to Alexandre Mergnat, Binbin Zhou, Dmitry Rokosov, George Stark, Jerome Brunet and Varshini Rajendran for their contributions. Further thanks go to AngeloGioacchino Del Regno, Conor Dooley, David Lechner, Fabrice Gasnier, Florian Fainelli, Guenter Roeck, Gustavo A. R. Silva, Krzysztof Kozlowski and Rob Herring for valuable patch review" * tag 'pwm/for-6.10-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/ukleinek/linux: (34 commits) pwm: pca9685: Drop explicit initialization of struct i2c_device_id::driver_data to 0 dt-bindings: pwm: snps,dw-apb-timers: Do not require pwm-cells twice dt-bindings: pwm: mediatek,pwm-disp: Do not require pwm-cells twice dt-bindings: pwm: mediatek,mt2712: Do not require pwm-cells twice dt-bindings: pwm: marvell,pxa: Do not require pwm-cells twice dt-bindings: pwm: google,cros-ec: Do not require pwm-cells twice dt-bindings: pwm: bcm2835: Do not require pwm-cells twice pwm: meson: Use mul_u64_u64_div_u64() for frequency calculating pwm: meson: Add check for error from clk_round_rate() pwm: meson: Drop unneeded check in .get_state() dt-bindings: pwm: mediatek,pwm-disp: add compatible for mt8365 SoC pwm: meson: Add generic compatible for meson8 to sm1 pwm: bcm2835: Drop open coded variant of devm_clk_rate_exclusive_get() pwm: bcm2835: Introduce a local variable for &pdev->dev pwm: stm32: Calculate prescaler with a division instead of a loop pwm: stm32: Fix for settings using period > UINT32_MAX pwm: stm32: Improve precision of calculation in .apply() pwm: stm32: Add error messages in .probe()'s error paths pwm: Make pwmchip_[sg]et_drvdata() a wrapper around dev_set_drvdata() pwm: Don't check pointer for being non-NULL after use ...
Diffstat (limited to 'drivers/pwm/core.c')
-rw-r--r--drivers/pwm/core.c604
1 files changed, 573 insertions, 31 deletions
diff --git a/drivers/pwm/core.c b/drivers/pwm/core.c
index 403525cc1783..18574857641e 100644
--- a/drivers/pwm/core.c
+++ b/drivers/pwm/core.c
@@ -343,9 +343,16 @@ static int pwm_device_request(struct pwm_device *pwm, const char *label)
if (!try_module_get(chip->owner))
return -ENODEV;
+ if (!get_device(&chip->dev)) {
+ err = -ENODEV;
+ goto err_get_device;
+ }
+
if (ops->request) {
err = ops->request(chip, pwm);
if (err) {
+ put_device(&chip->dev);
+err_get_device:
module_put(chip->owner);
return err;
}
@@ -454,36 +461,557 @@ of_pwm_single_xlate(struct pwm_chip *chip, const struct of_phandle_args *args)
}
EXPORT_SYMBOL_GPL(of_pwm_single_xlate);
+struct pwm_export {
+ struct device pwm_dev;
+ struct pwm_device *pwm;
+ struct mutex lock;
+ struct pwm_state suspend;
+};
+
+static inline struct pwm_chip *pwmchip_from_dev(struct device *pwmchip_dev)
+{
+ return container_of(pwmchip_dev, struct pwm_chip, dev);
+}
+
+static inline struct pwm_export *pwmexport_from_dev(struct device *pwm_dev)
+{
+ return container_of(pwm_dev, struct pwm_export, pwm_dev);
+}
+
+static inline struct pwm_device *pwm_from_dev(struct device *pwm_dev)
+{
+ struct pwm_export *export = pwmexport_from_dev(pwm_dev);
+
+ return export->pwm;
+}
+
+static ssize_t period_show(struct device *pwm_dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ const struct pwm_device *pwm = pwm_from_dev(pwm_dev);
+ struct pwm_state state;
+
+ pwm_get_state(pwm, &state);
+
+ return sysfs_emit(buf, "%llu\n", state.period);
+}
+
+static ssize_t period_store(struct device *pwm_dev,
+ struct device_attribute *attr,
+ const char *buf, size_t size)
+{
+ struct pwm_export *export = pwmexport_from_dev(pwm_dev);
+ struct pwm_device *pwm = export->pwm;
+ struct pwm_state state;
+ u64 val;
+ int ret;
+
+ ret = kstrtou64(buf, 0, &val);
+ if (ret)
+ return ret;
+
+ mutex_lock(&export->lock);
+ pwm_get_state(pwm, &state);
+ state.period = val;
+ ret = pwm_apply_might_sleep(pwm, &state);
+ mutex_unlock(&export->lock);
+
+ return ret ? : size;
+}
+
+static ssize_t duty_cycle_show(struct device *pwm_dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ const struct pwm_device *pwm = pwm_from_dev(pwm_dev);
+ struct pwm_state state;
+
+ pwm_get_state(pwm, &state);
+
+ return sysfs_emit(buf, "%llu\n", state.duty_cycle);
+}
+
+static ssize_t duty_cycle_store(struct device *pwm_dev,
+ struct device_attribute *attr,
+ const char *buf, size_t size)
+{
+ struct pwm_export *export = pwmexport_from_dev(pwm_dev);
+ struct pwm_device *pwm = export->pwm;
+ struct pwm_state state;
+ u64 val;
+ int ret;
+
+ ret = kstrtou64(buf, 0, &val);
+ if (ret)
+ return ret;
+
+ mutex_lock(&export->lock);
+ pwm_get_state(pwm, &state);
+ state.duty_cycle = val;
+ ret = pwm_apply_might_sleep(pwm, &state);
+ mutex_unlock(&export->lock);
+
+ return ret ? : size;
+}
+
+static ssize_t enable_show(struct device *pwm_dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ const struct pwm_device *pwm = pwm_from_dev(pwm_dev);
+ struct pwm_state state;
+
+ pwm_get_state(pwm, &state);
+
+ return sysfs_emit(buf, "%d\n", state.enabled);
+}
+
+static ssize_t enable_store(struct device *pwm_dev,
+ struct device_attribute *attr,
+ const char *buf, size_t size)
+{
+ struct pwm_export *export = pwmexport_from_dev(pwm_dev);
+ struct pwm_device *pwm = export->pwm;
+ struct pwm_state state;
+ int val, ret;
+
+ ret = kstrtoint(buf, 0, &val);
+ if (ret)
+ return ret;
+
+ mutex_lock(&export->lock);
+
+ pwm_get_state(pwm, &state);
+
+ switch (val) {
+ case 0:
+ state.enabled = false;
+ break;
+ case 1:
+ state.enabled = true;
+ break;
+ default:
+ ret = -EINVAL;
+ goto unlock;
+ }
+
+ ret = pwm_apply_might_sleep(pwm, &state);
+
+unlock:
+ mutex_unlock(&export->lock);
+ return ret ? : size;
+}
+
+static ssize_t polarity_show(struct device *pwm_dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ const struct pwm_device *pwm = pwm_from_dev(pwm_dev);
+ const char *polarity = "unknown";
+ struct pwm_state state;
+
+ pwm_get_state(pwm, &state);
+
+ switch (state.polarity) {
+ case PWM_POLARITY_NORMAL:
+ polarity = "normal";
+ break;
+
+ case PWM_POLARITY_INVERSED:
+ polarity = "inversed";
+ break;
+ }
+
+ return sysfs_emit(buf, "%s\n", polarity);
+}
+
+static ssize_t polarity_store(struct device *pwm_dev,
+ struct device_attribute *attr,
+ const char *buf, size_t size)
+{
+ struct pwm_export *export = pwmexport_from_dev(pwm_dev);
+ struct pwm_device *pwm = export->pwm;
+ enum pwm_polarity polarity;
+ struct pwm_state state;
+ int ret;
+
+ if (sysfs_streq(buf, "normal"))
+ polarity = PWM_POLARITY_NORMAL;
+ else if (sysfs_streq(buf, "inversed"))
+ polarity = PWM_POLARITY_INVERSED;
+ else
+ return -EINVAL;
+
+ mutex_lock(&export->lock);
+ pwm_get_state(pwm, &state);
+ state.polarity = polarity;
+ ret = pwm_apply_might_sleep(pwm, &state);
+ mutex_unlock(&export->lock);
+
+ return ret ? : size;
+}
+
+static ssize_t capture_show(struct device *pwm_dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct pwm_device *pwm = pwm_from_dev(pwm_dev);
+ struct pwm_capture result;
+ int ret;
+
+ ret = pwm_capture(pwm, &result, jiffies_to_msecs(HZ));
+ if (ret)
+ return ret;
+
+ return sysfs_emit(buf, "%u %u\n", result.period, result.duty_cycle);
+}
+
+static DEVICE_ATTR_RW(period);
+static DEVICE_ATTR_RW(duty_cycle);
+static DEVICE_ATTR_RW(enable);
+static DEVICE_ATTR_RW(polarity);
+static DEVICE_ATTR_RO(capture);
+
+static struct attribute *pwm_attrs[] = {
+ &dev_attr_period.attr,
+ &dev_attr_duty_cycle.attr,
+ &dev_attr_enable.attr,
+ &dev_attr_polarity.attr,
+ &dev_attr_capture.attr,
+ NULL
+};
+ATTRIBUTE_GROUPS(pwm);
+
+static void pwm_export_release(struct device *pwm_dev)
+{
+ struct pwm_export *export = pwmexport_from_dev(pwm_dev);
+
+ kfree(export);
+}
+
+static int pwm_export_child(struct device *pwmchip_dev, struct pwm_device *pwm)
+{
+ struct pwm_export *export;
+ char *pwm_prop[2];
+ int ret;
+
+ if (test_and_set_bit(PWMF_EXPORTED, &pwm->flags))
+ return -EBUSY;
+
+ export = kzalloc(sizeof(*export), GFP_KERNEL);
+ if (!export) {
+ clear_bit(PWMF_EXPORTED, &pwm->flags);
+ return -ENOMEM;
+ }
+
+ export->pwm = pwm;
+ mutex_init(&export->lock);
+
+ export->pwm_dev.release = pwm_export_release;
+ export->pwm_dev.parent = pwmchip_dev;
+ export->pwm_dev.devt = MKDEV(0, 0);
+ export->pwm_dev.groups = pwm_groups;
+ dev_set_name(&export->pwm_dev, "pwm%u", pwm->hwpwm);
+
+ ret = device_register(&export->pwm_dev);
+ if (ret) {
+ clear_bit(PWMF_EXPORTED, &pwm->flags);
+ put_device(&export->pwm_dev);
+ export = NULL;
+ return ret;
+ }
+ pwm_prop[0] = kasprintf(GFP_KERNEL, "EXPORT=pwm%u", pwm->hwpwm);
+ pwm_prop[1] = NULL;
+ kobject_uevent_env(&pwmchip_dev->kobj, KOBJ_CHANGE, pwm_prop);
+ kfree(pwm_prop[0]);
+
+ return 0;
+}
+
+static int pwm_unexport_match(struct device *pwm_dev, void *data)
+{
+ return pwm_from_dev(pwm_dev) == data;
+}
+
+static int pwm_unexport_child(struct device *pwmchip_dev, struct pwm_device *pwm)
+{
+ struct device *pwm_dev;
+ char *pwm_prop[2];
+
+ if (!test_and_clear_bit(PWMF_EXPORTED, &pwm->flags))
+ return -ENODEV;
+
+ pwm_dev = device_find_child(pwmchip_dev, pwm, pwm_unexport_match);
+ if (!pwm_dev)
+ return -ENODEV;
+
+ pwm_prop[0] = kasprintf(GFP_KERNEL, "UNEXPORT=pwm%u", pwm->hwpwm);
+ pwm_prop[1] = NULL;
+ kobject_uevent_env(&pwmchip_dev->kobj, KOBJ_CHANGE, pwm_prop);
+ kfree(pwm_prop[0]);
+
+ /* for device_find_child() */
+ put_device(pwm_dev);
+ device_unregister(pwm_dev);
+ pwm_put(pwm);
+
+ return 0;
+}
+
+static ssize_t export_store(struct device *pwmchip_dev,
+ struct device_attribute *attr,
+ const char *buf, size_t len)
+{
+ struct pwm_chip *chip = pwmchip_from_dev(pwmchip_dev);
+ struct pwm_device *pwm;
+ unsigned int hwpwm;
+ int ret;
+
+ ret = kstrtouint(buf, 0, &hwpwm);
+ if (ret < 0)
+ return ret;
+
+ if (hwpwm >= chip->npwm)
+ return -ENODEV;
+
+ pwm = pwm_request_from_chip(chip, hwpwm, "sysfs");
+ if (IS_ERR(pwm))
+ return PTR_ERR(pwm);
+
+ ret = pwm_export_child(pwmchip_dev, pwm);
+ if (ret < 0)
+ pwm_put(pwm);
+
+ return ret ? : len;
+}
+static DEVICE_ATTR_WO(export);
+
+static ssize_t unexport_store(struct device *pwmchip_dev,
+ struct device_attribute *attr,
+ const char *buf, size_t len)
+{
+ struct pwm_chip *chip = pwmchip_from_dev(pwmchip_dev);
+ unsigned int hwpwm;
+ int ret;
+
+ ret = kstrtouint(buf, 0, &hwpwm);
+ if (ret < 0)
+ return ret;
+
+ if (hwpwm >= chip->npwm)
+ return -ENODEV;
+
+ ret = pwm_unexport_child(pwmchip_dev, &chip->pwms[hwpwm]);
+
+ return ret ? : len;
+}
+static DEVICE_ATTR_WO(unexport);
+
+static ssize_t npwm_show(struct device *pwmchip_dev, struct device_attribute *attr,
+ char *buf)
+{
+ const struct pwm_chip *chip = pwmchip_from_dev(pwmchip_dev);
+
+ return sysfs_emit(buf, "%u\n", chip->npwm);
+}
+static DEVICE_ATTR_RO(npwm);
+
+static struct attribute *pwm_chip_attrs[] = {
+ &dev_attr_export.attr,
+ &dev_attr_unexport.attr,
+ &dev_attr_npwm.attr,
+ NULL,
+};
+ATTRIBUTE_GROUPS(pwm_chip);
+
+/* takes export->lock on success */
+static struct pwm_export *pwm_class_get_state(struct device *pwmchip_dev,
+ struct pwm_device *pwm,
+ struct pwm_state *state)
+{
+ struct device *pwm_dev;
+ struct pwm_export *export;
+
+ if (!test_bit(PWMF_EXPORTED, &pwm->flags))
+ return NULL;
+
+ pwm_dev = device_find_child(pwmchip_dev, pwm, pwm_unexport_match);
+ if (!pwm_dev)
+ return NULL;
+
+ export = pwmexport_from_dev(pwm_dev);
+ put_device(pwm_dev); /* 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_might_sleep(pwm, state);
+
+ /* release lock taken in pwm_class_get_state */
+ mutex_unlock(&export->lock);
+
+ return ret;
+}
+
+static int pwm_class_resume_npwm(struct device *pwmchip_dev, unsigned int npwm)
+{
+ struct pwm_chip *chip = pwmchip_from_dev(pwmchip_dev);
+ 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(pwmchip_dev, pwm, &state);
+ if (!export)
+ continue;
+
+ /* If pwmchip was not enabled before suspend, do nothing. */
+ if (!export->suspend.enabled) {
+ /* release lock taken in pwm_class_get_state */
+ mutex_unlock(&export->lock);
+ continue;
+ }
+
+ state.enabled = export->suspend.enabled;
+ ret = pwm_class_apply_state(export, pwm, &state);
+ if (ret < 0)
+ break;
+ }
+
+ return ret;
+}
+
+static int pwm_class_suspend(struct device *pwmchip_dev)
+{
+ struct pwm_chip *chip = pwmchip_from_dev(pwmchip_dev);
+ 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(pwmchip_dev, pwm, &state);
+ if (!export)
+ continue;
+
+ /*
+ * If pwmchip was not enabled before suspend, save
+ * state for resume time and do nothing else.
+ */
+ export->suspend = state;
+ if (!state.enabled) {
+ /* release lock taken in pwm_class_get_state */
+ mutex_unlock(&export->lock);
+ continue;
+ }
+
+ 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(pwmchip_dev, i);
+ break;
+ }
+ }
+
+ return ret;
+}
+
+static int pwm_class_resume(struct device *pwmchip_dev)
+{
+ struct pwm_chip *chip = pwmchip_from_dev(pwmchip_dev);
+
+ return pwm_class_resume_npwm(pwmchip_dev, chip->npwm);
+}
+
+static DEFINE_SIMPLE_DEV_PM_OPS(pwm_class_pm_ops, pwm_class_suspend, pwm_class_resume);
+
+static struct class pwm_class = {
+ .name = "pwm",
+ .dev_groups = pwm_chip_groups,
+ .pm = pm_sleep_ptr(&pwm_class_pm_ops),
+};
+
+static void pwmchip_sysfs_unexport(struct pwm_chip *chip)
+{
+ unsigned int i;
+
+ for (i = 0; i < chip->npwm; i++) {
+ struct pwm_device *pwm = &chip->pwms[i];
+
+ if (test_bit(PWMF_EXPORTED, &pwm->flags))
+ pwm_unexport_child(&chip->dev, pwm);
+ }
+}
+
#define PWMCHIP_ALIGN ARCH_DMA_MINALIGN
static void *pwmchip_priv(struct pwm_chip *chip)
{
- return (void *)chip + ALIGN(sizeof(*chip), PWMCHIP_ALIGN);
+ return (void *)chip + ALIGN(struct_size(chip, pwms, chip->npwm), PWMCHIP_ALIGN);
}
/* This is the counterpart to pwmchip_alloc() */
void pwmchip_put(struct pwm_chip *chip)
{
- kfree(chip);
+ put_device(&chip->dev);
}
EXPORT_SYMBOL_GPL(pwmchip_put);
+static void pwmchip_release(struct device *pwmchip_dev)
+{
+ struct pwm_chip *chip = pwmchip_from_dev(pwmchip_dev);
+
+ kfree(chip);
+}
+
struct pwm_chip *pwmchip_alloc(struct device *parent, unsigned int npwm, size_t sizeof_priv)
{
struct pwm_chip *chip;
+ struct device *pwmchip_dev;
size_t alloc_size;
+ unsigned int i;
- alloc_size = size_add(ALIGN(sizeof(*chip), PWMCHIP_ALIGN), sizeof_priv);
+ alloc_size = size_add(ALIGN(struct_size(chip, pwms, npwm), PWMCHIP_ALIGN),
+ sizeof_priv);
chip = kzalloc(alloc_size, GFP_KERNEL);
if (!chip)
return ERR_PTR(-ENOMEM);
- chip->dev = parent;
chip->npwm = npwm;
+ chip->uses_pwmchip_alloc = true;
+
+ pwmchip_dev = &chip->dev;
+ device_initialize(pwmchip_dev);
+ pwmchip_dev->class = &pwm_class;
+ pwmchip_dev->parent = parent;
+ pwmchip_dev->release = pwmchip_release;
pwmchip_set_drvdata(chip, pwmchip_priv(chip));
+ for (i = 0; i < chip->npwm; i++) {
+ struct pwm_device *pwm = &chip->pwms[i];
+ pwm->chip = chip;
+ pwm->hwpwm = i;
+ }
+
return chip;
}
EXPORT_SYMBOL_GPL(pwmchip_alloc);
@@ -555,47 +1083,56 @@ static bool pwm_ops_check(const struct pwm_chip *chip)
*/
int __pwmchip_add(struct pwm_chip *chip, struct module *owner)
{
- unsigned int i;
int ret;
if (!chip || !pwmchip_parent(chip) || !chip->ops || !chip->npwm)
return -EINVAL;
+ /*
+ * a struct pwm_chip must be allocated using (devm_)pwmchip_alloc,
+ * otherwise the embedded struct device might disappear too early
+ * resulting in memory corruption.
+ * Catch drivers that were not converted appropriately.
+ */
+ if (!chip->uses_pwmchip_alloc)
+ return -EINVAL;
+
if (!pwm_ops_check(chip))
return -EINVAL;
chip->owner = owner;
- chip->pwms = kcalloc(chip->npwm, sizeof(*chip->pwms), GFP_KERNEL);
- if (!chip->pwms)
- return -ENOMEM;
-
mutex_lock(&pwm_lock);
ret = idr_alloc(&pwm_chips, chip, 0, 0, GFP_KERNEL);
- if (ret < 0) {
- mutex_unlock(&pwm_lock);
- kfree(chip->pwms);
- return ret;
- }
+ if (ret < 0)
+ goto err_idr_alloc;
chip->id = ret;
- for (i = 0; i < chip->npwm; i++) {
- struct pwm_device *pwm = &chip->pwms[i];
+ dev_set_name(&chip->dev, "pwmchip%u", chip->id);
- pwm->chip = chip;
- pwm->hwpwm = i;
- }
+ if (IS_ENABLED(CONFIG_OF))
+ of_pwmchip_add(chip);
+
+ ret = device_add(&chip->dev);
+ if (ret)
+ goto err_device_add;
mutex_unlock(&pwm_lock);
+ return 0;
+
+err_device_add:
if (IS_ENABLED(CONFIG_OF))
- of_pwmchip_add(chip);
+ of_pwmchip_remove(chip);
- pwmchip_sysfs_export(chip);
+ idr_remove(&pwm_chips, chip->id);
+err_idr_alloc:
- return 0;
+ mutex_unlock(&pwm_lock);
+
+ return ret;
}
EXPORT_SYMBOL_GPL(__pwmchip_add);
@@ -618,7 +1155,7 @@ void pwmchip_remove(struct pwm_chip *chip)
mutex_unlock(&pwm_lock);
- kfree(chip->pwms);
+ device_del(&chip->dev);
}
EXPORT_SYMBOL_GPL(pwmchip_remove);
@@ -988,9 +1525,13 @@ EXPORT_SYMBOL_GPL(pwm_get);
*/
void pwm_put(struct pwm_device *pwm)
{
+ struct pwm_chip *chip;
+
if (!pwm)
return;
+ chip = pwm->chip;
+
mutex_lock(&pwm_lock);
if (!test_and_clear_bit(PWMF_REQUESTED, &pwm->flags)) {
@@ -998,12 +1539,14 @@ void pwm_put(struct pwm_device *pwm)
goto out;
}
- if (pwm->chip->ops->free)
+ if (chip->ops->free)
pwm->chip->ops->free(pwm->chip, pwm);
pwm->label = NULL;
- module_put(pwm->chip->owner);
+ put_device(&chip->dev);
+
+ module_put(chip->owner);
out:
mutex_unlock(&pwm_lock);
}
@@ -1076,7 +1619,6 @@ struct pwm_device *devm_fwnode_pwm_get(struct device *dev,
}
EXPORT_SYMBOL_GPL(devm_fwnode_pwm_get);
-#ifdef CONFIG_DEBUG_FS
static void pwm_dbg_show(struct pwm_chip *chip, struct seq_file *s)
{
unsigned int i;
@@ -1161,11 +1703,11 @@ static const struct seq_operations pwm_debugfs_sops = {
DEFINE_SEQ_ATTRIBUTE(pwm_debugfs);
-static int __init pwm_debugfs_init(void)
+static int __init pwm_init(void)
{
- debugfs_create_file("pwm", 0444, NULL, NULL, &pwm_debugfs_fops);
+ if (IS_ENABLED(CONFIG_DEBUG_FS))
+ debugfs_create_file("pwm", 0444, NULL, NULL, &pwm_debugfs_fops);
- return 0;
+ return class_register(&pwm_class);
}
-subsys_initcall(pwm_debugfs_init);
-#endif /* CONFIG_DEBUG_FS */
+subsys_initcall(pwm_init);