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