Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

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

[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
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -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;
+3 −2
Original line number Diff line number Diff line
@@ -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)		\
+149 −73
Original line number Diff line number Diff line
@@ -25,7 +25,12 @@

#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 (uorl == 0)
		GAFR_L(bank) = gafr;
	else
		GAFR_U(bank) = gafr;

	if (c & MFP_DIR_OUT)
	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,11 +206,10 @@ 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;

@@ -199,11 +217,11 @@ static int __init pxa25x_mfp_init(void)
		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,17 +251,15 @@ 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)
		if (i == 2 || i == 5 || i == 6 || i == 7 || i == 8)
			continue;

		gpio_desc[i].valid = 1;
@@ -269,9 +285,69 @@ static int __init pxa27x_mfp_init(void)

	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++) {

		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;
}
postcore_initcall(pxa27x_mfp_init);
#endif /* CONFIG_PXA27x */

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;
}
#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);
+3 −25
Original line number Diff line number Diff line
@@ -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);
}
@@ -329,6 +305,8 @@ static struct platform_device *pxa25x_devices[] __initdata = {
static struct sys_device pxa25x_sysdev[] = {
	{
		.cls	= &pxa_irq_sysclass,
	}, {
		.cls	= &pxa2xx_mfp_sysclass,
	}, {
		.cls	= &pxa_gpio_sysclass,
	},
+6 −31
Original line number Diff line number Diff line
@@ -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);
}

@@ -375,6 +348,8 @@ static struct platform_device *devices[] __initdata = {
static struct sys_device pxa27x_sysdev[] = {
	{
		.cls	= &pxa_irq_sysclass,
	}, {
		.cls	= &pxa2xx_mfp_sysclass,
	}, {
		.cls	= &pxa_gpio_sysclass,
	},