]> rtime.felk.cvut.cz Git - zynq/linux.git/blob - drivers/clk/zynqmp/divider.c
clk: zynqmp: Fix divider calculation
[zynq/linux.git] / drivers / clk / zynqmp / divider.c
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Zynq UltraScale+ MPSoC Divider support
4  *
5  *  Copyright (C) 2016-2018 Xilinx
6  *
7  * Adjustable divider clock implementation
8  */
9
10 #include <linux/clk.h>
11 #include <linux/clk-provider.h>
12 #include <linux/slab.h>
13 #include "clk-zynqmp.h"
14
15 /*
16  * DOC: basic adjustable divider clock that cannot gate
17  *
18  * Traits of this clock:
19  * prepare - clk_prepare only ensures that parents are prepared
20  * enable - clk_enable only ensures that parents are enabled
21  * rate - rate is adjustable.  clk->rate = ceiling(parent->rate / divisor)
22  * parent - fixed parent.  No clk_set_parent support
23  */
24
25 #define to_zynqmp_clk_divider(_hw)              \
26         container_of(_hw, struct zynqmp_clk_divider, hw)
27
28 #define CLK_FRAC        BIT(8) /* has a fractional parent */
29
30 /**
31  * struct zynqmp_clk_divider - adjustable divider clock
32  * @hw:         handle between common and hardware-specific interfaces
33  * @flags:      Hardware specific flags
34  * @clk_id:     Id of clock
35  * @div_type:   divisor type (TYPE_DIV1 or TYPE_DIV2)
36  * @max_div:    Maximum supported divisor
37  */
38 struct zynqmp_clk_divider {
39         struct clk_hw hw;
40         u16 flags;
41         u32 clk_id;
42         u32 div_type;
43         u32 max_div;
44 };
45
46 static inline int zynqmp_divider_get_val(unsigned long parent_rate,
47                                          unsigned long rate)
48 {
49         return DIV_ROUND_CLOSEST(parent_rate, rate);
50 }
51
52 /**
53  * zynqmp_clk_divider_recalc_rate() - Recalc rate of divider clock
54  * @hw:                 handle between common and hardware-specific interfaces
55  * @parent_rate:        rate of parent clock
56  *
57  * Return: 0 on success else error+reason
58  */
59 static unsigned long zynqmp_clk_divider_recalc_rate(struct clk_hw *hw,
60                                                     unsigned long parent_rate)
61 {
62         struct zynqmp_clk_divider *divider = to_zynqmp_clk_divider(hw);
63         const char *clk_name = clk_hw_get_name(hw);
64         u32 clk_id = divider->clk_id;
65         u32 div_type = divider->div_type;
66         u32 div, value;
67         int ret;
68         const struct zynqmp_eemi_ops *eemi_ops = zynqmp_pm_get_eemi_ops();
69
70         ret = eemi_ops->clock_getdivider(clk_id, &div);
71
72         if (ret)
73                 pr_warn_once("%s() get divider failed for %s, ret = %d\n",
74                              __func__, clk_name, ret);
75
76         if (div_type == TYPE_DIV1)
77                 value = div & 0xFFFF;
78         else
79                 value = div >> 16;
80
81         if (!value) {
82                 WARN(!(divider->flags & CLK_DIVIDER_ALLOW_ZERO),
83                      "%s: Zero divisor and CLK_DIVIDER_ALLOW_ZERO not set\n",
84                      clk_name);
85                 return parent_rate;
86         }
87
88         return DIV_ROUND_UP_ULL(parent_rate, value);
89 }
90
91 static void zynqmp_compute_divider(struct clk_hw *hw,
92                                    unsigned long rate,
93                                    unsigned long parent_rate,
94                                    u32 max_div,
95                                    int *bestdiv)
96 {
97         int div1;
98         int div2;
99         long error = LONG_MAX;
100         struct clk_hw *parent_hw = clk_hw_get_parent(hw);
101         struct zynqmp_clk_divider *pdivider = to_zynqmp_clk_divider(parent_hw);
102
103         if (!pdivider)
104                 return;
105
106         *bestdiv = 1;
107         for (div1 = 1; div1 <= pdivider->max_div; div1++) {
108                 for (div2 = 1; div2 <= max_div; div2++) {
109                         long new_error = ((parent_rate / div1) / div2) - rate;
110
111                         if (abs(new_error) < abs(error)) {
112                                 *bestdiv = div2;
113                                 error = new_error;
114                         }
115                 }
116         }
117 }
118
119 /**
120  * zynqmp_clk_divider_round_rate() - Round rate of divider clock
121  * @hw:                 handle between common and hardware-specific interfaces
122  * @rate:               rate of clock to be set
123  * @prate:              rate of parent clock
124  *
125  * Return: 0 on success else error+reason
126  */
127 static long zynqmp_clk_divider_round_rate(struct clk_hw *hw,
128                                           unsigned long rate,
129                                           unsigned long *prate)
130 {
131         struct zynqmp_clk_divider *divider = to_zynqmp_clk_divider(hw);
132         const char *clk_name = clk_hw_get_name(hw);
133         u32 clk_id = divider->clk_id;
134         u32 div_type = divider->div_type;
135         u32 bestdiv;
136         int ret;
137         const struct zynqmp_eemi_ops *eemi_ops = zynqmp_pm_get_eemi_ops();
138
139         /* if read only, just return current value */
140         if (divider->flags & CLK_DIVIDER_READ_ONLY) {
141                 ret = eemi_ops->clock_getdivider(clk_id, &bestdiv);
142
143                 if (ret)
144                         pr_warn_once("%s() get divider failed for %s, ret = %d\n",
145                                      __func__, clk_name, ret);
146                 if (div_type == TYPE_DIV1)
147                         bestdiv = bestdiv & 0xFFFF;
148                 else
149                         bestdiv  = bestdiv >> 16;
150
151                 return DIV_ROUND_UP_ULL((u64)*prate, bestdiv);
152         }
153
154         bestdiv = zynqmp_divider_get_val(*prate, rate);
155
156         /*
157          * In case of two divisors, compute best divider values and return
158          * divider2 value based on compute value. div1 will  be automatically
159          * set to optimum based on required total divider value.
160          */
161         if (bestdiv > divider->max_div && div_type == TYPE_DIV2 &&
162             (clk_hw_get_flags(hw) & CLK_SET_RATE_PARENT)) {
163                 zynqmp_compute_divider(hw, rate, *prate,
164                                        divider->max_div, &bestdiv);
165         }
166
167         if ((clk_hw_get_flags(hw) & CLK_SET_RATE_PARENT) &&
168             (divider->flags & CLK_FRAC))
169                 bestdiv = rate % *prate ? 1 : bestdiv;
170         *prate = rate * bestdiv;
171
172         return rate;
173 }
174
175 /**
176  * zynqmp_clk_divider_set_rate() - Set rate of divider clock
177  * @hw:                 handle between common and hardware-specific interfaces
178  * @rate:               rate of clock to be set
179  * @parent_rate:        rate of parent clock
180  *
181  * Return: 0 on success else error+reason
182  */
183 static int zynqmp_clk_divider_set_rate(struct clk_hw *hw, unsigned long rate,
184                                        unsigned long parent_rate)
185 {
186         struct zynqmp_clk_divider *divider = to_zynqmp_clk_divider(hw);
187         const char *clk_name = clk_hw_get_name(hw);
188         u32 clk_id = divider->clk_id;
189         u32 div_type = divider->div_type;
190         u32 value, div;
191         int ret;
192         const struct zynqmp_eemi_ops *eemi_ops = zynqmp_pm_get_eemi_ops();
193
194         value = zynqmp_divider_get_val(parent_rate, rate);
195         if (div_type == TYPE_DIV1) {
196                 div = value & 0xFFFF;
197                 div |= 0xffff << 16;
198         } else {
199                 div = 0xffff;
200                 div |= value << 16;
201         }
202
203         ret = eemi_ops->clock_setdivider(clk_id, div);
204
205         if (ret)
206                 pr_warn_once("%s() set divider failed for %s, ret = %d\n",
207                              __func__, clk_name, ret);
208
209         return ret;
210 }
211
212 static const struct clk_ops zynqmp_clk_divider_ops = {
213         .recalc_rate = zynqmp_clk_divider_recalc_rate,
214         .round_rate = zynqmp_clk_divider_round_rate,
215         .set_rate = zynqmp_clk_divider_set_rate,
216 };
217
218 /**
219  * zynqmp_clk_register_divider() - Register a divider clock
220  * @name:               Name of this clock
221  * @clk_id:             Id of clock
222  * @parents:            Name of this clock's parents
223  * @num_parents:        Number of parents
224  * @nodes:              Clock topology node
225  *
226  * Return: clock hardware to registered clock divider
227  */
228 struct clk_hw *zynqmp_clk_register_divider(const char *name,
229                                            u32 clk_id,
230                                            const char * const *parents,
231                                            u8 num_parents,
232                                            const struct clock_topology *nodes)
233 {
234         struct zynqmp_clk_divider *div;
235         struct clk_hw *hw;
236         struct clk_init_data init;
237         int ret;
238         const struct zynqmp_eemi_ops *eemi_ops = zynqmp_pm_get_eemi_ops();
239         struct zynqmp_pm_query_data qdata = {0};
240         u32 ret_payload[PAYLOAD_ARG_CNT];
241
242         /* allocate the divider */
243         div = kzalloc(sizeof(*div), GFP_KERNEL);
244         if (!div)
245                 return ERR_PTR(-ENOMEM);
246
247         init.name = name;
248         init.ops = &zynqmp_clk_divider_ops;
249         init.flags = nodes->flag;
250         init.parent_names = parents;
251         init.num_parents = 1;
252
253         /* struct clk_divider assignments */
254         div->flags = nodes->type_flag;
255         div->hw.init = &init;
256         div->clk_id = clk_id;
257         div->div_type = nodes->type;
258
259         /*
260          * To achieve best possible rate, maximum limit of divider is required
261          * while computation. Get maximum supported divisor from firmware. To
262          * maintain backward compatibility assign maximum possible value(0xFFFF)
263          * if query for max divisor is not successful.
264          */
265         qdata.qid = PM_QID_CLOCK_GET_MAX_DIVISOR;
266         qdata.arg1 = clk_id;
267         qdata.arg2 = nodes->type;
268         ret = eemi_ops->query_data(qdata, ret_payload);
269         if (ret)
270                 div->max_div = 0XFFFF;
271         else
272                 div->max_div = ret_payload[1];
273
274         hw = &div->hw;
275         ret = clk_hw_register(NULL, hw);
276         if (ret) {
277                 kfree(div);
278                 hw = ERR_PTR(ret);
279         }
280
281         return hw;
282 }
283 EXPORT_SYMBOL_GPL(zynqmp_clk_register_divider);