]>
git.ipfire.org Git - people/ms/u-boot.git/blob - cpu/ppc4xx/speed.c
3 * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
5 * See file CREDITS for list of people who contributed to this
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License as
10 * published by the Free Software Foundation; either version 2 of
11 * the License, or (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
25 #include <ppc_asm.tmpl>
27 #include <asm/processor.h>
29 /* ------------------------------------------------------------------------- */
31 #define ONE_BILLION 1000000000
34 #if defined(CONFIG_405GP) || defined(CONFIG_405CR)
36 void get_sys_info (PPC405_SYS_INFO
* sysInfo
)
39 unsigned long sysClkPeriodPs
= ONE_BILLION
/ (CONFIG_SYS_CLK_FREQ
/ 1000);
45 * Read PLL Mode register
47 pllmr
= mfdcr (pllmd
);
50 * Read Pin Strapping register
57 sysInfo
->pllFwdDiv
= 8 - ((pllmr
& PLLMR_FWD_DIV_MASK
) >> 29);
62 sysInfo
->pllFbkDiv
= ((pllmr
& PLLMR_FB_DIV_MASK
) >> 25);
63 if (sysInfo
->pllFbkDiv
== 0) {
64 sysInfo
->pllFbkDiv
= 16;
70 sysInfo
->pllPlbDiv
= ((pllmr
& PLLMR_CPU_TO_PLB_MASK
) >> 17) + 1;
75 sysInfo
->pllPciDiv
= ((pllmr
& PLLMR_PCI_TO_PLB_MASK
) >> 13) + 1;
78 * Determine EXTBUS_DIV.
80 sysInfo
->pllExtBusDiv
= ((pllmr
& PLLMR_EXB_TO_PLB_MASK
) >> 11) + 2;
85 sysInfo
->pllOpbDiv
= ((pllmr
& PLLMR_OPB_TO_PLB_MASK
) >> 15) + 1;
88 * Check if PPC405GPr used (mask minor revision field)
90 if ((pvr
& 0xfffffff0) == (PVR_405GPR_RB
& 0xfffffff0)) {
92 * Determine FWD_DIV B (only PPC405GPr with new mode strapping).
94 sysInfo
->pllFwdDivB
= 8 - (pllmr
& PLLMR_FWDB_DIV_MASK
);
97 * Determine factor m depending on PLL feedback clock source
99 if (!(psr
& PSR_PCI_ASYNC_EN
)) {
100 if (psr
& PSR_NEW_MODE_EN
) {
102 * sync pci clock used as feedback (new mode)
104 m
= 1 * sysInfo
->pllFwdDivB
* 2 * sysInfo
->pllPciDiv
;
107 * sync pci clock used as feedback (legacy mode)
109 m
= 1 * sysInfo
->pllFwdDivB
* sysInfo
->pllPlbDiv
* sysInfo
->pllPciDiv
;
111 } else if (psr
& PSR_NEW_MODE_EN
) {
112 if (psr
& PSR_PERCLK_SYNC_MODE_EN
) {
114 * PerClk used as feedback (new mode)
116 m
= 1 * sysInfo
->pllFwdDivB
* 2 * sysInfo
->pllExtBusDiv
;
119 * CPU clock used as feedback (new mode)
121 m
= sysInfo
->pllFbkDiv
* sysInfo
->pllFwdDiv
;
123 } else if (sysInfo
->pllExtBusDiv
== sysInfo
->pllFbkDiv
) {
125 * PerClk used as feedback (legacy mode)
127 m
= 1 * sysInfo
->pllFwdDivB
* sysInfo
->pllPlbDiv
* sysInfo
->pllExtBusDiv
;
130 * PLB clock used as feedback (legacy mode)
132 m
= sysInfo
->pllFbkDiv
* sysInfo
->pllFwdDivB
* sysInfo
->pllPlbDiv
;
135 sysInfo
->freqVCOMhz
= (1000000 * m
) / sysClkPeriodPs
;
136 sysInfo
->freqProcessor
= (sysInfo
->freqVCOMhz
* 1000000) / sysInfo
->pllFwdDiv
;
137 sysInfo
->freqPLB
= (sysInfo
->freqVCOMhz
* 1000000) /
138 (sysInfo
->pllFwdDivB
* sysInfo
->pllPlbDiv
);
141 * Check pllFwdDiv to see if running in bypass mode where the CPU speed
142 * is equal to the 405GP SYS_CLK_FREQ. If not in bypass mode, check VCO
143 * to make sure it is within the proper range.
144 * spec: VCO = SYS_CLOCK x FBKDIV x PLBDIV x FWDDIV
145 * Note freqVCO is calculated in Mhz to avoid errors introduced by rounding.
147 if (sysInfo
->pllFwdDiv
== 1) {
148 sysInfo
->freqProcessor
= CONFIG_SYS_CLK_FREQ
;
149 sysInfo
->freqPLB
= CONFIG_SYS_CLK_FREQ
/ sysInfo
->pllPlbDiv
;
151 sysInfo
->freqVCOMhz
= ( 1000000 *
156 if (sysInfo
->freqVCOMhz
>= VCO_MIN
157 && sysInfo
->freqVCOMhz
<= VCO_MAX
) {
158 sysInfo
->freqPLB
= (ONE_BILLION
/
159 ((sysClkPeriodPs
* 10) /
160 sysInfo
->pllFbkDiv
)) * 10000;
161 sysInfo
->freqProcessor
= sysInfo
->freqPLB
* sysInfo
->pllPlbDiv
;
163 printf ("\nInvalid VCO frequency calculated : %ld MHz \a\n",
164 sysInfo
->freqVCOMhz
);
165 printf ("It must be between %d-%d MHz \a\n",
167 printf ("PLL Mode reg : %8.8lx\a\n",
176 /********************************************
178 * return OPB bus freq in Hz
179 *********************************************/
180 ulong
get_OPB_freq (void)
184 PPC405_SYS_INFO sys_info
;
186 get_sys_info (&sys_info
);
187 val
= sys_info
.freqPLB
/ sys_info
.pllOpbDiv
;
193 /********************************************
195 * return PCI bus freq in Hz
196 *********************************************/
197 ulong
get_PCI_freq (void)
200 PPC405_SYS_INFO sys_info
;
202 get_sys_info (&sys_info
);
203 val
= sys_info
.freqPLB
/ sys_info
.pllPciDiv
;
208 #elif defined(CONFIG_440)
209 #if !defined(CONFIG_440_GX)
210 void get_sys_info (sys_info_t
* sysInfo
)
216 /* Extract configured divisors */
217 strp0
= mfdcr( cpc0_strp0
);
218 sysInfo
->pllFwdDivA
= 8 - ((strp0
& PLLSYS0_FWD_DIV_A_MASK
) >> 15);
219 sysInfo
->pllFwdDivB
= 8 - ((strp0
& PLLSYS0_FWD_DIV_B_MASK
) >> 12);
220 temp
= (strp0
& PLLSYS0_FB_DIV_MASK
) >> 18;
221 sysInfo
->pllFbkDiv
= temp
? temp
: 16;
222 sysInfo
->pllOpbDiv
= 1 + ((strp0
& PLLSYS0_OPB_DIV_MASK
) >> 10);
223 sysInfo
->pllExtBusDiv
= 1 + ((strp0
& PLLSYS0_EPB_DIV_MASK
) >> 8);
225 /* Calculate 'M' based on feedback source */
226 if( strp0
& PLLSYS0_EXTSL_MASK
)
227 m
= sysInfo
->pllExtBusDiv
* sysInfo
->pllOpbDiv
* sysInfo
->pllFwdDivB
;
229 m
= sysInfo
->pllFbkDiv
* sysInfo
->pllFwdDivA
;
231 /* Now calculate the individual clocks */
232 sysInfo
->freqVCOMhz
= (m
* CONFIG_SYS_CLK_FREQ
) + (m
>>1);
233 sysInfo
->freqProcessor
= sysInfo
->freqVCOMhz
/sysInfo
->pllFwdDivA
;
234 sysInfo
->freqPLB
= sysInfo
->freqVCOMhz
/sysInfo
->pllFwdDivB
;
235 if( get_pvr() == PVR_440GP_RB
) /* Rev B divs an extra 2 -- geez! */
236 sysInfo
->freqPLB
>>= 1;
237 sysInfo
->freqOPB
= sysInfo
->freqPLB
/sysInfo
->pllOpbDiv
;
238 sysInfo
->freqEPB
= sysInfo
->freqOPB
/sysInfo
->pllExtBusDiv
;
242 void get_sys_info (sys_info_t
* sysInfo
)
252 /* Extract configured divisors */
253 mfsdr( sdr_sdstp0
,strp0
);
254 mfsdr( sdr_sdstp1
,strp1
);
256 temp
= ((strp0
& PLLSYS0_FWD_DIV_A_MASK
) >> 8);
257 sysInfo
->pllFwdDivA
= temp
? temp
: 16 ;
258 temp
= ((strp0
& PLLSYS0_FWD_DIV_B_MASK
) >> 5);
259 sysInfo
->pllFwdDivB
= temp
? temp
: 8 ;
260 temp
= (strp0
& PLLSYS0_FB_DIV_MASK
) >> 12;
261 sysInfo
->pllFbkDiv
= temp
? temp
: 32;
262 temp
= (strp0
& PLLSYS0_OPB_DIV_MASK
);
263 sysInfo
->pllOpbDiv
= temp
? temp
: 4;
264 temp
= (strp1
& PLLSYS1_PERCLK_DIV_MASK
) >> 24;
265 sysInfo
->pllExtBusDiv
= temp
? temp
: 4;
267 /* Calculate 'M' based on feedback source */
268 temp
= (strp0
& PLLSYS0_SEL_MASK
) >> 27;
269 temp1
= (strp1
& PLLSYS1_LF_DIV_MASK
) >> 26;
270 lfdiv
= temp1
? temp1
: 64;
271 if (temp
== 0) { /* PLL output */
272 /* Figure which pll to use */
273 temp
= (strp0
& PLLSYS0_SRC_MASK
) >> 30;
275 m
= sysInfo
->pllFbkDiv
* lfdiv
* sysInfo
->pllFwdDivA
;
277 m
= sysInfo
->pllFbkDiv
* lfdiv
* sysInfo
->pllFwdDivB
;
279 else if (temp
== 1) /* CPU output */
280 m
= sysInfo
->pllFbkDiv
* sysInfo
->pllFwdDivA
;
282 m
= sysInfo
->pllExtBusDiv
* sysInfo
->pllOpbDiv
* sysInfo
->pllFwdDivB
;
284 /* Now calculate the individual clocks */
285 sysInfo
->freqVCOMhz
= (m
* CONFIG_SYS_CLK_FREQ
) + (m
>>1);
286 sysInfo
->freqProcessor
= sysInfo
->freqVCOMhz
/sysInfo
->pllFwdDivA
;
287 sysInfo
->freqPLB
= sysInfo
->freqVCOMhz
/sysInfo
->pllFwdDivB
;
288 sysInfo
->freqOPB
= sysInfo
->freqPLB
/sysInfo
->pllOpbDiv
;
289 sysInfo
->freqEPB
= sysInfo
->freqOPB
/sysInfo
->pllExtBusDiv
;
294 ulong
get_OPB_freq (void)
298 get_sys_info (&sys_info
);
299 return sys_info
.freqOPB
;
302 #elif defined(CONFIG_405)
304 void get_sys_info (sys_info_t
* sysInfo
) {
306 sysInfo
->freqVCOMhz
=3125000;
307 sysInfo
->freqProcessor
=12*1000*1000;
308 sysInfo
->freqPLB
=50*1000*1000;
309 sysInfo
->freqPCI
=66*1000*1000;
313 #elif defined(CONFIG_405EP)
314 void get_sys_info (PPC405_SYS_INFO
* sysInfo
)
316 unsigned long pllmr0
;
317 unsigned long pllmr1
;
318 unsigned long sysClkPeriodPs
= ONE_BILLION
/ (CONFIG_SYS_CLK_FREQ
/ 1000);
320 unsigned long pllmr0_ccdv
;
323 * Read PLL Mode registers
325 pllmr0
= mfdcr (cpc0_pllmr0
);
326 pllmr1
= mfdcr (cpc0_pllmr1
);
329 * Determine forward divider A
331 sysInfo
->pllFwdDiv
= 8 - ((pllmr1
& PLLMR1_FWDVA_MASK
) >> 16);
334 * Determine forward divider B (should be equal to A)
336 sysInfo
->pllFwdDivB
= 8 - ((pllmr1
& PLLMR1_FWDVB_MASK
) >> 12);
341 sysInfo
->pllFbkDiv
= ((pllmr1
& PLLMR1_FBMUL_MASK
) >> 20);
342 if (sysInfo
->pllFbkDiv
== 0) {
343 sysInfo
->pllFbkDiv
= 16;
349 sysInfo
->pllPlbDiv
= ((pllmr0
& PLLMR0_CPU_TO_PLB_MASK
) >> 16) + 1;
354 sysInfo
->pllPciDiv
= (pllmr0
& PLLMR0_PCI_TO_PLB_MASK
) + 1;
357 * Determine EXTBUS_DIV.
359 sysInfo
->pllExtBusDiv
= ((pllmr0
& PLLMR0_EXB_TO_PLB_MASK
) >> 8) + 2;
364 sysInfo
->pllOpbDiv
= ((pllmr0
& PLLMR0_OPB_TO_PLB_MASK
) >> 12) + 1;
367 * Determine the M factor
369 m
= sysInfo
->pllFbkDiv
* sysInfo
->pllFwdDivB
;
372 * Determine VCO clock frequency
374 sysInfo
->freqVCOMhz
= (1000000 * m
) / sysClkPeriodPs
;
377 * Determine CPU clock frequency
379 pllmr0_ccdv
= ((pllmr0
& PLLMR0_CPU_DIV_MASK
) >> 20) + 1;
380 if (pllmr1
& PLLMR1_SSCS_MASK
) {
381 sysInfo
->freqProcessor
= (CONFIG_SYS_CLK_FREQ
* sysInfo
->pllFbkDiv
)
384 sysInfo
->freqProcessor
= CONFIG_SYS_CLK_FREQ
/ pllmr0_ccdv
;
388 * Determine PLB clock frequency
390 sysInfo
->freqPLB
= sysInfo
->freqProcessor
/ sysInfo
->pllPlbDiv
;
392 if (!((sysInfo
->freqVCOMhz
>= VCO_MIN
) && (sysInfo
->freqVCOMhz
<= VCO_MAX
))) {
393 printf ("\nInvalid VCO frequency calculated : %ld MHz \a\n",
394 sysInfo
->freqVCOMhz
);
395 printf ("It must be between %d-%d MHz \a\n", VCO_MIN
, VCO_MAX
);
396 printf ("PLL Mode reg 0 : %8.8lx\a\n", pllmr0
);
397 printf ("PLL Mode reg 1 : %8.8lx\a\n", pllmr1
);
403 /********************************************
405 * return OPB bus freq in Hz
406 *********************************************/
407 ulong
get_OPB_freq (void)
411 PPC405_SYS_INFO sys_info
;
413 get_sys_info (&sys_info
);
414 val
= sys_info
.freqPLB
/ sys_info
.pllOpbDiv
;
420 /********************************************
422 * return PCI bus freq in Hz
423 *********************************************/
424 ulong
get_PCI_freq (void)
427 PPC405_SYS_INFO sys_info
;
429 get_sys_info (&sys_info
);
430 val
= sys_info
.freqPLB
/ sys_info
.pllPciDiv
;
436 int get_clocks (void)
438 #if defined(CONFIG_405GP) || defined(CONFIG_405CR) || defined(CONFIG_440) || defined(CONFIG_405) || defined(CONFIG_405EP)
439 DECLARE_GLOBAL_DATA_PTR
;
443 get_sys_info (&sys_info
);
444 gd
->cpu_clk
= sys_info
.freqProcessor
;
445 gd
->bus_clk
= sys_info
.freqPLB
;
447 #endif /* defined(CONFIG_405GP) || defined(CONFIG_405CR) */
450 DECLARE_GLOBAL_DATA_PTR
;
452 gd
->cpu_clk
= 66000000;
453 gd
->bus_clk
= 66000000;
459 /********************************************
461 * return PLB bus freq in Hz
462 *********************************************/
463 ulong
get_bus_freq (ulong dummy
)
467 #if defined(CONFIG_405GP) || defined(CONFIG_405CR) || defined(CONFIG_405) || defined(CONFIG_440) || defined(CONFIG_405EP)
470 get_sys_info (&sys_info
);
471 val
= sys_info
.freqPLB
;
473 #elif defined(CONFIG_IOP480)
478 # error get_bus_freq() not implemented