diff options
Diffstat (limited to 'arch/powerpc/boot/4xx.c')
-rw-r--r-- | arch/powerpc/boot/4xx.c | 272 |
1 files changed, 204 insertions, 68 deletions
diff --git a/arch/powerpc/boot/4xx.c b/arch/powerpc/boot/4xx.c index d16ea10d7546..1a83efe274c1 100644 --- a/arch/powerpc/boot/4xx.c +++ b/arch/powerpc/boot/4xx.c | |||
@@ -275,89 +275,225 @@ void ibm4xx_fixup_ebc_ranges(const char *ebc) | |||
275 | setprop(devp, "ranges", ranges, (p - ranges) * sizeof(u32)); | 275 | setprop(devp, "ranges", ranges, (p - ranges) * sizeof(u32)); |
276 | } | 276 | } |
277 | 277 | ||
278 | #define SPRN_CCR1 0x378 | 278 | /* Calculate 440GP clocks */ |
279 | void ibm440ep_fixup_clocks(unsigned int sysclk, unsigned int ser_clk) | 279 | void ibm440gp_fixup_clocks(unsigned int sys_clk, unsigned int ser_clk) |
280 | { | 280 | { |
281 | u32 cpu, plb, opb, ebc, tb, uart0, m, vco; | 281 | u32 sys0 = mfdcr(DCRN_CPC0_SYS0); |
282 | u32 reg; | 282 | u32 cr0 = mfdcr(DCRN_CPC0_CR0); |
283 | u32 fwdva, fwdvb, fbdv, lfbdv, opbdv0, perdv0, spcid0, prbdv0, tmp; | 283 | u32 cpu, plb, opb, ebc, tb, uart0, uart1, m; |
284 | 284 | u32 opdv = CPC0_SYS0_OPDV(sys0); | |
285 | mtdcr(DCRN_CPR0_ADDR, CPR0_PLLD0); | 285 | u32 epdv = CPC0_SYS0_EPDV(sys0); |
286 | reg = mfdcr(DCRN_CPR0_DATA); | 286 | |
287 | tmp = (reg & 0x000F0000) >> 16; | 287 | if (sys0 & CPC0_SYS0_BYPASS) { |
288 | fwdva = tmp ? tmp : 16; | 288 | /* Bypass system PLL */ |
289 | tmp = (reg & 0x00000700) >> 8; | 289 | cpu = plb = sys_clk; |
290 | fwdvb = tmp ? tmp : 8; | 290 | } else { |
291 | tmp = (reg & 0x1F000000) >> 24; | 291 | if (sys0 & CPC0_SYS0_EXTSL) |
292 | fbdv = tmp ? tmp : 32; | 292 | /* PerClk */ |
293 | lfbdv = (reg & 0x0000007F); | 293 | m = CPC0_SYS0_FWDVB(sys0) * opdv * epdv; |
294 | |||
295 | mtdcr(DCRN_CPR0_ADDR, CPR0_OPBD0); | ||
296 | reg = mfdcr(DCRN_CPR0_DATA); | ||
297 | tmp = (reg & 0x03000000) >> 24; | ||
298 | opbdv0 = tmp ? tmp : 4; | ||
299 | |||
300 | mtdcr(DCRN_CPR0_ADDR, CPR0_PERD0); | ||
301 | reg = mfdcr(DCRN_CPR0_DATA); | ||
302 | tmp = (reg & 0x07000000) >> 24; | ||
303 | perdv0 = tmp ? tmp : 8; | ||
304 | |||
305 | mtdcr(DCRN_CPR0_ADDR, CPR0_PRIMBD0); | ||
306 | reg = mfdcr(DCRN_CPR0_DATA); | ||
307 | tmp = (reg & 0x07000000) >> 24; | ||
308 | prbdv0 = tmp ? tmp : 8; | ||
309 | |||
310 | mtdcr(DCRN_CPR0_ADDR, CPR0_SCPID); | ||
311 | reg = mfdcr(DCRN_CPR0_DATA); | ||
312 | tmp = (reg & 0x03000000) >> 24; | ||
313 | spcid0 = tmp ? tmp : 4; | ||
314 | |||
315 | /* Calculate M */ | ||
316 | mtdcr(DCRN_CPR0_ADDR, CPR0_PLLC0); | ||
317 | reg = mfdcr(DCRN_CPR0_DATA); | ||
318 | tmp = (reg & 0x03000000) >> 24; | ||
319 | if (tmp == 0) { /* PLL output */ | ||
320 | tmp = (reg & 0x20000000) >> 29; | ||
321 | if (!tmp) /* PLLOUTA */ | ||
322 | m = fbdv * lfbdv * fwdva; | ||
323 | else | 294 | else |
324 | m = fbdv * lfbdv * fwdvb; | 295 | /* CPU clock */ |
296 | m = CPC0_SYS0_FBDV(sys0) * CPC0_SYS0_FWDVA(sys0); | ||
297 | cpu = sys_clk * m / CPC0_SYS0_FWDVA(sys0); | ||
298 | plb = sys_clk * m / CPC0_SYS0_FWDVB(sys0); | ||
325 | } | 299 | } |
326 | else if (tmp == 1) /* CPU output */ | 300 | |
327 | m = fbdv * fwdva; | 301 | opb = plb / opdv; |
302 | ebc = opb / epdv; | ||
303 | |||
304 | /* FIXME: Check if this is for all 440GP, or just Ebony */ | ||
305 | if ((mfpvr() & 0xf0000fff) == 0x40000440) | ||
306 | /* Rev. B 440GP, use external system clock */ | ||
307 | tb = sys_clk; | ||
328 | else | 308 | else |
329 | m = perdv0 * opbdv0 * fwdvb; | 309 | /* Rev. C 440GP, errata force us to use internal clock */ |
310 | tb = cpu; | ||
330 | 311 | ||
331 | vco = (m * sysclk) + (m >> 1); | 312 | if (cr0 & CPC0_CR0_U0EC) |
332 | cpu = vco / fwdva; | 313 | /* External UART clock */ |
333 | plb = vco / fwdvb / prbdv0; | 314 | uart0 = ser_clk; |
334 | opb = plb / opbdv0; | 315 | else |
335 | ebc = plb / perdv0; | 316 | /* Internal UART clock */ |
317 | uart0 = plb / CPC0_CR0_UDIV(cr0); | ||
318 | |||
319 | if (cr0 & CPC0_CR0_U1EC) | ||
320 | /* External UART clock */ | ||
321 | uart1 = ser_clk; | ||
322 | else | ||
323 | /* Internal UART clock */ | ||
324 | uart1 = plb / CPC0_CR0_UDIV(cr0); | ||
325 | |||
326 | printf("PPC440GP: SysClk = %dMHz (%x)\n\r", | ||
327 | (sys_clk + 500000) / 1000000, sys_clk); | ||
328 | |||
329 | dt_fixup_cpu_clocks(cpu, tb, 0); | ||
330 | |||
331 | dt_fixup_clock("/plb", plb); | ||
332 | dt_fixup_clock("/plb/opb", opb); | ||
333 | dt_fixup_clock("/plb/opb/ebc", ebc); | ||
334 | dt_fixup_clock("/plb/opb/serial@40000200", uart0); | ||
335 | dt_fixup_clock("/plb/opb/serial@40000300", uart1); | ||
336 | } | ||
337 | |||
338 | #define SPRN_CCR1 0x378 | ||
339 | |||
340 | static inline u32 __fix_zero(u32 v, u32 def) | ||
341 | { | ||
342 | return v ? v : def; | ||
343 | } | ||
344 | |||
345 | static unsigned int __ibm440eplike_fixup_clocks(unsigned int sys_clk, | ||
346 | unsigned int tmr_clk, | ||
347 | int per_clk_from_opb) | ||
348 | { | ||
349 | /* PLL config */ | ||
350 | u32 pllc = CPR0_READ(DCRN_CPR0_PLLC); | ||
351 | u32 plld = CPR0_READ(DCRN_CPR0_PLLD); | ||
352 | |||
353 | /* Dividers */ | ||
354 | u32 fbdv = __fix_zero((plld >> 24) & 0x1f, 32); | ||
355 | u32 fwdva = __fix_zero((plld >> 16) & 0xf, 16); | ||
356 | u32 fwdvb = __fix_zero((plld >> 8) & 7, 8); | ||
357 | u32 lfbdv = __fix_zero(plld & 0x3f, 64); | ||
358 | u32 pradv0 = __fix_zero((CPR0_READ(DCRN_CPR0_PRIMAD) >> 24) & 7, 8); | ||
359 | u32 prbdv0 = __fix_zero((CPR0_READ(DCRN_CPR0_PRIMBD) >> 24) & 7, 8); | ||
360 | u32 opbdv0 = __fix_zero((CPR0_READ(DCRN_CPR0_OPBD) >> 24) & 3, 4); | ||
361 | u32 perdv0 = __fix_zero((CPR0_READ(DCRN_CPR0_PERD) >> 24) & 3, 4); | ||
362 | |||
363 | /* Input clocks for primary dividers */ | ||
364 | u32 clk_a, clk_b; | ||
365 | |||
366 | /* Resulting clocks */ | ||
367 | u32 cpu, plb, opb, ebc, vco; | ||
368 | |||
369 | /* Timebase */ | ||
370 | u32 ccr1, tb = tmr_clk; | ||
371 | |||
372 | if (pllc & 0x40000000) { | ||
373 | u32 m; | ||
374 | |||
375 | /* Feedback path */ | ||
376 | switch ((pllc >> 24) & 7) { | ||
377 | case 0: | ||
378 | /* PLLOUTx */ | ||
379 | m = ((pllc & 0x20000000) ? fwdvb : fwdva) * lfbdv; | ||
380 | break; | ||
381 | case 1: | ||
382 | /* CPU */ | ||
383 | m = fwdva * pradv0; | ||
384 | break; | ||
385 | case 5: | ||
386 | /* PERClk */ | ||
387 | m = fwdvb * prbdv0 * opbdv0 * perdv0; | ||
388 | break; | ||
389 | default: | ||
390 | printf("WARNING ! Invalid PLL feedback source !\n"); | ||
391 | goto bypass; | ||
392 | } | ||
393 | m *= fbdv; | ||
394 | vco = sys_clk * m; | ||
395 | clk_a = vco / fwdva; | ||
396 | clk_b = vco / fwdvb; | ||
397 | } else { | ||
398 | bypass: | ||
399 | /* Bypass system PLL */ | ||
400 | vco = 0; | ||
401 | clk_a = clk_b = sys_clk; | ||
402 | } | ||
336 | 403 | ||
337 | /* FIXME */ | 404 | cpu = clk_a / pradv0; |
338 | uart0 = ser_clk; | 405 | plb = clk_b / prbdv0; |
406 | opb = plb / opbdv0; | ||
407 | ebc = (per_clk_from_opb ? opb : plb) / perdv0; | ||
339 | 408 | ||
340 | /* Figure out timebase. Either CPU or default TmrClk */ | 409 | /* Figure out timebase. Either CPU or default TmrClk */ |
341 | asm volatile ( | 410 | ccr1 = mfspr(SPRN_CCR1); |
342 | "mfspr %0,%1\n" | 411 | |
343 | : | 412 | /* If passed a 0 tmr_clk, force CPU clock */ |
344 | "=&r"(reg) : "i"(SPRN_CCR1)); | 413 | if (tb == 0) { |
345 | if (reg & 0x0080) | 414 | ccr1 &= ~0x80u; |
346 | tb = 25000000; /* TmrClk is 25MHz */ | 415 | mtspr(SPRN_CCR1, ccr1); |
347 | else | 416 | } |
417 | if ((ccr1 & 0x0080) == 0) | ||
348 | tb = cpu; | 418 | tb = cpu; |
349 | 419 | ||
350 | dt_fixup_cpu_clocks(cpu, tb, 0); | 420 | dt_fixup_cpu_clocks(cpu, tb, 0); |
351 | dt_fixup_clock("/plb", plb); | 421 | dt_fixup_clock("/plb", plb); |
352 | dt_fixup_clock("/plb/opb", opb); | 422 | dt_fixup_clock("/plb/opb", opb); |
353 | dt_fixup_clock("/plb/opb/ebc", ebc); | 423 | dt_fixup_clock("/plb/opb/ebc", ebc); |
354 | dt_fixup_clock("/plb/opb/serial@ef600300", uart0); | 424 | |
355 | dt_fixup_clock("/plb/opb/serial@ef600400", uart0); | 425 | return plb; |
356 | dt_fixup_clock("/plb/opb/serial@ef600500", uart0); | 426 | } |
357 | dt_fixup_clock("/plb/opb/serial@ef600600", uart0); | 427 | |
428 | static void eplike_fixup_uart_clk(int index, const char *path, | ||
429 | unsigned int ser_clk, | ||
430 | unsigned int plb_clk) | ||
431 | { | ||
432 | unsigned int sdr; | ||
433 | unsigned int clock; | ||
434 | |||
435 | switch (index) { | ||
436 | case 0: | ||
437 | sdr = SDR0_READ(DCRN_SDR0_UART0); | ||
438 | break; | ||
439 | case 1: | ||
440 | sdr = SDR0_READ(DCRN_SDR0_UART1); | ||
441 | break; | ||
442 | case 2: | ||
443 | sdr = SDR0_READ(DCRN_SDR0_UART2); | ||
444 | break; | ||
445 | case 3: | ||
446 | sdr = SDR0_READ(DCRN_SDR0_UART3); | ||
447 | break; | ||
448 | default: | ||
449 | return; | ||
450 | } | ||
451 | |||
452 | if (sdr & 0x00800000u) | ||
453 | clock = ser_clk; | ||
454 | else | ||
455 | clock = plb_clk / __fix_zero(sdr & 0xff, 256); | ||
456 | |||
457 | dt_fixup_clock(path, clock); | ||
458 | } | ||
459 | |||
460 | void ibm440ep_fixup_clocks(unsigned int sys_clk, | ||
461 | unsigned int ser_clk, | ||
462 | unsigned int tmr_clk) | ||
463 | { | ||
464 | unsigned int plb_clk = __ibm440eplike_fixup_clocks(sys_clk, tmr_clk, 0); | ||
465 | |||
466 | /* serial clocks beed fixup based on int/ext */ | ||
467 | eplike_fixup_uart_clk(0, "/plb/opb/serial@ef600300", ser_clk, plb_clk); | ||
468 | eplike_fixup_uart_clk(1, "/plb/opb/serial@ef600400", ser_clk, plb_clk); | ||
469 | eplike_fixup_uart_clk(2, "/plb/opb/serial@ef600500", ser_clk, plb_clk); | ||
470 | eplike_fixup_uart_clk(3, "/plb/opb/serial@ef600600", ser_clk, plb_clk); | ||
471 | } | ||
472 | |||
473 | void ibm440gx_fixup_clocks(unsigned int sys_clk, | ||
474 | unsigned int ser_clk, | ||
475 | unsigned int tmr_clk) | ||
476 | { | ||
477 | unsigned int plb_clk = __ibm440eplike_fixup_clocks(sys_clk, tmr_clk, 1); | ||
478 | |||
479 | /* serial clocks beed fixup based on int/ext */ | ||
480 | eplike_fixup_uart_clk(0, "/plb/opb/serial@40000200", ser_clk, plb_clk); | ||
481 | eplike_fixup_uart_clk(1, "/plb/opb/serial@40000300", ser_clk, plb_clk); | ||
482 | } | ||
483 | |||
484 | void ibm440spe_fixup_clocks(unsigned int sys_clk, | ||
485 | unsigned int ser_clk, | ||
486 | unsigned int tmr_clk) | ||
487 | { | ||
488 | unsigned int plb_clk = __ibm440eplike_fixup_clocks(sys_clk, tmr_clk, 1); | ||
489 | |||
490 | /* serial clocks beed fixup based on int/ext */ | ||
491 | eplike_fixup_uart_clk(0, "/plb/opb/serial@10000200", ser_clk, plb_clk); | ||
492 | eplike_fixup_uart_clk(1, "/plb/opb/serial@10000300", ser_clk, plb_clk); | ||
493 | eplike_fixup_uart_clk(2, "/plb/opb/serial@10000600", ser_clk, plb_clk); | ||
358 | } | 494 | } |
359 | 495 | ||
360 | void ibm405gp_fixup_clocks(unsigned int sysclk, unsigned int ser_clk) | 496 | void ibm405gp_fixup_clocks(unsigned int sys_clk, unsigned int ser_clk) |
361 | { | 497 | { |
362 | u32 pllmr = mfdcr(DCRN_CPC0_PLLMR); | 498 | u32 pllmr = mfdcr(DCRN_CPC0_PLLMR); |
363 | u32 cpc0_cr0 = mfdcr(DCRN_405_CPC0_CR0); | 499 | u32 cpc0_cr0 = mfdcr(DCRN_405_CPC0_CR0); |
@@ -374,7 +510,7 @@ void ibm405gp_fixup_clocks(unsigned int sysclk, unsigned int ser_clk) | |||
374 | 510 | ||
375 | m = fwdv * fbdv * cbdv; | 511 | m = fwdv * fbdv * cbdv; |
376 | 512 | ||
377 | cpu = sysclk * m / fwdv; | 513 | cpu = sys_clk * m / fwdv; |
378 | plb = cpu / cbdv; | 514 | plb = cpu / cbdv; |
379 | opb = plb / opdv; | 515 | opb = plb / opdv; |
380 | ebc = plb / epdv; | 516 | ebc = plb / epdv; |