1 // SPDX-License-Identifier: GPL-2.0
3 * MediaTek Pulse Width Modulator driver
5 * Copyright (C) 2015 John Crispin <blogic@openwrt.org>
6 * Copyright (C) 2017 Zhi Mao <zhi.mao@mediatek.com>
10 #include <linux/err.h>
12 #include <linux/ioport.h>
13 #include <linux/kernel.h>
14 #include <linux/module.h>
15 #include <linux/clk.h>
17 #include <linux/of_device.h>
18 #include <linux/platform_device.h>
19 #include <linux/pwm.h>
20 #include <linux/slab.h>
21 #include <linux/types.h>
23 /* PWM registers and bits definitions */
28 #define PWMWAVENUM 0x28
29 #define PWMDWIDTH 0x2c
30 #define PWM45DWIDTH_FIXUP 0x30
32 #define PWM45THRES_FIXUP 0x34
34 #define PWM_CLK_DIV_MAX 7
36 struct pwm_mediatek_of_data
{
37 unsigned int num_pwms
;
42 * struct pwm_mediatek_chip - struct representing PWM chip
43 * @chip: linux PWM chip representation
44 * @regs: base address of PWM chip
45 * @clk_top: the top clock generator
46 * @clk_main: the clock used by PWM core
47 * @clk_pwms: the clock used by each PWM channel
48 * @clk_freq: the fix clock frequency of legacy MIPS SoC
50 struct pwm_mediatek_chip
{
55 struct clk
**clk_pwms
;
56 const struct pwm_mediatek_of_data
*soc
;
59 static const unsigned int pwm_mediatek_reg_offset
[] = {
60 0x0010, 0x0050, 0x0090, 0x00d0, 0x0110, 0x0150, 0x0190, 0x0220
63 static inline struct pwm_mediatek_chip
*
64 to_pwm_mediatek_chip(struct pwm_chip
*chip
)
66 return container_of(chip
, struct pwm_mediatek_chip
, chip
);
69 static int pwm_mediatek_clk_enable(struct pwm_chip
*chip
,
70 struct pwm_device
*pwm
)
72 struct pwm_mediatek_chip
*pc
= to_pwm_mediatek_chip(chip
);
75 ret
= clk_prepare_enable(pc
->clk_top
);
79 ret
= clk_prepare_enable(pc
->clk_main
);
83 ret
= clk_prepare_enable(pc
->clk_pwms
[pwm
->hwpwm
]);
85 goto disable_clk_main
;
90 clk_disable_unprepare(pc
->clk_main
);
92 clk_disable_unprepare(pc
->clk_top
);
97 static void pwm_mediatek_clk_disable(struct pwm_chip
*chip
,
98 struct pwm_device
*pwm
)
100 struct pwm_mediatek_chip
*pc
= to_pwm_mediatek_chip(chip
);
102 clk_disable_unprepare(pc
->clk_pwms
[pwm
->hwpwm
]);
103 clk_disable_unprepare(pc
->clk_main
);
104 clk_disable_unprepare(pc
->clk_top
);
107 static inline u32
pwm_mediatek_readl(struct pwm_mediatek_chip
*chip
,
108 unsigned int num
, unsigned int offset
)
110 return readl(chip
->regs
+ pwm_mediatek_reg_offset
[num
] + offset
);
113 static inline void pwm_mediatek_writel(struct pwm_mediatek_chip
*chip
,
114 unsigned int num
, unsigned int offset
,
117 writel(value
, chip
->regs
+ pwm_mediatek_reg_offset
[num
] + offset
);
120 static int pwm_mediatek_config(struct pwm_chip
*chip
, struct pwm_device
*pwm
,
121 int duty_ns
, int period_ns
)
123 struct pwm_mediatek_chip
*pc
= to_pwm_mediatek_chip(chip
);
124 u32 clkdiv
= 0, cnt_period
, cnt_duty
, reg_width
= PWMDWIDTH
,
125 reg_thres
= PWMTHRES
;
129 ret
= pwm_mediatek_clk_enable(chip
, pwm
);
134 /* Using resolution in picosecond gets accuracy higher */
135 resolution
= (u64
)NSEC_PER_SEC
* 1000;
136 do_div(resolution
, clk_get_rate(pc
->clk_pwms
[pwm
->hwpwm
]));
138 cnt_period
= DIV_ROUND_CLOSEST_ULL((u64
)period_ns
* 1000, resolution
);
139 while (cnt_period
> 8191) {
142 cnt_period
= DIV_ROUND_CLOSEST_ULL((u64
)period_ns
* 1000,
146 if (clkdiv
> PWM_CLK_DIV_MAX
) {
147 pwm_mediatek_clk_disable(chip
, pwm
);
148 dev_err(chip
->dev
, "period %d not supported\n", period_ns
);
152 if (pc
->soc
->pwm45_fixup
&& pwm
->hwpwm
> 2) {
154 * PWM[4,5] has distinct offset for PWMDWIDTH and PWMTHRES
155 * from the other PWMs on MT7623.
157 reg_width
= PWM45DWIDTH_FIXUP
;
158 reg_thres
= PWM45THRES_FIXUP
;
161 cnt_duty
= DIV_ROUND_CLOSEST_ULL((u64
)duty_ns
* 1000, resolution
);
162 pwm_mediatek_writel(pc
, pwm
->hwpwm
, PWMCON
, BIT(15) | clkdiv
);
163 pwm_mediatek_writel(pc
, pwm
->hwpwm
, reg_width
, cnt_period
);
164 pwm_mediatek_writel(pc
, pwm
->hwpwm
, reg_thres
, cnt_duty
);
166 pwm_mediatek_clk_disable(chip
, pwm
);
171 static int pwm_mediatek_enable(struct pwm_chip
*chip
, struct pwm_device
*pwm
)
173 struct pwm_mediatek_chip
*pc
= to_pwm_mediatek_chip(chip
);
177 ret
= pwm_mediatek_clk_enable(chip
, pwm
);
181 value
= readl(pc
->regs
);
182 value
|= BIT(pwm
->hwpwm
);
183 writel(value
, pc
->regs
);
188 static void pwm_mediatek_disable(struct pwm_chip
*chip
, struct pwm_device
*pwm
)
190 struct pwm_mediatek_chip
*pc
= to_pwm_mediatek_chip(chip
);
193 value
= readl(pc
->regs
);
194 value
&= ~BIT(pwm
->hwpwm
);
195 writel(value
, pc
->regs
);
197 pwm_mediatek_clk_disable(chip
, pwm
);
200 static const struct pwm_ops pwm_mediatek_ops
= {
201 .config
= pwm_mediatek_config
,
202 .enable
= pwm_mediatek_enable
,
203 .disable
= pwm_mediatek_disable
,
204 .owner
= THIS_MODULE
,
207 static int pwm_mediatek_probe(struct platform_device
*pdev
)
209 struct pwm_mediatek_chip
*pc
;
210 struct resource
*res
;
214 pc
= devm_kzalloc(&pdev
->dev
, sizeof(*pc
), GFP_KERNEL
);
218 pc
->soc
= of_device_get_match_data(&pdev
->dev
);
220 res
= platform_get_resource(pdev
, IORESOURCE_MEM
, 0);
221 pc
->regs
= devm_ioremap_resource(&pdev
->dev
, res
);
222 if (IS_ERR(pc
->regs
))
223 return PTR_ERR(pc
->regs
);
225 pc
->clk_pwms
= devm_kcalloc(&pdev
->dev
, pc
->soc
->num_pwms
,
226 sizeof(*pc
->clk_pwms
), GFP_KERNEL
);
230 pc
->clk_top
= devm_clk_get(&pdev
->dev
, "top");
231 if (IS_ERR(pc
->clk_top
)) {
232 dev_err(&pdev
->dev
, "clock: top fail: %ld\n",
233 PTR_ERR(pc
->clk_top
));
234 return PTR_ERR(pc
->clk_top
);
237 pc
->clk_main
= devm_clk_get(&pdev
->dev
, "main");
238 if (IS_ERR(pc
->clk_main
)) {
239 dev_err(&pdev
->dev
, "clock: main fail: %ld\n",
240 PTR_ERR(pc
->clk_main
));
241 return PTR_ERR(pc
->clk_main
);
244 for (i
= 0; i
< pc
->soc
->num_pwms
; i
++) {
247 snprintf(name
, sizeof(name
), "pwm%d", i
+ 1);
249 pc
->clk_pwms
[i
] = devm_clk_get(&pdev
->dev
, name
);
250 if (IS_ERR(pc
->clk_pwms
[i
])) {
251 dev_err(&pdev
->dev
, "clock: %s fail: %ld\n",
252 name
, PTR_ERR(pc
->clk_pwms
[i
]));
253 return PTR_ERR(pc
->clk_pwms
[i
]);
257 platform_set_drvdata(pdev
, pc
);
259 pc
->chip
.dev
= &pdev
->dev
;
260 pc
->chip
.ops
= &pwm_mediatek_ops
;
262 pc
->chip
.npwm
= pc
->soc
->num_pwms
;
264 ret
= pwmchip_add(&pc
->chip
);
266 dev_err(&pdev
->dev
, "pwmchip_add() failed: %d\n", ret
);
273 static int pwm_mediatek_remove(struct platform_device
*pdev
)
275 struct pwm_mediatek_chip
*pc
= platform_get_drvdata(pdev
);
277 return pwmchip_remove(&pc
->chip
);
280 static const struct pwm_mediatek_of_data mt2712_pwm_data
= {
282 .pwm45_fixup
= false,
285 static const struct pwm_mediatek_of_data mt7622_pwm_data
= {
287 .pwm45_fixup
= false,
290 static const struct pwm_mediatek_of_data mt7623_pwm_data
= {
295 static const struct pwm_mediatek_of_data mt7628_pwm_data
= {
300 static const struct pwm_mediatek_of_data mt7629_pwm_data
= {
302 .pwm45_fixup
= false,
305 static const struct pwm_mediatek_of_data mt8516_pwm_data
= {
307 .pwm45_fixup
= false,
310 static const struct of_device_id pwm_mediatek_of_match
[] = {
311 { .compatible
= "mediatek,mt2712-pwm", .data
= &mt2712_pwm_data
},
312 { .compatible
= "mediatek,mt7622-pwm", .data
= &mt7622_pwm_data
},
313 { .compatible
= "mediatek,mt7623-pwm", .data
= &mt7623_pwm_data
},
314 { .compatible
= "mediatek,mt7628-pwm", .data
= &mt7628_pwm_data
},
315 { .compatible
= "mediatek,mt7629-pwm", .data
= &mt7629_pwm_data
},
316 { .compatible
= "mediatek,mt8516-pwm", .data
= &mt8516_pwm_data
},
319 MODULE_DEVICE_TABLE(of
, pwm_mediatek_of_match
);
321 static struct platform_driver pwm_mediatek_driver
= {
323 .name
= "pwm-mediatek",
324 .of_match_table
= pwm_mediatek_of_match
,
326 .probe
= pwm_mediatek_probe
,
327 .remove
= pwm_mediatek_remove
,
329 module_platform_driver(pwm_mediatek_driver
);
331 MODULE_AUTHOR("John Crispin <blogic@openwrt.org>");
332 MODULE_LICENSE("GPL v2");