Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

sm501: add support for the SM502 programmable PLL

SM502 has a programmable PLL which can provide the panel pixel clock instead
of the 288MHz and 336MHz PLLs.

[akpm@linux-foundation.org: coding-style fixes]
Signed-off-by: Ville Syrjala <syrjala@sci.fi>
Cc: Ben Dooks <ben-linux@fluff.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>

authored by

Ville Syrjala and committed by
Linus Torvalds
3149be50 245904a4

+133 -36
+128 -35
drivers/mfd/sm501.c
··· 48 48 unsigned int pdev_id; 49 49 unsigned int irq; 50 50 void __iomem *regs; 51 + unsigned int rev; 51 52 }; 52 53 53 54 #define MHZ (1000 * 1000) ··· 418 417 unsigned long mclk; 419 418 int divider; 420 419 int shift; 420 + unsigned int m, n, k; 421 421 }; 422 + 423 + /* sm501_calc_clock 424 + * 425 + * Calculates the nearest discrete clock frequency that 426 + * can be achieved with the specified input clock. 427 + * the maximum divisor is 3 or 5 428 + */ 429 + 430 + static int sm501_calc_clock(unsigned long freq, 431 + struct sm501_clock *clock, 432 + int max_div, 433 + unsigned long mclk, 434 + long *best_diff) 435 + { 436 + int ret = 0; 437 + int divider; 438 + int shift; 439 + long diff; 440 + 441 + /* try dividers 1 and 3 for CRT and for panel, 442 + try divider 5 for panel only.*/ 443 + 444 + for (divider = 1; divider <= max_div; divider += 2) { 445 + /* try all 8 shift values.*/ 446 + for (shift = 0; shift < 8; shift++) { 447 + /* Calculate difference to requested clock */ 448 + diff = sm501fb_round_div(mclk, divider << shift) - freq; 449 + if (diff < 0) 450 + diff = -diff; 451 + 452 + /* If it is less than the current, use it */ 453 + if (diff < *best_diff) { 454 + *best_diff = diff; 455 + 456 + clock->mclk = mclk; 457 + clock->divider = divider; 458 + clock->shift = shift; 459 + ret = 1; 460 + } 461 + } 462 + } 463 + 464 + return ret; 465 + } 466 + 467 + /* sm501_calc_pll 468 + * 469 + * Calculates the nearest discrete clock frequency that can be 470 + * achieved using the programmable PLL. 471 + * the maximum divisor is 3 or 5 472 + */ 473 + 474 + static unsigned long sm501_calc_pll(unsigned long freq, 475 + struct sm501_clock *clock, 476 + int max_div) 477 + { 478 + unsigned long mclk; 479 + unsigned int m, n, k; 480 + long best_diff = 999999999; 481 + 482 + /* 483 + * The SM502 datasheet doesn't specify the min/max values for M and N. 484 + * N = 1 at least doesn't work in practice. 485 + */ 486 + for (m = 2; m <= 255; m++) { 487 + for (n = 2; n <= 127; n++) { 488 + for (k = 0; k <= 1; k++) { 489 + mclk = (24000000UL * m / n) >> k; 490 + 491 + if (sm501_calc_clock(freq, clock, max_div, 492 + mclk, &best_diff)) { 493 + clock->m = m; 494 + clock->n = n; 495 + clock->k = k; 496 + } 497 + } 498 + } 499 + } 500 + 501 + /* Return best clock. */ 502 + return clock->mclk / (clock->divider << clock->shift); 503 + } 422 504 423 505 /* sm501_select_clock 424 506 * 425 - * selects nearest discrete clock frequency the SM501 can achive 507 + * Calculates the nearest discrete clock frequency that can be 508 + * achieved using the 288MHz and 336MHz PLLs. 426 509 * the maximum divisor is 3 or 5 427 510 */ 511 + 428 512 static unsigned long sm501_select_clock(unsigned long freq, 429 513 struct sm501_clock *clock, 430 514 int max_div) 431 515 { 432 516 unsigned long mclk; 433 - int divider; 434 - int shift; 435 - long diff; 436 517 long best_diff = 999999999; 437 518 438 519 /* Try 288MHz and 336MHz clocks. */ 439 520 for (mclk = 288000000; mclk <= 336000000; mclk += 48000000) { 440 - /* try dividers 1 and 3 for CRT and for panel, 441 - try divider 5 for panel only.*/ 442 - 443 - for (divider = 1; divider <= max_div; divider += 2) { 444 - /* try all 8 shift values.*/ 445 - for (shift = 0; shift < 8; shift++) { 446 - /* Calculate difference to requested clock */ 447 - diff = sm501fb_round_div(mclk, divider << shift) - freq; 448 - if (diff < 0) 449 - diff = -diff; 450 - 451 - /* If it is less than the current, use it */ 452 - if (diff < best_diff) { 453 - best_diff = diff; 454 - 455 - clock->mclk = mclk; 456 - clock->divider = divider; 457 - clock->shift = shift; 458 - } 459 - } 460 - } 521 + sm501_calc_clock(freq, clock, max_div, mclk, &best_diff); 461 522 } 462 523 463 524 /* Return best clock. */ ··· 541 478 unsigned long gate = readl(sm->regs + SM501_CURRENT_GATE); 542 479 unsigned long clock = readl(sm->regs + SM501_CURRENT_CLOCK); 543 480 unsigned char reg; 481 + unsigned int pll_reg = 0; 544 482 unsigned long sm501_freq; /* the actual frequency acheived */ 545 483 546 484 struct sm501_clock to; ··· 556 492 * requested frequency the value must be multiplied by 557 493 * 2. This clock also has an additional pre divisor */ 558 494 559 - sm501_freq = (sm501_select_clock(2 * req_freq, &to, 5) / 2); 560 - reg=to.shift & 0x07;/* bottom 3 bits are shift */ 561 - if (to.divider == 3) 562 - reg |= 0x08; /* /3 divider required */ 563 - else if (to.divider == 5) 564 - reg |= 0x10; /* /5 divider required */ 565 - if (to.mclk != 288000000) 566 - reg |= 0x20; /* which mclk pll is source */ 495 + if (sm->rev >= 0xC0) { 496 + /* SM502 -> use the programmable PLL */ 497 + sm501_freq = (sm501_calc_pll(2 * req_freq, 498 + &to, 5) / 2); 499 + reg = to.shift & 0x07;/* bottom 3 bits are shift */ 500 + if (to.divider == 3) 501 + reg |= 0x08; /* /3 divider required */ 502 + else if (to.divider == 5) 503 + reg |= 0x10; /* /5 divider required */ 504 + reg |= 0x40; /* select the programmable PLL */ 505 + pll_reg = 0x20000 | (to.k << 15) | (to.n << 8) | to.m; 506 + } else { 507 + sm501_freq = (sm501_select_clock(2 * req_freq, 508 + &to, 5) / 2); 509 + reg = to.shift & 0x07;/* bottom 3 bits are shift */ 510 + if (to.divider == 3) 511 + reg |= 0x08; /* /3 divider required */ 512 + else if (to.divider == 5) 513 + reg |= 0x10; /* /5 divider required */ 514 + if (to.mclk != 288000000) 515 + reg |= 0x20; /* which mclk pll is source */ 516 + } 567 517 break; 568 518 569 519 case SM501_CLOCK_V2XCLK: ··· 638 560 } 639 561 640 562 writel(mode, sm->regs + SM501_POWER_MODE_CONTROL); 563 + 564 + if (pll_reg) 565 + writel(pll_reg, sm->regs + SM501_PROGRAMMABLE_PLL_CONTROL); 566 + 641 567 sm501_sync_regs(sm); 642 568 643 569 dev_info(sm->dev, "gate %08lx, clock %08lx, mode %08lx\n", ··· 662 580 * finds the closest available frequency for a given clock 663 581 */ 664 582 665 - unsigned long sm501_find_clock(int clksrc, 583 + unsigned long sm501_find_clock(struct device *dev, 584 + int clksrc, 666 585 unsigned long req_freq) 667 586 { 587 + struct sm501_devdata *sm = dev_get_drvdata(dev); 668 588 unsigned long sm501_freq; /* the frequency achiveable by the 501 */ 669 589 struct sm501_clock to; 670 590 671 591 switch (clksrc) { 672 592 case SM501_CLOCK_P2XCLK: 673 - sm501_freq = (sm501_select_clock(2 * req_freq, &to, 5) / 2); 593 + if (sm->rev >= 0xC0) { 594 + /* SM502 -> use the programmable PLL */ 595 + sm501_freq = (sm501_calc_pll(2 * req_freq, 596 + &to, 5) / 2); 597 + } else { 598 + sm501_freq = (sm501_select_clock(2 * req_freq, 599 + &to, 5) / 2); 600 + } 674 601 break; 675 602 676 603 case SM501_CLOCK_V2XCLK: ··· 985 894 986 895 dev_info(sm->dev, "SM501 At %p: Version %08lx, %ld Mb, IRQ %d\n", 987 896 sm->regs, devid, (unsigned long)mem_avail >> 20, sm->irq); 897 + 898 + sm->rev = devid & SM501_DEVICEID_REVMASK; 988 899 989 900 sm501_dump_gate(sm); 990 901
+3
include/linux/sm501-regs.h
··· 129 129 130 130 #define SM501_DEVICEID_SM501 (0x05010000) 131 131 #define SM501_DEVICEID_IDMASK (0xffff0000) 132 + #define SM501_DEVICEID_REVMASK (0x000000ff) 132 133 133 134 #define SM501_PLLCLOCK_COUNT (0x000064) 134 135 #define SM501_MISC_TIMING (0x000068) 135 136 #define SM501_CURRENT_SDRAM_CLOCK (0x00006C) 137 + 138 + #define SM501_PROGRAMMABLE_PLL_CONTROL (0x000074) 136 139 137 140 /* GPIO base */ 138 141 #define SM501_GPIO (0x010000)
+2 -1
include/linux/sm501.h
··· 24 24 extern unsigned long sm501_set_clock(struct device *dev, 25 25 int clksrc, unsigned long freq); 26 26 27 - extern unsigned long sm501_find_clock(int clksrc, unsigned long req_freq); 27 + extern unsigned long sm501_find_clock(struct device *dev, 28 + int clksrc, unsigned long req_freq); 28 29 29 30 /* sm501_misc_control 30 31 *