From e38b9eda475fd5ef8d256ae3ced566452c1c5d58 Mon Sep 17 00:00:00 2001 From: Eliav Farber Date: Sun, 16 Aug 2026 06:06:00 +0000 Subject: [PATCH 01/13] notifier: add device-managed registration APIs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add devm_atomic_notifier_chain_register() and devm_blocking_notifier_chain_register() that automatically unregister the notifier when the device is unbound. Many drivers repeat the same boilerplate pattern: 1. Register the notifier with *_notifier_chain_register() 2. Check for error 3. Register a devm action to unregister on teardown 4. Implement a per-driver static unregister callback With the new devm_*_notifier_chain_register() APIs, this reduces to a single call with one error path, eliminating per-driver unregister callbacks entirely. The implementation follows the established devres pattern used by other device-managed kernel APIs. A devm variant for raw notifier chains is intentionally not included because raw notifiers require the caller to provide all locking. The devres teardown callback has no way to acquire the caller's lock, and devres_alloc() with GFP_KERNEL cannot be used if the caller's lock is a spinlock. Signed-off-by: Eliav Farber Acked-by: Linus Walleij Tested-by: Bradley Morgan Acked-by: Bradley Morgan # kernel/ Link: https://patch.msgid.link/20260816060648.4030-2-farbere@amazon.com [ukleinek: Add note to kdoc about devm_atomic_notifier_chain_register() not being callable in atomic context.] Signed-off-by: Uwe Kleine-König --- include/linux/notifier.h | 7 +++ kernel/notifier.c | 105 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 112 insertions(+) diff --git a/include/linux/notifier.h b/include/linux/notifier.h index 01b6c9d9956f..4eeae9741a6e 100644 --- a/include/linux/notifier.h +++ b/include/linux/notifier.h @@ -46,6 +46,7 @@ * often but notifier_blocks will seldom be removed. */ +struct device; struct notifier_block; typedef int (*notifier_fn_t)(struct notifier_block *nb, @@ -145,8 +146,14 @@ extern void srcu_init_notifier_head(struct srcu_notifier_head *nh); extern int atomic_notifier_chain_register(struct atomic_notifier_head *nh, struct notifier_block *nb); +int devm_atomic_notifier_chain_register(struct device *dev, + struct atomic_notifier_head *nh, + struct notifier_block *nb); extern int blocking_notifier_chain_register(struct blocking_notifier_head *nh, struct notifier_block *nb); +int devm_blocking_notifier_chain_register(struct device *dev, + struct blocking_notifier_head *nh, + struct notifier_block *nb); extern int raw_notifier_chain_register(struct raw_notifier_head *nh, struct notifier_block *nb); extern int srcu_notifier_chain_register(struct srcu_notifier_head *nh, diff --git a/kernel/notifier.c b/kernel/notifier.c index 2f9fe7c30287..55fbd2be7407 100644 --- a/kernel/notifier.c +++ b/kernel/notifier.c @@ -1,4 +1,5 @@ // SPDX-License-Identifier: GPL-2.0-only +#include #include #include #include @@ -197,6 +198,58 @@ int atomic_notifier_chain_unregister(struct atomic_notifier_head *nh, } EXPORT_SYMBOL_GPL(atomic_notifier_chain_unregister); +struct atomic_notifier_chain_devres { + struct atomic_notifier_head *nh; + struct notifier_block *nb; +}; + +static void devm_atomic_notifier_chain_unregister(struct device *dev, void *res) +{ + struct atomic_notifier_chain_devres *dr = res; + + atomic_notifier_chain_unregister(dr->nh, dr->nb); +} + +/** + * devm_atomic_notifier_chain_register - Device-managed atomic notifier registration + * @dev: Device to tie the notifier lifetime to + * @nh: Pointer to head of the atomic notifier chain + * @nb: New entry in notifier chain + * + * Adds a notifier to an atomic notifier chain and registers a cleanup + * action to automatically unregister it when @dev is unbound. + * + * Must be called in process context. + * + * Return: + * 0 on success, negative errno on error. + */ +int devm_atomic_notifier_chain_register(struct device *dev, + struct atomic_notifier_head *nh, + struct notifier_block *nb) +{ + struct atomic_notifier_chain_devres *dr; + int ret; + + dr = devres_alloc(devm_atomic_notifier_chain_unregister, + sizeof(*dr), GFP_KERNEL); + if (!dr) + return -ENOMEM; + + ret = atomic_notifier_chain_register(nh, nb); + if (ret) { + devres_free(dr); + return ret; + } + + dr->nh = nh; + dr->nb = nb; + devres_add(dev, dr); + + return 0; +} +EXPORT_SYMBOL_GPL(devm_atomic_notifier_chain_register); + /** * atomic_notifier_call_chain - Call functions in an atomic notifier chain * @nh: Pointer to head of the atomic notifier chain @@ -349,6 +402,58 @@ int blocking_notifier_call_chain_robust(struct blocking_notifier_head *nh, } EXPORT_SYMBOL_GPL(blocking_notifier_call_chain_robust); +struct blocking_notifier_chain_devres { + struct blocking_notifier_head *nh; + struct notifier_block *nb; +}; + +static void devm_blocking_notifier_chain_unregister(struct device *dev, + void *res) +{ + struct blocking_notifier_chain_devres *dr = res; + + blocking_notifier_chain_unregister(dr->nh, dr->nb); +} + +/** + * devm_blocking_notifier_chain_register - Device-managed blocking notifier registration + * @dev: Device to tie the notifier lifetime to + * @nh: Pointer to head of the blocking notifier chain + * @nb: New entry in notifier chain + * + * Adds a notifier to a blocking notifier chain and registers a cleanup + * action to automatically unregister it when @dev is unbound. + * Must be called in process context. + * + * Return: + * 0 on success, negative errno on error. + */ +int devm_blocking_notifier_chain_register(struct device *dev, + struct blocking_notifier_head *nh, + struct notifier_block *nb) +{ + struct blocking_notifier_chain_devres *dr; + int ret; + + dr = devres_alloc(devm_blocking_notifier_chain_unregister, + sizeof(*dr), GFP_KERNEL); + if (!dr) + return -ENOMEM; + + ret = blocking_notifier_chain_register(nh, nb); + if (ret) { + devres_free(dr); + return ret; + } + + dr->nh = nh; + dr->nb = nb; + devres_add(dev, dr); + + return 0; +} +EXPORT_SYMBOL_GPL(devm_blocking_notifier_chain_register); + /** * blocking_notifier_call_chain - Call functions in a blocking notifier chain * @nh: Pointer to head of the blocking notifier chain From 62cff15fbf2b12ebbb0b01ca720030c2bc12aed1 Mon Sep 17 00:00:00 2001 From: Ruoyu Wang Date: Fri, 14 Aug 2026 21:41:43 +0800 Subject: [PATCH 02/13] pwm: renesas-tpu: Fix runtime PM reference leak MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit tpu_pwm_timer_start() used pm_runtime_get_sync() without checking its return value before touching the TPU clock. A failed runtime resume was therefore ignored, and the callback continued into register access. Use pm_runtime_resume_and_get() and propagate a failed resume. Keep the matching put when clock preparation fails after a successful resume. tpu_pwm_disable() also needs to propagate a failed start so that the PWM apply callback does not access registers while runtime PM is inactive. This issue was found by a static analysis checker and confirmed by manual source review. Fixes: 99b82abb0a35 ("pwm: Add Renesas TPU PWM driver") Signed-off-by: Ruoyu Wang Link: https://patch.msgid.link/20260814134143.1388120-1-ruoyuw560@gmail.com Signed-off-by: Uwe Kleine-König --- drivers/pwm/pwm-renesas-tpu.c | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/drivers/pwm/pwm-renesas-tpu.c b/drivers/pwm/pwm-renesas-tpu.c index 140fd0bcb93f..e35f2e14777d 100644 --- a/drivers/pwm/pwm-renesas-tpu.c +++ b/drivers/pwm/pwm-renesas-tpu.c @@ -149,10 +149,14 @@ static int tpu_pwm_timer_start(struct tpu_pwm_device *tpd) if (!tpd->timer_on) { /* Wake up device and enable clock. */ - pm_runtime_get_sync(&tpd->tpu->pdev->dev); + ret = pm_runtime_resume_and_get(&tpd->tpu->pdev->dev); + if (ret < 0) + return ret; + ret = clk_prepare_enable(tpd->tpu->clk); if (ret) { dev_err(&tpd->tpu->pdev->dev, "cannot enable clock\n"); + pm_runtime_put(&tpd->tpu->pdev->dev); return ret; } tpd->timer_on = true; @@ -382,15 +386,21 @@ static int tpu_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) return 0; } -static void tpu_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) +static int tpu_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) { struct tpu_device *tpu = to_tpu_device(chip); struct tpu_pwm_device *tpd = &tpu->tpd[pwm->hwpwm]; + int ret; /* The timer must be running to modify the pin output configuration. */ - tpu_pwm_timer_start(tpd); + ret = tpu_pwm_timer_start(tpd); + if (ret < 0) + return ret; + tpu_pwm_set_pin(tpd, TPU_PIN_INACTIVE); tpu_pwm_timer_stop(tpd); + + return 0; } static int tpu_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, @@ -401,7 +411,10 @@ static int tpu_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, if (state->polarity != pwm->state.polarity) { if (enabled) { - tpu_pwm_disable(chip, pwm); + err = tpu_pwm_disable(chip, pwm); + if (err) + return err; + enabled = false; } @@ -412,7 +425,7 @@ static int tpu_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, if (!state->enabled) { if (enabled) - tpu_pwm_disable(chip, pwm); + return tpu_pwm_disable(chip, pwm); return 0; } From 6b0c6ff76795f62981352cc0be00163077d0961b Mon Sep 17 00:00:00 2001 From: Eliav Farber Date: Sun, 16 Aug 2026 06:06:01 +0000 Subject: [PATCH 03/13] pwm: iqs620a: Use devm_blocking_notifier_chain_register() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace the blocking_notifier_chain_register() + devm_add_action_or_reset() pattern with a single call to devm_blocking_notifier_chain_register(), removing the iqs620_pwm_notifier_unregister() callback. Signed-off-by: Eliav Farber Link: https://patch.msgid.link/20260816060648.4030-3-farbere@amazon.com Signed-off-by: Uwe Kleine-König --- drivers/pwm/pwm-iqs620a.c | 23 +++-------------------- 1 file changed, 3 insertions(+), 20 deletions(-) diff --git a/drivers/pwm/pwm-iqs620a.c b/drivers/pwm/pwm-iqs620a.c index 13e5e138c8e9..6c6caee65b8f 100644 --- a/drivers/pwm/pwm-iqs620a.c +++ b/drivers/pwm/pwm-iqs620a.c @@ -173,18 +173,6 @@ static const struct pwm_ops iqs620_pwm_ops = { .get_state = iqs620_pwm_get_state, }; -static void iqs620_pwm_notifier_unregister(void *context) -{ - struct iqs620_pwm_private *iqs620_pwm = context; - int ret; - - ret = blocking_notifier_chain_unregister(&iqs620_pwm->iqs62x->nh, - &iqs620_pwm->notifier); - if (ret) - dev_err(iqs620_pwm->dev, - "Failed to unregister notifier: %d\n", ret); -} - static int iqs620_pwm_probe(struct platform_device *pdev) { struct iqs62x_core *iqs62x = dev_get_drvdata(pdev->dev.parent); @@ -218,19 +206,14 @@ static int iqs620_pwm_probe(struct platform_device *pdev) mutex_init(&iqs620_pwm->lock); iqs620_pwm->notifier.notifier_call = iqs620_pwm_notifier; - ret = blocking_notifier_chain_register(&iqs620_pwm->iqs62x->nh, - &iqs620_pwm->notifier); + ret = devm_blocking_notifier_chain_register(&pdev->dev, + &iqs620_pwm->iqs62x->nh, + &iqs620_pwm->notifier); if (ret) { dev_err(&pdev->dev, "Failed to register notifier: %d\n", ret); return ret; } - ret = devm_add_action_or_reset(&pdev->dev, - iqs620_pwm_notifier_unregister, - iqs620_pwm); - if (ret) - return ret; - ret = devm_pwmchip_add(&pdev->dev, chip); if (ret) dev_err(&pdev->dev, "Failed to add device: %d\n", ret); From 960909d2c63e0914bbf95d595dbc8e17babedd4b Mon Sep 17 00:00:00 2001 From: Manuel Ebner Date: Fri, 4 Sep 2026 14:20:04 +0200 Subject: [PATCH 04/13] docs: pwm: Fix bracket and punctuation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove needless '(' and fix comma splice. Signed-off-by: Manuel Ebner Acked-by: Randy Dunlap Link: https://patch.msgid.link/20260904122005.760299-2-manuelebnerli@mailbox.org Signed-off-by: Uwe Kleine-König --- Documentation/driver-api/pwm.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/driver-api/pwm.rst b/Documentation/driver-api/pwm.rst index 0d27a40f5818..b6b67f7d4905 100644 --- a/Documentation/driver-api/pwm.rst +++ b/Documentation/driver-api/pwm.rst @@ -177,7 +177,7 @@ and pwm_put() may not be called from an atomic context. Most functions in the PWM consumer API might sleep and so must not be called from atomic context. The notable exception is pwm_apply_atomic() which has the same semantics as pwm_apply_might_sleep() but can be called from atomic context. -(The price for that is that it doesn't work for all PWM devices, use +The price for that is that it doesn't work for all PWM devices. Use pwm_might_sleep() to check if a given PWM supports atomic operation. Locking in the PWM core ensures that callbacks related to a single chip are From abc07c4a34aee0b884a6011ea7a3aed022945416 Mon Sep 17 00:00:00 2001 From: Eduard Bostina Date: Sat, 15 Aug 2026 07:48:17 +0000 Subject: [PATCH 05/13] dt-bindings: pwm: Convert TI PWM Subsystem to DT schema MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Convert the Texas Instruments SoC PWM Subsystem bindings to DT schema. During the conversion, the following updates were made: - 'counter' node was added to the allowed child nodes list to resolve dtbs_check warnings. Some eqep nodes use 'counter' instead of 'eqep'. Signed-off-by: Eduard Bostina Reviewed-by: Rob Herring (Arm) Link: https://patch.msgid.link/20260815074817.2117916-1-egbostina@gmail.com Signed-off-by: Uwe Kleine-König --- .../devicetree/bindings/pwm/pwm-tipwmss.txt | 58 ------------- .../bindings/pwm/ti,am33xx-pwmss.yaml | 82 +++++++++++++++++++ 2 files changed, 82 insertions(+), 58 deletions(-) delete mode 100644 Documentation/devicetree/bindings/pwm/pwm-tipwmss.txt create mode 100644 Documentation/devicetree/bindings/pwm/ti,am33xx-pwmss.yaml diff --git a/Documentation/devicetree/bindings/pwm/pwm-tipwmss.txt b/Documentation/devicetree/bindings/pwm/pwm-tipwmss.txt deleted file mode 100644 index 4633697fbda1..000000000000 --- a/Documentation/devicetree/bindings/pwm/pwm-tipwmss.txt +++ /dev/null @@ -1,58 +0,0 @@ -TI SOC based PWM Subsystem - -Required properties: -- compatible: Must be "ti,-pwmss". - for am33xx - compatible = "ti,am33xx-pwmss"; - for am4372 - compatible = "ti,am4372-pwmss","ti,am33xx-pwmss"; - for dra746 - compatible = "ti,dra746-pwmss", "ti,am33xx-pwmss" - -- reg: physical base address and size of the registers map. -- address-cells: Specify the number of u32 entries needed in child nodes. - Should set to 1. -- size-cells: specify number of u32 entries needed to specify child nodes size - in reg property. Should set to 1. -- ranges: describes the address mapping of a memory-mapped bus. Should set to - physical address map of child's base address, physical address within - parent's address space and length of the address map. For am33xx, - 3 set of child register maps present, ECAP register space, EQEP - register space, EHRPWM register space. - -Also child nodes should also populated under PWMSS DT node. - -Example: -epwmss0: epwmss@48300000 { /* PWMSS for am33xx */ - compatible = "ti,am33xx-pwmss"; - reg = <0x48300000 0x10>; - ti,hwmods = "epwmss0"; - #address-cells = <1>; - #size-cells = <1>; - ranges = <0x48300100 0x48300100 0x80 /* ECAP */ - 0x48300180 0x48300180 0x80 /* EQEP */ - 0x48300200 0x48300200 0x80>; /* EHRPWM */ - - /* child nodes go here */ -}; - -epwmss0: epwmss@48300000 { /* PWMSS for am4372 */ - compatible = "ti,am4372-pwmss","ti,am33xx-pwmss" - reg = <0x48300000 0x10>; - ti,hwmods = "epwmss0"; - #address-cells = <1>; - #size-cells = <1>; - ranges = <0x48300100 0x48300100 0x80 /* ECAP */ - 0x48300180 0x48300180 0x80 /* EQEP */ - 0x48300200 0x48300200 0x80>; /* EHRPWM */ - - /* child nodes go here */ -}; - -epwmss0: epwmss@4843e000 { /* PWMSS for DRA7xx */ - compatible = "ti,dra746-pwmss", "ti,am33xx-pwmss"; - reg = <0x4843e000 0x30>; - ti,hwmods = "epwmss0"; - #address-cells = <1>; - #size-cells = <1>; - ranges; - - /* child nodes go here */ -}; diff --git a/Documentation/devicetree/bindings/pwm/ti,am33xx-pwmss.yaml b/Documentation/devicetree/bindings/pwm/ti,am33xx-pwmss.yaml new file mode 100644 index 000000000000..89ce6b02b70c --- /dev/null +++ b/Documentation/devicetree/bindings/pwm/ti,am33xx-pwmss.yaml @@ -0,0 +1,82 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/pwm/ti,am33xx-pwmss.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Texas Instruments SoC PWM Subsystem + +maintainers: + - Eduard Bostina + +properties: + compatible: + oneOf: + - const: ti,am33xx-pwmss + - items: + - enum: + - ti,am4372-pwmss + - ti,dra746-pwmss + - const: ti,am33xx-pwmss + + reg: + maxItems: 1 + + "#address-cells": + const: 1 + + "#size-cells": + const: 1 + + ranges: true + +patternProperties: + "^(ecap|eqep|pwm|counter)@[0-9a-f]+$": + type: object + description: Child nodes for ECAP, EQEP (counter), and EHRPWM + required: + - compatible + +required: + - compatible + - reg + - "#address-cells" + - "#size-cells" + - ranges + +additionalProperties: false + +examples: + - | + pwm@48300000 { + compatible = "ti,am33xx-pwmss"; + reg = <0x48300000 0x10>; + #address-cells = <1>; + #size-cells = <1>; + ranges = <0x48300100 0x48300100 0x80>, /* ECAP */ + <0x48300180 0x48300180 0x80>, /* EQEP */ + <0x48300200 0x48300200 0x80>; /* EHRPWM */ + + pwm@48300100 { + compatible = "ti,am3352-ecap"; + #pwm-cells = <3>; + reg = <0x48300100 0x80>; + clocks = <&l4ls_gclk>; + clock-names = "fck"; + }; + + counter@48300180 { + compatible = "ti,am3352-eqep"; + reg = <0x48300180 0x80>; + clocks = <&l4ls_gclk>; + interrupts = <79>; + }; + + pwm@48300200 { + compatible = "ti,am3352-ehrpwm"; + #pwm-cells = <3>; + reg = <0x48300200 0x80>; + clocks = <&ehrpwm0_tbclk>, <&l4ls_gclk>; + clock-names = "tbclk", "fck"; + }; + }; From bdf16a4d74ae6b7fb6dc8953798ba0853ddc4705 Mon Sep 17 00:00:00 2001 From: Hemanth Selam Date: Mon, 7 Sep 2026 11:56:58 +0530 Subject: [PATCH 06/13] pwm: Fix typos in comments MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix typos in comments, reported by scripts/checkpatch.pl using the misspelling list in scripts/spelling.txt. Only touches comments, no code changes. Assisted-by: Cursor:claude-opus-5 Signed-off-by: Hemanth Selam Link: https://patch.msgid.link/20260907062659.15500-1-hemanth.selam@gmail.com Signed-off-by: Uwe Kleine-König --- drivers/pwm/pwm-brcmstb.c | 2 +- drivers/pwm/pwm-pca9685.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/pwm/pwm-brcmstb.c b/drivers/pwm/pwm-brcmstb.c index 790ef7ffbbe5..78378787e4e9 100644 --- a/drivers/pwm/pwm-brcmstb.c +++ b/drivers/pwm/pwm-brcmstb.c @@ -101,7 +101,7 @@ static int brcmstb_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, u32 value; /* - * If asking for a duty_ns equal to period_ns, we need to substract + * If asking for a duty_ns equal to period_ns, we need to subtract * the period value by 1 to make it shorter than the "on" time and * produce a flat 100% duty cycle signal, and max out the "on" time */ diff --git a/drivers/pwm/pwm-pca9685.c b/drivers/pwm/pwm-pca9685.c index a2792b803643..3374ed76f8d3 100644 --- a/drivers/pwm/pwm-pca9685.c +++ b/drivers/pwm/pwm-pca9685.c @@ -54,7 +54,7 @@ /* * The time value of one counter tick. Note that NSEC_PER_SEC is an integer * multiple of PCA9685_OSC_CLOCK_HZ, so there is no rounding involved and we're - * not loosing precision due to the early division. + * not losing precision due to the early division. */ #define PCA9685_QUANTUM_NS(_prescale) ((NSEC_PER_SEC / PCA9685_OSC_CLOCK_HZ) * (_prescale + 1)) From 0de9b836a3bc1cd30578ae2c4463d09afd85ed3d Mon Sep 17 00:00:00 2001 From: Keguang Zhang Date: Wed, 15 Jul 2026 19:05:24 +0800 Subject: [PATCH 07/13] pwm: loongson: Reload PWM configuration through counter reset MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Loongson PWM controller latches the LOW and PERIOD registers only at the start of each PWM period. After disabling and re-enabling the PWM, the controller resumes from the previous counter value and completes the current period before re-latching the updated LOW and PERIOD values. Reset the PWM counter when disabling the PWM and release it when enabling the PWM so that the updated LOW and PERIOD values are latched before the PWM starts running again. Fixes: 2b62c89448dd ("pwm: Add Loongson PWM controller support") Signed-off-by: Keguang Zhang Link: https://patch.msgid.link/20260715-pwm-loongson-fix-v3-2-0aab2847eaa7@gmail.com [ukleinek: Note LOW refers to the register currently named LOONGSON_PWM_REG_DUTY. That name is wrong, the fix is still under discussion.] Signed-off-by: Uwe Kleine-König --- drivers/pwm/pwm-loongson.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/pwm/pwm-loongson.c b/drivers/pwm/pwm-loongson.c index f2fb35b7af2b..98c809bde2b3 100644 --- a/drivers/pwm/pwm-loongson.c +++ b/drivers/pwm/pwm-loongson.c @@ -99,6 +99,7 @@ static void pwm_loongson_disable(struct pwm_chip *chip, struct pwm_device *pwm) struct pwm_loongson_ddata *ddata = to_pwm_loongson_ddata(chip); val = pwm_loongson_readl(ddata, LOONGSON_PWM_REG_CTRL); + val |= LOONGSON_PWM_CTRL_REG_RST; val &= ~LOONGSON_PWM_CTRL_REG_EN; pwm_loongson_writel(ddata, val, LOONGSON_PWM_REG_CTRL); } @@ -109,6 +110,7 @@ static int pwm_loongson_enable(struct pwm_chip *chip, struct pwm_device *pwm) struct pwm_loongson_ddata *ddata = to_pwm_loongson_ddata(chip); val = pwm_loongson_readl(ddata, LOONGSON_PWM_REG_CTRL); + val &= ~LOONGSON_PWM_CTRL_REG_RST; val |= LOONGSON_PWM_CTRL_REG_EN; pwm_loongson_writel(ddata, val, LOONGSON_PWM_REG_CTRL); From a662d944da1e9236a99355aa3e8a93735d8b3521 Mon Sep 17 00:00:00 2001 From: Surendra Singh Chouhan Date: Fri, 24 Jul 2026 10:18:54 +0530 Subject: [PATCH 08/13] pwm: ipq: Prevent potential 32-bit integer overflow in hi_div calculation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In ipq_pwm_get_state(), hi_div was calculated as: hi_div = hi_dur * (pre_div + 1); hi_dur and (pre_div + 1) are both unsigned int (32-bit) values. Evaluating their multiplication using 32-bit arithmetic before assigning to the 64-bit u64 hi_div variable can overflow 32-bit unsigned math. While effective_div explicitly uses (u64)(pwm_div + 1) * (pre_div + 1) to prevent overflow, hi_div was missing the (u64) cast. Fix this by casting hi_dur to (u64) before multiplication, matching the precision used for effective_div. Fixes: c436e3e9c265 ("pwm: Driver for qualcomm ipq6018 pwm block") Signed-off-by: Surendra Singh Chouhan Link: https://patch.msgid.link/20260724044854.33274-1-kr494167@gmail.com Signed-off-by: Uwe Kleine-König --- drivers/pwm/pwm-ipq.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/pwm/pwm-ipq.c b/drivers/pwm/pwm-ipq.c index 6b7aec566f7e..3e1231b212b1 100644 --- a/drivers/pwm/pwm-ipq.c +++ b/drivers/pwm/pwm-ipq.c @@ -186,7 +186,7 @@ static int ipq_pwm_get_state(struct pwm_chip *chip, struct pwm_device *pwm, state->period = DIV64_U64_ROUND_UP(effective_div * NSEC_PER_SEC, ipq_chip->clk_rate); - hi_div = hi_dur * (pre_div + 1); + hi_div = (u64)hi_dur * (pre_div + 1); state->duty_cycle = DIV64_U64_ROUND_UP(hi_div * NSEC_PER_SEC, ipq_chip->clk_rate); From c5b29baad42b2b02389cc6a716697a8a63209930 Mon Sep 17 00:00:00 2001 From: Chen-Yu Tsai Date: Thu, 27 Aug 2026 18:57:36 +0800 Subject: [PATCH 09/13] pwm: mtk-disp: Fix period readback MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The period value stored in the register is the number of clock cycles - 1. Account for the offset when calculating the period in nanoseconds. This fixes the discrepancy reported by the pwm core when CONFIG_PWM_DEBUG=1: mediatek-disp-pwm 1100e000.pwm: .apply is not idempotent (ena=1 pol=0 281500/499875) -> (ena=1 pol=0 281500/499750) Calculation discrepancy in the driver was found by AI run by a colleague while debugging another PWM related issue. Check against SoC datasheet and fix done by the author. Fixes: 3f2b16734914 ("pwm: mtk-disp: Implement atomic API .get_state()") Cc: Jitao Shi Signed-off-by: Chen-Yu Tsai Reviewed-by: AngeloGioacchino Del Regno Reviewed-by: Fei Shao Link: https://patch.msgid.link/20260827105738.8570-1-wenst@chromium.org [ukleinek: Adapt comment accordingly] Signed-off-by: Uwe Kleine-König --- drivers/pwm/pwm-mtk-disp.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/pwm/pwm-mtk-disp.c b/drivers/pwm/pwm-mtk-disp.c index b5409e15dac8..56d10cde5b46 100644 --- a/drivers/pwm/pwm-mtk-disp.c +++ b/drivers/pwm/pwm-mtk-disp.c @@ -210,9 +210,9 @@ static int mtk_disp_pwm_get_state(struct pwm_chip *chip, period = FIELD_GET(PWM_PERIOD_MASK, con1); /* * period has 12 bits, clk_div 11 and NSEC_PER_SEC has 30, - * so period * (clk_div + 1) * NSEC_PER_SEC doesn't overflow. + * so (period + 1) * (clk_div + 1) * NSEC_PER_SEC doesn't overflow. */ - state->period = DIV64_U64_ROUND_UP(period * (clk_div + 1) * NSEC_PER_SEC, rate); + state->period = DIV64_U64_ROUND_UP((period + 1) * (clk_div + 1) * NSEC_PER_SEC, rate); high_width = FIELD_GET(PWM_HIGH_WIDTH_MASK, con1); state->duty_cycle = DIV64_U64_ROUND_UP(high_width * (clk_div + 1) * NSEC_PER_SEC, rate); From 17dbb6938d3ab444cd6be1c95506450595a8800d Mon Sep 17 00:00:00 2001 From: Manush Prajwal Date: Sun, 6 Sep 2026 17:11:58 +0530 Subject: [PATCH 10/13] pwm: lp3943: fix NULL pointer dereference for an unconfigured channel MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit lp3943_pwm_probe() unconditionally registers a pwmchip with LP3943_NUM_PWMS(2) hwpwm channels, but lp3943_pwm_parse_dt() only populates pdata->pwms[i] for the channels whose ti,pwm0/ti,pwm1 property is actually present in the devicetree (the binding explicitly allows each PWM generator to drive zero or more outputs, and lp3943_pwm_parse_dt() only fails outright with -ENODATA if *neither* property is present). A devicetree that configures only one of the two channels leaves pdata->pwms[] NULL for the other. lp3943_pwm_request_map() dereferences pdata->pwms[hwpwm] with no NULL check: pwm_map->output = pdata->pwms[hwpwm]->output; pwm_map->num_outputs = pdata->pwms[hwpwm]->num_outputs; so requesting the unconfigured channel (e.g. exporting it from sysfs) crashes with a NULL pointer dereference instead of failing cleanly. Return -ENODEV from lp3943_pwm_request_map() when the channel was never configured, before the pointer is dereferenced. The caller, lp3943_pwm_request(), already propagates an ERR_PTR return correctly (it does so today for the existing -EBUSY case). Reported-by: Sashiko AI review Closes: https://sashiko.dev/#/patchset/6a9d4c6b.79b5ea6e.147aa1.5883@mx.google.com?part=1 Signed-off-by: Manush Prajwal Link: https://patch.msgid.link/6a9d5187.d63de05c.175391.a17c@mx.google.com Fixes: af66b3c0934e ("pwm: Add LP3943 PWM driver") Signed-off-by: Uwe Kleine-König --- drivers/pwm/pwm-lp3943.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/pwm/pwm-lp3943.c b/drivers/pwm/pwm-lp3943.c index 10537e74be28..e42af9007546 100644 --- a/drivers/pwm/pwm-lp3943.c +++ b/drivers/pwm/pwm-lp3943.c @@ -38,6 +38,9 @@ lp3943_pwm_request_map(struct lp3943_pwm *lp3943_pwm, int hwpwm) struct lp3943_pwm_map *pwm_map = &lp3943_pwm->pwm_map[hwpwm]; int i, offset; + if (!pdata->pwms[hwpwm]) + return ERR_PTR(-ENODEV); + pwm_map->output = pdata->pwms[hwpwm]->output; pwm_map->num_outputs = pdata->pwms[hwpwm]->num_outputs; From 45c3d07e3f18cff2b3ea8b797387451a3df9d421 Mon Sep 17 00:00:00 2001 From: Danish Khateeb Date: Mon, 28 Sep 2026 13:44:22 -0500 Subject: [PATCH 11/13] pwm: Allow 32-bit tasks to use the character device MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit pwm_cdev_fileops has no .compat_ioctl, so every ioctl on /dev/pwmchipN fails with -ENOTTY for 32-bit tasks on a 64-bit kernel, e.g. armhf userspace on arm64. struct pwmchip_waveform has the same layout for 32-bit and 64-bit tasks, so compat_ptr_ioctl() is enough. PWM_IOCTL_REQUEST and PWM_IOCTL_FREE take an integer rather than a pointer, which is fine too: compat_ptr() only changed the value on s390, which dropped compat support in v6.19. Fixes: 9c06f26ba5f5 ("pwm: Add support for pwmchip devices for faster and easier userspace access") Assisted-by: LLM Signed-off-by: Danish Khateeb Link: https://patch.msgid.link/20260928184422.380520-1-danishkhateeb03@gmail.com Signed-off-by: Uwe Kleine-König --- drivers/pwm/core.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/pwm/core.c b/drivers/pwm/core.c index 2a050ff4608b..4c6d716e5fde 100644 --- a/drivers/pwm/core.c +++ b/drivers/pwm/core.c @@ -2389,6 +2389,7 @@ static const struct file_operations pwm_cdev_fileops = { .release = pwm_cdev_release, .owner = THIS_MODULE, .unlocked_ioctl = pwm_cdev_ioctl, + .compat_ioctl = compat_ptr_ioctl, }; static dev_t pwm_devt; From e74b9a7ee50071aad25d3989bf985292f90b0c6e Mon Sep 17 00:00:00 2001 From: Alexander Sverdlin Date: Tue, 29 Sep 2026 14:08:02 +0200 Subject: [PATCH 12/13] pwm: tiehrpwm: Clear period on disable to allow reconfiguration MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Both channels share one period register, so ehrpwm_pwm_config() rejects a period differing from the sibling's period_cycles[]. That value was cleared only in .free(), not in .disable(), so a stopped channel kept blocking the sibling from switching to a new frequency. Clear it in .disable() too. Testcase: cd /sys/class/pwm/pwmchip0; echo 0 > export; echo 1 > export for p in pwm0 pwm1; do echo 1000000 > $p/period; echo 500000 > $p/duty_cycle echo 1 > $p/enable; echo 0 > $p/enable; echo 2000000 > $p/period done echo 1 > pwm0/enable # before: -EINVAL, after: succeeds Fixes: 01b2d4536f02 ("pwm: pwm-tiehrpwm: Fix conflicting channel period setting") Cc: stable@vger.kernel.org Signed-off-by: Alexander Sverdlin Link: https://patch.msgid.link/20260929120806.2790355-1-alexander.sverdlin@siemens.com Signed-off-by: Uwe Kleine-König --- drivers/pwm/pwm-tiehrpwm.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/pwm/pwm-tiehrpwm.c b/drivers/pwm/pwm-tiehrpwm.c index 3a53489f0b98..e3407d0a1f7f 100644 --- a/drivers/pwm/pwm-tiehrpwm.c +++ b/drivers/pwm/pwm-tiehrpwm.c @@ -358,6 +358,9 @@ static void ehrpwm_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) /* Disable clock on PWM disable */ pm_runtime_put_sync(pwmchip_parent(chip)); + + /* A disabled channel must not constrain the sibling's period */ + pc->period_cycles[pwm->hwpwm] = 0; } static void ehrpwm_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm) From 00e917df050d79ac26ec700717680ad6421d2808 Mon Sep 17 00:00:00 2001 From: Varadarajan Narayanan Date: Thu, 8 Oct 2026 14:14:00 +0530 Subject: [PATCH 13/13] dt-bindings: pwm: Add IPQ5210 binding MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add DT binding for the PWM block in Qualcomm IPQ5210 SoC with qcom,ipq6018-pwm as the fallback. Signed-off-by: Varadarajan Narayanan Acked-by: Rob Herring (Arm) Link: https://patch.msgid.link/20261008-pwm-v1-1-0b8776398632@oss.qualcomm.com Signed-off-by: Uwe Kleine-König --- Documentation/devicetree/bindings/pwm/qcom,ipq6018-pwm.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/pwm/qcom,ipq6018-pwm.yaml b/Documentation/devicetree/bindings/pwm/qcom,ipq6018-pwm.yaml index f9f1f652e752..c6b9004dd30e 100644 --- a/Documentation/devicetree/bindings/pwm/qcom,ipq6018-pwm.yaml +++ b/Documentation/devicetree/bindings/pwm/qcom,ipq6018-pwm.yaml @@ -15,6 +15,7 @@ properties: - items: - enum: - qcom,ipq5018-pwm + - qcom,ipq5210-pwm - qcom,ipq5332-pwm - qcom,ipq9574-pwm - const: qcom,ipq6018-pwm