2022-01-26 21:16:20

by Nikita Travkin

[permalink] [raw]
Subject: [PATCH v4 0/2] Clock based PWM output driver

This series introduces an "adapter" driver that allows PWM consumers
to control clock outputs with duty-cycle control.

Some platforms (e.g. some Qualcomm chipsets) have "General Purpose"
clocks that can be muxed to GPIO outputs and used as PWM outputs.
Those outputs may be connected to various peripherals such as
leds in display backlight or haptic feedback motor driver.

To avoid re-implementing every single PWM consumer driver with clk
support (like in [1]) and don't put the burden of providing the PWM
sources on the clock drivers (as was proposed in [2]), clk based
pwm controller driver is introduced.

There is an existing driver that provides the opposite function
in drivers/clk/clk-pwm.c with a compatible "pwm-clock" so the new
driver uses the opposite naming scheme: drivers/pwm/pwm-clk.c
and compatible "clk-pwm".

Changes in v2:
- Fix filename in the DT schema.
- Address Uwe's review comments.
Changes in v3:
- Fix node pattern in the core pwm schema.
- Address Uwe's review comments.
Changes in v4:
- Drop the (incorrect) pwm schema change.
- Use generic node name in the dt bindings example.

[1] https://lore.kernel.org/lkml/[email protected]/
[2] https://lore.kernel.org/lkml/CACRpkdZxu1LfK11OHEx5L_4kyjMZ7qERpvDzFj5u3Pk2kD1qRA@mail.gmail.com/

Nikita Travkin (2):
dt-bindings: pwm: Document clk based PWM controller
pwm: Add clock based PWM output driver

.../devicetree/bindings/pwm/clk-pwm.yaml | 45 ++++++
drivers/pwm/Kconfig | 10 ++
drivers/pwm/Makefile | 1 +
drivers/pwm/pwm-clk.c | 139 ++++++++++++++++++
4 files changed, 195 insertions(+)
create mode 100644 Documentation/devicetree/bindings/pwm/clk-pwm.yaml
create mode 100644 drivers/pwm/pwm-clk.c

--
2.34.1


2022-01-26 21:16:26

by Nikita Travkin

[permalink] [raw]
Subject: [PATCH v4 2/2] pwm: Add clock based PWM output driver

Some systems have clocks exposed to external devices. If the clock
controller supports duty-cycle configuration, such clocks can be used as
pwm outputs. In fact PWM and CLK subsystems are interfaced with in a
similar way and an "opposite" driver already exists (clk-pwm). Add a
driver that would enable pwm devices to be used via clk subsystem.

Signed-off-by: Nikita Travkin <[email protected]>
--

Changes in v2:
- Address Uwe's review comments:
- Round set clk rate up
- Add a description with limitations of the driver
- Disable and unprepare clock before removing pwmchip
Changes in v3:
- Use 64bit version of div round up
- Address Uwe's review comments:
- Reword the limitations to avoid incorrect claims
- Move the clk_enabled flag assignment
- Drop unnecessary statements
---
drivers/pwm/Kconfig | 10 +++
drivers/pwm/Makefile | 1 +
drivers/pwm/pwm-clk.c | 139 ++++++++++++++++++++++++++++++++++++++++++
3 files changed, 150 insertions(+)
create mode 100644 drivers/pwm/pwm-clk.c

diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig
index 21e3b05a5153..daa2491a4054 100644
--- a/drivers/pwm/Kconfig
+++ b/drivers/pwm/Kconfig
@@ -140,6 +140,16 @@ config PWM_BRCMSTB
To compile this driver as a module, choose M Here: the module
will be called pwm-brcmstb.c.

