xref: /netbsd-src/sys/arch/arm/sunxi/sunxi_ccu_fractional.c (revision bdc22b2e01993381dcefeff2bc9b56ca75a4235c)
1 /* $NetBSD: sunxi_ccu_fractional.c,v 1.2 2018/04/01 21:19:17 bouyer Exp $ */
2 
3 /*-
4  * Copyright (c) 2017 Jared McNeill <jmcneill@invisible.ca>
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
21  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
23  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
24  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26  * SUCH DAMAGE.
27  */
28 
29 #include <sys/cdefs.h>
30 __KERNEL_RCSID(0, "$NetBSD: sunxi_ccu_fractional.c,v 1.2 2018/04/01 21:19:17 bouyer Exp $");
31 
32 #include <sys/param.h>
33 #include <sys/bus.h>
34 
35 #include <dev/clk/clk_backend.h>
36 
37 #include <arm/sunxi/sunxi_ccu.h>
38 
39 int
40 sunxi_ccu_fractional_enable(struct sunxi_ccu_softc *sc,
41     struct sunxi_ccu_clk *clk, int enable)
42 {
43 	struct sunxi_ccu_fractional *fractional = &clk->u.fractional;
44 	uint32_t val;
45 
46 	KASSERT(clk->type == SUNXI_CCU_FRACTIONAL);
47 
48 	if (!fractional->enable)
49 		return enable ? 0 : EINVAL;
50 
51 	val = CCU_READ(sc, fractional->reg);
52 	if (enable)
53 		val |= fractional->enable;
54 	else
55 		val &= ~fractional->enable;
56 	CCU_WRITE(sc, fractional->reg, val);
57 
58 	return 0;
59 }
60 
61 u_int
62 sunxi_ccu_fractional_get_rate(struct sunxi_ccu_softc *sc,
63     struct sunxi_ccu_clk *clk)
64 {
65 	struct sunxi_ccu_fractional *fractional = &clk->u.fractional;
66 	struct clk *clkp, *clkp_parent;
67 	u_int rate, m;
68 	uint32_t val;
69 
70 	KASSERT(clk->type == SUNXI_CCU_FRACTIONAL);
71 
72 	clkp = &clk->base;
73 	clkp_parent = clk_get_parent(clkp);
74 	if (clkp_parent == NULL)
75 		return 0;
76 
77 	rate = clk_get_rate(clkp_parent);
78 	if (rate == 0)
79 		return 0;
80 
81 	if (fractional->prediv > 0)
82 		rate = rate / fractional->prediv;
83 
84 	val = CCU_READ(sc, fractional->reg);
85 
86 	if (fractional->enable && !(val & fractional->enable))
87 		return 0;
88 
89 	if ((val & fractional->div_en) == 0) {
90 		int sel = __SHIFTOUT(val, fractional->frac_sel);
91 		return fractional->frac[sel];
92 	}
93 	m = __SHIFTOUT(val, fractional->m);
94 
95 	return rate * m;
96 }
97 
98 int
99 sunxi_ccu_fractional_set_rate(struct sunxi_ccu_softc *sc,
100     struct sunxi_ccu_clk *clk, u_int new_rate)
101 {
102 	struct sunxi_ccu_fractional *fractional = &clk->u.fractional;
103 	struct clk *clkp, *clkp_parent;
104 	u_int parent_rate, best_rate, best_m;
105 	u_int m, rate;
106 	int best_diff;
107 	uint32_t val;
108 	int i;
109 
110 	clkp = &clk->base;
111 	clkp_parent = clk_get_parent(clkp);
112 	if (clkp_parent == NULL)
113 		return ENXIO;
114 
115 	parent_rate = clk_get_rate(clkp_parent);
116 	if (parent_rate == 0)
117 		return (new_rate == 0) ? 0 : ERANGE;
118 
119 	if (fractional->prediv > 0)
120 		parent_rate = parent_rate / fractional->prediv;
121 
122 	val = CCU_READ(sc, fractional->reg);
123 	for (i = 0; i < __arraycount(fractional->frac); i++) {
124 		if (fractional->frac[i] == new_rate) {
125 			val &= ~fractional->div_en;
126 			val &= ~fractional->frac_sel;
127 			val |= __SHIFTIN(i, fractional->frac_sel);
128 			CCU_WRITE(sc, fractional->reg, val);
129 			return 0;
130 		}
131 	}
132 	val |= fractional->div_en;
133 
134 	best_rate = 0;
135 	best_diff = INT_MAX;
136 
137 	for (m = fractional->m_min; m <= fractional->m_max; m++) {
138 		rate = parent_rate * m;
139 		const int diff = abs(new_rate - rate);
140 		if (diff < best_diff) {
141 			best_diff = diff;
142 			best_rate = rate;
143 			best_m = m;
144 			if (diff == 0)
145 				break;
146 		}
147 	}
148 
149 	if (best_rate == 0)
150 		return ERANGE;
151 
152 	val &= ~fractional->m;
153 	val |= __SHIFTIN(best_m, fractional->m);
154 	CCU_WRITE(sc, fractional->reg, val);
155 
156 	return 0;
157 }
158 
159 u_int
160 sunxi_ccu_fractional_round_rate(struct sunxi_ccu_softc *sc,
161     struct sunxi_ccu_clk *clk, u_int try_rate)
162 {
163 	struct sunxi_ccu_fractional *fractional = &clk->u.fractional;
164 	struct clk *clkp, *clkp_parent;
165 	u_int parent_rate, best_rate;
166 	u_int m, rate;
167 	int best_diff;
168 	int i;
169 
170 	clkp = &clk->base;
171 	clkp_parent = clk_get_parent(clkp);
172 	if (clkp_parent == NULL)
173 		return 0;
174 
175 	parent_rate = clk_get_rate(clkp_parent);
176 	if (parent_rate == 0)
177 		return 0;
178 
179 	if (fractional->prediv > 0)
180 		parent_rate = parent_rate / fractional->prediv;
181 
182 	for (i = 0; i < __arraycount(fractional->frac); i++) {
183 		if (fractional->frac[i] == try_rate) {
184 			return try_rate;
185 		}
186 	}
187 
188 	best_rate = 0;
189 	best_diff = INT_MAX;
190 
191 	for (m = fractional->m_min; m <= fractional->m_max; m++) {
192 		rate = parent_rate * m;
193 		const int diff = abs(try_rate - rate);
194 		if (diff < best_diff) {
195 			best_diff = diff;
196 			best_rate = rate;
197 			if (diff == 0)
198 				break;
199 		}
200 	}
201 
202 	return best_rate;
203 }
204 
205 const char *
206 sunxi_ccu_fractional_get_parent(struct sunxi_ccu_softc *sc,
207     struct sunxi_ccu_clk *clk)
208 {
209 	struct sunxi_ccu_fractional *fractional = &clk->u.fractional;
210 
211 	KASSERT(clk->type == SUNXI_CCU_FRACTIONAL);
212 
213 	return fractional->parent;
214 }
215