[PATCH RFC] pwm: add sysfs interface

From: Lars Poeschel
Date: Tue Feb 19 2013 - 09:27:31 EST


From: Lars Poeschel <poeschel@xxxxxxxxxxx>

This adds a simple sysfs interface to the pwm subsystem. It is
heavily inspired by the gpio sysfs interface.

/sys/class/pwm
/export ... asks the kernel to export a PWM to userspace
/unexport ... to return a PWM to the kernel
/pwmN ... for each exported PWM #N
/duty_ns ... r/w, length of duty portion
/period_ns ... r/w, length of the pwm period
/polarity ... r/w, normal(0) or inverse(1) polarity
only created if driver supports it
/run ... r/w, write 1 to start and 0 to stop the pwm
/pwmchipN ... for each pwmchip; #N is its first PWM
/base ... (r/o) same as N
/ngpio ... (r/o) number of PWM; numbered N .. MAX_PWMS

Signed-off-by: Lars Poeschel <poeschel@xxxxxxxxxxx>
---
Documentation/pwm.txt | 46 +++++
drivers/pwm/Kconfig | 15 ++
drivers/pwm/core.c | 526 ++++++++++++++++++++++++++++++++++++++++++++++++-
include/linux/pwm.h | 27 ++-
4 files changed, 608 insertions(+), 6 deletions(-)

diff --git a/Documentation/pwm.txt b/Documentation/pwm.txt
index 7d2b4c9..b349d16 100644
--- a/Documentation/pwm.txt
+++ b/Documentation/pwm.txt
@@ -45,6 +45,52 @@ 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/. If there are pwm
+drivers loaded and these drivers successfully probed a chip, this chip
+is exported as pwmchipX . Note that a single driver can probe multiple chips.
+Inside the directory you can read these properties:
+
+base - This is the linux global start where the chips pwm channels get
+exposed.
+
+npwm - This is the number of pwm channels this chip supports.
+
+If you want to start using a pwm channel with sysfs first you have to
+export it. If you are finished with it and want to free the pwm for other
+uses, you can unexport it:
+
+export - Write the global pwm channel number to this file to start using
+the channel with sysfs.
+
+unexport - Write the global pwm channel number of the channel you are finshed
+with to this file to make the channel available for other uses.
+
+Once a pwm is exported a pwmX (X ranging from 0 to MAX_PWMS) directory appears
+with the following read/write properties inside to control the pwm:
+
+duty_ns - Write the number of nanoseconds the active portion of the pwm period
+should last to this file. This can not be longer than the period_ns.
+
+period_ns - Write the length of the pwm period in nanoseconds to this file.
+This includes the active and inactive portion of the pwm period and can not
+be smaller than duty_ns.
+
+polarity - The normal behaviour is to put out a high for the active portion of
+the pwm period. Write a 1 to this file to inverse the signal and output a low
+on the active portion. Write a 0 to switch back to the normal behaviour. The
+polarity can only be changed if the pwm is not running. This file is only
+visible if the underlying driver/device supports changing the polarity.
+
+run - Write a 1 to this file to start the pwm signal generation, write a 0 to
+stop it. Set your desired period_ns, duty_ns and polarity before starting the
+pwm.
+
+It is recommend to set the period_ns at first and duty_ns after that.
+
Implementing a PWM driver
-------------------------

diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig
index e513cd9..1c3432e 100644
--- a/drivers/pwm/Kconfig
+++ b/drivers/pwm/Kconfig
@@ -28,6 +28,21 @@ menuconfig PWM

if PWM

