summaryrefslogtreecommitdiff
path: root/arch
diff options
context:
space:
mode:
authorPhilippe Langlais <philippe.langlais@linaro.org>2011-05-04 16:50:16 +0200
committerUlf Hansson <ulf.hansson@stericsson.com>2011-09-19 15:14:55 +0200
commit0c46c85ad0ab52370ccea4c1a6ea48cfc0393646 (patch)
tree7afa68fb4798e67642d037da9a9ec03267001d90 /arch
parent88f8f087a5b61fb5629a3f232b5dedf039c603d7 (diff)
mach-ux500: Force GPIO power settings in suspend
In order to get good power consumption when the system is in suspend this patch forces the correct power save settings. This should not be needed in the long when all drivers uses pm_runtime to control their GPIO sleep settings ST-Ericsson ID: 323382 Change-Id: Ib8d7e2089c157a36697ddf27d622e0f7c08cca58 Signed-off-by: Rickard Andersson <rickard.andersson@stericsson.com> Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/17700 Reviewed-by: Martin PERSSON <martin.persson@stericsson.com> Conflicts: arch/arm/mach-ux500/board-mop500-pins.c
Diffstat (limited to 'arch')
-rw-r--r--arch/arm/mach-ux500/board-mop500-pins.c229
-rw-r--r--arch/arm/mach-ux500/pm/context.c153
-rw-r--r--arch/arm/mach-ux500/pm/context.h5
-rw-r--r--arch/arm/mach-ux500/pm/suspend.c24
4 files changed, 409 insertions, 2 deletions
diff --git a/arch/arm/mach-ux500/board-mop500-pins.c b/arch/arm/mach-ux500/board-mop500-pins.c
index b6a5000cd22..a65a1e0e32a 100644
--- a/arch/arm/mach-ux500/board-mop500-pins.c
+++ b/arch/arm/mach-ux500/board-mop500-pins.c
@@ -7,9 +7,12 @@
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/gpio.h>
+#include <linux/io.h>
#include <asm/mach-types.h>
#include <plat/pincfg.h>
+#include <plat/gpio.h>
+
#include <mach/hardware.h>
#include "pins-db8500.h"
@@ -342,6 +345,232 @@ static pin_cfg_t snowball_pins[] = {
GPIO216_GPIO | PIN_INPUT_PULLUP,/* WLAN_IRQ */
};
+/*
+ * This function is called to force gpio power save
+ * settings during suspend.
+ * This is a temporary solution until all drivers are
+ * controlling their pin settings when in inactive mode.
+ */
+void mop500_pins_suspend_force(void)
+{
+ u32 bankaddr;
+ u32 w_imsc;
+ u32 imsc;
+
+ /*
+ * Apply HSI GPIO Config for DeepSleep
+ *
+ * Bank0
+ */
+ bankaddr = IO_ADDRESS(U8500_GPIOBANK0_BASE);
+
+ w_imsc = readl(bankaddr + NMK_GPIO_RWIMSC) |
+ readl(bankaddr + NMK_GPIO_FWIMSC);
+
+ imsc = readl(bankaddr + NMK_GPIO_RIMSC) |
+ readl(bankaddr + NMK_GPIO_FIMSC);
+
+ writel(0x409C702A & ~w_imsc, bankaddr + NMK_GPIO_DIR);
+ writel(0x001C002A & ~w_imsc, bankaddr + NMK_GPIO_DATS);
+ writel(0x807000 & ~w_imsc, bankaddr + NMK_GPIO_DATC);
+ writel(0x5FFFFFFF & ~w_imsc & ~imsc, bankaddr + NMK_GPIO_PDIS);
+ writel(0 , bankaddr + NMK_GPIO_SLPC);
+
+ /* Bank1 */
+ bankaddr = IO_ADDRESS(U8500_GPIOBANK1_BASE);
+
+ w_imsc = readl(bankaddr + NMK_GPIO_RWIMSC) |
+ readl(bankaddr + NMK_GPIO_FWIMSC);
+
+ imsc = readl(bankaddr + NMK_GPIO_RIMSC) |
+ readl(bankaddr + NMK_GPIO_FIMSC);
+
+ writel(0x3 & ~w_imsc, bankaddr + NMK_GPIO_DIRS);
+ writel(0x1C , bankaddr + NMK_GPIO_DIRC);
+ writel(0x2 & ~w_imsc, bankaddr + NMK_GPIO_DATC);
+ writel(0xFFFFFFFF & ~w_imsc & ~imsc, bankaddr + NMK_GPIO_PDIS);
+ writel(0 , bankaddr + NMK_GPIO_SLPC);
+
+ /* Bank2 */
+
+ bankaddr = IO_ADDRESS(U8500_GPIOBANK2_BASE);
+
+ w_imsc = readl(bankaddr + NMK_GPIO_RWIMSC) |
+ readl(bankaddr + NMK_GPIO_FWIMSC);
+
+ imsc = readl(bankaddr + NMK_GPIO_RIMSC) |
+ readl(bankaddr + NMK_GPIO_FIMSC);
+
+ writel(0x3D7C0 & ~w_imsc, bankaddr + NMK_GPIO_DIRS);
+ writel(0x803C2830, bankaddr + NMK_GPIO_DIRC);
+ writel(0x3D7C0 & ~w_imsc , bankaddr + NMK_GPIO_DATC);
+ writel(0xFFFFFFFF & ~w_imsc & ~imsc, bankaddr + NMK_GPIO_PDIS);
+ writel(0 , bankaddr + NMK_GPIO_SLPC);
+
+ /* Bank3 */
+ bankaddr = IO_ADDRESS(U8500_GPIOBANK3_BASE);
+
+ w_imsc = readl(bankaddr + NMK_GPIO_RWIMSC) |
+ readl(bankaddr + NMK_GPIO_FWIMSC);
+
+ imsc = readl(bankaddr + NMK_GPIO_RIMSC) |
+ readl(bankaddr + NMK_GPIO_FIMSC);
+
+ writel(0x3 & ~w_imsc, bankaddr + NMK_GPIO_DIRS);
+ writel(0x3 & ~w_imsc, bankaddr + NMK_GPIO_DATC);
+ writel(0xFFFFFFFF & ~w_imsc & ~imsc, bankaddr + NMK_GPIO_PDIS);
+ writel(0 , bankaddr + NMK_GPIO_SLPC);
+
+ /* Bank4 */
+ bankaddr = IO_ADDRESS(U8500_GPIOBANK4_BASE);
+
+ w_imsc = readl(bankaddr + NMK_GPIO_RWIMSC) |
+ readl(bankaddr + NMK_GPIO_FWIMSC);
+
+ imsc = readl(bankaddr + NMK_GPIO_RIMSC) |
+ readl(bankaddr + NMK_GPIO_FIMSC);
+
+ writel(0x5E000 & ~w_imsc, bankaddr + NMK_GPIO_DIRS);
+ writel(0xFFDA1800 , bankaddr + NMK_GPIO_DIRC);
+ writel(0x4E000 & ~w_imsc, bankaddr + NMK_GPIO_DATC);
+ writel(0x10000 & ~w_imsc, bankaddr + NMK_GPIO_DATS);
+ writel(0xFFFFFFF9 & ~w_imsc & ~imsc, bankaddr + NMK_GPIO_PDIS);
+ writel(0 , bankaddr + NMK_GPIO_SLPC);
+
+ /* Bank5 */
+ bankaddr = IO_ADDRESS(U8500_GPIOBANK5_BASE);
+
+ w_imsc = readl(bankaddr + NMK_GPIO_RWIMSC) |
+ readl(bankaddr + NMK_GPIO_FWIMSC);
+
+ imsc = readl(bankaddr + NMK_GPIO_RIMSC) |
+ readl(bankaddr + NMK_GPIO_FIMSC);
+
+ writel(0x3FF , bankaddr + NMK_GPIO_DIRC);
+ writel(0xC00 & ~w_imsc, bankaddr + NMK_GPIO_DIRS);
+ writel(0xC00 & ~w_imsc, bankaddr + NMK_GPIO_DATC);
+ writel(0xFFFFFFFF & ~w_imsc & ~imsc, bankaddr + NMK_GPIO_PDIS);
+ writel(0 , bankaddr + NMK_GPIO_SLPC);
+
+ /* Bank6 */
+ bankaddr = IO_ADDRESS(U8500_GPIOBANK6_BASE);
+
+ w_imsc = readl(bankaddr + NMK_GPIO_RWIMSC) |
+ readl(bankaddr + NMK_GPIO_FWIMSC);
+
+ imsc = readl(bankaddr + NMK_GPIO_RIMSC) |
+ readl(bankaddr + NMK_GPIO_FIMSC);
+
+ writel(0x8810810 & ~w_imsc, bankaddr + NMK_GPIO_DIRS);
+ writel(0xF57EF7EF, bankaddr + NMK_GPIO_DIRC);
+ writel(0x8810810 & ~w_imsc, bankaddr + NMK_GPIO_DATC);
+ writel(0xFFFFFFFF & ~w_imsc & ~imsc, bankaddr + NMK_GPIO_PDIS);
+ writel(0 , bankaddr + NMK_GPIO_SLPC);
+
+
+ /* Bank7 */
+ bankaddr = IO_ADDRESS(U8500_GPIOBANK7_BASE);
+
+ w_imsc = readl(bankaddr + NMK_GPIO_RWIMSC) |
+ readl(bankaddr + NMK_GPIO_FWIMSC);
+
+ imsc = readl(bankaddr + NMK_GPIO_RIMSC) |
+ readl(bankaddr + NMK_GPIO_FIMSC);
+
+ writel(0x1C & ~w_imsc, bankaddr + NMK_GPIO_DIRS);
+ writel(0x63 , bankaddr + NMK_GPIO_DIRC);
+ writel(0x18 & ~w_imsc, bankaddr + NMK_GPIO_DATC);
+ writel(0xFFFFFFFF & ~w_imsc & ~imsc, bankaddr + NMK_GPIO_PDIS);
+ writel(0 , bankaddr + NMK_GPIO_SLPC);
+
+ /* Bank8 */
+ bankaddr = IO_ADDRESS(U8500_GPIOBANK8_BASE);
+
+ w_imsc = readl(bankaddr + NMK_GPIO_RWIMSC) |
+ readl(bankaddr + NMK_GPIO_FWIMSC);
+
+ imsc = readl(bankaddr + NMK_GPIO_RIMSC) |
+ readl(bankaddr + NMK_GPIO_FIMSC);
+
+ writel(0x2 & ~w_imsc, bankaddr + NMK_GPIO_DIRS);
+ writel(0xFF0 , bankaddr + NMK_GPIO_DIRC);
+ writel(0x2 & ~w_imsc, bankaddr + NMK_GPIO_DATS);
+ writel(0x2 & ~w_imsc & ~imsc, bankaddr + NMK_GPIO_PDIS);
+ writel(0 , bankaddr + NMK_GPIO_SLPC);
+}
+
+/*
+ * This function is called to force gpio power save
+ * mux settings during suspend.
+ * This is a temporary solution until all drivers are
+ * controlling their pin settings when in inactive mode.
+ */
+void mop500_pins_suspend_force_mux(void)
+{
+ u32 bankaddr;
+
+
+ /*
+ * Apply HSI GPIO Config for DeepSleep
+ *
+ * Bank0
+ */
+ bankaddr = IO_ADDRESS(U8500_GPIOBANK0_BASE);
+
+ writel(0xE0000000, bankaddr + NMK_GPIO_AFSLA);
+ writel(0xE0000000, bankaddr + NMK_GPIO_AFSLB);
+
+ /* Bank1 */
+ bankaddr = IO_ADDRESS(U8500_GPIOBANK1_BASE);
+
+ writel(0x1 , bankaddr + NMK_GPIO_AFSLA);
+ writel(0x1 , bankaddr + NMK_GPIO_AFSLB);
+
+ /* Bank2 */
+ bankaddr = IO_ADDRESS(U8500_GPIOBANK2_BASE);
+
+ writel(0 , bankaddr + NMK_GPIO_AFSLA);
+ writel(0 , bankaddr + NMK_GPIO_AFSLB);
+
+ /* Bank3 */
+ bankaddr = IO_ADDRESS(U8500_GPIOBANK3_BASE);
+
+ writel(0 , bankaddr + NMK_GPIO_AFSLA);
+ writel(0 , bankaddr + NMK_GPIO_AFSLB);
+
+ /* Bank4 */
+ bankaddr = IO_ADDRESS(U8500_GPIOBANK4_BASE);
+
+ writel(0x7FF , bankaddr + NMK_GPIO_AFSLA);
+ writel(0 , bankaddr + NMK_GPIO_AFSLB);
+
+ /* Bank5 */
+ bankaddr = IO_ADDRESS(U8500_GPIOBANK5_BASE);
+
+ writel(0 , bankaddr + NMK_GPIO_AFSLA);
+ writel(0 , bankaddr + NMK_GPIO_AFSLB);
+
+ /* Bank6 */
+ bankaddr = IO_ADDRESS(U8500_GPIOBANK6_BASE);
+
+ writel(0 , bankaddr + NMK_GPIO_AFSLA);
+ writel(0 , bankaddr + NMK_GPIO_AFSLB);
+
+ /* Bank7 */
+ bankaddr = IO_ADDRESS(U8500_GPIOBANK7_BASE);
+
+ writel(0 , bankaddr + NMK_GPIO_AFSLA);
+ writel(0 , bankaddr + NMK_GPIO_AFSLB);
+
+ /* Bank8 */
+ bankaddr = IO_ADDRESS(U8500_GPIOBANK8_BASE);
+
+ writel(0 , bankaddr + NMK_GPIO_AFSLA);
+ writel(0 , bankaddr + NMK_GPIO_AFSLB);
+
+
+}
+
void __init mop500_pins_init(void)
{
nmk_config_pins(mop500_pins_common,
diff --git a/arch/arm/mach-ux500/pm/context.c b/arch/arm/mach-ux500/pm/context.c
index 48bb03eafb1..4763ae74e71 100644
--- a/arch/arm/mach-ux500/pm/context.c
+++ b/arch/arm/mach-ux500/pm/context.c
@@ -21,7 +21,13 @@
#include <mach/irqs.h>
#include <mach/scu.h>
+#include <plat/gpio.h>
+
#include "context.h"
+#include "pm.h"
+
+#define GPIO_NUM_BANKS 9
+#define GPIO_NUM_SAVE_REGISTERS 7
/*
* TODO:
@@ -167,6 +173,19 @@ static u32 backup_sram_storage[NR_CPUS] = {
IO_ADDRESS(U8500_CPU1_CP15_CR_BACKUPRAM_ADDR),
};
+static u32 gpio_bankaddr[GPIO_NUM_BANKS] = {IO_ADDRESS(U8500_GPIOBANK0_BASE),
+ IO_ADDRESS(U8500_GPIOBANK1_BASE),
+ IO_ADDRESS(U8500_GPIOBANK2_BASE),
+ IO_ADDRESS(U8500_GPIOBANK3_BASE),
+ IO_ADDRESS(U8500_GPIOBANK4_BASE),
+ IO_ADDRESS(U8500_GPIOBANK5_BASE),
+ IO_ADDRESS(U8500_GPIOBANK6_BASE),
+ IO_ADDRESS(U8500_GPIOBANK7_BASE),
+ IO_ADDRESS(U8500_GPIOBANK8_BASE)
+};
+
+static u32 gpio_save[GPIO_NUM_BANKS][GPIO_NUM_SAVE_REGISTERS];
+
/*
* Stacks and stack pointers
*/
@@ -513,7 +532,6 @@ static void restore_scu(void)
context_scu.base + SCU_ACCESS_CTRL_SAC);
}
-
/*
* Save VAPE context
*/
@@ -556,6 +574,138 @@ void context_vape_restore(void)
}
/*
+ * Save GPIO registers that might be modified
+ * for power save reasons.
+ */
+void context_gpio_save(void)
+{
+ int i;
+
+ for (i = 0; i < GPIO_NUM_BANKS; i++) {
+ gpio_save[i][0] = readl(gpio_bankaddr[i] + NMK_GPIO_AFSLA);
+ gpio_save[i][1] = readl(gpio_bankaddr[i] + NMK_GPIO_AFSLB);
+ gpio_save[i][2] = readl(gpio_bankaddr[i] + NMK_GPIO_PDIS);
+ gpio_save[i][3] = readl(gpio_bankaddr[i] + NMK_GPIO_DIR);
+ gpio_save[i][4] = readl(gpio_bankaddr[i] + NMK_GPIO_DAT);
+ gpio_save[i][6] = readl(gpio_bankaddr[i] + NMK_GPIO_SLPC);
+ }
+}
+
+/*
+ * Restore GPIO registers that might be modified
+ * for power save reasons.
+ */
+void context_gpio_restore(void)
+{
+ int i;
+ u32 output_state;
+ u32 pull_up;
+ u32 pull_down;
+ u32 pull;
+
+ for (i = 0; i < GPIO_NUM_BANKS; i++) {
+ writel(gpio_save[i][2], gpio_bankaddr[i] + NMK_GPIO_PDIS);
+
+ writel(gpio_save[i][3], gpio_bankaddr[i] + NMK_GPIO_DIR);
+
+ /* Set the high outputs. outpute_state = GPIO_DIR & GPIO_DAT */
+ output_state = gpio_save[i][3] & gpio_save[i][4];
+ writel(output_state, gpio_bankaddr[i] + NMK_GPIO_DATS);
+
+ /*
+ * Set the low outputs.
+ * outpute_state = ~(GPIO_DIR & GPIO_DAT) & GPIO_DIR
+ */
+ output_state = ~(gpio_save[i][3] & gpio_save[i][4]) &
+ gpio_save[i][3];
+ writel(output_state, gpio_bankaddr[i] + NMK_GPIO_DATC);
+
+ /*
+ * Restore pull up/down.
+ * Only write pull up/down settings on inputs where
+ * PDIS is not set.
+ * pull = (~GPIO_DIR & ~GPIO_PDIS)
+ */
+ pull = (~gpio_save[i][3] & ~gpio_save[i][2]);
+ nmk_gpio_read_pull(i, &pull_up);
+
+ pull_down = pull & ~pull_up;
+ pull_up = pull & pull_up;
+ /* Set pull ups */
+ writel(pull_up, gpio_bankaddr[i] + NMK_GPIO_DATS);
+ /* Set pull downs */
+ writel(pull_down, gpio_bankaddr[i] + NMK_GPIO_DATC);
+
+ writel(gpio_save[i][6], gpio_bankaddr[i] + NMK_GPIO_SLPC);
+
+ }
+
+}
+
+/*
+ * Restore GPIO mux registers that might be modified
+ * for power save reasons.
+ */
+void context_gpio_restore_mux(void)
+{
+ int i;
+
+ /* Change mux settings */
+ for (i = 0; i < GPIO_NUM_BANKS; i++) {
+ writel(gpio_save[i][0], gpio_bankaddr[i] + NMK_GPIO_AFSLA);
+ writel(gpio_save[i][1], gpio_bankaddr[i] + NMK_GPIO_AFSLB);
+ }
+}
+
+/*
+ * Safe sequence used to switch IOs between GPIO and Alternate-C mode:
+ * - Save SLPM registers
+ * - Set SLPM=0 for the IOs you want to switch and others to 1
+ * - Configure the GPIO registers for the IOs that are being switched
+ * - Set IOFORCE=1
+ * - Modify the AFLSA/B registers for the IOs that are being switched
+ * - Set IOFORCE=0
+ * - Restore SLPM registers
+ * - Any spurious wake up event during switch sequence to be ignored
+ * and cleared
+ */
+void context_gpio_mux_safe_switch(bool begin)
+{
+ int i;
+
+ static u32 slpc[GPIO_NUM_BANKS];
+ static u32 rwimsc[GPIO_NUM_BANKS];
+ static u32 fwimsc[GPIO_NUM_BANKS];
+
+ if (begin) {
+ for (i = 0; i < GPIO_NUM_BANKS; i++) {
+ /* Save registers */
+ slpc[i] = readl(gpio_bankaddr[i] + NMK_GPIO_SLPC);
+ rwimsc[i] = readl(gpio_bankaddr[i] + NMK_GPIO_RWIMSC);
+ fwimsc[i] = readl(gpio_bankaddr[i] + NMK_GPIO_FWIMSC);
+
+ /* Prevent spurious wakeups */
+ writel(0, gpio_bankaddr[i] + NMK_GPIO_RWIMSC);
+ writel(0, gpio_bankaddr[i] + NMK_GPIO_FWIMSC);
+ writel(0, gpio_bankaddr[i] + NMK_GPIO_SLPC);
+ }
+
+ ux500_pm_prcmu_set_ioforce(true);
+ } else {
+ ux500_pm_prcmu_set_ioforce(false);
+
+ /* Restore wake up settings */
+ for (i = 0; i < GPIO_NUM_BANKS; i++) {
+ writel(slpc[i], gpio_bankaddr[i] + NMK_GPIO_SLPC);
+ writel(rwimsc[i], gpio_bankaddr[i] + NMK_GPIO_RWIMSC);
+ writel(fwimsc[i], gpio_bankaddr[i] + NMK_GPIO_FWIMSC);
+ }
+
+ }
+
+}
+
+/*
* Save common
*
* This function must be called once for all cores before going to deep sleep.
@@ -737,4 +887,3 @@ static int __init context_init(void)
return 0;
}
subsys_initcall(context_init);
-
diff --git a/arch/arm/mach-ux500/pm/context.h b/arch/arm/mach-ux500/pm/context.h
index d043f8c805d..20ffaafdb41 100644
--- a/arch/arm/mach-ux500/pm/context.h
+++ b/arch/arm/mach-ux500/pm/context.h
@@ -36,6 +36,11 @@ int context_arm_notifier_unregister(struct notifier_block *nb);
void context_vape_save(void);
void context_vape_restore(void);
+void context_gpio_save(void);
+void context_gpio_restore(void);
+void context_gpio_restore_mux(void);
+void context_gpio_mux_safe_switch(bool begin);
+
void context_varm_save_common(void);
void context_varm_restore_common(void);
diff --git a/arch/arm/mach-ux500/pm/suspend.c b/arch/arm/mach-ux500/pm/suspend.c
index 02d5dd085e1..ae493ab4927 100644
--- a/arch/arm/mach-ux500/pm/suspend.c
+++ b/arch/arm/mach-ux500/pm/suspend.c
@@ -21,6 +21,9 @@
#include "pm.h"
#include "suspend_dbg.h"
+extern void mop500_pins_suspend_force(void);
+extern void mop500_pins_suspend_force_mux(void);
+
static atomic_t block_sleep = ATOMIC_INIT(0);
void suspend_block_sleep(void)
@@ -55,6 +58,20 @@ static int suspend(bool do_deepsleep)
context_vape_save();
+ /*
+ * Save GPIO settings before applying power save
+ * settings
+ */
+ context_gpio_save();
+
+ /* Apply GPIO power save mux settings */
+ context_gpio_mux_safe_switch(true);
+ mop500_pins_suspend_force_mux();
+ context_gpio_mux_safe_switch(false);
+
+ /* Apply GPIO power save settings */
+ mop500_pins_suspend_force();
+
ux500_pm_gic_decouple();
divps_rate = ux500_pm_arm_on_ext_clk(false);
@@ -111,6 +128,13 @@ static int suspend(bool do_deepsleep)
/* APE was turned off, restore IO ring */
ux500_pm_prcmu_set_ioforce(false);
+
+ /* Restore gpio settings */
+ context_gpio_mux_safe_switch(true);
+ context_gpio_restore_mux();
+ context_gpio_mux_safe_switch(false);
+ context_gpio_restore();
+
prcmu_disable_wakeups();
nmk_gpio_wakeups_resume();