From: Fenglin Wu <[email protected]>

Currently, PWM core driver provides interfaces for configuring PWM
period and duty length in nanoseconds with an integer data type, so
the max period can be only set to ~2.147 seconds. Add interfaces which
can set PWM period and duty with u64 data type to remove this
limitation.

Signed-off-by: Fenglin Wu <[email protected]>
[[email protected]: Fast-forward from kernel 4.14 to 5.8]
Signed-off-by: Konrad Dybcio <[email protected]>
Signed-off-by: Martin Botka <[email protected]>
---
 drivers/pwm/core.c  | 30 +++++++++++------
 drivers/pwm/sysfs.c |  6 ++--
 include/linux/pwm.h | 79 +++++++++++++++++++++++++++++++++++++++++----
 3 files changed, 95 insertions(+), 20 deletions(-)

diff --git a/drivers/pwm/core.c b/drivers/pwm/core.c
index f3aa44106962..82411e3ccbbb 100644
--- a/drivers/pwm/core.c
+++ b/drivers/pwm/core.c
@@ -511,12 +511,12 @@ static void pwm_apply_state_debug(struct pwm_device *pwm,
            last->period > s2.period &&
            last->period <= state->period)
                dev_warn(chip->dev,
-                        ".apply didn't pick the best available period 
(requested: %u, applied: %u, possible: %u)\n",
+                        ".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,
-                        ".apply is supposed to round down period (requested: 
%u, applied: %u)\n",
+                        ".apply is supposed to round down period (requested: 
%llu, applied: %llu)\n",
                         state->period, s2.period);
 
        if (state->enabled &&
@@ -525,14 +525,14 @@ static void pwm_apply_state_debug(struct pwm_device *pwm,
            last->duty_cycle > s2.duty_cycle &&
            last->duty_cycle <= state->duty_cycle)
                dev_warn(chip->dev,
-                        ".apply didn't pick the best available duty cycle 
(requested: %u/%u, applied: %u/%u, possible: %u/%u)\n",
+                        ".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,
-                        ".apply is supposed to round down duty_cycle 
(requested: %u/%u, applied: %u/%u)\n",
+                        ".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);
 
@@ -559,7 +559,7 @@ static void pwm_apply_state_debug(struct pwm_device *pwm,
            (s1.enabled && s1.period != last->period) ||
            (s1.enabled && s1.duty_cycle != last->duty_cycle)) {
                dev_err(chip->dev,
-                       ".apply is not idempotent (ena=%d pol=%d %u/%u) -> 
(ena=%d pol=%d %u/%u)\n",
+                       ".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,
                        last->period);
@@ -655,9 +655,19 @@ int pwm_apply_state(struct pwm_device *pwm, const struct 
pwm_state *state)
 
                if (state->period != pwm->state.period ||
                    state->duty_cycle != pwm->state.duty_cycle) {
-                       err = chip->ops->config(pwm->chip, pwm,
-                                               state->duty_cycle,
-                                               state->period);
+                       if (pwm->chip->ops->config_extend) {
+                                       err = 
pwm->chip->ops->config_extend(pwm->chip,
+                                                               pwm, 
state->duty_cycle,
+                                                               state->period);
+                       } else {
+                               if (state->period > UINT_MAX)
+                                       pr_warn("period %llu duty_cycle %llu 
will be truncated\n",
+                                                               state->period,
+                                                               
state->duty_cycle);
+                               err = pwm->chip->ops->config(pwm->chip, pwm,
+                                                               
state->duty_cycle,
+                                                               state->period);
+                       }
                        if (err)
                                return err;
 
@@ -1310,8 +1320,8 @@ static void pwm_dbg_show(struct pwm_chip *chip, struct 
seq_file *s)
                if (state.enabled)
                        seq_puts(s, " enabled");
 
-               seq_printf(s, " period: %u ns", state.period);
-               seq_printf(s, " duty: %u ns", state.duty_cycle);
+               seq_printf(s, " period: %llu ns", state.period);
+               seq_printf(s, " duty: %llu ns", state.duty_cycle);
                seq_printf(s, " polarity: %s",
                           state.polarity ? "inverse" : "normal");
 
diff --git a/drivers/pwm/sysfs.c b/drivers/pwm/sysfs.c
index 4ee1e81db0bc..f9d7ebfb48f4 100644
--- a/drivers/pwm/sysfs.c
+++ b/drivers/pwm/sysfs.c
@@ -42,7 +42,7 @@ static ssize_t period_show(struct device *child,
 
        pwm_get_state(pwm, &state);
 
-       return sprintf(buf, "%u\n", state.period);
+       return sprintf(buf, "%llu\n", state.period);
 }
 
 static ssize_t period_store(struct device *child,
@@ -77,7 +77,7 @@ static ssize_t duty_cycle_show(struct device *child,
 
        pwm_get_state(pwm, &state);
 
-       return sprintf(buf, "%u\n", state.duty_cycle);
+       return sprintf(buf, "%llu\n", state.duty_cycle);
 }
 
 static ssize_t duty_cycle_store(struct device *child,
@@ -212,7 +212,7 @@ static ssize_t capture_show(struct device *child,
        if (ret)
                return ret;
 
-       return sprintf(buf, "%u %u\n", result.period, result.duty_cycle);
+       return sprintf(buf, "%llu %llu\n", result.period, result.duty_cycle);
 }
 
 static ssize_t output_type_show(struct device *child,
diff --git a/include/linux/pwm.h b/include/linux/pwm.h
index 10a102efadc4..ab235007287f 100644
--- a/include/linux/pwm.h
+++ b/include/linux/pwm.h
@@ -39,7 +39,7 @@ enum pwm_polarity {
  * current PWM hardware state.
  */
 struct pwm_args {
-       unsigned int period;
+       u64 period;
        enum pwm_polarity polarity;
 };
 
@@ -66,9 +66,9 @@ enum pwm_output_type {
  * @cycles_per_duty: number of PWM period cycles an entry stays at
  */
 struct pwm_output_pattern {
-       unsigned int *duty_pattern;
+       u64 *duty_pattern;
        unsigned int num_entries;
-       unsigned int cycles_per_duty;
+       u64 cycles_per_duty;
 };
 
 /*
@@ -79,8 +79,8 @@ struct pwm_output_pattern {
  * @enabled: PWM enabled status
  */
 struct pwm_state {
-       unsigned int period;
-       unsigned int duty_cycle;
+       u64 period;
+       u64 duty_cycle;
        enum pwm_polarity polarity;
        enum pwm_output_type output_type;
        struct pwm_output_pattern *output_pattern;
@@ -138,12 +138,30 @@ static inline void pwm_set_period(struct pwm_device *pwm, 
unsigned int period)
                pwm->state.period = period;
 }
 
+static inline void pwm_set_period_extend(struct pwm_device *pwm, u64 period)
+{
+       if (pwm)
+               pwm->state.period = period;
+}
+
 static inline unsigned int pwm_get_period(const struct pwm_device *pwm)
 {
        struct pwm_state state;
 
        pwm_get_state(pwm, &state);
 
+       if (state.period > UINT_MAX)
+               pr_warn("PWM period %llu is truncated\n", state.period);
+
+       return (unsigned int)state.period;
+}
+
+static inline u64 pwm_get_period_extend(const struct pwm_device *pwm)
+{
+       struct pwm_state state;
+
+       pwm_get_state(pwm, &state);
+
        return state.period;
 }
 
@@ -153,12 +171,30 @@ static inline void pwm_set_duty_cycle(struct pwm_device 
*pwm, unsigned int duty)
                pwm->state.duty_cycle = duty;
 }
 
+static inline void pwm_set_duty_cycle_extend(struct pwm_device *pwm, u64 duty)
+{
+       if (pwm)
+               pwm->state.duty_cycle = duty;
+}
+
 static inline unsigned int pwm_get_duty_cycle(const struct pwm_device *pwm)
 {
        struct pwm_state state;
 
        pwm_get_state(pwm, &state);
 
+       if (state.duty_cycle > UINT_MAX)
+               pr_warn("PWM duty cycle %llu is truncated\n", state.duty_cycle);
+
+       return (unsigned int)state.duty_cycle;
+}
+
+static inline u64 pwm_get_duty_cycle_extend(const struct pwm_device *pwm)
+{
+       struct pwm_state state;
+
+       pwm_get_state(pwm, &state);
+
        return state.duty_cycle;
 }
 
@@ -296,6 +332,8 @@ pwm_set_relative_duty_cycle(struct pwm_state *state, 
unsigned int duty_cycle,
  *            registered.
  * @owner: helps prevent removal of modules exporting active PWMs
  * @config: configure duty cycles and period length for this PWM
+ * @config_extend: configure duty cycles and period length for this
+ *            PWM with u64 data type
  * @set_polarity: configure the polarity of this PWM
  * @enable: enable PWM output toggling
  * @disable: disable PWM output toggling
@@ -317,6 +355,8 @@ struct pwm_ops {
        /* Only used by legacy drivers */
        int (*config)(struct pwm_chip *chip, struct pwm_device *pwm,
                      int duty_ns, int period_ns);
+       int (*config_extend)(struct pwm_chip *chip, struct pwm_device *pwm,
+                     u64 duty_ns, u64 period_ns);
        int (*set_polarity)(struct pwm_chip *chip, struct pwm_device *pwm,
                            enum pwm_polarity polarity);
        int (*enable)(struct pwm_chip *chip, struct pwm_device *pwm);
@@ -362,8 +402,8 @@ struct pwm_chip {
  * @duty_cycle: duty cycle of the PWM signal (in nanoseconds)
  */
 struct pwm_capture {
-       unsigned int period;
-       unsigned int duty_cycle;
+       u64 period;
+       u64 duty_cycle;
 };
 
 #if IS_ENABLED(CONFIG_PWM)
@@ -415,6 +455,31 @@ static inline int pwm_config(struct pwm_device *pwm, int 
duty_ns,
        return pwm_apply_state(pwm, &state);
 }
 
+/*
+ * pwm_config_extend() - change PWM period and duty length with u64 data type
+ * @pwm: PWM device
+ * @duty_ns: "on" time (in nanoseconds)
+ * @period_ns: duration (in nanoseconds) of one cycle
+ *
+ * Returns: 0 on success or a negative error code on failure.
+ */
+static inline int pwm_config_extend(struct pwm_device *pwm, u64 duty_ns,
+                            u64 period_ns)
+{
+       struct pwm_state state;
+
+       if (!pwm)
+               return -EINVAL;
+
+       pwm_get_state(pwm, &state);
+       if (state.duty_cycle == duty_ns && state.period == period_ns)
+               return 0;
+
+       state.duty_cycle = duty_ns;
+       state.period = period_ns;
+       return pwm_apply_state(pwm, &state);
+}
+
 /**
  * pwm_enable() - start a PWM output toggling
  * @pwm: PWM device
-- 
2.27.0

Reply via email to