+config PWM_SYSFS
+ bool "/sys/class/pwm/... (sysfs interface)"
+ depends on SYSFS
+ help
+ Say Y here to add a sysfs interface for PWMs.
+
+ This exports directories and files to userspace using sysfs.
+ This way the PWM outputs of a device can easyly be used,
+ controlled and tested. For every instance of an PWM capable
+ device there is a pwmchipX directory exported to
+ /sys/class/pwm. If you want to use a PWM, you have to export
+ it to sysfs, which is done by writing the number into
+ /sys/class/pwm/export. After that /sys/class/pwm/pwmX is
+ reaady to be used.
+
config PWM_AB8500
tristate "AB8500 PWM support"
depends on AB8500_CORE && ARCH_U8500
diff --git a/drivers/pwm/core.c b/drivers/pwm/core.c
index 903138b..f7a6bf1 100644
--- a/drivers/pwm/core.c
+++ b/drivers/pwm/core.c
@@ -30,8 +30,6 @@
#include <linux/debugfs.h>
#include <linux/seq_file.h>

-#define MAX_PWMS 1024
-
/* flags in the third cell of the DT PWM specifier */
#define PWM_SPEC_POLARITY (1 << 0)

@@ -42,6 +40,497 @@ static LIST_HEAD(pwm_chips);
static DECLARE_BITMAP(allocated_pwms, MAX_PWMS);
static RADIX_TREE(pwm_tree, GFP_KERNEL);

