[ARM] pxa: better MFP low power state support for pxa25x/pxa27x
authorEric Miao <eric.miao@marvell.com>
Wed, 3 Sep 2008 10:06:34 +0000 (18:06 +0800)
committerRussell King <rmk+kernel@arm.linux.org.uk>
Fri, 26 Sep 2008 22:43:20 +0000 (23:43 +0100)
When configured as a specific low power state: MFP_LPM_DRIVE_LOW,
MFP_LPM_DRIVE_HIGH, the corresponding GPDR register bit during
low power mode shall be re-configured as output (if they are not
configured so), thus the PGSRx bits can output.

Create an additional low power values GPDR registers, and properly
save/restore the GAFR + GPDR registers when doing suspend/resume.

Signed-off-by: Eric Miao <eric.miao@marvell.com>
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
arch/arm/mach-pxa/generic.h
arch/arm/mach-pxa/include/mach/mfp.h
arch/arm/mach-pxa/mfp-pxa2xx.c
arch/arm/mach-pxa/pxa25x.c
arch/arm/mach-pxa/pxa27x.c

index 041c048..dc876a8 100644 (file)
@@ -65,4 +65,5 @@ static inline void pxa3xx_clear_reset_status(unsigned int mask) {}
 
 extern struct sysdev_class pxa_irq_sysclass;
 extern struct sysdev_class pxa_gpio_sysclass;
+extern struct sysdev_class pxa2xx_mfp_sysclass;
 extern struct sysdev_class pxa3xx_mfp_sysclass;
index 8769567..4821850 100644 (file)
@@ -274,12 +274,13 @@ typedef unsigned long mfp_cfg_t;
 #define MFP_DS_MASK            (0x7 << 13)
 #define MFP_DS(x)              (((x) >> 13) & 0x7)
 
-#define MFP_LPM_INPUT          (0x0 << 16)
+#define MFP_LPM_DEFAULT                (0x0 << 16)
 #define MFP_LPM_DRIVE_LOW      (0x1 << 16)
 #define MFP_LPM_DRIVE_HIGH     (0x2 << 16)
 #define MFP_LPM_PULL_LOW       (0x3 << 16)
 #define MFP_LPM_PULL_HIGH      (0x4 << 16)
 #define MFP_LPM_FLOAT          (0x5 << 16)
+#define MFP_LPM_INPUT          (0x6 << 16)
 #define MFP_LPM_STATE_MASK     (0x7 << 16)
 #define MFP_LPM_STATE(x)       (((x) >> 16) & 0x7)
 
@@ -297,7 +298,7 @@ typedef unsigned long mfp_cfg_t;
 #define MFP_PULL_MASK          (0x3 << 21)
 #define MFP_PULL(x)            (((x) >> 21) & 0x3)
 
-#define MFP_CFG_DEFAULT                (MFP_AF0 | MFP_DS03X | MFP_LPM_INPUT |\
+#define MFP_CFG_DEFAULT                (MFP_AF0 | MFP_DS03X | MFP_LPM_DEFAULT |\
                                 MFP_LPM_EDGE_NONE | MFP_PULL_NONE)
 
 #define MFP_CFG(pin, af)               \
index 925575f..3ee1f39 100644 (file)
 
 #include "generic.h"
 
-#define PGSR(x)                __REG2(0x40F00020, ((x) & 0x60) >> 3)
+#define gpio_to_bank(gpio)     ((gpio) >> 5)
+
+#define PGSR(x)                __REG2(0x40F00020, (x) << 2)
+#define __GAFR(u, x)   __REG2((u) ? 0x40E00058 : 0x40E00054, (x) << 3)
+#define GAFR_L(x)      __GAFR(0, x)
+#define GAFR_U(x)      __GAFR(1, x)
 
 #define PWER_WE35      (1 << 24)
 
@@ -38,49 +43,59 @@ struct gpio_desc {
 };
 
 static struct gpio_desc gpio_desc[MFP_PIN_GPIO127 + 1];
+static int gpio_nr;
 