+config PWM_CLK
+ tristate "Clock based PWM support"
+ depends on HAVE_CLK || COMPILE_TEST
+ help
+ Generic PWM framework driver for outputs that can be
+ muxed to clocks.
+
+ To compile this driver as a module, choose M here: the module
+ will be called pwm-clk.
+
config PWM_CLPS711X
tristate "CLPS711X PWM support"
depends on ARCH_CLPS711X || COMPILE_TEST
diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile
index 708840b7fba8..4a860103c470 100644
--- a/drivers/pwm/Makefile
+++ b/drivers/pwm/Makefile
@@ -10,6 +10,7 @@ obj-$(CONFIG_PWM_BCM_KONA) += pwm-bcm-kona.o
obj-$(CONFIG_PWM_BCM2835) += pwm-bcm2835.o
obj-$(CONFIG_PWM_BERLIN) += pwm-berlin.o
obj-$(CONFIG_PWM_BRCMSTB) += pwm-brcmstb.o
+obj-$(CONFIG_PWM_CLK) += pwm-clk.o
obj-$(CONFIG_PWM_CLPS711X) += pwm-clps711x.o
obj-$(CONFIG_PWM_CRC) += pwm-crc.o
obj-$(CONFIG_PWM_CROS_EC) += pwm-cros-ec.o
diff --git a/drivers/pwm/pwm-clk.c b/drivers/pwm/pwm-clk.c
new file mode 100644
index 000000000000..b3bfa12a0e73
--- /dev/null
+++ b/drivers/pwm/pwm-clk.c
@@ -0,0 +1,139 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Clock based PWM controller
+ *
+ * Copyright (c) 2021 Nikita Travkin <[email protected]>
+ *
+ * This is an "adapter" driver that allows PWM consumers to use
+ * system clocks with duty cycle control as PWM outputs.
+ *
+ * Limitations:
+ * - Glitches are possible when new pwm state is applied.
+ * - Due to the fact that exact behavior depends on the underlying
+ * clock driver, various limitations are possible.
+ * - Period depends on the clock and, in general, not guaranteed.
+ * - Underlying clock may not be able to give 0% or 100% duty cycle
+ * (constant off or on), exact behavior will depend on the clock.
+ * - When the PWM is disabled, the clock will be disabled as well,
+ * line state will depend on the clock.
+ */
+
+#include <linux/kernel.h>
+#include <linux/math64.h>
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/pwm.h>
+
+struct pwm_clk_chip {
+ struct pwm_chip chip;
+ struct clk *clk;
+ bool clk_enabled;
+};
+
+#define to_pwm_clk_chip(_chip) container_of(_chip, struct pwm_clk_chip, chip)
+
+static int pwm_clk_apply(struct pwm_chip *pwm_chip, struct pwm_device *pwm,
+ const struct pwm_state *state)
+{
+ struct pwm_clk_chip *chip = to_pwm_clk_chip(pwm_chip);
+ int ret;
+ u32 rate;
+ u64 period = state->period;
+ u64 duty_cycle = state->duty_cycle;
+
+ if (!state->enabled) {
+ if (pwm->state.enabled) {
+ clk_disable(chip->clk);
+ chip->clk_enabled = false;
+ }
+ return 0;
+ } else if (!pwm->state.enabled) {
+ ret = clk_enable(chip->clk);
+ if (ret)
+ return ret;
+ chip->clk_enabled = true;
+ }
+
+ rate = DIV64_U64_ROUND_UP(NSEC_PER_SEC, period);
+ ret = clk_set_rate(chip->clk, rate);
+ if (ret)
+ return ret;
+
+ if (state->polarity == PWM_POLARITY_INVERSED)
+ duty_cycle = period - duty_cycle;
+
+ return clk_set_duty_cycle(chip->clk, duty_cycle, period);
+}
+
+static const struct pwm_ops pwm_clk_ops = {
+ .apply = pwm_clk_apply,
+ .owner = THIS_MODULE,
+};
+
+static int pwm_clk_probe(struct platform_device *pdev)
+{
+ struct pwm_clk_chip *chip;
+ int ret;
+
+ chip = devm_kzalloc(&pdev->dev, sizeof(*chip), GFP_KERNEL);
+ if (!chip)
+ return -ENOMEM;
+
+ chip->clk = devm_clk_get(&pdev->dev, NULL);
+ if (IS_ERR(chip->clk))
+ return dev_err_probe(&pdev->dev, PTR_ERR(chip->clk),
+ "Failed to get clock\n");
+
+ chip->chip.dev = &pdev->dev;
+ chip->chip.ops = &pwm_clk_ops;
+ chip->chip.npwm = 1;
+
+ ret = clk_prepare(chip->clk);
+ if (ret < 0)
+ dev_err_probe(&pdev->dev, ret, "Failed to prepare clock\n");
+
+ ret = pwmchip_add(&chip->chip);
+ if (ret < 0)
+ dev_err_probe(&pdev->dev, ret, "Failed to add pwm chip\n");
+
+ platform_set_drvdata(pdev, chip);
+ return 0;
+}
+
+static int pwm_clk_remove(struct platform_device *pdev)
+{
+ struct pwm_clk_chip *chip = platform_get_drvdata(pdev);
+
+ pwmchip_remove(&chip->chip);
+
+ if (chip->clk_enabled)
+ clk_disable(chip->clk);
+
+ clk_unprepare(chip->clk);
+
+ return 0;
+}
+
+static const struct of_device_id pwm_clk_dt_ids[] = {
+ { .compatible = "clk-pwm", },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, pwm_clk_dt_ids);
+
+static struct platform_driver pwm_clk_driver = {
+ .driver = {
+ .name = "pwm-clk",
+ .of_match_table = pwm_clk_dt_ids,
+ },
+ .probe = pwm_clk_probe,
+ .remove = pwm_clk_remove,
+};
+module_platform_driver(pwm_clk_driver);
+
+MODULE_ALIAS("platform:pwm-clk");
+MODULE_AUTHOR("Nikita Travkin <[email protected]>");
+MODULE_DESCRIPTION("Clock based PWM driver");
+MODULE_LICENSE("GPL");
--
2.34.1

2022-01-26 21:16:50

by Nikita Travkin

[permalink] [raw]
Subject: [PATCH v4 1/2] dt-bindings: pwm: Document clk based PWM controller

Add YAML devicetree binding for clk based PWM controller

Signed-off-by: Nikita Travkin <[email protected]>
--
Changes in v2:
- fix the file name.
Changes in v4:
- Use generic node name in the dt bindings example.
---
.../devicetree/bindings/pwm/clk-pwm.yaml | 45 +++++++++++++++++++
1 file changed, 45 insertions(+)
create mode 100644 Documentation/devicetree/bindings/pwm/clk-pwm.yaml

