On Fri, Sep 13, 2019 at 03:57:43PM -0700, Guru Das Srinagesh wrote:
> From: Fenglin Wu <fengl...@codeaurora.org>
> 
> Normally, PWM channel has fixed output until software request to change
> its settings. There are some PWM devices which their outputs could be
> changed autonomously according to a predefined pattern programmed in
> hardware. Add pwm_output_type enum type to identify these two different
> PWM types and add relevant helper functions to set and get PWM output
> types and pattern.
> 
> Change-Id: Ia1f914a45ab4f4dd7be037a395eeb89d0e65a80e
> Signed-off-by: Fenglin Wu <fengl...@codeaurora.org>
> Signed-off-by: Guru Das Srinagesh <gu...@codeaurora.org>
> ---
>  drivers/pwm/core.c  | 26 ++++++++++++++++++++
>  drivers/pwm/sysfs.c | 50 ++++++++++++++++++++++++++++++++++++++
>  include/linux/pwm.h | 70 
> +++++++++++++++++++++++++++++++++++++++++++++++++++++
>  3 files changed, 146 insertions(+)

This doesn't seem right to me. Are you describing a PWM pin that's
actually driven in GPIO mode? We usually configure that using pinctrl.

Thierry

> 
> diff --git a/drivers/pwm/core.c b/drivers/pwm/core.c
> index 8edfac1..960a451 100644
> --- a/drivers/pwm/core.c
> +++ b/drivers/pwm/core.c
> @@ -282,6 +282,7 @@ int pwmchip_add_with_polarity(struct pwm_chip *chip,
>               pwm->pwm = chip->base + i;
>               pwm->hwpwm = i;
>               pwm->state.polarity = polarity;
> +             pwm->state.output_type = PWM_OUTPUT_FIXED;
>  
>               if (chip->ops->get_state)
>                       chip->ops->get_state(chip, pwm, &pwm->state);
> @@ -498,6 +499,31 @@ int pwm_apply_state(struct pwm_device *pwm, struct 
> pwm_state *state)
>                       pwm->state.polarity = state->polarity;
>               }
>  
> +             if (state->output_type != pwm->state.output_type) {
> +                     if (!pwm->chip->ops->set_output_type)
> +                             return -ENOTSUPP;
> +
> +                     err = pwm->chip->ops->set_output_type(pwm->chip, pwm,
> +                                             state->output_type);
> +                     if (err)
> +                             return err;
> +
> +                     pwm->state.output_type = state->output_type;
> +             }
> +
> +             if (state->output_pattern != pwm->state.output_pattern &&
> +                             state->output_pattern != NULL) {
> +                     if (!pwm->chip->ops->set_output_pattern)
> +                             return -ENOTSUPP;
> +
> +                     err = pwm->chip->ops->set_output_pattern(pwm->chip,
> +                                     pwm, state->output_pattern);
> +                     if (err)
> +                             return err;
> +
> +                     pwm->state.output_pattern = state->output_pattern;
> +             }
> +
>               if (state->period != pwm->state.period ||
>                   state->duty_cycle != pwm->state.duty_cycle) {
>                       err = pwm->chip->ops->config(pwm->chip, pwm,
> diff --git a/drivers/pwm/sysfs.c b/drivers/pwm/sysfs.c
> index 2389b86..ab703f2 100644
> --- a/drivers/pwm/sysfs.c
> +++ b/drivers/pwm/sysfs.c
> @@ -215,11 +215,60 @@ static ssize_t capture_show(struct device *child,
>       return sprintf(buf, "%u %u\n", result.period, result.duty_cycle);
>  }
>  
> +static ssize_t output_type_show(struct device *child,
> +                          struct device_attribute *attr,
> +                          char *buf)
> +{
> +     const struct pwm_device *pwm = child_to_pwm_device(child);
> +     const char *output_type = "unknown";
> +     struct pwm_state state;
> +
> +     pwm_get_state(pwm, &state);
> +     switch (state.output_type) {
> +     case PWM_OUTPUT_FIXED:
> +             output_type = "fixed";
> +             break;
> +     case PWM_OUTPUT_MODULATED:
> +             output_type = "modulated";
> +             break;
> +     default:
> +             break;
> +     }
> +
> +     return snprintf(buf, PAGE_SIZE, "%s\n", output_type);
> +}
> +
> +static ssize_t output_type_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 ret = -EINVAL;
> +
> +     mutex_lock(&export->lock);
> +     pwm_get_state(pwm, &state);
> +     if (sysfs_streq(buf, "fixed"))
> +             state.output_type = PWM_OUTPUT_FIXED;
> +     else if (sysfs_streq(buf, "modulated"))
> +             state.output_type = PWM_OUTPUT_MODULATED;
> +     else
> +             goto unlock;
> +
> +     ret = pwm_apply_state(pwm, &state);
> +unlock:
> +     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(output_type);
>  
>  static struct attribute *pwm_attrs[] = {
>       &dev_attr_period.attr,
> @@ -227,6 +276,7 @@ static ssize_t capture_show(struct device *child,
>       &dev_attr_enable.attr,
>       &dev_attr_polarity.attr,
>       &dev_attr_capture.attr,
> +     &dev_attr_output_type.attr,
>       NULL
>  };
>  ATTRIBUTE_GROUPS(pwm);
> diff --git a/include/linux/pwm.h b/include/linux/pwm.h
> index 24632a7..416f08e 100644
> --- a/include/linux/pwm.h
> +++ b/include/linux/pwm.h
> @@ -48,6 +48,29 @@ enum {
>       PWMF_EXPORTED = 1 << 1,
>  };
>  
> +/**
> + * enum pwm_output_type - output type of the PWM signal
> + * @PWM_OUTPUT_FIXED: PWM output is fixed until a change request
> + * @PWM_OUTPUT_MODULATED: PWM output is modulated in hardware
> + * autonomously with a predefined pattern
> + */
> +enum pwm_output_type {
> +     PWM_OUTPUT_FIXED = 1 << 0,
> +     PWM_OUTPUT_MODULATED = 1 << 1,
> +};
> +
> +/**
> + * struct pwm_output_pattern - PWM duty pattern for MODULATED duty type
> + * @duty_pattern: PWM duty cycles in the pattern for duty modulation
> + * @num_entries: number of entries in the pattern
> + * @cycles_per_duty: number of PWM period cycles an entry stays at
> + */
> +struct pwm_output_pattern {
> +     unsigned int *duty_pattern;
> +     unsigned int num_entries;
> +     unsigned int cycles_per_duty;
> +};
> +
>  /*
>   * struct pwm_state - state of a PWM channel
>   * @period: PWM period (in nanoseconds)
> @@ -59,6 +82,8 @@ struct pwm_state {
>       unsigned int period;
>       unsigned int duty_cycle;
>       enum pwm_polarity polarity;
> +     enum pwm_output_type output_type;
> +     struct pwm_output_pattern *output_pattern;
>       bool enabled;
>  };
>  
> @@ -144,6 +169,26 @@ static inline enum pwm_polarity pwm_get_polarity(const 
> struct pwm_device *pwm)
>       return state.polarity;
>  }
>  
> +static inline enum pwm_output_type pwm_get_output_type(
> +             const struct pwm_device *pwm)
> +{
> +     struct pwm_state state;
> +
> +     pwm_get_state(pwm, &state);
> +
> +     return state.output_type;
> +}
> +
> +static inline struct pwm_output_pattern *pwm_get_output_pattern(
> +                             struct pwm_device *pwm)
> +{
> +     struct pwm_state state;
> +
> +     pwm_get_state(pwm, &state);
> +
> +     return pwm->state.output_pattern ?: NULL;
> +}
> +
>  static inline void pwm_get_args(const struct pwm_device *pwm,
>                               struct pwm_args *args)
>  {
> @@ -250,6 +295,9 @@ static inline void pwm_init_state(const struct pwm_device 
> *pwm,
>   * @get_state: get the current PWM state. This function is only
>   *          called once per PWM device when the PWM chip is
>   *          registered.
> + * @get_output_type_supported: get the supported output type
> + * @set_output_type: set PWM output type
> + * @set_output_pattern: set the pattern for the modulated output
>   * @owner: helps prevent removal of modules exporting active PWMs
>   * @config: configure duty cycles and period length for this PWM
>   * @set_polarity: configure the polarity of this PWM
> @@ -265,6 +313,13 @@ struct pwm_ops {
>                    struct pwm_state *state);
>       void (*get_state)(struct pwm_chip *chip, struct pwm_device *pwm,
>                         struct pwm_state *state);
> +     int (*get_output_type_supported)(struct pwm_chip *chip,
> +                     struct pwm_device *pwm);
> +     int (*set_output_type)(struct pwm_chip *chip, struct pwm_device *pwm,
> +                     enum pwm_output_type output_type);
> +     int (*set_output_pattern)(struct pwm_chip *chip,
> +                     struct pwm_device *pwm,
> +                     struct pwm_output_pattern *output_pattern);
>       struct module *owner;
>  
>       /* Only used by legacy drivers */
> @@ -320,6 +375,21 @@ struct pwm_capture {
>  int pwm_adjust_config(struct pwm_device *pwm);
>  
>  /**
> + * pwm_output_type_support()
> + * @pwm: PWM device
> + *
> + * Returns:  output types supported by the PWM device
> + */
> +static inline int pwm_get_output_type_supported(struct pwm_device *pwm)
> +{
> +     if (pwm->chip->ops->get_output_type_supported != NULL)
> +             return pwm->chip->ops->get_output_type_supported(pwm->chip,
> +                             pwm);
> +     else
> +             return PWM_OUTPUT_FIXED;
> +}
> +
> +/**
>   * pwm_config() - change a PWM device configuration
>   * @pwm: PWM device
>   * @duty_ns: "on" time (in nanoseconds)
> -- 
> The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
> a Linux Foundation Collaborative Project
> 

Attachment: signature.asc
Description: PGP signature

Reply via email to