Add basic PWM modes: normal and complementary. These modes should
differentiate the single output PWM channels from two outputs PWM
channels. These modes could be set as follow:
1. PWM channels with one output per channel:
- normal mode
2. PWM channels with two outputs per channel:
- normal mode
- complementary mode
Since users could use a PWM channel with two output as one output PWM
channel, the PWM normal mode is allowed to be set for PWM channels with
two outputs; in fact PWM normal mode should be supported by all PWMs.

The PWM capabilities were implemented per PWM channel. Every PWM controller
will register a function to get PWM capabilities. If this is not explicitly
set by the driver a default function will be used to retrieve the the PWM
capabilities (in this case the PWM capabilities will contain only PWM
normal mode). This function is set in pwmchip_add_with_polarity() as a
member of "struct pwm_chip". To retrieve capabilities the pwm_get_caps()
function could be used.

Every PWM channel will have associated a mode in the PWM state. Proper
helper functions were added to get/set PWM mode. The mode could also be set
from DT via flag cells. The valid DT modes could be located in
include/dt-bindings/pwm/pwm.h. Only modes supported by PWM channel could be
set. If nothing is specified for a PWM channel, via DT, the first available
mode will be used (normally, this will be PWM normal mode).

Signed-off-by: Claudiu Beznea <claudiu.bez...@microchip.com>
---
 drivers/pwm/core.c  | 98 ++++++++++++++++++++++++++++++++++++++++++++++++++---
 drivers/pwm/sysfs.c | 56 ++++++++++++++++++++++++++++++
 include/linux/pwm.h | 87 +++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 237 insertions(+), 4 deletions(-)

diff --git a/drivers/pwm/core.c b/drivers/pwm/core.c
index 1581f6ab1b1f..16a409d452c0 100644
--- a/drivers/pwm/core.c
+++ b/drivers/pwm/core.c
@@ -136,6 +136,8 @@ struct pwm_device *
 of_pwm_xlate_with_flags(struct pwm_chip *pc, const struct of_phandle_args 
*args)
 {
        struct pwm_device *pwm;
+       struct pwm_caps caps;
+       int modebit;
 
        /* check, whether the driver supports a third cell for flags */
        if (pc->of_pwm_n_cells < 3)
@@ -152,11 +154,23 @@ of_pwm_xlate_with_flags(struct pwm_chip *pc, const struct 
of_phandle_args *args)
        if (IS_ERR(pwm))
                return pwm;
 
+       pwm_get_caps(pc, pwm, &caps);
+
        pwm->args.period = args->args[1];
        pwm->args.polarity = PWM_POLARITY_NORMAL;
+       pwm->args.mode = BIT(ffs(caps.modes) - 1);
+
+       if (args->args_count > 2) {
+               if (args->args[2] & PWM_POLARITY_INVERTED)
+                       pwm->args.polarity = PWM_POLARITY_INVERSED;
 
-       if (args->args_count > 2 && args->args[2] & PWM_POLARITY_INVERTED)
-               pwm->args.polarity = PWM_POLARITY_INVERSED;
+               for (modebit = PWM_MODE_COMPLEMENTARY_BIT;
+                    modebit < PWM_MODE_CNT; modebit++)
+                       if (args->args[2] & BIT(modebit)) {
+                               pwm->args.mode = BIT(modebit);
+                               break;
+                       }
+       }
 
        return pwm;
 }
@@ -166,6 +180,7 @@ static struct pwm_device *
 of_pwm_simple_xlate(struct pwm_chip *pc, const struct of_phandle_args *args)
 {
        struct pwm_device *pwm;
+       struct pwm_caps caps;
 
        /* sanity check driver support */
        if (pc->of_pwm_n_cells < 2)
@@ -182,7 +197,9 @@ of_pwm_simple_xlate(struct pwm_chip *pc, const struct 
of_phandle_args *args)
        if (IS_ERR(pwm))
                return pwm;
 
+       pwm_get_caps(pc, pwm, &caps);
        pwm->args.period = args->args[1];
+       pwm->args.mode = BIT(ffs(caps.modes) - 1);
 
        return pwm;
 }