+
+#ifdef CONFIG_PWM_SYSFS
+
+/* lock protects against unexport_pwm() being called while
+ * sysfs files are active.
+ */
+static DEFINE_MUTEX(sysfs_lock);
+static struct class pwm_class;
+static struct pwm_device *pwm_table[MAX_PWMS];
+
+/*
+ * /sys/class/pwm/pwm... only for PWMs that are exported
+ * /polarity
+ * * only visible if the underlying driver has registered a
+ * set_polarity function
+ * * always readable
+ * * may be written as "1" for inverted polarity or "0" for
+ * normal polarity
+ * * can only be written if PWM is not running
+ * /period_ns
+ * * always readable
+ * * write with desired pwm period in nanoseconds
+ * * may return with error depending on duty_ns
+ * /duty_ns
+ * * always readable
+ * * write with desired duty portion in nanoseconds
+ * * may return with error depending on period_ns
+ * /run
+ * * always readable
+ * * write with "1" to start generating pwm signal, "0" to stop it
+ */
+static ssize_t pwm_polarity_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ const struct pwm_device *pwm = dev_get_drvdata(dev);
+ ssize_t status;
+
+ mutex_lock(&sysfs_lock);
+
+ if (!test_bit(PWMF_EXPORT, &pwm->flags))
+ status = -EIO;
+ else
+ status = sprintf(buf, "%d\n", pwm->polarity);
+
+ mutex_unlock(&sysfs_lock);
+
+ return status;
+}
+
+static ssize_t pwm_polarity_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t size)
+{
+ struct pwm_device *pwm = dev_get_drvdata(dev);
+ ssize_t status;
+
+ mutex_lock(&sysfs_lock);
+
+ if (!test_bit(PWMF_EXPORT, &pwm->flags))
+ status = -EIO;
+ else {
+ long value;
+
+ status = kstrtol(buf, 0, &value);
+ if (status == 0) {
+ if (value == 0) {
+ if (pwm->polarity == PWM_POLARITY_NORMAL)
+ goto fail_unlock;
+ status = pwm_set_polarity(pwm,
+ PWM_POLARITY_NORMAL);
+ } else {
+ if (pwm->polarity == PWM_POLARITY_INVERSED)
+ goto fail_unlock;
+ status = pwm_set_polarity(pwm,
+ PWM_POLARITY_INVERSED);
+ }
+ }
+ }
+
+fail_unlock:
+ mutex_unlock(&sysfs_lock);
+ return status ? : size;
+}
+
+static const DEVICE_ATTR(polarity, 0644,
+ pwm_polarity_show, pwm_polarity_store);
+
+
+static ssize_t pwm_period_ns_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ const struct pwm_device *pwm = dev_get_drvdata(dev);
+ ssize_t status;
+
+ mutex_lock(&sysfs_lock);
+
+ if (!test_bit(PWMF_EXPORT, &pwm->flags))
+ status = -EIO;
+ else
+ status = sprintf(buf, "%d\n", pwm->period);
+
+ mutex_unlock(&sysfs_lock);
+
+ return status;
+}
+
+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_get_drvdata(dev);
+ ssize_t status;
+
+ mutex_lock(&sysfs_lock);
+
+ if (!test_bit(PWMF_EXPORT, &pwm->flags))
+ status = -EIO;
+ else {
+ long value;
+
+ status = kstrtol(buf, 0, &value);
+ if (status == 0) {
+ if (pwm->duty < 0)
+ pwm->period = value;
+ else
+ status = pwm_config(pwm, pwm->duty, value);
+ }
+ }
+
+ mutex_unlock(&sysfs_lock);
+
+ return status ? : size;
+}
+
+static const DEVICE_ATTR(period_ns, 0644,
+ pwm_period_ns_show, pwm_period_ns_store);
+
+static ssize_t pwm_duty_ns_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ const struct pwm_device *pwm = dev_get_drvdata(dev);
+ ssize_t status;
+
+ mutex_lock(&sysfs_lock);
+
+ if (!test_bit(PWMF_EXPORT, &pwm->flags))
+ status = -EIO;
+ else
+ status = sprintf(buf, "%d\n", pwm->duty);
+
+ mutex_unlock(&sysfs_lock);
+
+ return status;
+}
+
+static ssize_t pwm_duty_ns_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t size)
+{
+ struct pwm_device *pwm = dev_get_drvdata(dev);
+ ssize_t status;
+
+ mutex_lock(&sysfs_lock);
+
+ if (!test_bit(PWMF_EXPORT, &pwm->flags))
+ status = -EIO;
+ else {
+ long value;
+
+ status = kstrtol(buf, 0, &value);
+ if (status == 0) {
+ if (pwm->period <= 0)
+ pwm->duty = value;
+ else
+ status = pwm_config(pwm, value, pwm->period);
+ }
+ }
+
+ mutex_unlock(&sysfs_lock);
+
+ return status ? : size;
+}
+
+static const DEVICE_ATTR(duty_ns, 0644,
+ pwm_duty_ns_show, pwm_duty_ns_store);
+
+
+static ssize_t pwm_run_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ const struct pwm_device *pwm = dev_get_drvdata(dev);
+ ssize_t status;
+
+ mutex_lock(&sysfs_lock);
+
+ if (!test_bit(PWMF_EXPORT, &pwm->flags))
+ status = -EIO;
+ else
+ status = sprintf(buf, "%d\n",
+ !!test_bit(PWMF_ENABLED, &pwm->flags));
+
+ mutex_unlock(&sysfs_lock);
+
+ return status;
+}
+
+static ssize_t pwm_run_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t size)
+{
+ struct pwm_device *pwm = dev_get_drvdata(dev);
+ ssize_t status;
+
+ mutex_lock(&sysfs_lock);
+
+ if (!test_bit(PWMF_EXPORT, &pwm->flags))
+ status = -EIO;
+ else {
+ long value;
+
+ status = kstrtol(buf, 0, &value);
+ if (status == 0) {
+ if (value)
+ status = pwm_enable(pwm);
+ else
+ pwm_disable(pwm);
+ }
+ }
+
+ mutex_unlock(&sysfs_lock);
+
+ return status ? : size;
+}
+
+static DEVICE_ATTR(run, 0644, pwm_run_show, pwm_run_store);
+
+static const struct attribute *pwm_attrs[] = {
+ &dev_attr_period_ns.attr,
+ &dev_attr_duty_ns.attr,
+ &dev_attr_run.attr,
+ NULL,
+};
+
+static const struct attribute_group pwm_attr_group = {
+ .attrs = (struct attribute **) pwm_attrs,
+};
+
+/*
+ * /sys/class/pwm/pwmchipN/
+ * /base ... matching pwm_chip.base (N)
+ * /ngpio ... matching pwm_chip.npwm
+ */
+
+static ssize_t chip_base_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ const struct pwm_chip *chip = dev_get_drvdata(dev);
+
+ return sprintf(buf, "%d\n", chip->base);
+}
+static DEVICE_ATTR(base, 0444, chip_base_show, NULL);
+
+static ssize_t chip_npwm_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ const struct pwm_chip *chip = dev_get_drvdata(dev);
+
+ return sprintf(buf, "%u\n", chip->npwm);
+}
+static DEVICE_ATTR(npwm, 0444, chip_npwm_show, NULL);
+
+static const struct attribute *pwmchip_attrs[] = {
+ &dev_attr_base.attr,
+ &dev_attr_npwm.attr,
+ NULL,
+};
+
+static const struct attribute_group pwmchip_attr_group = {
+ .attrs = (struct attribute **) pwmchip_attrs,
+};
+
+static int pwm_export(struct pwm_device *pwm)
+{
+ struct device *dev;
+ int status;
+
+ mutex_lock(&sysfs_lock);
+
+ if (!test_bit(PWMF_REQUESTED, &pwm->flags) ||
+ test_bit(PWMF_EXPORT, &pwm->flags)) {
+ pr_debug("pwm %d unavailable (requested=%d, exported=%d)\n",
+ pwm->pwm,
+ test_bit(PWMF_REQUESTED, &pwm->flags),
+ test_bit(PWMF_EXPORT, &pwm->flags));
+
+ status = -EPERM;
+ goto fail_unlock;
+ }
+
+ dev = device_create(&pwm_class, pwm->chip->dev, MKDEV(0, 0),
+ pwm, "pwm%u", pwm->pwm);
+ if (IS_ERR(dev)) {
+ status = PTR_ERR(dev);
+ goto fail_unlock;
+ }
+
+ status = sysfs_create_group(&dev->kobj, &pwm_attr_group);
+ if (status)
+ goto fail_unregister_device;
+
+ if (pwm->chip->ops->set_polarity) {
+ status = device_create_file(dev, &dev_attr_polarity);
+ if (status)
+ goto fail_unregister_device;
+ }
+
+ set_bit(PWMF_EXPORT, &pwm->flags);
+ mutex_unlock(&sysfs_lock);
+ return 0;
+
+fail_unregister_device:
+ device_unregister(dev);
+fail_unlock:
+ mutex_unlock(&sysfs_lock);
+ return status;
+}
+
+static int match_export(struct device *dev, void *data)
+{
+ return dev_get_drvdata(dev) == data;
+}
+
+/*
+ * /sys/class/pwm/export ... write-only
+ * integer N ... number of pwm to export (full access)
+ * /sys/class/pwm/unexport ... write-only
+ * integer N ... number of pwm to unexport
+ */
+static ssize_t export_store(struct class *class,
+ struct class_attribute *attr,
+ const char *buf, size_t len)
+{
+ long pwm;
+ int status;
+ struct pwm_device *dev;
+ struct pwm_chip *chip;
+
+ status = kstrtol(buf, 0, &pwm);
+ if (status < 0)
+ goto done;
+
+ if (!pwm_is_valid(pwm) || !pwm_table[pwm]) {
+ status = -ENODEV;
+ goto done;
+ }
+ chip = pwm_table[pwm]->chip;
+ if (!chip) {
+ status = -ENODEV;
+ goto done;
+ }
+ dev = pwm_request_from_chip(chip, pwm - chip->base, "sysfs");
+ if (IS_ERR(dev)) {
+ status = -ENODEV;
+ goto done;
+ }
+ status = pwm_export(dev);
+ if (status < 0)
+ pwm_free(dev);
+done:
+ if (status)
+ pr_debug("%s: status %d\n", __func__, status);
+ return status ? : len;
+}
+
+static ssize_t unexport_store(struct class *class,
+ struct class_attribute *attr,
+ const char *buf, size_t len)
+{
+ long pwm;
+ int status;
+ struct pwm_device *dev;
+ struct device *d;
+
+ status = kstrtol(buf, 0, &pwm);
+ if (status < 0)
+ goto done;
+
+ status = -EINVAL;
+
+ /* reject bogus pwms */
+ if (!pwm_is_valid(pwm))
+ goto done;
+
+ dev = pwm_table[pwm];
+ if (dev && test_and_clear_bit(PWMF_EXPORT, &dev->flags)) {
+ d = class_find_device(&pwm_class, NULL, dev, match_export);
+ if (d)
+ device_unregister(d);
+ status = 0;
+ pwm_put(dev);
+ }
+done:
+ if (status)
+ pr_debug("%s: status %d\n", __func__, status);
+ return status ? : len;
+}
+
+static struct class_attribute pwm_class_attrs[] = {
+ __ATTR(export, 0200, NULL, export_store),
+ __ATTR(unexport, 0200, NULL, unexport_store),
+ __ATTR_NULL,
+};
+
+static struct class pwm_class = {
+ .name = "pwm",
+ .owner = THIS_MODULE,
+ .class_attrs = pwm_class_attrs,
+};
+
+static int pwmchip_export(struct pwm_chip *chip)
+{
+ int status;
+ struct device *dev;
+
+ mutex_lock(&sysfs_lock);
+ dev = device_create(&pwm_class, chip->dev, MKDEV(0, 0), chip,
+ "pwmchip%d", chip->base);
+ if (!IS_ERR(dev))
+ status = sysfs_create_group(&dev->kobj, &pwmchip_attr_group);
+ else
+ status = PTR_ERR(dev);
+
+ chip->exported = (status == 0);
+ mutex_unlock(&sysfs_lock);
+
+ if (status) {
+ unsigned i;
+
+ mutex_lock(&pwm_lock);
+ i = chip->base;
+ while (pwm_table[i]->chip == chip)
+ pwm_table[i++]->chip = NULL;
+
+ mutex_unlock(&pwm_lock);
+
+ pr_debug("%s: pwm chip status %d\n", __func__, status);
+ }
+
+ return status;
+}
+
+static void pwmchip_unexport(struct pwm_chip *chip)
+{
+ int status, i;
+ struct device *dev;
+
+ mutex_lock(&sysfs_lock);
+ dev = class_find_device(&pwm_class, NULL, chip, match_export);
+ if (dev) {
+ put_device(dev);
+ device_unregister(dev);
+ for (i = chip->base; i < chip->base + chip->npwm; i++)
+ pwm_table[i] = NULL;
+ chip->exported = 0;
+ status = 0;
+ } else
+ status = -ENODEV;
+ mutex_unlock(&sysfs_lock);
+
+ if (status)
+ pr_debug("%s: pwm chip status %d\n", __func__, status);
+}
+
+static int __init pwmlib_sysfs_init(void)
+{
+ int status;
+
+ status = class_register(&pwm_class);
+ return status;
+}
+postcore_initcall(pwmlib_sysfs_init);
+
+#else
+static inline int pwmchip_export(struct pwm_chip *chip)
+{
+ return 0;
+}
+
+static inline void pwmchip_unexport(struct pwm_chip *chip)
+{
+}
+
+#endif /* CONFIG_PWM_SYSFS */
+
+
static struct pwm_device *pwm_to_device(unsigned int pwm)
{
return radix_tree_lookup(&pwm_tree, pwm);
@@ -77,8 +566,10 @@ static void free_pwms(struct pwm_chip *chip)
for (i = 0; i < chip->npwm; i++) {
struct pwm_device *pwm = &chip->pwms[i];
radix_tree_delete(&pwm_tree, pwm->pwm);
+#ifdef CONFIG_PWM_SYSFS
+ pwm_table[i + chip->base]->chip = NULL;
+#endif
}
-
bitmap_clear(allocated_pwms, chip->base, chip->npwm);

kfree(chip->pwms);
@@ -258,6 +749,9 @@ int pwmchip_add(struct pwm_chip *chip)
pwm->chip = chip;
pwm->pwm = chip->base + i;
pwm->hwpwm = i;
+#ifdef CONFIG_PWM_SYSFS
+ pwm_table[i + chip->base] = pwm;
+#endif

radix_tree_insert(&pwm_tree, pwm->pwm, pwm);
}
@@ -272,6 +766,8 @@ int pwmchip_add(struct pwm_chip *chip)
if (IS_ENABLED(CONFIG_OF))
of_pwmchip_add(chip);

