Branch data Line data Source code
1 : : // SPDX-License-Identifier: GPL-2.0
2 : : /*
3 : : * Copyright (C) 2011 Sascha Hauer, Pengutronix <s.hauer@pengutronix.de>
4 : : */
5 : : #include <linux/module.h>
6 : : #include <linux/clk-provider.h>
7 : : #include <linux/slab.h>
8 : : #include <linux/err.h>
9 : : #include <linux/of.h>
10 : : #include <linux/platform_device.h>
11 : :
12 : : /*
13 : : * DOC: basic fixed multiplier and divider clock that cannot gate
14 : : *
15 : : * Traits of this clock:
16 : : * prepare - clk_prepare only ensures that parents are prepared
17 : : * enable - clk_enable only ensures that parents are enabled
18 : : * rate - rate is fixed. clk->rate = parent->rate / div * mult
19 : : * parent - fixed parent. No clk_set_parent support
20 : : */
21 : :
22 : 0 : static unsigned long clk_factor_recalc_rate(struct clk_hw *hw,
23 : : unsigned long parent_rate)
24 : : {
25 : 0 : struct clk_fixed_factor *fix = to_clk_fixed_factor(hw);
26 : 0 : unsigned long long int rate;
27 : :
28 : 0 : rate = (unsigned long long int)parent_rate * fix->mult;
29 : 0 : do_div(rate, fix->div);
30 : 0 : return (unsigned long)rate;
31 : : }
32 : :
33 : 0 : static long clk_factor_round_rate(struct clk_hw *hw, unsigned long rate,
34 : : unsigned long *prate)
35 : : {
36 : 0 : struct clk_fixed_factor *fix = to_clk_fixed_factor(hw);
37 : :
38 [ # # ]: 0 : if (clk_hw_get_flags(hw) & CLK_SET_RATE_PARENT) {
39 : 0 : unsigned long best_parent;
40 : :
41 : 0 : best_parent = (rate / fix->mult) * fix->div;
42 : 0 : *prate = clk_hw_round_rate(clk_hw_get_parent(hw), best_parent);
43 : : }
44 : :
45 : 0 : return (*prate / fix->div) * fix->mult;
46 : : }
47 : :
48 : 0 : static int clk_factor_set_rate(struct clk_hw *hw, unsigned long rate,
49 : : unsigned long parent_rate)
50 : : {
51 : : /*
52 : : * We must report success but we can do so unconditionally because
53 : : * clk_factor_round_rate returns values that ensure this call is a
54 : : * nop.
55 : : */
56 : :
57 : 0 : return 0;
58 : : }
59 : :
60 : : const struct clk_ops clk_fixed_factor_ops = {
61 : : .round_rate = clk_factor_round_rate,
62 : : .set_rate = clk_factor_set_rate,
63 : : .recalc_rate = clk_factor_recalc_rate,
64 : : };
65 : : EXPORT_SYMBOL_GPL(clk_fixed_factor_ops);
66 : :
67 : : static struct clk_hw *
68 : 0 : __clk_hw_register_fixed_factor(struct device *dev, struct device_node *np,
69 : : const char *name, const char *parent_name, int index,
70 : : unsigned long flags, unsigned int mult, unsigned int div)
71 : : {
72 : 0 : struct clk_fixed_factor *fix;
73 : 0 : struct clk_init_data init = { };
74 : 0 : struct clk_parent_data pdata = { .index = index };
75 : 0 : struct clk_hw *hw;
76 : 0 : int ret;
77 : :
78 : 0 : fix = kmalloc(sizeof(*fix), GFP_KERNEL);
79 [ # # ]: 0 : if (!fix)
80 : : return ERR_PTR(-ENOMEM);
81 : :
82 : : /* struct clk_fixed_factor assignments */
83 : 0 : fix->mult = mult;
84 : 0 : fix->div = div;
85 : 0 : fix->hw.init = &init;
86 : :
87 : 0 : init.name = name;
88 : 0 : init.ops = &clk_fixed_factor_ops;
89 : 0 : init.flags = flags;
90 [ # # ]: 0 : if (parent_name)
91 : 0 : init.parent_names = &parent_name;
92 : : else
93 : 0 : init.parent_data = &pdata;
94 : 0 : init.num_parents = 1;
95 : :
96 : 0 : hw = &fix->hw;
97 [ # # ]: 0 : if (dev)
98 : 0 : ret = clk_hw_register(dev, hw);
99 : : else
100 : 0 : ret = of_clk_hw_register(np, hw);
101 [ # # ]: 0 : if (ret) {
102 : 0 : kfree(fix);
103 : 0 : hw = ERR_PTR(ret);
104 : : }
105 : :
106 : : return hw;
107 : : }
108 : :
109 : 0 : struct clk_hw *clk_hw_register_fixed_factor(struct device *dev,
110 : : const char *name, const char *parent_name, unsigned long flags,
111 : : unsigned int mult, unsigned int div)
112 : : {
113 : 0 : return __clk_hw_register_fixed_factor(dev, NULL, name, parent_name, -1,
114 : : flags, mult, div);
115 : : }
116 : : EXPORT_SYMBOL_GPL(clk_hw_register_fixed_factor);
117 : :
118 : 0 : struct clk *clk_register_fixed_factor(struct device *dev, const char *name,
119 : : const char *parent_name, unsigned long flags,
120 : : unsigned int mult, unsigned int div)
121 : : {
122 : 0 : struct clk_hw *hw;
123 : :
124 : 0 : hw = clk_hw_register_fixed_factor(dev, name, parent_name, flags, mult,
125 : : div);
126 [ # # ]: 0 : if (IS_ERR(hw))
127 : : return ERR_CAST(hw);
128 : 0 : return hw->clk;
129 : : }
130 : : EXPORT_SYMBOL_GPL(clk_register_fixed_factor);
131 : :
132 : 0 : void clk_unregister_fixed_factor(struct clk *clk)
133 : : {
134 : 0 : struct clk_hw *hw;
135 : :
136 : 0 : hw = __clk_get_hw(clk);
137 [ # # ]: 0 : if (!hw)
138 : : return;
139 : :
140 : 0 : clk_unregister(clk);
141 : 0 : kfree(to_clk_fixed_factor(hw));
142 : : }
143 : : EXPORT_SYMBOL_GPL(clk_unregister_fixed_factor);
144 : :
145 : 0 : void clk_hw_unregister_fixed_factor(struct clk_hw *hw)
146 : : {
147 : 0 : struct clk_fixed_factor *fix;
148 : :
149 : 0 : fix = to_clk_fixed_factor(hw);
150 : :
151 : 0 : clk_hw_unregister(hw);
152 : 0 : kfree(fix);
153 : 0 : }
154 : : EXPORT_SYMBOL_GPL(clk_hw_unregister_fixed_factor);
155 : :
156 : : #ifdef CONFIG_OF
157 : : static const struct of_device_id set_rate_parent_matches[] = {
158 : : { .compatible = "allwinner,sun4i-a10-pll3-2x-clk" },
159 : : { /* Sentinel */ },
160 : : };
161 : :
162 : : static struct clk_hw *_of_fixed_factor_clk_setup(struct device_node *node)
163 : : {
164 : : struct clk_hw *hw;
165 : : const char *clk_name = node->name;
166 : : unsigned long flags = 0;
167 : : u32 div, mult;
168 : : int ret;
169 : :
170 : : if (of_property_read_u32(node, "clock-div", &div)) {
171 : : pr_err("%s Fixed factor clock <%pOFn> must have a clock-div property\n",
172 : : __func__, node);
173 : : return ERR_PTR(-EIO);
174 : : }
175 : :
176 : : if (of_property_read_u32(node, "clock-mult", &mult)) {
177 : : pr_err("%s Fixed factor clock <%pOFn> must have a clock-mult property\n",
178 : : __func__, node);
179 : : return ERR_PTR(-EIO);
180 : : }
181 : :
182 : : of_property_read_string(node, "clock-output-names", &clk_name);
183 : :
184 : : if (of_match_node(set_rate_parent_matches, node))
185 : : flags |= CLK_SET_RATE_PARENT;
186 : :
187 : : hw = __clk_hw_register_fixed_factor(NULL, node, clk_name, NULL, 0,
188 : : flags, mult, div);
189 : : if (IS_ERR(hw)) {
190 : : /*
191 : : * Clear OF_POPULATED flag so that clock registration can be
192 : : * attempted again from probe function.
193 : : */
194 : : of_node_clear_flag(node, OF_POPULATED);
195 : : return ERR_CAST(hw);
196 : : }
197 : :
198 : : ret = of_clk_add_hw_provider(node, of_clk_hw_simple_get, hw);
199 : : if (ret) {
200 : : clk_hw_unregister_fixed_factor(hw);
201 : : return ERR_PTR(ret);
202 : : }
203 : :
204 : : return hw;
205 : : }
206 : :
207 : : /**
208 : : * of_fixed_factor_clk_setup() - Setup function for simple fixed factor clock
209 : : */
210 : : void __init of_fixed_factor_clk_setup(struct device_node *node)
211 : : {
212 : : _of_fixed_factor_clk_setup(node);
213 : : }
214 : : CLK_OF_DECLARE(fixed_factor_clk, "fixed-factor-clock",
215 : : of_fixed_factor_clk_setup);
216 : :
217 : : static int of_fixed_factor_clk_remove(struct platform_device *pdev)
218 : : {
219 : : struct clk_hw *clk = platform_get_drvdata(pdev);
220 : :
221 : : of_clk_del_provider(pdev->dev.of_node);
222 : : clk_hw_unregister_fixed_factor(clk);
223 : :
224 : : return 0;
225 : : }
226 : :
227 : : static int of_fixed_factor_clk_probe(struct platform_device *pdev)
228 : : {
229 : : struct clk_hw *clk;
230 : :
231 : : /*
232 : : * This function is not executed when of_fixed_factor_clk_setup
233 : : * succeeded.
234 : : */
235 : : clk = _of_fixed_factor_clk_setup(pdev->dev.of_node);
236 : : if (IS_ERR(clk))
237 : : return PTR_ERR(clk);
238 : :
239 : : platform_set_drvdata(pdev, clk);
240 : :
241 : : return 0;
242 : : }
243 : :
244 : : static const struct of_device_id of_fixed_factor_clk_ids[] = {
245 : : { .compatible = "fixed-factor-clock" },
246 : : { }
247 : : };
248 : : MODULE_DEVICE_TABLE(of, of_fixed_factor_clk_ids);
249 : :
250 : : static struct platform_driver of_fixed_factor_clk_driver = {
251 : : .driver = {
252 : : .name = "of_fixed_factor_clk",
253 : : .of_match_table = of_fixed_factor_clk_ids,
254 : : },
255 : : .probe = of_fixed_factor_clk_probe,
256 : : .remove = of_fixed_factor_clk_remove,
257 : : };
258 : : builtin_platform_driver(of_fixed_factor_clk_driver);
259 : : #endif
|