@@ -250,6 +267,39 @@ static bool pwm_ops_check(const struct pwm_ops *ops)
 }
 
 /**
+ * pwm_get_caps() - get PWM capabilities
+ * @chip: PWM chip
+ * @pwm: PWM device to get the capabilities for
+ * @caps: returned capabilities
+ *
+ * Retrievers capabilities for PWM device.
+ */
+void pwm_get_caps(struct pwm_chip *chip, struct pwm_device *pwm,
+                 struct pwm_caps *caps)
+{
+       if (!chip || !pwm || !caps)
+               return;
+
+       if (chip->ops && chip->ops->get_caps)
+               pwm->chip->ops->get_caps(chip, pwm, caps);
+       else if (chip->get_default_caps)
+               chip->get_default_caps(caps);
+}
+EXPORT_SYMBOL_GPL(pwm_get_caps);
+
+static void pwmchip_get_default_caps(struct pwm_caps *caps)
+{
+       static const struct pwm_caps default_caps = {
+               .modes = PWM_MODE(NORMAL),
+       };
+
+       if (!caps)
+               return;
+
+       *caps = default_caps;
+}
+
+/**
  * pwmchip_add_with_polarity() - register a new PWM chip
  * @chip: the PWM chip to add
  * @polarity: initial polarity of PWM channels
@@ -264,7 +314,8 @@ int pwmchip_add_with_polarity(struct pwm_chip *chip,
                              enum pwm_polarity polarity)
 {
        struct pwm_device *pwm;
-       unsigned int i;
+       struct pwm_caps caps;
+       unsigned int i, j;
        int ret;
 
        if (!chip || !chip->dev || !chip->ops || !chip->npwm)
@@ -275,6 +326,8 @@ int pwmchip_add_with_polarity(struct pwm_chip *chip,
 
        mutex_lock(&pwm_lock);
 
+       chip->get_default_caps = pwmchip_get_default_caps;
+
        ret = alloc_pwms(chip->base, chip->npwm);
        if (ret < 0)
                goto out;
@@ -295,6 +348,16 @@ int pwmchip_add_with_polarity(struct pwm_chip *chip,
                pwm->hwpwm = i;
                pwm->state.polarity = polarity;
 
+               pwm_get_caps(chip, pwm, &caps);
+
+               /* Check if modes are supported. */
+               if (!pwm_caps_valid(caps)) {
+                       ret = -EINVAL;
+                       goto free;
+               }
+
+               pwm->state.mode = BIT(ffs(caps.modes) - 1);
+
                if (chip->ops->get_state)
                        chip->ops->get_state(chip, pwm, &pwm->state);
 
@@ -316,6 +379,17 @@ int pwmchip_add_with_polarity(struct pwm_chip *chip,
 out:
        mutex_unlock(&pwm_lock);
        return ret;
+
+free:
+       for (j = 0; j < i; j++) {
+               pwm = &chip->pwms[j];
+               radix_tree_delete(&pwm_tree, pwm->pwm);
+       }
+       kfree(chip->pwms);
+       chip->pwms = NULL;
+
+       mutex_unlock(&pwm_lock);
+       return ret;
 }
 EXPORT_SYMBOL_GPL(pwmchip_add_with_polarity);
 
@@ -466,10 +540,17 @@ EXPORT_SYMBOL_GPL(pwm_free);
  */
 int pwm_apply_state(struct pwm_device *pwm, struct pwm_state *state)
 {
+       struct pwm_caps caps;
        int err;
 
        if (!pwm || !state || !state->period ||
-           state->duty_cycle > state->period)
+           state->duty_cycle > state->period ||
+           !pwm_mode_valid(state->mode))
+               return -EINVAL;
+
+       /* Check if mode is supported by PWM. */
+       pwm_get_caps(pwm->chip, pwm, &caps);
+       if (!(caps.modes & state->mode))
                return -EINVAL;
 
        if (!memcmp(state, &pwm->state, sizeof(*state)))
