diff mbox series

[v3,037/108] pwm: Provide devm_pwmchip_alloc() function

Message ID 20231121134901.208535-38-u.kleine-koenig@pengutronix.de
State Superseded
Headers show
Series pwm: Fix lifetime issues for pwm_chips | expand

Commit Message

Uwe Kleine-König Nov. 21, 2023, 1:49 p.m. UTC
This function allocates a struct pwm_chip and driver data. Compared to
the status quo the split into pwm_chip and driver data is new, otherwise
it doesn't change anything relevant (yet).

The intention is that after all drivers are switched to use this
allocation function, its possible to add a struct device to struct
pwm_chip to properly track the latter's lifetime without touching all
drivers again. Proper lifetime tracking is a necessary precondition to
introduce character device support for PWMs (that implements atomic
setting and doesn't suffer from the sysfs overhead of the /sys/class/pwm
userspace support).

The new function pwmchip_priv() (obviously?) only works for chips
allocated with devm_pwmchip_alloc().

Signed-off-by: Uwe Kleine-König <u.kleine-koenig@pengutronix.de>
 .../driver-api/driver-model/devres.rst        |  1 +
 Documentation/driver-api/pwm.rst              | 10 +++---
 drivers/pwm/core.c                            | 32 ++++++++++++++++---
 include/linux/pwm.h                           |  5 +++
 4 files changed, 38 insertions(+), 10 deletions(-)
diff mbox series


diff --git a/Documentation/driver-api/driver-model/devres.rst b/Documentation/driver-api/driver-model/devres.rst
index c5f99d834ec5..e4df72c408d2 100644
--- a/Documentation/driver-api/driver-model/devres.rst
+++ b/Documentation/driver-api/driver-model/devres.rst
@@ -420,6 +420,7 @@  POWER
+  devm_pwmchip_alloc()
diff --git a/Documentation/driver-api/pwm.rst b/Documentation/driver-api/pwm.rst
index bb264490a87a..994ea5368805 100644
--- a/Documentation/driver-api/pwm.rst
+++ b/Documentation/driver-api/pwm.rst
@@ -134,11 +134,11 @@  to implement the pwm_*() functions itself. This means that it's impossible
 to have multiple PWM drivers in the system. For this reason it's mandatory
 for new drivers to use the generic PWM framework.
-A new PWM controller/chip can be added using pwmchip_add() and removed
-again with pwmchip_remove(). pwmchip_add() takes a filled in struct
-pwm_chip as argument which provides a description of the PWM chip, the
-number of PWM devices provided by the chip and the chip-specific
-implementation of the supported PWM operations to the framework.
+A new PWM controller/chip can be allocated using devm_pwmchip_alloc, then added
+using pwmchip_add() and removed again with pwmchip_remove(). pwmchip_add()
+takes a filled in struct pwm_chip as argument which provides a description of
+the PWM chip, the number of PWM devices provided by the chip and the
+chip-specific implementation of the supported PWM operations to the framework.
 When implementing polarity support in a PWM driver, make sure to respect the
 signal conventions in the PWM framework. By definition, normal polarity
diff --git a/drivers/pwm/core.c b/drivers/pwm/core.c
index 58a9d3df73a1..0346709b60d8 100644
--- a/drivers/pwm/core.c
+++ b/drivers/pwm/core.c
@@ -196,6 +196,30 @@  static bool pwm_ops_check(const struct pwm_chip *chip)
 	return true;
+void *pwmchip_priv(struct pwm_chip *chip)
+	return (void *)chip + sizeof(*chip);
+struct pwm_chip *devm_pwmchip_alloc(struct device *parent, unsigned int npwm, size_t sizeof_priv)
+	struct pwm_chip *chip;
+	size_t alloc_size;
+	alloc_size = size_add(sizeof(*chip), sizeof_priv);
+	chip = devm_kzalloc(parent, alloc_size, GFP_KERNEL);
+	if (!chip)
+		return ERR_PTR(-ENOMEM);
+	chip->dev = parent;
+	chip->npwm = npwm;
+	return chip;
  * __pwmchip_add() - register a new PWM chip
  * @chip: the PWM chip to add
@@ -208,9 +232,8 @@  static bool pwm_ops_check(const struct pwm_chip *chip)
 int __pwmchip_add(struct pwm_chip *chip, struct module *owner)
-	struct pwm_device *pwm;
-	unsigned int i;
 	int ret;
+	unsigned i;
 	if (!chip || !chip->dev || !chip->ops || !chip->npwm)
 		return -EINVAL;
@@ -220,7 +243,7 @@  int __pwmchip_add(struct pwm_chip *chip, struct module *owner)
 	chip->owner = owner;
-	chip->pwms = kcalloc(chip->npwm, sizeof(*pwm), GFP_KERNEL);
+	chip->pwms = kcalloc(chip->npwm, sizeof(*chip->pwms), GFP_KERNEL);
 	if (!chip->pwms)
 		return -ENOMEM;
@@ -234,9 +257,8 @@  int __pwmchip_add(struct pwm_chip *chip, struct module *owner)
 	chip->id = ret;
 	for (i = 0; i < chip->npwm; i++) {
-		pwm = &chip->pwms[i];
+		struct pwm_device *pwm = &chip->pwms[i];
 		pwm->chip = chip;
 		pwm->hwpwm = i;
diff --git a/include/linux/pwm.h b/include/linux/pwm.h
index dda3a68e06dc..3c62cf329ee0 100644
--- a/include/linux/pwm.h
+++ b/include/linux/pwm.h
@@ -5,6 +5,7 @@ 
 #include <linux/err.h>
 #include <linux/mutex.h>
 #include <linux/of.h>
+#include <linux/compiler_attributes.h>
 struct pwm_chip;
@@ -385,6 +386,10 @@  static inline void pwm_disable(struct pwm_device *pwm)
 int pwm_capture(struct pwm_device *pwm, struct pwm_capture *result,
 		unsigned long timeout);
+void *pwmchip_priv(struct pwm_chip *chip) __attribute_const__;
+struct pwm_chip *devm_pwmchip_alloc(struct device *parent, unsigned int npwm, size_t sizeof_priv);
 int __pwmchip_add(struct pwm_chip *chip, struct module *owner);
 #define pwmchip_add(chip) __pwmchip_add(chip, THIS_MODULE)
 void pwmchip_remove(struct pwm_chip *chip);