xref: /OK3568_Linux_fs/kernel/drivers/clk/mmp/clk-mix.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
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