Skip to content

Commit 530c28d

Browse files
committed
Merge tag 'pwm/for-5.20-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/thierry.reding/linux-pwm
Pull pwm updates from Thierry Reding: "After v5.19 had all drivers converted to the new atomic API and nobody has reported any breakage, this set of changes starts by dropping the legacy support. Some existing drivers get improvements and broader chip support and a new driver is added that emulates a PWM controller using a clock output. Other than that there's the usual bits of cleanups and minor fixes" * tag 'pwm/for-5.20-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/thierry.reding/linux-pwm: (21 commits) pwm: lpc18xx: Fix period handling pwm: lpc18xx: Convert to use dev_err_probe() pwm: twl-led: Document some limitations and link to the reference manual MAINTAINERS: Remove myself as PWM maintainer MAINTAINERS: Add include/dt-bindings/pwm to PWM SUBSYSTEM dt-bindings: pwm: mediatek: Add compatible string for MT8195 pwm: Add clock based PWM output driver dt-bindings: pwm: Document clk based PWM controller pwm: sifive: Shut down hardware only after pwmchip_remove() completed pwm: sifive: Ensure the clk is enabled exactly once per running PWM pwm: sifive: Simplify clk handling pwm: sifive: Enable clk only after period check in .apply() pwm: sifive: Reduce time the controller lock is held pwm: sifive: Fold pwm_sifive_enable() into its only caller pwm: sifive: Simplify offset calculation for PWMCMP registers pwm: mediatek: Add MT8365 support dt-bindings: pwm: Add MT8365 SoC binding pwm: Drop unused forward declaration from pwm.h pwm: Reorder header file to get rid of struct pwm_capture forward declaration pwm: atmel-tcb: Fix typo in comment ...
2 parents 0805c6f + 8933d30 commit 530c28d

File tree

13 files changed

+349
-187
lines changed

13 files changed

+349
-187
lines changed
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
2+
%YAML 1.2
3+
---
4+
$id: http://devicetree.org/schemas/pwm/clk-pwm.yaml#
5+
$schema: http://devicetree.org/meta-schemas/core.yaml#
6+
7+
title: Clock based PWM controller
8+
9+
maintainers:
10+
- Nikita Travkin <[email protected]>
11+
12+
description: |
13+
Some systems have clocks that can be exposed to external devices.
14+
(e.g. by muxing them to GPIO pins)
15+
It's often possible to control duty-cycle of such clocks which makes them
16+
suitable for generating PWM signal.
17+
18+
allOf:
19+
- $ref: pwm.yaml#
20+
21+
properties:
22+
compatible:
23+
const: clk-pwm
24+
25+
clocks:
26+
description: Clock used to generate the signal.
27+
maxItems: 1
28+
29+
"#pwm-cells":
30+
const: 2
31+
32+
unevaluatedProperties: false
33+
34+
required:
35+
- compatible
36+
- clocks
37+
38+
examples:
39+
- |
40+
pwm {
41+
compatible = "clk-pwm";
42+
#pwm-cells = <2>;
43+
clocks = <&gcc 0>;
44+
pinctrl-names = "default";
45+
pinctrl-0 = <&pwm_clk_flash_default>;
46+
};

Documentation/devicetree/bindings/pwm/pwm-mediatek.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ Required properties:
99
- "mediatek,mt7628-pwm": found on mt7628 SoC.
1010
- "mediatek,mt7629-pwm": found on mt7629 SoC.
1111
- "mediatek,mt8183-pwm": found on mt8183 SoC.
12+
- "mediatek,mt8195-pwm", "mediatek,mt8183-pwm": found on mt8195 SoC.
13+
- "mediatek,mt8365-pwm": found on mt8365 SoC.
1214
- "mediatek,mt8516-pwm": found on mt8516 SoC.
1315
- reg: physical base address and length of the controller's registers.
1416
- #pwm-cells: must be 2. See pwm.yaml in this directory for a description of
@@ -18,6 +20,7 @@ Required properties:
1820
has no clocks
1921
- "top": the top clock generator
2022
- "main": clock used by the PWM core
23+
- "pwm1-3": the three per PWM clocks for mt8365
2124
- "pwm1-8": the eight per PWM clocks for mt2712
2225
- "pwm1-6": the six per PWM clocks for mt7622
2326
- "pwm1-5": the five per PWM clocks for mt7623

