aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorH. Peter Anvin <hpa@linux.intel.com>2010-12-03 13:36:00 -0800
committerH. Peter Anvin <hpa@linux.intel.com>2010-12-14 10:48:40 -0800
commit42dd704f9ac40347113cf827c2e07e839069c3f3 (patch)
treec23795e902b6c8f97b97ba39a91f642865f1469c
parent8062e70d346f6be5aae1d0064890a934bd4e9209 (diff)
downloadmrst-s0i3-test-42dd704f9ac40347113cf827c2e07e839069c3f3.tar.gz
mrst-s0i3-test-42dd704f9ac40347113cf827c2e07e839069c3f3.tar.xz
mrst-s0i3-test-42dd704f9ac40347113cf827c2e07e839069c3f3.zip
s0i3: set the wakeup pointer, kernel_fpu_end() on S0i3 failure
Set the wakeup pointer using SFI. If we fail to enter S0i3 we need to kernel_fpu_end() since save_processor_state() will have called kernel_fpu_begin(). save/restore_processor_state might still be too heavyweight.
-rw-r--r--drivers/idle/mrst_s0i3.c42
-rw-r--r--drivers/idle/mrst_s0i3.h1
2 files changed, 41 insertions, 2 deletions
diff --git a/drivers/idle/mrst_s0i3.c b/drivers/idle/mrst_s0i3.c
index 6a851b7b54f..44a6fcddd7d 100644
--- a/drivers/idle/mrst_s0i3.c
+++ b/drivers/idle/mrst_s0i3.c
@@ -28,14 +28,17 @@
#include <trace/events/power.h>
#include <linux/sched.h>
#include <linux/suspend.h>
+#include <linux/sfi.h>
+#include <asm/i387.h>
#include <asm/msr.h>
#include <asm/mtrr.h>
#include <asm/mwait.h>
#include "mrst_s0i3.h"
static void do_s0i3(void);
-volatile u32 *pmu_reg;
-struct pci_dev *pmu_dev; /* South Complex PMU unit */
+static volatile u32 *pmu_reg;
+static struct pci_dev *pmu_dev; /* South Complex PMU unit */
+static u64 *wakeup_ptr;
/**
* mrst_s0i3_idle
@@ -236,8 +239,14 @@ static void s0i3_wait_for_pmu(void)
cpu_relax();
}
+static inline void s0i3_update_wake_pointer(void)
+{
+ *wakeup_ptr = virt_to_phys(mrst_s0i3_resume);
+}
+
static noinline void do_s0i3(void)
{
+ s0i3_update_wake_pointer();
s0i3_save_lapic();
s0i3_save_msrs();
save_processor_state();
@@ -252,9 +261,33 @@ static noinline void do_s0i3(void)
writel(0x0, &pmu_dev[PM_MSIC]);
s0i3_poke_other_cpu();
+ } else {
+ /* save_processor_state() did execute kernel_fpu_begin() */
+ kernel_fpu_end();
}
}
+static int s0i3_sfi_parse_wake(struct sfi_table_header *table)
+{
+ struct sfi_table_simple *sb;
+ struct sfi_wake_table_entry *pentry;
+ int num;
+
+ sb = (struct sfi_table_simple *)table;
+ pentry = (struct sfi_wake_table_entry *)sb->pentry;
+ num = SFI_GET_NUM_ENTRIES(sb, struct sfi_wake_table_entry);
+
+ if (num < 1) /* num == 1? */
+ return -EINVAL;
+
+ wakeup_ptr = ioremap_cache(pentry->phys_addr, 8);
+
+ printk("s0i3: wakeup pointer at 0x%llx mapped to %p\n",
+ pentry->phys_addr, wakeup_ptr);
+
+ return wakeup_ptr ? 0 : -ENOMEM;
+}
+
/* Hacky! */
static int s0i3_prepare(void)
{
@@ -273,6 +306,11 @@ static int s0i3_prepare(void)
if (err)
goto err_disable_pdev;
+ wakeup_ptr = NULL;
+ err = sfi_table_parse(SFI_SIG_WAKE, NULL, NULL, s0i3_sfi_parse_wake);
+ if (err)
+ goto err_disable_pdev;
+
pmu_reg = pci_iomap(pmu_dev, 0, 0);
if (!pmu_reg) {
err = -ENOMEM;
diff --git a/drivers/idle/mrst_s0i3.h b/drivers/idle/mrst_s0i3.h
index 38d3de55a13..960b7e3210f 100644
--- a/drivers/idle/mrst_s0i3.h
+++ b/drivers/idle/mrst_s0i3.h
@@ -56,6 +56,7 @@ struct mrst_s0i3_resume_stack {
int intel_idle(struct cpuidle_device *dev, struct cpuidle_state *state);
int mrst_s0i3_idle(struct cpuidle_device *dev, struct cpuidle_state *state);
int mrst_s0i3_entry(void);
+void mrst_s0i3_resume(void);
#endif /* __ASSEMBLY__ */
#endif /* MRST_S0I3_H */