1 // SPDX-License-Identifier: GPL-2.0
3 * PWM device driver for SUNPLUS SP7021 SoC
7 * https://sunplus-tibbo.atlassian.net/wiki/spaces/doc/overview
9 * Reference Manual(PWM module):
10 * https://sunplus.atlassian.net/wiki/spaces/doc/pages/461144198/12.+Pulse+Width+Modulation+PWM
13 * - Only supports normal polarity.
14 * - It output low when PWM channel disabled.
15 * - When the parameters change, current running period will not be completed
16 * and run new settings immediately.
17 * - In .apply() PWM output need to write register FREQ and DUTY. When first write FREQ
18 * done and not yet write DUTY, it has short timing gap use new FREQ and old DUTY.
20 * Author: Hammer Hsieh <hammerh0314@gmail.com>
22 #include <linux/bitfield.h>
23 #include <linux/clk.h>
25 #include <linux/kernel.h>
26 #include <linux/mod_devicetable.h>
27 #include <linux/module.h>
28 #include <linux/platform_device.h>
29 #include <linux/pwm.h>
31 #define SP7021_PWM_MODE0 0x000
32 #define SP7021_PWM_MODE0_PWMEN(ch) BIT(ch)
33 #define SP7021_PWM_MODE0_BYPASS(ch) BIT(8 + (ch))
34 #define SP7021_PWM_MODE1 0x004
35 #define SP7021_PWM_MODE1_CNT_EN(ch) BIT(ch)
36 #define SP7021_PWM_FREQ(ch) (0x008 + 4 * (ch))
37 #define SP7021_PWM_FREQ_MAX GENMASK(15, 0)
38 #define SP7021_PWM_DUTY(ch) (0x018 + 4 * (ch))
39 #define SP7021_PWM_DUTY_DD_SEL(ch) FIELD_PREP(GENMASK(9, 8), ch)
40 #define SP7021_PWM_DUTY_MAX GENMASK(7, 0)
41 #define SP7021_PWM_DUTY_MASK SP7021_PWM_DUTY_MAX
42 #define SP7021_PWM_FREQ_SCALER 256
43 #define SP7021_PWM_NUM 4
50 static inline struct sunplus_pwm
*to_sunplus_pwm(struct pwm_chip
*chip
)
52 return pwmchip_get_drvdata(chip
);
55 static int sunplus_pwm_apply(struct pwm_chip
*chip
, struct pwm_device
*pwm
,
56 const struct pwm_state
*state
)
58 struct sunplus_pwm
*priv
= to_sunplus_pwm(chip
);
59 u32 dd_freq
, duty
, mode0
, mode1
;
62 if (state
->polarity
!= pwm
->state
.polarity
)
65 if (!state
->enabled
) {
66 /* disable pwm channel output */
67 mode0
= readl(priv
->base
+ SP7021_PWM_MODE0
);
68 mode0
&= ~SP7021_PWM_MODE0_PWMEN(pwm
->hwpwm
);
69 writel(mode0
, priv
->base
+ SP7021_PWM_MODE0
);
70 /* disable pwm channel clk source */
71 mode1
= readl(priv
->base
+ SP7021_PWM_MODE1
);
72 mode1
&= ~SP7021_PWM_MODE1_CNT_EN(pwm
->hwpwm
);
73 writel(mode1
, priv
->base
+ SP7021_PWM_MODE1
);
77 clk_rate
= clk_get_rate(priv
->clk
);
80 * The following calculations might overflow if clk is bigger
81 * than 256 GHz. In practise it's 202.5MHz, so this limitation
84 if (clk_rate
> (u64
)SP7021_PWM_FREQ_SCALER
* NSEC_PER_SEC
)
88 * With clk_rate limited above we have dd_freq <= state->period,
89 * so this cannot overflow.
91 dd_freq
= mul_u64_u64_div_u64(clk_rate
, state
->period
, (u64
)SP7021_PWM_FREQ_SCALER
97 if (dd_freq
> SP7021_PWM_FREQ_MAX
)
98 dd_freq
= SP7021_PWM_FREQ_MAX
;
100 writel(dd_freq
, priv
->base
+ SP7021_PWM_FREQ(pwm
->hwpwm
));
102 /* cal and set pwm duty */
103 mode0
= readl(priv
->base
+ SP7021_PWM_MODE0
);
104 mode0
|= SP7021_PWM_MODE0_PWMEN(pwm
->hwpwm
);
105 mode1
= readl(priv
->base
+ SP7021_PWM_MODE1
);
106 mode1
|= SP7021_PWM_MODE1_CNT_EN(pwm
->hwpwm
);
107 if (state
->duty_cycle
== state
->period
) {
108 /* PWM channel output = high */
109 mode0
|= SP7021_PWM_MODE0_BYPASS(pwm
->hwpwm
);
110 duty
= SP7021_PWM_DUTY_DD_SEL(pwm
->hwpwm
) | SP7021_PWM_DUTY_MAX
;
112 mode0
&= ~SP7021_PWM_MODE0_BYPASS(pwm
->hwpwm
);
114 * duty_ns <= period_ns 27 bits, clk_rate 28 bits, won't overflow.
116 duty
= mul_u64_u64_div_u64(state
->duty_cycle
, clk_rate
,
117 (u64
)dd_freq
* NSEC_PER_SEC
);
118 duty
= SP7021_PWM_DUTY_DD_SEL(pwm
->hwpwm
) | duty
;
120 writel(duty
, priv
->base
+ SP7021_PWM_DUTY(pwm
->hwpwm
));
121 writel(mode1
, priv
->base
+ SP7021_PWM_MODE1
);
122 writel(mode0
, priv
->base
+ SP7021_PWM_MODE0
);
127 static int sunplus_pwm_get_state(struct pwm_chip
*chip
, struct pwm_device
*pwm
,
128 struct pwm_state
*state
)
130 struct sunplus_pwm
*priv
= to_sunplus_pwm(chip
);
131 u32 mode0
, dd_freq
, duty
;
134 mode0
= readl(priv
->base
+ SP7021_PWM_MODE0
);
136 if (mode0
& BIT(pwm
->hwpwm
)) {
137 clk_rate
= clk_get_rate(priv
->clk
);
138 dd_freq
= readl(priv
->base
+ SP7021_PWM_FREQ(pwm
->hwpwm
));
139 duty
= readl(priv
->base
+ SP7021_PWM_DUTY(pwm
->hwpwm
));
140 duty
= FIELD_GET(SP7021_PWM_DUTY_MASK
, duty
);
142 * dd_freq 16 bits, SP7021_PWM_FREQ_SCALER 8 bits
143 * NSEC_PER_SEC 30 bits, won't overflow.
145 state
->period
= DIV64_U64_ROUND_UP((u64
)dd_freq
* (u64
)SP7021_PWM_FREQ_SCALER
146 * NSEC_PER_SEC
, clk_rate
);
148 * dd_freq 16 bits, duty 8 bits, NSEC_PER_SEC 30 bits, won't overflow.
150 state
->duty_cycle
= DIV64_U64_ROUND_UP((u64
)dd_freq
* (u64
)duty
* NSEC_PER_SEC
,
152 state
->enabled
= true;
154 state
->enabled
= false;
157 state
->polarity
= PWM_POLARITY_NORMAL
;
162 static const struct pwm_ops sunplus_pwm_ops
= {
163 .apply
= sunplus_pwm_apply
,
164 .get_state
= sunplus_pwm_get_state
,
167 static void sunplus_pwm_clk_release(void *data
)
169 struct clk
*clk
= data
;
171 clk_disable_unprepare(clk
);
174 static int sunplus_pwm_probe(struct platform_device
*pdev
)
176 struct device
*dev
= &pdev
->dev
;
177 struct pwm_chip
*chip
;
178 struct sunplus_pwm
*priv
;
181 chip
= devm_pwmchip_alloc(dev
, SP7021_PWM_NUM
, sizeof(*priv
));
183 return PTR_ERR(chip
);
184 priv
= to_sunplus_pwm(chip
);
186 priv
->base
= devm_platform_ioremap_resource(pdev
, 0);
187 if (IS_ERR(priv
->base
))
188 return PTR_ERR(priv
->base
);
190 priv
->clk
= devm_clk_get(dev
, NULL
);
191 if (IS_ERR(priv
->clk
))
192 return dev_err_probe(dev
, PTR_ERR(priv
->clk
),
193 "get pwm clock failed\n");
195 ret
= clk_prepare_enable(priv
->clk
);
197 dev_err(dev
, "failed to enable clock: %d\n", ret
);
201 ret
= devm_add_action_or_reset(dev
, sunplus_pwm_clk_release
, priv
->clk
);
203 dev_err(dev
, "failed to release clock: %d\n", ret
);
207 chip
->ops
= &sunplus_pwm_ops
;
209 ret
= devm_pwmchip_add(dev
, chip
);
211 return dev_err_probe(dev
, ret
, "Cannot register sunplus PWM\n");
216 static const struct of_device_id sunplus_pwm_of_match
[] = {
217 { .compatible
= "sunplus,sp7021-pwm", },
220 MODULE_DEVICE_TABLE(of
, sunplus_pwm_of_match
);
222 static struct platform_driver sunplus_pwm_driver
= {
223 .probe
= sunplus_pwm_probe
,
225 .name
= "sunplus-pwm",
226 .of_match_table
= sunplus_pwm_of_match
,
229 module_platform_driver(sunplus_pwm_driver
);
231 MODULE_DESCRIPTION("Sunplus SoC PWM Driver");
232 MODULE_AUTHOR("Hammer Hsieh <hammerh0314@gmail.com>");
233 MODULE_LICENSE("GPL");