MAINTAINERS

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16369,7 +16369,6 @@ F: drivers/media/rc/pwm-ir-tx.c
1636916369
PWM SUBSYSTEM
1637016370
M: Thierry Reding <[email protected]>
1637116371
R: Uwe Kleine-König <[email protected]>
16372-
M: Lee Jones <[email protected]>
1637316372
1637416373
S: Maintained
1637516374
Q: https://patchwork.ozlabs.org/project/linux-pwm/list/
@@ -16380,6 +16379,7 @@ F: Documentation/driver-api/pwm.rst
1638016379
F: drivers/gpio/gpio-mvebu.c
1638116380
F: drivers/pwm/
1638216381
F: drivers/video/backlight/pwm_bl.c
16382+
F: include/dt-bindings/pwm/
1638316383
F: include/linux/pwm.h
1638416384
F: include/linux/pwm_backlight.h
1638516385
K: pwm_(config|apply_state|ops)

drivers/pwm/Kconfig

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,16 @@ config PWM_BRCMSTB
140140
To compile this driver as a module, choose M Here: the module
141141
will be called pwm-brcmstb.c.
142142

143+
config PWM_CLK
144+
tristate "Clock based PWM support"
145+
depends on HAVE_CLK || COMPILE_TEST
146+
help
147+
Generic PWM framework driver for outputs that can be
148+
muxed to clocks.
149+
150+
To compile this driver as a module, choose M here: the module
151+
will be called pwm-clk.
152+
143153
config PWM_CLPS711X
144154
tristate "CLPS711X PWM support"
145155
depends on ARCH_CLPS711X || COMPILE_TEST

drivers/pwm/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ obj-$(CONFIG_PWM_BCM_KONA) += pwm-bcm-kona.o
1010
obj-$(CONFIG_PWM_BCM2835) += pwm-bcm2835.o
1111
obj-$(CONFIG_PWM_BERLIN) += pwm-berlin.o
1212
obj-$(CONFIG_PWM_BRCMSTB) += pwm-brcmstb.o
13+
obj-$(CONFIG_PWM_CLK) += pwm-clk.o
1314
obj-$(CONFIG_PWM_CLPS711X) += pwm-clps711x.o
1415
obj-$(CONFIG_PWM_CRC) += pwm-crc.o
1516
obj-$(CONFIG_PWM_CROS_EC) += pwm-cros-ec.o

drivers/pwm/core.c

Lines changed: 1 addition & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -235,18 +235,8 @@ EXPORT_SYMBOL_GPL(pwm_get_chip_data);
235235

236236
static bool pwm_ops_check(const struct pwm_chip *chip)
237237
{
238-
239238
const struct pwm_ops *ops = chip->ops;
240239

241-
/* driver supports legacy, non-atomic operation */
242-
if (ops->config && ops->enable && ops->disable) {
243-
if (IS_ENABLED(CONFIG_PWM_DEBUG))
244-
dev_warn(chip->dev,
245-
"Driver needs updating to atomic API\n");
246-
247-
return true;
248-
}
249-
250240
if (!ops->apply)
251241
return false;
252242

@@ -548,73 +538,6 @@ static void pwm_apply_state_debug(struct pwm_device *pwm,
548538
}
549539
}
550540

