aboutsummaryrefslogtreecommitdiffstats
path: root/arch/arm/plat-s3c24xx/pm.c
diff options
context:
space:
mode:
authorBen Dooks <ben-linux@fluff.org>2008-01-28 07:01:24 -0500
committerRussell King <rmk+kernel@arm.linux.org.uk>2008-01-28 08:20:51 -0500
commit62feee648ca0ad6e1b54e44e6c8ddb69f225f812 (patch)
treed7fb702d7816380c91c19d3a465073ea37eb5506 /arch/arm/plat-s3c24xx/pm.c
parentbb6d9b56c1d5c1cbff39b7c337c900e2b3d8387a (diff)
[ARM] 4784/1: S3C24XX: Fix GPIO restore glitches
The core resume code may have caused glitches in the GPIO when restoring the GPIO state due to the order in which the GPIO registers were being written. Change the restore process take into account the state of the GPIOs on resume and the state the system wants to restore them to. See the code comments in the patch for more details of the process. Signed-off-by: Ben Dooks <ben-linux@fluff.org> Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
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