Hello,
This series adds support for atomic PWM update, or IOW, the capability to update all the parameters of a PWM device (enabled/disabled, period, duty and polarity) in one go.
It also adds support for initial PWM state retrieval (or hardware readout), which should allow smooth handover between the bootloader and Linux. For example, critical PWM users (like critical regulators controlled by a PWM) can query the current PWM state, and adapt the PWM config without having to disable/enable the PWM, or abruptly change the period/dutycyle/polarity config.
Thierry, I hope this version meets your expectations, if that's not the case, could you let me know quickly so I can adjust the implementation accordingly (I'd really like to get most of those changes in 4.7).
Best Regards,
Boris
Changes since v4: - introduce pwm_args to expose per-board/platform config - deprecate non-atomic APIs - implement non-atomic functions as wrappers around atomic ones - patch all PWM users to use the atomic API - rename the ->reset_state() hook into ->get_state() - drop most acks - rework PWM config in the pwm-regulator driver - patch sun4i and sti PWM drivers to support HW readout
Changes since v3: - rebased on pwm/for-next after pulling 4.4-rc1 - replace direct access to pwm fields by pwm_get/set_xxx() helpers, thus fixing some build errors - split changes to allow each maintainer to review/ack or take the modification through its subsystem
Changes since v2: - rebased on top of 4.3-rc2 - reintroduced pwm-regulator patches
Changes since v1: - dropped applied patches - squashed Heiko's fixes into the rockchip driver changes - made a few cosmetic changes - added kerneldoc comments - added Heiko's patch to display more information in debugfs - dropped pwm-regulator patches (should be submitted separately)
*** BLURB HERE ***
Boris Brezillon (45): pwm: rcar: make use of pwm_is_enabled() backlight: pwm_bl: remove useless call to pwm_set_period() backlight: lm3630a_bl: stop messing with the pwm->period field pwm: get rid of pwm->lock pwm: introduce the pwm_args concept pwm: use pwm_get/set_xxx() helpers where appropriate clk: pwm: use pwm_get_args() where appropriate hwmon: pwm-fan: use pwm_get_args() where appropriate misc: max77693-haptic: use pwm_get_args() where appropriate leds: pwm: use pwm_get_args() where appropriate regulator: pwm: use pwm_get_args() where appropriate fbdev: ssd1307fb: use pwm_get_args() where appropriate backlight: pwm_bl: use pwm_get_args() where appropriate pwm: keep PWM state in sync with hardware state pwm: introduce the pwm_state concept pwm: move the enabled/disabled info into pwm_state pwm: add the PWM initial state retrieval infra pwm: add the core infrastructure to allow atomic update pwm: switch to the atomic API pwm: rockchip: add initial state retrieval pwm: rockchip: avoid glitches on already running PWMs pwm: rockchip: add support for atomic update pwm: sti: add support for initial state retrieval pwm: sti: avoid glitches on already running PWMs pwm: sun4i: implement hardware readout regulator: pwm: adjust PWM config at probe time regulator: pwm: swith to the atomic PWM API regulator: pwm: properly initialize the ->state field regulator: pwm: retrieve correct voltage pwm: update documentation pwm: deprecate pwm_config(), pwm_enable() and pwm_disable() pwm: replace pwm_disable() by pwm_apply_state() clk: pwm: switch to the atomic API hwmon: pwm-fan: switch to the atomic API input: misc: max77693: switch to the atomic API input: misc: max8997: switch to the atomic PWM API input: misc: pwm-beeper: switch to the atomic PWM API leds: pwm: switch to the atomic PWM API backlight: lm3630a: switch to the atomic PWM API backlight: lp855x: switch to the atomic PWM API backlight: lp8788: switch to the atomic PWM API backlight: pwm_bl: switch to the atomic PWM API video: ssd1307fb: switch to the atomic PWM API drm: i915: switch to the atomic PWM API ARM: s3c24xx: rx1950: switch to the atomic PWM API
Heiko Stübner (1): pwm: add information about polarity, duty cycle and period to debugfs
Documentation/pwm.txt | 27 +++- arch/arm/mach-s3c24xx/mach-rx1950.c | 17 +- drivers/clk/clk-pwm.c | 36 ++++- drivers/gpu/drm/i915/intel_panel.c | 39 +++-- drivers/hwmon/pwm-fan.c | 88 ++++++---- drivers/input/misc/max77693-haptic.c | 28 +++- drivers/input/misc/max8997_haptic.c | 23 ++- drivers/input/misc/pwm-beeper.c | 46 ++++-- drivers/leds/leds-pwm.c | 15 +- drivers/pwm/core.c | 186 ++++++++++----------- drivers/pwm/pwm-clps711x.c | 2 +- drivers/pwm/pwm-crc.c | 2 +- drivers/pwm/pwm-lpc18xx-sct.c | 9 +- drivers/pwm/pwm-lpc32xx.c | 9 +- drivers/pwm/pwm-omap-dmtimer.c | 2 +- drivers/pwm/pwm-pxa.c | 2 +- drivers/pwm/pwm-rcar.c | 2 +- drivers/pwm/pwm-rockchip.c | 156 +++++++++++++++--- drivers/pwm/pwm-spear.c | 9 +- drivers/pwm/pwm-sti.c | 67 +++++++- drivers/pwm/pwm-sun4i.c | 73 ++++++--- drivers/pwm/sysfs.c | 98 ++++++++--- drivers/regulator/pwm-regulator.c | 151 ++++++++++++++--- drivers/video/backlight/lm3630a_bl.c | 15 +- drivers/video/backlight/lp855x_bl.c | 15 +- drivers/video/backlight/lp8788_bl.c | 17 +- drivers/video/backlight/pwm_bl.c | 51 +++--- drivers/video/fbdev/ssd1307fb.c | 28 +++- include/linux/pwm.h | 303 ++++++++++++++++++++++++++--------- 29 files changed, 1096 insertions(+), 420 deletions(-)
Commit 5c31252c4a86 ("pwm: Add the pwm_is_enabled() helper") introduced a new function to test whether a PWM device is enabled or not without manipulating PWM internal fields. Hiding this is necessary if we want to smoothly move to the atomic PWM config approach without impacting PWM drivers. Fix this driver to use pwm_is_enabled() instead of directly accessing the ->flags field.
Signed-off-by: Boris Brezillon boris.brezillon@free-electrons.com --- drivers/pwm/pwm-rcar.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/pwm/pwm-rcar.c b/drivers/pwm/pwm-rcar.c index 7b8ac06..1c85ecc 100644 --- a/drivers/pwm/pwm-rcar.c +++ b/drivers/pwm/pwm-rcar.c @@ -157,7 +157,7 @@ static int rcar_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, return div;
/* Let the core driver set pwm->period if disabled and duty_ns == 0 */ - if (!test_bit(PWMF_ENABLED, &pwm->flags) && !duty_ns) + if (!pwm_is_enabled(pwm) && !duty_ns) return 0;
rcar_pwm_update(rp, RCAR_PWMCR_SYNC, RCAR_PWMCR_SYNC, RCAR_PWMCR);
The PWM period will be set when calling pwm_config. Remove this useless call to pwm_set_period(), which might mess up with the internal PWM state.
Signed-off-by: Boris Brezillon boris.brezillon@free-electrons.com Acked-by: Lee Jones lee.jones@linaro.org --- drivers/video/backlight/pwm_bl.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-)
diff --git a/drivers/video/backlight/pwm_bl.c b/drivers/video/backlight/pwm_bl.c index 64f9e1b..a33a290 100644 --- a/drivers/video/backlight/pwm_bl.c +++ b/drivers/video/backlight/pwm_bl.c @@ -313,10 +313,8 @@ static int pwm_backlight_probe(struct platform_device *pdev) * via the PWM lookup table. */ pb->period = pwm_get_period(pb->pwm); - if (!pb->period && (data->pwm_period_ns > 0)) { + if (!pb->period && (data->pwm_period_ns > 0)) pb->period = data->pwm_period_ns; - pwm_set_period(pb->pwm, data->pwm_period_ns); - }
pb->lth_brightness = data->lth_brightness * (pb->period / pb->scale);
pwm->period field is not supposed to be changed by PWM users. The only ones authorized to change it are the PWM core and PWM drivers.
Signed-off-by: Boris Brezillon boris.brezillon@free-electrons.com --- drivers/video/backlight/lm3630a_bl.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/drivers/video/backlight/lm3630a_bl.c b/drivers/video/backlight/lm3630a_bl.c index 35fe482..3d16bd6 100644 --- a/drivers/video/backlight/lm3630a_bl.c +++ b/drivers/video/backlight/lm3630a_bl.c @@ -162,7 +162,7 @@ static int lm3630a_intr_config(struct lm3630a_chip *pchip)
static void lm3630a_pwm_ctrl(struct lm3630a_chip *pchip, int br, int br_max) { - unsigned int period = pwm_get_period(pchip->pwmd); + unsigned int period = pchip->pdata->pwm_period; unsigned int duty = br * period / br_max;
pwm_config(pchip->pwmd, duty, period); @@ -425,7 +425,6 @@ static int lm3630a_probe(struct i2c_client *client, return PTR_ERR(pchip->pwmd); } } - pchip->pwmd->period = pdata->pwm_period;
/* interrupt enable : irq 0 is not allowed */ pchip->irq = client->irq;
PWM devices are not protected against concurrent accesses. The lock in pwm_device might let PWM users think it is, but it's actually only protecting the enabled state.
Removing this lock should be fine as long as all PWM users are aware that accesses to the PWM device have to be serialized, which seems to be the case for all of them except the sysfs interface. Patch the sysfs code by adding a lock to the pwm_export struct and making sure it's taken for all accesses to the exported PWM device.
Signed-off-by: Boris Brezillon boris.brezillon@free-electrons.com --- drivers/pwm/core.c | 19 ++++-------------- drivers/pwm/sysfs.c | 57 ++++++++++++++++++++++++++++++++++++++++++----------- include/linux/pwm.h | 2 -- 3 files changed, 50 insertions(+), 28 deletions(-)
diff --git a/drivers/pwm/core.c b/drivers/pwm/core.c index 7831bc6..7c330ff 100644 --- a/drivers/pwm/core.c +++ b/drivers/pwm/core.c @@ -269,7 +269,6 @@ int pwmchip_add_with_polarity(struct pwm_chip *chip, pwm->pwm = chip->base + i; pwm->hwpwm = i; pwm->polarity = polarity; - mutex_init(&pwm->lock);
radix_tree_insert(&pwm_tree, pwm->pwm, pwm); } @@ -474,22 +473,16 @@ int pwm_set_polarity(struct pwm_device *pwm, enum pwm_polarity polarity) if (!pwm->chip->ops->set_polarity) return -ENOSYS;
- mutex_lock(&pwm->lock); - - if (pwm_is_enabled(pwm)) { - err = -EBUSY; - goto unlock; - } + if (pwm_is_enabled(pwm)) + return -EBUSY;
err = pwm->chip->ops->set_polarity(pwm->chip, pwm, polarity); if (err) - goto unlock; + return err;
pwm->polarity = polarity;
-unlock: - mutex_unlock(&pwm->lock); - return err; + return 0; } EXPORT_SYMBOL_GPL(pwm_set_polarity);
@@ -506,16 +499,12 @@ int pwm_enable(struct pwm_device *pwm) if (!pwm) return -EINVAL;
- mutex_lock(&pwm->lock); - if (!test_and_set_bit(PWMF_ENABLED, &pwm->flags)) { err = pwm->chip->ops->enable(pwm->chip, pwm); if (err) clear_bit(PWMF_ENABLED, &pwm->flags); }
- mutex_unlock(&pwm->lock); - return err; } EXPORT_SYMBOL_GPL(pwm_enable); diff --git a/drivers/pwm/sysfs.c b/drivers/pwm/sysfs.c index 9c90886..ab28c89 100644 --- a/drivers/pwm/sysfs.c +++ b/drivers/pwm/sysfs.c @@ -26,6 +26,7 @@ struct pwm_export { struct device child; struct pwm_device *pwm; + struct mutex lock; };
static struct pwm_export *child_to_pwm_export(struct device *child) @@ -44,16 +45,23 @@ static ssize_t period_show(struct device *child, struct device_attribute *attr, char *buf) { - const struct pwm_device *pwm = child_to_pwm_device(child); + struct pwm_export *export = child_to_pwm_export(child); + const struct pwm_device *pwm = export->pwm; + unsigned int period; + + mutex_lock(&export->lock); + period = pwm_get_period(pwm); + mutex_unlock(&export->lock);
- return sprintf(buf, "%u\n", pwm_get_period(pwm)); + return sprintf(buf, "%u\n", period); }
static ssize_t period_store(struct device *child, struct device_attribute *attr, const char *buf, size_t size) { - struct pwm_device *pwm = child_to_pwm_device(child); + struct pwm_export *export = child_to_pwm_export(child); + struct pwm_device *pwm = export->pwm; unsigned int val; int ret;
@@ -61,7 +69,9 @@ static ssize_t period_store(struct device *child, if (ret) return ret;
+ mutex_lock(&export->lock); ret = pwm_config(pwm, pwm_get_duty_cycle(pwm), val); + mutex_unlock(&export->lock);
return ret ? : size; } @@ -70,16 +80,23 @@ static ssize_t duty_cycle_show(struct device *child, struct device_attribute *attr, char *buf) { - const struct pwm_device *pwm = child_to_pwm_device(child); + struct pwm_export *export = child_to_pwm_export(child); + const struct pwm_device *pwm = export->pwm; + unsigned int duty; + + mutex_lock(&export->lock); + duty = pwm_get_duty_cycle(pwm); + mutex_unlock(&export->lock);
- return sprintf(buf, "%u\n", pwm_get_duty_cycle(pwm)); + return sprintf(buf, "%u\n", duty); }
static ssize_t duty_cycle_store(struct device *child, struct device_attribute *attr, const char *buf, size_t size) { - struct pwm_device *pwm = child_to_pwm_device(child); + struct pwm_export *export = child_to_pwm_export(child); + struct pwm_device *pwm = export->pwm; unsigned int val; int ret;
@@ -87,7 +104,9 @@ static ssize_t duty_cycle_store(struct device *child, if (ret) return ret;
+ mutex_lock(&export->lock); ret = pwm_config(pwm, val, pwm_get_period(pwm)); + mutex_unlock(&export->lock);
return ret ? : size; } @@ -96,22 +115,30 @@ static ssize_t enable_show(struct device *child, struct device_attribute *attr, char *buf) { - const struct pwm_device *pwm = child_to_pwm_device(child); + struct pwm_export *export = child_to_pwm_export(child); + const struct pwm_device *pwm = export->pwm; + bool enabled; + + mutex_lock(&export->lock); + enabled = pwm_is_enabled(pwm); + mutex_unlock(&export->lock);
- return sprintf(buf, "%d\n", pwm_is_enabled(pwm)); + return sprintf(buf, "%d\n", enabled); }
static ssize_t enable_store(struct device *child, struct device_attribute *attr, const char *buf, size_t size) { - struct pwm_device *pwm = child_to_pwm_device(child); + struct pwm_export *export = child_to_pwm_export(child); + struct pwm_device *pwm = export->pwm; int val, ret;
ret = kstrtoint(buf, 0, &val); if (ret) return ret;
+ mutex_lock(&export->lock); switch (val) { case 0: pwm_disable(pwm); @@ -123,6 +150,7 @@ static ssize_t enable_store(struct device *child, ret = -EINVAL; break; } + mutex_unlock(&export->lock);
return ret ? : size; } @@ -131,9 +159,11 @@ static ssize_t polarity_show(struct device *child, struct device_attribute *attr, char *buf) { - const struct pwm_device *pwm = child_to_pwm_device(child); + struct pwm_export *export = child_to_pwm_export(child); + const struct pwm_device *pwm = export->pwm; const char *polarity = "unknown";
+ mutex_lock(&export->lock); switch (pwm_get_polarity(pwm)) { case PWM_POLARITY_NORMAL: polarity = "normal"; @@ -143,6 +173,7 @@ static ssize_t polarity_show(struct device *child, polarity = "inversed"; break; } + mutex_unlock(&export->lock);
return sprintf(buf, "%s\n", polarity); } @@ -151,7 +182,8 @@ static ssize_t polarity_store(struct device *child, struct device_attribute *attr, const char *buf, size_t size) { - struct pwm_device *pwm = child_to_pwm_device(child); + struct pwm_export *export = child_to_pwm_export(child); + struct pwm_device *pwm = export->pwm; enum pwm_polarity polarity; int ret;
@@ -162,7 +194,9 @@ static ssize_t polarity_store(struct device *child, else return -EINVAL;
+ mutex_lock(&export->lock); ret = pwm_set_polarity(pwm, polarity); + mutex_unlock(&export->lock);
return ret ? : size; } @@ -203,6 +237,7 @@ static int pwm_export_child(struct device *parent, struct pwm_device *pwm) }
export->pwm = pwm; + mutex_init(&export->lock);
export->child.release = pwm_export_release; export->child.parent = parent; diff --git a/include/linux/pwm.h b/include/linux/pwm.h index cfc3ed4..6555f01 100644 --- a/include/linux/pwm.h +++ b/include/linux/pwm.h @@ -88,7 +88,6 @@ enum { * @pwm: global index of the PWM device * @chip: PWM chip providing this PWM device * @chip_data: chip-private data associated with the PWM device - * @lock: used to serialize accesses to the PWM device where necessary * @period: period of the PWM signal (in nanoseconds) * @duty_cycle: duty cycle of the PWM signal (in nanoseconds) * @polarity: polarity of the PWM signal @@ -100,7 +99,6 @@ struct pwm_device { unsigned int pwm; struct pwm_chip *chip; void *chip_data; - struct mutex lock;
unsigned int period; unsigned int duty_cycle;
Currently the PWM core mixes the current PWM state with the per-platform reference config (specified through the PWM lookup table, DT definition or directly hardcoded in PWM drivers).
Create a pwm_args struct to store this reference config, so that PWM users can differentiate the current config from the reference one.
Patch all places where pwm->args should be initialized. We keep the pwm_set_polarity/period() calls until all PWM users are patched to use pwm_args instead of pwm_get_period/polarity().
Signed-off-by: Boris Brezillon boris.brezillon@free-electrons.com --- drivers/pwm/core.c | 13 +++++++++---- drivers/pwm/pwm-clps711x.c | 3 ++- drivers/pwm/pwm-pxa.c | 1 + include/linux/pwm.h | 26 ++++++++++++++++++++++++++ 4 files changed, 38 insertions(+), 5 deletions(-)
diff --git a/drivers/pwm/core.c b/drivers/pwm/core.c index 7c330ff..cd55d61 100644 --- a/drivers/pwm/core.c +++ b/drivers/pwm/core.c @@ -146,12 +146,14 @@ of_pwm_xlate_with_flags(struct pwm_chip *pc, const struct of_phandle_args *args) if (IS_ERR(pwm)) return pwm;
- pwm_set_period(pwm, args->args[1]); + pwm->args.period = args->args[1]; + pwm_set_period(pwm, pwm->args.period);
if (args->args[2] & PWM_POLARITY_INVERTED) - pwm_set_polarity(pwm, PWM_POLARITY_INVERSED); + pwm->args.polarity = PWM_POLARITY_INVERSED; else - pwm_set_polarity(pwm, PWM_POLARITY_NORMAL); + pwm->args.polarity = PWM_POLARITY_NORMAL; + pwm_set_polarity(pwm, pwm->args.polarity);
return pwm; } @@ -172,7 +174,8 @@ of_pwm_simple_xlate(struct pwm_chip *pc, const struct of_phandle_args *args) if (IS_ERR(pwm)) return pwm;
- pwm_set_period(pwm, args->args[1]); + pwm->args.period = args->args[1]; + pwm_set_period(pwm, pwm->args.period);
return pwm; } @@ -740,6 +743,8 @@ struct pwm_device *pwm_get(struct device *dev, const char *con_id) if (IS_ERR(pwm)) goto out;
+ pwm->args.period = chosen->period; + pwm->args.polarity = chosen->polarity; pwm_set_period(pwm, chosen->period); pwm_set_polarity(pwm, chosen->polarity);
diff --git a/drivers/pwm/pwm-clps711x.c b/drivers/pwm/pwm-clps711x.c index a80c108..807a48d 100644 --- a/drivers/pwm/pwm-clps711x.c +++ b/drivers/pwm/pwm-clps711x.c @@ -60,7 +60,8 @@ static int clps711x_pwm_request(struct pwm_chip *chip, struct pwm_device *pwm) return -EINVAL;
/* Store constant period value */ - pwm_set_period(pwm, DIV_ROUND_CLOSEST(NSEC_PER_SEC, freq)); + pwm->args.period = DIV_ROUND_CLOSEST(NSEC_PER_SEC, freq); + pwm_set_period(pwm, pwm->args.period);
return 0; } diff --git a/drivers/pwm/pwm-pxa.c b/drivers/pwm/pwm-pxa.c index cb2f702..3fcc886 100644 --- a/drivers/pwm/pwm-pxa.c +++ b/drivers/pwm/pwm-pxa.c @@ -160,6 +160,7 @@ pxa_pwm_of_xlate(struct pwm_chip *pc, const struct of_phandle_args *args) if (IS_ERR(pwm)) return pwm;
+ pwm->args.period = args->args[0]; pwm_set_period(pwm, args->args[0]);
return pwm; diff --git a/include/linux/pwm.h b/include/linux/pwm.h index 6555f01..ed65354 100644 --- a/include/linux/pwm.h +++ b/include/linux/pwm.h @@ -74,6 +74,23 @@ enum pwm_polarity { PWM_POLARITY_INVERSED, };
+/** + * struct pwm_args - PWM arguments + * @period: reference period + * @polarity: reference polarity + * + * This structure describe board-dependent arguments attached to a PWM + * device. Those arguments are usually retrieved from the PWM lookup table or + * DT definition. + * This should not be confused with the PWM state: PWM args not representing + * the current PWM state, but the configuration the PWM user plan to use + * on this PWM device. + */ +struct pwm_args { + unsigned int period; + enum pwm_polarity polarity; +}; + enum { PWMF_REQUESTED = 1 << 0, PWMF_ENABLED = 1 << 1, @@ -91,6 +108,7 @@ enum { * @period: period of the PWM signal (in nanoseconds) * @duty_cycle: duty cycle of the PWM signal (in nanoseconds) * @polarity: polarity of the PWM signal + * @args: PWM arguments */ struct pwm_device { const char *label; @@ -103,6 +121,8 @@ struct pwm_device { unsigned int period; unsigned int duty_cycle; enum pwm_polarity polarity; + + struct pwm_args args; };
static inline bool pwm_is_enabled(const struct pwm_device *pwm) @@ -142,6 +162,12 @@ static inline enum pwm_polarity pwm_get_polarity(const struct pwm_device *pwm) return pwm ? pwm->polarity : PWM_POLARITY_NORMAL; }
+static inline void pwm_get_args(const struct pwm_device *pwm, + struct pwm_args *args) +{ + *args = pwm->args; +} + /** * struct pwm_ops - PWM controller operations * @request: optional hook for requesting a PWM
On 03/30, Boris Brezillon wrote:
@@ -74,6 +74,23 @@ enum pwm_polarity { PWM_POLARITY_INVERSED, };
+/**
- struct pwm_args - PWM arguments
- @period: reference period
- @polarity: reference polarity
- This structure describe board-dependent arguments attached to a PWM
s/describe/describes/
- device. Those arguments are usually retrieved from the PWM lookup table or
- DT definition.
- This should not be confused with the PWM state: PWM args not representing
s/not representing/don't represent/ ?
- the current PWM state, but the configuration the PWM user plan to use
s/plan/plans/
- on this PWM device.
- */
+struct pwm_args {
- unsigned int period;
- enum pwm_polarity polarity;
+};
Use pwm_get/set_xxx() helpers instead of directly accessing the pwm->xxx field. Doing that will ease adaptation of the PWM framework to support atomic update.
Signed-off-by: Boris Brezillon boris.brezillon@free-electrons.com --- Patch generated with the following coccinelle script:
--->8--- virtual patch
@@ struct pwm_device *p; expression e; @@ ( -(p)->polarity = e; +pwm_set_polarity(p, e); | -(p)->polarity +pwm_get_polarity(p) | -(p)->period = e; +pwm_set_period(p, e); | -(p)->period +pwm_get_period(p) | -(p)->duty_cycle = e; +pwm_set_duty_cycle(p, e); | -(p)->duty_cycle +pwm_get_duty_cycle(p) ) --->8--- --- drivers/pwm/pwm-crc.c | 2 +- drivers/pwm/pwm-lpc18xx-sct.c | 2 +- drivers/pwm/pwm-omap-dmtimer.c | 2 +- drivers/pwm/pwm-sun4i.c | 3 ++- 4 files changed, 5 insertions(+), 4 deletions(-)
diff --git a/drivers/pwm/pwm-crc.c b/drivers/pwm/pwm-crc.c index 7101c70..bd0ebd0 100644 --- a/drivers/pwm/pwm-crc.c +++ b/drivers/pwm/pwm-crc.c @@ -75,7 +75,7 @@ static int crc_pwm_config(struct pwm_chip *c, struct pwm_device *pwm, return -EINVAL; }
- if (pwm->period != period_ns) { + if (pwm_get_period(pwm) != period_ns) { int clk_div;
/* changing the clk divisor, need to disable fisrt */ diff --git a/drivers/pwm/pwm-lpc18xx-sct.c b/drivers/pwm/pwm-lpc18xx-sct.c index 9861fed..19dc64c 100644 --- a/drivers/pwm/pwm-lpc18xx-sct.c +++ b/drivers/pwm/pwm-lpc18xx-sct.c @@ -249,7 +249,7 @@ static int lpc18xx_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) LPC18XX_PWM_EVSTATEMSK(lpc18xx_data->duty_event), LPC18XX_PWM_EVSTATEMSK_ALL);
- if (pwm->polarity == PWM_POLARITY_NORMAL) { + if (pwm_get_polarity(pwm) == PWM_POLARITY_NORMAL) { set_event = lpc18xx_pwm->period_event; clear_event = lpc18xx_data->duty_event; res_action = LPC18XX_PWM_RES_SET; diff --git a/drivers/pwm/pwm-omap-dmtimer.c b/drivers/pwm/pwm-omap-dmtimer.c index b7e6ecb..3e95090 100644 --- a/drivers/pwm/pwm-omap-dmtimer.c +++ b/drivers/pwm/pwm-omap-dmtimer.c @@ -192,7 +192,7 @@ static int pwm_omap_dmtimer_config(struct pwm_chip *chip, load_value, load_value, match_value, match_value);
omap->pdata->set_pwm(omap->dm_timer, - pwm->polarity == PWM_POLARITY_INVERSED, + pwm_get_polarity(pwm) == PWM_POLARITY_INVERSED, true, PWM_OMAP_DMTIMER_TRIGGER_OVERFLOW_AND_COMPARE);
diff --git a/drivers/pwm/pwm-sun4i.c b/drivers/pwm/pwm-sun4i.c index 67af9f6..03a99a5 100644 --- a/drivers/pwm/pwm-sun4i.c +++ b/drivers/pwm/pwm-sun4i.c @@ -354,7 +354,8 @@ static int sun4i_pwm_probe(struct platform_device *pdev) val = sun4i_pwm_readl(pwm, PWM_CTRL_REG); for (i = 0; i < pwm->chip.npwm; i++) if (!(val & BIT_CH(PWM_ACT_STATE, i))) - pwm->chip.pwms[i].polarity = PWM_POLARITY_INVERSED; + pwm_set_polarity(&pwm->chip.pwms[i], + PWM_POLARITY_INVERSED); clk_disable_unprepare(pwm->clk);
return 0;
The PWM framework has clarified the concept of reference PWM config (the platform dependent config retrieved from the DT or the PWM lookup table) and real PWM state.
Use pwm_get_args() when the PWM user wants to retrieve this reference config and not the current state.
This is part of the rework allowing the PWM framework to support hardware readout and expose real PWM state even when the PWM has just been requested (before the user calls pwm_config/enable/disable()).
Signed-off-by: Boris Brezillon boris.brezillon@free-electrons.com --- drivers/clk/clk-pwm.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-)
diff --git a/drivers/clk/clk-pwm.c b/drivers/clk/clk-pwm.c index 8830458..ebcd738 100644 --- a/drivers/clk/clk-pwm.c +++ b/drivers/clk/clk-pwm.c @@ -56,6 +56,7 @@ static const struct clk_ops clk_pwm_ops = { static int clk_pwm_probe(struct platform_device *pdev) { struct device_node *node = pdev->dev.of_node; + struct pwm_args pargs = { }; struct clk_init_data init; struct clk_pwm *clk_pwm; struct pwm_device *pwm; @@ -71,22 +72,23 @@ static int clk_pwm_probe(struct platform_device *pdev) if (IS_ERR(pwm)) return PTR_ERR(pwm);
- if (!pwm->period) { + pwm_get_args(pwm, &pargs); + if (!pargs.period) { dev_err(&pdev->dev, "invalid PWM period\n"); return -EINVAL; }
if (of_property_read_u32(node, "clock-frequency", &clk_pwm->fixed_rate)) - clk_pwm->fixed_rate = NSEC_PER_SEC / pwm->period; + clk_pwm->fixed_rate = NSEC_PER_SEC / pargs.period;
- if (pwm->period != NSEC_PER_SEC / clk_pwm->fixed_rate && - pwm->period != DIV_ROUND_UP(NSEC_PER_SEC, clk_pwm->fixed_rate)) { + if (pargs.period != NSEC_PER_SEC / clk_pwm->fixed_rate && + pargs.period != DIV_ROUND_UP(NSEC_PER_SEC, clk_pwm->fixed_rate)) { dev_err(&pdev->dev, "clock-frequency does not match PWM period\n"); return -EINVAL; }
- ret = pwm_config(pwm, (pwm->period + 1) >> 1, pwm->period); + ret = pwm_config(pwm, (pargs.period + 1) >> 1, pargs.period); if (ret < 0) return ret;
On 03/30, Boris Brezillon wrote:
The PWM framework has clarified the concept of reference PWM config (the platform dependent config retrieved from the DT or the PWM lookup table) and real PWM state.
Use pwm_get_args() when the PWM user wants to retrieve this reference config and not the current state.
This is part of the rework allowing the PWM framework to support hardware readout and expose real PWM state even when the PWM has just been requested (before the user calls pwm_config/enable/disable()).
Signed-off-by: Boris Brezillon boris.brezillon@free-electrons.com
Acked-by: Stephen Boyd sboyd@codeaurora.org
The PWM framework has clarified the concept of reference PWM config (the platform dependent config retrieved from the DT or the PWM lookup table) and real PWM state.
Use pwm_get_args() when the PWM user wants to retrieve this reference config and not the current state.
This is part of the rework allowing the PWM framework to support hardware readout and expose real PWM state even when the PWM has just been requested (before the user calls pwm_config/enable/disable()).
Signed-off-by: Boris Brezillon boris.brezillon@free-electrons.com --- drivers/hwmon/pwm-fan.c | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-)
diff --git a/drivers/hwmon/pwm-fan.c b/drivers/hwmon/pwm-fan.c index 3e23003..82c5656 100644 --- a/drivers/hwmon/pwm-fan.c +++ b/drivers/hwmon/pwm-fan.c @@ -40,15 +40,18 @@ struct pwm_fan_ctx {
static int __set_pwm(struct pwm_fan_ctx *ctx, unsigned long pwm) { + struct pwm_args pargs = { }; unsigned long duty; int ret = 0;
+ pwm_get_args(ctx->pwm, &pargs); + mutex_lock(&ctx->lock); if (ctx->pwm_value == pwm) goto exit_set_pwm_err;
- duty = DIV_ROUND_UP(pwm * (ctx->pwm->period - 1), MAX_PWM); - ret = pwm_config(ctx->pwm, duty, ctx->pwm->period); + duty = DIV_ROUND_UP(pwm * (pargs.period - 1), MAX_PWM); + ret = pwm_config(ctx->pwm, duty, pargs.period); if (ret) goto exit_set_pwm_err;
@@ -214,6 +217,7 @@ static int pwm_fan_of_get_cooling_data(struct device *dev, static int pwm_fan_probe(struct platform_device *pdev) { struct thermal_cooling_device *cdev; + struct pwm_args pargs = { }; struct pwm_fan_ctx *ctx; struct device *hwmon; int duty_cycle; @@ -234,10 +238,11 @@ static int pwm_fan_probe(struct platform_device *pdev) platform_set_drvdata(pdev, ctx);
/* Set duty cycle to maximum allowed */ - duty_cycle = ctx->pwm->period - 1; + pwm_get_args(ctx->pwm, &pargs); + duty_cycle = pargs.period - 1; ctx->pwm_value = MAX_PWM;
- ret = pwm_config(ctx->pwm, duty_cycle, ctx->pwm->period); + ret = pwm_config(ctx->pwm, duty_cycle, pargs.period); if (ret) { dev_err(&pdev->dev, "Failed to configure PWM\n"); return ret; @@ -303,14 +308,16 @@ static int pwm_fan_suspend(struct device *dev) static int pwm_fan_resume(struct device *dev) { struct pwm_fan_ctx *ctx = dev_get_drvdata(dev); + struct pwm_args pargs = { }; unsigned long duty; int ret;
if (ctx->pwm_value == 0) return 0;
- duty = DIV_ROUND_UP(ctx->pwm_value * (ctx->pwm->period - 1), MAX_PWM); - ret = pwm_config(ctx->pwm, duty, ctx->pwm->period); + pwm_get_args(ctx->pwm, &pargs); + duty = DIV_ROUND_UP(ctx->pwm_value * (pargs.period - 1), MAX_PWM); + ret = pwm_config(ctx->pwm, duty, pargs.period); if (ret) return ret; return pwm_enable(ctx->pwm);
On Wed, Mar 30, 2016 at 10:03:31PM +0200, Boris Brezillon wrote:
The PWM framework has clarified the concept of reference PWM config (the platform dependent config retrieved from the DT or the PWM lookup table) and real PWM state.
Use pwm_get_args() when the PWM user wants to retrieve this reference config and not the current state.
This is part of the rework allowing the PWM framework to support hardware readout and expose real PWM state even when the PWM has just been requested (before the user calls pwm_config/enable/disable()).
Signed-off-by: Boris Brezillon boris.brezillon@free-electrons.com
drivers/hwmon/pwm-fan.c | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-)
diff --git a/drivers/hwmon/pwm-fan.c b/drivers/hwmon/pwm-fan.c index 3e23003..82c5656 100644 --- a/drivers/hwmon/pwm-fan.c +++ b/drivers/hwmon/pwm-fan.c @@ -40,15 +40,18 @@ struct pwm_fan_ctx {
static int __set_pwm(struct pwm_fan_ctx *ctx, unsigned long pwm) {
- struct pwm_args pargs = { };
Hi Boris,
I guess I am missing some context; sorry for that. Unfortunately, I did not easily find an explanation, so please bear with me.
Two questions: Why do we need a local copy of struct pwm_args instead of a pointer to it ? If it can change while being used, isn't it inconsistent anyway ?
Also, assuming the local copy is necessary, why initialize pargs ? After all, pwm_get_args() just overwrites it.
Thanks, Guenter
unsigned long duty; int ret = 0;
- pwm_get_args(ctx->pwm, &pargs);
- mutex_lock(&ctx->lock); if (ctx->pwm_value == pwm) goto exit_set_pwm_err;
- duty = DIV_ROUND_UP(pwm * (ctx->pwm->period - 1), MAX_PWM);
- ret = pwm_config(ctx->pwm, duty, ctx->pwm->period);
- duty = DIV_ROUND_UP(pwm * (pargs.period - 1), MAX_PWM);
- ret = pwm_config(ctx->pwm, duty, pargs.period); if (ret) goto exit_set_pwm_err;
@@ -214,6 +217,7 @@ static int pwm_fan_of_get_cooling_data(struct device *dev, static int pwm_fan_probe(struct platform_device *pdev) { struct thermal_cooling_device *cdev;
- struct pwm_args pargs = { }; struct pwm_fan_ctx *ctx; struct device *hwmon; int duty_cycle;
@@ -234,10 +238,11 @@ static int pwm_fan_probe(struct platform_device *pdev) platform_set_drvdata(pdev, ctx);
/* Set duty cycle to maximum allowed */
- duty_cycle = ctx->pwm->period - 1;
- pwm_get_args(ctx->pwm, &pargs);
- duty_cycle = pargs.period - 1; ctx->pwm_value = MAX_PWM;
- ret = pwm_config(ctx->pwm, duty_cycle, ctx->pwm->period);
- ret = pwm_config(ctx->pwm, duty_cycle, pargs.period); if (ret) { dev_err(&pdev->dev, "Failed to configure PWM\n"); return ret;
@@ -303,14 +308,16 @@ static int pwm_fan_suspend(struct device *dev) static int pwm_fan_resume(struct device *dev) { struct pwm_fan_ctx *ctx = dev_get_drvdata(dev);
struct pwm_args pargs = { }; unsigned long duty; int ret;
if (ctx->pwm_value == 0) return 0;
- duty = DIV_ROUND_UP(ctx->pwm_value * (ctx->pwm->period - 1), MAX_PWM);
- ret = pwm_config(ctx->pwm, duty, ctx->pwm->period);
- pwm_get_args(ctx->pwm, &pargs);
- duty = DIV_ROUND_UP(ctx->pwm_value * (pargs.period - 1), MAX_PWM);
- ret = pwm_config(ctx->pwm, duty, pargs.period); if (ret) return ret; return pwm_enable(ctx->pwm);
-- 2.5.0
The PWM framework has clarified the concept of reference PWM config (the platform dependent config retrieved from the DT or the PWM lookup table) and real PWM state.
Use pwm_get_args() when the PWM user wants to retrieve this reference config and not the current state.
This is part of the rework allowing the PWM framework to support hardware readout and expose real PWM state even when the PWM has just been requested (before the user calls pwm_config/enable/disable()).
Signed-off-by: Boris Brezillon boris.brezillon@free-electrons.com --- drivers/input/misc/max77693-haptic.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-)
diff --git a/drivers/input/misc/max77693-haptic.c b/drivers/input/misc/max77693-haptic.c index 6d96bff..cf6aac0 100644 --- a/drivers/input/misc/max77693-haptic.c +++ b/drivers/input/misc/max77693-haptic.c @@ -70,10 +70,13 @@ struct max77693_haptic {
static int max77693_haptic_set_duty_cycle(struct max77693_haptic *haptic) { - int delta = (haptic->pwm_dev->period + haptic->pwm_duty) / 2; + struct pwm_args pargs = { }; + int delta; int error;
- error = pwm_config(haptic->pwm_dev, delta, haptic->pwm_dev->period); + pwm_get_args(haptic->pwm_dev, &pargs); + delta = (pargs.period + haptic->pwm_duty) / 2; + error = pwm_config(haptic->pwm_dev, delta, pargs.period); if (error) { dev_err(haptic->dev, "failed to configure pwm: %d\n", error); return error; @@ -234,6 +237,7 @@ static int max77693_haptic_play_effect(struct input_dev *dev, void *data, struct ff_effect *effect) { struct max77693_haptic *haptic = input_get_drvdata(dev); + struct pwm_args pargs = { }; u64 period_mag_multi;
haptic->magnitude = effect->u.rumble.strong_magnitude; @@ -245,7 +249,8 @@ static int max77693_haptic_play_effect(struct input_dev *dev, void *data, * The formula to convert magnitude to pwm_duty as follows: * - pwm_duty = (magnitude * pwm_period) / MAX_MAGNITUDE(0xFFFF) */ - period_mag_multi = (u64)haptic->pwm_dev->period * haptic->magnitude; + pwm_get_args(haptic->pwm_dev, &pargs); + period_mag_multi = (u64)pargs.period * haptic->magnitude; haptic->pwm_duty = (unsigned int)(period_mag_multi >> MAX_MAGNITUDE_SHIFT);
The PWM framework has clarified the concept of reference PWM config (the platform dependent config retrieved from the DT or the PWM lookup table) and real PWM state.
Use pwm_get_args() when the PWM user wants to retrieve this reference config and not the current state.
This is part of the rework allowing the PWM framework to support hardware readout and expose real PWM state even when the PWM has just been requested (before the user calls pwm_config/enable/disable()).
Signed-off-by: Boris Brezillon boris.brezillon@free-electrons.com --- drivers/leds/leds-pwm.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/drivers/leds/leds-pwm.c b/drivers/leds/leds-pwm.c index 4783bac..b48231c 100644 --- a/drivers/leds/leds-pwm.c +++ b/drivers/leds/leds-pwm.c @@ -91,6 +91,7 @@ static int led_pwm_add(struct device *dev, struct led_pwm_priv *priv, struct led_pwm *led, struct device_node *child) { struct led_pwm_data *led_data = &priv->leds[priv->num_leds]; + struct pwm_args pargs = { }; int ret;
led_data->active_low = led->active_low; @@ -117,7 +118,8 @@ static int led_pwm_add(struct device *dev, struct led_pwm_priv *priv, else led_data->cdev.brightness_set_blocking = led_pwm_set_blocking;
- led_data->period = pwm_get_period(led_data->pwm); + pwm_get_args(led_data->pwm, &pargs); + led_data->period = pargs.period; if (!led_data->period && (led->pwm_period_ns > 0)) led_data->period = led->pwm_period_ns;
The PWM framework has clarified the concept of reference PWM config (the platform dependent config retrieved from the DT or the PWM lookup table) and real PWM state.
Use pwm_get_args() when the PWM user wants to retrieve this reference config and not the current state.
This is part of the rework allowing the PWM framework to support hardware readout and expose real PWM state even when the PWM has just been requested (before the user calls pwm_config/enable/disable()).
Signed-off-by: Boris Brezillon boris.brezillon@free-electrons.com --- drivers/regulator/pwm-regulator.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-)
diff --git a/drivers/regulator/pwm-regulator.c b/drivers/regulator/pwm-regulator.c index 4689d62..9154c47 100644 --- a/drivers/regulator/pwm-regulator.c +++ b/drivers/regulator/pwm-regulator.c @@ -59,16 +59,16 @@ static int pwm_regulator_set_voltage_sel(struct regulator_dev *rdev, unsigned selector) { struct pwm_regulator_data *drvdata = rdev_get_drvdata(rdev); - unsigned int pwm_reg_period; + struct pwm_args pargs = { }; int dutycycle; int ret;
- pwm_reg_period = pwm_get_period(drvdata->pwm); + pwm_get_args(drvdata->pwm, &pargs);
- dutycycle = (pwm_reg_period * + dutycycle = (pargs.period * drvdata->duty_cycle_table[selector].dutycycle) / 100;
- ret = pwm_config(drvdata->pwm, dutycycle, pwm_reg_period); + ret = pwm_config(drvdata->pwm, dutycycle, pargs.period); if (ret) { dev_err(&rdev->dev, "Failed to configure PWM\n"); return ret; @@ -138,13 +138,15 @@ static int pwm_regulator_set_voltage(struct regulator_dev *rdev, { struct pwm_regulator_data *drvdata = rdev_get_drvdata(rdev); unsigned int ramp_delay = rdev->constraints->ramp_delay; - unsigned int period = pwm_get_period(drvdata->pwm); + struct pwm_args pargs = { }; int duty_cycle; int ret;
+ pwm_get_args(drvdata->pwm, &pargs); duty_cycle = pwm_voltage_to_duty_cycle_percentage(rdev, min_uV);
- ret = pwm_config(drvdata->pwm, (period / 100) * duty_cycle, period); + ret = pwm_config(drvdata->pwm, (pargs.period / 100) * duty_cycle, + pargs.period); if (ret) { dev_err(&rdev->dev, "Failed to configure PWM\n"); return ret;
The PWM framework has clarified the concept of reference PWM config (the platform dependent config retrieved from the DT or the PWM lookup table) and real PWM state.
Use pwm_get_args() when the PWM user wants to retrieve this reference config and not the current state.
This is part of the rework allowing the PWM framework to support hardware readout and expose real PWM state even when the PWM has just been requested (before the user calls pwm_config/enable/disable()).
Signed-off-by: Boris Brezillon boris.brezillon@free-electrons.com --- drivers/video/fbdev/ssd1307fb.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/drivers/video/fbdev/ssd1307fb.c b/drivers/video/fbdev/ssd1307fb.c index fa34808..df9c63a 100644 --- a/drivers/video/fbdev/ssd1307fb.c +++ b/drivers/video/fbdev/ssd1307fb.c @@ -286,6 +286,7 @@ static int ssd1307fb_init(struct ssd1307fb_par *par) { int ret; u32 precharge, dclk, com_invdir, compins; + struct pwm_args pargs = { };
if (par->device_info->need_pwm) { par->pwm = pwm_get(&par->client->dev, NULL); @@ -294,7 +295,8 @@ static int ssd1307fb_init(struct ssd1307fb_par *par) return PTR_ERR(par->pwm); }
- par->pwm_period = pwm_get_period(par->pwm); + pwm_get_args(par->pwm, &pargs); + par->pwm_period = pargs.period; /* Enable the PWM */ pwm_config(par->pwm, par->pwm_period / 2, par->pwm_period); pwm_enable(par->pwm);
The PWM framework has clarified the concept of reference PWM config (the platform dependent config retrieved from the DT or the PWM lookup table) and real PWM state.
Use pwm_get_args() when the PWM user wants to retrieve this reference config and not the current state.
This is part of the rework allowing the PWM framework to support hardware readout and expose real PWM state even when the PWM has just been requested (before the user calls pwm_config/enable/disable()).
Signed-off-by: Boris Brezillon boris.brezillon@free-electrons.com --- drivers/video/backlight/pwm_bl.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/drivers/video/backlight/pwm_bl.c b/drivers/video/backlight/pwm_bl.c index a33a290..2479c11 100644 --- a/drivers/video/backlight/pwm_bl.c +++ b/drivers/video/backlight/pwm_bl.c @@ -201,6 +201,7 @@ static int pwm_backlight_probe(struct platform_device *pdev) struct device_node *node = pdev->dev.of_node; struct pwm_bl_data *pb; int initial_blank = FB_BLANK_UNBLANK; + struct pwm_args pargs = { }; int ret;
if (!data) { @@ -312,7 +313,8 @@ static int pwm_backlight_probe(struct platform_device *pdev) * set the period from platform data if it has not already been set * via the PWM lookup table. */ - pb->period = pwm_get_period(pb->pwm); + pwm_get_args(pb->pwm, &pargs); + pb->period = pargs.period; if (!pb->period && (data->pwm_period_ns > 0)) pb->period = data->pwm_period_ns;
Before the introduction of pwm_args, the core and some drivers were resetting the PWM period and polarity states to the reference values (those provided through the DT, a PWM lookup table or hardcoded in the driver).
Now that all PWM users are correctly using pwm_args to configure their PWM device, we can safely remove some pwm_set_period/polarity() calls.
Signed-off-by: Boris Brezillon boris.brezillon@free-electrons.com --- drivers/pwm/core.c | 5 ----- drivers/pwm/pwm-clps711x.c | 1 - drivers/pwm/pwm-pxa.c | 1 - 3 files changed, 7 deletions(-)
diff --git a/drivers/pwm/core.c b/drivers/pwm/core.c index cd55d61..6433059 100644 --- a/drivers/pwm/core.c +++ b/drivers/pwm/core.c @@ -147,13 +147,11 @@ of_pwm_xlate_with_flags(struct pwm_chip *pc, const struct of_phandle_args *args) return pwm;
pwm->args.period = args->args[1]; - pwm_set_period(pwm, pwm->args.period);
if (args->args[2] & PWM_POLARITY_INVERTED) pwm->args.polarity = PWM_POLARITY_INVERSED; else pwm->args.polarity = PWM_POLARITY_NORMAL; - pwm_set_polarity(pwm, pwm->args.polarity);
return pwm; } @@ -175,7 +173,6 @@ of_pwm_simple_xlate(struct pwm_chip *pc, const struct of_phandle_args *args) return pwm;
pwm->args.period = args->args[1]; - pwm_set_period(pwm, pwm->args.period);
return pwm; } @@ -745,8 +742,6 @@ struct pwm_device *pwm_get(struct device *dev, const char *con_id)
pwm->args.period = chosen->period; pwm->args.polarity = chosen->polarity; - pwm_set_period(pwm, chosen->period); - pwm_set_polarity(pwm, chosen->polarity);
out: mutex_unlock(&pwm_lookup_lock); diff --git a/drivers/pwm/pwm-clps711x.c b/drivers/pwm/pwm-clps711x.c index 807a48d..7d33542 100644 --- a/drivers/pwm/pwm-clps711x.c +++ b/drivers/pwm/pwm-clps711x.c @@ -61,7 +61,6 @@ static int clps711x_pwm_request(struct pwm_chip *chip, struct pwm_device *pwm)
/* Store constant period value */ pwm->args.period = DIV_ROUND_CLOSEST(NSEC_PER_SEC, freq); - pwm_set_period(pwm, pwm->args.period);
return 0; } diff --git a/drivers/pwm/pwm-pxa.c b/drivers/pwm/pwm-pxa.c index 3fcc886..58b709f 100644 --- a/drivers/pwm/pwm-pxa.c +++ b/drivers/pwm/pwm-pxa.c @@ -161,7 +161,6 @@ pxa_pwm_of_xlate(struct pwm_chip *pc, const struct of_phandle_args *args) return pwm;
pwm->args.period = args->args[0]; - pwm_set_period(pwm, args->args[0]);
return pwm; }
The PWM state, represented by its period, duty_cycle and polarity, is currently directly stored in the PWM device. Declare a pwm_state structure embedding those field so that we can later use this struct to atomically update all the PWM parameters at once.
All pwm_get_xxx() helpers are now implemented as wrappers around pwm_get_state().
Signed-off-by: Boris Brezillon boris.brezillon@free-electrons.com --- drivers/pwm/core.c | 8 ++++---- include/linux/pwm.h | 54 +++++++++++++++++++++++++++++++++++++++++------------ 2 files changed, 46 insertions(+), 16 deletions(-)
diff --git a/drivers/pwm/core.c b/drivers/pwm/core.c index 6433059..f3f91e7 100644 --- a/drivers/pwm/core.c +++ b/drivers/pwm/core.c @@ -268,7 +268,7 @@ int pwmchip_add_with_polarity(struct pwm_chip *chip, pwm->chip = chip; pwm->pwm = chip->base + i; pwm->hwpwm = i; - pwm->polarity = polarity; + pwm->state.polarity = polarity;
radix_tree_insert(&pwm_tree, pwm->pwm, pwm); } @@ -446,8 +446,8 @@ int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns) if (err) return err;
- pwm->duty_cycle = duty_ns; - pwm->period = period_ns; + pwm->state.duty_cycle = duty_ns; + pwm->state.period = period_ns;
return 0; } @@ -480,7 +480,7 @@ int pwm_set_polarity(struct pwm_device *pwm, enum pwm_polarity polarity) if (err) return err;
- pwm->polarity = polarity; + pwm->state.polarity = polarity;
return 0; } diff --git a/include/linux/pwm.h b/include/linux/pwm.h index ed65354..f0f0f37 100644 --- a/include/linux/pwm.h +++ b/include/linux/pwm.h @@ -97,6 +97,18 @@ enum { PWMF_EXPORTED = 1 << 2, };
+/* + * struct pwm_state - state of a PWM channel + * @period: PWM period (in nanoseconds) + * @duty_cycle: PWM duty cycle (in nanoseconds) + * @polarity: PWM polarity + */ +struct pwm_state { + unsigned int period; + unsigned int duty_cycle; + enum pwm_polarity polarity; +}; + /** * struct pwm_device - PWM channel object * @label: name of the PWM device @@ -105,10 +117,8 @@ enum { * @pwm: global index of the PWM device * @chip: PWM chip providing this PWM device * @chip_data: chip-private data associated with the PWM device - * @period: period of the PWM signal (in nanoseconds) - * @duty_cycle: duty cycle of the PWM signal (in nanoseconds) - * @polarity: polarity of the PWM signal * @args: PWM arguments + * @state: curent PWM channel state */ struct pwm_device { const char *label; @@ -118,13 +128,21 @@ struct pwm_device { struct pwm_chip *chip; void *chip_data;
- unsigned int period; - unsigned int duty_cycle; - enum pwm_polarity polarity; - struct pwm_args args; + struct pwm_state state; };
+/** + * pwm_get_state() - retrieve the current PWM state + * @pwm: PWM device + * @state: state to fill with the current PWM state + */ +static inline void pwm_get_state(const struct pwm_device *pwm, + struct pwm_state *state) +{ + *state = pwm->state; +} + static inline bool pwm_is_enabled(const struct pwm_device *pwm) { return test_bit(PWMF_ENABLED, &pwm->flags); @@ -133,23 +151,31 @@ static inline bool pwm_is_enabled(const struct pwm_device *pwm) static inline void pwm_set_period(struct pwm_device *pwm, unsigned int period) { if (pwm) - pwm->period = period; + pwm->state.period = period; }
static inline unsigned int pwm_get_period(const struct pwm_device *pwm) { - return pwm ? pwm->period : 0; + struct pwm_state pstate; + + pwm_get_state(pwm, &pstate); + + return pstate.period; }
static inline void pwm_set_duty_cycle(struct pwm_device *pwm, unsigned int duty) { if (pwm) - pwm->duty_cycle = duty; + pwm->state.duty_cycle = duty; }
static inline unsigned int pwm_get_duty_cycle(const struct pwm_device *pwm) { - return pwm ? pwm->duty_cycle : 0; + struct pwm_state pstate; + + pwm_get_state(pwm, &pstate); + + return pstate.duty_cycle; }
/* @@ -159,7 +185,11 @@ int pwm_set_polarity(struct pwm_device *pwm, enum pwm_polarity polarity);
static inline enum pwm_polarity pwm_get_polarity(const struct pwm_device *pwm) { - return pwm ? pwm->polarity : PWM_POLARITY_NORMAL; + struct pwm_state pstate; + + pwm_get_state(pwm, &pstate); + + return pstate.polarity; }
static inline void pwm_get_args(const struct pwm_device *pwm,
Prepare the transition to PWM atomic update by moving the enabled/disabled state into the pwm_state struct. This way we can easily update the whole PWM state by copying the new state in the ->state field.
Signed-off-by: Boris Brezillon boris.brezillon@free-electrons.com --- drivers/pwm/core.c | 13 +++++++++---- include/linux/pwm.h | 11 ++++++++--- 2 files changed, 17 insertions(+), 7 deletions(-)
diff --git a/drivers/pwm/core.c b/drivers/pwm/core.c index f3f91e7..c240b54 100644 --- a/drivers/pwm/core.c +++ b/drivers/pwm/core.c @@ -499,10 +499,10 @@ int pwm_enable(struct pwm_device *pwm) if (!pwm) return -EINVAL;
- if (!test_and_set_bit(PWMF_ENABLED, &pwm->flags)) { + if (!pwm_is_enabled(pwm)) { err = pwm->chip->ops->enable(pwm->chip, pwm); - if (err) - clear_bit(PWMF_ENABLED, &pwm->flags); + if (!err) + pwm->state.enabled = true; }
return err; @@ -515,8 +515,13 @@ EXPORT_SYMBOL_GPL(pwm_enable); */ void pwm_disable(struct pwm_device *pwm) { - if (pwm && test_and_clear_bit(PWMF_ENABLED, &pwm->flags)) + if (!pwm) + return; + + if (pwm_is_enabled(pwm)) { pwm->chip->ops->disable(pwm->chip, pwm); + pwm->state.enabled = false; + } } EXPORT_SYMBOL_GPL(pwm_disable);
diff --git a/include/linux/pwm.h b/include/linux/pwm.h index f0f0f37..55bf463 100644 --- a/include/linux/pwm.h +++ b/include/linux/pwm.h @@ -93,8 +93,7 @@ struct pwm_args {
enum { PWMF_REQUESTED = 1 << 0, - PWMF_ENABLED = 1 << 1, - PWMF_EXPORTED = 1 << 2, + PWMF_EXPORTED = 1 << 1, };
/* @@ -102,11 +101,13 @@ enum { * @period: PWM period (in nanoseconds) * @duty_cycle: PWM duty cycle (in nanoseconds) * @polarity: PWM polarity + * @enabled: PWM enabled status */ struct pwm_state { unsigned int period; unsigned int duty_cycle; enum pwm_polarity polarity; + bool enabled; };
/** @@ -145,7 +146,11 @@ static inline void pwm_get_state(const struct pwm_device *pwm,
static inline bool pwm_is_enabled(const struct pwm_device *pwm) { - return test_bit(PWMF_ENABLED, &pwm->flags); + struct pwm_state pstate; + + pwm_get_state(pwm, &pstate); + + return pstate.enabled; }
static inline void pwm_set_period(struct pwm_device *pwm, unsigned int period)
Add a ->get_state() function to the pwm_ops struct to let PWM drivers initialize the PWM state attached to a PWM device.
Signed-off-by: Boris Brezillon boris.brezillon@free-electrons.com --- drivers/pwm/core.c | 3 +++ include/linux/pwm.h | 5 +++++ 2 files changed, 8 insertions(+)
diff --git a/drivers/pwm/core.c b/drivers/pwm/core.c index c240b54..a909c64 100644 --- a/drivers/pwm/core.c +++ b/drivers/pwm/core.c @@ -270,6 +270,9 @@ int pwmchip_add_with_polarity(struct pwm_chip *chip, pwm->hwpwm = i; pwm->state.polarity = polarity;
+ if (chip->ops->get_state) + chip->ops->get_state(chip, pwm, &pwm->state); + radix_tree_insert(&pwm_tree, pwm->pwm, pwm); }
diff --git a/include/linux/pwm.h b/include/linux/pwm.h index 55bf463..ceedcf8 100644 --- a/include/linux/pwm.h +++ b/include/linux/pwm.h @@ -211,6 +211,9 @@ static inline void pwm_get_args(const struct pwm_device *pwm, * @set_polarity: configure the polarity of this PWM * @enable: enable PWM output toggling * @disable: disable PWM output toggling + * @get_state: get the current PWM state. This function is only + * called once per PWM device when the PWM chip is + * registered. * @dbg_show: optional routine to show contents in debugfs * @owner: helps prevent removal of modules exporting active PWMs */ @@ -223,6 +226,8 @@ struct pwm_ops { enum pwm_polarity polarity); int (*enable)(struct pwm_chip *chip, struct pwm_device *pwm); void (*disable)(struct pwm_chip *chip, struct pwm_device *pwm); + void (*get_state)(struct pwm_chip *chip, struct pwm_device *pwm, + struct pwm_state *pstate); #ifdef CONFIG_DEBUG_FS void (*dbg_show)(struct pwm_chip *chip, struct seq_file *s); #endif
Add an ->apply() method to the pwm_ops struct to allow PWM drivers to implement atomic update. This method will be preferred over the ->enable(), ->disable() and ->config() methods if available.
Add the pwm_apply_state() function to the PWM user API.
Note that the pwm_apply_state() does not guarantee the atomicity of the update operation, it all depends on the availability and implementation of the ->apply() method.
pwm_enable/disable/set_polarity/config() are now implemented as wrappers around the pwm_apply_state() function.
Signed-off-by: Boris Brezillon boris.brezillon@free-electrons.com --- drivers/pwm/core.c | 155 ++++++++++++++++++--------------------- include/linux/pwm.h | 207 +++++++++++++++++++++++++++++++++++++--------------- 2 files changed, 220 insertions(+), 142 deletions(-)
diff --git a/drivers/pwm/core.c b/drivers/pwm/core.c index a909c64..62e6b3c 100644 --- a/drivers/pwm/core.c +++ b/drivers/pwm/core.c @@ -226,6 +226,19 @@ void *pwm_get_chip_data(struct pwm_device *pwm) } EXPORT_SYMBOL_GPL(pwm_get_chip_data);
+static bool pwm_ops_check(const struct pwm_ops *ops) +{ + /* driver supports legacy, non-atomic operation */ + if (ops->config && ops->enable && ops->disable) + return true; + + /* driver supports atomic operation */ + if (ops->apply) + return true; + + return false; +} + /** * pwmchip_add_with_polarity() - register a new PWM chip * @chip: the PWM chip to add @@ -244,8 +257,10 @@ int pwmchip_add_with_polarity(struct pwm_chip *chip, unsigned int i; int ret;
- if (!chip || !chip->dev || !chip->ops || !chip->ops->config || - !chip->ops->enable || !chip->ops->disable || !chip->npwm) + if (!chip || !chip->dev || !chip->ops || !chip->npwm) + return -EINVAL; + + if (!pwm_ops_check(chip->ops)) return -EINVAL;
mutex_lock(&pwm_lock); @@ -431,102 +446,72 @@ void pwm_free(struct pwm_device *pwm) EXPORT_SYMBOL_GPL(pwm_free);
/** - * pwm_config() - change a PWM device configuration + * pwm_apply_state() - atomically apply a new state to a PWM device * @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. + * @state: new state to apply. This can be adjusted by the PWM driver + * if the requested config is not achievable, for example, + * ->duty_cycle and ->period might be approximated. */ -int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns) +int pwm_apply_state(struct pwm_device *pwm, struct pwm_state *state) { int err;
- if (!pwm || duty_ns < 0 || period_ns <= 0 || duty_ns > period_ns) - return -EINVAL; - - err = pwm->chip->ops->config(pwm->chip, pwm, duty_ns, period_ns); - if (err) - return err; - - pwm->state.duty_cycle = duty_ns; - pwm->state.period = period_ns; - - return 0; -} -EXPORT_SYMBOL_GPL(pwm_config); - -/** - * pwm_set_polarity() - configure the polarity of a PWM signal - * @pwm: PWM device - * @polarity: new polarity of the PWM signal - * - * Note that the polarity cannot be configured while the PWM device is - * enabled. - * - * Returns: 0 on success or a negative error code on failure. - */ -int pwm_set_polarity(struct pwm_device *pwm, enum pwm_polarity polarity) -{ - int err; - - if (!pwm || !pwm->chip->ops) - return -EINVAL; - - if (!pwm->chip->ops->set_polarity) - return -ENOSYS; - - if (pwm_is_enabled(pwm)) - return -EBUSY; - - err = pwm->chip->ops->set_polarity(pwm->chip, pwm, polarity); - if (err) - return err; - - pwm->state.polarity = polarity; - - return 0; -} -EXPORT_SYMBOL_GPL(pwm_set_polarity); - -/** - * pwm_enable() - start a PWM output toggling - * @pwm: PWM device - * - * Returns: 0 on success or a negative error code on failure. - */ -int pwm_enable(struct pwm_device *pwm) -{ - int err = 0; - if (!pwm) return -EINVAL;
- if (!pwm_is_enabled(pwm)) { - err = pwm->chip->ops->enable(pwm->chip, pwm); - if (!err) - pwm->state.enabled = true; - } + if (!memcmp(state, &pwm->state, sizeof(*state))) + return 0;
- return err; -} -EXPORT_SYMBOL_GPL(pwm_enable); + if (pwm->chip->ops->apply) { + err = pwm->chip->ops->apply(pwm->chip, pwm, state); + if (err) + return err; + } else { + /* + * FIXME: restore the initial state in case of error. + */ + if (state->polarity != pwm->state.polarity) { + if (!pwm->chip->ops->set_polarity) + return -ENOTSUPP; + + /* + * Changing the polarity of a running PWM is + * only allowed when the PWM driver implements + * ->apply(). + */ + if (pwm->state.enabled) + return -EBUSY; + + err = pwm->chip->ops->set_polarity(pwm->chip, pwm, + state->polarity); + if (err) + return err; + }
-/** - * pwm_disable() - stop a PWM output toggling - * @pwm: PWM device - */ -void pwm_disable(struct pwm_device *pwm) -{ - if (!pwm) - return; + if (state->period != pwm->state.period || + state->duty_cycle != pwm->state.duty_cycle) { + err = pwm->chip->ops->config(pwm->chip, pwm, + state->period, + state->duty_cycle); + if (err) + return err; + }
- if (pwm_is_enabled(pwm)) { - pwm->chip->ops->disable(pwm->chip, pwm); - pwm->state.enabled = false; + if (state->enabled != pwm->state.enabled) { + if (state->enabled) { + err = pwm->chip->ops->enable(pwm->chip, pwm); + if (err) + return err; + } else { + pwm->chip->ops->disable(pwm->chip, pwm); + } + } } + + pwm->state = *state; + return 0; } -EXPORT_SYMBOL_GPL(pwm_disable); +EXPORT_SYMBOL_GPL(pwm_apply_state);
static struct pwm_chip *of_node_to_pwmchip(struct device_node *np) { diff --git a/include/linux/pwm.h b/include/linux/pwm.h index ceedcf8..4aad4eb 100644 --- a/include/linux/pwm.h +++ b/include/linux/pwm.h @@ -5,59 +5,7 @@ #include <linux/mutex.h> #include <linux/of.h>
-struct pwm_device; struct seq_file; - -#if IS_ENABLED(CONFIG_PWM) -/* - * pwm_request - request a PWM device - */ -struct pwm_device *pwm_request(int pwm_id, const char *label); - -/* - * pwm_free - free a PWM device - */ -void pwm_free(struct pwm_device *pwm); - -/* - * pwm_config - change a PWM device configuration - */ -int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns); - -/* - * pwm_enable - start a PWM output toggling - */ -int pwm_enable(struct pwm_device *pwm); - -/* - * pwm_disable - stop a PWM output toggling - */ -void pwm_disable(struct pwm_device *pwm); -#else -static inline struct pwm_device *pwm_request(int pwm_id, const char *label) -{ - return ERR_PTR(-ENODEV); -} - -static inline void pwm_free(struct pwm_device *pwm) -{ -} - -static inline int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns) -{ - return -EINVAL; -} - -static inline int pwm_enable(struct pwm_device *pwm) -{ - return -EINVAL; -} - -static inline void pwm_disable(struct pwm_device *pwm) -{ -} -#endif - struct pwm_chip;
/** @@ -183,11 +131,6 @@ static inline unsigned int pwm_get_duty_cycle(const struct pwm_device *pwm) return pstate.duty_cycle; }
-/* - * pwm_set_polarity - configure the polarity of a PWM signal - */ -int pwm_set_polarity(struct pwm_device *pwm, enum pwm_polarity polarity); - static inline enum pwm_polarity pwm_get_polarity(const struct pwm_device *pwm) { struct pwm_state pstate; @@ -211,6 +154,10 @@ static inline void pwm_get_args(const struct pwm_device *pwm, * @set_polarity: configure the polarity of this PWM * @enable: enable PWM output toggling * @disable: disable PWM output toggling + * @apply: atomically apply a new PWM config. The state argument + * should be adjusted with the real hardware config (if the + * approximate the period or duty_cycle value, state should + * reflect it) * @get_state: get the current PWM state. This function is only * called once per PWM device when the PWM chip is * registered. @@ -226,6 +173,8 @@ struct pwm_ops { enum pwm_polarity polarity); int (*enable)(struct pwm_chip *chip, struct pwm_device *pwm); void (*disable)(struct pwm_chip *chip, struct pwm_device *pwm); + int (*apply)(struct pwm_chip *chip, struct pwm_device *pwm, + struct pwm_state *state); void (*get_state)(struct pwm_chip *chip, struct pwm_device *pwm, struct pwm_state *pstate); #ifdef CONFIG_DEBUG_FS @@ -263,6 +212,114 @@ struct pwm_chip { };
#if IS_ENABLED(CONFIG_PWM) +/* PWM user APIs */ +struct pwm_device *pwm_request(int pwm_id, const char *label); +void pwm_free(struct pwm_device *pwm); +int pwm_apply_state(struct pwm_device *pwm, struct pwm_state *state); + +/** + * pwm_config() - change a PWM device configuration + * @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(struct pwm_device *pwm, int duty_ns, + int period_ns) +{ + struct pwm_state pstate; + + if (!pwm) + return -EINVAL; + + pwm_get_state(pwm, &pstate); + if (pstate.duty_cycle == duty_ns && pstate.period == period_ns) + return 0; + + pstate.duty_cycle = duty_ns; + pstate.period = period_ns; + return pwm_apply_state(pwm, &pstate); +} + +/** + * pwm_set_polarity() - configure the polarity of a PWM signal + * @pwm: PWM device + * @polarity: new polarity of the PWM signal + * + * Note that the polarity cannot be configured while the PWM device is + * enabled. + * + * Returns: 0 on success or a negative error code on failure. + */ +static inline int pwm_set_polarity(struct pwm_device *pwm, + enum pwm_polarity polarity) +{ + struct pwm_state pstate; + + if (!pwm) + return -EINVAL; + + pwm_get_state(pwm, &pstate); + if (pstate.polarity == polarity) + return 0; + + /* + * Changing the polarity of a running PWM without adjusting the + * dutycycle/period value is a bit risky (can introduce glitches). + * Return -EBUSY in this case. + * Note that this is allowed when using pwm_apply_state() because + * the user specifies all the parameters. + */ + if (pstate.enabled) + return -EBUSY; + + pstate.polarity = polarity; + return pwm_apply_state(pwm, &pstate); +} + +/** + * pwm_enable() - start a PWM output toggling + * @pwm: PWM device + * + * Returns: 0 on success or a negative error code on failure. + */ +static inline int pwm_enable(struct pwm_device *pwm) +{ + struct pwm_state pstate; + + if (!pwm) + return -EINVAL; + + pwm_get_state(pwm, &pstate); + if (pstate.enabled) + return 0; + + pstate.enabled = true; + return pwm_apply_state(pwm, &pstate); +} + +/** + * pwm_disable() - stop a PWM output toggling + * @pwm: PWM device + */ +static inline void pwm_disable(struct pwm_device *pwm) +{ + struct pwm_state pstate; + + if (!pwm) + return; + + pwm_get_state(pwm, &pstate); + if (!pstate.enabled) + return; + + pstate.enabled = false; + pwm_apply_state(pwm, &pstate); +} + + +/* PWM provider APIs */ int pwm_set_chip_data(struct pwm_device *pwm, void *data); void *pwm_get_chip_data(struct pwm_device *pwm);
@@ -288,6 +345,42 @@ void devm_pwm_put(struct device *dev, struct pwm_device *pwm);
bool pwm_can_sleep(struct pwm_device *pwm); #else +static inline struct pwm_device *pwm_request(int pwm_id, const char *label) +{ + return ERR_PTR(-ENODEV); +} + +static inline void pwm_free(struct pwm_device *pwm) +{ +} + +static inline int pwm_apply_state(struct pwm_device *pwm, + const struct pwm_state *state) +{ + return -ENOTSUPP; +} + +static inline int pwm_config(struct pwm_device *pwm, int duty_ns, + int period_ns) +{ + return -EINVAL; +} + +static inline int pwm_set_polarity(struct pwm_device *pwm, + enum pwm_polarity polarity) +{ + return -ENOTSUPP; +} + +static inline int pwm_enable(struct pwm_device *pwm) +{ + return -EINVAL; +} + +static inline void pwm_disable(struct pwm_device *pwm) +{ +} + static inline int pwm_set_chip_data(struct pwm_device *pwm, void *data) { return -EINVAL;
Replace legacy pwm_get/set_xxx() and pwm_config/enable/disable() calls by pwm_get/apply_state().
Signed-off-by: Boris Brezillon boris.brezillon@free-electrons.com --- drivers/pwm/core.c | 5 ++++- drivers/pwm/sysfs.c | 61 +++++++++++++++++++++++++++++++---------------------- 2 files changed, 40 insertions(+), 26 deletions(-)
diff --git a/drivers/pwm/core.c b/drivers/pwm/core.c index 62e6b3c..c2b1569 100644 --- a/drivers/pwm/core.c +++ b/drivers/pwm/core.c @@ -882,13 +882,16 @@ static void pwm_dbg_show(struct pwm_chip *chip, struct seq_file *s)
for (i = 0; i < chip->npwm; i++) { struct pwm_device *pwm = &chip->pwms[i]; + struct pwm_state pstate; + + pwm_get_state(pwm, &pstate);
seq_printf(s, " pwm-%-3d (%-20.20s):", i, pwm->label);
if (test_bit(PWMF_REQUESTED, &pwm->flags)) seq_puts(s, " requested");
- if (pwm_is_enabled(pwm)) + if (pstate.enabled) seq_puts(s, " enabled");
seq_puts(s, "\n"); diff --git a/drivers/pwm/sysfs.c b/drivers/pwm/sysfs.c index ab28c89..7ec1747 100644 --- a/drivers/pwm/sysfs.c +++ b/drivers/pwm/sysfs.c @@ -47,13 +47,13 @@ static ssize_t period_show(struct device *child, { struct pwm_export *export = child_to_pwm_export(child); const struct pwm_device *pwm = export->pwm; - unsigned int period; + struct pwm_state pstate;
mutex_lock(&export->lock); - period = pwm_get_period(pwm); + pwm_get_state(pwm, &pstate); mutex_unlock(&export->lock);
- return sprintf(buf, "%u\n", period); + return sprintf(buf, "%u\n", pstate.period); }
static ssize_t period_store(struct device *child, @@ -62,6 +62,7 @@ static ssize_t period_store(struct device *child, { struct pwm_export *export = child_to_pwm_export(child); struct pwm_device *pwm = export->pwm; + struct pwm_state pstate; unsigned int val; int ret;
@@ -70,7 +71,9 @@ static ssize_t period_store(struct device *child, return ret;
mutex_lock(&export->lock); - ret = pwm_config(pwm, pwm_get_duty_cycle(pwm), val); + pwm_get_state(pwm, &pstate); + pstate.period = val; + ret = pwm_apply_state(pwm, &pstate); mutex_unlock(&export->lock);
return ret ? : size; @@ -82,13 +85,13 @@ static ssize_t duty_cycle_show(struct device *child, { struct pwm_export *export = child_to_pwm_export(child); const struct pwm_device *pwm = export->pwm; - unsigned int duty; + struct pwm_state pstate;
mutex_lock(&export->lock); - duty = pwm_get_duty_cycle(pwm); + pwm_get_state(pwm, &pstate); mutex_unlock(&export->lock);
- return sprintf(buf, "%u\n", duty); + return sprintf(buf, "%u\n", pstate.duty_cycle); }
static ssize_t duty_cycle_store(struct device *child, @@ -97,6 +100,7 @@ static ssize_t duty_cycle_store(struct device *child, { struct pwm_export *export = child_to_pwm_export(child); struct pwm_device *pwm = export->pwm; + struct pwm_state pstate; unsigned int val; int ret;
@@ -105,7 +109,9 @@ static ssize_t duty_cycle_store(struct device *child, return ret;
mutex_lock(&export->lock); - ret = pwm_config(pwm, val, pwm_get_period(pwm)); + pwm_get_state(pwm, &pstate); + pstate.duty_cycle = val; + ret = pwm_apply_state(pwm, &pstate); mutex_unlock(&export->lock);
return ret ? : size; @@ -117,13 +123,13 @@ static ssize_t enable_show(struct device *child, { struct pwm_export *export = child_to_pwm_export(child); const struct pwm_device *pwm = export->pwm; - bool enabled; + struct pwm_state pstate;
mutex_lock(&export->lock); - enabled = pwm_is_enabled(pwm); + pwm_get_state(pwm, &pstate); mutex_unlock(&export->lock);
- return sprintf(buf, "%d\n", enabled); + return sprintf(buf, "%d\n", pstate.enabled); }
static ssize_t enable_store(struct device *child, @@ -132,24 +138,20 @@ static ssize_t enable_store(struct device *child, { struct pwm_export *export = child_to_pwm_export(child); struct pwm_device *pwm = export->pwm; + struct pwm_state pstate; int val, ret;
ret = kstrtoint(buf, 0, &val); if (ret) return ret;
+ if (val != 0 && val != 1) + return -EINVAL; + mutex_lock(&export->lock); - switch (val) { - case 0: - pwm_disable(pwm); - break; - case 1: - ret = pwm_enable(pwm); - break; - default: - ret = -EINVAL; - break; - } + pwm_get_state(pwm, &pstate); + pstate.enabled = val; + ret = pwm_apply_state(pwm, &pstate); mutex_unlock(&export->lock);
return ret ? : size; @@ -162,9 +164,13 @@ static ssize_t polarity_show(struct device *child, struct pwm_export *export = child_to_pwm_export(child); const struct pwm_device *pwm = export->pwm; const char *polarity = "unknown"; + struct pwm_state pstate;
mutex_lock(&export->lock); - switch (pwm_get_polarity(pwm)) { + pwm_get_state(pwm, &pstate); + mutex_unlock(&export->lock); + + switch (pstate.polarity) { case PWM_POLARITY_NORMAL: polarity = "normal"; break; @@ -173,7 +179,6 @@ static ssize_t polarity_show(struct device *child, polarity = "inversed"; break; } - mutex_unlock(&export->lock);
return sprintf(buf, "%s\n", polarity); } @@ -185,6 +190,7 @@ static ssize_t polarity_store(struct device *child, struct pwm_export *export = child_to_pwm_export(child); struct pwm_device *pwm = export->pwm; enum pwm_polarity polarity; + struct pwm_state pstate; int ret;
if (sysfs_streq(buf, "normal")) @@ -195,7 +201,12 @@ static ssize_t polarity_store(struct device *child, return -EINVAL;
mutex_lock(&export->lock); - ret = pwm_set_polarity(pwm, polarity); + pwm_get_state(pwm, &pstate); + pstate.polarity = polarity; + if (pstate.enabled) + ret = -EBUSY; + else + ret = pwm_apply_state(pwm, &pstate); mutex_unlock(&export->lock);
return ret ? : size;
From: Heiko Stübner heiko@sntech.de
The pwm-states make it possible to also output the polarity, duty cycle and period information in the debugfs pwm summary-outout. This makes it easier to gather overview information about pwms without needing to walk through the sysfs attributes of every pwm.
Signed-off-by: Heiko Stuebner heiko@sntech.de Signed-off-by: Boris Brezillon boris.brezillon@free-electrons.com --- drivers/pwm/core.c | 5 +++++ 1 file changed, 5 insertions(+)
diff --git a/drivers/pwm/core.c b/drivers/pwm/core.c index c2b1569..9a83840 100644 --- a/drivers/pwm/core.c +++ b/drivers/pwm/core.c @@ -894,6 +894,11 @@ static void pwm_dbg_show(struct pwm_chip *chip, struct seq_file *s) if (pstate.enabled) seq_puts(s, " enabled");
+ seq_printf(s, " period:%uns", pstate.period); + seq_printf(s, " duty:%uns", pstate.duty_cycle); + seq_printf(s, " polarity:%s", + pstate.polarity ? "inverse" : "normal"); + seq_puts(s, "\n"); } }
Implement the ->get_state() function to expose initial state.
Signed-off-by: Boris Brezillon boris.brezillon@free-electrons.com --- drivers/pwm/pwm-rockchip.c | 69 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 69 insertions(+)
diff --git a/drivers/pwm/pwm-rockchip.c b/drivers/pwm/pwm-rockchip.c index 7d9cc90..6a1087c 100644 --- a/drivers/pwm/pwm-rockchip.c +++ b/drivers/pwm/pwm-rockchip.c @@ -51,6 +51,8 @@ struct rockchip_pwm_data {
void (*set_enable)(struct pwm_chip *chip, struct pwm_device *pwm, bool enable); + void (*get_state)(struct pwm_chip *chip, struct pwm_device *pwm, + struct pwm_state *pstate); };
static inline struct rockchip_pwm_chip *to_rockchip_pwm_chip(struct pwm_chip *c) @@ -75,6 +77,19 @@ static void rockchip_pwm_set_enable_v1(struct pwm_chip *chip, writel_relaxed(val, pc->base + pc->data->regs.ctrl); }
+static void rockchip_pwm_get_state_v1(struct pwm_chip *chip, + struct pwm_device *pwm, + struct pwm_state *pstate) +{ + struct rockchip_pwm_chip *pc = to_rockchip_pwm_chip(chip); + u32 enable_conf = PWM_CTRL_OUTPUT_EN | PWM_CTRL_TIMER_EN; + u32 val; + + val = readl(pc->base + pc->data->regs.ctrl); + if ((val & enable_conf) == enable_conf) + pstate->enabled = true; +} + static void rockchip_pwm_set_enable_v2(struct pwm_chip *chip, struct pwm_device *pwm, bool enable) { @@ -98,6 +113,55 @@ static void rockchip_pwm_set_enable_v2(struct pwm_chip *chip, writel_relaxed(val, pc->base + pc->data->regs.ctrl); }
+static void rockchip_pwm_get_state_v2(struct pwm_chip *chip, + struct pwm_device *pwm, + struct pwm_state *pstate) +{ + struct rockchip_pwm_chip *pc = to_rockchip_pwm_chip(chip); + u32 enable_conf = PWM_OUTPUT_LEFT | PWM_LP_DISABLE | PWM_ENABLE | + PWM_CONTINUOUS; + u32 val; + + val = readl(pc->base + pc->data->regs.ctrl); + if ((val & enable_conf) != enable_conf) + return; + + pstate->enabled = true; + + if (!(val & PWM_DUTY_POSITIVE)) + pstate->polarity = PWM_POLARITY_INVERSED; +} + +static void rockchip_pwm_get_state(struct pwm_chip *chip, + struct pwm_device *pwm, + struct pwm_state *pstate) +{ + struct rockchip_pwm_chip *pc = to_rockchip_pwm_chip(chip); + unsigned long clk_rate; + u64 tmp; + int ret; + + ret = clk_enable(pc->clk); + if (ret) + return; + + clk_rate = clk_get_rate(pc->clk); + + tmp = readl(pc->base + pc->data->regs.period); + tmp *= pc->data->prescaler * NSEC_PER_SEC; + do_div(tmp, clk_rate); + pstate->period = tmp; + + tmp = readl(pc->base + pc->data->regs.duty); + tmp *= pc->data->prescaler * NSEC_PER_SEC; + do_div(tmp, clk_rate); + pstate->duty_cycle = tmp; + + pc->data->get_state(chip, pwm, pstate); + + clk_disable(pc->clk); +} + static int rockchip_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, int duty_ns, int period_ns) { @@ -171,6 +235,7 @@ static void rockchip_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) }
static const struct pwm_ops rockchip_pwm_ops_v1 = { + .get_state = rockchip_pwm_get_state, .config = rockchip_pwm_config, .enable = rockchip_pwm_enable, .disable = rockchip_pwm_disable, @@ -178,6 +243,7 @@ static const struct pwm_ops rockchip_pwm_ops_v1 = { };
static const struct pwm_ops rockchip_pwm_ops_v2 = { + .get_state = rockchip_pwm_get_state, .config = rockchip_pwm_config, .set_polarity = rockchip_pwm_set_polarity, .enable = rockchip_pwm_enable, @@ -195,6 +261,7 @@ static const struct rockchip_pwm_data pwm_data_v1 = { .prescaler = 2, .ops = &rockchip_pwm_ops_v1, .set_enable = rockchip_pwm_set_enable_v1, + .get_state = rockchip_pwm_get_state_v1, };
static const struct rockchip_pwm_data pwm_data_v2 = { @@ -207,6 +274,7 @@ static const struct rockchip_pwm_data pwm_data_v2 = { .prescaler = 1, .ops = &rockchip_pwm_ops_v2, .set_enable = rockchip_pwm_set_enable_v2, + .get_state = rockchip_pwm_get_state_v2, };
static const struct rockchip_pwm_data pwm_data_vop = { @@ -219,6 +287,7 @@ static const struct rockchip_pwm_data pwm_data_vop = { .prescaler = 1, .ops = &rockchip_pwm_ops_v2, .set_enable = rockchip_pwm_set_enable_v2, + .get_state = rockchip_pwm_get_state_v2, };
static const struct of_device_id rockchip_pwm_dt_ids[] = {
The current logic will disable the PWM clk even if the PWM was left enabled by the bootloader (because it's controlling a critical device like a regulator for example). Keep the PWM clk enabled if the PWM is enabled to avoid any glitches.
Signed-off-by: Boris Brezillon boris.brezillon@free-electrons.com --- drivers/pwm/pwm-rockchip.c | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-)
diff --git a/drivers/pwm/pwm-rockchip.c b/drivers/pwm/pwm-rockchip.c index 6a1087c..5c7e79c 100644 --- a/drivers/pwm/pwm-rockchip.c +++ b/drivers/pwm/pwm-rockchip.c @@ -302,6 +302,7 @@ static int rockchip_pwm_probe(struct platform_device *pdev) { const struct of_device_id *id; struct rockchip_pwm_chip *pc; + struct pwm_state pstate; struct resource *r; int ret;
@@ -322,7 +323,7 @@ static int rockchip_pwm_probe(struct platform_device *pdev) if (IS_ERR(pc->clk)) return PTR_ERR(pc->clk);
- ret = clk_prepare(pc->clk); + ret = clk_prepare_enable(pc->clk); if (ret) return ret;
@@ -345,12 +346,33 @@ static int rockchip_pwm_probe(struct platform_device *pdev) dev_err(&pdev->dev, "pwmchip_add() failed: %d\n", ret); }
+ /* Keep the PWM clk enabled if the PWM appears to be up and running. */ + pwm_get_state(pc->chip.pwms, &pstate); + if (!pstate.enabled) + clk_disable(pc->clk); + return ret; }
static int rockchip_pwm_remove(struct platform_device *pdev) { struct rockchip_pwm_chip *pc = platform_get_drvdata(pdev); + struct pwm_state pstate; + + /* + * Disable the PWM clk before unpreparing it if the PWM device is still + * running. This should only happen when the last PWM user left it + * enabled, or when nobody requested a PWM that was previously enabled + * by the bootloader. + * + * FIXME: Maybe the core should disable all PWM devices in + * pwmchip_remove(). In this case we'd only have to call + * clk_unprepare() after pwmchip_remove(). + * + */ + pwm_get_state(pc->chip.pwms, &pstate); + if (pstate.enabled) + clk_disable(pc->clk);
clk_unprepare(pc->clk);
Implement the ->apply() function to add support for atomic update.
Signed-off-by: Boris Brezillon boris.brezillon@free-electrons.com Tested-by: Heiko Stuebner heiko@sntech.de --- drivers/pwm/pwm-rockchip.c | 63 ++++++++++++++++++++++++++++------------------ 1 file changed, 39 insertions(+), 24 deletions(-)
diff --git a/drivers/pwm/pwm-rockchip.c b/drivers/pwm/pwm-rockchip.c index 5c7e79c..ed27740 100644 --- a/drivers/pwm/pwm-rockchip.c +++ b/drivers/pwm/pwm-rockchip.c @@ -50,7 +50,8 @@ struct rockchip_pwm_data { const struct pwm_ops *ops;
void (*set_enable)(struct pwm_chip *chip, - struct pwm_device *pwm, bool enable); + struct pwm_device *pwm, bool enable, + enum pwm_polarity polarity); void (*get_state)(struct pwm_chip *chip, struct pwm_device *pwm, struct pwm_state *pstate); }; @@ -61,7 +62,8 @@ static inline struct rockchip_pwm_chip *to_rockchip_pwm_chip(struct pwm_chip *c) }
static void rockchip_pwm_set_enable_v1(struct pwm_chip *chip, - struct pwm_device *pwm, bool enable) + struct pwm_device *pwm, bool enable, + enum pwm_polarity polarity) { struct rockchip_pwm_chip *pc = to_rockchip_pwm_chip(chip); u32 enable_conf = PWM_CTRL_OUTPUT_EN | PWM_CTRL_TIMER_EN; @@ -91,14 +93,15 @@ static void rockchip_pwm_get_state_v1(struct pwm_chip *chip, }
static void rockchip_pwm_set_enable_v2(struct pwm_chip *chip, - struct pwm_device *pwm, bool enable) + struct pwm_device *pwm, bool enable, + enum pwm_polarity polarity) { struct rockchip_pwm_chip *pc = to_rockchip_pwm_chip(chip); u32 enable_conf = PWM_OUTPUT_LEFT | PWM_LP_DISABLE | PWM_ENABLE | PWM_CONTINUOUS; u32 val;
- if (pwm_get_polarity(pwm) == PWM_POLARITY_INVERSED) + if (polarity == PWM_POLARITY_INVERSED) enable_conf |= PWM_DUTY_NEGATIVE | PWM_INACTIVE_POSITIVE; else enable_conf |= PWM_DUTY_POSITIVE | PWM_INACTIVE_NEGATIVE; @@ -168,7 +171,6 @@ static int rockchip_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, struct rockchip_pwm_chip *pc = to_rockchip_pwm_chip(chip); unsigned long period, duty; u64 clk_rate, div; - int ret;
clk_rate = clk_get_rate(pc->clk);
@@ -185,15 +187,8 @@ static int rockchip_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, do_div(div, pc->data->prescaler * NSEC_PER_SEC); duty = div;
- ret = clk_enable(pc->clk); - if (ret) - return ret; - writel(period, pc->base + pc->data->regs.period); writel(duty, pc->base + pc->data->regs.duty); - writel(0, pc->base + pc->data->regs.cntr); - - clk_disable(pc->clk);
return 0; } @@ -211,43 +206,63 @@ static int rockchip_pwm_set_polarity(struct pwm_chip *chip, return 0; }
-static int rockchip_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) +static int rockchip_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, + struct pwm_state *state) { struct rockchip_pwm_chip *pc = to_rockchip_pwm_chip(chip); + struct pwm_state curstate; + bool enabled; int ret;
+ pwm_get_state(pwm, &curstate); + enabled = curstate.enabled; + ret = clk_enable(pc->clk); if (ret) return ret;
- pc->data->set_enable(chip, pwm, true); + if (state->polarity != curstate.polarity && enabled) { + pc->data->set_enable(chip, pwm, false, state->polarity); + enabled = false; + }
- return 0; -} + ret = rockchip_pwm_config(chip, pwm, state->duty_cycle, state->period); + if (ret) { + if (enabled != curstate.enabled) + pc->data->set_enable(chip, pwm, !enabled, + state->polarity);
-static void rockchip_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) -{ - struct rockchip_pwm_chip *pc = to_rockchip_pwm_chip(chip); + goto out; + }
- pc->data->set_enable(chip, pwm, false); + if (state->enabled != enabled) + pc->data->set_enable(chip, pwm, state->enabled, + state->polarity);
+ /* + * Update the state with the real hardware, which can differ a bit + * because of period/duty_cycle approximation. + */ + rockchip_pwm_get_state(chip, pwm, state); + +out: clk_disable(pc->clk); + + return ret; }
static const struct pwm_ops rockchip_pwm_ops_v1 = { .get_state = rockchip_pwm_get_state, .config = rockchip_pwm_config, - .enable = rockchip_pwm_enable, - .disable = rockchip_pwm_disable, + .apply = rockchip_pwm_apply, .owner = THIS_MODULE, };
static const struct pwm_ops rockchip_pwm_ops_v2 = { .get_state = rockchip_pwm_get_state, .config = rockchip_pwm_config, + .apply = rockchip_pwm_apply, .set_polarity = rockchip_pwm_set_polarity, - .enable = rockchip_pwm_enable, - .disable = rockchip_pwm_disable, .owner = THIS_MODULE, };
Implement ->get_state() to provide support for initial state retrieval.
Signed-off-by: Boris Brezillon boris.brezillon@free-electrons.com --- drivers/pwm/pwm-sti.c | 41 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+)
diff --git a/drivers/pwm/pwm-sti.c b/drivers/pwm/pwm-sti.c index 92abbd5..0fbca94 100644 --- a/drivers/pwm/pwm-sti.c +++ b/drivers/pwm/pwm-sti.c @@ -238,6 +238,46 @@ static void sti_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) mutex_unlock(&pc->sti_pwm_lock); }
+static void sti_pwm_get_state(struct pwm_chip *chip, + struct pwm_device *pwm, + struct pwm_state *pstate) +{ + struct sti_pwm_chip *pc = to_sti_pwmchip(chip); + unsigned int regval, prescaler; + u64 timens; + int ret; + + /* The clock has to be enabled to access PWM registers */ + ret = clk_enable(pc->clk); + if (ret) { + dev_err(chip->dev, "Failed to enable PWM clk"); + return; + } + + regmap_field_read(pc->prescale_high, ®val); + prescaler = regval << 4; + regmap_field_read(pc->prescale_low, ®val); + prescaler |= regval; + + timens = (u64)(prescaler + 1) * NSEC_PER_SEC * + (pc->cdata->max_pwm_cnt + 1); + do_div(timens, pc->clk_rate); + + pstate->period = timens; + + regmap_read(pc->regmap, STI_DS_REG(pwm->hwpwm), ®val); + timens = (u64)(regval + 1) * pstate->period; + do_div(timens, pc->cdata->max_pwm_cnt + 1); + pstate->duty_cycle = timens; + + regmap_field_read(pc->pwm_en, ®val); + pstate->enabled = regval; + + pstate->polarity = PWM_POLARITY_NORMAL; + + clk_disable(pc->clk); +} + static void sti_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm) { struct sti_pwm_chip *pc = to_sti_pwmchip(chip); @@ -249,6 +289,7 @@ static const struct pwm_ops sti_pwm_ops = { .config = sti_pwm_config, .enable = sti_pwm_enable, .disable = sti_pwm_disable, + .get_state = sti_pwm_get_state, .free = sti_pwm_free, .owner = THIS_MODULE, };
The current logic will disable the PWM clk even if a PWM was left enabled by the bootloader (because it's controlling a critical device like a regulator for example). Keep the PWM clk enabled if at least one PWM is enabled to avoid any glitches.
Signed-off-by: Boris Brezillon boris.brezillon@free-electrons.com --- drivers/pwm/pwm-sti.c | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-)
diff --git a/drivers/pwm/pwm-sti.c b/drivers/pwm/pwm-sti.c index 0fbca94..bea1d17 100644 --- a/drivers/pwm/pwm-sti.c +++ b/drivers/pwm/pwm-sti.c @@ -343,7 +343,7 @@ static int sti_pwm_probe(struct platform_device *pdev) struct sti_pwm_compat_data *cdata; struct sti_pwm_chip *pc; struct resource *res; - int ret; + int i, ret;
pc = devm_kzalloc(dev, sizeof(*pc), GFP_KERNEL); if (!pc) @@ -394,7 +394,7 @@ static int sti_pwm_probe(struct platform_device *pdev) return -EINVAL; }
- ret = clk_prepare(pc->clk); + ret = clk_prepare_enable(pc->clk); if (ret) { dev_err(dev, "failed to prepare clock\n"); return ret; @@ -412,6 +412,19 @@ static int sti_pwm_probe(struct platform_device *pdev) return ret; }
+ /* + * Keep the PWM clk enabled if some PWMs appear to be up and + * running. + */ + for (i = 0; i < pc->chip.npwm; i++) { + struct pwm_state pstate; + + pwm_get_state(&pc->chip.pwms[i], &pstate); + if (pstate.enabled) + clk_enable(pc->clk); + } + clk_disable(pc->clk); + platform_set_drvdata(pdev, pc);
return 0;
Implement ->get_state() instead of only initializing the polarity in the probe function.
This implementation also takes care of keeping the PWM clk enabled if at least one of the PWM exported by the PWM chip is already enabled, which should prevent glitches.
Signed-off-by: Boris Brezillon boris.brezillon@free-electrons.com --- drivers/pwm/pwm-sun4i.c | 74 ++++++++++++++++++++++++++++++++++++------------- 1 file changed, 55 insertions(+), 19 deletions(-)
diff --git a/drivers/pwm/pwm-sun4i.c b/drivers/pwm/pwm-sun4i.c index 03a99a5..34cb296 100644 --- a/drivers/pwm/pwm-sun4i.c +++ b/drivers/pwm/pwm-sun4i.c @@ -252,11 +252,65 @@ static void sun4i_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) clk_disable_unprepare(sun4i_pwm->clk); }
+static void sun4i_pwm_get_state(struct pwm_chip *chip, + struct pwm_device *pwm, + struct pwm_state *pstate) +{ + struct sun4i_pwm_chip *sun4i_pwm = to_sun4i_pwm_chip(chip); + unsigned int clk_rate = clk_get_rate(sun4i_pwm->clk); + int prescaler, prescalerid; + int ret; + u32 val; + + ret = clk_prepare_enable(sun4i_pwm->clk); + if (ret) { + dev_err(chip->dev, "Failed to enable PWM clock"); + return; + } + + val = sun4i_pwm_readl(sun4i_pwm, PWM_CTRL_REG); + if (val & BIT_CH(PWM_ACT_STATE, pwm->hwpwm)) + pstate->polarity = PWM_POLARITY_INVERSED; + else + pstate->polarity = PWM_POLARITY_NORMAL; + + if ((val & BIT_CH(PWM_EN, pwm->hwpwm)) && + (val & BIT_CH(PWM_CLK_GATING, pwm->hwpwm))) + pstate->enabled = true; + else + pstate->enabled = false; + + pstate->period = 0; + pstate->duty_cycle = 0; + prescalerid = (val >> (PWMCH_OFFSET * pwm->hwpwm)) & PWM_PRESCAL_MASK; + prescaler = prescaler_table[prescalerid]; + if (prescaler) { + u64 timens; + + clk_rate /= prescaler; + + val = sun4i_pwm_readl(sun4i_pwm, PWM_CH_PRD(pwm->hwpwm)); + + timens = ((val >> 16) & PWM_PRD_MASK) + 1; + timens *= NSEC_PER_SEC; + do_div(timens, clk_rate); + pstate->period = timens; + + timens = val & PWM_DTY_MASK; + timens *= NSEC_PER_SEC; + do_div(timens, clk_rate); + pstate->duty_cycle = timens; + } + + clk_disable_unprepare(sun4i_pwm->clk); +} + static const struct pwm_ops sun4i_pwm_ops = { .config = sun4i_pwm_config, .set_polarity = sun4i_pwm_set_polarity, .enable = sun4i_pwm_enable, .disable = sun4i_pwm_disable, + .get_state = sun4i_pwm_get_state, .owner = THIS_MODULE, };
@@ -307,8 +361,7 @@ static int sun4i_pwm_probe(struct platform_device *pdev) { struct sun4i_pwm_chip *pwm; struct resource *res; - u32 val; - int i, ret; + int ret; const struct of_device_id *match;
match = of_match_device(sun4i_pwm_dt_ids, &pdev->dev); @@ -345,24 +398,7 @@ static int sun4i_pwm_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, pwm);
- ret = clk_prepare_enable(pwm->clk); - if (ret) { - dev_err(&pdev->dev, "failed to enable PWM clock\n"); - goto clk_error; - } - - val = sun4i_pwm_readl(pwm, PWM_CTRL_REG); - for (i = 0; i < pwm->chip.npwm; i++) - if (!(val & BIT_CH(PWM_ACT_STATE, i))) - pwm_set_polarity(&pwm->chip.pwms[i], - PWM_POLARITY_INVERSED); - clk_disable_unprepare(pwm->clk); - return 0; - -clk_error: - pwmchip_remove(&pwm->chip); - return ret; }
static int sun4i_pwm_remove(struct platform_device *pdev)
The PWM attached to a PWM regulator device might have been previously configured by the bootloader. Make sure the bootloader and linux config are in sync, and adjust the PWM config if that's not the case.
Signed-off-by: Boris Brezillon boris.brezillon@free-electrons.com --- drivers/regulator/pwm-regulator.c | 50 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+)
diff --git a/drivers/regulator/pwm-regulator.c b/drivers/regulator/pwm-regulator.c index 9154c47..9590fb0 100644 --- a/drivers/regulator/pwm-regulator.c +++ b/drivers/regulator/pwm-regulator.c @@ -240,6 +240,52 @@ static int pwm_regulator_init_continuous(struct platform_device *pdev, return 0; }
+static int pwm_regulator_adjust_pwm_config(struct pwm_regulator_data *drvdata) +{ + struct pwm_state pstate = { }; + struct pwm_args pargs = { }; + + pwm_get_args(drvdata->pwm, &pargs); + pwm_get_state(drvdata->pwm, &pstate); + + /* + * if the current period is zero this either means the PWM driver + * does not support initial state retrieval or the PWM was not + * configured. + * In any case, we setup the new period and poloarity, and assign a + * duty_cycle of 0. + */ + if (!pstate.period) { + pstate.duty_cycle = 0; + pstate.period = pargs.period; + pstate.polarity = pargs.polarity; + + return pwm_apply_state(drvdata->pwm, &pstate); + } + + /* + * Adjust the PWM dutycycle/period based on the period value provided + * in PWM args. + */ + if (pargs.period != pstate.period) { + u64 dutycycle = (u64)pstate.duty_cycle * pargs.period; + + do_div(dutycycle, pstate.period); + pstate.duty_cycle = dutycycle; + pstate.period = pargs.period; + } + + /* + * If the polarity changed, we should also change the dutycycle value. + */ + if (pargs.polarity != pstate.polarity) { + pstate.polarity = pargs.polarity; + pstate.duty_cycle = pstate.period - pstate.duty_cycle; + } + + return pwm_apply_state(drvdata->pwm, &pstate); +} + static int pwm_regulator_probe(struct platform_device *pdev) { const struct regulator_init_data *init_data; @@ -283,6 +329,10 @@ static int pwm_regulator_probe(struct platform_device *pdev) return PTR_ERR(drvdata->pwm); }
+ ret = pwm_regulator_adjust_pwm_config(drvdata); + if (ret) + return ret; + regulator = devm_regulator_register(&pdev->dev, &drvdata->desc, &config); if (IS_ERR(regulator)) {
On Wed, Mar 30, 2016 at 10:03:50PM +0200, Boris Brezillon wrote:
The PWM attached to a PWM regulator device might have been previously configured by the bootloader. Make sure the bootloader and linux config are in sync, and adjust the PWM config if that's not the case.
Acked-by: Mark Brown broonie@kernel.org
pwm_config/enable/disable() have been deprecated in favor of pwm_apply_state(). Replace all those calls with the equivalent pwm_get/apply_state().
Signed-off-by: Boris Brezillon boris.brezillon@free-electrons.com --- drivers/regulator/pwm-regulator.c | 54 ++++++++++++++++++++++++--------------- 1 file changed, 34 insertions(+), 20 deletions(-)
diff --git a/drivers/regulator/pwm-regulator.c b/drivers/regulator/pwm-regulator.c index 9590fb0..42cc312 100644 --- a/drivers/regulator/pwm-regulator.c +++ b/drivers/regulator/pwm-regulator.c @@ -59,16 +59,18 @@ static int pwm_regulator_set_voltage_sel(struct regulator_dev *rdev, unsigned selector) { struct pwm_regulator_data *drvdata = rdev_get_drvdata(rdev); - struct pwm_args pargs = { }; - int dutycycle; + struct pwm_state pstate = { }; + u64 dutycycle; int ret;
- pwm_get_args(drvdata->pwm, &pargs); + pwm_get_state(drvdata->pwm, &pstate);
- dutycycle = (pargs.period * - drvdata->duty_cycle_table[selector].dutycycle) / 100; + dutycycle = drvdata->duty_cycle_table[selector].dutycycle; + dutycycle *= pstate.period; + do_div(dutycycle, 100); + pstate.duty_cycle = dutycycle;
- ret = pwm_config(drvdata->pwm, dutycycle, pargs.period); + ret = pwm_apply_state(drvdata->pwm, &pstate); if (ret) { dev_err(&rdev->dev, "Failed to configure PWM\n"); return ret; @@ -93,24 +95,39 @@ static int pwm_regulator_list_voltage(struct regulator_dev *rdev, static int pwm_regulator_enable(struct regulator_dev *dev) { struct pwm_regulator_data *drvdata = rdev_get_drvdata(dev); + struct pwm_state pstate = { }; + + pwm_get_state(drvdata->pwm, &pstate); + if (pstate.enabled) + return 0; + + pstate.enabled = true;
- return pwm_enable(drvdata->pwm); + return pwm_apply_state(drvdata->pwm, &pstate); }
static int pwm_regulator_disable(struct regulator_dev *dev) { struct pwm_regulator_data *drvdata = rdev_get_drvdata(dev); + struct pwm_state pstate = { };
- pwm_disable(drvdata->pwm); + pwm_get_state(drvdata->pwm, &pstate); + if (!pstate.enabled) + return 0;
- return 0; + pstate.enabled = false; + + return pwm_apply_state(drvdata->pwm, &pstate); }
static int pwm_regulator_is_enabled(struct regulator_dev *dev) { struct pwm_regulator_data *drvdata = rdev_get_drvdata(dev); + struct pwm_state pstate = { };
- return pwm_is_enabled(drvdata->pwm); + pwm_get_state(drvdata->pwm, &pstate); + + return pstate.enabled; }
/** @@ -138,25 +155,22 @@ static int pwm_regulator_set_voltage(struct regulator_dev *rdev, { struct pwm_regulator_data *drvdata = rdev_get_drvdata(rdev); unsigned int ramp_delay = rdev->constraints->ramp_delay; - struct pwm_args pargs = { }; - int duty_cycle; + struct pwm_state pstate = { }; + u64 duty_cycle; int ret;
- pwm_get_args(drvdata->pwm, &pargs); + pwm_get_state(drvdata->pwm, &pstate); duty_cycle = pwm_voltage_to_duty_cycle_percentage(rdev, min_uV); + duty_cycle *= pstate.period; + do_div(duty_cycle, 100); + pstate.duty_cycle = duty_cycle;
- ret = pwm_config(drvdata->pwm, (pargs.period / 100) * duty_cycle, - pargs.period); + ret = pwm_apply_state(drvdata->pwm, &pstate); if (ret) { dev_err(&rdev->dev, "Failed to configure PWM\n"); return ret; }
- ret = pwm_enable(drvdata->pwm); - if (ret) { - dev_err(&rdev->dev, "Failed to enable PWM\n"); - return ret; - } drvdata->volt_uV = min_uV;
/* Delay required by PWM regulator to settle to the new voltage */
On Wed, Mar 30, 2016 at 10:03:51PM +0200, Boris Brezillon wrote:
pwm_config/enable/disable() have been deprecated in favor of pwm_apply_state(). Replace all those calls with the equivalent pwm_get/apply_state().
Acked-by: Mark Brown broonie@kernel.org
The ->state field is currently initialized to 0, thus referencing the voltage selector at index 0, which might not reflect the current voltage value. If possible, retrieve the current voltage selector from the PWM state, else return -EINVAL.
Signed-off-by: Boris Brezillon boris.brezillon@free-electrons.com Tested-by: Heiko Stuebner heiko@sntech.de Acked-by: Mark Brown broonie@kernel.org --- drivers/regulator/pwm-regulator.c | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+)
diff --git a/drivers/regulator/pwm-regulator.c b/drivers/regulator/pwm-regulator.c index 42cc312..9374796 100644 --- a/drivers/regulator/pwm-regulator.c +++ b/drivers/regulator/pwm-regulator.c @@ -48,10 +48,35 @@ struct pwm_voltages { /** * Voltage table call-backs */ +static void pwm_regulator_init_state(struct regulator_dev *rdev) +{ + struct pwm_regulator_data *drvdata = rdev_get_drvdata(rdev); + struct pwm_state pwm_state; + unsigned int dutycycle; + int i; + + pwm_get_state(drvdata->pwm, &pwm_state); + + if (!pwm_state.period) + return; + + dutycycle = (pwm_state.duty_cycle * 100) / pwm_state.period; + + for (i = 0; i < rdev->desc->n_voltages; i++) { + if (dutycycle == drvdata->duty_cycle_table[i].dutycycle) { + drvdata->state = i; + return; + } + } +} + static int pwm_regulator_get_voltage_sel(struct regulator_dev *rdev) { struct pwm_regulator_data *drvdata = rdev_get_drvdata(rdev);
+ if (drvdata->state < 0) + pwm_regulator_init_state(rdev); + return drvdata->state; }
@@ -234,6 +259,7 @@ static int pwm_regulator_init_table(struct platform_device *pdev, return ret; }
+ drvdata->state = -EINVAL; drvdata->duty_cycle_table = duty_cycle_table; memcpy(&drvdata->ops, &pwm_regulator_voltage_table_ops, sizeof(drvdata->ops));
The continuous PWM voltage regulator is caching the voltage value in the ->volt_uV field. While most of the time this value should reflect the real voltage, sometime it can be sightly different if the PWM device rounded the set_duty_cycle request. Moreover, this value is not valid until someone has modified the regulator output.
Remove the ->volt_uV field and always rely on the PWM state to calculate the regulator output.
Signed-off-by: Boris Brezillon boris.brezillon@free-electrons.com --- drivers/regulator/pwm-regulator.c | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-)
diff --git a/drivers/regulator/pwm-regulator.c b/drivers/regulator/pwm-regulator.c index 9374796..77f42d8 100644 --- a/drivers/regulator/pwm-regulator.c +++ b/drivers/regulator/pwm-regulator.c @@ -35,9 +35,6 @@ struct pwm_regulator_data { struct regulator_ops ops;
int state; - - /* Continuous voltage */ - int volt_uV; };
struct pwm_voltages { @@ -167,11 +164,27 @@ static int pwm_voltage_to_duty_cycle_percentage(struct regulator_dev *rdev, int return ((req_uV * 100) - (min_uV * 100)) / diff; }
+static int pwm_duty_cycle_percentage_to_voltage(struct regulator_dev *rdev, + int dutycycle) +{ + int min_uV = rdev->constraints->min_uV; + int max_uV = rdev->constraints->max_uV; + int diff = max_uV - min_uV; + + return min_uV + ((diff * dutycycle) / 100); +} + static int pwm_regulator_get_voltage(struct regulator_dev *rdev) { struct pwm_regulator_data *drvdata = rdev_get_drvdata(rdev); + struct pwm_state pstate; + u64 dutycycle;
- return drvdata->volt_uV; + pwm_get_state(drvdata->pwm, &pstate); + dutycycle = pstate.duty_cycle * 100; + do_div(dutycycle, pstate.period); + + return pwm_duty_cycle_percentage_to_voltage(rdev, dutycycle); }
static int pwm_regulator_set_voltage(struct regulator_dev *rdev, @@ -196,8 +209,6 @@ static int pwm_regulator_set_voltage(struct regulator_dev *rdev, return ret; }
- drvdata->volt_uV = min_uV; - /* Delay required by PWM regulator to settle to the new voltage */ usleep_range(ramp_delay, ramp_delay + 1000);
On Wed, Mar 30, 2016 at 10:03:53PM +0200, Boris Brezillon wrote:
The continuous PWM voltage regulator is caching the voltage value in the ->volt_uV field. While most of the time this value should reflect the real voltage, sometime it can be sightly different if the PWM device rounded the set_duty_cycle request. Moreover, this value is not valid until someone has modified the regulator output.
Acked-by: Mark Brown broonie@kernel.org
Hi Mark,
On Wed, 30 Mar 2016 14:24:10 -0700 Mark Brown broonie@kernel.org wrote:
On Wed, Mar 30, 2016 at 10:03:53PM +0200, Boris Brezillon wrote:
The continuous PWM voltage regulator is caching the voltage value in the ->volt_uV field. While most of the time this value should reflect the real voltage, sometime it can be sightly different if the PWM device rounded the set_duty_cycle request. Moreover, this value is not valid until someone has modified the regulator output.
Acked-by: Mark Brown broonie@kernel.org
Actually this patch introduces a bug (reported by Stephen):
" I applied your patch series [PATCH v5 00/46] pwm: add support for atomic update and found a null pointer dereference when probing a pwm-regulator at boot. See the below stack trace:
[ 4.282374] [<ffffffc000399088>] pwm_regulator_get_voltage+0x78/0xa0 [ 4.289344] [<ffffffc000390740>] regulator_attr_is_visible+0x7c/0x264 [ 4.296408] [<ffffffc0001f75a0>] internal_create_group+0x14c/0x280 [ 4.303184] [<ffffffc0001f76e8>] sysfs_create_group+0x14/0x1c [ 4.309483] [<ffffffc0001f77c8>] sysfs_create_groups+0x30/0x78 [ 4.315881] [<ffffffc00043631c>] device_add+0x224/0x4d8 [ 4.321609] [<ffffffc0004365ec>] device_register+0x1c/0x28 [ 4.327623] [<ffffffc0003952a4>] regulator_register+0x2e4/0xc14 [ 4.334112] [<ffffffc000396844>] devm_regulator_register+0x54/0x94 [ 4.340887] [<ffffffc000399328>] pwm_regulator_probe+0x278/0x2b8 [ 4.347473] [<ffffffc000439fe4>] platform_drv_probe+0x58/0xa4 [ 4.353772] [<ffffffc0004387a8>] driver_probe_device+0x114/0x2ac [ 4.360358] [<ffffffc0004389a4>] __driver_attach+0x64/0x90 [ 4.366371] [<ffffffc000436f50>] bus_for_each_dev+0x74/0x90 [ 4.372478] [<ffffffc000438bd4>] driver_attach+0x20/0x28 [ 4.378299] [<ffffffc00043778c>] bus_add_driver+0xe8/0x1e0 [ 4.384312] [<ffffffc00043959c>] driver_register+0x98/0xe4 [ 4.390326] [<ffffffc00043aa04>] __platform_driver_register+0x48/0x50 [ 4.397388] [<ffffffc000cb2710>] pwm_regulator_driver_init+0x18/0x20 [ 4.404356] [<ffffffc000c8ca7c>] do_one_initcall+0xf8/0x180 [ 4.410466] [<ffffffc000c8cc58>] kernel_init_freeable+0x154/0x1f4 [ 4.417148] [<ffffffc000929cf4>] kernel_init+0x10/0xf8 [ 4.422782] [<ffffffc000084450>] ret_from_fork+0x10/0x40
It looks like the root cause is that regulator_attr_is_visible will try to get the voltage, but at this point in regulator_register, rdev->constraints is still null. So pwm_duty_cycle_percentage_to_voltage will dereference a null rdev->constraints pointer. "
The problem is that we need to know the min and max voltage constraints to calculate the current voltage. ->get_voltage() is called when the sysfs attributes are created (part of device registration), and set_machine_constraints() is called after device_register(), thus leading to the NULL pointer dereference.
Is there any reason for calling set_machine_constraints() after device_register() in regulator_register()?
Best Regards,
Boris
Update the PWM subsystem documentation to reflect the atomic PWM changes.
Signed-off-by: Boris Brezillon boris.brezillon@free-electrons.com --- Documentation/pwm.txt | 27 +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-)
diff --git a/Documentation/pwm.txt b/Documentation/pwm.txt index ca895fd..cb25fca 100644 --- a/Documentation/pwm.txt +++ b/Documentation/pwm.txt @@ -42,9 +42,23 @@ variants of these functions, devm_pwm_get() and devm_pwm_put(), also exist.
After being requested, a PWM has to be configured using:
-int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns); +int pwm_apply_state(struct pwm_device *pwm, struct pwm_state *pstate);
-To start/stop toggling the PWM output use pwm_enable()/pwm_disable(). +This API controls both the PWM period/duty_cycle config and the +enable/disable state. + +The legacy pwm_config(), pwm_enable() and pwm_disable() are now deprecated, +and should be replaced by pwm_apply_state() calls. + +The PWM user API also allows one to query the PWM state with pwm_get_state(). + +In addition to the PWM state, the PWM API also exposes PWM arguments, which +are the reference PWM config one should use on this PWM. +PWM arguments are usually platform-specific and allows the PWM user to only +care about dutycycle relatively to the full period (like, duty = 50% of the +period). struct pwm_args contains 2 fields (period and polarity) and should +be used to set the initial PWM config (usually done in the probe function +of the PWM user). PWM arguments are retrieved with pwm_get_args().
Using PWMs with the sysfs interface ----------------------------------- @@ -105,6 +119,15 @@ goes low for the remainder of the period. Conversely, a signal with inversed polarity starts low for the duration of the duty cycle and goes high for the remainder of the period.
+Drivers are encouraged to implement ->apply() instead of the legacy +->enable(), ->disable() and ->config() methods. Doing that should provide +atomicity in the PWM config workflow, which is required when the PWM controls +a critical device (like a regulator). + +The implementation of ->get_state() (a method used to retrieve initial PWM +state) is also encouraged for the same reason: letting the PWM user know +about the current PWM state would allow him to avoid glitches. + Locking -------
Prefix those function as deprecated to encourage all existing users to switch to pwm_apply_state().
Signed-off-by: Boris Brezillon boris.brezillon@free-electrons.com --- include/linux/pwm.h | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-)
diff --git a/include/linux/pwm.h b/include/linux/pwm.h index 4aad4eb..9bac10f 100644 --- a/include/linux/pwm.h +++ b/include/linux/pwm.h @@ -225,8 +225,8 @@ int pwm_apply_state(struct pwm_device *pwm, struct pwm_state *state); * * Returns: 0 on success or a negative error code on failure. */ -static inline int pwm_config(struct pwm_device *pwm, int duty_ns, - int period_ns) +static inline int __deprecated pwm_config(struct pwm_device *pwm, int duty_ns, + int period_ns) { struct pwm_state pstate;
@@ -252,8 +252,8 @@ static inline int pwm_config(struct pwm_device *pwm, int duty_ns, * * Returns: 0 on success or a negative error code on failure. */ -static inline int pwm_set_polarity(struct pwm_device *pwm, - enum pwm_polarity polarity) +static inline int __deprecated pwm_set_polarity(struct pwm_device *pwm, + enum pwm_polarity polarity) { struct pwm_state pstate;
@@ -284,7 +284,7 @@ static inline int pwm_set_polarity(struct pwm_device *pwm, * * Returns: 0 on success or a negative error code on failure. */ -static inline int pwm_enable(struct pwm_device *pwm) +static inline int __deprecated pwm_enable(struct pwm_device *pwm) { struct pwm_state pstate;
@@ -303,7 +303,7 @@ static inline int pwm_enable(struct pwm_device *pwm) * pwm_disable() - stop a PWM output toggling * @pwm: PWM device */ -static inline void pwm_disable(struct pwm_device *pwm) +static inline void __deprecated pwm_disable(struct pwm_device *pwm) { struct pwm_state pstate;
@@ -360,24 +360,24 @@ static inline int pwm_apply_state(struct pwm_device *pwm, return -ENOTSUPP; }
-static inline int pwm_config(struct pwm_device *pwm, int duty_ns, - int period_ns) +static inline int __deprecated pwm_config(struct pwm_device *pwm, int duty_ns, + int period_ns) { return -EINVAL; }
-static inline int pwm_set_polarity(struct pwm_device *pwm, - enum pwm_polarity polarity) +static inline int __deprecated pwm_set_polarity(struct pwm_device *pwm, + enum pwm_polarity polarity) { return -ENOTSUPP; }
-static inline int pwm_enable(struct pwm_device *pwm) +static inline int __deprecated pwm_enable(struct pwm_device *pwm) { return -EINVAL; }
-static inline void pwm_disable(struct pwm_device *pwm) +static inline void __deprecated pwm_disable(struct pwm_device *pwm) { }
Hi Boris,
On Wed, Mar 30, 2016 at 10:03:55PM +0200, Boris Brezillon wrote:
Prefix those function as deprecated to encourage all existing users to switch to pwm_apply_state().
Why not keep at least some of them as wrappers where we do not need to chnage several parameters at once? It is much easier to have a driver do:
error = pwm_enable(pwm); if (error) ...
rather than declaring the state variable, fectch it, adjust and then apply.
Signed-off-by: Boris Brezillon boris.brezillon@free-electrons.com
include/linux/pwm.h | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-)
diff --git a/include/linux/pwm.h b/include/linux/pwm.h index 4aad4eb..9bac10f 100644 --- a/include/linux/pwm.h +++ b/include/linux/pwm.h @@ -225,8 +225,8 @@ int pwm_apply_state(struct pwm_device *pwm, struct pwm_state *state);
- Returns: 0 on success or a negative error code on failure.
*/ -static inline int pwm_config(struct pwm_device *pwm, int duty_ns,
int period_ns)
+static inline int __deprecated pwm_config(struct pwm_device *pwm, int duty_ns,
int period_ns)
{ struct pwm_state pstate;
@@ -252,8 +252,8 @@ static inline int pwm_config(struct pwm_device *pwm, int duty_ns,
- Returns: 0 on success or a negative error code on failure.
*/ -static inline int pwm_set_polarity(struct pwm_device *pwm,
enum pwm_polarity polarity)
+static inline int __deprecated pwm_set_polarity(struct pwm_device *pwm,
enum pwm_polarity polarity)
{ struct pwm_state pstate;
@@ -284,7 +284,7 @@ static inline int pwm_set_polarity(struct pwm_device *pwm,
- Returns: 0 on success or a negative error code on failure.
*/ -static inline int pwm_enable(struct pwm_device *pwm) +static inline int __deprecated pwm_enable(struct pwm_device *pwm) { struct pwm_state pstate;
@@ -303,7 +303,7 @@ static inline int pwm_enable(struct pwm_device *pwm)
- pwm_disable() - stop a PWM output toggling
- @pwm: PWM device
*/ -static inline void pwm_disable(struct pwm_device *pwm) +static inline void __deprecated pwm_disable(struct pwm_device *pwm) { struct pwm_state pstate;
@@ -360,24 +360,24 @@ static inline int pwm_apply_state(struct pwm_device *pwm, return -ENOTSUPP; }
-static inline int pwm_config(struct pwm_device *pwm, int duty_ns,
int period_ns)
+static inline int __deprecated pwm_config(struct pwm_device *pwm, int duty_ns,
int period_ns)
{ return -EINVAL; }
-static inline int pwm_set_polarity(struct pwm_device *pwm,
enum pwm_polarity polarity)
+static inline int __deprecated pwm_set_polarity(struct pwm_device *pwm,
enum pwm_polarity polarity)
{ return -ENOTSUPP; }
-static inline int pwm_enable(struct pwm_device *pwm) +static inline int __deprecated pwm_enable(struct pwm_device *pwm) { return -EINVAL; }
-static inline void pwm_disable(struct pwm_device *pwm) +static inline void __deprecated pwm_disable(struct pwm_device *pwm) { }
-- 2.5.0
Hi Dmitry,
On Thu, 31 Mar 2016 10:38:58 -0700 Dmitry Torokhov dmitry.torokhov@gmail.com wrote:
Hi Boris,
On Wed, Mar 30, 2016 at 10:03:55PM +0200, Boris Brezillon wrote:
Prefix those function as deprecated to encourage all existing users to switch to pwm_apply_state().
Why not keep at least some of them as wrappers where we do not need to chnage several parameters at once? It is much easier to have a driver do:
error = pwm_enable(pwm); if (error) ...
rather than declaring the state variable, fectch it, adjust and then apply.
True. Actually deprecating the non-atomic API was not my primary goal. Thierry would you mind if we keep both APIs around?
Signed-off-by: Boris Brezillon boris.brezillon@free-electrons.com
include/linux/pwm.h | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-)
diff --git a/include/linux/pwm.h b/include/linux/pwm.h index 4aad4eb..9bac10f 100644 --- a/include/linux/pwm.h +++ b/include/linux/pwm.h @@ -225,8 +225,8 @@ int pwm_apply_state(struct pwm_device *pwm, struct pwm_state *state);
- Returns: 0 on success or a negative error code on failure.
*/ -static inline int pwm_config(struct pwm_device *pwm, int duty_ns,
int period_ns)
+static inline int __deprecated pwm_config(struct pwm_device *pwm, int duty_ns,
int period_ns)
{ struct pwm_state pstate;
@@ -252,8 +252,8 @@ static inline int pwm_config(struct pwm_device *pwm, int duty_ns,
- Returns: 0 on success or a negative error code on failure.
*/ -static inline int pwm_set_polarity(struct pwm_device *pwm,
enum pwm_polarity polarity)
+static inline int __deprecated pwm_set_polarity(struct pwm_device *pwm,
enum pwm_polarity polarity)
{ struct pwm_state pstate;
@@ -284,7 +284,7 @@ static inline int pwm_set_polarity(struct pwm_device *pwm,
- Returns: 0 on success or a negative error code on failure.
*/ -static inline int pwm_enable(struct pwm_device *pwm) +static inline int __deprecated pwm_enable(struct pwm_device *pwm) { struct pwm_state pstate;
@@ -303,7 +303,7 @@ static inline int pwm_enable(struct pwm_device *pwm)
- pwm_disable() - stop a PWM output toggling
- @pwm: PWM device
*/ -static inline void pwm_disable(struct pwm_device *pwm) +static inline void __deprecated pwm_disable(struct pwm_device *pwm) { struct pwm_state pstate;
@@ -360,24 +360,24 @@ static inline int pwm_apply_state(struct pwm_device *pwm, return -ENOTSUPP; }
-static inline int pwm_config(struct pwm_device *pwm, int duty_ns,
int period_ns)
+static inline int __deprecated pwm_config(struct pwm_device *pwm, int duty_ns,
int period_ns)
{ return -EINVAL; }
-static inline int pwm_set_polarity(struct pwm_device *pwm,
enum pwm_polarity polarity)
+static inline int __deprecated pwm_set_polarity(struct pwm_device *pwm,
enum pwm_polarity polarity)
{ return -ENOTSUPP; }
-static inline int pwm_enable(struct pwm_device *pwm) +static inline int __deprecated pwm_enable(struct pwm_device *pwm) { return -EINVAL; }
-static inline void pwm_disable(struct pwm_device *pwm) +static inline void __deprecated pwm_disable(struct pwm_device *pwm) { }
-- 2.5.0
Some PWM drivers are calling the deprecated pwm_disable() function in their pwm->free() or pdev->remove() function. Replace those calls by the pwm_apply_state().
Signed-off-by: Boris Brezillon boris.brezillon@free-electrons.com --- drivers/pwm/pwm-lpc18xx-sct.c | 7 +++++-- drivers/pwm/pwm-lpc32xx.c | 9 +++++++-- drivers/pwm/pwm-spear.c | 9 +++++++-- drivers/pwm/pwm-sti.c | 9 +++++++-- 4 files changed, 26 insertions(+), 8 deletions(-)
diff --git a/drivers/pwm/pwm-lpc18xx-sct.c b/drivers/pwm/pwm-lpc18xx-sct.c index 19dc64c..c4d7cb1 100644 --- a/drivers/pwm/pwm-lpc18xx-sct.c +++ b/drivers/pwm/pwm-lpc18xx-sct.c @@ -305,9 +305,12 @@ static void lpc18xx_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm) { struct lpc18xx_pwm_chip *lpc18xx_pwm = to_lpc18xx_pwm_chip(chip); struct lpc18xx_pwm_data *lpc18xx_data = pwm_get_chip_data(pwm); + struct pwm_state pstate;
- pwm_disable(pwm); - pwm_set_duty_cycle(pwm, 0); + pwm_get_state(pwm, &pstate); + pstate.duty_cycle = 0; + pstate.enabled = false; + pwm_apply_state(pwm, &pstate); clear_bit(lpc18xx_data->duty_event, &lpc18xx_pwm->event_map); }
diff --git a/drivers/pwm/pwm-lpc32xx.c b/drivers/pwm/pwm-lpc32xx.c index 4d470c1..95870e0 100644 --- a/drivers/pwm/pwm-lpc32xx.c +++ b/drivers/pwm/pwm-lpc32xx.c @@ -138,8 +138,13 @@ static int lpc32xx_pwm_remove(struct platform_device *pdev) struct lpc32xx_pwm_chip *lpc32xx = platform_get_drvdata(pdev); unsigned int i;
- for (i = 0; i < lpc32xx->chip.npwm; i++) - pwm_disable(&lpc32xx->chip.pwms[i]); + for (i = 0; i < lpc32xx->chip.npwm; i++) { + struct pwm_state pstate; + + pwm_get_state(&lpc32xx->chip.pwms[i], &pstate); + pstate.enabled = false; + pwm_apply_state(&lpc32xx->chip.pwms[i], &pstate); + }
return pwmchip_remove(&lpc32xx->chip); } diff --git a/drivers/pwm/pwm-spear.c b/drivers/pwm/pwm-spear.c index 6c6b44f..2ee4cd5 100644 --- a/drivers/pwm/pwm-spear.c +++ b/drivers/pwm/pwm-spear.c @@ -233,8 +233,13 @@ static int spear_pwm_remove(struct platform_device *pdev) struct spear_pwm_chip *pc = platform_get_drvdata(pdev); int i;
- for (i = 0; i < NUM_PWM; i++) - pwm_disable(&pc->chip.pwms[i]); + for (i = 0; i < NUM_PWM; i++) { + struct pwm_state pstate; + + pwm_get_state(&pc->chip.pwms[i], &pstate); + pstate.enabled = false; + pwm_apply_state(&pc->chip.pwms[i], &pstate); + }
/* clk was prepared in probe, hence unprepare it here */ clk_unprepare(pc->clk); diff --git a/drivers/pwm/pwm-sti.c b/drivers/pwm/pwm-sti.c index bea1d17..0ec44a8 100644 --- a/drivers/pwm/pwm-sti.c +++ b/drivers/pwm/pwm-sti.c @@ -435,8 +435,13 @@ static int sti_pwm_remove(struct platform_device *pdev) struct sti_pwm_chip *pc = platform_get_drvdata(pdev); unsigned int i;
- for (i = 0; i < pc->cdata->num_chan; i++) - pwm_disable(&pc->chip.pwms[i]); + for (i = 0; i < pc->cdata->num_chan; i++) { + struct pwm_state pstate; + + pwm_get_state(&pc->chip.pwms[i], &pstate); + pstate.enabled = false; + pwm_apply_state(&pc->chip.pwms[i], &pstate); + }
clk_unprepare(pc->clk);
pwm_config/enable/disable() have been deprecated and should be replaced by pwm_apply_state().
Signed-off-by: Boris Brezillon boris.brezillon@free-electrons.com --- drivers/clk/clk-pwm.c | 26 +++++++++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-)
diff --git a/drivers/clk/clk-pwm.c b/drivers/clk/clk-pwm.c index ebcd738..49ec5b1 100644 --- a/drivers/clk/clk-pwm.c +++ b/drivers/clk/clk-pwm.c @@ -28,15 +28,29 @@ static inline struct clk_pwm *to_clk_pwm(struct clk_hw *hw) static int clk_pwm_prepare(struct clk_hw *hw) { struct clk_pwm *clk_pwm = to_clk_pwm(hw); + struct pwm_state pstate;
- return pwm_enable(clk_pwm->pwm); + pwm_get_state(clk_pwm->pwm, &pstate); + if (pstate.enabled) + return 0; + + pstate.enabled = true; + + return pwm_apply_state(clk_pwm->pwm, &pstate); }
static void clk_pwm_unprepare(struct clk_hw *hw) { struct clk_pwm *clk_pwm = to_clk_pwm(hw); + struct pwm_state pstate; + + pwm_get_state(clk_pwm->pwm, &pstate); + if (!pstate.enabled) + return;
- pwm_disable(clk_pwm->pwm); + pstate.enabled = false; + + pwm_apply_state(clk_pwm->pwm, &pstate); }
static unsigned long clk_pwm_recalc_rate(struct clk_hw *hw, @@ -56,6 +70,7 @@ static const struct clk_ops clk_pwm_ops = { static int clk_pwm_probe(struct platform_device *pdev) { struct device_node *node = pdev->dev.of_node; + struct pwm_state pstate; struct pwm_args pargs = { }; struct clk_init_data init; struct clk_pwm *clk_pwm; @@ -88,7 +103,12 @@ static int clk_pwm_probe(struct platform_device *pdev) return -EINVAL; }
- ret = pwm_config(pwm, (pargs.period + 1) >> 1, pargs.period); + pwm_get_state(pwm, &pstate); + pstate.period = pargs.period; + pstate.polarity = pargs.polarity; + pstate.duty_cycle = (pargs.period + 1) >> 1; + + ret = pwm_apply_state(pwm, &pstate); if (ret < 0) return ret;
On 03/30, Boris Brezillon wrote:
diff --git a/drivers/clk/clk-pwm.c b/drivers/clk/clk-pwm.c index ebcd738..49ec5b1 100644 --- a/drivers/clk/clk-pwm.c +++ b/drivers/clk/clk-pwm.c @@ -28,15 +28,29 @@ static inline struct clk_pwm *to_clk_pwm(struct clk_hw *hw) static int clk_pwm_prepare(struct clk_hw *hw) { struct clk_pwm *clk_pwm = to_clk_pwm(hw);
- struct pwm_state pstate;
- return pwm_enable(clk_pwm->pwm);
- pwm_get_state(clk_pwm->pwm, &pstate);
- if (pstate.enabled)
return 0;
- pstate.enabled = true;
- return pwm_apply_state(clk_pwm->pwm, &pstate);
This doesn't seem atomic anymore if we're checking the state and then not calling apply_state if it's already enabled. But I assume this doesn't matter because we "own" the pwm here? Otherwise I would think this would be unconditional apply state and duplicates would be ignored in the pwm framework.
pwm_config/enable/disable() have been deprecated and should be replaced by pwm_apply_state().
Signed-off-by: Boris Brezillon boris.brezillon@free-electrons.com --- drivers/hwmon/pwm-fan.c | 81 ++++++++++++++++++++++++++++--------------------- 1 file changed, 46 insertions(+), 35 deletions(-)
diff --git a/drivers/hwmon/pwm-fan.c b/drivers/hwmon/pwm-fan.c index 82c5656..da4e4ab 100644 --- a/drivers/hwmon/pwm-fan.c +++ b/drivers/hwmon/pwm-fan.c @@ -40,8 +40,8 @@ struct pwm_fan_ctx {
static int __set_pwm(struct pwm_fan_ctx *ctx, unsigned long pwm) { + struct pwm_state pstate; struct pwm_args pargs = { }; - unsigned long duty; int ret = 0;
pwm_get_args(ctx->pwm, &pargs); @@ -50,19 +50,17 @@ static int __set_pwm(struct pwm_fan_ctx *ctx, unsigned long pwm) if (ctx->pwm_value == pwm) goto exit_set_pwm_err;
- duty = DIV_ROUND_UP(pwm * (pargs.period - 1), MAX_PWM); - ret = pwm_config(ctx->pwm, duty, pargs.period); - if (ret) - goto exit_set_pwm_err; - + pwm_get_state(ctx->pwm, &pstate); + pstate.period = pargs.period; + pstate.duty_cycle = DIV_ROUND_UP(pwm * (pargs.period - 1), MAX_PWM); if (pwm == 0) - pwm_disable(ctx->pwm); + pstate.enabled = false; + else + pstate.enabled = true;
- if (ctx->pwm_value == 0) { - ret = pwm_enable(ctx->pwm); - if (ret) - goto exit_set_pwm_err; - } + ret = pwm_apply_state(ctx->pwm, &pstate); + if (ret) + goto exit_set_pwm_err;
ctx->pwm_value = pwm; exit_set_pwm_err: @@ -217,10 +215,10 @@ static int pwm_fan_of_get_cooling_data(struct device *dev, static int pwm_fan_probe(struct platform_device *pdev) { struct thermal_cooling_device *cdev; + struct pwm_state pstate; struct pwm_args pargs = { }; struct pwm_fan_ctx *ctx; struct device *hwmon; - int duty_cycle; int ret;
ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL); @@ -239,27 +237,25 @@ static int pwm_fan_probe(struct platform_device *pdev)
/* Set duty cycle to maximum allowed */ pwm_get_args(ctx->pwm, &pargs); - duty_cycle = pargs.period - 1; + pwm_get_state(ctx->pwm, &pstate); + + pstate.period = pargs.period; + pstate.duty_cycle = pargs.period - 1; + pstate.enabled = true; ctx->pwm_value = MAX_PWM;
- ret = pwm_config(ctx->pwm, duty_cycle, pargs.period); + ret = pwm_apply_state(ctx->pwm, &pstate); if (ret) { dev_err(&pdev->dev, "Failed to configure PWM\n"); return ret; }
- /* Enbale PWM output */ - ret = pwm_enable(ctx->pwm); - if (ret) { - dev_err(&pdev->dev, "Failed to enable PWM\n"); - return ret; - } - hwmon = devm_hwmon_device_register_with_groups(&pdev->dev, "pwmfan", ctx, pwm_fan_groups); if (IS_ERR(hwmon)) { dev_err(&pdev->dev, "Failed to register hwmon device\n"); - pwm_disable(ctx->pwm); + pstate.enabled = false; + pwm_apply_state(ctx->pwm, &pstate); return PTR_ERR(hwmon); }
@@ -275,7 +271,8 @@ static int pwm_fan_probe(struct platform_device *pdev) if (IS_ERR(cdev)) { dev_err(&pdev->dev, "Failed to register pwm-fan as cooling device"); - pwm_disable(ctx->pwm); + pstate.enabled = false; + pwm_apply_state(ctx->pwm, &pstate); return PTR_ERR(cdev); } ctx->cdev = cdev; @@ -290,8 +287,14 @@ static int pwm_fan_remove(struct platform_device *pdev) struct pwm_fan_ctx *ctx = platform_get_drvdata(pdev);
thermal_cooling_device_unregister(ctx->cdev); - if (ctx->pwm_value) - pwm_disable(ctx->pwm); + if (ctx->pwm_value) { + struct pwm_state pstate; + + pwm_get_state(ctx->pwm, &pstate); + pstate.enabled = false; + pwm_apply_state(ctx->pwm, &pstate); + } + return 0; }
@@ -300,27 +303,35 @@ static int pwm_fan_suspend(struct device *dev) { struct pwm_fan_ctx *ctx = dev_get_drvdata(dev);
- if (ctx->pwm_value) - pwm_disable(ctx->pwm); + if (ctx->pwm_value) { + struct pwm_state pstate; + + pwm_get_state(ctx->pwm, &pstate); + pstate.enabled = false; + pwm_apply_state(ctx->pwm, &pstate); + } + return 0; }
static int pwm_fan_resume(struct device *dev) { struct pwm_fan_ctx *ctx = dev_get_drvdata(dev); + struct pwm_state pstate; struct pwm_args pargs = { }; - unsigned long duty; - int ret;
if (ctx->pwm_value == 0) return 0;
pwm_get_args(ctx->pwm, &pargs); - duty = DIV_ROUND_UP(ctx->pwm_value * (pargs.period - 1), MAX_PWM); - ret = pwm_config(ctx->pwm, duty, pargs.period); - if (ret) - return ret; - return pwm_enable(ctx->pwm); + pwm_get_state(ctx->pwm, &pstate); + + pstate.period = pargs.period; + pstate.duty_cycle = DIV_ROUND_UP(ctx->pwm_value * (pargs.period - 1), + MAX_PWM); + pstate.enabled = true; + + return pwm_apply_state(ctx->pwm, &pstate); } #endif
pwm_config/enable/disable() have been deprecated and should be replaced by pwm_apply_state().
Signed-off-by: Boris Brezillon boris.brezillon@free-electrons.com --- drivers/input/misc/max77693-haptic.c | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-)
diff --git a/drivers/input/misc/max77693-haptic.c b/drivers/input/misc/max77693-haptic.c index cf6aac0..aef7dc4 100644 --- a/drivers/input/misc/max77693-haptic.c +++ b/drivers/input/misc/max77693-haptic.c @@ -70,13 +70,16 @@ struct max77693_haptic {
static int max77693_haptic_set_duty_cycle(struct max77693_haptic *haptic) { + struct pwm_state pstate; struct pwm_args pargs = { }; - int delta; int error;
pwm_get_args(haptic->pwm_dev, &pargs); - delta = (pargs.period + haptic->pwm_duty) / 2; - error = pwm_config(haptic->pwm_dev, delta, pargs.period); + pwm_get_state(haptic->pwm_dev, &pstate); + + pstate.period = pargs.period; + pstate.duty_cycle = (pargs.period + haptic->pwm_duty) / 2; + error = pwm_apply_state(haptic->pwm_dev, &pstate); if (error) { dev_err(haptic->dev, "failed to configure pwm: %d\n", error); return error; @@ -161,12 +164,16 @@ static int max77693_haptic_lowsys(struct max77693_haptic *haptic, bool enable)
static void max77693_haptic_enable(struct max77693_haptic *haptic) { + struct pwm_state pstate; int error;
if (haptic->enabled) return;
- error = pwm_enable(haptic->pwm_dev); + pwm_get_state(haptic->pwm_dev, &pstate); + pstate.enabled = true; + + error = pwm_apply_state(haptic->pwm_dev, &pstate); if (error) { dev_err(haptic->dev, "failed to enable haptic pwm device: %d\n", error); @@ -188,11 +195,13 @@ static void max77693_haptic_enable(struct max77693_haptic *haptic) err_enable_config: max77693_haptic_lowsys(haptic, false); err_enable_lowsys: - pwm_disable(haptic->pwm_dev); + pstate.enabled = false; + pwm_apply_state(haptic->pwm_dev, &pstate); }
static void max77693_haptic_disable(struct max77693_haptic *haptic) { + struct pwm_state pstate; int error;
if (!haptic->enabled) @@ -206,7 +215,9 @@ static void max77693_haptic_disable(struct max77693_haptic *haptic) if (error) goto err_disable_lowsys;
- pwm_disable(haptic->pwm_dev); + pwm_get_state(haptic->pwm_dev, &pstate); + pstate.enabled = false; + pwm_apply_state(haptic->pwm_dev, &pstate); haptic->enabled = false;
return;
Hi Boris,
On Wed, Mar 30, 2016 at 10:03:59PM +0200, Boris Brezillon wrote:
pwm_config/enable/disable() have been deprecated and should be replaced by pwm_apply_state().
Signed-off-by: Boris Brezillon boris.brezillon@free-electrons.com
drivers/input/misc/max77693-haptic.c | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-)
diff --git a/drivers/input/misc/max77693-haptic.c b/drivers/input/misc/max77693-haptic.c index cf6aac0..aef7dc4 100644 --- a/drivers/input/misc/max77693-haptic.c +++ b/drivers/input/misc/max77693-haptic.c @@ -70,13 +70,16 @@ struct max77693_haptic {
static int max77693_haptic_set_duty_cycle(struct max77693_haptic *haptic) {
- struct pwm_state pstate; struct pwm_args pargs = { };
int delta; int error;
pwm_get_args(haptic->pwm_dev, &pargs);
delta = (pargs.period + haptic->pwm_duty) / 2;
error = pwm_config(haptic->pwm_dev, delta, pargs.period);
- pwm_get_state(haptic->pwm_dev, &pstate);
- pstate.period = pargs.period;
- pstate.duty_cycle = (pargs.period + haptic->pwm_duty) / 2;
- error = pwm_apply_state(haptic->pwm_dev, &pstate);
This does not make sense with regard to the atomic API. If you look in max77693_haptic_play_work(), right after calling max77693_haptic_set_duty_cycle() we either try to enable or disable the pwm. When switching to this new API we should combine both actions.
if (error) { dev_err(haptic->dev, "failed to configure pwm: %d\n", error); return error; @@ -161,12 +164,16 @@ static int max77693_haptic_lowsys(struct max77693_haptic *haptic, bool enable)
static void max77693_haptic_enable(struct max77693_haptic *haptic) {
struct pwm_state pstate; int error;
if (haptic->enabled) return;
- error = pwm_enable(haptic->pwm_dev);
- pwm_get_state(haptic->pwm_dev, &pstate);
- pstate.enabled = true;
- error = pwm_apply_state(haptic->pwm_dev, &pstate);
As I mentioned I'd rather we did not deprecate pwm_enable() and pwm_disable() (and maybe others), as it forces us to add unnecessary boilerplate code to the drivers.
if (error) { dev_err(haptic->dev, "failed to enable haptic pwm device: %d\n", error); @@ -188,11 +195,13 @@ static void max77693_haptic_enable(struct max77693_haptic *haptic) err_enable_config: max77693_haptic_lowsys(haptic, false); err_enable_lowsys:
- pwm_disable(haptic->pwm_dev);
- pstate.enabled = false;
- pwm_apply_state(haptic->pwm_dev, &pstate);
}
static void max77693_haptic_disable(struct max77693_haptic *haptic) {
struct pwm_state pstate; int error;
if (!haptic->enabled)
@@ -206,7 +215,9 @@ static void max77693_haptic_disable(struct max77693_haptic *haptic) if (error) goto err_disable_lowsys;
- pwm_disable(haptic->pwm_dev);
- pwm_get_state(haptic->pwm_dev, &pstate);
- pstate.enabled = false;
- pwm_apply_state(haptic->pwm_dev, &pstate);
Same here.
haptic->enabled = false;
return;
2.5.0
Thanks.
On Thu, 31 Mar 2016 10:48:01 -0700 Dmitry Torokhov dmitry.torokhov@gmail.com wrote:
Hi Boris,
On Wed, Mar 30, 2016 at 10:03:59PM +0200, Boris Brezillon wrote:
pwm_config/enable/disable() have been deprecated and should be replaced by pwm_apply_state().
Signed-off-by: Boris Brezillon boris.brezillon@free-electrons.com
drivers/input/misc/max77693-haptic.c | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-)
diff --git a/drivers/input/misc/max77693-haptic.c b/drivers/input/misc/max77693-haptic.c index cf6aac0..aef7dc4 100644 --- a/drivers/input/misc/max77693-haptic.c +++ b/drivers/input/misc/max77693-haptic.c @@ -70,13 +70,16 @@ struct max77693_haptic {
static int max77693_haptic_set_duty_cycle(struct max77693_haptic *haptic) {
- struct pwm_state pstate; struct pwm_args pargs = { };
int delta; int error;
pwm_get_args(haptic->pwm_dev, &pargs);
delta = (pargs.period + haptic->pwm_duty) / 2;
error = pwm_config(haptic->pwm_dev, delta, pargs.period);
- pwm_get_state(haptic->pwm_dev, &pstate);
- pstate.period = pargs.period;
- pstate.duty_cycle = (pargs.period + haptic->pwm_duty) / 2;
- error = pwm_apply_state(haptic->pwm_dev, &pstate);
This does not make sense with regard to the atomic API. If you look in max77693_haptic_play_work(), right after calling max77693_haptic_set_duty_cycle() we either try to enable or disable the pwm. When switching to this new API we should combine both actions.
True. I'll address that, unless Thierry is fine keeping the non-atomic API, in which case I'll just drop patches 32 to 46.
if (error) { dev_err(haptic->dev, "failed to configure pwm: %d\n", error); return error; @@ -161,12 +164,16 @@ static int max77693_haptic_lowsys(struct max77693_haptic *haptic, bool enable)
static void max77693_haptic_enable(struct max77693_haptic *haptic) {
struct pwm_state pstate; int error;
if (haptic->enabled) return;
- error = pwm_enable(haptic->pwm_dev);
- pwm_get_state(haptic->pwm_dev, &pstate);
- pstate.enabled = true;
- error = pwm_apply_state(haptic->pwm_dev, &pstate);
As I mentioned I'd rather we did not deprecate pwm_enable() and pwm_disable() (and maybe others), as it forces us to add unnecessary boilerplate code to the drivers.
if (error) { dev_err(haptic->dev, "failed to enable haptic pwm device: %d\n", error); @@ -188,11 +195,13 @@ static void max77693_haptic_enable(struct max77693_haptic *haptic) err_enable_config: max77693_haptic_lowsys(haptic, false); err_enable_lowsys:
- pwm_disable(haptic->pwm_dev);
- pstate.enabled = false;
- pwm_apply_state(haptic->pwm_dev, &pstate);
}
static void max77693_haptic_disable(struct max77693_haptic *haptic) {
struct pwm_state pstate; int error;
if (!haptic->enabled)
@@ -206,7 +215,9 @@ static void max77693_haptic_disable(struct max77693_haptic *haptic) if (error) goto err_disable_lowsys;
- pwm_disable(haptic->pwm_dev);
- pwm_get_state(haptic->pwm_dev, &pstate);
- pstate.enabled = false;
- pwm_apply_state(haptic->pwm_dev, &pstate);
Same here.
haptic->enabled = false;
return;
2.5.0
Thanks.
pwm_config/enable/disable() have been deprecated and should be replaced by pwm_apply_state().
Signed-off-by: Boris Brezillon boris.brezillon@free-electrons.com --- drivers/input/misc/max8997_haptic.c | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-)
diff --git a/drivers/input/misc/max8997_haptic.c b/drivers/input/misc/max8997_haptic.c index a806ba3..fcc0eb7 100644 --- a/drivers/input/misc/max8997_haptic.c +++ b/drivers/input/misc/max8997_haptic.c @@ -72,8 +72,12 @@ static int max8997_haptic_set_duty_cycle(struct max8997_haptic *chip) int ret = 0;
if (chip->mode == MAX8997_EXTERNAL_MODE) { - unsigned int duty = chip->pwm_period * chip->level / 100; - ret = pwm_config(chip->pwm, duty, chip->pwm_period); + struct pwm_state pstate; + + pwm_get_state(chip->pwm, &pstate); + pstate.period = chip->pwm_period; + pstate.duty_cycle = chip->pwm_period * chip->level / 100; + ret = pwm_apply_state(chip->pwm, &pstate); } else { int i; u8 duty_index = 0; @@ -188,7 +192,11 @@ static void max8997_haptic_enable(struct max8997_haptic *chip) } max8997_haptic_configure(chip); if (chip->mode == MAX8997_EXTERNAL_MODE) { - error = pwm_enable(chip->pwm); + struct pwm_state pstate; + + pwm_get_state(chip->pwm, &pstate); + pstate.enabled = true; + error = pwm_apply_state(chip->pwm, &pstate); if (error) { dev_err(chip->dev, "Failed to enable PWM\n"); regulator_disable(chip->regulator); @@ -209,8 +217,13 @@ static void max8997_haptic_disable(struct max8997_haptic *chip) if (chip->enabled) { chip->enabled = false; max8997_haptic_configure(chip); - if (chip->mode == MAX8997_EXTERNAL_MODE) - pwm_disable(chip->pwm); + if (chip->mode == MAX8997_EXTERNAL_MODE) { + struct pwm_state pstate; + + pwm_get_state(chip->pwm, &pstate); + pstate.enabled = false; + pwm_apply_state(chip->pwm, &pstate); + } regulator_disable(chip->regulator); }
pwm_config/enable/disable() have been deprecated and should be replaced by pwm_apply_state().
Signed-off-by: Boris Brezillon boris.brezillon@free-electrons.com --- drivers/input/misc/pwm-beeper.c | 46 +++++++++++++++++++++++++++-------------- 1 file changed, 31 insertions(+), 15 deletions(-)
diff --git a/drivers/input/misc/pwm-beeper.c b/drivers/input/misc/pwm-beeper.c index f2261ab..36c9897 100644 --- a/drivers/input/misc/pwm-beeper.c +++ b/drivers/input/misc/pwm-beeper.c @@ -34,7 +34,7 @@ static int pwm_beeper_event(struct input_dev *input, { int ret = 0; struct pwm_beeper *beeper = input_get_drvdata(input); - unsigned long period; + struct pwm_state pstate;
if (type != EV_SND || value < 0) return -EINVAL; @@ -49,19 +49,22 @@ static int pwm_beeper_event(struct input_dev *input, return -EINVAL; }
+ pwm_get_state(beeper->pwm, &pstate); + if (value == 0) { - pwm_disable(beeper->pwm); + pstate.enabled = false; } else { - period = HZ_TO_NANOSECONDS(value); - ret = pwm_config(beeper->pwm, period / 2, period); - if (ret) - return ret; - ret = pwm_enable(beeper->pwm); - if (ret) - return ret; - beeper->period = period; + pstate.enabled = false; + pstate.period = HZ_TO_NANOSECONDS(value); + pstate.duty_cycle = pstate.period / 2; }
+ ret = pwm_apply_state(beeper->pwm, &pstate); + if (ret) + return ret; + + beeper->period = value ? pstate.period : 0; + return 0; }
@@ -132,10 +135,13 @@ err_free: static int pwm_beeper_remove(struct platform_device *pdev) { struct pwm_beeper *beeper = platform_get_drvdata(pdev); + struct pwm_state pstate;
input_unregister_device(beeper->input);
- pwm_disable(beeper->pwm); + pwm_get_state(beeper->pwm, &pstate); + pstate.enabled = false; + pwm_apply_state(beeper->pwm, &pstate); pwm_free(beeper->pwm);
kfree(beeper); @@ -147,8 +153,13 @@ static int __maybe_unused pwm_beeper_suspend(struct device *dev) { struct pwm_beeper *beeper = dev_get_drvdata(dev);
- if (beeper->period) - pwm_disable(beeper->pwm); + if (beeper->period) { + struct pwm_state pstate; + + pwm_get_state(beeper->pwm, &pstate); + pstate.enabled = false; + pwm_apply_state(beeper->pwm, &pstate); + }
return 0; } @@ -158,8 +169,13 @@ static int __maybe_unused pwm_beeper_resume(struct device *dev) struct pwm_beeper *beeper = dev_get_drvdata(dev);
if (beeper->period) { - pwm_config(beeper->pwm, beeper->period / 2, beeper->period); - pwm_enable(beeper->pwm); + struct pwm_state pstate; + + pwm_get_state(beeper->pwm, &pstate); + pstate.period = beeper->period; + pstate.duty_cycle = beeper->period / 2; + pstate.enabled = true; + pwm_apply_state(beeper->pwm, &pstate); }
return 0;
pwm_config/enable/disable() have been deprecated and should be replaced by pwm_apply_state().
Signed-off-by: Boris Brezillon boris.brezillon@free-electrons.com --- drivers/leds/leds-pwm.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-)
diff --git a/drivers/leds/leds-pwm.c b/drivers/leds/leds-pwm.c index b48231c..f69b222 100644 --- a/drivers/leds/leds-pwm.c +++ b/drivers/leds/leds-pwm.c @@ -40,13 +40,18 @@ struct led_pwm_priv { static void __led_pwm_set(struct led_pwm_data *led_dat) { int new_duty = led_dat->duty; + struct pwm_state pstate;
- pwm_config(led_dat->pwm, new_duty, led_dat->period); + pwm_get_state(led_dat->pwm, &pstate); + pstate.duty_cycle = new_duty; + pstate.period = led_dat->period;
if (new_duty == 0) - pwm_disable(led_dat->pwm); + pstate.enabled = false; else - pwm_enable(led_dat->pwm); + pstate.enabled = true; + + pwm_apply_state(led_dat->pwm, &pstate); }
static void led_pwm_set(struct led_classdev *led_cdev,
pwm_config/enable/disable() have been deprecated and should be replaced by pwm_apply_state().
Signed-off-by: Boris Brezillon boris.brezillon@free-electrons.com --- drivers/video/backlight/lm3630a_bl.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-)
diff --git a/drivers/video/backlight/lm3630a_bl.c b/drivers/video/backlight/lm3630a_bl.c index 3d16bd6..fdad23c 100644 --- a/drivers/video/backlight/lm3630a_bl.c +++ b/drivers/video/backlight/lm3630a_bl.c @@ -162,14 +162,16 @@ static int lm3630a_intr_config(struct lm3630a_chip *pchip)
static void lm3630a_pwm_ctrl(struct lm3630a_chip *pchip, int br, int br_max) { - unsigned int period = pchip->pdata->pwm_period; - unsigned int duty = br * period / br_max; + struct pwm_state pstate;
- pwm_config(pchip->pwmd, duty, period); - if (duty) - pwm_enable(pchip->pwmd); + pwm_get_state(pchip->pwmd, &pstate); + pstate.period = pchip->pdata->pwm_period; + pstate.duty_cycle = br * pstate.period / br_max; + if (pstate.duty_cycle) + pstate.enabled = true; else - pwm_disable(pchip->pwmd); + pstate.enabled = false; + pwm_apply_state(pchip->pwmd, &pstate); }
/* update and get brightness */
pwm_config/enable/disable() have been deprecated and should be replaced by pwm_apply_state().
Signed-off-by: Boris Brezillon boris.brezillon@free-electrons.com --- drivers/video/backlight/lp855x_bl.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-)
diff --git a/drivers/video/backlight/lp855x_bl.c b/drivers/video/backlight/lp855x_bl.c index daca9e6..5468e7a 100644 --- a/drivers/video/backlight/lp855x_bl.c +++ b/drivers/video/backlight/lp855x_bl.c @@ -235,8 +235,7 @@ err:
static void lp855x_pwm_ctrl(struct lp855x *lp, int br, int max_br) { - unsigned int period = lp->pdata->period_ns; - unsigned int duty = br * period / max_br; + struct pwm_state pstate; struct pwm_device *pwm;
/* request pwm device with the consumer name */ @@ -248,11 +247,15 @@ static void lp855x_pwm_ctrl(struct lp855x *lp, int br, int max_br) lp->pwm = pwm; }
- pwm_config(lp->pwm, duty, period); - if (duty) - pwm_enable(lp->pwm); + pwm_get_state(lp->pwm, &pstate); + pstate.period = lp->pdata->period_ns; + pstate.duty_cycle = br * pstate.period / max_br; + if (pstate.duty_cycle) + pstate.enabled = true; else - pwm_disable(lp->pwm); + pstate.enabled = false; + + pwm_apply_state(lp->pwm, &pstate); }
static int lp855x_bl_update_status(struct backlight_device *bl)
pwm_config/enable/disable() have been deprecated and should be replaced by pwm_apply_state().
Signed-off-by: Boris Brezillon boris.brezillon@free-electrons.com --- drivers/video/backlight/lp8788_bl.c | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-)
diff --git a/drivers/video/backlight/lp8788_bl.c b/drivers/video/backlight/lp8788_bl.c index 5d583d7..521c730 100644 --- a/drivers/video/backlight/lp8788_bl.c +++ b/drivers/video/backlight/lp8788_bl.c @@ -124,16 +124,13 @@ static int lp8788_backlight_configure(struct lp8788_bl *bl)
static void lp8788_pwm_ctrl(struct lp8788_bl *bl, int br, int max_br) { - unsigned int period; - unsigned int duty; struct device *dev; + struct pwm_state pstate; struct pwm_device *pwm;
if (!bl->pdata) return;
- period = bl->pdata->period_ns; - duty = br * period / max_br; dev = bl->lp->dev;
/* request PWM device with the consumer name */ @@ -147,11 +144,15 @@ static void lp8788_pwm_ctrl(struct lp8788_bl *bl, int br, int max_br) bl->pwm = pwm; }
- pwm_config(bl->pwm, duty, period); - if (duty) - pwm_enable(bl->pwm); + pwm_get_state(bl->pwm, &pstate); + pstate.period = bl->pdata->period_ns; + pstate.duty_cycle = br * pstate.period / max_br; + if (pstate.duty_cycle) + pstate.enabled = true; else - pwm_disable(bl->pwm); + pstate.enabled = false; + + pwm_apply_state(bl->pwm, &pstate); }
static int lp8788_bl_update_status(struct backlight_device *bl_dev)
pwm_config/enable/disable() have been deprecated and should be replaced by pwm_apply_state().
Signed-off-by: Boris Brezillon boris.brezillon@free-electrons.com --- drivers/video/backlight/pwm_bl.c | 43 ++++++++++++++++++++++------------------ 1 file changed, 24 insertions(+), 19 deletions(-)
diff --git a/drivers/video/backlight/pwm_bl.c b/drivers/video/backlight/pwm_bl.c index 2479c11..b069fb2 100644 --- a/drivers/video/backlight/pwm_bl.c +++ b/drivers/video/backlight/pwm_bl.c @@ -43,8 +43,22 @@ struct pwm_bl_data { void (*exit)(struct device *); };
+static int compute_duty_cycle(struct pwm_bl_data *pb, int brightness) +{ + unsigned int lth = pb->lth_brightness; + int duty_cycle; + + if (pb->levels) + duty_cycle = pb->levels[brightness]; + else + duty_cycle = brightness; + + return (duty_cycle * (pb->period - lth) / pb->scale) + lth; +} + static void pwm_backlight_power_on(struct pwm_bl_data *pb, int brightness) { + struct pwm_state pstate; int err;
if (pb->enabled) @@ -57,17 +71,24 @@ static void pwm_backlight_power_on(struct pwm_bl_data *pb, int brightness) if (pb->enable_gpio) gpiod_set_value(pb->enable_gpio, 1);
- pwm_enable(pb->pwm); + pwm_get_state(pb->pwm, &pstate); + pstate.duty_cycle = compute_duty_cycle(pb, brightness); + pstate.enabled = true; + pwm_apply_state(pb->pwm, &pstate); pb->enabled = true; }
static void pwm_backlight_power_off(struct pwm_bl_data *pb) { + struct pwm_state pstate; + if (!pb->enabled) return;
- pwm_config(pb->pwm, 0, pb->period); - pwm_disable(pb->pwm); + pwm_get_state(pb->pwm, &pstate); + pstate.duty_cycle = 0; + pstate.enabled = false; + pwm_apply_state(pb->pwm, &pstate);
if (pb->enable_gpio) gpiod_set_value(pb->enable_gpio, 0); @@ -76,24 +97,10 @@ static void pwm_backlight_power_off(struct pwm_bl_data *pb) pb->enabled = false; }
-static int compute_duty_cycle(struct pwm_bl_data *pb, int brightness) -{ - unsigned int lth = pb->lth_brightness; - int duty_cycle; - - if (pb->levels) - duty_cycle = pb->levels[brightness]; - else - duty_cycle = brightness; - - return (duty_cycle * (pb->period - lth) / pb->scale) + lth; -} - static int pwm_backlight_update_status(struct backlight_device *bl) { struct pwm_bl_data *pb = bl_get_data(bl); int brightness = bl->props.brightness; - int duty_cycle;
if (bl->props.power != FB_BLANK_UNBLANK || bl->props.fb_blank != FB_BLANK_UNBLANK || @@ -104,8 +111,6 @@ static int pwm_backlight_update_status(struct backlight_device *bl) brightness = pb->notify(pb->dev, brightness);
if (brightness > 0) { - duty_cycle = compute_duty_cycle(pb, brightness); - pwm_config(pb->pwm, duty_cycle, pb->period); pwm_backlight_power_on(pb, brightness); } else pwm_backlight_power_off(pb);
pwm_config/enable/disable() have been deprecated and should be replaced by pwm_apply_state().
Signed-off-by: Boris Brezillon boris.brezillon@free-electrons.com --- drivers/video/fbdev/ssd1307fb.c | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-)
diff --git a/drivers/video/fbdev/ssd1307fb.c b/drivers/video/fbdev/ssd1307fb.c index df9c63a..ed9a115 100644 --- a/drivers/video/fbdev/ssd1307fb.c +++ b/drivers/video/fbdev/ssd1307fb.c @@ -289,6 +289,8 @@ static int ssd1307fb_init(struct ssd1307fb_par *par) struct pwm_args pargs = { };
if (par->device_info->need_pwm) { + struct pwm_state pstate; + par->pwm = pwm_get(&par->client->dev, NULL); if (IS_ERR(par->pwm)) { dev_err(&par->client->dev, "Could not get PWM from device tree!\n"); @@ -296,10 +298,14 @@ static int ssd1307fb_init(struct ssd1307fb_par *par) }
pwm_get_args(par->pwm, &pargs); + pwm_get_state(par->pwm, &pstate); par->pwm_period = pargs.period; + /* Enable the PWM */ - pwm_config(par->pwm, par->pwm_period / 2, par->pwm_period); - pwm_enable(par->pwm); + pstate.period = pargs.period; + pstate.duty_cycle = pstate.period / 2; + pstate.enabled = true; + pwm_apply_state(par->pwm, &pstate);
dev_dbg(&par->client->dev, "Using PWM%d with a %dns period.\n", par->pwm->pwm, par->pwm_period); @@ -685,8 +691,12 @@ static int ssd1307fb_probe(struct i2c_client *client, bl_init_error: unregister_framebuffer(info); panel_init_error: - if (par->device_info->need_pwm) { - pwm_disable(par->pwm); + if (par->device_info->need_pwm && par->pwm) { + struct pwm_state pstate; + + pwm_get_state(par->pwm, &pstate); + pstate.enabled = false; + pwm_apply_state(par->pwm, &pstate); pwm_put(par->pwm); }; reset_oled_error: @@ -707,7 +717,11 @@ static int ssd1307fb_remove(struct i2c_client *client)
unregister_framebuffer(info); if (par->device_info->need_pwm) { - pwm_disable(par->pwm); + struct pwm_state pstate; + + pwm_get_state(par->pwm, &pstate); + pstate.enabled = false; + pwm_apply_state(par->pwm, &pstate); pwm_put(par->pwm); }; fb_deferred_io_cleanup(info);
pwm_config/enable/disable() have been deprecated and should be replaced by pwm_apply_state().
Signed-off-by: Boris Brezillon boris.brezillon@free-electrons.com --- drivers/gpu/drm/i915/intel_panel.c | 39 +++++++++++++++++++++++++------------- 1 file changed, 26 insertions(+), 13 deletions(-)
diff --git a/drivers/gpu/drm/i915/intel_panel.c b/drivers/gpu/drm/i915/intel_panel.c index 21ee647..b86bd20 100644 --- a/drivers/gpu/drm/i915/intel_panel.c +++ b/drivers/gpu/drm/i915/intel_panel.c @@ -538,10 +538,10 @@ static u32 bxt_get_backlight(struct intel_connector *connector) static u32 pwm_get_backlight(struct intel_connector *connector) { struct intel_panel *panel = &connector->panel; - int duty_ns; + struct pwm_state pstate;
- duty_ns = pwm_get_duty_cycle(panel->backlight.pwm); - return DIV_ROUND_UP(duty_ns * 100, CRC_PMIC_PWM_PERIOD_NS); + pwm_get_state(panel->backlight.pwm, &pstate); + return DIV_ROUND_UP(pstate.duty_cycle * 100, CRC_PMIC_PWM_PERIOD_NS); }
static u32 intel_panel_get_backlight(struct intel_connector *connector) @@ -630,9 +630,12 @@ static void bxt_set_backlight(struct intel_connector *connector, u32 level) static void pwm_set_backlight(struct intel_connector *connector, u32 level) { struct intel_panel *panel = &connector->panel; - int duty_ns = DIV_ROUND_UP(level * CRC_PMIC_PWM_PERIOD_NS, 100); + struct pwm_state pstate;
- pwm_config(panel->backlight.pwm, duty_ns, CRC_PMIC_PWM_PERIOD_NS); + pwm_get_state(panel->backlight.pwm, &pstate); + pstate.duty_cycle = DIV_ROUND_UP(level * CRC_PMIC_PWM_PERIOD_NS, 100); + pstate.period = CRC_PMIC_PWM_PERIOD_NS; + pwm_apply_state(panel->backlight.pwm, &pstate); }
static void @@ -801,11 +804,15 @@ static void bxt_disable_backlight(struct intel_connector *connector) static void pwm_disable_backlight(struct intel_connector *connector) { struct intel_panel *panel = &connector->panel; + struct pwm_state pstate;
/* Disable the backlight */ - pwm_config(panel->backlight.pwm, 0, CRC_PMIC_PWM_PERIOD_NS); + pwm_get_state(panel->backlight.pwm, &pstate); + pstate.duty_cycle = 0; + pwm_apply_state(panel->backlight.pwm, &pstate); usleep_range(2000, 3000); - pwm_disable(panel->backlight.pwm); + pstate.enabled = false; + pwm_apply_state(panel->backlight.pwm, &pstate); }
void intel_panel_disable_backlight(struct intel_connector *connector) @@ -1068,8 +1075,11 @@ static void bxt_enable_backlight(struct intel_connector *connector) static void pwm_enable_backlight(struct intel_connector *connector) { struct intel_panel *panel = &connector->panel; + struct pwm_state pstate;
- pwm_enable(panel->backlight.pwm); + pwm_get_state(panel->backlight.pwm, &pstate); + pstate.enabled = true; + pwm_apply_state(panel->backlight.pwm, &pstate); intel_panel_actually_set_backlight(connector, panel->backlight.level); }
@@ -1630,6 +1640,7 @@ static int pwm_setup_backlight(struct intel_connector *connector, { struct drm_device *dev = connector->base.dev; struct intel_panel *panel = &connector->panel; + struct pwm_state pstate; int retval;
/* Get the PWM chip for backlight control */ @@ -1640,8 +1651,10 @@ static int pwm_setup_backlight(struct intel_connector *connector, return -ENODEV; }
- retval = pwm_config(panel->backlight.pwm, CRC_PMIC_PWM_PERIOD_NS, - CRC_PMIC_PWM_PERIOD_NS); + pwm_get_state(panel->backlight.pwm, &pstate); + pstate.period = CRC_PMIC_PWM_PERIOD_NS; + pstate.duty_cycle = CRC_PMIC_PWM_PERIOD_NS; + retval = pwm_apply_state(panel->backlight.pwm, &pstate); if (retval < 0) { DRM_ERROR("Failed to configure the pwm chip\n"); pwm_put(panel->backlight.pwm); @@ -1651,9 +1664,9 @@ static int pwm_setup_backlight(struct intel_connector *connector,
panel->backlight.min = 0; /* 0% */ panel->backlight.max = 100; /* 100% */ - panel->backlight.level = DIV_ROUND_UP( - pwm_get_duty_cycle(panel->backlight.pwm) * 100, - CRC_PMIC_PWM_PERIOD_NS); + pwm_get_state(panel->backlight.pwm, &pstate); + panel->backlight.level = DIV_ROUND_UP(pstate.duty_cycle * 100, + pstate.period); panel->backlight.enabled = panel->backlight.level != 0;
return 0;
Replace pwm_disable/enable/config() by pwm_apply_state().
Signed-off-by: Boris Brezillon boris.brezillon@free-electrons.com --- arch/arm/mach-s3c24xx/mach-rx1950.c | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-)
diff --git a/arch/arm/mach-s3c24xx/mach-rx1950.c b/arch/arm/mach-s3c24xx/mach-rx1950.c index 774c982..2dc9487 100644 --- a/arch/arm/mach-s3c24xx/mach-rx1950.c +++ b/arch/arm/mach-s3c24xx/mach-rx1950.c @@ -384,10 +384,15 @@ static struct pwm_device *lcd_pwm;
static void rx1950_lcd_power(int enable) { + struct pwm_state pstate; int i; static int enabled; + if (enabled == enable) return; + + pwm_get_state(lcd_pwm, &pstate); + if (!enable) {
/* GPC11-GPC15->OUTPUT */ @@ -433,15 +438,21 @@ static void rx1950_lcd_power(int enable)
/* GPB1->OUTPUT, GPB1->0 */ gpio_direction_output(S3C2410_GPB(1), 0); - pwm_config(lcd_pwm, 0, LCD_PWM_PERIOD); + + pstate.enabled = false; + pstate.period = LCD_PWM_PERIOD; + pstate.duty_cycle = 0; + pwm_apply_state(lcd_pwm, &pstate); pwm_disable(lcd_pwm);
/* GPC0->0, GPC10->0 */ gpio_direction_output(S3C2410_GPC(0), 0); gpio_direction_output(S3C2410_GPC(10), 0); } else { - pwm_config(lcd_pwm, LCD_PWM_DUTY, LCD_PWM_PERIOD); - pwm_enable(lcd_pwm); + pstate.enabled = true; + pstate.period = LCD_PWM_PERIOD; + pstate.duty_cycle = LCD_PWM_DUTY; + pwm_apply_state(lcd_pwm, &pstate);
gpio_direction_output(S3C2410_GPC(0), 1); gpio_direction_output(S3C2410_GPC(5), 1);
+Doug (sorry, I forgot to add you in to the recipient list)
On Wed, 30 Mar 2016 22:03:23 +0200 Boris Brezillon boris.brezillon@free-electrons.com wrote:
Hello,
This series adds support for atomic PWM update, or IOW, the capability to update all the parameters of a PWM device (enabled/disabled, period, duty and polarity) in one go.
It also adds support for initial PWM state retrieval (or hardware readout), which should allow smooth handover between the bootloader and Linux. For example, critical PWM users (like critical regulators controlled by a PWM) can query the current PWM state, and adapt the PWM config without having to disable/enable the PWM, or abruptly change the period/dutycyle/polarity config.
Thierry, I hope this version meets your expectations, if that's not the case, could you let me know quickly so I can adjust the implementation accordingly (I'd really like to get most of those changes in 4.7).
Oh, I forgot to mention that I'm not necessarily expecting all those patches to be taken in one go. We only need patches 1 to 31 for the problem exposed by Doug. Patch 32 is deprecating the non-atomic APIs and patches 33 to 46 are switching all PWM users to the atomic APIs to avoid compilation warnings. It's up to you to decide which you'd like to take.
Best Regards,
Boris
Changes since v4:
- introduce pwm_args to expose per-board/platform config
- deprecate non-atomic APIs
- implement non-atomic functions as wrappers around atomic ones
- patch all PWM users to use the atomic API
- rename the ->reset_state() hook into ->get_state()
- drop most acks
- rework PWM config in the pwm-regulator driver
- patch sun4i and sti PWM drivers to support HW readout
Changes since v3:
- rebased on pwm/for-next after pulling 4.4-rc1
- replace direct access to pwm fields by pwm_get/set_xxx() helpers, thus fixing some build errors
- split changes to allow each maintainer to review/ack or take the modification through its subsystem
Changes since v2:
- rebased on top of 4.3-rc2
- reintroduced pwm-regulator patches
Changes since v1:
- dropped applied patches
- squashed Heiko's fixes into the rockchip driver changes
- made a few cosmetic changes
- added kerneldoc comments
- added Heiko's patch to display more information in debugfs
- dropped pwm-regulator patches (should be submitted separately)
*** BLURB HERE ***
Boris Brezillon (45): pwm: rcar: make use of pwm_is_enabled() backlight: pwm_bl: remove useless call to pwm_set_period() backlight: lm3630a_bl: stop messing with the pwm->period field pwm: get rid of pwm->lock pwm: introduce the pwm_args concept pwm: use pwm_get/set_xxx() helpers where appropriate clk: pwm: use pwm_get_args() where appropriate hwmon: pwm-fan: use pwm_get_args() where appropriate misc: max77693-haptic: use pwm_get_args() where appropriate leds: pwm: use pwm_get_args() where appropriate regulator: pwm: use pwm_get_args() where appropriate fbdev: ssd1307fb: use pwm_get_args() where appropriate backlight: pwm_bl: use pwm_get_args() where appropriate pwm: keep PWM state in sync with hardware state pwm: introduce the pwm_state concept pwm: move the enabled/disabled info into pwm_state pwm: add the PWM initial state retrieval infra pwm: add the core infrastructure to allow atomic update pwm: switch to the atomic API pwm: rockchip: add initial state retrieval pwm: rockchip: avoid glitches on already running PWMs pwm: rockchip: add support for atomic update pwm: sti: add support for initial state retrieval pwm: sti: avoid glitches on already running PWMs pwm: sun4i: implement hardware readout regulator: pwm: adjust PWM config at probe time regulator: pwm: swith to the atomic PWM API regulator: pwm: properly initialize the ->state field regulator: pwm: retrieve correct voltage pwm: update documentation pwm: deprecate pwm_config(), pwm_enable() and pwm_disable() pwm: replace pwm_disable() by pwm_apply_state() clk: pwm: switch to the atomic API hwmon: pwm-fan: switch to the atomic API input: misc: max77693: switch to the atomic API input: misc: max8997: switch to the atomic PWM API input: misc: pwm-beeper: switch to the atomic PWM API leds: pwm: switch to the atomic PWM API backlight: lm3630a: switch to the atomic PWM API backlight: lp855x: switch to the atomic PWM API backlight: lp8788: switch to the atomic PWM API backlight: pwm_bl: switch to the atomic PWM API video: ssd1307fb: switch to the atomic PWM API drm: i915: switch to the atomic PWM API ARM: s3c24xx: rx1950: switch to the atomic PWM API
Heiko Stübner (1): pwm: add information about polarity, duty cycle and period to debugfs
Documentation/pwm.txt | 27 +++- arch/arm/mach-s3c24xx/mach-rx1950.c | 17 +- drivers/clk/clk-pwm.c | 36 ++++- drivers/gpu/drm/i915/intel_panel.c | 39 +++-- drivers/hwmon/pwm-fan.c | 88 ++++++---- drivers/input/misc/max77693-haptic.c | 28 +++- drivers/input/misc/max8997_haptic.c | 23 ++- drivers/input/misc/pwm-beeper.c | 46 ++++-- drivers/leds/leds-pwm.c | 15 +- drivers/pwm/core.c | 186 ++++++++++----------- drivers/pwm/pwm-clps711x.c | 2 +- drivers/pwm/pwm-crc.c | 2 +- drivers/pwm/pwm-lpc18xx-sct.c | 9 +- drivers/pwm/pwm-lpc32xx.c | 9 +- drivers/pwm/pwm-omap-dmtimer.c | 2 +- drivers/pwm/pwm-pxa.c | 2 +- drivers/pwm/pwm-rcar.c | 2 +- drivers/pwm/pwm-rockchip.c | 156 +++++++++++++++--- drivers/pwm/pwm-spear.c | 9 +- drivers/pwm/pwm-sti.c | 67 +++++++- drivers/pwm/pwm-sun4i.c | 73 ++++++--- drivers/pwm/sysfs.c | 98 ++++++++--- drivers/regulator/pwm-regulator.c | 151 ++++++++++++++--- drivers/video/backlight/lm3630a_bl.c | 15 +- drivers/video/backlight/lp855x_bl.c | 15 +- drivers/video/backlight/lp8788_bl.c | 17 +- drivers/video/backlight/pwm_bl.c | 51 +++--- drivers/video/fbdev/ssd1307fb.c | 28 +++- include/linux/pwm.h | 303 ++++++++++++++++++++++++++--------- 29 files changed, 1096 insertions(+), 420 deletions(-)
Hi Thierry,
On Wed, 30 Mar 2016 22:03:23 +0200 Boris Brezillon boris.brezillon@free-electrons.com wrote:
Hello,
This series adds support for atomic PWM update, or IOW, the capability to update all the parameters of a PWM device (enabled/disabled, period, duty and polarity) in one go.
It also adds support for initial PWM state retrieval (or hardware readout), which should allow smooth handover between the bootloader and Linux. For example, critical PWM users (like critical regulators controlled by a PWM) can query the current PWM state, and adapt the PWM config without having to disable/enable the PWM, or abruptly change the period/dutycyle/polarity config.
Thierry, I hope this version meets your expectations, if that's not the case, could you let me know quickly so I can adjust the implementation accordingly (I'd really like to get most of those changes in 4.7).
Still haven't had a clear feedback from your side on this series (you commented on a few details, but nothing on the general approach). Could you please have at a quick look at it, and let me know if I should adjust the implementation?
Note that I plan to send a new version addressing comments made by other maintainers/developers by the end of the week. In the meantime, could you have a look at the first set of patches (patch 1 to 4 are completely independent), and apply them if you're happy with it.
As you can see, I now have a lot of patches. This helps in showing the big picture, but also annoys people when I send this 50+ patchset. So, if you don't mind, I'd like to drop the changes touching PWM user drivers (to make them use the atomic API) until we get the other parts applied.
Thanks,
Boris
dri-devel@lists.freedesktop.org