]>
git.ipfire.org Git - people/ms/u-boot.git/blob - cpu/ppc4xx/speed.c
553c491e245bab4a6527ac8fab12f3ec29b3f2a6
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
->freqVCOHz
= (1000000000000LL * (unsigned long long)m
) /
136 (unsigned long long)sysClkPeriodPs
;
137 sysInfo
->freqProcessor
= sysInfo
->freqVCOHz
/ sysInfo
->pllFwdDiv
;
138 sysInfo
->freqPLB
= sysInfo
->freqVCOHz
/ (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
->freqVCOHz
= ( 1000000000000LL *
152 (unsigned long long)sysInfo
->pllFwdDiv
*
153 (unsigned long long)sysInfo
->pllFbkDiv
*
154 (unsigned long long)sysInfo
->pllPlbDiv
155 ) / (unsigned long long)sysClkPeriodPs
;
156 sysInfo
->freqPLB
= (ONE_BILLION
/ ((sysClkPeriodPs
* 10) /
157 sysInfo
->pllFbkDiv
)) * 10000;
158 sysInfo
->freqProcessor
= sysInfo
->freqPLB
* sysInfo
->pllPlbDiv
;
164 /********************************************
166 * return OPB bus freq in Hz
167 *********************************************/
168 ulong
get_OPB_freq (void)
172 PPC405_SYS_INFO sys_info
;
174 get_sys_info (&sys_info
);
175 val
= sys_info
.freqPLB
/ sys_info
.pllOpbDiv
;
181 /********************************************
183 * return PCI bus freq in Hz
184 *********************************************/
185 ulong
get_PCI_freq (void)
188 PPC405_SYS_INFO sys_info
;
190 get_sys_info (&sys_info
);
191 val
= sys_info
.freqPLB
/ sys_info
.pllPciDiv
;
196 #elif defined(CONFIG_440)
198 #if defined(CONFIG_440EP) || defined(CONFIG_440GR)
199 void get_sys_info (sys_info_t
*sysInfo
)
205 unsigned long prbdv0
;
207 WARNING: ASSUMES the following:
213 /* Decode CPR0_PLLD0 for divisors */
214 mfclk(clk_plld
, reg
);
215 temp
= (reg
& PLLD_FWDVA_MASK
) >> 16;
216 sysInfo
->pllFwdDivA
= temp
? temp
: 16;
217 temp
= (reg
& PLLD_FWDVB_MASK
) >> 8;
218 sysInfo
->pllFwdDivB
= temp
? temp
: 8 ;
219 temp
= (reg
& PLLD_FBDV_MASK
) >> 24;
220 sysInfo
->pllFbkDiv
= temp
? temp
: 32;
221 lfdiv
= reg
& PLLD_LFBDV_MASK
;
223 mfclk(clk_opbd
, reg
);
224 temp
= (reg
& OPBDDV_MASK
) >> 24;
225 sysInfo
->pllOpbDiv
= temp
? temp
: 4;
227 mfclk(clk_perd
, reg
);
228 temp
= (reg
& PERDV_MASK
) >> 24;
229 sysInfo
->pllExtBusDiv
= temp
? temp
: 8;
231 mfclk(clk_primbd
, reg
);
232 temp
= (reg
& PRBDV_MASK
) >> 24;
233 prbdv0
= temp
? temp
: 8;
235 mfclk(clk_spcid
, reg
);
236 temp
= (reg
& SPCID_MASK
) >> 24;
237 sysInfo
->pllPciDiv
= temp
? temp
: 4;
239 /* Calculate 'M' based on feedback source */
240 mfsdr(sdr_sdstp0
, reg
);
241 temp
= (reg
& PLLSYS0_SEL_MASK
) >> 27;
242 if (temp
== 0) { /* PLL output */
243 /* Figure which pll to use */
244 mfclk(clk_pllc
, reg
);
245 temp
= (reg
& PLLC_SRC_MASK
) >> 29;
246 if (!temp
) /* PLLOUTA */
247 m
= sysInfo
->pllFbkDiv
* lfdiv
* sysInfo
->pllFwdDivA
;
249 m
= sysInfo
->pllFbkDiv
* lfdiv
* sysInfo
->pllFwdDivB
;
251 else if (temp
== 1) /* CPU output */
252 m
= sysInfo
->pllFbkDiv
* sysInfo
->pllFwdDivA
;
254 m
= sysInfo
->pllExtBusDiv
* sysInfo
->pllOpbDiv
* sysInfo
->pllFwdDivB
;
256 /* Now calculate the individual clocks */
257 sysInfo
->freqVCOMhz
= (m
* CONFIG_SYS_CLK_FREQ
) + (m
>>1);
258 sysInfo
->freqProcessor
= sysInfo
->freqVCOMhz
/sysInfo
->pllFwdDivA
;
259 sysInfo
->freqPLB
= sysInfo
->freqVCOMhz
/sysInfo
->pllFwdDivB
/prbdv0
;
260 sysInfo
->freqOPB
= sysInfo
->freqPLB
/sysInfo
->pllOpbDiv
;
261 sysInfo
->freqEPB
= sysInfo
->freqPLB
/sysInfo
->pllExtBusDiv
;
262 sysInfo
->freqPCI
= sysInfo
->freqPLB
/sysInfo
->pllPciDiv
;
264 /* Figure which timer source to use */
265 if (mfspr(ccr1
) & 0x0080) { /* External Clock, assume same as SYS_CLK */
266 temp
= sysInfo
->freqProcessor
/ 2; /* Max extern clock speed */
267 if (CONFIG_SYS_CLK_FREQ
> temp
)
268 sysInfo
->freqTmrClk
= temp
;
270 sysInfo
->freqTmrClk
= CONFIG_SYS_CLK_FREQ
;
272 else /* Internal clock */
273 sysInfo
->freqTmrClk
= sysInfo
->freqProcessor
;
275 /********************************************
277 * return PCI bus freq in Hz
278 *********************************************/
279 ulong
get_PCI_freq (void)
282 get_sys_info (&sys_info
);
283 return sys_info
.freqPCI
;
286 #elif !defined(CONFIG_440GX) && !defined(CONFIG_440SP)
287 void get_sys_info (sys_info_t
* sysInfo
)
293 /* Extract configured divisors */
294 strp0
= mfdcr( cpc0_strp0
);
295 sysInfo
->pllFwdDivA
= 8 - ((strp0
& PLLSYS0_FWD_DIV_A_MASK
) >> 15);
296 sysInfo
->pllFwdDivB
= 8 - ((strp0
& PLLSYS0_FWD_DIV_B_MASK
) >> 12);
297 temp
= (strp0
& PLLSYS0_FB_DIV_MASK
) >> 18;
298 sysInfo
->pllFbkDiv
= temp
? temp
: 16;
299 sysInfo
->pllOpbDiv
= 1 + ((strp0
& PLLSYS0_OPB_DIV_MASK
) >> 10);
300 sysInfo
->pllExtBusDiv
= 1 + ((strp0
& PLLSYS0_EPB_DIV_MASK
) >> 8);
302 /* Calculate 'M' based on feedback source */
303 if( strp0
& PLLSYS0_EXTSL_MASK
)
304 m
= sysInfo
->pllExtBusDiv
* sysInfo
->pllOpbDiv
* sysInfo
->pllFwdDivB
;
306 m
= sysInfo
->pllFbkDiv
* sysInfo
->pllFwdDivA
;
308 /* Now calculate the individual clocks */
309 sysInfo
->freqVCOMhz
= (m
* CONFIG_SYS_CLK_FREQ
) + (m
>>1);
310 sysInfo
->freqProcessor
= sysInfo
->freqVCOMhz
/sysInfo
->pllFwdDivA
;
311 sysInfo
->freqPLB
= sysInfo
->freqVCOMhz
/sysInfo
->pllFwdDivB
;
312 if( get_pvr() == PVR_440GP_RB
) /* Rev B divs an extra 2 -- geez! */
313 sysInfo
->freqPLB
>>= 1;
314 sysInfo
->freqOPB
= sysInfo
->freqPLB
/sysInfo
->pllOpbDiv
;
315 sysInfo
->freqEPB
= sysInfo
->freqOPB
/sysInfo
->pllExtBusDiv
;
319 void get_sys_info (sys_info_t
* sysInfo
)
327 unsigned long prbdv0
;
329 /* Extract configured divisors */
330 mfsdr( sdr_sdstp0
,strp0
);
331 mfsdr( sdr_sdstp1
,strp1
);
333 temp
= ((strp0
& PLLSYS0_FWD_DIV_A_MASK
) >> 8);
334 sysInfo
->pllFwdDivA
= temp
? temp
: 16 ;
335 temp
= ((strp0
& PLLSYS0_FWD_DIV_B_MASK
) >> 5);
336 sysInfo
->pllFwdDivB
= temp
? temp
: 8 ;
337 temp
= (strp0
& PLLSYS0_FB_DIV_MASK
) >> 12;
338 sysInfo
->pllFbkDiv
= temp
? temp
: 32;
339 temp
= (strp0
& PLLSYS0_OPB_DIV_MASK
);
340 sysInfo
->pllOpbDiv
= temp
? temp
: 4;
341 temp
= (strp1
& PLLSYS1_PERCLK_DIV_MASK
) >> 24;
342 sysInfo
->pllExtBusDiv
= temp
? temp
: 4;
343 prbdv0
= (strp0
>> 2) & 0x7;
345 /* Calculate 'M' based on feedback source */
346 temp
= (strp0
& PLLSYS0_SEL_MASK
) >> 27;
347 temp1
= (strp1
& PLLSYS1_LF_DIV_MASK
) >> 26;
348 lfdiv
= temp1
? temp1
: 64;
349 if (temp
== 0) { /* PLL output */
350 /* Figure which pll to use */
351 temp
= (strp0
& PLLSYS0_SRC_MASK
) >> 30;
353 m
= sysInfo
->pllFbkDiv
* lfdiv
* sysInfo
->pllFwdDivA
;
355 m
= sysInfo
->pllFbkDiv
* lfdiv
* sysInfo
->pllFwdDivB
;
357 else if (temp
== 1) /* CPU output */
358 m
= sysInfo
->pllFbkDiv
* sysInfo
->pllFwdDivA
;
360 m
= sysInfo
->pllExtBusDiv
* sysInfo
->pllOpbDiv
* sysInfo
->pllFwdDivB
;
362 /* Now calculate the individual clocks */
363 sysInfo
->freqVCOMhz
= (m
* CONFIG_SYS_CLK_FREQ
) + (m
>>1);
364 sysInfo
->freqProcessor
= sysInfo
->freqVCOMhz
/sysInfo
->pllFwdDivA
;
365 sysInfo
->freqPLB
= sysInfo
->freqVCOMhz
/sysInfo
->pllFwdDivB
/prbdv0
;
366 sysInfo
->freqOPB
= sysInfo
->freqPLB
/sysInfo
->pllOpbDiv
;
367 sysInfo
->freqEPB
= sysInfo
->freqOPB
/sysInfo
->pllExtBusDiv
;
372 ulong
get_OPB_freq (void)
376 get_sys_info (&sys_info
);
377 return sys_info
.freqOPB
;
380 #elif defined(CONFIG_XILINX_ML300)
381 extern void get_sys_info (sys_info_t
* sysInfo
);
382 extern ulong
get_PCI_freq (void);
384 #elif defined(CONFIG_AP1000)
385 void get_sys_info (sys_info_t
* sysInfo
) {
386 sysInfo
->freqProcessor
= 240 * 1000 * 1000;
387 sysInfo
->freqPLB
= 80 * 1000 * 1000;
388 sysInfo
->freqPCI
= 33 * 1000 * 1000;
391 #elif defined(CONFIG_405)
393 void get_sys_info (sys_info_t
* sysInfo
) {
395 sysInfo
->freqVCOMhz
=3125000;
396 sysInfo
->freqProcessor
=12*1000*1000;
397 sysInfo
->freqPLB
=50*1000*1000;
398 sysInfo
->freqPCI
=66*1000*1000;
402 #elif defined(CONFIG_405EP)
403 void get_sys_info (PPC405_SYS_INFO
* sysInfo
)
405 unsigned long pllmr0
;
406 unsigned long pllmr1
;
407 unsigned long sysClkPeriodPs
= ONE_BILLION
/ (CONFIG_SYS_CLK_FREQ
/ 1000);
409 unsigned long pllmr0_ccdv
;
412 * Read PLL Mode registers
414 pllmr0
= mfdcr (cpc0_pllmr0
);
415 pllmr1
= mfdcr (cpc0_pllmr1
);
418 * Determine forward divider A
420 sysInfo
->pllFwdDiv
= 8 - ((pllmr1
& PLLMR1_FWDVA_MASK
) >> 16);
423 * Determine forward divider B (should be equal to A)
425 sysInfo
->pllFwdDivB
= 8 - ((pllmr1
& PLLMR1_FWDVB_MASK
) >> 12);
430 sysInfo
->pllFbkDiv
= ((pllmr1
& PLLMR1_FBMUL_MASK
) >> 20);
431 if (sysInfo
->pllFbkDiv
== 0) {
432 sysInfo
->pllFbkDiv
= 16;
438 sysInfo
->pllPlbDiv
= ((pllmr0
& PLLMR0_CPU_TO_PLB_MASK
) >> 16) + 1;
443 sysInfo
->pllPciDiv
= (pllmr0
& PLLMR0_PCI_TO_PLB_MASK
) + 1;
446 * Determine EXTBUS_DIV.
448 sysInfo
->pllExtBusDiv
= ((pllmr0
& PLLMR0_EXB_TO_PLB_MASK
) >> 8) + 2;
453 sysInfo
->pllOpbDiv
= ((pllmr0
& PLLMR0_OPB_TO_PLB_MASK
) >> 12) + 1;
456 * Determine the M factor
458 m
= sysInfo
->pllFbkDiv
* sysInfo
->pllFwdDivB
;
461 * Determine VCO clock frequency
463 sysInfo
->freqVCOHz
= (1000000000000LL * (unsigned long long)m
) /
464 (unsigned long long)sysClkPeriodPs
;
467 * Determine CPU clock frequency
469 pllmr0_ccdv
= ((pllmr0
& PLLMR0_CPU_DIV_MASK
) >> 20) + 1;
470 if (pllmr1
& PLLMR1_SSCS_MASK
) {
472 * This is true if FWDVA == FWDVB:
473 * sysInfo->freqProcessor = (CONFIG_SYS_CLK_FREQ * sysInfo->pllFbkDiv)
476 sysInfo
->freqProcessor
= (CONFIG_SYS_CLK_FREQ
* sysInfo
->pllFbkDiv
* sysInfo
->pllFwdDivB
)
477 / sysInfo
->pllFwdDiv
/ pllmr0_ccdv
;
479 sysInfo
->freqProcessor
= CONFIG_SYS_CLK_FREQ
/ pllmr0_ccdv
;
483 * Determine PLB clock frequency
485 sysInfo
->freqPLB
= sysInfo
->freqProcessor
/ sysInfo
->pllPlbDiv
;
489 /********************************************
491 * return OPB bus freq in Hz
492 *********************************************/
493 ulong
get_OPB_freq (void)
497 PPC405_SYS_INFO sys_info
;
499 get_sys_info (&sys_info
);
500 val
= sys_info
.freqPLB
/ sys_info
.pllOpbDiv
;
506 /********************************************
508 * return PCI bus freq in Hz
509 *********************************************/
510 ulong
get_PCI_freq (void)
513 PPC405_SYS_INFO sys_info
;
515 get_sys_info (&sys_info
);
516 val
= sys_info
.freqPLB
/ sys_info
.pllPciDiv
;
522 int get_clocks (void)
524 #if defined(CONFIG_405GP) || defined(CONFIG_405CR) || defined(CONFIG_440) || defined(CONFIG_405) || defined(CONFIG_405EP)
525 DECLARE_GLOBAL_DATA_PTR
;
529 get_sys_info (&sys_info
);
530 gd
->cpu_clk
= sys_info
.freqProcessor
;
531 gd
->bus_clk
= sys_info
.freqPLB
;
533 #endif /* defined(CONFIG_405GP) || defined(CONFIG_405CR) */
536 DECLARE_GLOBAL_DATA_PTR
;
538 gd
->cpu_clk
= 66000000;
539 gd
->bus_clk
= 66000000;
545 /********************************************
547 * return PLB bus freq in Hz
548 *********************************************/
549 ulong
get_bus_freq (ulong dummy
)
553 #if defined(CONFIG_405GP) || defined(CONFIG_405CR) || defined(CONFIG_405) || defined(CONFIG_440) || defined(CONFIG_405EP)
556 get_sys_info (&sys_info
);
557 val
= sys_info
.freqPLB
;
559 #elif defined(CONFIG_IOP480)
564 # error get_bus_freq() not implemented