-static int __mfp_config_lpm(unsigned gpio, unsigned long lpm)
-{
-       unsigned mask = GPIO_bit(gpio);
-
-       /* low power state */
-       switch (lpm) {
-       case MFP_LPM_DRIVE_HIGH:
-               PGSR(gpio) |= mask;
-               break;
-       case MFP_LPM_DRIVE_LOW:
-               PGSR(gpio) &= ~mask;
-               break;
-       case MFP_LPM_INPUT:
-               break;
-       default:
-               pr_warning("%s: invalid low power state for GPIO%d\n",
-                               __func__, gpio);
-               return -EINVAL;
-       }
-       return 0;
-}
+static unsigned long gpdr_lpm[4];
 
 static int __mfp_config_gpio(unsigned gpio, unsigned long c)
 {
        unsigned long gafr, mask = GPIO_bit(gpio);
-       int fn;
+       int bank = gpio_to_bank(gpio);
+       int uorl = !!(gpio & 0x10); /* GAFRx_U or GAFRx_L ? */
+       int shft = (gpio & 0xf) << 1;
+       int fn = MFP_AF(c);
+       int dir = c & MFP_DIR_OUT;
 
-       fn = MFP_AF(c);
        if (fn > 3)
                return -EINVAL;
 
-       /* alternate function and direction */
-       gafr = GAFR(gpio) & ~(0x3 << ((gpio & 0xf) * 2));
-       GAFR(gpio) = gafr |  (fn  << ((gpio & 0xf) * 2));
+       /* alternate function and direction at run-time */
+       gafr = (uorl == 0) ? GAFR_L(bank) : GAFR_U(bank);
+       gafr = (gafr & ~(0x3 << shft)) | (fn << shft);
 
-       if (c & MFP_DIR_OUT)
+       if (uorl == 0)
+               GAFR_L(bank) = gafr;
+       else
+               GAFR_U(bank) = gafr;
+
+       if (dir == MFP_DIR_OUT)
                GPDR(gpio) |= mask;
        else
                GPDR(gpio) &= ~mask;
 
-       if (__mfp_config_lpm(gpio, c & MFP_LPM_STATE_MASK))
-               return -EINVAL;
+       /* alternate function and direction at low power mode */
+       switch (c & MFP_LPM_STATE_MASK) {
+       case MFP_LPM_DRIVE_HIGH:
+               PGSR(bank) |= mask;
+               dir = MFP_DIR_OUT;
+               break;
+       case MFP_LPM_DRIVE_LOW:
+               PGSR(bank) &= ~mask;
+               dir = MFP_DIR_OUT;
+               break;
+       case MFP_LPM_DEFAULT:
+               break;
+       default:
+               /* warning and fall through, treat as MFP_LPM_DEFAULT */
+               pr_warning("%s: GPIO%d: unsupported low power mode\n",
+                               __func__, gpio);
+               break;
+       }
+
+       if (dir == MFP_DIR_OUT)
+               gpdr_lpm[bank] |= mask;
+       else
+               gpdr_lpm[bank] &= ~mask;
 
        /* give early warning if MFP_LPM_CAN_WAKEUP is set on the
         * configurations of those pins not able to wakeup
@@ -91,7 +106,7 @@ static int __mfp_config_gpio(unsigned gpio, unsigned long c)
                return -EINVAL;
        }
 
-       if ((c & MFP_LPM_CAN_WAKEUP) && (c & MFP_DIR_OUT)) {
+       if ((c & MFP_LPM_CAN_WAKEUP) && (dir == MFP_DIR_OUT)) {
                pr_warning("%s: output GPIO%d unable to wakeup\n",
                                __func__, gpio);
                return -EINVAL;
@@ -135,7 +150,7 @@ void pxa2xx_mfp_config(unsigned long *mfp_cfgs, int num)
 
 void pxa2xx_mfp_set_lpm(int mfp, unsigned long lpm)
 {
-       unsigned long flags;
+       unsigned long flags, c;
        int gpio;
 
        gpio = __mfp_validate(mfp);
@@ -143,7 +158,11 @@ void pxa2xx_mfp_set_lpm(int mfp, unsigned long lpm)
                return;
 
        local_irq_save(flags);
-       __mfp_config_lpm(gpio, lpm);
+
+       c = gpio_desc[gpio].config;
+       c = (c & ~MFP_LPM_STATE_MASK) | lpm;
+       __mfp_config_gpio(gpio, c);
+
        local_irq_restore(flags);
 }
 
@@ -187,23 +206,22 @@ int gpio_set_wake(unsigned int gpio, unsigned int on)
 }
 
 #ifdef CONFIG_PXA25x
-static int __init pxa25x_mfp_init(void)
+static void __init pxa25x_mfp_init(void)
 {
        int i;
 
-       if (cpu_is_pxa25x()) {
-               for (i = 0; i <= 84; i++)
-                       gpio_desc[i].valid = 1;
+       for (i = 0; i <= 84; i++)
+               gpio_desc[i].valid = 1;
 
-               for (i = 0; i <= 15; i++) {
-                       gpio_desc[i].can_wakeup = 1;
-                       gpio_desc[i].mask = GPIO_bit(i);
-               }
+       for (i = 0; i <= 15; i++) {
+               gpio_desc[i].can_wakeup = 1;
+               gpio_desc[i].mask = GPIO_bit(i);
        }
 
-       return 0;
+       gpio_nr = 85;
 }
-postcore_initcall(pxa25x_mfp_init);
+#else
+static inline void pxa25x_mfp_init(void) {}
 #endif /* CONFIG_PXA25x */
 
 #ifdef CONFIG_PXA27x
@@ -233,45 +251,103 @@ int keypad_set_wake(unsigned int on)
        return 0;
 }
 
