1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3  * Copyright (C) 2016 Maxime Ripard
4  * Maxime Ripard <maxime.ripard@free-electrons.com>
5  */
6 #include "ccu.h"
7 #include "ccu_gate.h"
8 #include "ccu_mux.h"
9 
ccu_mux_get_prediv(struct ccu_common * common,struct ccu_mux_internal * cm,int parent_index)10 static u16 ccu_mux_get_prediv(struct ccu_common *common,
11                               struct ccu_mux_internal *cm,
12                               int parent_index)
13 {
14     u16 prediv = 1;
15     u32 reg;
16 
17     if (!((common->features & CCU_FEATURE_FIXED_PREDIV) ||
18           (common->features & CCU_FEATURE_VARIABLE_PREDIV) ||
19           (common->features & CCU_FEATURE_ALL_PREDIV)))
20     {
21         return 1;
22     }
23 
24     if (common->features & CCU_FEATURE_ALL_PREDIV)
25     {
26         return common->prediv;
27     }
28 
29     reg = readl(common->base + common->reg);
30     if (parent_index < 0)
31     {
32         parent_index = reg >> cm->shift;
33         parent_index &= (1 << cm->width) - 1;
34     }
35 
36     if (common->features & CCU_FEATURE_FIXED_PREDIV)
37     {
38         int i;
39 
40         for (i = 0; i < cm->n_predivs; i++)
41             if (parent_index == cm->fixed_predivs[i].index)
42             {
43                 prediv = cm->fixed_predivs[i].div;
44             }
45     }
46 
47     if (common->features & CCU_FEATURE_VARIABLE_PREDIV)
48     {
49         int i;
50 
51         for (i = 0; i < cm->n_var_predivs; i++)
52             if (parent_index == cm->var_predivs[i].index)
53             {
54                 u8 div;
55 
56                 div = reg >> cm->var_predivs[i].shift;
57                 div &= (1 << cm->var_predivs[i].width) - 1;
58                 prediv = div + 1;
59             }
60     }
61 
62     return prediv;
63 }
64 
ccu_mux_helper_apply_prediv(struct ccu_common * common,struct ccu_mux_internal * cm,int parent_index,unsigned long parent_rate)65 unsigned long ccu_mux_helper_apply_prediv(struct ccu_common *common,
66         struct ccu_mux_internal *cm,
67         int parent_index,
68         unsigned long parent_rate)
69 {
70     return parent_rate / ccu_mux_get_prediv(common, cm, parent_index);
71 }
72 
ccu_mux_helper_unapply_prediv(struct ccu_common * common,struct ccu_mux_internal * cm,int parent_index,unsigned long parent_rate)73 static unsigned long ccu_mux_helper_unapply_prediv(struct ccu_common *common,
74         struct ccu_mux_internal *cm,
75         int parent_index,
76         unsigned long parent_rate)
77 {
78     return parent_rate * ccu_mux_get_prediv(common, cm, parent_index);
79 }
80 
ccu_mux_helper_determine_rate(struct ccu_common * common,struct ccu_mux_internal * cm,struct clk_rate_request * req,unsigned long (* round)(struct ccu_mux_internal *,struct clk_hw *,unsigned long *,unsigned long,void *),void * data)81 int ccu_mux_helper_determine_rate(struct ccu_common *common,
82                                   struct ccu_mux_internal *cm,
83                                   struct clk_rate_request *req,
84                                   unsigned long (*round)(struct ccu_mux_internal *,
85                                           struct clk_hw *,
86                                           unsigned long *,
87                                           unsigned long,
88                                           void *),
89                                   void *data)
90 {
91     unsigned long best_parent_rate = 0, best_rate = 0;
92     struct clk_hw *best_parent, *hw = &common->hw;
93     unsigned int i;
94 
95     if (clk_hw_get_flags(hw) & CLK_SET_RATE_NO_REPARENT)
96     {
97         unsigned long adj_parent_rate;
98 
99         best_parent = clk_hw_get_parent(hw);
100         best_parent_rate = clk_hw_get_rate(best_parent);
101         adj_parent_rate = ccu_mux_helper_apply_prediv(common, cm, -1,
102                           best_parent_rate);
103 
104         best_rate = round(cm, best_parent, &adj_parent_rate,
105                           req->rate, data);
106 
107         /*
108          * adj_parent_rate might have been modified by our clock.
109          * Unapply the pre-divider if there's one, and give
110          * the actual frequency the parent needs to run at.
111          */
112         best_parent_rate = ccu_mux_helper_unapply_prediv(common, cm, -1,
113                            adj_parent_rate);
114 
115         goto out;
116     }
117 
118     for (i = 0; i < clk_hw_get_num_parents(hw); i++)
119     {
120         unsigned long tmp_rate, parent_rate;
121         struct clk_hw *parent;
122 
123         parent = clk_hw_get_parent_by_index(hw, i);
124         if (!parent)
125         {
126             continue;
127         }
128 
129         parent_rate = ccu_mux_helper_apply_prediv(common, cm, i,
130                       clk_hw_get_rate(parent));
131 
132         tmp_rate = round(cm, parent, &parent_rate, req->rate, data);
133 
134         /*
135          * parent_rate might have been modified by our clock.
136          * Unapply the pre-divider if there's one, and give
137          * the actual frequency the parent needs to run at.
138          */
139         parent_rate = ccu_mux_helper_unapply_prediv(common, cm, i,
140                       parent_rate);
141         if (tmp_rate == req->rate)
142         {
143             best_parent = parent;
144             best_parent_rate = parent_rate;
145             best_rate = tmp_rate;
146             goto out;
147         }
148 
149         if ((req->rate - tmp_rate) < (req->rate - best_rate))
150         {
151             best_rate = tmp_rate;
152             best_parent_rate = parent_rate;
153             best_parent = parent;
154         }
155     }
156 
157     if (best_rate == 0)
158     {
159         return -1;
160     }
161 
162 out:
163     req->best_parent_hw = best_parent;
164     req->best_parent_rate = best_parent_rate;
165     req->rate = best_rate;
166     return 0;
167 }
168 
ccu_mux_helper_get_parent(struct ccu_common * common,struct ccu_mux_internal * cm)169 u8 ccu_mux_helper_get_parent(struct ccu_common *common,
170                              struct ccu_mux_internal *cm)
171 {
172     u32 reg;
173     u8 parent;
174 
175     reg = readl(common->base + common->reg);
176     parent = reg >> cm->shift;
177     parent &= (1 << cm->width) - 1;
178 
179     if (cm->table)
180     {
181         int num_parents = clk_hw_get_num_parents(&common->hw);
182         int i;
183 
184         for (i = 0; i < num_parents; i++)
185             if (cm->table[i] == parent)
186             {
187                 return i;
188             }
189     }
190 
191     return parent;
192 }
193 
ccu_mux_helper_set_parent(struct ccu_common * common,struct ccu_mux_internal * cm,u8 index)194 int ccu_mux_helper_set_parent(struct ccu_common *common,
195                               struct ccu_mux_internal *cm,
196                               u8 index)
197 {
198     u32 reg;
199     u32 __cspr;
200 
201     if (cm->table)
202     {
203         index = cm->table[index];
204     }
205 
206     __cspr = hal_spin_lock_irqsave(&common->lock);
207 
208     reg = readl(common->base + common->reg);
209     reg &= ~GENMASK(cm->width + cm->shift - 1, cm->shift);
210     writel(reg | (index << cm->shift), common->base + common->reg);
211 
212     hal_spin_unlock_irqrestore(&common->lock, __cspr);
213 
214     return 0;
215 }
216 
ccu_mux_disable(struct clk_hw * hw)217 static void ccu_mux_disable(struct clk_hw *hw)
218 {
219     struct ccu_mux *cm = hw_to_ccu_mux(hw);
220 
221     return ccu_gate_helper_disable(&cm->common, cm->enable);
222 }
223 
ccu_mux_enable(struct clk_hw * hw)224 static int ccu_mux_enable(struct clk_hw *hw)
225 {
226     struct ccu_mux *cm = hw_to_ccu_mux(hw);
227 
228     return ccu_gate_helper_enable(&cm->common, cm->enable);
229 }
230 
ccu_mux_is_enabled(struct clk_hw * hw)231 static int ccu_mux_is_enabled(struct clk_hw *hw)
232 {
233     struct ccu_mux *cm = hw_to_ccu_mux(hw);
234 
235     return ccu_gate_helper_is_enabled(&cm->common, cm->enable);
236 }
237 
ccu_mux_get_parent(struct clk_hw * hw)238 static u8 ccu_mux_get_parent(struct clk_hw *hw)
239 {
240     struct ccu_mux *cm = hw_to_ccu_mux(hw);
241 
242     return ccu_mux_helper_get_parent(&cm->common, &cm->mux);
243 }
244 
ccu_mux_set_parent(struct clk_hw * hw,u8 index)245 static int ccu_mux_set_parent(struct clk_hw *hw, u8 index)
246 {
247     struct ccu_mux *cm = hw_to_ccu_mux(hw);
248 
249     return ccu_mux_helper_set_parent(&cm->common, &cm->mux, index);
250 }
251 
ccu_mux_recalc_rate(struct clk_hw * hw,unsigned long parent_rate)252 static unsigned long ccu_mux_recalc_rate(struct clk_hw *hw,
253         unsigned long parent_rate)
254 {
255     struct ccu_mux *cm = hw_to_ccu_mux(hw);
256 
257     return ccu_mux_helper_apply_prediv(&cm->common, &cm->mux, -1,
258                                        parent_rate);
259 }
260 
261 const struct clk_ops ccu_mux_ops =
262 {
263     .disable    = ccu_mux_disable,
264     .enable     = ccu_mux_enable,
265     .is_enabled = ccu_mux_is_enabled,
266 
267     .get_parent = ccu_mux_get_parent,
268     .set_parent = ccu_mux_set_parent,
269 
270     .determine_rate = __clk_mux_determine_rate,
271     .recalc_rate    = ccu_mux_recalc_rate,
272 };
273 
274