551-
static int pwm_apply_legacy(struct pwm_chip *chip, struct pwm_device *pwm,
552-
const struct pwm_state *state)
553-
{
554-
int err;
555-
struct pwm_state initial_state = pwm->state;
556-
557-
if (state->polarity != pwm->state.polarity) {
558-
if (!chip->ops->set_polarity)
559-
return -EINVAL;
560-
561-
/*
562-
* Changing the polarity of a running PWM is only allowed when
563-
* the PWM driver implements ->apply().
564-
*/
565-
if (pwm->state.enabled) {
566-
chip->ops->disable(chip, pwm);
567-
568-
/*
569-
* Update pwm->state already here in case
570-
* .set_polarity() or another callback depend on that.
571-
*/
572-
pwm->state.enabled = false;
573-
}
574-
575-
err = chip->ops->set_polarity(chip, pwm, state->polarity);
576-
if (err)
577-
goto rollback;
578-
579-
pwm->state.polarity = state->polarity;
580-
}
581-
582-
if (!state->enabled) {
583-
if (pwm->state.enabled)
584-
chip->ops->disable(chip, pwm);
585-
586-
return 0;
587-
}
588-
589-
/*
590-
* We cannot skip calling ->config even if state->period ==
591-
* pwm->state.period && state->duty_cycle == pwm->state.duty_cycle
592-
* because we might have exited early in the last call to
593-
* pwm_apply_state because of !state->enabled and so the two values in
594-
* pwm->state might not be configured in hardware.
595-
*/
596-
err = chip->ops->config(pwm->chip, pwm,
597-
state->duty_cycle,
598-
state->period);
599-
if (err)
600-
goto rollback;
601-
602-
pwm->state.period = state->period;
603-
pwm->state.duty_cycle = state->duty_cycle;
604-
605-
if (!pwm->state.enabled) {
606-
err = chip->ops->enable(chip, pwm);
607-
if (err)
608-
goto rollback;
609-
}
610-
611-
return 0;
612-
613-
rollback:
614-
pwm->state = initial_state;
615-
return err;
616-
}
617-
618541
/**
619542
* pwm_apply_state() - atomically apply a new state to a PWM device
620543
* @pwm: PWM device
@@ -647,10 +570,7 @@ int pwm_apply_state(struct pwm_device *pwm, const struct pwm_state *state)
647570
state->usage_power == pwm->state.usage_power)
648571
return 0;
649572

650-
if (chip->ops->apply)
651-
err = chip->ops->apply(chip, pwm, state);
652-
else
653-
err = pwm_apply_legacy(chip, pwm, state);
573+
err = chip->ops->apply(chip, pwm, state);
654574
if (err)
655575
return err;
656576

drivers/pwm/pwm-atmel-tcb.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -304,7 +304,7 @@ static int atmel_tcb_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
304304
/*
305305
* Find best clk divisor:
306306
* the smallest divisor which can fulfill the period_ns requirements.
307-
* If there is a gclk, the first divisor is actuallly the gclk selector
307+
* If there is a gclk, the first divisor is actually the gclk selector
308308
*/
309309
if (tcbpwmc->gclk)
310310
i = 1;

