Commit 5a3d9651 authored by Eric Miao's avatar Eric Miao Committed by Russell King

[ARM] pxa: better MFP low power state support for pxa25x/pxa27x

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: default avatarEric Miao <eric.miao@marvell.com>
Signed-off-by: default avatarRussell King <rmk+kernel@arm.linux.org.uk>
parent 4fa7c24e
...@@ -65,4 +65,5 @@ static inline void pxa3xx_clear_reset_status(unsigned int mask) {} ...@@ -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_irq_sysclass;
extern struct sysdev_class pxa_gpio_sysclass; extern struct sysdev_class pxa_gpio_sysclass;
extern struct sysdev_class pxa2xx_mfp_sysclass;
extern struct sysdev_class pxa3xx_mfp_sysclass; extern struct sysdev_class pxa3xx_mfp_sysclass;
...@@ -274,12 +274,13 @@ typedef unsigned long mfp_cfg_t; ...@@ -274,12 +274,13 @@ typedef unsigned long mfp_cfg_t;
#define MFP_DS_MASK (0x7 << 13) #define MFP_DS_MASK (0x7 << 13)
#define MFP_DS(x) (((x) >> 13) & 0x7) #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_LOW (0x1 << 16)
#define MFP_LPM_DRIVE_HIGH (0x2 << 16) #define MFP_LPM_DRIVE_HIGH (0x2 << 16)
#define MFP_LPM_PULL_LOW (0x3 << 16) #define MFP_LPM_PULL_LOW (0x3 << 16)
#define MFP_LPM_PULL_HIGH (0x4 << 16) #define MFP_LPM_PULL_HIGH (0x4 << 16)
#define MFP_LPM_FLOAT (0x5 << 16) #define MFP_LPM_FLOAT (0x5 << 16)
#define MFP_LPM_INPUT (0x6 << 16)
#define MFP_LPM_STATE_MASK (0x7 << 16) #define MFP_LPM_STATE_MASK (0x7 << 16)
#define MFP_LPM_STATE(x) (((x) >> 16) & 0x7) #define MFP_LPM_STATE(x) (((x) >> 16) & 0x7)
...@@ -297,7 +298,7 @@ typedef unsigned long mfp_cfg_t; ...@@ -297,7 +298,7 @@ typedef unsigned long mfp_cfg_t;
#define MFP_PULL_MASK (0x3 << 21) #define MFP_PULL_MASK (0x3 << 21)
#define MFP_PULL(x) (((x) >> 21) & 0x3) #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) MFP_LPM_EDGE_NONE | MFP_PULL_NONE)
#define MFP_CFG(pin, af) \ #define MFP_CFG(pin, af) \
......
...@@ -25,7 +25,12 @@ ...@@ -25,7 +25,12 @@
#include "generic.h" #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) #define PWER_WE35 (1 << 24)
...@@ -38,49 +43,59 @@ struct gpio_desc { ...@@ -38,49 +43,59 @@ struct gpio_desc {
}; };
static struct gpio_desc gpio_desc[MFP_PIN_GPIO127 + 1]; static struct gpio_desc gpio_desc[MFP_PIN_GPIO127 + 1];
static int gpio_nr;
static int __mfp_config_lpm(unsigned gpio, unsigned long lpm) static unsigned long gpdr_lpm[4];
{
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 int __mfp_config_gpio(unsigned gpio, unsigned long c) static int __mfp_config_gpio(unsigned gpio, unsigned long c)
{ {
unsigned long gafr, mask = GPIO_bit(gpio); 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) if (fn > 3)
return -EINVAL; return -EINVAL;
/* alternate function and direction */ /* alternate function and direction at run-time */
gafr = GAFR(gpio) & ~(0x3 << ((gpio & 0xf) * 2)); gafr = (uorl == 0) ? GAFR_L(bank) : GAFR_U(bank);
GAFR(gpio) = gafr | (fn << ((gpio & 0xf) * 2)); 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; GPDR(gpio) |= mask;
else else
GPDR(gpio) &= ~mask; GPDR(gpio) &= ~mask;
if (__mfp_config_lpm(gpio, c & MFP_LPM_STATE_MASK)) /* alternate function and direction at low power mode */
return -EINVAL; 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 /* give early warning if MFP_LPM_CAN_WAKEUP is set on the
* configurations of those pins not able to wakeup * configurations of those pins not able to wakeup
...@@ -91,7 +106,7 @@ static int __mfp_config_gpio(unsigned gpio, unsigned long c) ...@@ -91,7 +106,7 @@ static int __mfp_config_gpio(unsigned gpio, unsigned long c)
return -EINVAL; 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", pr_warning("%s: output GPIO%d unable to wakeup\n",
__func__, gpio); __func__, gpio);
return -EINVAL; return -EINVAL;
...@@ -135,7 +150,7 @@ void pxa2xx_mfp_config(unsigned long *mfp_cfgs, int num) ...@@ -135,7 +150,7 @@ void pxa2xx_mfp_config(unsigned long *mfp_cfgs, int num)
void pxa2xx_mfp_set_lpm(int mfp, unsigned long lpm) void pxa2xx_mfp_set_lpm(int mfp, unsigned long lpm)
{ {
unsigned long flags; unsigned long flags, c;
int gpio; int gpio;
gpio = __mfp_validate(mfp); gpio = __mfp_validate(mfp);
...@@ -143,7 +158,11 @@ void pxa2xx_mfp_set_lpm(int mfp, unsigned long lpm) ...@@ -143,7 +158,11 @@ void pxa2xx_mfp_set_lpm(int mfp, unsigned long lpm)
return; return;
local_irq_save(flags); 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); local_irq_restore(flags);
} }
...@@ -187,23 +206,22 @@ int gpio_set_wake(unsigned int gpio, unsigned int on) ...@@ -187,23 +206,22 @@ int gpio_set_wake(unsigned int gpio, unsigned int on)
} }
#ifdef CONFIG_PXA25x #ifdef CONFIG_PXA25x
static int __init pxa25x_mfp_init(void) static void __init pxa25x_mfp_init(void)
{ {
int i; int i;
if (cpu_is_pxa25x()) { for (i = 0; i <= 84; i++)
for (i = 0; i <= 84; i++) gpio_desc[i].valid = 1;
gpio_desc[i].valid = 1;
for (i = 0; i <= 15; i++) { for (i = 0; i <= 15; i++) {
gpio_desc[i].can_wakeup = 1; gpio_desc[i].can_wakeup = 1;
gpio_desc[i].mask = GPIO_bit(i); 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 */ #endif /* CONFIG_PXA25x */
#ifdef CONFIG_PXA27x #ifdef CONFIG_PXA27x
...@@ -233,45 +251,103 @@ int keypad_set_wake(unsigned int on) ...@@ -233,45 +251,103 @@ int keypad_set_wake(unsigned int on)
return 0; return 0;
} }
static int __init pxa27x_mfp_init(void) static void __init pxa27x_mfp_init(void)
{ {
int i, gpio; int i, gpio;
if (cpu_is_pxa27x()) { for (i = 0; i <= 120; i++) {
for (i = 0; i <= 120; i++) { /* skip GPIO2, 5, 6, 7, 8, they are not
/* skip GPIO2, 5, 6, 7, 8, they are not * valid pins allow configuration
* valid pins allow configuration */
*/ if (i == 2 || i == 5 || i == 6 || i == 7 || i == 8)
if (i == 2 || i == 5 || i == 6 || continue;
i == 7 || i == 8)
continue;
gpio_desc[i].valid = 1; gpio_desc[i].valid = 1;
} }
/* Keypad GPIOs */ /* Keypad GPIOs */
for (i = 0; i < ARRAY_SIZE(pxa27x_pkwr_gpio); i++) { for (i = 0; i < ARRAY_SIZE(pxa27x_pkwr_gpio); i++) {
gpio = pxa27x_pkwr_gpio[i]; gpio = pxa27x_pkwr_gpio[i];
gpio_desc[gpio].can_wakeup = 1; gpio_desc[gpio].can_wakeup = 1;
gpio_desc[gpio].keypad_gpio = 1; gpio_desc[gpio].keypad_gpio = 1;
gpio_desc[gpio].mask = 1 << i; gpio_desc[gpio].mask = 1 << i;
} }
/* Overwrite GPIO13 as a PWER wakeup source */ /* Overwrite GPIO13 as a PWER wakeup source */
for (i = 0; i <= 15; i++) { for (i = 0; i <= 15; i++) {
/* skip GPIO2, 5, 6, 7, 8 */ /* skip GPIO2, 5, 6, 7, 8 */
if (GPIO_bit(i) & 0x1e4) if (GPIO_bit(i) & 0x1e4)
continue; continue;
gpio_desc[i].can_wakeup = 1; gpio_desc[i].can_wakeup = 1;
gpio_desc[i].mask = GPIO_bit(i); 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; saved_gafr[0][i] = GAFR_L(i);
gpio_desc[35].mask = PWER_WE35; 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; return 0;
} }
postcore_initcall(pxa27x_mfp_init); #else
#endif /* CONFIG_PXA27x */ #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);
...@@ -203,33 +203,17 @@ static struct clk pxa25x_clks[] = { ...@@ -203,33 +203,17 @@ static struct clk pxa25x_clks[] = {
* More ones like CP and general purpose register values are preserved * More ones like CP and general purpose register values are preserved
* with the stack pointer in sleep.S. * with the stack pointer in sleep.S.
*/ */
enum { SLEEP_SAVE_PGSR0, SLEEP_SAVE_PGSR1, SLEEP_SAVE_PGSR2, enum {
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_PSTR, SLEEP_SAVE_PSTR,
SLEEP_SAVE_CKEN, SLEEP_SAVE_CKEN,
SLEEP_SAVE_COUNT SLEEP_SAVE_COUNT
}; };
static void pxa25x_cpu_pm_save(unsigned long *sleep_save) 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(CKEN);
SAVE(PSTR); SAVE(PSTR);
/* Clear GPIO transition detect bits */
GEDR0 = GEDR0; GEDR1 = GEDR1; GEDR2 = GEDR2;
} }
static void pxa25x_cpu_pm_restore(unsigned long *sleep_save) static void pxa25x_cpu_pm_restore(unsigned long *sleep_save)
...@@ -237,14 +221,6 @@ 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 */ /* ensure not to come back here if it wasn't intended */
PSPR = 0; 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(CKEN);
RESTORE(PSTR); RESTORE(PSTR);
} }
...@@ -329,6 +305,8 @@ static struct platform_device *pxa25x_devices[] __initdata = { ...@@ -329,6 +305,8 @@ static struct platform_device *pxa25x_devices[] __initdata = {
static struct sys_device pxa25x_sysdev[] = { static struct sys_device pxa25x_sysdev[] = {
{ {
.cls = &pxa_irq_sysclass, .cls = &pxa_irq_sysclass,
}, {
.cls = &pxa2xx_mfp_sysclass,
}, { }, {
.cls = &pxa_gpio_sysclass, .cls = &pxa_gpio_sysclass,
}, },
......
...@@ -183,36 +183,18 @@ static struct clk pxa27x_clks[] = { ...@@ -183,36 +183,18 @@ static struct clk pxa27x_clks[] = {
* More ones like CP and general purpose register values are preserved * More ones like CP and general purpose register values are preserved
* with the stack pointer in sleep.S. * with the stack pointer in sleep.S.
*/ */
enum { SLEEP_SAVE_PGSR0, SLEEP_SAVE_PGSR1, SLEEP_SAVE_PGSR2, SLEEP_SAVE_PGSR3, enum {
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,
SLEEP_SAVE_PSTR, SLEEP_SAVE_PSTR,
SLEEP_SAVE_CKEN, SLEEP_SAVE_CKEN,
SLEEP_SAVE_MDREFR, SLEEP_SAVE_MDREFR,
SLEEP_SAVE_PWER, SLEEP_SAVE_PCFR, SLEEP_SAVE_PRER, SLEEP_SAVE_PCFR,
SLEEP_SAVE_PFER, SLEEP_SAVE_PKWR,
SLEEP_SAVE_COUNT SLEEP_SAVE_COUNT
}; };
void pxa27x_cpu_pm_save(unsigned long *sleep_save) 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(MDREFR);
SAVE(PWER); SAVE(PCFR); SAVE(PRER); SAVE(PCFR);
SAVE(PFER); SAVE(PKWR);
SAVE(CKEN); SAVE(CKEN);
SAVE(PSTR); SAVE(PSTR);
...@@ -223,21 +205,12 @@ void pxa27x_cpu_pm_restore(unsigned long *sleep_save) ...@@ -223,21 +205,12 @@ void pxa27x_cpu_pm_restore(unsigned long *sleep_save)
/* ensure not to come back here if it wasn't intended */ /* ensure not to come back here if it wasn't intended */
PSPR = 0; 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(MDREFR);
RESTORE(PWER); RESTORE(PCFR); RESTORE(PRER); RESTORE(PCFR);
RESTORE(PFER); RESTORE(PKWR);
PSSR = PSSR_RDH | PSSR_PH; PSSR = PSSR_RDH | PSSR_PH;
RESTORE(CKEN); RESTORE(CKEN);
RESTORE(PSTR); RESTORE(PSTR);
} }
...@@ -375,6 +348,8 @@ static struct platform_device *devices[] __initdata = { ...@@ -375,6 +348,8 @@ static struct platform_device *devices[] __initdata = {
static struct sys_device pxa27x_sysdev[] = { static struct sys_device pxa27x_sysdev[] = {
{ {
.cls = &pxa_irq_sysclass, .cls = &pxa_irq_sysclass,
}, {
.cls = &pxa2xx_mfp_sysclass,
}, { }, {
.cls = &pxa_gpio_sysclass, .cls = &pxa_gpio_sysclass,
}, },
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment