Skip to contentSkip to conversation

pwm: add sysfs interface

PATCH Superseded H Hartley Sweeten 2 participants 4 messages May 30, 2013 – Jun 10, 2013

Subsystems (2)

v3 of 5 Superseded Latest is v5 →

lore
Share & tools
lore
#0 lore raw

PATCH v3 1/1 pwm: add sysfs interface

Add a simple sysfs interface to the generic PWM framework.

/sys/class/pwm/

  `-- pwmchipN/          for each PWM chip
      |-- export         (w/o) ask the kernel to export a PWM channel
      |-- npwn           (r/o) number of PWM channels in this PWM chip
      |-- pwmX/          for each exported PWM channel
      |   |-- duty_ns    (r/w) duty cycle (in nanoseconds)
      |   |-- enable     (r/w) enable/disable PWM
      |   |-- period_ns  (r/w) period (in nanoseconds)
      |   `-- polarity   (r/w) polarity of PWM
      `-- unexport       (w/o) return a PWM channel to the kernel
Cc Thierry Reding <[email protected]>
Cc Lars Poeschel <[email protected]>
Cc Ryan Mallon <[email protected]>
Cc Rob Landley <[email protected]>

---
v3: * fix an issue with the export/unexport of the PWM chip
v2: * add API documentation and update Documentation/pwm.txt
* fix some issues pointed out by Ryan Mallon
* add the pwm attributes to dev.groups so they are created when the device is registered for the exported PWM
v1: * Based on previous work by Lars Poecshel

 Documentation/ABI/testing/sysfs-class-pwm |  80 +++++++
 Documentation/pwm.txt                     |  39 ++++
 drivers/pwm/Kconfig                       |  12 +
 drivers/pwm/Makefile                      |   1 +
 drivers/pwm/core.c                        |  25 ++-
 drivers/pwm/pwm-sysfs.c                   | 358 ++++++++++++++++++++++++++++++
 include/linux/pwm.h                       |  28 +++
 7 files changed, 541 insertions(+), 2 deletions(-)
 create mode 100644 Documentation/ABI/testing/sysfs-class-pwm
 create mode 100644 drivers/pwm/pwm-sysfs.c
Documentation/ABI/testing/sysfs-class-pwm new file +80 -0
new file mode 100644
index 0000000..eaa4e1f
@@ -0,0 +1,80 @@
+What:		/sys/class/pwm/
+Date:		May 2013
+KernelVersion:	3.11
+Contact:	H Hartley Sweeten <[email protected]>
+Description:
+		The pwm/ class sub-directory belongs to the Generic PWM
+		Framework and provides a sysfs interface for using PWM
+		channels.
+
+What:		/sys/class/pwm/pwmchipN/
+Date:		May 2013
+KernelVersion:	3.11
+Contact:	H Hartley Sweeten <[email protected]>
+Description:
+		A /sys/class/pwm/pwmchipN directory is created for each
+		probed PWM controller/chip where N is the base of the
+		PWM chip.
+
+What:		/sys/class/pwm/pwmchipN/npwm
+Date:		May 2013
+KernelVersion:	3.11
+Contact:	H Hartley Sweeten <[email protected]>
+Description:
+		The number of PWM channels supported by the PWM chip.
+
+What:		/sys/class/pwm/pwmchipN/export
+Date:		May 2013
+KernelVersion:	3.11
+Contact:	H Hartley Sweeten <[email protected]>
+Description:
+		Exports a PWM channel from the PWM chip for sysfs control.
+		Value is between 0 and /sys/class/pwm/pwmchipN/npwm - 1.
+
+What:		/sys/class/pwm/pwmchipN/unexport
+Date:		May 2013
+KernelVersion:	3.11
+Contact:	H Hartley Sweeten <[email protected]>
+Description:
+		Unexports a PWM channel.
+
+What:		/sys/class/pwm/pwmchipN/pwmX
+Date:		May 2013
+KernelVersion:	3.11
+Contact:	H Hartley Sweeten <[email protected]>
+Description:
+		A /sys/class/pwm/pwmchipN/pwmX directory is created for
+		each exported PWM channel where X is the PWM channel number.
+
+What:		/sys/class/pwm/pwmchipN/pwmX/period_ns
+Date:		May 2013
+KernelVersion:	3.11
+Contact:	H Hartley Sweeten <[email protected]>
+Description:
+		Sets the PWM period in nanoseconds.
+
+What:		/sys/class/pwm/pwmchipN/pwmX/duty_ns
+Date:		May 2013
+KernelVersion:	3.11
+Contact:	H Hartley Sweeten <[email protected]>
+Description:
+		Sets the PWM duty cycle in nanoseconds, default is half the
+		/sys/class/pwm/pwmchipN/pwmX/period_ns.
+
+What:		/sys/class/pwm/pwmchipN/pwmX/polarity
+Date:		May 2013
+KernelVersion:	3.11
+Contact:	H Hartley Sweeten <[email protected]>
+Description:
+		Sets the output polarity of the PWM.
+		0 is normal polarity
+		1 is inversed polarity
+
+What:		/sys/class/pwm/pwmchipN/pwmX/enable
+Date:		May 2013
+KernelVersion:	3.11
+Contact:	H Hartley Sweeten <[email protected]>
+Description:
+		Enables/disables the PWM.
+		0 is disabled
+		1 is enabled
Documentation/pwm.txt +39 -0
index 7d2b4c9..b58526d 100644
@@ -45,6 +45,45 @@ int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns);
 
 To start/stop toggling the PWM output use pwm_enable()/pwm_disable().
 
+Using PWMs with the sysfs interface
+-----------------------------------
+
+You have to enable CONFIG_PWM_SYSFS in your kernel configuration to use
+the sysfs interface. It is exposed at /sys/class/pwm/. Each probed PWM
+controller/chip will be exported as pwmchipN, where N is the base of the
+PWM chip. Inside the directory you will find:
+
+npwm - The number of PWM channels this chip supports (read-only).
+
+export - Exports a PWM channel for use with sysfs (write-only).
+
+unexport - Unexports a PWM channel from sysfs (write-only).
+
+The PWM channels are PWM chip specific from 0 to npwn-1.
+
+When a PWM channel is exported a pwmX directory will be created in the
+pwmchipN directory it is associated with, where X is the channel number
+that was exported. The following properties will then be available:
+
+period_ns - The total period of the PWM (read/write).
+	Value is in nanoseconds and is the sum of the active and inactive
+	time of the PWM. If duty_ns is zero when this property is written
+	it will be automatically set to half the period_ns.
+
+duty_ns - The active time of the PWM (read/write).
+	Value is in nanoseconds and must be less than the period_ns.
+
+polarity - Changes the polarity of the PWM (read/write).
+	Writes to this property only work if the PWM chip supports changing
+	the polarity. The polarity can only be changed if the PWM is not
+	enabled.
+	0 - normal polarity
+	1 - inversed polarity
+
+enable - Enables/disables the PWM (read/write).
+	0 - disabled
+	1 - enabled
+
 Implementing a PWM driver
 -------------------------
 
drivers/pwm/Kconfig +12 -0
index 115b644..e77405d 100644
@@ -28,6 +28,18 @@ menuconfig PWM
 
 if PWM
 
+config PWM_SYSFS
+	bool "/sys/class/pwm/... (sysfs interface)"
+	depends on SYSFS
+	help
+	  Say Y here to provide a sysfs interface to control PWMs.
+
+	  For every instance of a PWM device there is a pwmchipN directory
+	  created in /sys/class/pwm. Use the export attribute to request
+	  a PWM to be accessible from userspace and the unexport attribute
+	  to return the PWM to the kernel. Each exported PWM will have a
+	  pwmX directory in the pwmchipN it is associated with.
+
 config PWM_AB8500
 	tristate "AB8500 PWM support"
 	depends on AB8500_CORE && ARCH_U8500
drivers/pwm/Makefile +1 -0
index 94ba21e..fb92e1d 100644
@@ -1,4 +1,5 @@
 obj-$(CONFIG_PWM)		+= core.o
+obj-$(CONFIG_PWM_SYSFS)		+= pwm-sysfs.o
 obj-$(CONFIG_PWM_AB8500)	+= pwm-ab8500.o
 obj-$(CONFIG_PWM_ATMEL_TCB)	+= pwm-atmel-tcb.o
 obj-$(CONFIG_PWM_BFIN)		+= pwm-bfin.o
drivers/pwm/core.c +23 -2
index 32221cb..f67465c 100644
@@ -274,6 +274,8 @@ int pwmchip_add(struct pwm_chip *chip)
 	if (IS_ENABLED(CONFIG_OF))
 		of_pwmchip_add(chip);
 
+	pwmchip_sysfs_export(chip);
+
 out:
 	mutex_unlock(&pwm_lock);
 	return ret;
@@ -310,6 +312,8 @@ int pwmchip_remove(struct pwm_chip *chip)
 
 	free_pwms(chip);
 
+	pwmchip_sysfs_unexport(chip);
+
 out:
 	mutex_unlock(&pwm_lock);
 	return ret;
@@ -402,10 +406,19 @@ EXPORT_SYMBOL_GPL(pwm_free);
  */
 int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns)
 {
+	int err;
+
 	if (!pwm || duty_ns < 0 || period_ns <= 0 || duty_ns > period_ns)
 		return -EINVAL;
 
-	return pwm->chip->ops->config(pwm->chip, pwm, duty_ns, period_ns);
+	err = pwm->chip->ops->config(pwm->chip, pwm, duty_ns, period_ns);
+	if (err)
+		return err;
+
+	pwm->duty = duty_ns;
+	pwm->period = period_ns;
+
+	return 0;
 }
 EXPORT_SYMBOL_GPL(pwm_config);
 
@@ -418,6 +431,8 @@ EXPORT_SYMBOL_GPL(pwm_config);
  */
 int pwm_set_polarity(struct pwm_device *pwm, enum pwm_polarity polarity)
 {
+	int err;
+
 	if (!pwm || !pwm->chip->ops)
 		return -EINVAL;
 
@@ -427,7 +442,13 @@ int pwm_set_polarity(struct pwm_device *pwm, enum pwm_polarity polarity)
 	if (test_bit(PWMF_ENABLED, &pwm->flags))
 		return -EBUSY;
 
-	return pwm->chip->ops->set_polarity(pwm->chip, pwm, polarity);
+	err = pwm->chip->ops->set_polarity(pwm->chip, pwm, polarity);
+	if (err)
+		return err;
+
+	pwm->polarity = polarity;
+
+	return 0;
 }
 EXPORT_SYMBOL_GPL(pwm_set_polarity);
 
drivers/pwm/pwm-sysfs.c new file +358 -0

358 lines not on this page · raw message

include/linux/pwm.h +28 -0
index a4df204..6a43484 100644
@@ -76,6 +76,7 @@ enum pwm_polarity {
 enum {
 	PWMF_REQUESTED = 1 << 0,
 	PWMF_ENABLED = 1 << 1,
+	PWMF_EXPORTED = 1 << 2,
 };
 
 struct pwm_device {
@@ -86,7 +87,10 @@ struct pwm_device {
 	struct pwm_chip		*chip;
 	void			*chip_data;
 
+	struct device		*dev;	/* for sysfs export */
 	unsigned int		period; /* in nanoseconds */
+	unsigned int		duty;	/* in nanoseconds */
+	enum pwm_polarity	polarity;
 };
 
 static inline void pwm_set_period(struct pwm_device *pwm, unsigned int period)
@@ -100,6 +104,17 @@ static inline unsigned int pwm_get_period(struct pwm_device *pwm)
 	return pwm ? pwm->period : 0;
 }
 
+static inline void pwm_set_duty_cycle(struct pwm_device *pwm, unsigned int duty)
+{
+	if (pwm)
+		pwm->duty = duty;
+}
+
+static inline unsigned int pwm_get_duty_cycle(struct pwm_device *pwm)
+{
+	return pwm ? pwm->duty : 0;
+}
+
 /*
  * pwm_set_polarity - configure the polarity of a PWM signal
  */
@@ -278,4 +293,17 @@ static inline void pwm_add_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 */
Signature · 1 line
1.8.1.4
#1 lore raw

Ping?

-----Original Message-----
From: H Hartley Sweeten
Sent: Thursday, May 30, 2013 2:31 PM
To: Linux Kernel
Cc: [email protected]; [email protected]; [email protected]; [email protected]; Ryan Mallon; [email protected]; H Hartley Sweeten
Subject: [PATCH v3] pwm: add sysfs interface

Add a simple sysfs interface to the generic PWM framework.

/sys/class/pwm/

  `-- pwmchipN/          for each PWM chip
      |-- export         (w/o) ask the kernel to export a PWM channel
      |-- npwn           (r/o) number of PWM channels in this PWM chip
      |-- pwmX/          for each exported PWM channel
      |   |-- duty_ns    (r/w) duty cycle (in nanoseconds)
      |   |-- enable     (r/w) enable/disable PWM
      |   |-- period_ns  (r/w) period (in nanoseconds)
      |   `-- polarity   (r/w) polarity of PWM
      `-- unexport       (w/o) return a PWM channel to the kernel
Cc Thierry Reding <[email protected]>
Cc Lars Poeschel <[email protected]>
Cc Ryan Mallon <[email protected]>
Cc Rob Landley <[email protected]>

---
v3: * fix an issue with the export/unexport of the PWM chip
v2: * add API documentation and update Documentation/pwm.txt
* fix some issues pointed out by Ryan Mallon
* add the pwm attributes to dev.groups so they are created when the device is registered for the exported PWM
v1: * Based on previous work by Lars Poecshel

 Documentation/ABI/testing/sysfs-class-pwm |  80 +++++++
 Documentation/pwm.txt                     |  39 ++++
 drivers/pwm/Kconfig                       |  12 +
 drivers/pwm/Makefile                      |   1 +
 drivers/pwm/core.c                        |  25 ++-
 drivers/pwm/pwm-sysfs.c                   | 358 ++++++++++++++++++++++++++++++
 include/linux/pwm.h                       |  28 +++
 7 files changed, 541 insertions(+), 2 deletions(-)
 create mode 100644 Documentation/ABI/testing/sysfs-class-pwm
 create mode 100644 drivers/pwm/pwm-sysfs.c
Documentation/ABI/testing/sysfs-class-pwm new file +80 -0
new file mode 100644
index 0000000..eaa4e1f
@@ -0,0 +1,80 @@
+What:          /sys/class/pwm/
+Date:          May 2013
+KernelVersion: 3.11
+Contact:       H Hartley Sweeten <[email protected]>
+Description:
+               The pwm/ class sub-directory belongs to the Generic PWM
+               Framework and provides a sysfs interface for using PWM
+               channels.
+
+What:          /sys/class/pwm/pwmchipN/
+Date:          May 2013
+KernelVersion: 3.11
+Contact:       H Hartley Sweeten <[email protected]>
+Description:
+               A /sys/class/pwm/pwmchipN directory is created for each
+               probed PWM controller/chip where N is the base of the
+               PWM chip.
+
+What:          /sys/class/pwm/pwmchipN/npwm
+Date:          May 2013
+KernelVersion: 3.11
+Contact:       H Hartley Sweeten <[email protected]>
+Description:
+               The number of PWM channels supported by the PWM chip.
+
+What:          /sys/class/pwm/pwmchipN/export
+Date:          May 2013
+KernelVersion: 3.11
+Contact:       H Hartley Sweeten <[email protected]>
+Description:
+               Exports a PWM channel from the PWM chip for sysfs control.
+               Value is between 0 and /sys/class/pwm/pwmchipN/npwm - 1.
+
+What:          /sys/class/pwm/pwmchipN/unexport
+Date:          May 2013
+KernelVersion: 3.11
+Contact:       H Hartley Sweeten <[email protected]>
+Description:
+               Unexports a PWM channel.
+
+What:          /sys/class/pwm/pwmchipN/pwmX
+Date:          May 2013
+KernelVersion: 3.11
+Contact:       H Hartley Sweeten <[email protected]>
+Description:
+               A /sys/class/pwm/pwmchipN/pwmX directory is created for
+               each exported PWM channel where X is the PWM channel number.
+
+What:          /sys/class/pwm/pwmchipN/pwmX/period_ns
+Date:          May 2013
+KernelVersion: 3.11
+Contact:       H Hartley Sweeten <[email protected]>
+Description:
+               Sets the PWM period in nanoseconds.
+
+What:          /sys/class/pwm/pwmchipN/pwmX/duty_ns
+Date:          May 2013
+KernelVersion: 3.11
+Contact:       H Hartley Sweeten <[email protected]>
+Description:
+               Sets the PWM duty cycle in nanoseconds, default is half the
+               /sys/class/pwm/pwmchipN/pwmX/period_ns.
+
+What:          /sys/class/pwm/pwmchipN/pwmX/polarity
+Date:          May 2013
+KernelVersion: 3.11
+Contact:       H Hartley Sweeten <[email protected]>
+Description:
+               Sets the output polarity of the PWM.
+               0 is normal polarity
+               1 is inversed polarity
+
+What:          /sys/class/pwm/pwmchipN/pwmX/enable
+Date:          May 2013
+KernelVersion: 3.11
+Contact:       H Hartley Sweeten <[email protected]>
+Description:
+               Enables/disables the PWM.
+               0 is disabled
+               1 is enabled
Documentation/pwm.txt +39 -0
index 7d2b4c9..b58526d 100644
@@ -45,6 +45,45 @@ int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns);
 
 To start/stop toggling the PWM output use pwm_enable()/pwm_disable().
 
+Using PWMs with the sysfs interface
+-----------------------------------
+
+You have to enable CONFIG_PWM_SYSFS in your kernel configuration to use
+the sysfs interface. It is exposed at /sys/class/pwm/. Each probed PWM
+controller/chip will be exported as pwmchipN, where N is the base of the
+PWM chip. Inside the directory you will find:
+
+npwm - The number of PWM channels this chip supports (read-only).
+
+export - Exports a PWM channel for use with sysfs (write-only).
+
+unexport - Unexports a PWM channel from sysfs (write-only).
+
+The PWM channels are PWM chip specific from 0 to npwn-1.
+
+When a PWM channel is exported a pwmX directory will be created in the
+pwmchipN directory it is associated with, where X is the channel number
+that was exported. The following properties will then be available:
+
+period_ns - The total period of the PWM (read/write).
+       Value is in nanoseconds and is the sum of the active and inactive
+       time of the PWM. If duty_ns is zero when this property is written
+       it will be automatically set to half the period_ns.
+
+duty_ns - The active time of the PWM (read/write).
+       Value is in nanoseconds and must be less than the period_ns.
+
+polarity - Changes the polarity of the PWM (read/write).
+       Writes to this property only work if the PWM chip supports changing
+       the polarity. The polarity can only be changed if the PWM is not
+       enabled.
+       0 - normal polarity
+       1 - inversed polarity
+
+enable - Enables/disables the PWM (read/write).
+       0 - disabled
+       1 - enabled
+
 Implementing a PWM driver
 -------------------------
 
drivers/pwm/Kconfig +12 -0
index 115b644..e77405d 100644
@@ -28,6 +28,18 @@ menuconfig PWM
 
 if PWM
 
+config PWM_SYSFS
+       bool "/sys/class/pwm/... (sysfs interface)"
+       depends on SYSFS
+       help
+         Say Y here to provide a sysfs interface to control PWMs.
+
+         For every instance of a PWM device there is a pwmchipN directory
+         created in /sys/class/pwm. Use the export attribute to request
+         a PWM to be accessible from userspace and the unexport attribute
+         to return the PWM to the kernel. Each exported PWM will have a
+         pwmX directory in the pwmchipN it is associated with.
+
 config PWM_AB8500
        tristate "AB8500 PWM support"
        depends on AB8500_CORE && ARCH_U8500
drivers/pwm/Makefile +1 -0
index 94ba21e..fb92e1d 100644
@@ -1,4 +1,5 @@
 obj-$(CONFIG_PWM)              += core.o
+obj-$(CONFIG_PWM_SYSFS)                += pwm-sysfs.o
 obj-$(CONFIG_PWM_AB8500)       += pwm-ab8500.o
 obj-$(CONFIG_PWM_ATMEL_TCB)    += pwm-atmel-tcb.o
 obj-$(CONFIG_PWM_BFIN)         += pwm-bfin.o
drivers/pwm/core.c +23 -2
index 32221cb..f67465c 100644
@@ -274,6 +274,8 @@ int pwmchip_add(struct pwm_chip *chip)
        if (IS_ENABLED(CONFIG_OF))
                of_pwmchip_add(chip);
 
+       pwmchip_sysfs_export(chip);
+
 out:
        mutex_unlock(&pwm_lock);
        return ret;
@@ -310,6 +312,8 @@ int pwmchip_remove(struct pwm_chip *chip)
 
        free_pwms(chip);
 
+       pwmchip_sysfs_unexport(chip);
+
 out:
        mutex_unlock(&pwm_lock);
        return ret;
@@ -402,10 +406,19 @@ EXPORT_SYMBOL_GPL(pwm_free);
  */
 int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns)
 {
+       int err;
+
        if (!pwm || duty_ns < 0 || period_ns <= 0 || duty_ns > period_ns)
                return -EINVAL;
 
-       return pwm->chip->ops->config(pwm->chip, pwm, duty_ns, period_ns);
+       err = pwm->chip->ops->config(pwm->chip, pwm, duty_ns, period_ns);
+       if (err)
+               return err;
+
+       pwm->duty = duty_ns;
+       pwm->period = period_ns;
+
+       return 0;
 }
 EXPORT_SYMBOL_GPL(pwm_config);
 
@@ -418,6 +431,8 @@ EXPORT_SYMBOL_GPL(pwm_config);
  */
 int pwm_set_polarity(struct pwm_device *pwm, enum pwm_polarity polarity)
 {
+       int err;
+
        if (!pwm || !pwm->chip->ops)
                return -EINVAL;
 
@@ -427,7 +442,13 @@ int pwm_set_polarity(struct pwm_device *pwm, enum pwm_polarity polarity)
        if (test_bit(PWMF_ENABLED, &pwm->flags))
                return -EBUSY;
 
-       return pwm->chip->ops->set_polarity(pwm->chip, pwm, polarity);
+       err = pwm->chip->ops->set_polarity(pwm->chip, pwm, polarity);
+       if (err)
+               return err;
+
+       pwm->polarity = polarity;
+
+       return 0;
 }
 EXPORT_SYMBOL_GPL(pwm_set_polarity);
 
drivers/pwm/pwm-sysfs.c new file +358 -0

358 lines not on this page · raw message

