@@ -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
@@ -1,6 +1,5 @@
# SPDX-License-Identifier: GPL-2.0
-obj-$(CONFIG_PWM) += core.o
-obj-$(CONFIG_PWM_SYSFS) += sysfs.o
+obj-$(CONFIG_PWM) += core.o sysfs.o
obj-$(CONFIG_PWM_AB8500) += pwm-ab8500.o
obj-$(CONFIG_PWM_APPLE) += pwm-apple.o
obj-$(CONFIG_PWM_ATMEL) += pwm-atmel.o
@@ -340,13 +340,13 @@ static int pwm_device_request(struct pwm_device *pwm, const char *label)
if (test_bit(PWMF_REQUESTED, &pwm->flags))
return -EBUSY;
- if (!try_module_get(chip->owner))
+ if (!get_device(&chip->dev))
return -ENODEV;
if (ops->request) {
err = ops->request(chip, pwm);
if (err) {
- module_put(chip->owner);
+ put_device(&chip->dev);
return err;
}
}
@@ -464,13 +464,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 *dev)
+{
+ struct pwm_chip *chip = container_of(dev, struct pwm_chip, dev);
+
+ kfree(chip);
+}
+
struct pwm_chip *pwmchip_alloc(struct device *parent, unsigned int npwm, size_t sizeof_priv)
{
struct pwm_chip *chip;
+ struct device *dev;
size_t alloc_size;
unsigned int i;
@@ -480,7 +488,13 @@ struct pwm_chip *pwmchip_alloc(struct device *parent, unsigned int npwm, size_t
if (!chip)
return ERR_PTR(-ENOMEM);
- chip->dev = parent;
+ dev = &chip->dev;
+
+ device_initialize(dev);
+ dev->class = &pwm_class;
+ dev->parent = parent;
+ dev->release = pwmchip_release;
+
chip->npwm = npwm;
chip->uses_pwmchip_alloc = true;
@@ -582,25 +596,42 @@ int __pwmchip_add(struct pwm_chip *chip, struct module *owner)
return -EINVAL;
chip->owner = owner;
+ if (!try_module_get(owner))
+ return -EINVAL;
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);
+ module_put(owner);
+
+ return ret;
}
EXPORT_SYMBOL_GPL(__pwmchip_add);
@@ -622,6 +653,10 @@ void pwmchip_remove(struct pwm_chip *chip)
idr_remove(&pwm_chips, chip->id);
mutex_unlock(&pwm_lock);
+
+ module_put(chip->owner);
+
+ device_del(&chip->dev);
}
EXPORT_SYMBOL_GPL(pwmchip_remove);
@@ -1006,7 +1041,7 @@ void pwm_put(struct pwm_device *pwm)
pwm->label = NULL;
- module_put(pwm->chip->owner);
+ put_device(&pwm->chip->dev);
out:
mutex_unlock(&pwm_lock);
}
@@ -311,7 +311,7 @@ static ssize_t export_store(struct device *parent,
struct device_attribute *attr,
const char *buf, size_t len)
{
- struct pwm_chip *chip = dev_get_drvdata(parent);
+ struct pwm_chip *chip = container_of(parent, struct pwm_chip, dev);
struct pwm_device *pwm;
unsigned int hwpwm;
int ret;
@@ -339,7 +339,7 @@ static ssize_t unexport_store(struct device *parent,
struct device_attribute *attr,
const char *buf, size_t len)
{
- struct pwm_chip *chip = dev_get_drvdata(parent);
+ struct pwm_chip *chip = container_of(parent, struct pwm_chip, dev);
unsigned int hwpwm;
int ret;
@@ -359,7 +359,7 @@ static DEVICE_ATTR_WO(unexport);
static ssize_t npwm_show(struct device *parent, struct device_attribute *attr,
char *buf)
{
- const struct pwm_chip *chip = dev_get_drvdata(parent);
+ const struct pwm_chip *chip = container_of(parent, struct pwm_chip, dev);
return sysfs_emit(buf, "%u\n", chip->npwm);
}
@@ -411,7 +411,7 @@ static int pwm_class_apply_state(struct pwm_export *export,
static int pwm_class_resume_npwm(struct device *parent, unsigned int npwm)
{
- struct pwm_chip *chip = dev_get_drvdata(parent);
+ struct pwm_chip *chip = container_of(parent, struct pwm_chip, dev);
unsigned int i;
int ret = 0;
@@ -442,7 +442,7 @@ static int pwm_class_resume_npwm(struct device *parent, unsigned int npwm)
static int pwm_class_suspend(struct device *parent)
{
- struct pwm_chip *chip = dev_get_drvdata(parent);
+ struct pwm_chip *chip = container_of(parent, struct pwm_chip, dev);
unsigned int i;
int ret = 0;
@@ -483,59 +483,30 @@ static int pwm_class_suspend(struct device *parent)
static int pwm_class_resume(struct device *parent)
{
- struct pwm_chip *chip = dev_get_drvdata(parent);
+ struct pwm_chip *chip = container_of(parent, struct pwm_chip, dev);
return pwm_class_resume_npwm(parent, chip->npwm);
}
static DEFINE_SIMPLE_DEV_PM_OPS(pwm_class_pm_ops, pwm_class_suspend, pwm_class_resume);
-static struct class pwm_class = {
+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 *parent, const void *data)
-{
- return dev_get_drvdata(parent) == data;
-}
-
-void pwmchip_sysfs_export(struct pwm_chip *chip)
-{
- struct device *parent;
-
- /*
- * If device_create() fails the pwm_chip is still usable by
- * the kernel it's just not exported.
- */
- parent = device_create(&pwm_class, pwmchip_parent(chip), MKDEV(0, 0), chip,
- "pwmchip%d", chip->id);
- if (IS_ERR(parent)) {
- dev_warn(pwmchip_parent(chip),
- "device_create failed for pwm_chip sysfs export\n");
- }
-}
-
void pwmchip_sysfs_unexport(struct pwm_chip *chip)
{
- struct device *parent;
+ struct device *parent = &chip->dev;
unsigned int i;
- parent = class_find_device(&pwm_class, NULL, chip,
- pwmchip_sysfs_match);
- if (!parent)
- 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(parent, pwm);
}
-
- put_device(parent);
- device_unregister(parent);
}
static int __init pwm_sysfs_init(void)
@@ -2,6 +2,7 @@
#ifndef __LINUX_PWM_H
#define __LINUX_PWM_H
+#include <linux/device.h>
#include <linux/err.h>
#include <linux/mutex.h>
#include <linux/of.h>
@@ -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)
@@ -630,17 +631,7 @@ 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);
+extern struct class pwm_class;
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 */
The memory allocated to hold a struct pwm_chip is tied now to the struct device that is added to struct pwm_chip. This way it's only freed when the last reference is dropped. Currently this isn't required yet as device links make sure that a consumer is gone before the PWM chip goes away. However there are plans to introduce character device support for PWM chips and with that in place it can happen that a reference to a pwm_chip is held with its driver going away. Signed-off-by: Uwe Kleine-König <u.kleine-koenig@pengutronix.de> --- drivers/pwm/Kconfig | 4 ---- drivers/pwm/Makefile | 3 +-- drivers/pwm/core.c | 57 +++++++++++++++++++++++++++++++++++--------- drivers/pwm/sysfs.c | 45 +++++++--------------------------- include/linux/pwm.h | 17 ++++--------- 5 files changed, 59 insertions(+), 67 deletions(-)