-static int __init pxa27x_mfp_init(void)
+static void __init pxa27x_mfp_init(void)
 {
        int i, gpio;
 
-       if (cpu_is_pxa27x()) {
-               for (i = 0; i <= 120; i++) {
-                       /* skip GPIO2, 5, 6, 7, 8, they are not
-                        * valid pins allow configuration
-                        */
-                       if (i == 2 || i == 5 || i == 6 ||
-                           i == 7 || i == 8)
-                               continue;
+       for (i = 0; i <= 120; i++) {
+               /* skip GPIO2, 5, 6, 7, 8, they are not
+                * valid pins allow configuration
+                */
+               if (i == 2 || i == 5 || i == 6 || i == 7 || i == 8)
+                       continue;
 
-                       gpio_desc[i].valid = 1;
-               }
+               gpio_desc[i].valid = 1;
+       }
 
-               /* Keypad GPIOs */
-               for (i = 0; i < ARRAY_SIZE(pxa27x_pkwr_gpio); i++) {
-                       gpio = pxa27x_pkwr_gpio[i];
-                       gpio_desc[gpio].can_wakeup = 1;
-                       gpio_desc[gpio].keypad_gpio = 1;
-                       gpio_desc[gpio].mask = 1 << i;
-               }
+       /* Keypad GPIOs */
+       for (i = 0; i < ARRAY_SIZE(pxa27x_pkwr_gpio); i++) {
+               gpio = pxa27x_pkwr_gpio[i];
+               gpio_desc[gpio].can_wakeup = 1;
+               gpio_desc[gpio].keypad_gpio = 1;
+               gpio_desc[gpio].mask = 1 << i;
+       }
 
-               /* Overwrite GPIO13 as a PWER wakeup source */
-               for (i = 0; i <= 15; i++) {
-                       /* skip GPIO2, 5, 6, 7, 8 */
-                       if (GPIO_bit(i) & 0x1e4)
-                               continue;
+       /* Overwrite GPIO13 as a PWER wakeup source */
+       for (i = 0; i <= 15; i++) {
+               /* skip GPIO2, 5, 6, 7, 8 */
+               if (GPIO_bit(i) & 0x1e4)
+                       continue;
 
-                       gpio_desc[i].can_wakeup = 1;
-                       gpio_desc[i].mask = GPIO_bit(i);
-               }
+               gpio_desc[i].can_wakeup = 1;
+               gpio_desc[i].mask = GPIO_bit(i);
+       }
+
+       gpio_desc[35].can_wakeup = 1;
+       gpio_desc[35].mask = PWER_WE35;
+
+       gpio_nr = 121;
+}
+#else
+static inline void pxa27x_mfp_init(void) {}
+#endif /* CONFIG_PXA27x */
+
+#ifdef CONFIG_PM
+static unsigned long saved_gafr[2][4];
+static unsigned long saved_gpdr[4];
+
+static int pxa2xx_mfp_suspend(struct sys_device *d, pm_message_t state)
+{
+       int i;
+
+       for (i = 0; i <= gpio_to_bank(gpio_nr); i++) {
 
-               gpio_desc[35].can_wakeup = 1;
-               gpio_desc[35].mask = PWER_WE35;
+               saved_gafr[0][i] = GAFR_L(i);
+               saved_gafr[1][i] = GAFR_U(i);
+               saved_gpdr[i] = GPDR(i * 32);
+
+               GPDR(i * 32) = gpdr_lpm[i];
        }
+       return 0;
+}
 
+static int pxa2xx_mfp_resume(struct sys_device *d)
+{
+       int i;
+
+       for (i = 0; i <= gpio_to_bank(gpio_nr); i++) {
+               GAFR_L(i) = saved_gafr[0][i];
+               GAFR_U(i) = saved_gafr[1][i];
+               GPDR(i * 32) = saved_gpdr[i];
+       }
+       PSSR = PSSR_RDH | PSSR_PH;
        return 0;
 }
-postcore_initcall(pxa27x_mfp_init);
-#endif /* CONFIG_PXA27x */
+#else
+#define pxa2xx_mfp_suspend     NULL
+#define pxa2xx_mfp_resume      NULL
+#endif
+
+struct sysdev_class pxa2xx_mfp_sysclass = {
+       .name           = "mfp",
+       .suspend        = pxa2xx_mfp_suspend,
+       .resume         = pxa2xx_mfp_resume,
+};
+
+static int __init pxa2xx_mfp_init(void)
+{
+       int i;
+
+       if (cpu_is_pxa25x())
+               pxa25x_mfp_init();
+
+       if (cpu_is_pxa27x())
+               pxa27x_mfp_init();
+
+       /* initialize gafr_run[], pgsr_lpm[] from existing values */
+       for (i = 0; i <= gpio_to_bank(gpio_nr); i++)
+               gpdr_lpm[i] = GPDR(i * 32);
+
+       return sysdev_class_register(&pxa2xx_mfp_sysclass);
+}
+postcore_initcall(pxa2xx_mfp_init);
index 305452b..f0eda20 100644 (file)
@@ -203,33 +203,17 @@ static struct clk pxa25x_clks[] = {
  * More ones like CP and general purpose register values are preserved
  * with the stack pointer in sleep.S.
  */
-enum { SLEEP_SAVE_PGSR0, SLEEP_SAVE_PGSR1, SLEEP_SAVE_PGSR2,
-
-       SLEEP_SAVE_GAFR0_L, SLEEP_SAVE_GAFR0_U,
-       SLEEP_SAVE_GAFR1_L, SLEEP_SAVE_GAFR1_U,
-       SLEEP_SAVE_GAFR2_L, SLEEP_SAVE_GAFR2_U,
-
+enum {
        SLEEP_SAVE_PSTR,
-
        SLEEP_SAVE_CKEN,
-
        SLEEP_SAVE_COUNT
 };
 
 
 static void pxa25x_cpu_pm_save(unsigned long *sleep_save)
 {
-       SAVE(PGSR0); SAVE(PGSR1); SAVE(PGSR2);
-
-       SAVE(GAFR0_L); SAVE(GAFR0_U);
-       SAVE(GAFR1_L); SAVE(GAFR1_U);
-       SAVE(GAFR2_L); SAVE(GAFR2_U);
-
        SAVE(CKEN);
        SAVE(PSTR);
-
-       /* Clear GPIO transition detect bits */
-       GEDR0 = GEDR0; GEDR1 = GEDR1; GEDR2 = GEDR2;
 }
 
 static void pxa25x_cpu_pm_restore(unsigned long *sleep_save)
@@ -237,14 +221,6 @@ static void pxa25x_cpu_pm_restore(unsigned long *sleep_save)
        /* ensure not to come back here if it wasn't intended */
        PSPR = 0;
 
-       /* restore registers */
-       RESTORE(GAFR0_L); RESTORE(GAFR0_U);
-       RESTORE(GAFR1_L); RESTORE(GAFR1_U);
-       RESTORE(GAFR2_L); RESTORE(GAFR2_U);
-       RESTORE(PGSR0); RESTORE(PGSR1); RESTORE(PGSR2);
-
-       PSSR = PSSR_RDH | PSSR_PH;
-
        RESTORE(CKEN);
        RESTORE(PSTR);
 }
@@ -330,6 +306,8 @@ static struct sys_device pxa25x_sysdev[] = {
        {
                .cls    = &pxa_irq_sysclass,
        }, {
+               .cls    = &pxa2xx_mfp_sysclass,
+       }, {
                .cls    = &pxa_gpio_sysclass,
        },
 };
index f9f6a9c..0288665 100644 (file)
@@ -183,36 +183,18 @@ static struct clk pxa27x_clks[] = {
  * More ones like CP and general purpose register values are preserved
  * with the stack pointer in sleep.S.
  */
-enum { SLEEP_SAVE_PGSR0, SLEEP_SAVE_PGSR1, SLEEP_SAVE_PGSR2, SLEEP_SAVE_PGSR3,
-
-       SLEEP_SAVE_GAFR0_L, SLEEP_SAVE_GAFR0_U,
-       SLEEP_SAVE_GAFR1_L, SLEEP_SAVE_GAFR1_U,
-       SLEEP_SAVE_GAFR2_L, SLEEP_SAVE_GAFR2_U,
-       SLEEP_SAVE_GAFR3_L, SLEEP_SAVE_GAFR3_U,
-
+enum {
        SLEEP_SAVE_PSTR,
-
        SLEEP_SAVE_CKEN,
-
        SLEEP_SAVE_MDREFR,
-       SLEEP_SAVE_PWER, SLEEP_SAVE_PCFR, SLEEP_SAVE_PRER,
-       SLEEP_SAVE_PFER, SLEEP_SAVE_PKWR,
-
+       SLEEP_SAVE_PCFR,
        SLEEP_SAVE_COUNT
 };
 
 void pxa27x_cpu_pm_save(unsigned long *sleep_save)
 {
-       SAVE(PGSR0); SAVE(PGSR1); SAVE(PGSR2); SAVE(PGSR3);
-
-       SAVE(GAFR0_L); SAVE(GAFR0_U);
-       SAVE(GAFR1_L); SAVE(GAFR1_U);
-       SAVE(GAFR2_L); SAVE(GAFR2_U);
-       SAVE(GAFR3_L); SAVE(GAFR3_U);
-
        SAVE(MDREFR);
-       SAVE(PWER); SAVE(PCFR); SAVE(PRER);
-       SAVE(PFER); SAVE(PKWR);
+       SAVE(PCFR);
 
        SAVE(CKEN);
        SAVE(PSTR);
@@ -223,21 +205,12 @@ void pxa27x_cpu_pm_restore(unsigned long *sleep_save)
        /* ensure not to come back here if it wasn't intended */
        PSPR = 0;
 
-       /* restore registers */
-       RESTORE(GAFR0_L); RESTORE(GAFR0_U);
-       RESTORE(GAFR1_L); RESTORE(GAFR1_U);
-       RESTORE(GAFR2_L); RESTORE(GAFR2_U);
-       RESTORE(GAFR3_L); RESTORE(GAFR3_U);
-       RESTORE(PGSR0); RESTORE(PGSR1); RESTORE(PGSR2); RESTORE(PGSR3);
-
        RESTORE(MDREFR);
-       RESTORE(PWER); RESTORE(PCFR); RESTORE(PRER);
-       RESTORE(PFER); RESTORE(PKWR);
+       RESTORE(PCFR);
 
        PSSR = PSSR_RDH | PSSR_PH;
 
        RESTORE(CKEN);
-
        RESTORE(PSTR);
 }
 
@@ -376,6 +349,8 @@ static struct sys_device pxa27x_sysdev[] = {
        {
                .cls    = &pxa_irq_sysclass,
        }, {
+               .cls    = &pxa2xx_mfp_sysclass,
+       }, {
                .cls    = &pxa_gpio_sysclass,
        },
 };