xref: /OK3568_Linux_fs/kernel/drivers/clk/sunxi-ng/ccu_nm.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-or-later
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun  * Copyright (C) 2016 Maxime Ripard
4*4882a593Smuzhiyun  * Maxime Ripard <maxime.ripard@free-electrons.com>
5*4882a593Smuzhiyun  */
6*4882a593Smuzhiyun 
7*4882a593Smuzhiyun #include <linux/clk-provider.h>
8*4882a593Smuzhiyun #include <linux/io.h>
9*4882a593Smuzhiyun 
10*4882a593Smuzhiyun #include "ccu_frac.h"
11*4882a593Smuzhiyun #include "ccu_gate.h"
12*4882a593Smuzhiyun #include "ccu_nm.h"
13*4882a593Smuzhiyun 
14*4882a593Smuzhiyun struct _ccu_nm {
15*4882a593Smuzhiyun 	unsigned long	n, min_n, max_n;
16*4882a593Smuzhiyun 	unsigned long	m, min_m, max_m;
17*4882a593Smuzhiyun };
18*4882a593Smuzhiyun 
ccu_nm_calc_rate(unsigned long parent,unsigned long n,unsigned long m)19*4882a593Smuzhiyun static unsigned long ccu_nm_calc_rate(unsigned long parent,
20*4882a593Smuzhiyun 				      unsigned long n, unsigned long m)
21*4882a593Smuzhiyun {
22*4882a593Smuzhiyun 	u64 rate = parent;
23*4882a593Smuzhiyun 
24*4882a593Smuzhiyun 	rate *= n;
25*4882a593Smuzhiyun 	do_div(rate, m);
26*4882a593Smuzhiyun 
27*4882a593Smuzhiyun 	return rate;
28*4882a593Smuzhiyun }
29*4882a593Smuzhiyun 
ccu_nm_find_best(unsigned long parent,unsigned long rate,struct _ccu_nm * nm)30*4882a593Smuzhiyun static void ccu_nm_find_best(unsigned long parent, unsigned long rate,
31*4882a593Smuzhiyun 			     struct _ccu_nm *nm)
32*4882a593Smuzhiyun {
33*4882a593Smuzhiyun 	unsigned long best_rate = 0;
34*4882a593Smuzhiyun 	unsigned long best_n = 0, best_m = 0;
35*4882a593Smuzhiyun 	unsigned long _n, _m;
36*4882a593Smuzhiyun 
37*4882a593Smuzhiyun 	for (_n = nm->min_n; _n <= nm->max_n; _n++) {
38*4882a593Smuzhiyun 		for (_m = nm->min_m; _m <= nm->max_m; _m++) {
39*4882a593Smuzhiyun 			unsigned long tmp_rate = ccu_nm_calc_rate(parent,
40*4882a593Smuzhiyun 								  _n, _m);
41*4882a593Smuzhiyun 
42*4882a593Smuzhiyun 			if (tmp_rate > rate)
43*4882a593Smuzhiyun 				continue;
44*4882a593Smuzhiyun 
45*4882a593Smuzhiyun 			if ((rate - tmp_rate) < (rate - best_rate)) {
46*4882a593Smuzhiyun 				best_rate = tmp_rate;
47*4882a593Smuzhiyun 				best_n = _n;
48*4882a593Smuzhiyun 				best_m = _m;
49*4882a593Smuzhiyun 			}
50*4882a593Smuzhiyun 		}
51*4882a593Smuzhiyun 	}
52*4882a593Smuzhiyun 
53*4882a593Smuzhiyun 	nm->n = best_n;
54*4882a593Smuzhiyun 	nm->m = best_m;
55*4882a593Smuzhiyun }
56*4882a593Smuzhiyun 
ccu_nm_disable(struct clk_hw * hw)57*4882a593Smuzhiyun static void ccu_nm_disable(struct clk_hw *hw)
58*4882a593Smuzhiyun {
59*4882a593Smuzhiyun 	struct ccu_nm *nm = hw_to_ccu_nm(hw);
60*4882a593Smuzhiyun 
61*4882a593Smuzhiyun 	return ccu_gate_helper_disable(&nm->common, nm->enable);
62*4882a593Smuzhiyun }
63*4882a593Smuzhiyun 
ccu_nm_enable(struct clk_hw * hw)64*4882a593Smuzhiyun static int ccu_nm_enable(struct clk_hw *hw)
65*4882a593Smuzhiyun {
66*4882a593Smuzhiyun 	struct ccu_nm *nm = hw_to_ccu_nm(hw);
67*4882a593Smuzhiyun 
68*4882a593Smuzhiyun 	return ccu_gate_helper_enable(&nm->common, nm->enable);
69*4882a593Smuzhiyun }
70*4882a593Smuzhiyun 
ccu_nm_is_enabled(struct clk_hw * hw)71*4882a593Smuzhiyun static int ccu_nm_is_enabled(struct clk_hw *hw)
72*4882a593Smuzhiyun {
73*4882a593Smuzhiyun 	struct ccu_nm *nm = hw_to_ccu_nm(hw);
74*4882a593Smuzhiyun 
75*4882a593Smuzhiyun 	return ccu_gate_helper_is_enabled(&nm->common, nm->enable);
76*4882a593Smuzhiyun }
77*4882a593Smuzhiyun 
ccu_nm_recalc_rate(struct clk_hw * hw,unsigned long parent_rate)78*4882a593Smuzhiyun static unsigned long ccu_nm_recalc_rate(struct clk_hw *hw,
79*4882a593Smuzhiyun 					unsigned long parent_rate)
80*4882a593Smuzhiyun {
81*4882a593Smuzhiyun 	struct ccu_nm *nm = hw_to_ccu_nm(hw);
82*4882a593Smuzhiyun 	unsigned long rate;
83*4882a593Smuzhiyun 	unsigned long n, m;
84*4882a593Smuzhiyun 	u32 reg;
85*4882a593Smuzhiyun 
86*4882a593Smuzhiyun 	if (ccu_frac_helper_is_enabled(&nm->common, &nm->frac)) {
87*4882a593Smuzhiyun 		rate = ccu_frac_helper_read_rate(&nm->common, &nm->frac);
88*4882a593Smuzhiyun 
89*4882a593Smuzhiyun 		if (nm->common.features & CCU_FEATURE_FIXED_POSTDIV)
90*4882a593Smuzhiyun 			rate /= nm->fixed_post_div;
91*4882a593Smuzhiyun 
92*4882a593Smuzhiyun 		return rate;
93*4882a593Smuzhiyun 	}
94*4882a593Smuzhiyun 
95*4882a593Smuzhiyun 	reg = readl(nm->common.base + nm->common.reg);
96*4882a593Smuzhiyun 
97*4882a593Smuzhiyun 	n = reg >> nm->n.shift;
98*4882a593Smuzhiyun 	n &= (1 << nm->n.width) - 1;
99*4882a593Smuzhiyun 	n += nm->n.offset;
100*4882a593Smuzhiyun 	if (!n)
101*4882a593Smuzhiyun 		n++;
102*4882a593Smuzhiyun 
103*4882a593Smuzhiyun 	m = reg >> nm->m.shift;
104*4882a593Smuzhiyun 	m &= (1 << nm->m.width) - 1;
105*4882a593Smuzhiyun 	m += nm->m.offset;
106*4882a593Smuzhiyun 	if (!m)
107*4882a593Smuzhiyun 		m++;
108*4882a593Smuzhiyun 
109*4882a593Smuzhiyun 	if (ccu_sdm_helper_is_enabled(&nm->common, &nm->sdm))
110*4882a593Smuzhiyun 		rate = ccu_sdm_helper_read_rate(&nm->common, &nm->sdm, m, n);
111*4882a593Smuzhiyun 	else
112*4882a593Smuzhiyun 		rate = ccu_nm_calc_rate(parent_rate, n, m);
113*4882a593Smuzhiyun 
114*4882a593Smuzhiyun 	if (nm->common.features & CCU_FEATURE_FIXED_POSTDIV)
115*4882a593Smuzhiyun 		rate /= nm->fixed_post_div;
116*4882a593Smuzhiyun 
117*4882a593Smuzhiyun 	return rate;
118*4882a593Smuzhiyun }
119*4882a593Smuzhiyun 
ccu_nm_round_rate(struct clk_hw * hw,unsigned long rate,unsigned long * parent_rate)120*4882a593Smuzhiyun static long ccu_nm_round_rate(struct clk_hw *hw, unsigned long rate,
121*4882a593Smuzhiyun 			      unsigned long *parent_rate)
122*4882a593Smuzhiyun {
123*4882a593Smuzhiyun 	struct ccu_nm *nm = hw_to_ccu_nm(hw);
124*4882a593Smuzhiyun 	struct _ccu_nm _nm;
125*4882a593Smuzhiyun 
126*4882a593Smuzhiyun 	if (nm->common.features & CCU_FEATURE_FIXED_POSTDIV)
127*4882a593Smuzhiyun 		rate *= nm->fixed_post_div;
128*4882a593Smuzhiyun 
129*4882a593Smuzhiyun 	if (rate < nm->min_rate) {
130*4882a593Smuzhiyun 		rate = nm->min_rate;
131*4882a593Smuzhiyun 		if (nm->common.features & CCU_FEATURE_FIXED_POSTDIV)
132*4882a593Smuzhiyun 			rate /= nm->fixed_post_div;
133*4882a593Smuzhiyun 		return rate;
134*4882a593Smuzhiyun 	}
135*4882a593Smuzhiyun 
136*4882a593Smuzhiyun 	if (nm->max_rate && rate > nm->max_rate) {
137*4882a593Smuzhiyun 		rate = nm->max_rate;
138*4882a593Smuzhiyun 		if (nm->common.features & CCU_FEATURE_FIXED_POSTDIV)
139*4882a593Smuzhiyun 			rate /= nm->fixed_post_div;
140*4882a593Smuzhiyun 		return rate;
141*4882a593Smuzhiyun 	}
142*4882a593Smuzhiyun 
143*4882a593Smuzhiyun 	if (ccu_frac_helper_has_rate(&nm->common, &nm->frac, rate)) {
144*4882a593Smuzhiyun 		if (nm->common.features & CCU_FEATURE_FIXED_POSTDIV)
145*4882a593Smuzhiyun 			rate /= nm->fixed_post_div;
146*4882a593Smuzhiyun 		return rate;
147*4882a593Smuzhiyun 	}
148*4882a593Smuzhiyun 
149*4882a593Smuzhiyun 	if (ccu_sdm_helper_has_rate(&nm->common, &nm->sdm, rate)) {
150*4882a593Smuzhiyun 		if (nm->common.features & CCU_FEATURE_FIXED_POSTDIV)
151*4882a593Smuzhiyun 			rate /= nm->fixed_post_div;
152*4882a593Smuzhiyun 		return rate;
153*4882a593Smuzhiyun 	}
154*4882a593Smuzhiyun 
155*4882a593Smuzhiyun 	_nm.min_n = nm->n.min ?: 1;
156*4882a593Smuzhiyun 	_nm.max_n = nm->n.max ?: 1 << nm->n.width;
157*4882a593Smuzhiyun 	_nm.min_m = 1;
158*4882a593Smuzhiyun 	_nm.max_m = nm->m.max ?: 1 << nm->m.width;
159*4882a593Smuzhiyun 
160*4882a593Smuzhiyun 	ccu_nm_find_best(*parent_rate, rate, &_nm);
161*4882a593Smuzhiyun 	rate = ccu_nm_calc_rate(*parent_rate, _nm.n, _nm.m);
162*4882a593Smuzhiyun 
163*4882a593Smuzhiyun 	if (nm->common.features & CCU_FEATURE_FIXED_POSTDIV)
164*4882a593Smuzhiyun 		rate /= nm->fixed_post_div;
165*4882a593Smuzhiyun 
166*4882a593Smuzhiyun 	return rate;
167*4882a593Smuzhiyun }
168*4882a593Smuzhiyun 
ccu_nm_set_rate(struct clk_hw * hw,unsigned long rate,unsigned long parent_rate)169*4882a593Smuzhiyun static int ccu_nm_set_rate(struct clk_hw *hw, unsigned long rate,
170*4882a593Smuzhiyun 			   unsigned long parent_rate)
171*4882a593Smuzhiyun {
172*4882a593Smuzhiyun 	struct ccu_nm *nm = hw_to_ccu_nm(hw);
173*4882a593Smuzhiyun 	struct _ccu_nm _nm;
174*4882a593Smuzhiyun 	unsigned long flags;
175*4882a593Smuzhiyun 	u32 reg;
176*4882a593Smuzhiyun 
177*4882a593Smuzhiyun 	/* Adjust target rate according to post-dividers */
178*4882a593Smuzhiyun 	if (nm->common.features & CCU_FEATURE_FIXED_POSTDIV)
179*4882a593Smuzhiyun 		rate = rate * nm->fixed_post_div;
180*4882a593Smuzhiyun 
181*4882a593Smuzhiyun 	if (ccu_frac_helper_has_rate(&nm->common, &nm->frac, rate)) {
182*4882a593Smuzhiyun 		spin_lock_irqsave(nm->common.lock, flags);
183*4882a593Smuzhiyun 
184*4882a593Smuzhiyun 		/* most SoCs require M to be 0 if fractional mode is used */
185*4882a593Smuzhiyun 		reg = readl(nm->common.base + nm->common.reg);
186*4882a593Smuzhiyun 		reg &= ~GENMASK(nm->m.width + nm->m.shift - 1, nm->m.shift);
187*4882a593Smuzhiyun 		writel(reg, nm->common.base + nm->common.reg);
188*4882a593Smuzhiyun 
189*4882a593Smuzhiyun 		spin_unlock_irqrestore(nm->common.lock, flags);
190*4882a593Smuzhiyun 
191*4882a593Smuzhiyun 		ccu_frac_helper_enable(&nm->common, &nm->frac);
192*4882a593Smuzhiyun 
193*4882a593Smuzhiyun 		return ccu_frac_helper_set_rate(&nm->common, &nm->frac,
194*4882a593Smuzhiyun 						rate, nm->lock);
195*4882a593Smuzhiyun 	} else {
196*4882a593Smuzhiyun 		ccu_frac_helper_disable(&nm->common, &nm->frac);
197*4882a593Smuzhiyun 	}
198*4882a593Smuzhiyun 
199*4882a593Smuzhiyun 	_nm.min_n = nm->n.min ?: 1;
200*4882a593Smuzhiyun 	_nm.max_n = nm->n.max ?: 1 << nm->n.width;
201*4882a593Smuzhiyun 	_nm.min_m = 1;
202*4882a593Smuzhiyun 	_nm.max_m = nm->m.max ?: 1 << nm->m.width;
203*4882a593Smuzhiyun 
204*4882a593Smuzhiyun 	if (ccu_sdm_helper_has_rate(&nm->common, &nm->sdm, rate)) {
205*4882a593Smuzhiyun 		ccu_sdm_helper_enable(&nm->common, &nm->sdm, rate);
206*4882a593Smuzhiyun 
207*4882a593Smuzhiyun 		/* Sigma delta modulation requires specific N and M factors */
208*4882a593Smuzhiyun 		ccu_sdm_helper_get_factors(&nm->common, &nm->sdm, rate,
209*4882a593Smuzhiyun 					   &_nm.m, &_nm.n);
210*4882a593Smuzhiyun 	} else {
211*4882a593Smuzhiyun 		ccu_sdm_helper_disable(&nm->common, &nm->sdm);
212*4882a593Smuzhiyun 		ccu_nm_find_best(parent_rate, rate, &_nm);
213*4882a593Smuzhiyun 	}
214*4882a593Smuzhiyun 
215*4882a593Smuzhiyun 	spin_lock_irqsave(nm->common.lock, flags);
216*4882a593Smuzhiyun 
217*4882a593Smuzhiyun 	reg = readl(nm->common.base + nm->common.reg);
218*4882a593Smuzhiyun 	reg &= ~GENMASK(nm->n.width + nm->n.shift - 1, nm->n.shift);
219*4882a593Smuzhiyun 	reg &= ~GENMASK(nm->m.width + nm->m.shift - 1, nm->m.shift);
220*4882a593Smuzhiyun 
221*4882a593Smuzhiyun 	reg |= (_nm.n - nm->n.offset) << nm->n.shift;
222*4882a593Smuzhiyun 	reg |= (_nm.m - nm->m.offset) << nm->m.shift;
223*4882a593Smuzhiyun 	writel(reg, nm->common.base + nm->common.reg);
224*4882a593Smuzhiyun 
225*4882a593Smuzhiyun 	spin_unlock_irqrestore(nm->common.lock, flags);
226*4882a593Smuzhiyun 
227*4882a593Smuzhiyun 	ccu_helper_wait_for_lock(&nm->common, nm->lock);
228*4882a593Smuzhiyun 
229*4882a593Smuzhiyun 	return 0;
230*4882a593Smuzhiyun }
231*4882a593Smuzhiyun 
232*4882a593Smuzhiyun const struct clk_ops ccu_nm_ops = {
233*4882a593Smuzhiyun 	.disable	= ccu_nm_disable,
234*4882a593Smuzhiyun 	.enable		= ccu_nm_enable,
235*4882a593Smuzhiyun 	.is_enabled	= ccu_nm_is_enabled,
236*4882a593Smuzhiyun 
237*4882a593Smuzhiyun 	.recalc_rate	= ccu_nm_recalc_rate,
238*4882a593Smuzhiyun 	.round_rate	= ccu_nm_round_rate,
239*4882a593Smuzhiyun 	.set_rate	= ccu_nm_set_rate,
240*4882a593Smuzhiyun };
241