From 6f897a1085083c9866a234ab6092b05b986242fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Thu, 9 Sep 2021 10:09:03 +0200 Subject: [PATCH 1/9] pwm: atmel: Drop unused header MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since commit 52eaba4cedbd ("pwm: atmel: Rework tracking updates pending in hardware") the driver doesn't make use of mutexes any more, so the header defining these doesn't need to be included. Signed-off-by: Uwe Kleine-König Acked-by: Alexandre Belloni Acked-by: Claudiu Beznea Signed-off-by: Thierry Reding --- drivers/pwm/pwm-atmel.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/pwm/pwm-atmel.c b/drivers/pwm/pwm-atmel.c index e748604403cc..98b34ea9f38e 100644 --- a/drivers/pwm/pwm-atmel.c +++ b/drivers/pwm/pwm-atmel.c @@ -24,7 +24,6 @@ #include #include #include -#include #include #include #include From 27d9a4d69433af1827a764fe235866d5d5501fdb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Thu, 9 Sep 2021 11:48:48 +0200 Subject: [PATCH 2/9] pwm: Add might_sleep() annotations for !CONFIG_PWM API functions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The normal implementations of these functions make use of mutexes. To make it obvious that these functions might sleep also add annotations to the dummy implementations in the !CONFIG_PWM case. Signed-off-by: Uwe Kleine-König Signed-off-by: Thierry Reding --- include/linux/pwm.h | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/include/linux/pwm.h b/include/linux/pwm.h index 725c9b784e60..515e33978e97 100644 --- a/include/linux/pwm.h +++ b/include/linux/pwm.h @@ -429,11 +429,13 @@ struct pwm_device *devm_fwnode_pwm_get(struct device *dev, #else static inline struct pwm_device *pwm_request(int pwm_id, const char *label) { + might_sleep(); return ERR_PTR(-ENODEV); } static inline void pwm_free(struct pwm_device *pwm) { + might_sleep(); } static inline int pwm_apply_state(struct pwm_device *pwm, @@ -493,12 +495,14 @@ static inline struct pwm_device *pwm_request_from_chip(struct pwm_chip *chip, unsigned int index, const char *label) { + might_sleep(); return ERR_PTR(-ENODEV); } static inline struct pwm_device *pwm_get(struct device *dev, const char *consumer) { + might_sleep(); return ERR_PTR(-ENODEV); } @@ -506,16 +510,19 @@ static inline struct pwm_device *of_pwm_get(struct device *dev, struct device_node *np, const char *con_id) { + might_sleep(); return ERR_PTR(-ENODEV); } static inline void pwm_put(struct pwm_device *pwm) { + might_sleep(); } static inline struct pwm_device *devm_pwm_get(struct device *dev, const char *consumer) { + might_sleep(); return ERR_PTR(-ENODEV); } @@ -523,6 +530,7 @@ static inline struct pwm_device *devm_of_pwm_get(struct device *dev, struct device_node *np, const char *con_id) { + might_sleep(); return ERR_PTR(-ENODEV); } @@ -530,6 +538,7 @@ static inline struct pwm_device * devm_fwnode_pwm_get(struct device *dev, struct fwnode_handle *fwnode, const char *con_id) { + might_sleep(); return ERR_PTR(-ENODEV); } #endif From 4ad91a227817ae48f931595d1101fc7100073ce9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Thu, 9 Sep 2021 11:48:49 +0200 Subject: [PATCH 3/9] pwm: Make it explicit that pwm_apply_state() might sleep MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit At least some implementations sleep. So mark pwm_apply_state() with a might_sleep() to make callers aware. In the worst case this uncovers a valid atomic user, then we revert this patch and at least gained some more knowledge and then can work on a concept similar to gpio_get_value/gpio_get_value_cansleep. Signed-off-by: Uwe Kleine-König Signed-off-by: Thierry Reding --- drivers/pwm/core.c | 9 +++++++++ include/linux/pwm.h | 4 ++++ 2 files changed, 13 insertions(+) diff --git a/drivers/pwm/core.c b/drivers/pwm/core.c index 4527f09a5c50..fb04a439462c 100644 --- a/drivers/pwm/core.c +++ b/drivers/pwm/core.c @@ -532,6 +532,15 @@ int pwm_apply_state(struct pwm_device *pwm, const struct pwm_state *state) struct pwm_chip *chip; int err; + /* + * Some lowlevel driver's implementations of .apply() make use of + * mutexes, also with some drivers only returning when the new + * configuration is active calling pwm_apply_state() from atomic context + * is a bad idea. So make it explicit that calling this function might + * sleep. + */ + might_sleep(); + if (!pwm || !state || !state->period || state->duty_cycle > state->period) return -EINVAL; diff --git a/include/linux/pwm.h b/include/linux/pwm.h index 515e33978e97..e6dac95e4960 100644 --- a/include/linux/pwm.h +++ b/include/linux/pwm.h @@ -441,6 +441,7 @@ static inline void pwm_free(struct pwm_device *pwm) static inline int pwm_apply_state(struct pwm_device *pwm, const struct pwm_state *state) { + might_sleep(); return -ENOTSUPP; } @@ -452,6 +453,7 @@ static inline int pwm_adjust_config(struct pwm_device *pwm) static inline int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns) { + might_sleep(); return -EINVAL; } @@ -464,11 +466,13 @@ static inline int pwm_capture(struct pwm_device *pwm, static inline int pwm_enable(struct pwm_device *pwm) { + might_sleep(); return -EINVAL; } static inline void pwm_disable(struct pwm_device *pwm) { + might_sleep(); } static inline int pwm_set_chip_data(struct pwm_device *pwm, void *data) From 06dfae38d98891f8f4d55367a049f52d880c038e Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Fri, 24 Sep 2021 15:31:48 +0200 Subject: [PATCH 4/9] pwm: samsung: Describe driver in Kconfig MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Describe better which driver applies to which SoC, to make configuring kernel for Samsung SoC easier. Signed-off-by: Krzysztof Kozlowski Acked-by: Uwe Kleine-König Signed-off-by: Thierry Reding --- drivers/pwm/Kconfig | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig index aa29841bbb79..21e3b05a5153 100644 --- a/drivers/pwm/Kconfig +++ b/drivers/pwm/Kconfig @@ -476,7 +476,9 @@ config PWM_SAMSUNG depends on PLAT_SAMSUNG || ARCH_S5PV210 || ARCH_EXYNOS || COMPILE_TEST depends on HAS_IOMEM help - Generic PWM framework driver for Samsung. + Generic PWM framework driver for Samsung S3C24xx, S3C64xx, S5Pv210 + and Exynos SoCs. + Choose Y here only if you build for such Samsung SoC. To compile this driver as a module, choose M here: the module will be called pwm-samsung. From 6facd84083485dff109f1f78cbf6a1d760efc939 Mon Sep 17 00:00:00 2001 From: zhaoxiao Date: Mon, 13 Sep 2021 14:15:53 +0800 Subject: [PATCH 5/9] pwm: visconti: Simplify using devm_pwmchip_add() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This allows to drop the platform_driver's remove function. This is the only user of driver data so this can go away, too. Signed-off-by: zhaoxiao Reviewed-by: Uwe Kleine-König Signed-off-by: Thierry Reding --- drivers/pwm/pwm-visconti.c | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/drivers/pwm/pwm-visconti.c b/drivers/pwm/pwm-visconti.c index af4e37d3e3a6..927c4cbb1daf 100644 --- a/drivers/pwm/pwm-visconti.c +++ b/drivers/pwm/pwm-visconti.c @@ -144,28 +144,17 @@ static int visconti_pwm_probe(struct platform_device *pdev) if (IS_ERR(priv->base)) return PTR_ERR(priv->base); - platform_set_drvdata(pdev, priv); - priv->chip.dev = dev; priv->chip.ops = &visconti_pwm_ops; priv->chip.npwm = 4; - ret = pwmchip_add(&priv->chip); + ret = devm_pwmchip_add(&pdev->dev, &priv->chip); if (ret < 0) return dev_err_probe(&pdev->dev, ret, "Cannot register visconti PWM\n"); return 0; } -static int visconti_pwm_remove(struct platform_device *pdev) -{ - struct visconti_pwm_chip *priv = platform_get_drvdata(pdev); - - pwmchip_remove(&priv->chip); - - return 0; -} - static const struct of_device_id visconti_pwm_of_match[] = { { .compatible = "toshiba,visconti-pwm", }, { } @@ -178,7 +167,6 @@ static struct platform_driver visconti_pwm_driver = { .of_match_table = visconti_pwm_of_match, }, .probe = visconti_pwm_probe, - .remove = visconti_pwm_remove, }; module_platform_driver(visconti_pwm_driver); From 5d82e661398e06fa32dc7e11909df5dc34c808d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A5rten=20Lindahl?= Date: Thu, 9 Sep 2021 12:10:42 +0200 Subject: [PATCH 6/9] pwm: pwm-samsung: Trigger manual update when disabling PWM MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When duty-cycle is at full level (100%), the TCNTn and TCMPn registers needs to be flushed in order to disable the signal. The PWM manual does not say anything about this, but states that only clearing the TCON auto-reload bit should be needed, and this seems to be true when the PWM duty-cycle is not at full level. This can be observed on an Axis ARTPEC-8, by running: echo > pwm/period echo > pwm/duty_cycle echo 1 > pwm/enable echo 0 > pwm/enable Since the TCNTn and TCMPn registers are activated when enabling the PWM (setting TCON auto-reload bit), and are not touched when disabling the PWM, the double buffered auto-reload function seems to be still active. Lowering duty-cycle, and restoring it again in between the enabling and disabling, makes the disable work since it triggers a reload of the TCNTn and TCMPn registers. Fix this by securing a reload of the TCNTn and TCMPn registers when disabling the PWM and having a full duty-cycle. Signed-off-by: Mårten Lindahl Reviewed-by: Krzysztof Kozlowski Acked-by: Uwe Kleine-König Signed-off-by: Thierry Reding --- drivers/pwm/pwm-samsung.c | 30 ++++++++++++++++++++++-------- 1 file changed, 22 insertions(+), 8 deletions(-) diff --git a/drivers/pwm/pwm-samsung.c b/drivers/pwm/pwm-samsung.c index dd94c4312a0c..0a4ff55fad04 100644 --- a/drivers/pwm/pwm-samsung.c +++ b/drivers/pwm/pwm-samsung.c @@ -117,6 +117,20 @@ static inline unsigned int to_tcon_channel(unsigned int channel) return (channel == 0) ? 0 : (channel + 1); } +static void __pwm_samsung_manual_update(struct samsung_pwm_chip *chip, + struct pwm_device *pwm) +{ + unsigned int tcon_chan = to_tcon_channel(pwm->hwpwm); + u32 tcon; + + tcon = readl(chip->base + REG_TCON); + tcon |= TCON_MANUALUPDATE(tcon_chan); + writel(tcon, chip->base + REG_TCON); + + tcon &= ~TCON_MANUALUPDATE(tcon_chan); + writel(tcon, chip->base + REG_TCON); +} + static void pwm_samsung_set_divisor(struct samsung_pwm_chip *pwm, unsigned int channel, u8 divisor) { @@ -276,6 +290,13 @@ static void pwm_samsung_disable(struct pwm_chip *chip, struct pwm_device *pwm) tcon &= ~TCON_AUTORELOAD(tcon_chan); writel(tcon, our_chip->base + REG_TCON); + /* + * In case the PWM is at 100% duty cycle, force a manual + * update to prevent the signal from staying high. + */ + if (readl(our_chip->base + REG_TCMPB(pwm->hwpwm)) == (u32)-1U) + __pwm_samsung_manual_update(our_chip, pwm); + our_chip->disabled_mask |= BIT(pwm->hwpwm); spin_unlock_irqrestore(&samsung_pwm_lock, flags); @@ -284,18 +305,11 @@ static void pwm_samsung_disable(struct pwm_chip *chip, struct pwm_device *pwm) static void pwm_samsung_manual_update(struct samsung_pwm_chip *chip, struct pwm_device *pwm) { - unsigned int tcon_chan = to_tcon_channel(pwm->hwpwm); - u32 tcon; unsigned long flags; spin_lock_irqsave(&samsung_pwm_lock, flags); - tcon = readl(chip->base + REG_TCON); - tcon |= TCON_MANUALUPDATE(tcon_chan); - writel(tcon, chip->base + REG_TCON); - - tcon &= ~TCON_MANUALUPDATE(tcon_chan); - writel(tcon, chip->base + REG_TCON); + __pwm_samsung_manual_update(chip, pwm); spin_unlock_irqrestore(&samsung_pwm_lock, flags); } From 8aea22fb2d57eb3bcd77631c91f69b6bbdc90821 Mon Sep 17 00:00:00 2001 From: Duc Nguyen Date: Wed, 27 Oct 2021 14:28:09 +0200 Subject: [PATCH 7/9] dt-bindings: pwm: tpu: Add R-Car V3U device tree bindings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add device tree bindings for TPU with the PWM controller found on R-Car V3U SoCs. Signed-off-by: Duc Nguyen Signed-off-by: Wolfram Sang Acked-by: Rob Herring Signed-off-by: Geert Uytterhoeven Reviewed-by: Laurent Pinchart Acked-by: Uwe Kleine-König Signed-off-by: Thierry Reding --- Documentation/devicetree/bindings/pwm/renesas,tpu-pwm.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/pwm/renesas,tpu-pwm.yaml b/Documentation/devicetree/bindings/pwm/renesas,tpu-pwm.yaml index 81ccb2110162..0171a04257b8 100644 --- a/Documentation/devicetree/bindings/pwm/renesas,tpu-pwm.yaml +++ b/Documentation/devicetree/bindings/pwm/renesas,tpu-pwm.yaml @@ -38,6 +38,7 @@ properties: - renesas,tpu-r8a77965 # R-Car M3-N - renesas,tpu-r8a77970 # R-Car V3M - renesas,tpu-r8a77980 # R-Car V3H + - renesas,tpu-r8a779a0 # R-Car V3U - const: renesas,tpu reg: From 76c40c220f630f77475c8489f291e32189064e4b Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Wed, 27 Oct 2021 14:29:22 +0200 Subject: [PATCH 8/9] dt-bindings: pwm: tpu: Add R-Car M3-W+ device tree bindings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add device tree bindings for TPU found on R-Car M3-W+ SoCs. Reported-by: Geert Uytterhoeven Signed-off-by: Wolfram Sang Acked-by: Rob Herring Signed-off-by: Geert Uytterhoeven Reviewed-by: Laurent Pinchart Acked-by: Uwe Kleine-König Reviewed-by: Kieran Bingham Signed-off-by: Thierry Reding --- Documentation/devicetree/bindings/pwm/renesas,tpu-pwm.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/pwm/renesas,tpu-pwm.yaml b/Documentation/devicetree/bindings/pwm/renesas,tpu-pwm.yaml index 0171a04257b8..1f5c6384182e 100644 --- a/Documentation/devicetree/bindings/pwm/renesas,tpu-pwm.yaml +++ b/Documentation/devicetree/bindings/pwm/renesas,tpu-pwm.yaml @@ -35,6 +35,7 @@ properties: - renesas,tpu-r8a7794 # R-Car E2 - renesas,tpu-r8a7795 # R-Car H3 - renesas,tpu-r8a7796 # R-Car M3-W + - renesas,tpu-r8a77961 # R-Car M3-W+ - renesas,tpu-r8a77965 # R-Car M3-N - renesas,tpu-r8a77970 # R-Car V3M - renesas,tpu-r8a77980 # R-Car V3H From e9d866d5a6296c701e5b46a94c0bbd6e2c0e357e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Tue, 2 Nov 2021 10:28:04 +0100 Subject: [PATCH 9/9] pwm: vt8500: Rename pwm_busy_wait() to make it obviously driver-specific MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The pwm_ prefix suggests that pwm_busy_wait() is a function provided by the pwm core. Use the otherwise consistently used driver prefix for this function, too. Signed-off-by: Uwe Kleine-König Signed-off-by: Thierry Reding --- drivers/pwm/pwm-vt8500.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/drivers/pwm/pwm-vt8500.c b/drivers/pwm/pwm-vt8500.c index ea2aa151080a..480bfc29782f 100644 --- a/drivers/pwm/pwm-vt8500.c +++ b/drivers/pwm/pwm-vt8500.c @@ -56,7 +56,7 @@ struct vt8500_chip { #define to_vt8500_chip(chip) container_of(chip, struct vt8500_chip, chip) #define msecs_to_loops(t) (loops_per_jiffy / 1000 * HZ * t) -static inline void pwm_busy_wait(struct vt8500_chip *vt8500, int nr, u8 bitmask) +static inline void vt8500_pwm_busy_wait(struct vt8500_chip *vt8500, int nr, u8 bitmask) { int loops = msecs_to_loops(10); u32 mask = bitmask << (nr << 8); @@ -106,18 +106,18 @@ static int vt8500_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, dc = c; writel(prescale, vt8500->base + REG_SCALAR(pwm->hwpwm)); - pwm_busy_wait(vt8500, pwm->hwpwm, STATUS_SCALAR_UPDATE); + vt8500_pwm_busy_wait(vt8500, pwm->hwpwm, STATUS_SCALAR_UPDATE); writel(pv, vt8500->base + REG_PERIOD(pwm->hwpwm)); - pwm_busy_wait(vt8500, pwm->hwpwm, STATUS_PERIOD_UPDATE); + vt8500_pwm_busy_wait(vt8500, pwm->hwpwm, STATUS_PERIOD_UPDATE); writel(dc, vt8500->base + REG_DUTY(pwm->hwpwm)); - pwm_busy_wait(vt8500, pwm->hwpwm, STATUS_DUTY_UPDATE); + vt8500_pwm_busy_wait(vt8500, pwm->hwpwm, STATUS_DUTY_UPDATE); val = readl(vt8500->base + REG_CTRL(pwm->hwpwm)); val |= CTRL_AUTOLOAD; writel(val, vt8500->base + REG_CTRL(pwm->hwpwm)); - pwm_busy_wait(vt8500, pwm->hwpwm, STATUS_CTRL_UPDATE); + vt8500_pwm_busy_wait(vt8500, pwm->hwpwm, STATUS_CTRL_UPDATE); clk_disable(vt8500->clk); return 0; @@ -138,7 +138,7 @@ static int vt8500_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) val = readl(vt8500->base + REG_CTRL(pwm->hwpwm)); val |= CTRL_ENABLE; writel(val, vt8500->base + REG_CTRL(pwm->hwpwm)); - pwm_busy_wait(vt8500, pwm->hwpwm, STATUS_CTRL_UPDATE); + vt8500_pwm_busy_wait(vt8500, pwm->hwpwm, STATUS_CTRL_UPDATE); return 0; } @@ -151,7 +151,7 @@ static void vt8500_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) val = readl(vt8500->base + REG_CTRL(pwm->hwpwm)); val &= ~CTRL_ENABLE; writel(val, vt8500->base + REG_CTRL(pwm->hwpwm)); - pwm_busy_wait(vt8500, pwm->hwpwm, STATUS_CTRL_UPDATE); + vt8500_pwm_busy_wait(vt8500, pwm->hwpwm, STATUS_CTRL_UPDATE); clk_disable(vt8500->clk); } @@ -171,7 +171,7 @@ static int vt8500_pwm_set_polarity(struct pwm_chip *chip, val &= ~CTRL_INVERT; writel(val, vt8500->base + REG_CTRL(pwm->hwpwm)); - pwm_busy_wait(vt8500, pwm->hwpwm, STATUS_CTRL_UPDATE); + vt8500_pwm_busy_wait(vt8500, pwm->hwpwm, STATUS_CTRL_UPDATE); return 0; }