From 05947224ff469bf17b3791fd009bc27ce5151997 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Wed, 14 Feb 2024 10:33:28 +0100 Subject: pwm: Ensure that pwm_chips are allocated using pwmchip_alloc() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Memory holding a struct device must not be freed before the reference count drops to zero. So a struct pwm_chip must not live in memory freed by a driver on unbind. All in-tree drivers were fixed accordingly, but as out-of-tree drivers, that were not adapted, still compile fine, catch these in pwmchip_add(). Link: https://lore.kernel.org/r/35f5b229c98f78b2f6ce2397259a4a936be477c0.1707900770.git.u.kleine-koenig@pengutronix.de Signed-off-by: Uwe Kleine-König --- include/linux/pwm.h | 2 ++ 1 file changed, 2 insertions(+) (limited to 'include/linux/pwm.h') diff --git a/include/linux/pwm.h b/include/linux/pwm.h index 4a6568dfdf3f..94a642a88817 100644 --- a/include/linux/pwm.h +++ b/include/linux/pwm.h @@ -272,6 +272,7 @@ struct pwm_ops { * @npwm: number of PWMs controlled by this chip * @of_xlate: request a PWM device given a device tree PWM specifier * @atomic: can the driver's ->apply() be called in atomic context + * @uses_pwmchip_alloc: signals if pwmchip_allow was used to allocate this chip * @driver_data: Private pointer for driver specific info * @pwms: array of PWM devices allocated by the framework */ @@ -287,6 +288,7 @@ struct pwm_chip { bool atomic; /* only used internally by the PWM framework */ + bool uses_pwmchip_alloc; void *driver_data; struct pwm_device *pwms; }; -- cgit v1.2.3-59-g8ed1b From e9cc807f87ffd1ccc919731e8f624982935af3e0 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Sun, 17 Mar 2024 11:40:34 +0100 Subject: pwm: Move contents of sysfs.c into core.c MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit With the upcoming restructuring having all in a single file simplifies things a bit. The relevant and somewhat visible changes are: - Some dropped prototypes from include/linux/pwm.h that were only necessary that core.c has a declaration of the symbols defined in sysfs.c. The respective functions are static now. - The pwm class now also exists if CONFIG_SYSFS isn't enabled. Having CONFIG_SYSFS is not very relevant today, but even without it the class and device stuff still provides lifetime tracking. - Both files had an initcall, these are merged into a single one now. Instead of a big #ifdef block for CONFIG_DEBUG_FS, a single if (IS_ENABLED(CONFIG_DEBUG_FS)) is used now. This increases compile coverage a bit and is a tad nicer on the eyes. Link: https://lore.kernel.org/r/9e2d39a5280d7dda5bfc6682a8aef510148635b2.1710670958.git.u.kleine-koenig@pengutronix.de Signed-off-by: Uwe Kleine-König --- drivers/pwm/Kconfig | 4 - drivers/pwm/Makefile | 1 - drivers/pwm/core.c | 540 +++++++++++++++++++++++++++++++++++++++++++++++++- drivers/pwm/sysfs.c | 550 --------------------------------------------------- include/linux/pwm.h | 13 -- 5 files changed, 534 insertions(+), 574 deletions(-) delete mode 100644 drivers/pwm/sysfs.c (limited to 'include/linux/pwm.h') diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig index 4b956d661755..1dd7921194f5 100644 --- a/drivers/pwm/Kconfig +++ b/drivers/pwm/Kconfig @@ -29,10 +29,6 @@ menuconfig PWM if PWM -config PWM_SYSFS - bool - default y if SYSFS - config PWM_DEBUG bool "PWM lowlevel drivers additional checks and debug messages" depends on DEBUG_KERNEL diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile index c5ec9e168ee7..90913519f11a 100644 --- a/drivers/pwm/Makefile +++ b/drivers/pwm/Makefile @@ -1,6 +1,5 @@ # SPDX-License-Identifier: GPL-2.0 obj-$(CONFIG_PWM) += core.o -obj-$(CONFIG_PWM_SYSFS) += sysfs.o obj-$(CONFIG_PWM_AB8500) += pwm-ab8500.o obj-$(CONFIG_PWM_APPLE) += pwm-apple.o obj-$(CONFIG_PWM_ATMEL) += pwm-atmel.o diff --git a/drivers/pwm/core.c b/drivers/pwm/core.c index d3f475eca97f..faa0f885e057 100644 --- a/drivers/pwm/core.c +++ b/drivers/pwm/core.c @@ -454,6 +454,535 @@ 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 dev_get_drvdata(pwmchip_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 int pwmchip_sysfs_match(struct device *pwmchip_dev, const void *data) +{ + return pwmchip_from_dev(pwmchip_dev) == data; +} + +static void pwmchip_sysfs_export(struct pwm_chip *chip) +{ + struct device *pwmchip_dev; + + /* + * If device_create() fails the pwm_chip is still usable by + * the kernel it's just not exported. + */ + pwmchip_dev = device_create(&pwm_class, pwmchip_parent(chip), MKDEV(0, 0), chip, + "pwmchip%d", chip->id); + if (IS_ERR(pwmchip_dev)) { + dev_warn(pwmchip_parent(chip), + "device_create failed for pwm_chip sysfs export\n"); + } +} + +static void pwmchip_sysfs_unexport(struct pwm_chip *chip) +{ + struct device *pwmchip_dev; + unsigned int i; + + pwmchip_dev = class_find_device(&pwm_class, NULL, chip, + pwmchip_sysfs_match); + if (!pwmchip_dev) + return; + + for (i = 0; i < chip->npwm; i++) { + struct pwm_device *pwm = &chip->pwms[i]; + + if (test_bit(PWMF_EXPORTED, &pwm->flags)) + pwm_unexport_child(pwmchip_dev, pwm); + } + + put_device(pwmchip_dev); + device_unregister(pwmchip_dev); +} + #define PWMCHIP_ALIGN ARCH_DMA_MINALIGN static void *pwmchip_priv(struct pwm_chip *chip) @@ -1086,7 +1615,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; @@ -1171,11 +1699,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); diff --git a/drivers/pwm/sysfs.c b/drivers/pwm/sysfs.c deleted file mode 100644 index 1a1b1f6dd98f..000000000000 --- a/drivers/pwm/sysfs.c +++ /dev/null @@ -1,550 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * A simple sysfs interface for the generic PWM framework - * - * Copyright (C) 2013 H Hartley Sweeten - * - * Based on previous work by Lars Poeschel - */ - -#include -#include -#include -#include -#include -#include - -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 dev_get_drvdata(pwmchip_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 int pwmchip_sysfs_match(struct device *pwmchip_dev, const void *data) -{ - return pwmchip_from_dev(pwmchip_dev) == data; -} - -void pwmchip_sysfs_export(struct pwm_chip *chip) -{ - struct device *pwmchip_dev; - - /* - * If device_create() fails the pwm_chip is still usable by - * the kernel it's just not exported. - */ - pwmchip_dev = device_create(&pwm_class, pwmchip_parent(chip), MKDEV(0, 0), chip, - "pwmchip%d", chip->id); - if (IS_ERR(pwmchip_dev)) { - dev_warn(pwmchip_parent(chip), - "device_create failed for pwm_chip sysfs export\n"); - } -} - -void pwmchip_sysfs_unexport(struct pwm_chip *chip) -{ - struct device *pwmchip_dev; - unsigned int i; - - pwmchip_dev = class_find_device(&pwm_class, NULL, chip, - pwmchip_sysfs_match); - if (!pwmchip_dev) - return; - - for (i = 0; i < chip->npwm; i++) { - struct pwm_device *pwm = &chip->pwms[i]; - - if (test_bit(PWMF_EXPORTED, &pwm->flags)) - pwm_unexport_child(pwmchip_dev, pwm); - } - - put_device(pwmchip_dev); - device_unregister(pwmchip_dev); -} - -static int __init pwm_sysfs_init(void) -{ - return class_register(&pwm_class); -} -subsys_initcall(pwm_sysfs_init); diff --git a/include/linux/pwm.h b/include/linux/pwm.h index 94a642a88817..17e45d8413ed 100644 --- a/include/linux/pwm.h +++ b/include/linux/pwm.h @@ -630,17 +630,4 @@ static inline void pwm_remove_table(struct pwm_lookup *table, size_t num) } #endif -#ifdef CONFIG_PWM_SYSFS -void pwmchip_sysfs_export(struct pwm_chip *chip); -void pwmchip_sysfs_unexport(struct pwm_chip *chip); -#else -static inline void pwmchip_sysfs_export(struct pwm_chip *chip) -{ -} - -static inline void pwmchip_sysfs_unexport(struct pwm_chip *chip) -{ -} -#endif /* CONFIG_PWM_SYSFS */ - #endif /* __LINUX_PWM_H */ -- cgit v1.2.3-59-g8ed1b From ee37bf50749f06b29394d7ba8a85b47f023b61e2 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Sun, 17 Mar 2024 11:40:35 +0100 Subject: pwm: Ensure a struct pwm has the same lifetime as its pwm_chip MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It's required to not free the memory underlying a requested PWM while a consumer still has a reference to it. While currently a pwm_chip doesn't live long enough in all cases, linking the struct pwm to the pwm_chip results in the right lifetime as soon as the pwmchip is living long enough. This happens with the following commits. Note this is a breaking change for all pwm drivers that don't use pwmchip_alloc(). Reviewed-by: Gustavo A. R. Silva # for struct_size() and __counted_by() Link: https://lore.kernel.org/r/7e9e958841f049026c0023b309cc9deecf0ab61d.1710670958.git.u.kleine-koenig@pengutronix.de Signed-off-by: Uwe Kleine-König --- drivers/pwm/core.c | 27 ++++++++++----------------- include/linux/pwm.h | 2 +- 2 files changed, 11 insertions(+), 18 deletions(-) (limited to 'include/linux/pwm.h') diff --git a/drivers/pwm/core.c b/drivers/pwm/core.c index faa0f885e057..19386452e6c8 100644 --- a/drivers/pwm/core.c +++ b/drivers/pwm/core.c @@ -987,7 +987,7 @@ static void pwmchip_sysfs_unexport(struct pwm_chip *chip) 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() */ @@ -1001,8 +1001,10 @@ struct pwm_chip *pwmchip_alloc(struct device *parent, unsigned int npwm, size_t { struct pwm_chip *chip; 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) @@ -1014,6 +1016,12 @@ struct pwm_chip *pwmchip_alloc(struct device *parent, unsigned int npwm, size_t 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); @@ -1085,7 +1093,6 @@ 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) @@ -1105,28 +1112,16 @@ int __pwmchip_add(struct pwm_chip *chip, struct module *owner) 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; } chip->id = ret; - for (i = 0; i < chip->npwm; i++) { - struct pwm_device *pwm = &chip->pwms[i]; - - pwm->chip = chip; - pwm->hwpwm = i; - } - mutex_unlock(&pwm_lock); if (IS_ENABLED(CONFIG_OF)) @@ -1156,8 +1151,6 @@ void pwmchip_remove(struct pwm_chip *chip) idr_remove(&pwm_chips, chip->id); mutex_unlock(&pwm_lock); - - kfree(chip->pwms); } EXPORT_SYMBOL_GPL(pwmchip_remove); diff --git a/include/linux/pwm.h b/include/linux/pwm.h index 17e45d8413ed..78b9061572ff 100644 --- a/include/linux/pwm.h +++ b/include/linux/pwm.h @@ -290,7 +290,7 @@ struct pwm_chip { /* only used internally by the PWM framework */ bool uses_pwmchip_alloc; void *driver_data; - struct pwm_device *pwms; + struct pwm_device pwms[] __counted_by(npwm); }; static inline struct device *pwmchip_parent(const struct pwm_chip *chip) -- cgit v1.2.3-59-g8ed1b From 4c56b1434b814899c42a9d9f43d8265371282cd0 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Sun, 17 Mar 2024 11:40:36 +0100 Subject: pwm: Add a struct device to struct pwm_chip MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This replaces the formerly dynamically allocated struct device. This allows to additionally use it to track the lifetime of the struct pwm_chip. Otherwise the new struct device provides the same sysfs API as was provided by the dynamic device before. Link: https://lore.kernel.org/r/35c65ea7f6de789a568ff39d7b6b4ce80de4b7dc.1710670958.git.u.kleine-koenig@pengutronix.de Signed-off-by: Uwe Kleine-König --- drivers/pwm/core.c | 93 +++++++++++++++++++++++++++++------------------------ include/linux/pwm.h | 5 +-- 2 files changed, 54 insertions(+), 44 deletions(-) (limited to 'include/linux/pwm.h') diff --git a/drivers/pwm/core.c b/drivers/pwm/core.c index 19386452e6c8..32871687c0a4 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; } @@ -463,7 +470,7 @@ struct pwm_export { static inline struct pwm_chip *pwmchip_from_dev(struct device *pwmchip_dev) { - return dev_get_drvdata(pwmchip_dev); + return container_of(pwmchip_dev, struct pwm_chip, dev); } static inline struct pwm_export *pwmexport_from_dev(struct device *pwm_dev) @@ -941,46 +948,16 @@ static struct class pwm_class = { .pm = pm_sleep_ptr(&pwm_class_pm_ops), }; -static int pwmchip_sysfs_match(struct device *pwmchip_dev, const void *data) -{ - return pwmchip_from_dev(pwmchip_dev) == data; -} - -static void pwmchip_sysfs_export(struct pwm_chip *chip) -{ - struct device *pwmchip_dev; - - /* - * If device_create() fails the pwm_chip is still usable by - * the kernel it's just not exported. - */ - pwmchip_dev = device_create(&pwm_class, pwmchip_parent(chip), MKDEV(0, 0), chip, - "pwmchip%d", chip->id); - if (IS_ERR(pwmchip_dev)) { - dev_warn(pwmchip_parent(chip), - "device_create failed for pwm_chip sysfs export\n"); - } -} - static void pwmchip_sysfs_unexport(struct pwm_chip *chip) { - struct device *pwmchip_dev; unsigned int i; - pwmchip_dev = class_find_device(&pwm_class, NULL, chip, - pwmchip_sysfs_match); - if (!pwmchip_dev) - return; - for (i = 0; i < chip->npwm; i++) { struct pwm_device *pwm = &chip->pwms[i]; if (test_bit(PWMF_EXPORTED, &pwm->flags)) - pwm_unexport_child(pwmchip_dev, pwm); + pwm_unexport_child(&chip->dev, pwm); } - - put_device(pwmchip_dev); - device_unregister(pwmchip_dev); } #define PWMCHIP_ALIGN ARCH_DMA_MINALIGN @@ -993,13 +970,21 @@ static void *pwmchip_priv(struct pwm_chip *chip) /* 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; @@ -1010,10 +995,15 @@ struct pwm_chip *pwmchip_alloc(struct device *parent, unsigned int npwm, size_t 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++) { @@ -1115,21 +1105,34 @@ int __pwmchip_add(struct pwm_chip *chip, struct module *owner) mutex_lock(&pwm_lock); ret = idr_alloc(&pwm_chips, chip, 0, 0, GFP_KERNEL); - if (ret < 0) { - mutex_unlock(&pwm_lock); - return ret; - } + if (ret < 0) + goto err_idr_alloc; chip->id = ret; - mutex_unlock(&pwm_lock); + dev_set_name(&chip->dev, "pwmchip%u", chip->id); if (IS_ENABLED(CONFIG_OF)) of_pwmchip_add(chip); - pwmchip_sysfs_export(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_remove(chip); + + idr_remove(&pwm_chips, chip->id); +err_idr_alloc: + + mutex_unlock(&pwm_lock); + + return ret; } EXPORT_SYMBOL_GPL(__pwmchip_add); @@ -1151,6 +1154,8 @@ void pwmchip_remove(struct pwm_chip *chip) idr_remove(&pwm_chips, chip->id); mutex_unlock(&pwm_lock); + + device_del(&chip->dev); } EXPORT_SYMBOL_GPL(pwmchip_remove); @@ -1520,6 +1525,8 @@ EXPORT_SYMBOL_GPL(pwm_get); */ void pwm_put(struct pwm_device *pwm) { + struct pwm_chip *chip = pwm->chip; + if (!pwm) return; @@ -1530,12 +1537,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); } diff --git a/include/linux/pwm.h b/include/linux/pwm.h index 78b9061572ff..495e23761f34 100644 --- a/include/linux/pwm.h +++ b/include/linux/pwm.h @@ -2,6 +2,7 @@ #ifndef __LINUX_PWM_H #define __LINUX_PWM_H +#include #include #include #include @@ -277,7 +278,7 @@ struct pwm_ops { * @pwms: array of PWM devices allocated by the framework */ struct pwm_chip { - struct device *dev; + struct device dev; const struct pwm_ops *ops; struct module *owner; unsigned int id; @@ -295,7 +296,7 @@ struct pwm_chip { static inline struct device *pwmchip_parent(const struct pwm_chip *chip) { - return chip->dev; + return chip->dev.parent; } static inline void *pwmchip_get_drvdata(struct pwm_chip *chip) -- cgit v1.2.3-59-g8ed1b From 38ae7142e35165571123997addd71393a9dabde5 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Sun, 17 Mar 2024 11:40:37 +0100 Subject: pwm: Make pwmchip_[sg]et_drvdata() a wrapper around dev_set_drvdata() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Now that a pwm_chip has a dedicated struct device, pwmchip_set_drvdata() and pwmchip_get_drvdata() can be made thin wrappers around dev_set_drvdata() and dev_get_drvdata() respectively and the previously needed pointer can be dropped from struct pwm_chip. Link: https://lore.kernel.org/r/a5e05bd2d83421a26fdef6a87d69253c0f98becf.1710670958.git.u.kleine-koenig@pengutronix.de Signed-off-by: Uwe Kleine-König --- include/linux/pwm.h | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) (limited to 'include/linux/pwm.h') diff --git a/include/linux/pwm.h b/include/linux/pwm.h index 495e23761f34..60b92c2c75ef 100644 --- a/include/linux/pwm.h +++ b/include/linux/pwm.h @@ -274,7 +274,6 @@ struct pwm_ops { * @of_xlate: request a PWM device given a device tree PWM specifier * @atomic: can the driver's ->apply() be called in atomic context * @uses_pwmchip_alloc: signals if pwmchip_allow was used to allocate this chip - * @driver_data: Private pointer for driver specific info * @pwms: array of PWM devices allocated by the framework */ struct pwm_chip { @@ -290,7 +289,6 @@ struct pwm_chip { /* only used internally by the PWM framework */ bool uses_pwmchip_alloc; - void *driver_data; struct pwm_device pwms[] __counted_by(npwm); }; @@ -301,20 +299,12 @@ static inline struct device *pwmchip_parent(const struct pwm_chip *chip) static inline void *pwmchip_get_drvdata(struct pwm_chip *chip) { - /* - * After pwm_chip got a dedicated struct device, this can be replaced by - * dev_get_drvdata(&chip->dev); - */ - return chip->driver_data; + return dev_get_drvdata(&chip->dev); } static inline void pwmchip_set_drvdata(struct pwm_chip *chip, void *data) { - /* - * After pwm_chip got a dedicated struct device, this can be replaced by - * dev_set_drvdata(&chip->dev, data); - */ - chip->driver_data = data; + dev_set_drvdata(&chip->dev, data); } #if IS_ENABLED(CONFIG_PWM) -- cgit v1.2.3-59-g8ed1b