aboutsummaryrefslogtreecommitdiffstats
path: root/arch/powerpc/boot/4xx.c
diff options
context:
space:
mode:
authorBenjamin Herrenschmidt <benh@kernel.crashing.org>2007-12-20 23:39:33 -0500
committerJosh Boyer <jwboyer@linux.vnet.ibm.com>2007-12-23 14:14:48 -0500
commit190de005384204ba4dc339c3d05f8d40f6b16643 (patch)
tree83fff853bdde49ec29e0fb488d8bf9578bec627a /arch/powerpc/boot/4xx.c
parentbc0b4e7ffb528282df5f8ba9c7c3f60135603e9e (diff)
[POWERPC] 4xx: Rework clock probing in boot wrapper
This reworks the boot wrapper library function that probes the chip clocks. Better separate the base function that is used on 440GX,SPe,EP,... from the uart fixups as those need different device-tree path on different processors. Also, rework the function itself based on the arch/ppc code from Eugene Surovegin which I find more readable, and which handles one more bypass case. Also handle the subtle difference between 440EP/EPx and 440SPe/GX, on the former, PerClk is derived from the PLB clock while on the later, it's derived from the OPB. Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org> Signed-off-by: Josh Boyer <jwboyer@linux.vnet.ibm.com>
Diffstat (limited to 'arch/powerpc/boot/4xx.c')
-rw-r--r--arch/powerpc/boot/4xx.c272
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 */
279void ibm440ep_fixup_clocks(unsigned int sysclk, unsigned int ser_clk) 279void 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
340static inline u32 __fix_zero(u32 v, u32 def)
341{
342 return v ? v : def;
343}
344
345static 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 {
398bypass:
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
428static 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
460void 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
473void 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
484void 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
360void ibm405gp_fixup_clocks(unsigned int sysclk, unsigned int ser_clk) 496void 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;