@@ -530,6 +611,9 @@ int pwm_apply_state(struct pwm_device *pwm, struct 
pwm_state *state)
 
                        pwm->state.enabled = state->enabled;
                }
+
+               /* No mode support for non-atomic PWM. */
+               pwm->state.mode = state->mode;
        }
 
        return 0;
@@ -579,6 +663,8 @@ int pwm_adjust_config(struct pwm_device *pwm)
        pwm_get_args(pwm, &pargs);
        pwm_get_state(pwm, &state);
 
+       state.mode = pargs.mode;
+
        /*
         * If the current period is zero it means that either the PWM driver
         * does not support initial state retrieval or the PWM has not yet
@@ -767,6 +853,7 @@ struct pwm_device *pwm_get(struct device *dev, const char 
*con_id)
        unsigned int best = 0;
        struct pwm_lookup *p, *chosen = NULL;
        unsigned int match;
+       struct pwm_caps caps;
        int err;
 
        /* look up via DT first */
@@ -848,8 +935,10 @@ struct pwm_device *pwm_get(struct device *dev, const char 
*con_id)
        if (IS_ERR(pwm))
                return pwm;
 
+       pwm_get_caps(chip, pwm, &caps);
        pwm->args.period = chosen->period;
        pwm->args.polarity = chosen->polarity;
+       pwm->args.mode = BIT(ffs(caps.modes) - 1);
 
        return pwm;
 }
@@ -999,6 +1088,7 @@ static void pwm_dbg_show(struct pwm_chip *chip, struct 
seq_file *s)
                seq_printf(s, " duty: %u ns", state.duty_cycle);
                seq_printf(s, " polarity: %s",
                           state.polarity ? "inverse" : "normal");
+               seq_printf(s, " mode: %s", pwm_mode_desc(state.mode));
 
                seq_puts(s, "\n");
        }
diff --git a/drivers/pwm/sysfs.c b/drivers/pwm/sysfs.c
index 83f2b0b15712..d9eb0eb63d23 100644
--- a/drivers/pwm/sysfs.c
+++ b/drivers/pwm/sysfs.c
@@ -223,11 +223,66 @@ static ssize_t capture_show(struct device *child,
        return sprintf(buf, "%u %u\n", result.period, result.duty_cycle);
 }
 
+static ssize_t mode_show(struct device *child,
+                        struct device_attribute *attr,
+                        char *buf)
+{
+       struct pwm_device *pwm = child_to_pwm_device(child);
+       struct pwm_state state;
+       struct pwm_caps caps;
+       int modebit, len = 0;
+
+       pwm_get_state(pwm, &state);
+       pwm_get_caps(pwm->chip, pwm, &caps);
+
+       for (modebit = PWM_MODE_NORMAL_BIT; modebit < PWM_MODE_CNT; modebit++)
+               if (caps.modes & BIT(modebit)) {
+                       if (state.mode == BIT(modebit))
+                               len += scnprintf(buf + len,
+                                                PAGE_SIZE - len, "[%s] ",
+                                                pwm_mode_desc(BIT(modebit)));
+                       else
+                               len += scnprintf(buf + len,
+                                                PAGE_SIZE - len, "%s ",
+                                                pwm_mode_desc(BIT(modebit)));
+               }
+
+       len += scnprintf(buf + len, PAGE_SIZE - len, "\n");
+
+       return len;
+}
+
+static ssize_t mode_store(struct device *child,
+                         struct device_attribute *attr,
+                         const char *buf, size_t size)
+{
+       struct pwm_export *export = child_to_pwm_export(child);
+       struct pwm_device *pwm = export->pwm;
+       struct pwm_state state;
+       int modebit, ret;
+
+       for (modebit = PWM_MODE_NORMAL_BIT; modebit < PWM_MODE_CNT; modebit++)
+               if (sysfs_streq(buf, pwm_mode_desc(BIT(modebit))))
+                       break;
+
+       if (modebit == PWM_MODE_CNT)
+               return -EINVAL;
+
+       mutex_lock(&export->lock);
+       pwm_get_state(pwm, &state);
+       state.mode = BIT(modebit);
+       ret = pwm_apply_state(pwm, &state);
+       mutex_unlock(&export->lock);
+
+       return ret ? : size;
+}
+
 static DEVICE_ATTR_RW(period);
 static DEVICE_ATTR_RW(duty_cycle);
 static DEVICE_ATTR_RW(enable);
 static DEVICE_ATTR_RW(polarity);
 static DEVICE_ATTR_RO(capture);
