imx233: fix auto slow divisor
[maemo-rb.git] / firmware / target / arm / imx233 / clkctrl-imx233.c
blobccccaae59d1269c052e32311b429b98e6cd66974
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
10 * Copyright © 2011 by Amaury Pouly
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
20 ****************************************************************************/
21 #include "clkctrl-imx233.h"
23 #define __CLK_CLKGATE (1 << 31)
24 #define __CLK_BUSY (1 << 29)
26 void imx233_clkctrl_enable_xtal(enum imx233_xtal_clk_t xtal_clk, bool enable)
28 if(enable)
29 __REG_CLR(HW_CLKCTRL_XTAL) = xtal_clk;
30 else
31 __REG_SET(HW_CLKCTRL_XTAL) = xtal_clk;
34 bool imx233_clkctrl_is_xtal_enable(enum imx233_xtal_clk_t clk)
36 return HW_CLKCTRL_XTAL & clk;
39 void imx233_clkctrl_enable_clock(enum imx233_clock_t clk, bool enable)
41 volatile uint32_t *REG;
42 switch(clk)
44 case CLK_PIX: REG = &HW_CLKCTRL_PIX; break;
45 case CLK_SSP: REG = &HW_CLKCTRL_SSP; break;
46 case CLK_PLL:
48 if(enable)
50 __REG_SET(HW_CLKCTRL_PLLCTRL0) = HW_CLKCTRL_PLLCTRL0__POWER;
51 while(!(HW_CLKCTRL_PLLCTRL1 & HW_CLKCTRL_PLLCTRL1__LOCK));
53 else
54 __REG_CLR(HW_CLKCTRL_PLLCTRL0) = HW_CLKCTRL_PLLCTRL0__POWER;
55 return;
57 default: return;
60 /* warning: some registers like HW_CLKCTRL_PIX don't have a CLR/SET variant ! */
61 if(enable)
63 *REG = (*REG) & ~__CLK_CLKGATE;
64 while((*REG) & __CLK_CLKGATE);
65 while((*REG) & __CLK_BUSY);
67 else
69 *REG |= __CLK_CLKGATE;
70 while(!((*REG) & __CLK_CLKGATE));
74 bool imx233_clkctrl_is_clock_enabled(enum imx233_clock_t clk)
76 volatile uint32_t *REG;
77 switch(clk)
79 case CLK_PLL: return HW_CLKCTRL_PLLCTRL0 & HW_CLKCTRL_PLLCTRL0__POWER;
80 case CLK_PIX: REG = &HW_CLKCTRL_PIX; break;
81 case CLK_SSP: REG = &HW_CLKCTRL_SSP; break;
82 default: return true;
85 return !((*REG) & __CLK_CLKGATE);
88 void imx233_clkctrl_set_clock_divisor(enum imx233_clock_t clk, int div)
90 /* warning: some registers like HW_CLKCTRL_PIX don't have a CLR/SET variant ! */
91 switch(clk)
93 case CLK_PIX:
94 __FIELD_SET(HW_CLKCTRL_PIX, DIV, div);
95 break;
96 case CLK_SSP:
97 __FIELD_SET(HW_CLKCTRL_SSP, DIV, div);
98 break;
99 case CLK_CPU:
100 __FIELD_SET(HW_CLKCTRL_CPU, DIV_CPU, div);
101 break;
102 case CLK_EMI:
103 __FIELD_SET(HW_CLKCTRL_EMI, DIV_EMI, div);
104 break;
105 case CLK_HBUS:
106 __FIELD_SET(HW_CLKCTRL_HBUS, DIV, div);
107 break;
108 case CLK_XBUS:
109 __FIELD_SET(HW_CLKCTRL_XBUS, DIV, div);
110 break;
111 default: return;
115 int imx233_clkctrl_get_clock_divisor(enum imx233_clock_t clk)
117 switch(clk)
119 case CLK_PIX: return __XTRACT(HW_CLKCTRL_PIX, DIV);
120 case CLK_SSP: return __XTRACT(HW_CLKCTRL_SSP, DIV);
121 case CLK_CPU: return __XTRACT(HW_CLKCTRL_CPU, DIV_CPU);
122 case CLK_EMI: return __XTRACT(HW_CLKCTRL_EMI, DIV_EMI);
123 case CLK_HBUS:
124 if(HW_CLKCTRL_HBUS & HW_CLKCTRL_HBUS__DIV_FRAC_EN)
125 return 0;
126 else
127 return __XTRACT(HW_CLKCTRL_HBUS, DIV);
128 case CLK_XBUS: return __XTRACT(HW_CLKCTRL_XBUS, DIV);
129 default: return 0;
133 void imx233_clkctrl_set_fractional_divisor(enum imx233_clock_t clk, int fracdiv)
135 /* NOTE: HW_CLKCTRL_FRAC only support byte access ! */
136 volatile uint8_t *REG;
137 switch(clk)
139 case CLK_HBUS:
140 __FIELD_SET(HW_CLKCTRL_HBUS, DIV, fracdiv);
141 __REG_SET(HW_CLKCTRL_HBUS) = HW_CLKCTRL_HBUS__DIV_FRAC_EN;
142 return;
143 case CLK_PIX: REG = &HW_CLKCTRL_FRAC_PIX; break;
144 case CLK_IO: REG = &HW_CLKCTRL_FRAC_IO; break;
145 case CLK_CPU: REG = &HW_CLKCTRL_FRAC_CPU; break;
146 case CLK_EMI: REG = &HW_CLKCTRL_FRAC_EMI; break;
147 default: return;
150 if(fracdiv != 0)
151 *REG = fracdiv;
152 else
153 *REG = HW_CLKCTRL_FRAC_XX__CLKGATEXX;
156 int imx233_clkctrl_get_fractional_divisor(enum imx233_clock_t clk)
158 /* NOTE: HW_CLKCTRL_FRAC only support byte access ! */
159 volatile uint8_t *REG;
160 switch(clk)
162 case CLK_HBUS:
163 if(HW_CLKCTRL_HBUS & HW_CLKCTRL_HBUS__DIV_FRAC_EN)
164 return __XTRACT(HW_CLKCTRL_HBUS, DIV);
165 else
166 return 0;
167 case CLK_PIX: REG = &HW_CLKCTRL_FRAC_PIX; break;
168 case CLK_IO: REG = &HW_CLKCTRL_FRAC_IO; break;
169 case CLK_CPU: REG = &HW_CLKCTRL_FRAC_CPU; break;
170 case CLK_EMI: REG = &HW_CLKCTRL_FRAC_EMI; break;
171 default: return 0;
174 if((*REG) & HW_CLKCTRL_FRAC_XX__CLKGATEXX)
175 return 0;
176 else
177 return *REG & ~HW_CLKCTRL_FRAC_XX__XX_STABLE;
180 void imx233_clkctrl_set_bypass_pll(enum imx233_clock_t clk, bool bypass)
182 uint32_t msk;
183 switch(clk)
185 case CLK_PIX: msk = HW_CLKCTRL_CLKSEQ__BYPASS_PIX; break;
186 case CLK_SSP: msk = HW_CLKCTRL_CLKSEQ__BYPASS_SSP; break;
187 case CLK_CPU: msk = HW_CLKCTRL_CLKSEQ__BYPASS_CPU; break;
188 case CLK_EMI: msk = HW_CLKCTRL_CLKSEQ__BYPASS_EMI; break;
189 default: return;
192 if(bypass)
193 __REG_SET(HW_CLKCTRL_CLKSEQ) = msk;
194 else
195 __REG_CLR(HW_CLKCTRL_CLKSEQ) = msk;
198 bool imx233_clkctrl_get_bypass_pll(enum imx233_clock_t clk)
200 uint32_t msk;
201 switch(clk)
203 case CLK_PIX: msk = HW_CLKCTRL_CLKSEQ__BYPASS_PIX; break;
204 case CLK_SSP: msk = HW_CLKCTRL_CLKSEQ__BYPASS_SSP; break;
205 case CLK_CPU: msk = HW_CLKCTRL_CLKSEQ__BYPASS_CPU; break;
206 case CLK_EMI: msk = HW_CLKCTRL_CLKSEQ__BYPASS_EMI; break;
207 default: return false;
210 return HW_CLKCTRL_CLKSEQ & msk;
213 void imx233_clkctrl_enable_usb_pll(bool enable)
215 if(enable)
216 __REG_SET(HW_CLKCTRL_PLLCTRL0) = HW_CLKCTRL_PLLCTRL0__EN_USB_CLKS;
217 else
218 __REG_CLR(HW_CLKCTRL_PLLCTRL0) = HW_CLKCTRL_PLLCTRL0__EN_USB_CLKS;
221 bool imx233_clkctrl_is_usb_pll_enabled(void)
223 return HW_CLKCTRL_PLLCTRL0 & HW_CLKCTRL_PLLCTRL0__EN_USB_CLKS;
226 void imx233_clkctrl_set_auto_slow_divisor(enum imx233_as_div_t div)
228 /* the SLOW_DIV must only be set when auto-slow is disabled */
229 bool old_status = imx233_clkctrl_is_auto_slow_enabled();
230 imx233_clkctrl_enable_auto_slow(false);
231 __FIELD_SET(HW_CLKCTRL_HBUS, SLOW_DIV, div);
232 imx233_clkctrl_enable_auto_slow(old_status);
235 enum imx233_as_div_t imx233_clkctrl_get_auto_slow_divisor(void)
237 return __XTRACT(HW_CLKCTRL_HBUS, SLOW_DIV);
240 void imx233_clkctrl_enable_auto_slow(bool enable)
242 if(enable)
243 __REG_SET(HW_CLKCTRL_HBUS) = HW_CLKCTRL_HBUS__AUTO_SLOW_MODE;
244 else
245 __REG_CLR(HW_CLKCTRL_HBUS) = HW_CLKCTRL_HBUS__AUTO_SLOW_MODE;
248 bool imx233_clkctrl_is_auto_slow_enabled(void)
250 return HW_CLKCTRL_HBUS & HW_CLKCTRL_HBUS__AUTO_SLOW_MODE;
253 void imx233_clkctrl_enable_auto_slow_monitor(enum imx233_as_monitor_t monitor, bool enable)
255 if(enable)
256 __REG_SET(HW_CLKCTRL_HBUS) = monitor;
257 else
258 __REG_CLR(HW_CLKCTRL_HBUS) = monitor;
261 bool imx233_clkctrl_is_auto_slow_monitor_enabled(enum imx233_as_monitor_t monitor)
263 return HW_CLKCTRL_HBUS & monitor;
266 bool imx233_clkctrl_is_emi_sync_enabled(void)
268 return !!(HW_CLKCTRL_EMI & HW_CLKCTRL_EMI__SYNC_MODE_EN);
271 unsigned imx233_clkctrl_get_clock_freq(enum imx233_clock_t clk)
273 switch(clk)
275 case CLK_PLL: /* PLL: 480MHz when enable */
276 return imx233_clkctrl_is_clock_enabled(CLK_PLL) ? 480000 : 0;
277 case CLK_XTAL: /* crystal: 24MHz */
278 return 24000;
279 case CLK_CPU:
281 unsigned ref;
282 /* In bypass mode: clk_p derived from clk_xtal via int/binfrac divider
283 * otherwise, clk_p derived from clk_cpu via int div and clk_cpu
284 * derived from clk_pll fracdiv */
285 if(imx233_clkctrl_get_bypass_pll(CLK_CPU))
287 ref = imx233_clkctrl_get_clock_freq(CLK_XTAL);
288 /* Integer divide mode vs fractional divide mode */
289 if(HW_CLKCTRL_CPU & HW_CLKCTRL_CPU__DIV_XTAL_FRAC_EN)
291 return (ref * __XTRACT(HW_CLKCTRL_CPU, DIV_XTAL)) / 32;
292 else
293 return ref / imx233_clkctrl_get_clock_divisor(CLK_CPU);
295 else
297 ref = imx233_clkctrl_get_clock_freq(CLK_PLL);
298 /* fractional divider enable ? */
299 if(imx233_clkctrl_get_fractional_divisor(CLK_CPU) != 0)
300 ref = (ref * 18) / imx233_clkctrl_get_fractional_divisor(CLK_CPU);
301 return ref / imx233_clkctrl_get_clock_divisor(CLK_CPU);
304 case CLK_HBUS:
306 /* Derived from clk_p via integer/fractional div */
307 unsigned ref = imx233_clkctrl_get_clock_freq(CLK_CPU);
308 if(imx233_clkctrl_get_fractional_divisor(CLK_HBUS) != 0)
309 ref = (ref * imx233_clkctrl_get_fractional_divisor(CLK_HBUS)) / 32;
310 if(imx233_clkctrl_get_clock_divisor(CLK_HBUS) != 0)
311 ref /= imx233_clkctrl_get_clock_divisor(CLK_HBUS);
312 return ref;
314 case CLK_IO:
316 /* Derived from clk_pll via fracdiv */
317 unsigned ref = imx233_clkctrl_get_clock_freq(CLK_PLL);
318 if(imx233_clkctrl_get_fractional_divisor(CLK_IO) != 0)
319 ref = (ref * 18) / imx233_clkctrl_get_fractional_divisor(CLK_IO);
320 return ref;
322 case CLK_PIX:
324 unsigned ref;
325 /* Derived from clk_pll or clk_xtal */
326 if(!imx233_clkctrl_is_clock_enabled(CLK_PIX))
327 ref = 0;
328 else if(imx233_clkctrl_get_bypass_pll(CLK_PIX))
329 ref = imx233_clkctrl_get_clock_freq(CLK_XTAL);
330 else
332 ref = imx233_clkctrl_get_clock_freq(CLK_PLL);
333 if(imx233_clkctrl_get_fractional_divisor(CLK_PIX) != 0)
334 ref = (ref * 18) / imx233_clkctrl_get_fractional_divisor(CLK_PIX);
336 return ref / imx233_clkctrl_get_clock_divisor(CLK_PIX);
338 case CLK_SSP:
340 unsigned ref;
341 /* Derived from clk_pll or clk_xtal */
342 if(!imx233_clkctrl_is_clock_enabled(CLK_SSP))
343 ref = 0;
344 else if(imx233_clkctrl_get_bypass_pll(CLK_SSP))
345 ref = imx233_clkctrl_get_clock_freq(CLK_XTAL);
346 else
347 ref = imx233_clkctrl_get_clock_freq(CLK_IO);
348 return ref / imx233_clkctrl_get_clock_divisor(CLK_SSP);
350 case CLK_EMI:
352 unsigned ref;
353 /* Derived from clk_pll or clk_xtal */
354 if(imx233_clkctrl_get_bypass_pll(CLK_EMI))
356 ref = imx233_clkctrl_get_clock_freq(CLK_XTAL);
357 if(HW_CLKCTRL_EMI & HW_CLKCTRL_EMI__CLKGATE)
358 return 0;
359 else
360 return ref / __XTRACT(HW_CLKCTRL_EMI, DIV_XTAL);
362 else
364 ref = imx233_clkctrl_get_clock_freq(CLK_PLL);
365 if(imx233_clkctrl_get_fractional_divisor(CLK_EMI) != 0)
366 ref = (ref * 18) / imx233_clkctrl_get_fractional_divisor(CLK_EMI);
367 return ref / imx233_clkctrl_get_clock_divisor(CLK_EMI);
370 case CLK_XBUS:
371 return imx233_clkctrl_get_clock_freq(CLK_XTAL) /
372 imx233_clkctrl_get_clock_divisor(CLK_XBUS);
373 default:
374 return 0;