diff --git a/Documentation/devicetree/bindings/pwm/clk-pwm.yaml b/Documentation/devicetree/bindings/pwm/clk-pwm.yaml
new file mode 100644
index 000000000000..d3416ba549b5
--- /dev/null
+++ b/Documentation/devicetree/bindings/pwm/clk-pwm.yaml
@@ -0,0 +1,45 @@
+# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/pwm/clk-pwm.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Clock based PWM controller
+
+maintainers:
+ - Nikita Travkin <[email protected]>
+
+description: |
+ Some systems have clocks that can be exposed to external devices.
+ (e.g. by muxing them to GPIO pins)
+ It's often possible to control duty-cycle of such clocks which makes them
+ suitable for generating PWM signal.
+
+allOf:
+ - $ref: pwm.yaml#
+
+properties:
+ compatible:
+ const: clk-pwm
+
+ clocks:
+ description: Clock used to generate the signal.
+ maxItems: 1
+
+ "#pwm-cells":
+ const: 2
+
+unevaluatedProperties: false
+
+required:
+ - clocks
+
+examples:
+ - |
+ pwm {
+ compatible = "clk-pwm";
+ #pwm-cells = <2>;
+ clocks = <&gcc 0>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&pwm_clk_flash_default>;
+ };
--
2.34.1

2022-01-26 21:18:38

by Krzysztof Kozlowski

[permalink] [raw]
Subject: Re: [PATCH v4 1/2] dt-bindings: pwm: Document clk based PWM controller

On 26/01/2022 13:58, Nikita Travkin wrote:
> Add YAML devicetree binding for clk based PWM controller
>
> Signed-off-by: Nikita Travkin <[email protected]>
> --
> Changes in v2:
> - fix the file name.
> Changes in v4:
> - Use generic node name in the dt bindings example.
> ---
> .../devicetree/bindings/pwm/clk-pwm.yaml | 45 +++++++++++++++++++
> 1 file changed, 45 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/pwm/clk-pwm.yaml
>
> diff --git a/Documentation/devicetree/bindings/pwm/clk-pwm.yaml b/Documentation/devicetree/bindings/pwm/clk-pwm.yaml
> new file mode 100644
> index 000000000000..d3416ba549b5
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/pwm/clk-pwm.yaml
> @@ -0,0 +1,45 @@
> +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
> +%YAML 1.2
> +---
> +$id: http://devicetree.org/schemas/pwm/clk-pwm.yaml#
> +$schema: http://devicetree.org/meta-schemas/core.yaml#
> +
> +title: Clock based PWM controller
> +
> +maintainers:
> + - Nikita Travkin <[email protected]>
> +
> +description: |
> + Some systems have clocks that can be exposed to external devices.
> + (e.g. by muxing them to GPIO pins)
> + It's often possible to control duty-cycle of such clocks which makes them
> + suitable for generating PWM signal.
> +
> +allOf:
> + - $ref: pwm.yaml#
> +
> +properties:
> + compatible:
> + const: clk-pwm
> +
> + clocks:
> + description: Clock used to generate the signal.
> + maxItems: 1
> +
> + "#pwm-cells":
> + const: 2
> +
> +unevaluatedProperties: false
> +
> +required:

You need a compatible. pwm-cells can be skipped as pwm.yaml will require
them.

> + - clocks
> +
> +examples:
> + - |
> + pwm {
> + compatible = "clk-pwm";
> + #pwm-cells = <2>;
> + clocks = <&gcc 0>;
> + pinctrl-names = "default";
> + pinctrl-0 = <&pwm_clk_flash_default>;
> + };


Best regards,
Krzysztof

2022-01-26 21:19:57

by Krzysztof Kozlowski

[permalink] [raw]
Subject: Re: [PATCH v4 2/2] pwm: Add clock based PWM output driver