+static DEVICE_ATTR_RW(mode);
 
 static struct attribute *pwm_attrs[] = {
        &dev_attr_period.attr,
@@ -235,6 +290,7 @@ static struct attribute *pwm_attrs[] = {
        &dev_attr_enable.attr,
        &dev_attr_polarity.attr,
        &dev_attr_capture.attr,
+       &dev_attr_mode.attr,
        NULL
 };
 ATTRIBUTE_GROUPS(pwm);
diff --git a/include/linux/pwm.h b/include/linux/pwm.h
index 56518adc31dd..e62349f48129 100644
--- a/include/linux/pwm.h
+++ b/include/linux/pwm.h
@@ -26,9 +26,32 @@ enum pwm_polarity {
 };
 
 /**
+ * PWM modes
+ * @PWM_MODE_NORMAL_BIT: PWM has one output
+ * @PWM_MODE_COMPLEMENTARY_BIT: PWM has 2 outputs with opposite polarities
+ * @PWM_MODE_CNT: PWM modes count
+ */
+enum {
+       PWM_MODE_NORMAL_BIT,
+       PWM_MODE_COMPLEMENTARY_BIT,
+       PWM_MODE_CNT,
+};
+
+#define PWM_MODE(name)         BIT(PWM_MODE_##name##_BIT)
+
+/**
+ * struct pwm_caps - PWM capabilities
+ * @modes: PWM modes
+ */
+struct pwm_caps {
+       unsigned long modes;
+};
+
+/**
  * struct pwm_args - board-dependent PWM arguments
  * @period: reference period
  * @polarity: reference polarity
+ * @mode: reference mode
  *
  * This structure describes board-dependent arguments attached to a PWM
  * device. These arguments are usually retrieved from the PWM lookup table or
@@ -41,6 +64,7 @@ enum pwm_polarity {
 struct pwm_args {
        unsigned int period;
        enum pwm_polarity polarity;
+       unsigned long mode;
 };
 
 enum {
@@ -53,12 +77,14 @@ enum {
  * @period: PWM period (in nanoseconds)
  * @duty_cycle: PWM duty cycle (in nanoseconds)
  * @polarity: PWM polarity
+ * @mode: PWM mode
  * @enabled: PWM enabled status
  */
 struct pwm_state {
        unsigned int period;
        unsigned int duty_cycle;
        enum pwm_polarity polarity;
+       unsigned long mode;
        bool enabled;
 };
 
@@ -181,6 +207,7 @@ static inline void pwm_init_state(const struct pwm_device 
*pwm,
        state->period = args.period;
        state->polarity = args.polarity;
        state->duty_cycle = 0;
+       state->mode = args.mode;
 }
 
 /**
@@ -254,6 +281,7 @@ pwm_set_relative_duty_cycle(struct pwm_state *state, 
unsigned int duty_cycle,
  * @get_state: get the current PWM state. This function is only
  *            called once per PWM device when the PWM chip is
  *            registered.
+ * @get_caps: get PWM capabilities.
  * @dbg_show: optional routine to show contents in debugfs
  * @owner: helps prevent removal of modules exporting active PWMs
  */
@@ -272,6 +300,8 @@ struct pwm_ops {
                     struct pwm_state *state);
        void (*get_state)(struct pwm_chip *chip, struct pwm_device *pwm,
                          struct pwm_state *state);
+       void (*get_caps)(struct pwm_chip *chip, struct pwm_device *pwm,
+                        struct pwm_caps *caps);
 #ifdef CONFIG_DEBUG_FS
        void (*dbg_show)(struct pwm_chip *chip, struct seq_file *s);
 #endif
@@ -287,6 +317,7 @@ struct pwm_ops {
  * @npwm: number of PWMs controlled by this chip
  * @pwms: array of PWM devices allocated by the framework
  * @of_xlate: request a PWM device given a device tree PWM specifier
+ * @get_default_caps: get default PWM capabilities
  * @of_pwm_n_cells: number of cells expected in the device tree PWM specifier
  */
 struct pwm_chip {
@@ -300,6 +331,7 @@ struct pwm_chip {
 
        struct pwm_device * (*of_xlate)(struct pwm_chip *pc,
                                        const struct of_phandle_args *args);
+       void (*get_default_caps)(struct pwm_caps *caps);
        unsigned int of_pwm_n_cells;
 };
 
@@ -424,6 +456,37 @@ static inline void pwm_disable(struct pwm_device *pwm)
        pwm_apply_state(pwm, &state);
 }
 
+static inline bool pwm_mode_valid(unsigned long mode)
+{
+       return (mode &&
+               hweight_long(mode) == 1 &&
+               ffs(mode) - 1 < PWM_MODE_CNT);
+}
+
+static inline bool pwm_caps_valid(struct pwm_caps caps)
+{
+       unsigned int last;
+
+       if (!caps.modes)
+               return false;
+
+       last = fls(caps.modes) - 1;
+       if (last >= PWM_MODE_CNT)
+               return false;
+
+       return true;
+}
+
+static inline const char * const pwm_mode_desc(unsigned long mode)
+{
+       static const char * const modes[] = { "normal", "complementary" };
+
+       if (!pwm_mode_valid(mode))
+               return "invalid";
+
+       return modes[ffs(mode) - 1];
+}
+
 /* PWM provider APIs */
 int pwm_capture(struct pwm_device *pwm, struct pwm_capture *result,
                unsigned long timeout);
@@ -438,6 +501,9 @@ struct pwm_device *pwm_request_from_chip(struct pwm_chip 
*chip,
                                         unsigned int index,
                                         const char *label);
 
+void pwm_get_caps(struct pwm_chip *chip, struct pwm_device *pwm,
+                 struct pwm_caps *caps);
+
 struct pwm_device *of_pwm_xlate_with_flags(struct pwm_chip *pc,
                const struct of_phandle_args *args);
 
@@ -498,6 +564,26 @@ static inline void pwm_disable(struct pwm_device *pwm)
 {
 }
 
+static inline bool pwm_mode_valid(unsigned long mode)
+{
+       return false;
+}
+
+static inline bool pwm_caps_valid(struct pwm_caps caps)
+{
+       return false;
+}
+
+static inline const char * const pwm_mode_desc(unsigned long mode)
+{
+       return NULL;
+}
+
+static inline void pwm_get_caps(struct pwm_chip *chip, struct pwm_device *pwm,
+                               struct pwm_caps *caps)
+{
+}
+
 static inline int pwm_set_chip_data(struct pwm_device *pwm, void *data)
 {
        return -EINVAL;
@@ -592,6 +678,7 @@ static inline void pwm_apply_args(struct pwm_device *pwm)
        state.enabled = false;
        state.polarity = pwm->args.polarity;
        state.period = pwm->args.period;
+       state.mode = pwm->args.mode;
 
        pwm_apply_state(pwm, &state);
 }
-- 
2.7.4

--
To unsubscribe from this list: send the line "unsubscribe linux-hwmon" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to