include/linux/pwm.h +28 -0
index a4df204..6a43484 100644
@@ -76,6 +76,7 @@ enum pwm_polarity {
 enum {
        PWMF_REQUESTED = 1 << 0,
        PWMF_ENABLED = 1 << 1,
+       PWMF_EXPORTED = 1 << 2,
 };
 
 struct pwm_device {
@@ -86,7 +87,10 @@ struct pwm_device {
        struct pwm_chip         *chip;
        void                    *chip_data;
 
+       struct device           *dev;   /* for sysfs export */
        unsigned int            period; /* in nanoseconds */
+       unsigned int            duty;   /* in nanoseconds */
+       enum pwm_polarity       polarity;
 };
 
 static inline void pwm_set_period(struct pwm_device *pwm, unsigned int period)
@@ -100,6 +104,17 @@ static inline unsigned int pwm_get_period(struct pwm_device *pwm)
        return pwm ? pwm->period : 0;
 }
 
+static inline void pwm_set_duty_cycle(struct pwm_device *pwm, unsigned int duty)
+{
+       if (pwm)
+               pwm->duty = duty;
+}
+
+static inline unsigned int pwm_get_duty_cycle(struct pwm_device *pwm)
+{
+       return pwm ? pwm->duty : 0;
+}
+
 /*
  * pwm_set_polarity - configure the polarity of a PWM signal
  */
@@ -278,4 +293,17 @@ static inline void pwm_add_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 */

--
1.8.1.4

#2 lore raw
In reply to H Hartley Sweeten
Quoted 1 line · H Hartley Sweeten

Add a simple sysfs interface to the generic PWM framework.

Sorry for taking so long to review this.

Quoted 4 lines

/sys/class/pwm/

  `-- pwmchipN/          for each PWM chip
      |-- export         (w/o) ask the kernel to export a PWM channel
      |-- npwn           (r/o) number of PWM channels in this PWM chip

"npwm"?

Quoted 4 lines
      |-- pwmX/          for each exported PWM channel
      |   |-- duty_ns    (r/w) duty cycle (in nanoseconds)
      |   |-- enable     (r/w) enable/disable PWM
      |   |-- period_ns  (r/w) period (in nanoseconds)

I'm not sure if we need the _ns suffix. If the documentation already specifies that it should be in nanoseconds, shouldn't that be enough?

Quoted 1 line
Documentation/ABI/testing/sysfs-class-pwm +0 -0

[...]

Documentation/ABI/testing/sysfs-class-pwm line 71

Quoted 8 lines
Documentation/ABI/testing/sysfs-class-pwm +8 -0
+What:		/sys/class/pwm/pwmchipN/pwmX/polarity
+Date:		May 2013
+KernelVersion:	3.11
+Contact:	H Hartley Sweeten <[email protected]>
+Description:
+		Sets the output polarity of the PWM.
+		0 is normal polarity
+		1 is inversed polarity

I think this was discussed before, and I think it makes sense to use the string representation here. polarity = 0 or polarity = 1 aren't very intuitive notations in my opinion.

Quoted 1 line
Documentation/pwm.txt +0 -0

[...]

Quoted 1 line

+The PWM channels are PWM chip specific from 0 to npwn-1.

"npwm-1". "channels are PWM chip specific" sounds a bit confusing. Maybe something like "The PWM channels are numbered using a per-chip index from 0 to npwm-1." is more precise?

Documentation/pwm.txt int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns);

Quoted 3 lines
Documentation/pwm.txt +3 -0
+When a PWM channel is exported a pwmX directory will be created in the
+pwmchipN directory it is associated with, where X is the channel number
+that was exported.

"..., where X is the number of the channel that was exported."?

Documentation/pwm.txt

Quoted 3 lines

The following properties will then be available:

Documentation/pwm.txt +2 -0
+
+period_ns - The total period of the PWM (read/write).

"PWM signal"?

Documentation/pwm.txt int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns); · line 71

Quoted 3 lines
Documentation/pwm.txt +3 -0
+	Value is in nanoseconds and is the sum of the active and inactive
+	time of the PWM. If duty_ns is zero when this property is written
+	it will be automatically set to half the period_ns.

I'm not sure that's the best approach. How about deferring the PWM configuration until both values have been set? Or only configure the PWM channel when it has been enabled, and refuse to enable unless both the period and the duty cycle have been set?

drivers/pwm/Makefile line 2

Quoted 7 lines
drivers/pwm/Makefile +1 -0
index 94ba21e..fb92e1d 100644
@@ -1,4 +1,5 @@
 obj-$(CONFIG_PWM)		+= core.o
+obj-$(CONFIG_PWM_SYSFS)		+= pwm-sysfs.o

I'd prefer this to simple be named sysfs.[co] in order to differentiate it from drivers and make it consistent with the naming of core.[co].

Quoted 1 line
drivers/pwm/pwm-sysfs.c +0 -0

[...]

drivers/pwm/pwm-sysfs.c line 38

Quoted 3 lines
drivers/pwm/pwm-sysfs.c +3 -0
+static struct pwm_device *dev_to_pwm_device(struct device *dev)
+{
+	struct pwm_export *pwm_export = dev_to_pwm_export(dev);

Maybe drop the pwm_ prefix on the variable name?

Quoted 6 lines
drivers/pwm/pwm-sysfs.c +6 -0
+static ssize_t pwm_period_ns_store(struct device *dev,
+				   struct device_attribute *attr,
+				   const char *buf, size_t size)
+{
+	struct pwm_device *pwm = dev_to_pwm_device(dev);
+	unsigned int duty = pwm->duty;

drivers/pwm/pwm-sysfs.c line 67

Quoted 10 lines
drivers/pwm/pwm-sysfs.c +10 -0
+	unsigned int val;
+	int ret;
+
+	ret = kstrtouint(buf, 0, &val);
+	if (ret)
+		return ret;
+
+	/* if not set, default to 50% duty cycle */
+	if (duty == 0)
+		duty = val / 2;

How does this differentiate between the case where the user explicitly sets the duty cycle to 0 to "disable" the PWM?

drivers/pwm/pwm-sysfs.c line 116

Quoted 7 lines
drivers/pwm/pwm-sysfs.c +7 -0
+static ssize_t pwm_enable_store(struct device *dev,
+				struct device_attribute *attr,
+				const char *buf, size_t size)
+{
+	struct pwm_device *pwm = dev_to_pwm_device(dev);
+	int val;
+	int ret;

These could go on one line.

drivers/pwm/pwm-sysfs.c line 125

Quoted 9 lines
drivers/pwm/pwm-sysfs.c +9 -0
+
+	ret = kstrtoint(buf, 0, &val);
+	if (ret)
+		return ret;
+
+	switch (val) {
+	case 0:
+		pwm_disable(pwm);
+		ret = 0;

I don't think ret can be anything other than 0 given the above validity check.

Quoted 7 lines
drivers/pwm/pwm-sysfs.c +7 -0
+static ssize_t pwm_polarity_show(struct device *dev,
+				 struct device_attribute *attr,
+				 char *buf)
+{
+	const struct pwm_device *pwm = dev_to_pwm_device(dev);
+
+	return sprintf(buf, "%d\n", pwm->polarity);

drivers/pwm/pwm-sysfs.c line 154

Quoted 10 lines
drivers/pwm/pwm-sysfs.c +10 -0
+}
+
+static ssize_t pwm_polarity_store(struct device *dev,
+				  struct device_attribute *attr,
+				  const char *buf, size_t size)
+{
+	struct pwm_device *pwm = dev_to_pwm_device(dev);
+	enum pwm_polarity polarity;
+	int val;
+	int ret;

Could go on a single line.

Quoted 27 lines
drivers/pwm/pwm-sysfs.c +27 -0
+
+	ret = kstrtoint(buf, 0, &val);
+	if (ret)
+		return ret;
+
+	switch (val) {
+	case 0:
+		polarity = PWM_POLARITY_NORMAL;
+		break;
+	case 1:
+		polarity = PWM_POLARITY_INVERSED;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	ret = pwm_set_polarity(pwm, polarity);
+
+	return ret ? : size;
+}
+
+static DEVICE_ATTR(period_ns, 0644, pwm_period_ns_show, pwm_period_ns_store);
+static DEVICE_ATTR(duty_ns, 0644, pwm_duty_ns_show, pwm_duty_ns_store);
+static DEVICE_ATTR(enable, 0644, pwm_enable_show, pwm_enable_store);
+static DEVICE_ATTR(polarity, 0644, pwm_polarity_show, pwm_polarity_store);
+
+static struct attribute *pwm_attrs[] = {

drivers/pwm/pwm-sysfs.c line 191

Quoted 10 lines
drivers/pwm/pwm-sysfs.c +10 -0
+	&dev_attr_period_ns.attr,
+	&dev_attr_duty_ns.attr,
+	&dev_attr_enable.attr,
+	&dev_attr_polarity.attr,
+	NULL
+};
+
+static struct attribute_group pwm_attr_group = {
+	.attrs		= pwm_attrs,
+};

static const?

drivers/pwm/pwm-sysfs.c line 203

Quoted 6 lines
drivers/pwm/pwm-sysfs.c +6 -0
+static void pwm_export_release(struct device *dev)
+{
+	struct pwm_export *pwm_export = dev_to_pwm_export(dev);
+
+	kfree(pwm_export);
+}

Again, the pwm_ prefix for the variable name seems gratuitous here.

drivers/pwm/pwm-sysfs.c line 207

Quoted 4 lines
drivers/pwm/pwm-sysfs.c +4 -0
+
+static int pwm_export(struct device *dev, struct pwm_device *pwm)
+{
+	struct pwm_export *pwm_export;

And here as well.

drivers/pwm/pwm-sysfs.c line 242

Quoted 6 lines
drivers/pwm/pwm-sysfs.c +6 -0
+static int pwm_unexport_match(struct device *dev, void *data)
+{
+	return dev_to_pwm_device(dev) == data;
+}
+
+static int pwm_unexport(struct device *dev, struct pwm_device *pwm)

I think the current naming of variables is very confusing. It's hard to keep track of what's what. Maybe dev --> parent, or dev --> chip? Then...

drivers/pwm/pwm-sysfs.c

Quoted 2 lines
drivers/pwm/pwm-sysfs.c +2 -0
+{
+	struct device *export_dev;

export_dev --> dev, and...

drivers/pwm/pwm-sysfs.c

Quoted 1 line
drivers/pwm/pwm-sysfs.c +1 -0
+	struct pwm_export *pwm_export;

pwm_export --> export?

drivers/pwm/pwm-sysfs.c line 253

Quoted 8 lines
drivers/pwm/pwm-sysfs.c +8 -0
+
+	if (!test_and_clear_bit(PWMF_EXPORTED, &pwm->flags))
+		return -ENODEV;
+
+	export_dev = device_find_child(dev, pwm, pwm_unexport_match);
+	pwm_export = dev_to_pwm_export(export_dev);
+	put_device(&pwm_export->dev);
+	device_unregister(&pwm_export->dev);

device_unregister() already calls put_device(), why do you do it again here?

Quoted 5 lines
drivers/pwm/pwm-sysfs.c +5 -0
+	pwm_put(pwm);
+
+	return 0;
+}
+

[...]

drivers/pwm/pwm-sysfs.c line 341

Quoted 9 lines
drivers/pwm/pwm-sysfs.c +9 -0
+void pwmchip_sysfs_export(struct pwm_chip *chip)
+{
+	/*
+	 * If device_create() fails the pwm_chip is still usable by
+	 * the kernel its just not exported.
+	 */
+	device_create(&pwm_class, chip->dev, MKDEV(0, 0), chip,
+		      "pwmchip%d", chip->base);
+}

Maybe a warning message would still be useful in that case?

drivers/pwm/pwm-sysfs.c line 350

Quoted 8 lines
drivers/pwm/pwm-sysfs.c +8 -0
+void pwmchip_sysfs_unexport(struct pwm_chip *chip)
+{
+	struct device *dev;
+
+	dev = class_find_device(&pwm_class, NULL, chip, pwmchip_sysfs_match);
+	if (dev) {
+		put_device(dev);
+		device_unregister(dev);

device_unregister() already calls put_device(), why do you do it again here?

Quoted 1 line
include/linux/pwm.h +0 -0

[...]

include/linux/pwm.h struct pwm_device { · line 92

Quoted 1 line
include/linux/pwm.h +1 -0
+	unsigned int		duty;	/* in nanoseconds */

I'd prefer this to be called duty_cycle.

Thierry

#3 lore raw
Quoted 4 lines · Thierry Reding
Quoted 1 line · H Hartley Sweeten

Add a simple sysfs interface to the generic PWM framework.

Sorry for taking so long to review this.

Not a problem. Thanks for the review.

Quoted 6 lines
Quoted 4 lines

/sys/class/pwm/

  `-- pwmchipN/          for each PWM chip
      |-- export         (w/o) ask the kernel to export a PWM channel
      |-- npwn           (r/o) number of PWM channels in this PWM chip

"npwm"?

Fat-fingered that. Fixed.

Quoted 7 lines
Quoted 4 lines
      |-- pwmX/          for each exported PWM channel
      |   |-- duty_ns    (r/w) duty cycle (in nanoseconds)
      |   |-- enable     (r/w) enable/disable PWM
      |   |-- period_ns  (r/w) period (in nanoseconds)

I'm not sure if we need the _ns suffix. If the documentation already specifies that it should be in nanoseconds, shouldn't that be enough?

In the earlier review of Lars Poeschel's patch I think you said you wanted the attributes to match the variable names.

But, "duty' and "period" are probably close enough. Fixed.

Quoted 14 lines
Quoted 1 line
Documentation/ABI/testing/sysfs-class-pwm +0 -0

[...]

Documentation/ABI/testing/sysfs-class-pwm line 71

Quoted 8 lines
Documentation/ABI/testing/sysfs-class-pwm +8 -0
+What:		/sys/class/pwm/pwmchipN/pwmX/polarity
+Date:		May 2013
+KernelVersion:	3.11
+Contact:	H Hartley Sweeten <[email protected]>
+Description:
+		Sets the output polarity of the PWM.
+		0 is normal polarity
+		1 is inversed polarity

I think this was discussed before, and I think it makes sense to use the string representation here. polarity = 0 or polarity = 1 aren't very intuitive notations in my opinion.

You did mention that before. I overlooked it.

I'll change this.

Quoted 7 lines
Quoted 1 line
Documentation/pwm.txt +0 -0

[...]

Quoted 1 line

+The PWM channels are PWM chip specific from 0 to npwn-1.

"npwm-1". "channels are PWM chip specific" sounds a bit confusing. Maybe something like "The PWM channels are numbered using a per-chip index from 0 to npwm-1." is more precise?

Ok.

Quoted 5 lines

Documentation/pwm.txt int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns);