On 26/01/2022 13:58, Nikita Travkin wrote:
> Some systems have clocks exposed to external devices. If the clock
> controller supports duty-cycle configuration, such clocks can be used as
> pwm outputs. In fact PWM and CLK subsystems are interfaced with in a
> similar way and an "opposite" driver already exists (clk-pwm). Add a
> driver that would enable pwm devices to be used via clk subsystem.
>
> Signed-off-by: Nikita Travkin <[email protected]>
> --
>
> Changes in v2:
> - Address Uwe's review comments:
> - Round set clk rate up
> - Add a description with limitations of the driver
> - Disable and unprepare clock before removing pwmchip
> Changes in v3:
> - Use 64bit version of div round up
> - Address Uwe's review comments:
> - Reword the limitations to avoid incorrect claims
> - Move the clk_enabled flag assignment
> - Drop unnecessary statements
> ---
> drivers/pwm/Kconfig | 10 +++
> drivers/pwm/Makefile | 1 +
> drivers/pwm/pwm-clk.c | 139 ++++++++++++++++++++++++++++++++++++++++++
> 3 files changed, 150 insertions(+)
> create mode 100644 drivers/pwm/pwm-clk.c
>
> diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig
> index 21e3b05a5153..daa2491a4054 100644
> --- a/drivers/pwm/Kconfig
> +++ b/drivers/pwm/Kconfig
> @@ -140,6 +140,16 @@ config PWM_BRCMSTB
> To compile this driver as a module, choose M Here: the module
> will be called pwm-brcmstb.c.
>
> +config PWM_CLK
> + tristate "Clock based PWM support"
> + depends on HAVE_CLK || COMPILE_TEST
> + help
> + Generic PWM framework driver for outputs that can be
> + muxed to clocks.
> +
> + To compile this driver as a module, choose M here: the module
> + will be called pwm-clk.
> +
> config PWM_CLPS711X
> tristate "CLPS711X PWM support"
> depends on ARCH_CLPS711X || COMPILE_TEST
> diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile
> index 708840b7fba8..4a860103c470 100644
> --- a/drivers/pwm/Makefile
> +++ b/drivers/pwm/Makefile
> @@ -10,6 +10,7 @@ obj-$(CONFIG_PWM_BCM_KONA) += pwm-bcm-kona.o
> obj-$(CONFIG_PWM_BCM2835) += pwm-bcm2835.o
> obj-$(CONFIG_PWM_BERLIN) += pwm-berlin.o
> obj-$(CONFIG_PWM_BRCMSTB) += pwm-brcmstb.o
> +obj-$(CONFIG_PWM_CLK) += pwm-clk.o
> obj-$(CONFIG_PWM_CLPS711X) += pwm-clps711x.o
> obj-$(CONFIG_PWM_CRC) += pwm-crc.o
> obj-$(CONFIG_PWM_CROS_EC) += pwm-cros-ec.o
> diff --git a/drivers/pwm/pwm-clk.c b/drivers/pwm/pwm-clk.c
> new file mode 100644
> index 000000000000..b3bfa12a0e73
> --- /dev/null
> +++ b/drivers/pwm/pwm-clk.c
> @@ -0,0 +1,139 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Clock based PWM controller
> + *
> + * Copyright (c) 2021 Nikita Travkin <[email protected]>
> + *
> + * This is an "adapter" driver that allows PWM consumers to use
> + * system clocks with duty cycle control as PWM outputs.
> + *
> + * Limitations:
> + * - Glitches are possible when new pwm state is applied.
> + * - Due to the fact that exact behavior depends on the underlying
> + * clock driver, various limitations are possible.
> + * - Period depends on the clock and, in general, not guaranteed.
> + * - Underlying clock may not be able to give 0% or 100% duty cycle
> + * (constant off or on), exact behavior will depend on the clock.
> + * - When the PWM is disabled, the clock will be disabled as well,
> + * line state will depend on the clock.
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/math64.h>
> +#include <linux/err.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/platform_device.h>
> +#include <linux/clk.h>
> +#include <linux/pwm.h>
> +
> +struct pwm_clk_chip {
> + struct pwm_chip chip;
> + struct clk *clk;
> + bool clk_enabled;
> +};
> +
> +#define to_pwm_clk_chip(_chip) container_of(_chip, struct pwm_clk_chip, chip)
> +
> +static int pwm_clk_apply(struct pwm_chip *pwm_chip, struct pwm_device *pwm,
> + const struct pwm_state *state)
> +{
> + struct pwm_clk_chip *chip = to_pwm_clk_chip(pwm_chip);
> + int ret;
> + u32 rate;
> + u64 period = state->period;
> + u64 duty_cycle = state->duty_cycle;
> +
> + if (!state->enabled) {
> + if (pwm->state.enabled) {
> + clk_disable(chip->clk);
> + chip->clk_enabled = false;
> + }
> + return 0;
> + } else if (!pwm->state.enabled) {
> + ret = clk_enable(chip->clk);
> + if (ret)
> + return ret;
> + chip->clk_enabled = true;
> + }
> +
> + rate = DIV64_U64_ROUND_UP(NSEC_PER_SEC, period);
> + ret = clk_set_rate(chip->clk, rate);
> + if (ret)
> + return ret;
> +
> + if (state->polarity == PWM_POLARITY_INVERSED)
> + duty_cycle = period - duty_cycle;
> +
> + return clk_set_duty_cycle(chip->clk, duty_cycle, period);
> +}
> +
> +static const struct pwm_ops pwm_clk_ops = {
> + .apply = pwm_clk_apply,
> + .owner = THIS_MODULE,
> +};
> +
> +static int pwm_clk_probe(struct platform_device *pdev)
> +{
> + struct pwm_clk_chip *chip;
> + int ret;
> +
> + chip = devm_kzalloc(&pdev->dev, sizeof(*chip), GFP_KERNEL);
> + if (!chip)
> + return -ENOMEM;
> +
> + chip->clk = devm_clk_get(&pdev->dev, NULL);
> + if (IS_ERR(chip->clk))
> + return dev_err_probe(&pdev->dev, PTR_ERR(chip->clk),
> + "Failed to get clock\n");
> +
> + chip->chip.dev = &pdev->dev;
> + chip->chip.ops = &pwm_clk_ops;
> + chip->chip.npwm = 1;
> +
> + ret = clk_prepare(chip->clk);
> + if (ret < 0)
> + dev_err_probe(&pdev->dev, ret, "Failed to prepare clock\n");
> +
> + ret = pwmchip_add(&chip->chip);
> + if (ret < 0)
> + dev_err_probe(&pdev->dev, ret, "Failed to add pwm chip\n");
> +

What is the point of probing the driver if pwmchip_add() fails? This
should be rather fatal error.

The same with clock. If preparing clock fails, there is little point in
enabling/disabling it later.

Best regards,
Krzysztof

2022-01-26 21:20:59

by Nikita Travkin

[permalink] [raw]
Subject: Re: [PATCH v4 1/2] dt-bindings: pwm: Document clk based PWM controller

Krzysztof Kozlowski писал(а) 26.01.2022 18:14:
> On 26/01/2022 13:58, Nikita Travkin wrote:
>> Add YAML devicetree binding for clk based PWM controller
>>
>> Signed-off-by: Nikita Travkin <[email protected]>
>> --
>> Changes in v2:
>> - fix the file name.
>> Changes in v4:
>> - Use generic node name in the dt bindings example.
>> ---
>> .../devicetree/bindings/pwm/clk-pwm.yaml | 45 +++++++++++++++++++
>> 1 file changed, 45 insertions(+)
>> create mode 100644 Documentation/devicetree/bindings/pwm/clk-pwm.yaml
>>
>> diff --git a/Documentation/devicetree/bindings/pwm/clk-pwm.yaml b/Documentation/devicetree/bindings/pwm/clk-pwm.yaml
>> new file mode 100644
>> index 000000000000..d3416ba549b5
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/pwm/clk-pwm.yaml
>> @@ -0,0 +1,45 @@
>> +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
>> +%YAML 1.2
>> +---
>> +$id: http://devicetree.org/schemas/pwm/clk-pwm.yaml#
>> +$schema: http://devicetree.org/meta-schemas/core.yaml#
>> +
>> +title: Clock based PWM controller
>> +
>> +maintainers:
>> + - Nikita Travkin <[email protected]>
>> +
>> +description: |
>> + Some systems have clocks that can be exposed to external devices.
>> + (e.g. by muxing them to GPIO pins)
>> + It's often possible to control duty-cycle of such clocks which makes them
>> + suitable for generating PWM signal.
>> +
>> +allOf:
>> + - $ref: pwm.yaml#
>> +
>> +properties:
>> + compatible:
>> + const: clk-pwm
>> +
>> + clocks:
>> + description: Clock used to generate the signal.
>> + maxItems: 1
>> +
>> + "#pwm-cells":
>> + const: 2
>> +
>> +unevaluatedProperties: false
>> +
>> +required:
>
> You need a compatible. pwm-cells can be skipped as pwm.yaml will require
> them.
>

Oops, thanks for noticing that, will add. (Though I'd assume compatible
to be implicitly required as the schema wouldn't even match otherwise...)

Nikita

>> + - clocks
>> +
>> +examples:
>> + - |
>> + pwm {
>> + compatible = "clk-pwm";
>> + #pwm-cells = <2>;
>> + clocks = <&gcc 0>;
>> + pinctrl-names = "default";
>> + pinctrl-0 = <&pwm_clk_flash_default>;
>> + };
>
>
> Best regards,
> Krzysztof

2022-01-26 21:22:35

by Nikita Travkin

[permalink] [raw]
Subject: Re: [PATCH v4 2/2] pwm: Add clock based PWM output driver

Krzysztof Kozlowski писал(а) 26.01.2022 18:18:
> On 26/01/2022 13:58, Nikita Travkin wrote:
>> Some systems have clocks exposed to external devices. If the clock
>> controller supports duty-cycle configuration, such clocks can be used as
>> pwm outputs. In fact PWM and CLK subsystems are interfaced with in a
>> similar way and an "opposite" driver already exists (clk-pwm). Add a
>> driver that would enable pwm devices to be used via clk subsystem.
>>
>> Signed-off-by: Nikita Travkin <[email protected]>
>> --
>>
>> Changes in v2:
>> - Address Uwe's review comments:
>> - Round set clk rate up
>> - Add a description with limitations of the driver
>> - Disable and unprepare clock before removing pwmchip
>> Changes in v3:
>> - Use 64bit version of div round up
>> - Address Uwe's review comments:
>> - Reword the limitations to avoid incorrect claims
>> - Move the clk_enabled flag assignment
>> - Drop unnecessary statements
>> ---
>> drivers/pwm/Kconfig | 10 +++
>> drivers/pwm/Makefile | 1 +
>> drivers/pwm/pwm-clk.c | 139 ++++++++++++++++++++++++++++++++++++++++++
>> 3 files changed, 150 insertions(+)
>> create mode 100644 drivers/pwm/pwm-clk.c
>>
>> diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig
>> index 21e3b05a5153..daa2491a4054 100644
>> --- a/drivers/pwm/Kconfig
>> +++ b/drivers/pwm/Kconfig
>> @@ -140,6 +140,16 @@ config PWM_BRCMSTB
>> To compile this driver as a module, choose M Here: the module
>> will be called pwm-brcmstb.c.
>>
>> +config PWM_CLK
>> + tristate "Clock based PWM support"
>> + depends on HAVE_CLK || COMPILE_TEST
>> + help
>> + Generic PWM framework driver for outputs that can be
>> + muxed to clocks.
>> +
>> + To compile this driver as a module, choose M here: the module
>> + will be called pwm-clk.
>> +
>> config PWM_CLPS711X
>> tristate "CLPS711X PWM support"
>> depends on ARCH_CLPS711X || COMPILE_TEST
>> diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile
>> index 708840b7fba8..4a860103c470 100644
>> --- a/drivers/pwm/Makefile
>> +++ b/drivers/pwm/Makefile
>> @@ -10,6 +10,7 @@ obj-$(CONFIG_PWM_BCM_KONA) += pwm-bcm-kona.o
>> obj-$(CONFIG_PWM_BCM2835) += pwm-bcm2835.o
>> obj-$(CONFIG_PWM_BERLIN) += pwm-berlin.o
>> obj-$(CONFIG_PWM_BRCMSTB) += pwm-brcmstb.o
>> +obj-$(CONFIG_PWM_CLK) += pwm-clk.o
>> obj-$(CONFIG_PWM_CLPS711X) += pwm-clps711x.o
>> obj-$(CONFIG_PWM_CRC) += pwm-crc.o
>> obj-$(CONFIG_PWM_CROS_EC) += pwm-cros-ec.o
>> diff --git a/drivers/pwm/pwm-clk.c b/drivers/pwm/pwm-clk.c
>> new file mode 100644
>> index 000000000000..b3bfa12a0e73
>> --- /dev/null
>> +++ b/drivers/pwm/pwm-clk.c
>> @@ -0,0 +1,139 @@
>> +// SPDX-License-Identifier: GPL-2.0
>> +/*
>> + * Clock based PWM controller
>> + *
>> + * Copyright (c) 2021 Nikita Travkin <[email protected]>
>> + *
>> + * This is an "adapter" driver that allows PWM consumers to use
>> + * system clocks with duty cycle control as PWM outputs.
>> + *
>> + * Limitations:
>> + * - Glitches are possible when new pwm state is applied.
>> + * - Due to the fact that exact behavior depends on the underlying
>> + * clock driver, various limitations are possible.
>> + * - Period depends on the clock and, in general, not guaranteed.
>> + * - Underlying clock may not be able to give 0% or 100% duty cycle
>> + * (constant off or on), exact behavior will depend on the clock.
>> + * - When the PWM is disabled, the clock will be disabled as well,
>> + * line state will depend on the clock.
>> + */
>> +
>> +#include <linux/kernel.h>
>> +#include <linux/math64.h>
>> +#include <linux/err.h>
>> +#include <linux/module.h>
>> +#include <linux/of.h>
>> +#include <linux/platform_device.h>
>> +#include <linux/clk.h>
>> +#include <linux/pwm.h>
>> +
>> +struct pwm_clk_chip {
>> + struct pwm_chip chip;
>> + struct clk *clk;
>> + bool clk_enabled;
>> +};
>> +
>> +#define to_pwm_clk_chip(_chip) container_of(_chip, struct pwm_clk_chip, chip)
>> +
>> +static int pwm_clk_apply(struct pwm_chip *pwm_chip, struct pwm_device *pwm,
>> + const struct pwm_state *state)
>> +{
>> + struct pwm_clk_chip *chip = to_pwm_clk_chip(pwm_chip);
>> + int ret;
>> + u32 rate;
>> + u64 period = state->period;
>> + u64 duty_cycle = state->duty_cycle;
>> +
>> + if (!state->enabled) {
>> + if (pwm->state.enabled) {
>> + clk_disable(chip->clk);
>> + chip->clk_enabled = false;
>> + }
>> + return 0;
>> + } else if (!pwm->state.enabled) {
>> + ret = clk_enable(chip->clk);
>> + if (ret)
>> + return ret;
>> + chip->clk_enabled = true;
>> + }
>> +
>> + rate = DIV64_U64_ROUND_UP(NSEC_PER_SEC, period);
>> + ret = clk_set_rate(chip->clk, rate);
>> + if (ret)
>> + return ret;
>> +
>> + if (state->polarity == PWM_POLARITY_INVERSED)
>> + duty_cycle = period - duty_cycle;
>> +
>> + return clk_set_duty_cycle(chip->clk, duty_cycle, period);
>> +}
>> +
>> +static const struct pwm_ops pwm_clk_ops = {
>> + .apply = pwm_clk_apply,
>> + .owner = THIS_MODULE,
>> +};
>> +
>> +static int pwm_clk_probe(struct platform_device *pdev)
>> +{
>> + struct pwm_clk_chip *chip;
>> + int ret;
>> +
>> + chip = devm_kzalloc(&pdev->dev, sizeof(*chip), GFP_KERNEL);
>> + if (!chip)
>> + return -ENOMEM;
>> +
>> + chip->clk = devm_clk_get(&pdev->dev, NULL);
>> + if (IS_ERR(chip->clk))
>> + return dev_err_probe(&pdev->dev, PTR_ERR(chip->clk),
>> + "Failed to get clock\n");
>> +
>> + chip->chip.dev = &pdev->dev;
>> + chip->chip.ops = &pwm_clk_ops;
>> + chip->chip.npwm = 1;
>> +
>> + ret = clk_prepare(chip->clk);
>> + if (ret < 0)
>> + dev_err_probe(&pdev->dev, ret, "Failed to prepare clock\n");
>> +
>> + ret = pwmchip_add(&chip->chip);
>> + if (ret < 0)
>> + dev_err_probe(&pdev->dev, ret, "Failed to add pwm chip\n");
>> +
>
> What is the point of probing the driver if pwmchip_add() fails? This
> should be rather fatal error.
>
> The same with clock. If preparing clock fails, there is little point in
> enabling/disabling it later.
>

Uh oh... Seems like I forgot a return in both cases... For some reason
I had an incorrect assumption in my mind that dev_err_probe is a macro
that does the return on it's own, yet I used it correctly just a couple
of lines earlier...

Thanks for pointing this out! Will fix those in v5.

Nikita

> Best regards,
> Krzysztof

2022-01-26 22:05:04

by Uwe Kleine-König

[permalink] [raw]
Subject: Re: [PATCH v4 2/2] pwm: Add clock based PWM output driver

On Wed, Jan 26, 2022 at 06:35:15PM +0500, Nikita Travkin wrote:
> Krzysztof Kozlowski писал(а) 26.01.2022 18:18:
> > On 26/01/2022 13:58, Nikita Travkin wrote:
> >> Some systems have clocks exposed to external devices. If the clock
> >> controller supports duty-cycle configuration, such clocks can be used as
> >> pwm outputs. In fact PWM and CLK subsystems are interfaced with in a
> >> similar way and an "opposite" driver already exists (clk-pwm). Add a
> >> driver that would enable pwm devices to be used via clk subsystem.
> >>
> >> Signed-off-by: Nikita Travkin <[email protected]>
> >> --
> >>
> >> Changes in v2:
> >> - Address Uwe's review comments:
> >> - Round set clk rate up
> >> - Add a description with limitations of the driver
> >> - Disable and unprepare clock before removing pwmchip
> >> Changes in v3:
> >> - Use 64bit version of div round up
> >> - Address Uwe's review comments:
> >> - Reword the limitations to avoid incorrect claims
> >> - Move the clk_enabled flag assignment
> >> - Drop unnecessary statements
> >> ---
> >> drivers/pwm/Kconfig | 10 +++
> >> drivers/pwm/Makefile | 1 +
> >> drivers/pwm/pwm-clk.c | 139 ++++++++++++++++++++++++++++++++++++++++++
> >> 3 files changed, 150 insertions(+)
> >> create mode 100644 drivers/pwm/pwm-clk.c
> >>
> >> diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig
> >> index 21e3b05a5153..daa2491a4054 100644
> >> --- a/drivers/pwm/Kconfig
> >> +++ b/drivers/pwm/Kconfig
> >> @@ -140,6 +140,16 @@ config PWM_BRCMSTB
> >> To compile this driver as a module, choose M Here: the module
> >> will be called pwm-brcmstb.c.
> >>
> >> +config PWM_CLK
> >> + tristate "Clock based PWM support"
> >> + depends on HAVE_CLK || COMPILE_TEST
> >> + help
> >> + Generic PWM framework driver for outputs that can be
> >> + muxed to clocks.
> >> +
> >> + To compile this driver as a module, choose M here: the module
> >> + will be called pwm-clk.
> >> +
> >> config PWM_CLPS711X
> >> tristate "CLPS711X PWM support"
> >> depends on ARCH_CLPS711X || COMPILE_TEST
> >> diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile
> >> index 708840b7fba8..4a860103c470 100644
> >> --- a/drivers/pwm/Makefile
> >> +++ b/drivers/pwm/Makefile
> >> @@ -10,6 +10,7 @@ obj-$(CONFIG_PWM_BCM_KONA) += pwm-bcm-kona.o
> >> obj-$(CONFIG_PWM_BCM2835) += pwm-bcm2835.o
> >> obj-$(CONFIG_PWM_BERLIN) += pwm-berlin.o
> >> obj-$(CONFIG_PWM_BRCMSTB) += pwm-brcmstb.o
> >> +obj-$(CONFIG_PWM_CLK) += pwm-clk.o
> >> obj-$(CONFIG_PWM_CLPS711X) += pwm-clps711x.o
> >> obj-$(CONFIG_PWM_CRC) += pwm-crc.o
> >> obj-$(CONFIG_PWM_CROS_EC) += pwm-cros-ec.o
> >> diff --git a/drivers/pwm/pwm-clk.c b/drivers/pwm/pwm-clk.c
> >> new file mode 100644
> >> index 000000000000..b3bfa12a0e73
> >> --- /dev/null
> >> +++ b/drivers/pwm/pwm-clk.c
> >> @@ -0,0 +1,139 @@
> >> +// SPDX-License-Identifier: GPL-2.0
> >> +/*
> >> + * Clock based PWM controller
> >> + *
> >> + * Copyright (c) 2021 Nikita Travkin <[email protected]>
> >> + *
> >> + * This is an "adapter" driver that allows PWM consumers to use
> >> + * system clocks with duty cycle control as PWM outputs.
> >> + *
> >> + * Limitations:
> >> + * - Glitches are possible when new pwm state is applied.
> >> + * - Due to the fact that exact behavior depends on the underlying
> >> + * clock driver, various limitations are possible.
> >> + * - Period depends on the clock and, in general, not guaranteed.
> >> + * - Underlying clock may not be able to give 0% or 100% duty cycle
> >> + * (constant off or on), exact behavior will depend on the clock.
> >> + * - When the PWM is disabled, the clock will be disabled as well,
> >> + * line state will depend on the clock.
> >> + */
> >> +
> >> +#include <linux/kernel.h>
> >> +#include <linux/math64.h>
> >> +#include <linux/err.h>
> >> +#include <linux/module.h>
> >> +#include <linux/of.h>
> >> +#include <linux/platform_device.h>
> >> +#include <linux/clk.h>
> >> +#include <linux/pwm.h>
> >> +
> >> +struct pwm_clk_chip {
> >> + struct pwm_chip chip;
> >> + struct clk *clk;
> >> + bool clk_enabled;
> >> +};
> >> +
> >> +#define to_pwm_clk_chip(_chip) container_of(_chip, struct pwm_clk_chip, chip)
> >> +
> >> +static int pwm_clk_apply(struct pwm_chip *pwm_chip, struct pwm_device *pwm,
> >> + const struct pwm_state *state)
> >> +{
> >> + struct pwm_clk_chip *chip = to_pwm_clk_chip(pwm_chip);
> >> + int ret;
> >> + u32 rate;
> >> + u64 period = state->period;
> >> + u64 duty_cycle = state->duty_cycle;
> >> +
> >> + if (!state->enabled) {
> >> + if (pwm->state.enabled) {
> >> + clk_disable(chip->clk);
> >> + chip->clk_enabled = false;
> >> + }
> >> + return 0;
> >> + } else if (!pwm->state.enabled) {
> >> + ret = clk_enable(chip->clk);
> >> + if (ret)
> >> + return ret;
> >> + chip->clk_enabled = true;
> >> + }
> >> +
> >> + rate = DIV64_U64_ROUND_UP(NSEC_PER_SEC, period);
> >> + ret = clk_set_rate(chip->clk, rate);
> >> + if (ret)
> >> + return ret;
> >> +
> >> + if (state->polarity == PWM_POLARITY_INVERSED)
> >> + duty_cycle = period - duty_cycle;
> >> +
> >> + return clk_set_duty_cycle(chip->clk, duty_cycle, period);
> >> +}
> >> +
> >> +static const struct pwm_ops pwm_clk_ops = {
> >> + .apply = pwm_clk_apply,
> >> + .owner = THIS_MODULE,
> >> +};
> >> +
> >> +static int pwm_clk_probe(struct platform_device *pdev)
> >> +{
> >> + struct pwm_clk_chip *chip;
> >> + int ret;
> >> +
> >> + chip = devm_kzalloc(&pdev->dev, sizeof(*chip), GFP_KERNEL);
> >> + if (!chip)
> >> + return -ENOMEM;
> >> +
> >> + chip->clk = devm_clk_get(&pdev->dev, NULL);
> >> + if (IS_ERR(chip->clk))
> >> + return dev_err_probe(&pdev->dev, PTR_ERR(chip->clk),
> >> + "Failed to get clock\n");
> >> +
> >> + chip->chip.dev = &pdev->dev;
> >> + chip->chip.ops = &pwm_clk_ops;
> >> + chip->chip.npwm = 1;
> >> +
> >> + ret = clk_prepare(chip->clk);
> >> + if (ret < 0)
> >> + dev_err_probe(&pdev->dev, ret, "Failed to prepare clock\n");
> >> +
> >> + ret = pwmchip_add(&chip->chip);
> >> + if (ret < 0)
> >> + dev_err_probe(&pdev->dev, ret, "Failed to add pwm chip\n");
> >> +
> >
> > What is the point of probing the driver if pwmchip_add() fails? This
> > should be rather fatal error.
> >
> > The same with clock. If preparing clock fails, there is little point in
> > enabling/disabling it later.
> >
>
> Uh oh... Seems like I forgot a return in both cases... For some reason
> I had an incorrect assumption in my mind that dev_err_probe is a macro
> that does the return on it's own, yet I used it correctly just a couple
> of lines earlier...

dev_err_probe returns the error code, so

return dev_err_probe(&pdev->dev, ret, "Failed to add pwm chip\n");

is the right magic.

Best regards
Uwe

--
Pengutronix e.K. | Uwe Kleine-König |
Industrial Linux Solutions | https://www.pengutronix.de/ |


Attachments:
(No filename) (6.91 kB)
signature.asc (499.00 B)
Download all attachments