+ ret = pwmchip_export(chip);
+
out:
mutex_unlock(&pwm_lock);
return ret;
@@ -307,6 +803,7 @@ int pwmchip_remove(struct pwm_chip *chip)
of_pwmchip_remove(chip);

free_pwms(chip);
+ pwmchip_unexport(chip);

out:
mutex_unlock(&pwm_lock);
@@ -400,10 +897,19 @@ EXPORT_SYMBOL_GPL(pwm_free);
*/
int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns)
{
+ int status;
+
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);
+ status = pwm->chip->ops->config(pwm->chip, pwm, duty_ns, period_ns);
+#ifdef CONFIG_PWM_SYSFS
+ if (status == 0) {
+ pwm->period = period_ns;
+ pwm->duty = duty_ns;
+ }
+#endif
+ return status;
}
EXPORT_SYMBOL_GPL(pwm_config);

@@ -416,6 +922,8 @@ EXPORT_SYMBOL_GPL(pwm_config);
*/
int pwm_set_polarity(struct pwm_device *pwm, enum pwm_polarity polarity)
{
+ int status;
+
if (!pwm || !pwm->chip->ops)
return -EINVAL;

@@ -425,7 +933,12 @@ 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);
+ status = pwm->chip->ops->set_polarity(pwm->chip, pwm, polarity);
+#ifdef CONFIG_PWM_SYSFS
+ if (!status)
+ pwm->polarity = polarity;
+#endif
+ return status;
}
EXPORT_SYMBOL_GPL(pwm_set_polarity);

