aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorH. Peter Anvin <hpa@linux.intel.com>2010-12-14 15:00:27 -0800
committerH. Peter Anvin <hpa@linux.intel.com>2010-12-14 15:00:27 -0800
commita816ab366b55f39c08ebaba59d0e84a403e8b36a (patch)
tree206c994c0b75e6a4fc05dd390cfa5a14fdded56a
parente203358312b4762936de133caf00b126c115952c (diff)
downloadmrst-s0i3-test-a816ab366b55f39c08ebaba59d0e84a403e8b36a.tar.gz
mrst-s0i3-test-a816ab366b55f39c08ebaba59d0e84a403e8b36a.tar.xz
mrst-s0i3-test-a816ab366b55f39c08ebaba59d0e84a403e8b36a.zip
s0i3: add hooks into the PCI PM layer - not functional yets0i3-pm-test
Try to add hooks into the PCI power management layer. They do not work yet. Signed-off-by: H. Peter Anvin <hpa@linux.intel.com>
-rw-r--r--arch/x86/include/asm/mrst.h5
-rw-r--r--arch/x86/pci/mrst.c2
-rw-r--r--drivers/idle/mrst_s0i3.c92
3 files changed, 95 insertions, 4 deletions
diff --git a/arch/x86/include/asm/mrst.h b/arch/x86/include/asm/mrst.h
index 719f00b28ff..81ade903546 100644
--- a/arch/x86/include/asm/mrst.h
+++ b/arch/x86/include/asm/mrst.h
@@ -61,5 +61,10 @@ extern void intel_scu_devices_destroy(void);
/*#define MRST_VRTC_PGOFFSET (0xc00) */
extern void mrst_rtc_init(void);
+#ifdef CONFIG_MRST_S0I3
+extern void s0i3_pci_init(void);
+#else
+static inline void s0i3_pci_init(void) { }
+#endif
#endif /* _ASM_X86_MRST_H */
diff --git a/arch/x86/pci/mrst.c b/arch/x86/pci/mrst.c
index cb29191cee5..cf708773610 100644
--- a/arch/x86/pci/mrst.c
+++ b/arch/x86/pci/mrst.c
@@ -31,6 +31,7 @@
#include <asm/pci_x86.h>
#include <asm/hw_irq.h>
#include <asm/io_apic.h>
+#include <asm/mrst.h>
#define PCIE_CAP_OFFSET 0x100
@@ -235,6 +236,7 @@ int __init pci_mrst_init(void)
pci_mmcfg_late_init();
pcibios_enable_irq = mrst_pci_irq_enable;
pci_root_ops = pci_mrst_ops;
+ s0i3_pci_init();
/* Continue with standard init */
return 1;
}
diff --git a/drivers/idle/mrst_s0i3.c b/drivers/idle/mrst_s0i3.c
index d44e806662a..1047f68ac81 100644
--- a/drivers/idle/mrst_s0i3.c
+++ b/drivers/idle/mrst_s0i3.c
@@ -34,8 +34,10 @@
#include <asm/i387.h>
#include <asm/msr.h>
#include <asm/mtrr.h>
+#include <asm/mrst.h>
#include <asm/mwait.h>
#include "mrst_s0i3.h"
+#include "../pci/pci.h"
/* PMU register interface */
struct mrst_pmu_reg {
@@ -60,6 +62,7 @@ static u64 *wakeup_ptr;
static phys_addr_t s0i3_trampoline_phys;
static void *s0i3_trampoline_base;
static volatile bool s0i3_pmu_command_pending;
+static unsigned long s0i3_exclude_mask = 0x7f; /* All devices active */
/**
* mrst_idle
@@ -84,7 +87,8 @@ int mrst_idle(struct cpuidle_device *dev, struct cpuidle_state *state)
* is uninitialized, we cannot enter S0i3
*/
if (eax == -1UL &&
- (!pmu_reg || !cpumask_equal(cpu_online_mask, cpumask_of(cpu)))) {
+ (s0i3_exclude_mask || !pmu_reg ||
+ !cpumask_equal(cpu_online_mask, cpumask_of(cpu)))) {
eax = 0x52; /* Demote to C6 */
}
@@ -361,7 +365,12 @@ static irqreturn_t s0i3_pmu_irq(int irq, void *dummy)
return IRQ_HANDLED;
}
-/* XXX: Replace this with unified trampoline code */
+/*
+ * Reserve memory for the return-to-C6 trampoline. This is called
+ * extremely early in initialization in order to allocate low memory.
+ *
+ * XXX: Replace this with unified trampoline code.
+ */
extern const char s0i3_trampoline_data[], s0i3_trampoline_data_end[];
void s0i3_reserve_memory(void)
@@ -382,7 +391,82 @@ void s0i3_reserve_memory(void)
memblock_x86_reserve_range(mem, mem + size, "S0I3");
}
-/* Hacky! */
+/*
+ * Callback for power management. We need to track certain Langwell
+ * devices because they do not work with S0i3.
+ * The use of a hardcoded list here is a hack.
+ */
+static int mrst_pci_set_power_state(struct pci_dev *dev, pci_power_t state)
+{
+ unsigned int index;
+
+ printk(KERN_DEBUG "s0i3: mrst_pci_set_power_state %04x:%04x %d\n",
+ dev->vendor, dev->device, state);
+
+ if (dev->vendor != PCI_VENDOR_ID_INTEL)
+ return 0;
+
+ switch (dev->device) {
+ case 0x080a: /* Audio controller */
+ index = 0;
+ break;
+ case 0x080d: /* Primary display device */
+ index = 1;
+ break;
+ case 0x4102: /* Secondary display device */
+ index = 2;
+ break;
+ case 0x0806: /* USB controller #1 */
+ index = 3;
+ break;
+ case 0x0811: /* USB controller #2 */
+ index = 4;
+ break;
+ case 0x0814: /* DMA controller 1 */
+ index = 5;
+ break;
+ case 0x0813: /* DMA controller 2 */
+ index = 6;
+ break;
+ default:
+ return 0;
+ }
+
+ switch (state) {
+ case PCI_D0: /* Consider these states "on" */
+ case PCI_D1:
+ case PCI_D2:
+ set_bit(index, &s0i3_exclude_mask);
+ break;
+ case PCI_D3hot:
+ case PCI_D3cold:
+ clear_bit(index, &s0i3_exclude_mask);
+ break;
+ }
+
+ printk(KERN_DEBUG "s0i3: exclude_mask = %lx\n", s0i3_exclude_mask);
+
+ return 0;
+}
+
+static bool mrst_pci_power_manageable(struct pci_dev *dev)
+{
+ (void)dev;
+ return true;
+}
+
+static struct pci_platform_pm_ops mrst_s0i3_pci_platform_pm = {
+ .is_manageable = mrst_pci_power_manageable,
+ .set_state = mrst_pci_set_power_state,
+};
+
+/* Called from arch/x86/pci/mrst.c */
+void __init s0i3_pci_init(void)
+{
+ pci_set_platform_pm(&mrst_s0i3_pci_platform_pm);
+}
+
+/* Hacky - should be replaced with a registered PCI ID driver */
static int s0i3_prepare(void)
{
int err;
@@ -428,7 +512,7 @@ static int s0i3_prepare(void)
tmp |= 0x100; /* Enable interrupts */
writel(tmp, &pmu_reg->pm_ics);
- /* Set up the wakeup trampoline */
+ /* Set up the return-to-C6 code trampoline in low memory */
memcpy(s0i3_trampoline_base, s0i3_trampoline_data,
s0i3_trampoline_data_end - s0i3_trampoline_data);