1 /***************************************************************************
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
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
)
29 __REG_CLR(HW_CLKCTRL_XTAL
) = xtal_clk
;
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
;
44 case CLK_PIX
: REG
= &HW_CLKCTRL_PIX
; break;
45 case CLK_SSP
: REG
= &HW_CLKCTRL_SSP
; break;
50 __REG_SET(HW_CLKCTRL_PLLCTRL0
) = HW_CLKCTRL_PLLCTRL0__POWER
;
51 while(!(HW_CLKCTRL_PLLCTRL1
& HW_CLKCTRL_PLLCTRL1__LOCK
));
54 __REG_CLR(HW_CLKCTRL_PLLCTRL0
) = HW_CLKCTRL_PLLCTRL0__POWER
;
60 /* warning: some registers like HW_CLKCTRL_PIX don't have a CLR/SET variant ! */
63 *REG
= (*REG
) & ~__CLK_CLKGATE
;
64 while((*REG
) & __CLK_CLKGATE
);
65 while((*REG
) & __CLK_BUSY
);
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
;
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;
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 ! */
94 __FIELD_SET(HW_CLKCTRL_PIX
, DIV
, div
);
97 __FIELD_SET(HW_CLKCTRL_SSP
, DIV
, div
);
100 __FIELD_SET(HW_CLKCTRL_CPU
, DIV_CPU
, div
);
103 __FIELD_SET(HW_CLKCTRL_EMI
, DIV_EMI
, div
);
106 __FIELD_SET(HW_CLKCTRL_HBUS
, DIV
, div
);
109 __FIELD_SET(HW_CLKCTRL_XBUS
, DIV
, div
);
115 int imx233_clkctrl_get_clock_divisor(enum imx233_clock_t 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
);
124 if(HW_CLKCTRL_HBUS
& HW_CLKCTRL_HBUS__DIV_FRAC_EN
)
127 return __XTRACT(HW_CLKCTRL_HBUS
, DIV
);
128 case CLK_XBUS
: return __XTRACT(HW_CLKCTRL_XBUS
, DIV
);
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
;
140 __FIELD_SET(HW_CLKCTRL_HBUS
, DIV
, fracdiv
);
141 __REG_SET(HW_CLKCTRL_HBUS
) = HW_CLKCTRL_HBUS__DIV_FRAC_EN
;
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;
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
;
163 if(HW_CLKCTRL_HBUS
& HW_CLKCTRL_HBUS__DIV_FRAC_EN
)
164 return __XTRACT(HW_CLKCTRL_HBUS
, DIV
);
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;
174 if((*REG
) & HW_CLKCTRL_FRAC_XX__CLKGATEXX
)
177 return *REG
& ~HW_CLKCTRL_FRAC_XX__XX_STABLE
;
180 void imx233_clkctrl_set_bypass_pll(enum imx233_clock_t clk
, bool bypass
)
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;
193 __REG_SET(HW_CLKCTRL_CLKSEQ
) = msk
;
195 __REG_CLR(HW_CLKCTRL_CLKSEQ
) = msk
;
198 bool imx233_clkctrl_get_bypass_pll(enum imx233_clock_t 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
)
216 __REG_SET(HW_CLKCTRL_PLLCTRL0
) = HW_CLKCTRL_PLLCTRL0__EN_USB_CLKS
;
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
)
243 __REG_SET(HW_CLKCTRL_HBUS
) = HW_CLKCTRL_HBUS__AUTO_SLOW_MODE
;
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
)
256 __REG_SET(HW_CLKCTRL_HBUS
) = monitor
;
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
)
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 */
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;
293 return ref
/ imx233_clkctrl_get_clock_divisor(CLK_CPU
);
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
);
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
);
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
);
325 /* Derived from clk_pll or clk_xtal */
326 if(!imx233_clkctrl_is_clock_enabled(CLK_PIX
))
328 else if(imx233_clkctrl_get_bypass_pll(CLK_PIX
))
329 ref
= imx233_clkctrl_get_clock_freq(CLK_XTAL
);
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
);
341 /* Derived from clk_pll or clk_xtal */
342 if(!imx233_clkctrl_is_clock_enabled(CLK_SSP
))
344 else if(imx233_clkctrl_get_bypass_pll(CLK_SSP
))
345 ref
= imx233_clkctrl_get_clock_freq(CLK_XTAL
);
347 ref
= imx233_clkctrl_get_clock_freq(CLK_IO
);
348 return ref
/ imx233_clkctrl_get_clock_divisor(CLK_SSP
);
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
)
360 return ref
/ __XTRACT(HW_CLKCTRL_EMI
, DIV_XTAL
);
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
);
371 return imx233_clkctrl_get_clock_freq(CLK_XTAL
) /
372 imx233_clkctrl_get_clock_divisor(CLK_XBUS
);