Quoted 3 lines
Documentation/pwm.txt +3 -0
+When a PWM channel is exported a pwmX directory will be created in the
+pwmchipN directory it is associated with, where X is the channel number
+that was exported.

"..., where X is the number of the channel that was exported."?

Ok.

Quoted 5 lines

Documentation/pwm.txt

Quoted 3 lines

The following properties will then be available:

Documentation/pwm.txt +2 -0
+
+period_ns - The total period of the PWM (read/write).

"PWM signal"?

OK.

Quoted 8 lines

Documentation/pwm.txt int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns); · line 71

Quoted 3 lines
Documentation/pwm.txt +3 -0
+	Value is in nanoseconds and is the sum of the active and inactive
+	time of the PWM. If duty_ns is zero when this property is written
+	it will be automatically set to half the period_ns.

I'm not sure that's the best approach. How about deferring the PWM configuration until both values have been set? Or only configure the PWM channel when it has been enabled, and refuse to enable unless both the period and the duty cycle have been set?

See below.

Quoted 10 lines

drivers/pwm/Makefile line 2

Quoted 7 lines
drivers/pwm/Makefile +1 -0
index 94ba21e..fb92e1d 100644
@@ -1,4 +1,5 @@
 obj-$(CONFIG_PWM)		+= core.o
