1*4882a593Smuzhiyun /*
2*4882a593Smuzhiyun * mmp mix(div and mux) clock operation source file
3*4882a593Smuzhiyun *
4*4882a593Smuzhiyun * Copyright (C) 2014 Marvell
5*4882a593Smuzhiyun * Chao Xie <chao.xie@marvell.com>
6*4882a593Smuzhiyun *
7*4882a593Smuzhiyun * This file is licensed under the terms of the GNU General Public
8*4882a593Smuzhiyun * License version 2. This program is licensed "as is" without any
9*4882a593Smuzhiyun * warranty of any kind, whether express or implied.
10*4882a593Smuzhiyun */
11*4882a593Smuzhiyun
12*4882a593Smuzhiyun #include <linux/clk-provider.h>
13*4882a593Smuzhiyun #include <linux/slab.h>
14*4882a593Smuzhiyun #include <linux/io.h>
15*4882a593Smuzhiyun #include <linux/err.h>
16*4882a593Smuzhiyun
17*4882a593Smuzhiyun #include "clk.h"
18*4882a593Smuzhiyun
19*4882a593Smuzhiyun /*
20*4882a593Smuzhiyun * The mix clock is a clock combined mux and div type clock.
21*4882a593Smuzhiyun * Because the div field and mux field need to be set at same
22*4882a593Smuzhiyun * time, we can not divide it into 2 types of clock
23*4882a593Smuzhiyun */
24*4882a593Smuzhiyun
25*4882a593Smuzhiyun #define to_clk_mix(hw) container_of(hw, struct mmp_clk_mix, hw)
26*4882a593Smuzhiyun
_get_maxdiv(struct mmp_clk_mix * mix)27*4882a593Smuzhiyun static unsigned int _get_maxdiv(struct mmp_clk_mix *mix)
28*4882a593Smuzhiyun {
29*4882a593Smuzhiyun unsigned int div_mask = (1 << mix->reg_info.width_div) - 1;
30*4882a593Smuzhiyun unsigned int maxdiv = 0;
31*4882a593Smuzhiyun struct clk_div_table *clkt;
32*4882a593Smuzhiyun
33*4882a593Smuzhiyun if (mix->div_flags & CLK_DIVIDER_ONE_BASED)
34*4882a593Smuzhiyun return div_mask;
35*4882a593Smuzhiyun if (mix->div_flags & CLK_DIVIDER_POWER_OF_TWO)
36*4882a593Smuzhiyun return 1 << div_mask;
37*4882a593Smuzhiyun if (mix->div_table) {
38*4882a593Smuzhiyun for (clkt = mix->div_table; clkt->div; clkt++)
39*4882a593Smuzhiyun if (clkt->div > maxdiv)
40*4882a593Smuzhiyun maxdiv = clkt->div;
41*4882a593Smuzhiyun return maxdiv;
42*4882a593Smuzhiyun }
43*4882a593Smuzhiyun return div_mask + 1;
44*4882a593Smuzhiyun }
45*4882a593Smuzhiyun
_get_div(struct mmp_clk_mix * mix,unsigned int val)46*4882a593Smuzhiyun static unsigned int _get_div(struct mmp_clk_mix *mix, unsigned int val)
47*4882a593Smuzhiyun {
48*4882a593Smuzhiyun struct clk_div_table *clkt;
49*4882a593Smuzhiyun
50*4882a593Smuzhiyun if (mix->div_flags & CLK_DIVIDER_ONE_BASED)
51*4882a593Smuzhiyun return val;
52*4882a593Smuzhiyun if (mix->div_flags & CLK_DIVIDER_POWER_OF_TWO)
53*4882a593Smuzhiyun return 1 << val;
54*4882a593Smuzhiyun if (mix->div_table) {
55*4882a593Smuzhiyun for (clkt = mix->div_table; clkt->div; clkt++)
56*4882a593Smuzhiyun if (clkt->val == val)
57*4882a593Smuzhiyun return clkt->div;
58*4882a593Smuzhiyun if (clkt->div == 0)
59*4882a593Smuzhiyun return 0;
60*4882a593Smuzhiyun }
61*4882a593Smuzhiyun return val + 1;
62*4882a593Smuzhiyun }
63*4882a593Smuzhiyun
_get_mux(struct mmp_clk_mix * mix,unsigned int val)64*4882a593Smuzhiyun static unsigned int _get_mux(struct mmp_clk_mix *mix, unsigned int val)
65*4882a593Smuzhiyun {
66*4882a593Smuzhiyun int num_parents = clk_hw_get_num_parents(&mix->hw);
67*4882a593Smuzhiyun int i;
68*4882a593Smuzhiyun
69*4882a593Smuzhiyun if (mix->mux_flags & CLK_MUX_INDEX_BIT)
70*4882a593Smuzhiyun return ffs(val) - 1;
71*4882a593Smuzhiyun if (mix->mux_flags & CLK_MUX_INDEX_ONE)
72*4882a593Smuzhiyun return val - 1;
73*4882a593Smuzhiyun if (mix->mux_table) {
74*4882a593Smuzhiyun for (i = 0; i < num_parents; i++)
75*4882a593Smuzhiyun if (mix->mux_table[i] == val)
76*4882a593Smuzhiyun return i;
77*4882a593Smuzhiyun if (i == num_parents)
78*4882a593Smuzhiyun return 0;
79*4882a593Smuzhiyun }
80*4882a593Smuzhiyun
81*4882a593Smuzhiyun return val;
82*4882a593Smuzhiyun }
_get_div_val(struct mmp_clk_mix * mix,unsigned int div)83*4882a593Smuzhiyun static unsigned int _get_div_val(struct mmp_clk_mix *mix, unsigned int div)
84*4882a593Smuzhiyun {
85*4882a593Smuzhiyun struct clk_div_table *clkt;
86*4882a593Smuzhiyun
87*4882a593Smuzhiyun if (mix->div_flags & CLK_DIVIDER_ONE_BASED)
88*4882a593Smuzhiyun return div;
89*4882a593Smuzhiyun if (mix->div_flags & CLK_DIVIDER_POWER_OF_TWO)
90*4882a593Smuzhiyun return __ffs(div);
91*4882a593Smuzhiyun if (mix->div_table) {
92*4882a593Smuzhiyun for (clkt = mix->div_table; clkt->div; clkt++)
93*4882a593Smuzhiyun if (clkt->div == div)
94*4882a593Smuzhiyun return clkt->val;
95*4882a593Smuzhiyun if (clkt->div == 0)
96*4882a593Smuzhiyun return 0;
97*4882a593Smuzhiyun }
98*4882a593Smuzhiyun
99*4882a593Smuzhiyun return div - 1;
100*4882a593Smuzhiyun }
101*4882a593Smuzhiyun
_get_mux_val(struct mmp_clk_mix * mix,unsigned int mux)102*4882a593Smuzhiyun static unsigned int _get_mux_val(struct mmp_clk_mix *mix, unsigned int mux)
103*4882a593Smuzhiyun {
104*4882a593Smuzhiyun if (mix->mux_table)
105*4882a593Smuzhiyun return mix->mux_table[mux];
106*4882a593Smuzhiyun
107*4882a593Smuzhiyun return mux;
108*4882a593Smuzhiyun }
109*4882a593Smuzhiyun
_filter_clk_table(struct mmp_clk_mix * mix,struct mmp_clk_mix_clk_table * table,unsigned int table_size)110*4882a593Smuzhiyun static void _filter_clk_table(struct mmp_clk_mix *mix,
111*4882a593Smuzhiyun struct mmp_clk_mix_clk_table *table,
112*4882a593Smuzhiyun unsigned int table_size)
113*4882a593Smuzhiyun {
114*4882a593Smuzhiyun int i;
115*4882a593Smuzhiyun struct mmp_clk_mix_clk_table *item;
116*4882a593Smuzhiyun struct clk_hw *parent, *hw;
117*4882a593Smuzhiyun unsigned long parent_rate;
118*4882a593Smuzhiyun
119*4882a593Smuzhiyun hw = &mix->hw;
120*4882a593Smuzhiyun
121*4882a593Smuzhiyun for (i = 0; i < table_size; i++) {
122*4882a593Smuzhiyun item = &table[i];
123*4882a593Smuzhiyun parent = clk_hw_get_parent_by_index(hw, item->parent_index);
124*4882a593Smuzhiyun parent_rate = clk_hw_get_rate(parent);
125*4882a593Smuzhiyun if (parent_rate % item->rate) {
126*4882a593Smuzhiyun item->valid = 0;
127*4882a593Smuzhiyun } else {
128*4882a593Smuzhiyun item->divisor = parent_rate / item->rate;
129*4882a593Smuzhiyun item->valid = 1;
130*4882a593Smuzhiyun }
131*4882a593Smuzhiyun }
132*4882a593Smuzhiyun }
133*4882a593Smuzhiyun
_set_rate(struct mmp_clk_mix * mix,u32 mux_val,u32 div_val,unsigned int change_mux,unsigned int change_div)134*4882a593Smuzhiyun static int _set_rate(struct mmp_clk_mix *mix, u32 mux_val, u32 div_val,
135*4882a593Smuzhiyun unsigned int change_mux, unsigned int change_div)
136*4882a593Smuzhiyun {
137*4882a593Smuzhiyun struct mmp_clk_mix_reg_info *ri = &mix->reg_info;
138*4882a593Smuzhiyun u8 width, shift;
139*4882a593Smuzhiyun u32 mux_div, fc_req;
140*4882a593Smuzhiyun int ret, timeout = 50;
141*4882a593Smuzhiyun unsigned long flags = 0;
142*4882a593Smuzhiyun
143*4882a593Smuzhiyun if (!change_mux && !change_div)
144*4882a593Smuzhiyun return -EINVAL;
145*4882a593Smuzhiyun
146*4882a593Smuzhiyun if (mix->lock)
147*4882a593Smuzhiyun spin_lock_irqsave(mix->lock, flags);
148*4882a593Smuzhiyun
149*4882a593Smuzhiyun if (mix->type == MMP_CLK_MIX_TYPE_V1
150*4882a593Smuzhiyun || mix->type == MMP_CLK_MIX_TYPE_V2)
151*4882a593Smuzhiyun mux_div = readl(ri->reg_clk_ctrl);
152*4882a593Smuzhiyun else
153*4882a593Smuzhiyun mux_div = readl(ri->reg_clk_sel);
154*4882a593Smuzhiyun
155*4882a593Smuzhiyun if (change_div) {
156*4882a593Smuzhiyun width = ri->width_div;
157*4882a593Smuzhiyun shift = ri->shift_div;
158*4882a593Smuzhiyun mux_div &= ~MMP_CLK_BITS_MASK(width, shift);
159*4882a593Smuzhiyun mux_div |= MMP_CLK_BITS_SET_VAL(div_val, width, shift);
160*4882a593Smuzhiyun }
161*4882a593Smuzhiyun
162*4882a593Smuzhiyun if (change_mux) {
163*4882a593Smuzhiyun width = ri->width_mux;
164*4882a593Smuzhiyun shift = ri->shift_mux;
165*4882a593Smuzhiyun mux_div &= ~MMP_CLK_BITS_MASK(width, shift);
166*4882a593Smuzhiyun mux_div |= MMP_CLK_BITS_SET_VAL(mux_val, width, shift);
167*4882a593Smuzhiyun }
168*4882a593Smuzhiyun
169*4882a593Smuzhiyun if (mix->type == MMP_CLK_MIX_TYPE_V1) {
170*4882a593Smuzhiyun writel(mux_div, ri->reg_clk_ctrl);
171*4882a593Smuzhiyun } else if (mix->type == MMP_CLK_MIX_TYPE_V2) {
172*4882a593Smuzhiyun mux_div |= (1 << ri->bit_fc);
173*4882a593Smuzhiyun writel(mux_div, ri->reg_clk_ctrl);
174*4882a593Smuzhiyun
175*4882a593Smuzhiyun do {
176*4882a593Smuzhiyun fc_req = readl(ri->reg_clk_ctrl);
177*4882a593Smuzhiyun timeout--;
178*4882a593Smuzhiyun if (!(fc_req & (1 << ri->bit_fc)))
179*4882a593Smuzhiyun break;
180*4882a593Smuzhiyun } while (timeout);
181*4882a593Smuzhiyun
182*4882a593Smuzhiyun if (timeout == 0) {
183*4882a593Smuzhiyun pr_err("%s:%s cannot do frequency change\n",
184*4882a593Smuzhiyun __func__, clk_hw_get_name(&mix->hw));
185*4882a593Smuzhiyun ret = -EBUSY;
186*4882a593Smuzhiyun goto error;
187*4882a593Smuzhiyun }
188*4882a593Smuzhiyun } else {
189*4882a593Smuzhiyun fc_req = readl(ri->reg_clk_ctrl);
190*4882a593Smuzhiyun fc_req |= 1 << ri->bit_fc;
191*4882a593Smuzhiyun writel(fc_req, ri->reg_clk_ctrl);
192*4882a593Smuzhiyun writel(mux_div, ri->reg_clk_sel);
193*4882a593Smuzhiyun fc_req &= ~(1 << ri->bit_fc);
194*4882a593Smuzhiyun }
195*4882a593Smuzhiyun
196*4882a593Smuzhiyun ret = 0;
197*4882a593Smuzhiyun error:
198*4882a593Smuzhiyun if (mix->lock)
199*4882a593Smuzhiyun spin_unlock_irqrestore(mix->lock, flags);
200*4882a593Smuzhiyun
201*4882a593Smuzhiyun return ret;
202*4882a593Smuzhiyun }
203*4882a593Smuzhiyun
mmp_clk_mix_determine_rate(struct clk_hw * hw,struct clk_rate_request * req)204*4882a593Smuzhiyun static int mmp_clk_mix_determine_rate(struct clk_hw *hw,
205*4882a593Smuzhiyun struct clk_rate_request *req)
206*4882a593Smuzhiyun {
207*4882a593Smuzhiyun struct mmp_clk_mix *mix = to_clk_mix(hw);
208*4882a593Smuzhiyun struct mmp_clk_mix_clk_table *item;
209*4882a593Smuzhiyun struct clk_hw *parent, *parent_best;
210*4882a593Smuzhiyun unsigned long parent_rate, mix_rate, mix_rate_best, parent_rate_best;
211*4882a593Smuzhiyun unsigned long gap, gap_best;
212*4882a593Smuzhiyun u32 div_val_max;
213*4882a593Smuzhiyun unsigned int div;
214*4882a593Smuzhiyun int i, j;
215*4882a593Smuzhiyun
216*4882a593Smuzhiyun
217*4882a593Smuzhiyun mix_rate_best = 0;
218*4882a593Smuzhiyun parent_rate_best = 0;
219*4882a593Smuzhiyun gap_best = ULONG_MAX;
220*4882a593Smuzhiyun parent_best = NULL;
221*4882a593Smuzhiyun
222*4882a593Smuzhiyun if (mix->table) {
223*4882a593Smuzhiyun for (i = 0; i < mix->table_size; i++) {
224*4882a593Smuzhiyun item = &mix->table[i];
225*4882a593Smuzhiyun if (item->valid == 0)
226*4882a593Smuzhiyun continue;
227*4882a593Smuzhiyun parent = clk_hw_get_parent_by_index(hw,
228*4882a593Smuzhiyun item->parent_index);
229*4882a593Smuzhiyun parent_rate = clk_hw_get_rate(parent);
230*4882a593Smuzhiyun mix_rate = parent_rate / item->divisor;
231*4882a593Smuzhiyun gap = abs(mix_rate - req->rate);
232*4882a593Smuzhiyun if (!parent_best || gap < gap_best) {
233*4882a593Smuzhiyun parent_best = parent;
234*4882a593Smuzhiyun parent_rate_best = parent_rate;
235*4882a593Smuzhiyun mix_rate_best = mix_rate;
236*4882a593Smuzhiyun gap_best = gap;
237*4882a593Smuzhiyun if (gap_best == 0)
238*4882a593Smuzhiyun goto found;
239*4882a593Smuzhiyun }
240*4882a593Smuzhiyun }
241*4882a593Smuzhiyun } else {
242*4882a593Smuzhiyun for (i = 0; i < clk_hw_get_num_parents(hw); i++) {
243*4882a593Smuzhiyun parent = clk_hw_get_parent_by_index(hw, i);
244*4882a593Smuzhiyun parent_rate = clk_hw_get_rate(parent);
245*4882a593Smuzhiyun div_val_max = _get_maxdiv(mix);
246*4882a593Smuzhiyun for (j = 0; j < div_val_max; j++) {
247*4882a593Smuzhiyun div = _get_div(mix, j);
248*4882a593Smuzhiyun mix_rate = parent_rate / div;
249*4882a593Smuzhiyun gap = abs(mix_rate - req->rate);
250*4882a593Smuzhiyun if (!parent_best || gap < gap_best) {
251*4882a593Smuzhiyun parent_best = parent;
252*4882a593Smuzhiyun parent_rate_best = parent_rate;
253*4882a593Smuzhiyun mix_rate_best = mix_rate;
254*4882a593Smuzhiyun gap_best = gap;
255*4882a593Smuzhiyun if (gap_best == 0)
256*4882a593Smuzhiyun goto found;
257*4882a593Smuzhiyun }
258*4882a593Smuzhiyun }
259*4882a593Smuzhiyun }
260*4882a593Smuzhiyun }
261*4882a593Smuzhiyun
262*4882a593Smuzhiyun found:
263*4882a593Smuzhiyun if (!parent_best)
264*4882a593Smuzhiyun return -EINVAL;
265*4882a593Smuzhiyun
266*4882a593Smuzhiyun req->best_parent_rate = parent_rate_best;
267*4882a593Smuzhiyun req->best_parent_hw = parent_best;
268*4882a593Smuzhiyun req->rate = mix_rate_best;
269*4882a593Smuzhiyun
270*4882a593Smuzhiyun return 0;
271*4882a593Smuzhiyun }
272*4882a593Smuzhiyun
mmp_clk_mix_set_rate_and_parent(struct clk_hw * hw,unsigned long rate,unsigned long parent_rate,u8 index)273*4882a593Smuzhiyun static int mmp_clk_mix_set_rate_and_parent(struct clk_hw *hw,
274*4882a593Smuzhiyun unsigned long rate,
275*4882a593Smuzhiyun unsigned long parent_rate,
276*4882a593Smuzhiyun u8 index)
277*4882a593Smuzhiyun {
278*4882a593Smuzhiyun struct mmp_clk_mix *mix = to_clk_mix(hw);
279*4882a593Smuzhiyun unsigned int div;
280*4882a593Smuzhiyun u32 div_val, mux_val;
281*4882a593Smuzhiyun
282*4882a593Smuzhiyun div = parent_rate / rate;
283*4882a593Smuzhiyun div_val = _get_div_val(mix, div);
284*4882a593Smuzhiyun mux_val = _get_mux_val(mix, index);
285*4882a593Smuzhiyun
286*4882a593Smuzhiyun return _set_rate(mix, mux_val, div_val, 1, 1);
287*4882a593Smuzhiyun }
288*4882a593Smuzhiyun
mmp_clk_mix_get_parent(struct clk_hw * hw)289*4882a593Smuzhiyun static u8 mmp_clk_mix_get_parent(struct clk_hw *hw)
290*4882a593Smuzhiyun {
291*4882a593Smuzhiyun struct mmp_clk_mix *mix = to_clk_mix(hw);
292*4882a593Smuzhiyun struct mmp_clk_mix_reg_info *ri = &mix->reg_info;
293*4882a593Smuzhiyun unsigned long flags = 0;
294*4882a593Smuzhiyun u32 mux_div = 0;
295*4882a593Smuzhiyun u8 width, shift;
296*4882a593Smuzhiyun u32 mux_val;
297*4882a593Smuzhiyun
298*4882a593Smuzhiyun if (mix->lock)
299*4882a593Smuzhiyun spin_lock_irqsave(mix->lock, flags);
300*4882a593Smuzhiyun
301*4882a593Smuzhiyun if (mix->type == MMP_CLK_MIX_TYPE_V1
302*4882a593Smuzhiyun || mix->type == MMP_CLK_MIX_TYPE_V2)
303*4882a593Smuzhiyun mux_div = readl(ri->reg_clk_ctrl);
304*4882a593Smuzhiyun else
305*4882a593Smuzhiyun mux_div = readl(ri->reg_clk_sel);
306*4882a593Smuzhiyun
307*4882a593Smuzhiyun if (mix->lock)
308*4882a593Smuzhiyun spin_unlock_irqrestore(mix->lock, flags);
309*4882a593Smuzhiyun
310*4882a593Smuzhiyun width = mix->reg_info.width_mux;
311*4882a593Smuzhiyun shift = mix->reg_info.shift_mux;
312*4882a593Smuzhiyun
313*4882a593Smuzhiyun mux_val = MMP_CLK_BITS_GET_VAL(mux_div, width, shift);
314*4882a593Smuzhiyun
315*4882a593Smuzhiyun return _get_mux(mix, mux_val);
316*4882a593Smuzhiyun }
317*4882a593Smuzhiyun
mmp_clk_mix_recalc_rate(struct clk_hw * hw,unsigned long parent_rate)318*4882a593Smuzhiyun static unsigned long mmp_clk_mix_recalc_rate(struct clk_hw *hw,
319*4882a593Smuzhiyun unsigned long parent_rate)
320*4882a593Smuzhiyun {
321*4882a593Smuzhiyun struct mmp_clk_mix *mix = to_clk_mix(hw);
322*4882a593Smuzhiyun struct mmp_clk_mix_reg_info *ri = &mix->reg_info;
323*4882a593Smuzhiyun unsigned long flags = 0;
324*4882a593Smuzhiyun u32 mux_div = 0;
325*4882a593Smuzhiyun u8 width, shift;
326*4882a593Smuzhiyun unsigned int div;
327*4882a593Smuzhiyun
328*4882a593Smuzhiyun if (mix->lock)
329*4882a593Smuzhiyun spin_lock_irqsave(mix->lock, flags);
330*4882a593Smuzhiyun
331*4882a593Smuzhiyun if (mix->type == MMP_CLK_MIX_TYPE_V1
332*4882a593Smuzhiyun || mix->type == MMP_CLK_MIX_TYPE_V2)
333*4882a593Smuzhiyun mux_div = readl(ri->reg_clk_ctrl);
334*4882a593Smuzhiyun else
335*4882a593Smuzhiyun mux_div = readl(ri->reg_clk_sel);
336*4882a593Smuzhiyun
337*4882a593Smuzhiyun if (mix->lock)
338*4882a593Smuzhiyun spin_unlock_irqrestore(mix->lock, flags);
339*4882a593Smuzhiyun
340*4882a593Smuzhiyun width = mix->reg_info.width_div;
341*4882a593Smuzhiyun shift = mix->reg_info.shift_div;
342*4882a593Smuzhiyun
343*4882a593Smuzhiyun div = _get_div(mix, MMP_CLK_BITS_GET_VAL(mux_div, width, shift));
344*4882a593Smuzhiyun
345*4882a593Smuzhiyun return parent_rate / div;
346*4882a593Smuzhiyun }
347*4882a593Smuzhiyun
mmp_clk_set_parent(struct clk_hw * hw,u8 index)348*4882a593Smuzhiyun static int mmp_clk_set_parent(struct clk_hw *hw, u8 index)
349*4882a593Smuzhiyun {
350*4882a593Smuzhiyun struct mmp_clk_mix *mix = to_clk_mix(hw);
351*4882a593Smuzhiyun struct mmp_clk_mix_clk_table *item;
352*4882a593Smuzhiyun int i;
353*4882a593Smuzhiyun u32 div_val, mux_val;
354*4882a593Smuzhiyun
355*4882a593Smuzhiyun if (mix->table) {
356*4882a593Smuzhiyun for (i = 0; i < mix->table_size; i++) {
357*4882a593Smuzhiyun item = &mix->table[i];
358*4882a593Smuzhiyun if (item->valid == 0)
359*4882a593Smuzhiyun continue;
360*4882a593Smuzhiyun if (item->parent_index == index)
361*4882a593Smuzhiyun break;
362*4882a593Smuzhiyun }
363*4882a593Smuzhiyun if (i < mix->table_size) {
364*4882a593Smuzhiyun div_val = _get_div_val(mix, item->divisor);
365*4882a593Smuzhiyun mux_val = _get_mux_val(mix, item->parent_index);
366*4882a593Smuzhiyun } else
367*4882a593Smuzhiyun return -EINVAL;
368*4882a593Smuzhiyun } else {
369*4882a593Smuzhiyun mux_val = _get_mux_val(mix, index);
370*4882a593Smuzhiyun div_val = 0;
371*4882a593Smuzhiyun }
372*4882a593Smuzhiyun
373*4882a593Smuzhiyun return _set_rate(mix, mux_val, div_val, 1, div_val ? 1 : 0);
374*4882a593Smuzhiyun }
375*4882a593Smuzhiyun
mmp_clk_set_rate(struct clk_hw * hw,unsigned long rate,unsigned long best_parent_rate)376*4882a593Smuzhiyun static int mmp_clk_set_rate(struct clk_hw *hw, unsigned long rate,
377*4882a593Smuzhiyun unsigned long best_parent_rate)
378*4882a593Smuzhiyun {
379*4882a593Smuzhiyun struct mmp_clk_mix *mix = to_clk_mix(hw);
380*4882a593Smuzhiyun struct mmp_clk_mix_clk_table *item;
381*4882a593Smuzhiyun unsigned long parent_rate;
382*4882a593Smuzhiyun unsigned int best_divisor;
383*4882a593Smuzhiyun struct clk_hw *parent;
384*4882a593Smuzhiyun int i;
385*4882a593Smuzhiyun
386*4882a593Smuzhiyun best_divisor = best_parent_rate / rate;
387*4882a593Smuzhiyun
388*4882a593Smuzhiyun if (mix->table) {
389*4882a593Smuzhiyun for (i = 0; i < mix->table_size; i++) {
390*4882a593Smuzhiyun item = &mix->table[i];
391*4882a593Smuzhiyun if (item->valid == 0)
392*4882a593Smuzhiyun continue;
393*4882a593Smuzhiyun parent = clk_hw_get_parent_by_index(hw,
394*4882a593Smuzhiyun item->parent_index);
395*4882a593Smuzhiyun parent_rate = clk_hw_get_rate(parent);
396*4882a593Smuzhiyun if (parent_rate == best_parent_rate
397*4882a593Smuzhiyun && item->divisor == best_divisor)
398*4882a593Smuzhiyun break;
399*4882a593Smuzhiyun }
400*4882a593Smuzhiyun if (i < mix->table_size)
401*4882a593Smuzhiyun return _set_rate(mix,
402*4882a593Smuzhiyun _get_mux_val(mix, item->parent_index),
403*4882a593Smuzhiyun _get_div_val(mix, item->divisor),
404*4882a593Smuzhiyun 1, 1);
405*4882a593Smuzhiyun else
406*4882a593Smuzhiyun return -EINVAL;
407*4882a593Smuzhiyun } else {
408*4882a593Smuzhiyun for (i = 0; i < clk_hw_get_num_parents(hw); i++) {
409*4882a593Smuzhiyun parent = clk_hw_get_parent_by_index(hw, i);
410*4882a593Smuzhiyun parent_rate = clk_hw_get_rate(parent);
411*4882a593Smuzhiyun if (parent_rate == best_parent_rate)
412*4882a593Smuzhiyun break;
413*4882a593Smuzhiyun }
414*4882a593Smuzhiyun if (i < clk_hw_get_num_parents(hw))
415*4882a593Smuzhiyun return _set_rate(mix, _get_mux_val(mix, i),
416*4882a593Smuzhiyun _get_div_val(mix, best_divisor), 1, 1);
417*4882a593Smuzhiyun else
418*4882a593Smuzhiyun return -EINVAL;
419*4882a593Smuzhiyun }
420*4882a593Smuzhiyun }
421*4882a593Smuzhiyun
mmp_clk_mix_init(struct clk_hw * hw)422*4882a593Smuzhiyun static int mmp_clk_mix_init(struct clk_hw *hw)
423*4882a593Smuzhiyun {
424*4882a593Smuzhiyun struct mmp_clk_mix *mix = to_clk_mix(hw);
425*4882a593Smuzhiyun
426*4882a593Smuzhiyun if (mix->table)
427*4882a593Smuzhiyun _filter_clk_table(mix, mix->table, mix->table_size);
428*4882a593Smuzhiyun
429*4882a593Smuzhiyun return 0;
430*4882a593Smuzhiyun }
431*4882a593Smuzhiyun
432*4882a593Smuzhiyun const struct clk_ops mmp_clk_mix_ops = {
433*4882a593Smuzhiyun .determine_rate = mmp_clk_mix_determine_rate,
434*4882a593Smuzhiyun .set_rate_and_parent = mmp_clk_mix_set_rate_and_parent,
435*4882a593Smuzhiyun .set_rate = mmp_clk_set_rate,
436*4882a593Smuzhiyun .set_parent = mmp_clk_set_parent,
437*4882a593Smuzhiyun .get_parent = mmp_clk_mix_get_parent,
438*4882a593Smuzhiyun .recalc_rate = mmp_clk_mix_recalc_rate,
439*4882a593Smuzhiyun .init = mmp_clk_mix_init,
440*4882a593Smuzhiyun };
441*4882a593Smuzhiyun
mmp_clk_register_mix(struct device * dev,const char * name,const char * const * parent_names,u8 num_parents,unsigned long flags,struct mmp_clk_mix_config * config,spinlock_t * lock)442*4882a593Smuzhiyun struct clk *mmp_clk_register_mix(struct device *dev,
443*4882a593Smuzhiyun const char *name,
444*4882a593Smuzhiyun const char * const *parent_names,
445*4882a593Smuzhiyun u8 num_parents,
446*4882a593Smuzhiyun unsigned long flags,
447*4882a593Smuzhiyun struct mmp_clk_mix_config *config,
448*4882a593Smuzhiyun spinlock_t *lock)
449*4882a593Smuzhiyun {
450*4882a593Smuzhiyun struct mmp_clk_mix *mix;
451*4882a593Smuzhiyun struct clk *clk;
452*4882a593Smuzhiyun struct clk_init_data init;
453*4882a593Smuzhiyun size_t table_bytes;
454*4882a593Smuzhiyun
455*4882a593Smuzhiyun mix = kzalloc(sizeof(*mix), GFP_KERNEL);
456*4882a593Smuzhiyun if (!mix)
457*4882a593Smuzhiyun return ERR_PTR(-ENOMEM);
458*4882a593Smuzhiyun
459*4882a593Smuzhiyun init.name = name;
460*4882a593Smuzhiyun init.flags = flags | CLK_GET_RATE_NOCACHE;
461*4882a593Smuzhiyun init.parent_names = parent_names;
462*4882a593Smuzhiyun init.num_parents = num_parents;
463*4882a593Smuzhiyun init.ops = &mmp_clk_mix_ops;
464*4882a593Smuzhiyun
465*4882a593Smuzhiyun memcpy(&mix->reg_info, &config->reg_info, sizeof(config->reg_info));
466*4882a593Smuzhiyun if (config->table) {
467*4882a593Smuzhiyun table_bytes = sizeof(*config->table) * config->table_size;
468*4882a593Smuzhiyun mix->table = kmemdup(config->table, table_bytes, GFP_KERNEL);
469*4882a593Smuzhiyun if (!mix->table)
470*4882a593Smuzhiyun goto free_mix;
471*4882a593Smuzhiyun
472*4882a593Smuzhiyun mix->table_size = config->table_size;
473*4882a593Smuzhiyun }
474*4882a593Smuzhiyun
475*4882a593Smuzhiyun if (config->mux_table) {
476*4882a593Smuzhiyun table_bytes = sizeof(u32) * num_parents;
477*4882a593Smuzhiyun mix->mux_table = kmemdup(config->mux_table, table_bytes,
478*4882a593Smuzhiyun GFP_KERNEL);
479*4882a593Smuzhiyun if (!mix->mux_table) {
480*4882a593Smuzhiyun kfree(mix->table);
481*4882a593Smuzhiyun goto free_mix;
482*4882a593Smuzhiyun }
483*4882a593Smuzhiyun }
484*4882a593Smuzhiyun
485*4882a593Smuzhiyun mix->div_flags = config->div_flags;
486*4882a593Smuzhiyun mix->mux_flags = config->mux_flags;
487*4882a593Smuzhiyun mix->lock = lock;
488*4882a593Smuzhiyun mix->hw.init = &init;
489*4882a593Smuzhiyun
490*4882a593Smuzhiyun if (config->reg_info.bit_fc >= 32)
491*4882a593Smuzhiyun mix->type = MMP_CLK_MIX_TYPE_V1;
492*4882a593Smuzhiyun else if (config->reg_info.reg_clk_sel)
493*4882a593Smuzhiyun mix->type = MMP_CLK_MIX_TYPE_V3;
494*4882a593Smuzhiyun else
495*4882a593Smuzhiyun mix->type = MMP_CLK_MIX_TYPE_V2;
496*4882a593Smuzhiyun clk = clk_register(dev, &mix->hw);
497*4882a593Smuzhiyun
498*4882a593Smuzhiyun if (IS_ERR(clk)) {
499*4882a593Smuzhiyun kfree(mix->mux_table);
500*4882a593Smuzhiyun kfree(mix->table);
501*4882a593Smuzhiyun kfree(mix);
502*4882a593Smuzhiyun }
503*4882a593Smuzhiyun
504*4882a593Smuzhiyun return clk;
505*4882a593Smuzhiyun
506*4882a593Smuzhiyun free_mix:
507*4882a593Smuzhiyun kfree(mix);
508*4882a593Smuzhiyun return ERR_PTR(-ENOMEM);
509*4882a593Smuzhiyun }
510