@@ -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
@@ -71,18 +71,18 @@ static void pwm_apply_debug(struct pwm_device *pwm,
if (s2.polarity != state->polarity &&
state->duty_cycle < state->period)
- dev_warn(chip->dev, ".apply ignored .polarity\n");
+ dev_warn(chip->dev.parent, ".apply ignored .polarity\n");
if (state->enabled &&
last->polarity == state->polarity &&
last->period > s2.period &&
last->period <= state->period)
- dev_warn(chip->dev,
+ dev_warn(chip->dev.parent,
".apply didn't pick the best available period (requested: %llu, applied: %llu, possible: %llu)\n",
state->period, s2.period, last->period);
if (state->enabled && state->period < s2.period)
- dev_warn(chip->dev,
+ dev_warn(chip->dev.parent,
".apply is supposed to round down period (requested: %llu, applied: %llu)\n",
state->period, s2.period);
@@ -91,20 +91,20 @@ static void pwm_apply_debug(struct pwm_device *pwm,
last->period == s2.period &&
last->duty_cycle > s2.duty_cycle &&
last->duty_cycle <= state->duty_cycle)
- dev_warn(chip->dev,
+ dev_warn(chip->dev.parent,
".apply didn't pick the best available duty cycle (requested: %llu/%llu, applied: %llu/%llu, possible: %llu/%llu)\n",
state->duty_cycle, state->period,
s2.duty_cycle, s2.period,
last->duty_cycle, last->period);
if (state->enabled && state->duty_cycle < s2.duty_cycle)
- dev_warn(chip->dev,
+ dev_warn(chip->dev.parent,
".apply is supposed to round down duty_cycle (requested: %llu/%llu, applied: %llu/%llu)\n",
state->duty_cycle, state->period,
s2.duty_cycle, s2.period);
if (!state->enabled && s2.enabled && s2.duty_cycle > 0)
- dev_warn(chip->dev,
+ dev_warn(chip->dev.parent,
"requested disabled, but yielded enabled with duty > 0\n");
/* reapply the state that the driver reported being configured. */
@@ -112,7 +112,7 @@ static void pwm_apply_debug(struct pwm_device *pwm,
trace_pwm_apply(pwm, &s1, err);
if (err) {
*last = s1;
- dev_err(chip->dev, "failed to reapply current setting\n");
+ dev_err(chip->dev.parent, "failed to reapply current setting\n");
return;
}
@@ -127,7 +127,7 @@ static void pwm_apply_debug(struct pwm_device *pwm,
s1.polarity != last->polarity ||
(s1.enabled && s1.period != last->period) ||
(s1.enabled && s1.duty_cycle != last->duty_cycle)) {
- dev_err(chip->dev,
+ dev_err(chip->dev.parent,
".apply is not idempotent (ena=%d pol=%d %llu/%llu) -> (ena=%d pol=%d %llu/%llu)\n",
s1.enabled, s1.polarity, s1.duty_cycle, s1.period,
last->enabled, last->polarity, last->duty_cycle,
@@ -318,7 +318,7 @@ static struct pwm_chip *pwmchip_find_by_name(const char *name)
mutex_lock(&pwm_lock);
idr_for_each_entry_ul(&pwm_chips, chip, tmp, id) {
- const char *chip_name = dev_name(chip->dev);
+ const char *chip_name = dev_name(chip->dev.parent);
if (chip_name && strcmp(chip_name, name) == 0) {
mutex_unlock(&pwm_lock);
@@ -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;
}
}
@@ -459,19 +459,33 @@ static void *pwmchip_priv(struct pwm_chip *chip)
return (void *)chip + struct_size(chip, pwms, chip->npwm);
}
-struct pwm_chip *devm_pwmchip_alloc(struct device *parent, unsigned int npwm, size_t sizeof_priv)
+static void pwmchip_release(struct device *dev)
+{
+ struct pwm_chip *chip = container_of(dev, struct pwm_chip, dev);
+
+ kfree(chip);
+}
+
+static 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;
alloc_size = size_add(struct_size(chip, pwms, npwm), sizeof_priv);
- chip = devm_kzalloc(parent, alloc_size, GFP_KERNEL);
+ chip = kzalloc(alloc_size, GFP_KERNEL);
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;
@@ -486,23 +500,46 @@ struct pwm_chip *devm_pwmchip_alloc(struct device *parent, unsigned int npwm, si
return chip;
}
+
+static void devm_pwmchip_put(void *_chip)
+{
+ struct pwm_chip *chip = _chip;
+
+ put_device(&chip->dev);
+}
+
+struct pwm_chip *devm_pwmchip_alloc(struct device *parent, unsigned int npwm, size_t sizeof_priv)
+{
+ struct pwm_chip *chip;
+ int ret;
+
+ chip = pwmchip_alloc(parent, npwm, sizeof_priv);
+ if (IS_ERR(chip))
+ return chip;
+
+ ret = devm_add_action_or_reset(parent, devm_pwmchip_put, chip);
+ if (ret)
+ return ERR_PTR(ret);
+
+ return chip;
+}
EXPORT_SYMBOL_GPL(devm_pwmchip_alloc);
static void of_pwmchip_add(struct pwm_chip *chip)
{
- if (!chip->dev || !chip->dev->of_node)
+ if (!chip->dev.parent || !chip->dev.parent->of_node)
return;
if (!chip->of_xlate)
chip->of_xlate = of_pwm_xlate_with_flags;
- of_node_get(chip->dev->of_node);
+ of_node_get(chip->dev.parent->of_node);
}
static void of_pwmchip_remove(struct pwm_chip *chip)
{
- if (chip->dev)
- of_node_put(chip->dev->of_node);
+ if (chip->dev.parent)
+ of_node_put(chip->dev.parent->of_node);
}
static bool pwm_ops_check(const struct pwm_chip *chip)
@@ -513,7 +550,7 @@ static bool pwm_ops_check(const struct pwm_chip *chip)
return false;
if (IS_ENABLED(CONFIG_PWM_DEBUG) && !ops->get_state)
- dev_warn(chip->dev,
+ dev_warn(chip->dev.parent,
"Please implement the .get_state() callback\n");
return true;
@@ -533,7 +570,7 @@ int __pwmchip_add(struct pwm_chip *chip, struct module *owner)
{
int ret;
- if (!chip || !chip->dev || !chip->ops || !chip->npwm)
+ if (!chip || !chip->dev.parent || !chip->ops || !chip->npwm)
return -EINVAL;
/*
@@ -549,25 +586,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);
@@ -589,6 +643,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);
@@ -622,15 +680,15 @@ static struct device_link *pwm_device_link_add(struct device *dev,
* impact the PM sequence ordering: the PWM supplier may get
* suspended before the consumer.
*/
- dev_warn(pwm->chip->dev,
+ dev_warn(pwm->chip->dev.parent,
"No consumer device specified to create a link to\n");
return NULL;
}
- dl = device_link_add(dev, pwm->chip->dev, DL_FLAG_AUTOREMOVE_CONSUMER);
+ dl = device_link_add(dev, pwm->chip->dev.parent, DL_FLAG_AUTOREMOVE_CONSUMER);
if (!dl) {
dev_err(dev, "failed to create device link to %s\n",
- dev_name(pwm->chip->dev));
+ dev_name(pwm->chip->dev.parent));
return ERR_PTR(-EINVAL);
}
@@ -645,7 +703,7 @@ static struct pwm_chip *fwnode_to_pwmchip(struct fwnode_handle *fwnode)
mutex_lock(&pwm_lock);
idr_for_each_entry_ul(&pwm_chips, chip, tmp, id)
- if (chip->dev && device_match_fwnode(chip->dev, fwnode)) {
+ if (chip->dev.parent && device_match_fwnode(chip->dev.parent, fwnode)) {
mutex_unlock(&pwm_lock);
return chip;
}
@@ -973,7 +1031,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);
}
@@ -1113,8 +1171,8 @@ static int pwm_seq_show(struct seq_file *s, void *v)
seq_printf(s, "%s%d: %s/%s, %d PWM device%s\n",
(char *)s->private, chip->id,
- chip->dev->bus ? chip->dev->bus->name : "no-bus",
- dev_name(chip->dev), chip->npwm,
+ chip->dev.parent->bus ? chip->dev.parent->bus->name : "no-bus",
+ dev_name(chip->dev.parent), chip->npwm,
(chip->npwm != 1) ? "s" : "");
pwm_dbg_show(chip, s);
@@ -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, chip->dev, MKDEV(0, 0), chip,
- "pwmchip%d", chip->id);
- if (IS_ERR(parent)) {
- dev_warn(chip->dev,
- "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(struct pwm_chip *chip)
{
- return chip->dev;
+ return chip->dev.parent;
}
static inline void *pwmchip_get_drvdata(struct pwm_chip *chip)
@@ -610,17 +611,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 | 124 +++++++++++++++++++++++++++++++------------ drivers/pwm/sysfs.c | 45 +++------------- include/linux/pwm.h | 17 ++---- 5 files changed, 104 insertions(+), 89 deletions(-)