@@ -749,6 +1262,9 @@ static void pwm_dbg_show(struct pwm_chip *chip, struct seq_file *s)
if (test_bit(PWMF_ENABLED, &pwm->flags))
seq_printf(s, " enabled");

+ if (test_bit(PWMF_EXPORT, &pwm->flags))
+ seq_printf(s, " sysfs_exported");
+
seq_printf(s, "\n");
}
}
diff --git a/include/linux/pwm.h b/include/linux/pwm.h
index 6d661f3..afc22c6 100644
--- a/include/linux/pwm.h
+++ b/include/linux/pwm.h
@@ -4,10 +4,27 @@
#include <linux/err.h>
#include <linux/of.h>

+#define MAX_PWMS 1024
+
struct pwm_device;
struct seq_file;

#if IS_ENABLED(CONFIG_PWM) || IS_ENABLED(CONFIG_HAVE_PWM)
+
+/*
+ * "valid" PWM numbers are nonnegative and may be passed to
+ * setup routines like pwm_get(). only some valid numbers
+ * can successfully be requested and used.
+ *
+ * Invalid PWM numbers are useful for indicating no-such-PWM in
+ * platform data and other tables.
+ */
+
+static inline bool pwm_is_valid(int number)
+{
+ return number >= 0 && number < MAX_PWMS;
+}
+
/*
* pwm_request - request a PWM device
*/
@@ -75,7 +92,8 @@ enum pwm_polarity {

enum {
PWMF_REQUESTED = 1 << 0,
- PWMF_ENABLED = 1 << 1,
+ PWMF_ENABLED = 1 << 1, /* set running via /sys/class/pwm/pwmX/run */
+ PWMF_EXPORT = 1 << 2, /* exported via /sys/class/pwm/export */
};

struct pwm_device {
@@ -87,6 +105,10 @@ struct pwm_device {
void *chip_data;

unsigned int period; /* in nanoseconds */
+#ifdef CONFIG_PWM_SYSFS
+ unsigned int duty; /* in nanoseconds */
+ enum pwm_polarity polarity;
+#endif
};

static inline void pwm_set_period(struct pwm_device *pwm, unsigned int period)
@@ -159,6 +181,9 @@ struct pwm_chip {
struct pwm_device * (*of_xlate)(struct pwm_chip *pc,
const struct of_phandle_args *args);
unsigned int of_pwm_n_cells;
+#ifdef CONFIG_PWM_SYSFS
+ unsigned exported:1;
+#endif
};

#if IS_ENABLED(CONFIG_PWM)
--
1.7.10.4

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/