drivers/pwm/pwm-clk.c

Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
/*
3+
* Clock based PWM controller
4+
*
5+
* Copyright (c) 2021 Nikita Travkin <[email protected]>
6+
*
7+
* This is an "adapter" driver that allows PWM consumers to use
8+
* system clocks with duty cycle control as PWM outputs.
9+
*
10+
* Limitations:
11+
* - Due to the fact that exact behavior depends on the underlying
12+
* clock driver, various limitations are possible.
13+
* - Underlying clock may not be able to give 0% or 100% duty cycle
14+
* (constant off or on), exact behavior will depend on the clock.
15+
* - When the PWM is disabled, the clock will be disabled as well,
16+
* line state will depend on the clock.
17+
* - The clk API doesn't expose the necessary calls to implement
18+
* .get_state().
19+
*/
20+
21+
#include <linux/kernel.h>
22+
#include <linux/math64.h>
23+
#include <linux/err.h>
24+
#include <linux/module.h>
25+
#include <linux/of.h>
26+
#include <linux/platform_device.h>
27+
#include <linux/clk.h>
28+
#include <linux/pwm.h>
29+
30+
struct pwm_clk_chip {
31+
struct pwm_chip chip;
32+
struct clk *clk;
33+
bool clk_enabled;
34+
};
35+
36+
#define to_pwm_clk_chip(_chip) container_of(_chip, struct pwm_clk_chip, chip)
37+
38+
static int pwm_clk_apply(struct pwm_chip *chip, struct pwm_device *pwm,
39+
const struct pwm_state *state)
40+
{
41+
struct pwm_clk_chip *pcchip = to_pwm_clk_chip(chip);
42+
int ret;
43+
u32 rate;
44+
u64 period = state->period;
45+
u64 duty_cycle = state->duty_cycle;
46+
47+
if (!state->enabled) {
48+
if (pwm->state.enabled) {
49+
clk_disable(pcchip->clk);
50+
pcchip->clk_enabled = false;
51+
}
52+
return 0;
53+
} else if (!pwm->state.enabled) {
54+
ret = clk_enable(pcchip->clk);
55+
if (ret)
56+
return ret;
57+
pcchip->clk_enabled = true;
58+
}
59+
60+
/*
61+
* We have to enable the clk before setting the rate and duty_cycle,
62+
* that however results in a window where the clk is on with a
63+
* (potentially) different setting. Also setting period and duty_cycle
64+
* are two separate calls, so that probably isn't atomic either.
65+
*/
66+
67+
rate = DIV64_U64_ROUND_UP(NSEC_PER_SEC, period);
68+
ret = clk_set_rate(pcchip->clk, rate);
69+
if (ret)
70+
return ret;
71+
72+
if (state->polarity == PWM_POLARITY_INVERSED)
73+
duty_cycle = period - duty_cycle;
74+
75+
return clk_set_duty_cycle(pcchip->clk, duty_cycle, period);
76+
}
77+
78+
static const struct pwm_ops pwm_clk_ops = {
79+
.apply = pwm_clk_apply,
80+
.owner = THIS_MODULE,
81+
};
82+
83+
static int pwm_clk_probe(struct platform_device *pdev)
84+
{
85+
struct pwm_clk_chip *pcchip;
86+
int ret;
87+
88+
pcchip = devm_kzalloc(&pdev->dev, sizeof(*pcchip), GFP_KERNEL);
89+
if (!pcchip)
90+
return -ENOMEM;
91+
92+
pcchip->clk = devm_clk_get(&pdev->dev, NULL);
93+
if (IS_ERR(pcchip->clk))
94+
return dev_err_probe(&pdev->dev, PTR_ERR(pcchip->clk),
95+
"Failed to get clock\n");
96+
97+
pcchip->chip.dev = &pdev->dev;
98+
pcchip->chip.ops = &pwm_clk_ops;
99+
pcchip->chip.npwm = 1;
100+
101+
ret = clk_prepare(pcchip->clk);
102+
if (ret < 0)
103+
return dev_err_probe(&pdev->dev, ret, "Failed to prepare clock\n");
104+
105+
ret = pwmchip_add(&pcchip->chip);
106+
if (ret < 0) {
107+
clk_unprepare(pcchip->clk);
108+
return dev_err_probe(&pdev->dev, ret, "Failed to add pwm chip\n");
109+
}
110+
111+
platform_set_drvdata(pdev, pcchip);
112+
return 0;
113+
}
114+
115+
static int pwm_clk_remove(struct platform_device *pdev)
116+
{
117+
struct pwm_clk_chip *pcchip = platform_get_drvdata(pdev);
118+
119+
pwmchip_remove(&pcchip->chip);
120+
121+
if (pcchip->clk_enabled)
122+
clk_disable(pcchip->clk);
123+
124+
clk_unprepare(pcchip->clk);
125+
126+
return 0;
127+
}
128+
129+
static const struct of_device_id pwm_clk_dt_ids[] = {
130+
{ .compatible = "clk-pwm", },
131+
{ /* sentinel */ }
132+
};
133+
MODULE_DEVICE_TABLE(of, pwm_clk_dt_ids);
134+
135+
static struct platform_driver pwm_clk_driver = {
136+
.driver = {
137+
.name = "pwm-clk",
138+
.of_match_table = pwm_clk_dt_ids,
139+
},
140+
.probe = pwm_clk_probe,
141+
.remove = pwm_clk_remove,
142+
};
143+
module_platform_driver(pwm_clk_driver);
144+
145+
MODULE_ALIAS("platform:pwm-clk");
146+
MODULE_AUTHOR("Nikita Travkin <[email protected]>");
147+
MODULE_DESCRIPTION("Clock based PWM driver");
148+
MODULE_LICENSE("GPL");

0 commit comments

Comments
 (0)