1 // SPDX-License-Identifier: GPL-2.0-only
3 * Copyright (C) 2014 Philipp Zabel, Pengutronix
5 * PWM (mis)used as clock output
7 #include <linux/clk-provider.h>
8 #include <linux/kernel.h>
9 #include <linux/module.h>
11 #include <linux/platform_device.h>
12 #include <linux/pwm.h>
16 struct pwm_device
*pwm
;
20 static inline struct clk_pwm
*to_clk_pwm(struct clk_hw
*hw
)
22 return container_of(hw
, struct clk_pwm
, hw
);
25 static int clk_pwm_prepare(struct clk_hw
*hw
)
27 struct clk_pwm
*clk_pwm
= to_clk_pwm(hw
);
29 return pwm_enable(clk_pwm
->pwm
);
32 static void clk_pwm_unprepare(struct clk_hw
*hw
)
34 struct clk_pwm
*clk_pwm
= to_clk_pwm(hw
);
36 pwm_disable(clk_pwm
->pwm
);
39 static unsigned long clk_pwm_recalc_rate(struct clk_hw
*hw
,
40 unsigned long parent_rate
)
42 struct clk_pwm
*clk_pwm
= to_clk_pwm(hw
);
44 return clk_pwm
->fixed_rate
;
47 static int clk_pwm_get_duty_cycle(struct clk_hw
*hw
, struct clk_duty
*duty
)
49 struct clk_pwm
*clk_pwm
= to_clk_pwm(hw
);
50 struct pwm_state state
;
52 pwm_get_state(clk_pwm
->pwm
, &state
);
54 duty
->num
= state
.duty_cycle
;
55 duty
->den
= state
.period
;
60 static const struct clk_ops clk_pwm_ops
= {
61 .prepare
= clk_pwm_prepare
,
62 .unprepare
= clk_pwm_unprepare
,
63 .recalc_rate
= clk_pwm_recalc_rate
,
64 .get_duty_cycle
= clk_pwm_get_duty_cycle
,
67 static int clk_pwm_probe(struct platform_device
*pdev
)
69 struct device_node
*node
= pdev
->dev
.of_node
;
70 struct clk_init_data init
;
71 struct clk_pwm
*clk_pwm
;
72 struct pwm_device
*pwm
;
73 struct pwm_args pargs
;
77 clk_pwm
= devm_kzalloc(&pdev
->dev
, sizeof(*clk_pwm
), GFP_KERNEL
);
81 pwm
= devm_pwm_get(&pdev
->dev
, NULL
);
85 pwm_get_args(pwm
, &pargs
);
87 dev_err(&pdev
->dev
, "invalid PWM period\n");
91 if (of_property_read_u32(node
, "clock-frequency", &clk_pwm
->fixed_rate
))
92 clk_pwm
->fixed_rate
= div64_u64(NSEC_PER_SEC
, pargs
.period
);
94 if (!clk_pwm
->fixed_rate
) {
95 dev_err(&pdev
->dev
, "fixed_rate cannot be zero\n");
99 if (pargs
.period
!= NSEC_PER_SEC
/ clk_pwm
->fixed_rate
&&
100 pargs
.period
!= DIV_ROUND_UP(NSEC_PER_SEC
, clk_pwm
->fixed_rate
)) {
102 "clock-frequency does not match PWM period\n");
107 * FIXME: pwm_apply_args() should be removed when switching to the
111 ret
= pwm_config(pwm
, (pargs
.period
+ 1) >> 1, pargs
.period
);
115 clk_name
= node
->name
;
116 of_property_read_string(node
, "clock-output-names", &clk_name
);
118 init
.name
= clk_name
;
119 init
.ops
= &clk_pwm_ops
;
121 init
.num_parents
= 0;
124 clk_pwm
->hw
.init
= &init
;
125 ret
= devm_clk_hw_register(&pdev
->dev
, &clk_pwm
->hw
);
129 return of_clk_add_hw_provider(node
, of_clk_hw_simple_get
, &clk_pwm
->hw
);
132 static int clk_pwm_remove(struct platform_device
*pdev
)
134 of_clk_del_provider(pdev
->dev
.of_node
);
139 static const struct of_device_id clk_pwm_dt_ids
[] = {
140 { .compatible
= "pwm-clock" },
143 MODULE_DEVICE_TABLE(of
, clk_pwm_dt_ids
);
145 static struct platform_driver clk_pwm_driver
= {
146 .probe
= clk_pwm_probe
,
147 .remove
= clk_pwm_remove
,
150 .of_match_table
= clk_pwm_dt_ids
,
154 module_platform_driver(clk_pwm_driver
);
156 MODULE_AUTHOR("Philipp Zabel <p.zabel@pengutronix.de>");
157 MODULE_DESCRIPTION("PWM clock driver");
158 MODULE_LICENSE("GPL");