From 334c7b13d38321e47d1a51dba0bef9f4c403ec75 Mon Sep 17 00:00:00 2001 From: Emil Renner Berthing Date: Wed, 9 Nov 2022 12:37:24 +0100 Subject: [PATCH 01/12] pwm: sifive: Always let the first pwm_apply_state succeed Commit 2cfe9bbec56ea579135cdd92409fff371841904f added support for the RGB and green PWM controlled LEDs on the HiFive Unmatched board managed by the leds-pwm-multicolor and leds-pwm drivers respectively. All three colours of the RGB LED and the green LED run from different lines of the same PWM, but with the same period so this works fine when the LED drivers are loaded one after the other. Unfortunately it does expose a race in the PWM driver when both LED drivers are loaded at roughly the same time. Here is an example: | Thread A | Thread B | | led_pwm_mc_probe | led_pwm_probe | | devm_fwnode_pwm_get | | | pwm_sifive_request | | | ddata->user_count++ | | | | devm_fwnode_pwm_get | | | pwm_sifive_request | | | ddata->user_count++ | | ... | ... | | pwm_state_apply | pwm_state_apply | | pwm_sifive_apply | pwm_sifive_apply | Now both calls to pwm_sifive_apply will see that ddata->approx_period, initially 0, is different from the requested period and the clock needs to be updated. But since ddata->user_count >= 2 both calls will fail with -EBUSY, which will then cause both LED drivers to fail to probe. Fix it by letting the first call to pwm_sifive_apply update the clock even when ddata->user_count != 1. Fixes: 9e37a53eb051 ("pwm: sifive: Add a driver for SiFive SoC PWM") Signed-off-by: Emil Renner Berthing Signed-off-by: Thierry Reding --- drivers/pwm/pwm-sifive.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/pwm/pwm-sifive.c b/drivers/pwm/pwm-sifive.c index 62b6acc6373db..393a4b97fc19e 100644 --- a/drivers/pwm/pwm-sifive.c +++ b/drivers/pwm/pwm-sifive.c @@ -161,7 +161,13 @@ static int pwm_sifive_apply(struct pwm_chip *chip, struct pwm_device *pwm, mutex_lock(&ddata->lock); if (state->period != ddata->approx_period) { - if (ddata->user_count != 1) { + /* + * Don't let a 2nd user change the period underneath the 1st user. + * However if ddate->approx_period == 0 this is the first time we set + * any period, so let whoever gets here first set the period so other + * users who agree on the period won't fail. + */ + if (ddata->user_count != 1 && ddata->approx_period) { mutex_unlock(&ddata->lock); return -EBUSY; } From b3c650ad9bb88ecf36b9aeacf9e7eb7478258da7 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Mon, 21 Nov 2022 17:15:41 +0100 Subject: [PATCH 02/12] pwm: Move pwm_capture() dummy to restore order MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move the dummy pwm_capture(), to make the declaration order of all dummies to match the declaration order of the real functions. Signed-off-by: Geert Uytterhoeven Acked-by: Uwe Kleine-König Signed-off-by: Thierry Reding --- include/linux/pwm.h | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/include/linux/pwm.h b/include/linux/pwm.h index 161e91167b9c0..7b7b93b6fb81a 100644 --- a/include/linux/pwm.h +++ b/include/linux/pwm.h @@ -440,13 +440,6 @@ static inline int pwm_config(struct pwm_device *pwm, int duty_ns, return -EINVAL; } -static inline int pwm_capture(struct pwm_device *pwm, - struct pwm_capture *result, - unsigned long timeout) -{ - return -EINVAL; -} - static inline int pwm_enable(struct pwm_device *pwm) { might_sleep(); @@ -458,6 +451,13 @@ static inline void pwm_disable(struct pwm_device *pwm) might_sleep(); } +static inline int pwm_capture(struct pwm_device *pwm, + struct pwm_capture *result, + unsigned long timeout) +{ + return -EINVAL; +} + static inline int pwm_set_chip_data(struct pwm_device *pwm, void *data) { return -EINVAL; From 3066bc2d58be31275afb51a589668f265e419c37 Mon Sep 17 00:00:00 2001 From: Fabrice Gasnier Date: Wed, 23 Nov 2022 14:36:52 +0100 Subject: [PATCH 03/12] pwm: stm32-lp: fix the check on arr and cmp registers update MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The ARR (auto reload register) and CMP (compare) registers are successively written. The status bits to check the update of these registers are polled together with regmap_read_poll_timeout(). The condition to end the loop may become true, even if one of the register isn't correctly updated. So ensure both status bits are set before clearing them. Fixes: e70a540b4e02 ("pwm: Add STM32 LPTimer PWM driver") Signed-off-by: Fabrice Gasnier Acked-by: Uwe Kleine-König Signed-off-by: Thierry Reding --- drivers/pwm/pwm-stm32-lp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/pwm/pwm-stm32-lp.c b/drivers/pwm/pwm-stm32-lp.c index 514ff58a4471d..f315fa106be87 100644 --- a/drivers/pwm/pwm-stm32-lp.c +++ b/drivers/pwm/pwm-stm32-lp.c @@ -127,7 +127,7 @@ static int stm32_pwm_lp_apply(struct pwm_chip *chip, struct pwm_device *pwm, /* ensure CMP & ARR registers are properly written */ ret = regmap_read_poll_timeout(priv->regmap, STM32_LPTIM_ISR, val, - (val & STM32_LPTIM_CMPOK_ARROK), + (val & STM32_LPTIM_CMPOK_ARROK) == STM32_LPTIM_CMPOK_ARROK, 100, 1000); if (ret) { dev_err(priv->chip.dev, "ARR/CMP registers write issue\n"); From 3e98855ca0cf823330b27f51be41e92fdbaa9057 Mon Sep 17 00:00:00 2001 From: AngeloGioacchino Del Regno Date: Mon, 28 Nov 2022 12:20:28 +0100 Subject: [PATCH 04/12] dt-bindings: pwm: mediatek: Convert pwm-mediatek to DT schema This converts pwm-mediatek.txt to mediatek,mt2712-pwm.yaml schema; while at it, the clock names were clarified as previously they were documented as "pwmX-Y", but valid names are "pwmN" only. Also, the example was changed to use "mediatek,mt2712-pwm" instead for consistency with the schema filename. Signed-off-by: AngeloGioacchino Del Regno Reviewed-by: Krzysztof Kozlowski Signed-off-by: Thierry Reding --- .../bindings/pwm/mediatek,mt2712-pwm.yaml | 93 +++++++++++++++++++ .../devicetree/bindings/pwm/pwm-mediatek.txt | 52 ----------- 2 files changed, 93 insertions(+), 52 deletions(-) create mode 100644 Documentation/devicetree/bindings/pwm/mediatek,mt2712-pwm.yaml delete mode 100644 Documentation/devicetree/bindings/pwm/pwm-mediatek.txt diff --git a/Documentation/devicetree/bindings/pwm/mediatek,mt2712-pwm.yaml b/Documentation/devicetree/bindings/pwm/mediatek,mt2712-pwm.yaml new file mode 100644 index 0000000000000..dbc974bff9e91 --- /dev/null +++ b/Documentation/devicetree/bindings/pwm/mediatek,mt2712-pwm.yaml @@ -0,0 +1,93 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/pwm/mediatek,mt2712-pwm.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: MediaTek PWM Controller + +maintainers: + - John Crispin + +allOf: + - $ref: pwm.yaml# + +properties: + compatible: + oneOf: + - enum: + - mediatek,mt2712-pwm + - mediatek,mt6795-pwm + - mediatek,mt7622-pwm + - mediatek,mt7623-pwm + - mediatek,mt7628-pwm + - mediatek,mt7629-pwm + - mediatek,mt8183-pwm + - mediatek,mt8365-pwm + - mediatek,mt8516-pwm + - items: + - enum: + - mediatek,mt8195-pwm + - const: mediatek,mt8183-pwm + + reg: + maxItems: 1 + + "#pwm-cells": + const: 2 + + interrupts: + maxItems: 1 + + clocks: + minItems: 2 + maxItems: 10 + + clock-names: + description: + This controller needs two input clocks for its core and one + clock for each PWM output. + minItems: 2 + items: + - const: top + - const: main + - const: pwm1 + - const: pwm2 + - const: pwm3 + - const: pwm4 + - const: pwm5 + - const: pwm6 + - const: pwm7 + - const: pwm8 + +required: + - compatible + - reg + - "#pwm-cells" + - clocks + - clock-names + +additionalProperties: false + +examples: + - | + #include + #include + #include + + pwm0: pwm@11006000 { + compatible = "mediatek,mt2712-pwm"; + reg = <0x11006000 0x1000>; + #pwm-cells = <2>; + interrupts = ; + clocks = <&topckgen CLK_TOP_PWM_SEL>, <&pericfg CLK_PERI_PWM>, + <&pericfg CLK_PERI_PWM0>, <&pericfg CLK_PERI_PWM1>, + <&pericfg CLK_PERI_PWM2>, <&pericfg CLK_PERI_PWM3>, + <&pericfg CLK_PERI_PWM4>, <&pericfg CLK_PERI_PWM5>, + <&pericfg CLK_PERI_PWM6>, <&pericfg CLK_PERI_PWM7>; + clock-names = "top", "main", + "pwm1", "pwm2", + "pwm3", "pwm4", + "pwm5", "pwm6", + "pwm7", "pwm8"; + }; diff --git a/Documentation/devicetree/bindings/pwm/pwm-mediatek.txt b/Documentation/devicetree/bindings/pwm/pwm-mediatek.txt deleted file mode 100644 index 554c96b6d0c3e..0000000000000 --- a/Documentation/devicetree/bindings/pwm/pwm-mediatek.txt +++ /dev/null @@ -1,52 +0,0 @@ -MediaTek PWM controller - -Required properties: - - compatible: should be "mediatek,-pwm": - - "mediatek,mt2712-pwm": found on mt2712 SoC. - - "mediatek,mt6795-pwm": found on mt6795 SoC. - - "mediatek,mt7622-pwm": found on mt7622 SoC. - - "mediatek,mt7623-pwm": found on mt7623 SoC. - - "mediatek,mt7628-pwm": found on mt7628 SoC. - - "mediatek,mt7629-pwm": found on mt7629 SoC. - - "mediatek,mt8183-pwm": found on mt8183 SoC. - - "mediatek,mt8195-pwm", "mediatek,mt8183-pwm": found on mt8195 SoC. - - "mediatek,mt8365-pwm": found on mt8365 SoC. - - "mediatek,mt8516-pwm": found on mt8516 SoC. - - reg: physical base address and length of the controller's registers. - - #pwm-cells: must be 2. See pwm.yaml in this directory for a description of - the cell format. - - clocks: phandle and clock specifier of the PWM reference clock. - - clock-names: must contain the following, except for MT7628 which - has no clocks - - "top": the top clock generator - - "main": clock used by the PWM core - - "pwm1-3": the three per PWM clocks for mt8365 - - "pwm1-8": the eight per PWM clocks for mt2712 - - "pwm1-6": the six per PWM clocks for mt7622 - - "pwm1-5": the five per PWM clocks for mt7623 - - "pwm1" : the PWM1 clock for mt7629 - - pinctrl-names: Must contain a "default" entry. - - pinctrl-0: One property must exist for each entry in pinctrl-names. - See pinctrl/pinctrl-bindings.txt for details of the property values. - -Optional properties: -- assigned-clocks: Reference to the PWM clock entries. -- assigned-clock-parents: The phandle of the parent clock of PWM clock. - -Example: - pwm0: pwm@11006000 { - compatible = "mediatek,mt7623-pwm"; - reg = <0 0x11006000 0 0x1000>; - #pwm-cells = <2>; - clocks = <&topckgen CLK_TOP_PWM_SEL>, - <&pericfg CLK_PERI_PWM>, - <&pericfg CLK_PERI_PWM1>, - <&pericfg CLK_PERI_PWM2>, - <&pericfg CLK_PERI_PWM3>, - <&pericfg CLK_PERI_PWM4>, - <&pericfg CLK_PERI_PWM5>; - clock-names = "top", "main", "pwm1", "pwm2", - "pwm3", "pwm4", "pwm5"; - pinctrl-names = "default"; - pinctrl-0 = <&pwm0_pins>; - }; From 2781f8e9203685208c9f3717593601d4b4674372 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Mon, 19 Dec 2022 09:10:08 +0100 Subject: [PATCH 05/12] pwm: lp3943: Drop unused i2c include MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit lp3943 is a platform driver that doesn't use any symbol provided in . So drop the include. Signed-off-by: Uwe Kleine-König Signed-off-by: Thierry Reding --- drivers/pwm/pwm-lp3943.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/pwm/pwm-lp3943.c b/drivers/pwm/pwm-lp3943.c index 215ef90691144..35675e4058c6b 100644 --- a/drivers/pwm/pwm-lp3943.c +++ b/drivers/pwm/pwm-lp3943.c @@ -8,7 +8,6 @@ */ #include -#include #include #include #include From 486dd4e846814016443abfcbfee0b8b3f3b35330 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Wed, 18 Jan 2023 16:48:16 +0100 Subject: [PATCH 06/12] pwm: ab8500: Fix calculation of duty and period MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit After a check of the manual it becomes obvious that the calculations in .apply() are totally bogus: FreqPWMOutx was always written as zero, so the period was fixed at 3413333.33 ns. However state->period wasn't checked at all. The lower 10 bits of duty_cycle were just used as DutyPWMOutx. So if a duty cycle of 512 ns (or 1536 ns) was requested, it actually programmed 1710000 ns. Other values were wrong by the same factor. Signed-off-by: Uwe Kleine-König Signed-off-by: Thierry Reding --- drivers/pwm/pwm-ab8500.c | 69 ++++++++++++++++++++++++++++++++++------ 1 file changed, 60 insertions(+), 9 deletions(-) diff --git a/drivers/pwm/pwm-ab8500.c b/drivers/pwm/pwm-ab8500.c index ad37bc46f2721..c5239eef3b8fb 100644 --- a/drivers/pwm/pwm-ab8500.c +++ b/drivers/pwm/pwm-ab8500.c @@ -3,6 +3,7 @@ * Copyright (C) ST-Ericsson SA 2010 * * Author: Arun R Murthy + * Datasheet: https://web.archive.org/web/20130614115108/http://www.stericsson.com/developers/CD00291561_UM1031_AB8500_user_manual-rev5_CTDS_public.pdf */ #include #include @@ -20,6 +21,8 @@ #define AB8500_PWM_OUT_CTRL2_REG 0x61 #define AB8500_PWM_OUT_CTRL7_REG 0x66 +#define AB8500_PWM_CLKRATE 9600000 + struct ab8500_pwm_chip { struct pwm_chip chip; unsigned int hwid; @@ -35,13 +38,60 @@ static int ab8500_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, { int ret; u8 reg; - unsigned int higher_val, lower_val; + u8 higher_val, lower_val; + unsigned int duty_steps, div; struct ab8500_pwm_chip *ab8500 = ab8500_pwm_from_chip(chip); if (state->polarity != PWM_POLARITY_NORMAL) return -EINVAL; - if (!state->enabled) { + if (state->enabled) { + /* + * A time quantum is + * q = (32 - FreqPWMOutx[3:0]) / AB8500_PWM_CLKRATE + * The period is always 1024 q, duty_cycle is between 1q and 1024q. + * + * FreqPWMOutx[3:0] | output frequency | output frequency | 1024q = period + * | (from manual) | (1 / 1024q) | = 1 / freq + * -----------------+------------------+------------------+-------------- + * b0000 | 293 Hz | 292.968750 Hz | 3413333.33 ns + * b0001 | 302 Hz | 302.419355 Hz | 3306666.66 ns + * b0010 | 312 Hz | 312.500000 Hz | 3200000 ns + * b0011 | 323 Hz | 323.275862 Hz | 3093333.33 ns + * b0100 | 334 Hz | 334.821429 Hz | 2986666.66 ns + * b0101 | 347 Hz | 347.222222 Hz | 2880000 ns + * b0110 | 360 Hz | 360.576923 Hz | 2773333.33 ns + * b0111 | 375 Hz | 375.000000 Hz | 2666666.66 ns + * b1000 | 390 Hz | 390.625000 Hz | 2560000 ns + * b1001 | 407 Hz | 407.608696 Hz | 2453333.33 ns + * b1010 | 426 Hz | 426.136364 Hz | 2346666.66 ns + * b1011 | 446 Hz | 446.428571 Hz | 2240000 ns + * b1100 | 468 Hz | 468.750000 Hz | 2133333.33 ns + * b1101 | 493 Hz | 493.421053 Hz | 2026666.66 ns + * b1110 | 520 Hz | 520.833333 Hz | 1920000 ns + * b1111 | 551 Hz | 551.470588 Hz | 1813333.33 ns + * + * + * AB8500_PWM_CLKRATE is a multiple of 1024, so the division by + * 1024 can be done in this factor without loss of precision. + */ + div = min_t(u64, mul_u64_u64_div_u64(state->period, + AB8500_PWM_CLKRATE >> 10, + NSEC_PER_SEC), 32); /* 32 - FreqPWMOutx[3:0] */ + if (div <= 16) + /* requested period < 3413333.33 */ + return -EINVAL; + + duty_steps = max_t(u64, mul_u64_u64_div_u64(state->duty_cycle, + AB8500_PWM_CLKRATE, + (u64)NSEC_PER_SEC * div), 1024); + } + + /* + * The hardware doesn't support duty_steps = 0 explicitly, but emits low + * when disabled. + */ + if (!state->enabled || duty_steps == 0) { ret = abx500_mask_and_set_register_interruptible(chip->dev, AB8500_MISC, AB8500_PWM_OUT_CTRL7_REG, 1 << ab8500->hwid, 0); @@ -53,28 +103,29 @@ static int ab8500_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, } /* - * get the first 8 bits that are be written to + * The lower 8 bits of duty_steps is written to ... * AB8500_PWM_OUT_CTRL1_REG[0:7] */ - lower_val = state->duty_cycle & 0x00FF; + lower_val = (duty_steps - 1) & 0x00ff; /* - * get bits [9:10] that are to be written to - * AB8500_PWM_OUT_CTRL2_REG[0:1] + * The two remaining high bits to + * AB8500_PWM_OUT_CTRL2_REG[0:1]; together with FreqPWMOutx. */ - higher_val = ((state->duty_cycle & 0x0300) >> 8); + higher_val = ((duty_steps - 1) & 0x0300) >> 8 | (32 - div) << 4; reg = AB8500_PWM_OUT_CTRL1_REG + (ab8500->hwid * 2); ret = abx500_set_register_interruptible(chip->dev, AB8500_MISC, - reg, (u8)lower_val); + reg, lower_val); if (ret < 0) return ret; ret = abx500_set_register_interruptible(chip->dev, AB8500_MISC, - (reg + 1), (u8)higher_val); + (reg + 1), higher_val); if (ret < 0) return ret; + /* enable */ ret = abx500_mask_and_set_register_interruptible(chip->dev, AB8500_MISC, AB8500_PWM_OUT_CTRL7_REG, 1 << ab8500->hwid, 1 << ab8500->hwid); From 327437884e9a752ccbf759cbab641439ca708f5b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Wed, 18 Jan 2023 16:48:17 +0100 Subject: [PATCH 07/12] pwm: ab8500: Implement .get_state() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The registers are readable, so it's possible to implement the .get_state() callback for this PWM. Signed-off-by: Uwe Kleine-König Signed-off-by: Thierry Reding --- drivers/pwm/pwm-ab8500.c | 43 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/drivers/pwm/pwm-ab8500.c b/drivers/pwm/pwm-ab8500.c index c5239eef3b8fb..507ff0d5f7bd8 100644 --- a/drivers/pwm/pwm-ab8500.c +++ b/drivers/pwm/pwm-ab8500.c @@ -136,8 +136,51 @@ static int ab8500_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, return ret; } +static int ab8500_pwm_get_state(struct pwm_chip *chip, struct pwm_device *pwm, + struct pwm_state *state) +{ + u8 ctrl7, lower_val, higher_val; + int ret; + struct ab8500_pwm_chip *ab8500 = ab8500_pwm_from_chip(chip); + unsigned int div, duty_steps; + + ret = abx500_get_register_interruptible(chip->dev, AB8500_MISC, + AB8500_PWM_OUT_CTRL7_REG, + &ctrl7); + if (ret) + return ret; + + state->polarity = PWM_POLARITY_NORMAL; + + if (!(ctrl7 & 1 << ab8500->hwid)) { + state->enabled = false; + return 0; + } + + ret = abx500_get_register_interruptible(chip->dev, AB8500_MISC, + AB8500_PWM_OUT_CTRL1_REG + (ab8500->hwid * 2), + &lower_val); + if (ret) + return ret; + + ret = abx500_get_register_interruptible(chip->dev, AB8500_MISC, + AB8500_PWM_OUT_CTRL2_REG + (ab8500->hwid * 2), + &higher_val); + if (ret) + return ret; + + div = 32 - ((higher_val & 0xf0) >> 4); + duty_steps = ((higher_val & 3) << 8 | lower_val) + 1; + + state->period = DIV64_U64_ROUND_UP((u64)div << 10, AB8500_PWM_CLKRATE); + state->duty_cycle = DIV64_U64_ROUND_UP((u64)div * duty_steps, AB8500_PWM_CLKRATE); + + return 0; +} + static const struct pwm_ops ab8500_pwm_ops = { .apply = ab8500_pwm_apply, + .get_state = ab8500_pwm_get_state, .owner = THIS_MODULE, }; From 860793bbdcdfbeae684d552ce0121846cffc4803 Mon Sep 17 00:00:00 2001 From: Jeff LaBundy Date: Sun, 5 Feb 2023 20:56:22 -0600 Subject: [PATCH 08/12] pwm: iqs620a: Replace one remaining instance of regmap_update_bits() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The call to regmap_update_bits() which was responsible for clearing the PWM output enable register bit was recently dropped in favor of a call to regmap_clear_bits(), thereby simplifying the code. Similarly, the call to regmap_update_bits() which sets the same bit can be simplified with a call to regmap_set_bits(). Signed-off-by: Jeff LaBundy Reviewed-by: Uwe Kleine-König Signed-off-by: Thierry Reding --- drivers/pwm/pwm-iqs620a.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/pwm/pwm-iqs620a.c b/drivers/pwm/pwm-iqs620a.c index 4987ca940b648..8362b4870c66c 100644 --- a/drivers/pwm/pwm-iqs620a.c +++ b/drivers/pwm/pwm-iqs620a.c @@ -55,8 +55,8 @@ static int iqs620_pwm_init(struct iqs620_pwm_private *iqs620_pwm, if (ret) return ret; - return regmap_update_bits(iqs62x->regmap, IQS620_PWR_SETTINGS, - IQS620_PWR_SETTINGS_PWM_OUT, 0xff); + return regmap_set_bits(iqs62x->regmap, IQS620_PWR_SETTINGS, + IQS620_PWR_SETTINGS_PWM_OUT); } static int iqs620_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, From 0f03bf300833c05d914ab7f5ab3d8bc8564e9912 Mon Sep 17 00:00:00 2001 From: Ben Dooks Date: Fri, 23 Dec 2022 15:38:11 +0000 Subject: [PATCH 09/12] dt-bindings: pwm: Document Synopsys DesignWare snps,pwm-dw-apb-timers-pwm2 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add documentation for the bindings for Synopsys' DesignWare PWM block as we will be adding DT/platform support to the Linux driver soon. Signed-off-by: Ben Dooks Reviewed-by: Krzysztof Kozlowski Acked-by: Uwe Kleine-König Signed-off-by: Thierry Reding --- .../bindings/pwm/snps,dw-apb-timers-pwm2.yaml | 68 +++++++++++++++++++ 1 file changed, 68 insertions(+) create mode 100644 Documentation/devicetree/bindings/pwm/snps,dw-apb-timers-pwm2.yaml diff --git a/Documentation/devicetree/bindings/pwm/snps,dw-apb-timers-pwm2.yaml b/Documentation/devicetree/bindings/pwm/snps,dw-apb-timers-pwm2.yaml new file mode 100644 index 0000000000000..9aabdb373afa2 --- /dev/null +++ b/Documentation/devicetree/bindings/pwm/snps,dw-apb-timers-pwm2.yaml @@ -0,0 +1,68 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +# Copyright (C) 2022 SiFive, Inc. +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/pwm/snps,dw-apb-timers-pwm2.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Synopsys DW-APB timers PWM controller + +maintainers: + - Ben Dooks + +description: + This describes the DesignWare APB timers module when used in the PWM + mode. The IP core can be generated with various options which can + control the functionality, the number of PWMs available and other + internal controls the designer requires. + + The IP block has a version register so this can be used for detection + instead of having to encode the IP version number in the device tree + comaptible. + +allOf: + - $ref: pwm.yaml# + +properties: + compatible: + const: snps,dw-apb-timers-pwm2 + + reg: + maxItems: 1 + + "#pwm-cells": + const: 3 + + clocks: + items: + - description: Interface bus clock + - description: PWM reference clock + + clock-names: + items: + - const: bus + - const: timer + + snps,pwm-number: + $ref: /schemas/types.yaml#/definitions/uint32 + description: The number of PWM channels configured for this instance + enum: [1, 2, 3, 4, 5, 6, 7, 8] + +required: + - compatible + - reg + - "#pwm-cells" + - clocks + - clock-names + +additionalProperties: false + +examples: + - | + pwm: pwm@180000 { + compatible = "snps,dw-apb-timers-pwm2"; + reg = <0x180000 0x200>; + #pwm-cells = <3>; + clocks = <&bus>, <&timer>; + clock-names = "bus", "timer"; + }; From f7c843d6d7f82d26ee9efe689f9b3208dbf7ed87 Mon Sep 17 00:00:00 2001 From: Ben Dooks Date: Fri, 23 Dec 2022 15:38:13 +0000 Subject: [PATCH 10/12] pwm: dwc: Change &pci->dev to dev in probe MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The dwc_pwm_probe() assigns dev to be &pci->dev but then uses &pci->dev throughout the function. Change these all to the 'dev' variable to make lines shorter. Signed-off-by: Ben Dooks Acked-by: Uwe Kleine-König Signed-off-by: Thierry Reding --- drivers/pwm/pwm-dwc.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/drivers/pwm/pwm-dwc.c b/drivers/pwm/pwm-dwc.c index bd2308812096d..de1c497d5b488 100644 --- a/drivers/pwm/pwm-dwc.c +++ b/drivers/pwm/pwm-dwc.c @@ -204,14 +204,13 @@ static int dwc_pwm_probe(struct pci_dev *pci, const struct pci_device_id *id) struct dwc_pwm *dwc; int ret; - dwc = devm_kzalloc(&pci->dev, sizeof(*dwc), GFP_KERNEL); + dwc = devm_kzalloc(dev, sizeof(*dwc), GFP_KERNEL); if (!dwc) return -ENOMEM; ret = pcim_enable_device(pci); if (ret) { - dev_err(&pci->dev, - "Failed to enable device (%pe)\n", ERR_PTR(ret)); + dev_err(dev, "Failed to enable device (%pe)\n", ERR_PTR(ret)); return ret; } @@ -219,14 +218,13 @@ static int dwc_pwm_probe(struct pci_dev *pci, const struct pci_device_id *id) ret = pcim_iomap_regions(pci, BIT(0), pci_name(pci)); if (ret) { - dev_err(&pci->dev, - "Failed to iomap PCI BAR (%pe)\n", ERR_PTR(ret)); + dev_err(dev, "Failed to iomap PCI BAR (%pe)\n", ERR_PTR(ret)); return ret; } dwc->base = pcim_iomap_table(pci)[0]; if (!dwc->base) { - dev_err(&pci->dev, "Base address missing\n"); + dev_err(dev, "Base address missing\n"); return -ENOMEM; } From a357d1493f0c66ce8006dd28c07646d1f891259a Mon Sep 17 00:00:00 2001 From: Ben Dooks Date: Fri, 23 Dec 2022 15:38:14 +0000 Subject: [PATCH 11/12] pwm: dwc: Move memory allocation to own function MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In preparation for adding other bus support, move the allocation of the PWM structure out of the main driver code. Signed-off-by: Ben Dooks Acked-by: Uwe Kleine-König Signed-off-by: Thierry Reding --- drivers/pwm/pwm-dwc.c | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/drivers/pwm/pwm-dwc.c b/drivers/pwm/pwm-dwc.c index de1c497d5b488..e9399df702ed4 100644 --- a/drivers/pwm/pwm-dwc.c +++ b/drivers/pwm/pwm-dwc.c @@ -198,13 +198,29 @@ static const struct pwm_ops dwc_pwm_ops = { .owner = THIS_MODULE, }; +static struct dwc_pwm *dwc_pwm_alloc(struct device *dev) +{ + struct dwc_pwm *dwc; + + dwc = devm_kzalloc(dev, sizeof(*dwc), GFP_KERNEL); + if (!dwc) + return NULL; + + dwc->chip.dev = dev; + dwc->chip.ops = &dwc_pwm_ops; + dwc->chip.npwm = DWC_TIMERS_TOTAL; + + dev_set_drvdata(dev, dwc); + return dwc; +} + static int dwc_pwm_probe(struct pci_dev *pci, const struct pci_device_id *id) { struct device *dev = &pci->dev; struct dwc_pwm *dwc; int ret; - dwc = devm_kzalloc(dev, sizeof(*dwc), GFP_KERNEL); + dwc = dwc_pwm_alloc(dev); if (!dwc) return -ENOMEM; @@ -228,12 +244,6 @@ static int dwc_pwm_probe(struct pci_dev *pci, const struct pci_device_id *id) return -ENOMEM; } - pci_set_drvdata(pci, dwc); - - dwc->chip.dev = dev; - dwc->chip.ops = &dwc_pwm_ops; - dwc->chip.npwm = DWC_TIMERS_TOTAL; - ret = pwmchip_add(&dwc->chip); if (ret) return ret; From cf70d01a62c712ee715df1f7892b58c77474bcfb Mon Sep 17 00:00:00 2001 From: Ben Dooks Date: Fri, 23 Dec 2022 15:38:15 +0000 Subject: [PATCH 12/12] pwm: dwc: Use devm_pwmchip_add() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add the PWM chip using devm_pwmchip_add() to avoid having to manually remove it. This is useful for subsequent patches adding platform device support. Signed-off-by: Ben Dooks Reviewed-by: Uwe Kleine-König Signed-off-by: Thierry Reding --- drivers/pwm/pwm-dwc.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/drivers/pwm/pwm-dwc.c b/drivers/pwm/pwm-dwc.c index e9399df702ed4..3bbb26c862c35 100644 --- a/drivers/pwm/pwm-dwc.c +++ b/drivers/pwm/pwm-dwc.c @@ -244,7 +244,7 @@ static int dwc_pwm_probe(struct pci_dev *pci, const struct pci_device_id *id) return -ENOMEM; } - ret = pwmchip_add(&dwc->chip); + ret = devm_pwmchip_add(dev, &dwc->chip); if (ret) return ret; @@ -256,12 +256,8 @@ static int dwc_pwm_probe(struct pci_dev *pci, const struct pci_device_id *id) static void dwc_pwm_remove(struct pci_dev *pci) { - struct dwc_pwm *dwc = pci_get_drvdata(pci); - pm_runtime_forbid(&pci->dev); pm_runtime_get_noresume(&pci->dev); - - pwmchip_remove(&dwc->chip); } #ifdef CONFIG_PM_SLEEP