aboutsummaryrefslogtreecommitdiffstats
path: root/arch/arm/plat-s3c24xx/pm.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/arm/plat-s3c24xx/pm.c')
-rw-r--r--arch/arm/plat-s3c24xx/pm.c247
1 files changed, 214 insertions, 33 deletions
diff --git a/arch/arm/plat-s3c24xx/pm.c b/arch/arm/plat-s3c24xx/pm.c
index 4fdb3117744f..bf5581a9aeea 100644
--- a/arch/arm/plat-s3c24xx/pm.c
+++ b/arch/arm/plat-s3c24xx/pm.c
@@ -83,38 +83,39 @@ static struct sleep_save core_save[] = {
83 SAVE_ITEM(S3C2410_REFRESH), 83 SAVE_ITEM(S3C2410_REFRESH),
84}; 84};
85 85
86static struct sleep_save gpio_save[] = { 86static struct gpio_sleep {
87 SAVE_ITEM(S3C2410_GPACON), 87 void __iomem *base;
88 SAVE_ITEM(S3C2410_GPADAT), 88 unsigned int gpcon;
89 89 unsigned int gpdat;
90 SAVE_ITEM(S3C2410_GPBCON), 90 unsigned int gpup;
91 SAVE_ITEM(S3C2410_GPBDAT), 91} gpio_save[] = {
92 SAVE_ITEM(S3C2410_GPBUP), 92 [0] = {
93 93 .base = S3C2410_GPACON,
94 SAVE_ITEM(S3C2410_GPCCON), 94 },
95 SAVE_ITEM(S3C2410_GPCDAT), 95 [1] = {
96 SAVE_ITEM(S3C2410_GPCUP), 96 .base = S3C2410_GPBCON,
97 97 },
98 SAVE_ITEM(S3C2410_GPDCON), 98 [2] = {
99 SAVE_ITEM(S3C2410_GPDDAT), 99 .base = S3C2410_GPCCON,
100 SAVE_ITEM(S3C2410_GPDUP), 100 },
101 101 [3] = {
102 SAVE_ITEM(S3C2410_GPECON), 102 .base = S3C2410_GPDCON,
103 SAVE_ITEM(S3C2410_GPEDAT), 103 },
104 SAVE_ITEM(S3C2410_GPEUP), 104 [4] = {
105 105 .base = S3C2410_GPECON,
106 SAVE_ITEM(S3C2410_GPFCON), 106 },
107 SAVE_ITEM(S3C2410_GPFDAT), 107 [5] = {
108 SAVE_ITEM(S3C2410_GPFUP), 108 .base = S3C2410_GPFCON,
109 109 },
110 SAVE_ITEM(S3C2410_GPGCON), 110 [6] = {
111 SAVE_ITEM(S3C2410_GPGDAT), 111 .base = S3C2410_GPGCON,
112 SAVE_ITEM(S3C2410_GPGUP), 112 },
113 113 [7] = {
114 SAVE_ITEM(S3C2410_GPHCON), 114 .base = S3C2410_GPHCON,
115 SAVE_ITEM(S3C2410_GPHDAT), 115 },
116 SAVE_ITEM(S3C2410_GPHUP), 116};
117 117
118static struct sleep_save misc_save[] = {
118 SAVE_ITEM(S3C2410_DCLKCON), 119 SAVE_ITEM(S3C2410_DCLKCON),
119}; 120};
120 121
@@ -486,6 +487,184 @@ static void s3c2410_pm_configure_extint(void)
486 } 487 }
487} 488}
488 489
490/* offsets for CON/DAT/UP registers */
491
492#define OFFS_CON (S3C2410_GPACON - S3C2410_GPACON)
493#define OFFS_DAT (S3C2410_GPADAT - S3C2410_GPACON)
494#define OFFS_UP (S3C2410_GPBUP - S3C2410_GPBCON)
495
496/* s3c2410_pm_save_gpios()
497 *
498 * Save the state of the GPIOs
499 */
500
501static void s3c2410_pm_save_gpios(void)
502{
503 struct gpio_sleep *gps = gpio_save;
504 unsigned int gpio;
505
506 for (gpio = 0; gpio < ARRAY_SIZE(gpio_save); gpio++, gps++) {
507 void __iomem *base = gps->base;
508
509 gps->gpcon = __raw_readl(base + OFFS_CON);
510 gps->gpdat = __raw_readl(base + OFFS_DAT);
511
512 if (gpio > 0)
513 gps->gpup = __raw_readl(base + OFFS_UP);
514
515 }
516}
517
518/* Test whether the given masked+shifted bits of an GPIO configuration
519 * are one of the SFN (special function) modes. */
520
521static inline int is_sfn(unsigned long con)
522{
523 return (con == 2 || con == 3);
524}
525
526/* Test if the given masked+shifted GPIO configuration is an input */
527
528static inline int is_in(unsigned long con)
529{
530 return con == 0;
531}
532
533/* Test if the given masked+shifted GPIO configuration is an output */
534
535static inline int is_out(unsigned long con)
536{
537 return con == 1;
538}
539
540/* s3c2410_pm_restore_gpio()
541 *
542 * Restore one of the GPIO banks that was saved during suspend. This is
543 * not as simple as once thought, due to the possibility of glitches
544 * from the order that the CON and DAT registers are set in.
545 *
546 * The three states the pin can be are {IN,OUT,SFN} which gives us 9
547 * combinations of changes to check. Three of these, if the pin stays
548 * in the same configuration can be discounted. This leaves us with
549 * the following:
550 *
551 * { IN => OUT } Change DAT first
552 * { IN => SFN } Change CON first
553 * { OUT => SFN } Change CON first, so new data will not glitch
554 * { OUT => IN } Change CON first, so new data will not glitch
555 * { SFN => IN } Change CON first
556 * { SFN => OUT } Change DAT first, so new data will not glitch [1]
557 *
558 * We do not currently deal with the UP registers as these control
559 * weak resistors, so a small delay in change should not need to bring
560 * these into the calculations.
561 *
562 * [1] this assumes that writing to a pin DAT whilst in SFN will set the
563 * state for when it is next output.
564 */
565
566static void s3c2410_pm_restore_gpio(int index, struct gpio_sleep *gps)
567{
568 void __iomem *base = gps->base;
569 unsigned long gps_gpcon = gps->gpcon;
570 unsigned long gps_gpdat = gps->gpdat;
571 unsigned long old_gpcon;
572 unsigned long old_gpdat;
573 unsigned long old_gpup = 0x0;
574 unsigned long gpcon;
575 int nr;
576
577 old_gpcon = __raw_readl(base + OFFS_CON);
578 old_gpdat = __raw_readl(base + OFFS_DAT);
579
580 if (base == S3C2410_GPACON) {
581 /* GPACON only has one bit per control / data and no PULLUPs.
582 * GPACON[x] = 0 => Output, 1 => SFN */
583
584 /* first set all SFN bits to SFN */
585
586 gpcon = old_gpcon | gps->gpcon;
587 __raw_writel(gpcon, base + OFFS_CON);
588
589 /* now set all the other bits */
590
591 __raw_writel(gps_gpdat, base + OFFS_DAT);
592 __raw_writel(gps_gpcon, base + OFFS_CON);
593 } else {
594 unsigned long old, new, mask;
595 unsigned long change_mask = 0x0;
596
597 old_gpup = __raw_readl(base + OFFS_UP);
598
599 /* Create a change_mask of all the items that need to have
600 * their CON value changed before their DAT value, so that
601 * we minimise the work between the two settings.
602 */
603
604 for (nr = 0, mask = 0x03; nr < 32; nr += 2, mask <<= 2) {
605 old = (old_gpcon & mask) >> nr;
606 new = (gps_gpcon & mask) >> nr;
607
608 /* If there is no change, then skip */
609
610 if (old == new)
611 continue;
612
613 /* If both are special function, then skip */
614
615 if (is_sfn(old) && is_sfn(new))
616 continue;
617
618 /* Change is IN => OUT, do not change now */
619
620 if (is_in(old) && is_out(new))
621 continue;
622
623 /* Change is SFN => OUT, do not change now */
624
625 if (is_sfn(old) && is_out(new))
626 continue;
627
628 /* We should now be at the case of IN=>SFN,
629 * OUT=>SFN, OUT=>IN, SFN=>IN. */
630
631 change_mask |= mask;
632 }
633
634 /* Write the new CON settings */
635
636 gpcon = old_gpcon & ~change_mask;
637 gpcon |= gps_gpcon & change_mask;
638
639 __raw_writel(gpcon, base + OFFS_CON);
640
641 /* Now change any items that require DAT,CON */
642
643 __raw_writel(gps_gpdat, base + OFFS_DAT);
644 __raw_writel(gps_gpcon, base + OFFS_CON);
645 __raw_writel(gps->gpup, base + OFFS_UP);
646 }
647
648 DBG("GPIO[%d] CON %08lx => %08lx, DAT %08lx => %08lx\n",
649 index, old_gpcon, gps_gpcon, old_gpdat, gps_gpdat);
650}
651
652
653/** s3c2410_pm_restore_gpios()
654 *
655 * Restore the state of the GPIOs
656 */
657
658static void s3c2410_pm_restore_gpios(void)
659{
660 struct gpio_sleep *gps = gpio_save;
661 int gpio;
662
663 for (gpio = 0; gpio < ARRAY_SIZE(gpio_save); gpio++, gps++) {
664 s3c2410_pm_restore_gpio(gpio, gps);
665 }
666}
667
489void (*pm_cpu_prep)(void); 668void (*pm_cpu_prep)(void);
490void (*pm_cpu_sleep)(void); 669void (*pm_cpu_sleep)(void);
491 670
@@ -535,7 +714,8 @@ static int s3c2410_pm_enter(suspend_state_t state)
535 714
536 /* save all necessary core registers not covered by the drivers */ 715 /* save all necessary core registers not covered by the drivers */
537 716
538 s3c2410_pm_do_save(gpio_save, ARRAY_SIZE(gpio_save)); 717 s3c2410_pm_save_gpios();
718 s3c2410_pm_do_save(misc_save, ARRAY_SIZE(misc_save));
539 s3c2410_pm_do_save(core_save, ARRAY_SIZE(core_save)); 719 s3c2410_pm_do_save(core_save, ARRAY_SIZE(core_save));
540 s3c2410_pm_do_save(uart_save, ARRAY_SIZE(uart_save)); 720 s3c2410_pm_do_save(uart_save, ARRAY_SIZE(uart_save));
541 721
@@ -585,8 +765,9 @@ static int s3c2410_pm_enter(suspend_state_t state)
585 /* restore the system state */ 765 /* restore the system state */
586 766
587 s3c2410_pm_do_restore_core(core_save, ARRAY_SIZE(core_save)); 767 s3c2410_pm_do_restore_core(core_save, ARRAY_SIZE(core_save));
588 s3c2410_pm_do_restore(gpio_save, ARRAY_SIZE(gpio_save)); 768 s3c2410_pm_do_restore(misc_save, ARRAY_SIZE(misc_save));
589 s3c2410_pm_do_restore(uart_save, ARRAY_SIZE(uart_save)); 769 s3c2410_pm_do_restore(uart_save, ARRAY_SIZE(uart_save));
770 s3c2410_pm_restore_gpios();
590 771
591 s3c2410_pm_debug_init(); 772 s3c2410_pm_debug_init();
592 773