+obj-$(CONFIG_PWM_SYSFS)		+= pwm-sysfs.o

I'd prefer this to simple be named sysfs.[co] in order to differentiate it from drivers and make it consistent with the naming of core.[co].

Ok.

Quoted 7 lines
Quoted 1 line
drivers/pwm/pwm-sysfs.c +0 -0

[...]

drivers/pwm/pwm-sysfs.c line 38

Quoted 3 lines
drivers/pwm/pwm-sysfs.c +3 -0
+static struct pwm_device *dev_to_pwm_device(struct device *dev)
+{
+	struct pwm_export *pwm_export = dev_to_pwm_export(dev);

Maybe drop the pwm_ prefix on the variable name?

Ok.

Quoted 11 lines
Quoted 11 lines
drivers/pwm/pwm-sysfs.c +11 -0
+static ssize_t pwm_period_ns_store(struct device *dev,
+				   struct device_attribute *attr,
+				   const char *buf, size_t size)
+{
+	struct pwm_device *pwm = dev_to_pwm_device(dev);
+	unsigned int duty = pwm->duty;
+	unsigned int val;
+	int ret;
+
+	ret = kstrtouint(buf, 0, &val);
+	if (ret)
Quoted 8 lines

drivers/pwm/pwm-sysfs.c line 67

Quoted 5 lines
drivers/pwm/pwm-sysfs.c +5 -0
+		return ret;
+
+	/* if not set, default to 50% duty cycle */
+	if (duty == 0)
+		duty = val / 2;

How does this differentiate between the case where the user explicitly sets the duty cycle to 0 to "disable" the PWM?

It doesn't.

Actually, looking at pwm_config() a duty_ns value of 0 is allowed so this check is not necessary.

Quoted 9 lines

drivers/pwm/pwm-sysfs.c line 116

Quoted 7 lines
drivers/pwm/pwm-sysfs.c +7 -0
+static ssize_t pwm_enable_store(struct device *dev,
+				struct device_attribute *attr,
+				const char *buf, size_t size)
+{
+	struct pwm_device *pwm = dev_to_pwm_device(dev);
+	int val;
+	int ret;

These could go on one line.

Nitpick... Ok.

Quoted 4 lines
Quoted 4 lines
drivers/pwm/pwm-sysfs.c +4 -0
+
+	ret = kstrtoint(buf, 0, &val);
+	if (ret)
+		return ret;
Quoted 8 lines

drivers/pwm/pwm-sysfs.c line 125

Quoted 5 lines
drivers/pwm/pwm-sysfs.c +5 -0
+
+	switch (val) {
+	case 0:
+		pwm_disable(pwm);
+		ret = 0;

I don't think ret can be anything other than 0 given the above validity check.

Ok.

Quoted 11 lines
Quoted 11 lines
drivers/pwm/pwm-sysfs.c +11 -0
+static ssize_t pwm_polarity_show(struct device *dev,
+				 struct device_attribute *attr,
+				 char *buf)
+{
+	const struct pwm_device *pwm = dev_to_pwm_device(dev);
+
+	return sprintf(buf, "%d\n", pwm->polarity);
+}
+
+static ssize_t pwm_polarity_store(struct device *dev,
+				  struct device_attribute *attr,
Quoted 8 lines

drivers/pwm/pwm-sysfs.c line 154

Quoted 6 lines
drivers/pwm/pwm-sysfs.c +6 -0
+				  const char *buf, size_t size)
+{
+	struct pwm_device *pwm = dev_to_pwm_device(dev);
+	enum pwm_polarity polarity;
+	int val;
+	int ret;

Could go on a single line.

Ok.

Quoted 31 lines
Quoted 31 lines
drivers/pwm/pwm-sysfs.c +31 -0
+
+	ret = kstrtoint(buf, 0, &val);
+	if (ret)
+		return ret;
+
+	switch (val) {
+	case 0:
+		polarity = PWM_POLARITY_NORMAL;
+		break;
+	case 1:
+		polarity = PWM_POLARITY_INVERSED;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	ret = pwm_set_polarity(pwm, polarity);
+
+	return ret ? : size;
+}
+
+static DEVICE_ATTR(period_ns, 0644, pwm_period_ns_show, pwm_period_ns_store);
+static DEVICE_ATTR(duty_ns, 0644, pwm_duty_ns_show, pwm_duty_ns_store);
+static DEVICE_ATTR(enable, 0644, pwm_enable_show, pwm_enable_store);
+static DEVICE_ATTR(polarity, 0644, pwm_polarity_show, pwm_polarity_store);
+
+static struct attribute *pwm_attrs[] = {
+	&dev_attr_period_ns.attr,
+	&dev_attr_duty_ns.attr,
+	&dev_attr_enable.attr,
+	&dev_attr_polarity.attr,
Quoted 8 lines

drivers/pwm/pwm-sysfs.c line 191

Quoted 6 lines
drivers/pwm/pwm-sysfs.c +6 -0
+	NULL
+};
+
+static struct attribute_group pwm_attr_group = {
+	.attrs		= pwm_attrs,
+};

static const?

Ok.

Quoted 8 lines

drivers/pwm/pwm-sysfs.c line 203

Quoted 6 lines
drivers/pwm/pwm-sysfs.c +6 -0
+static void pwm_export_release(struct device *dev)
+{
+	struct pwm_export *pwm_export = dev_to_pwm_export(dev);
+
+	kfree(pwm_export);
+}

Again, the pwm_ prefix for the variable name seems gratuitous here.

Ok.

Quoted 6 lines

drivers/pwm/pwm-sysfs.c line 207

Quoted 4 lines
drivers/pwm/pwm-sysfs.c +4 -0
+
+static int pwm_export(struct device *dev, struct pwm_device *pwm)
+{
+	struct pwm_export *pwm_export;

And here as well.

Ok.

Quoted 11 lines

drivers/pwm/pwm-sysfs.c line 242

Quoted 6 lines
drivers/pwm/pwm-sysfs.c +6 -0
+static int pwm_unexport_match(struct device *dev, void *data)
+{
+	return dev_to_pwm_device(dev) == data;
+}
+
+static int pwm_unexport(struct device *dev, struct pwm_device *pwm)

I think the current naming of variables is very confusing. It's hard to keep track of what's what. Maybe dev --> parent, or dev --> chip? Then...

Quoted 8 lines

drivers/pwm/pwm-sysfs.c

Quoted 2 lines
drivers/pwm/pwm-sysfs.c +2 -0
+{
+	struct device *export_dev;

export_dev --> dev, and...

drivers/pwm/pwm-sysfs.c

Quoted 1 line
drivers/pwm/pwm-sysfs.c +1 -0
+	struct pwm_export *pwm_export;

pwm_export --> export?

I'll work out a clearer naming for the variables.

Quoted 11 lines

drivers/pwm/pwm-sysfs.c line 253

Quoted 8 lines
drivers/pwm/pwm-sysfs.c +8 -0
+
+	if (!test_and_clear_bit(PWMF_EXPORTED, &pwm->flags))
+		return -ENODEV;
+
+	export_dev = device_find_child(dev, pwm, pwm_unexport_match);
+	pwm_export = dev_to_pwm_export(export_dev);
+	put_device(&pwm_export->dev);
+	device_unregister(&pwm_export->dev);

device_unregister() already calls put_device(), why do you do it again here?

device_find_child() does a get_device().

Quoted 7 lines
Quoted 5 lines
drivers/pwm/pwm-sysfs.c +5 -0
+	pwm_put(pwm);
+
+	return 0;
+}
+

[...]

Quoted 11 lines

drivers/pwm/pwm-sysfs.c line 341

Quoted 9 lines
drivers/pwm/pwm-sysfs.c +9 -0
+void pwmchip_sysfs_export(struct pwm_chip *chip)
+{
+	/*
+	 * If device_create() fails the pwm_chip is still usable by
+	 * the kernel its just not exported.
+	 */
+	device_create(&pwm_class, chip->dev, MKDEV(0, 0), chip,
+		      "pwmchip%d", chip->base);
+}

Maybe a warning message would still be useful in that case?

OK.

Quoted 11 lines

drivers/pwm/pwm-sysfs.c line 350

Quoted 8 lines
drivers/pwm/pwm-sysfs.c +8 -0
+void pwmchip_sysfs_unexport(struct pwm_chip *chip)
+{
+	struct device *dev;
+
+	dev = class_find_device(&pwm_class, NULL, chip, pwmchip_sysfs_match);
+	if (dev) {
+		put_device(dev);
+		device_unregister(dev);

device_unregister() already calls put_device(), why do you do it again here?

class_find_device() does a get_device().

Quoted 5 lines
Quoted 1 line
include/linux/pwm.h +0 -0

[...]

include/linux/pwm.h struct pwm_device { · line 92

Quoted 1 line
include/linux/pwm.h +1 -0
+	unsigned int		duty;	/* in nanoseconds */

I'd prefer this to be called duty_cycle.

Ok.

I'll clean this all up and post a v4 later.

Thanks,
Hartley