aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorH. Peter Anvin <hpa@linux.intel.com>2010-12-03 17:52:15 -0800
committerH. Peter Anvin <hpa@linux.intel.com>2010-12-14 10:48:40 -0800
commitca5bf9c69938d95b0915c420351bea990d0bf5f7 (patch)
treeb62763ece9adadbb6d4d1b20cb80970fb5d15977
parent42dd704f9ac40347113cf827c2e07e839069c3f3 (diff)
downloadmrst-s0i3-test-ca5bf9c69938d95b0915c420351bea990d0bf5f7.tar.gz
mrst-s0i3-test-ca5bf9c69938d95b0915c420351bea990d0bf5f7.tar.xz
mrst-s0i3-test-ca5bf9c69938d95b0915c420351bea990d0bf5f7.zip
s0i3: add interrupt handler
-rw-r--r--drivers/idle/mrst_s0i3.c100
1 files changed, 70 insertions, 30 deletions
diff --git a/drivers/idle/mrst_s0i3.c b/drivers/idle/mrst_s0i3.c
index 44a6fcddd7d..6c6ca7925c8 100644
--- a/drivers/idle/mrst_s0i3.c
+++ b/drivers/idle/mrst_s0i3.c
@@ -35,8 +35,24 @@
#include <asm/mwait.h>
#include "mrst_s0i3.h"
+/* PMU register interface */
+struct mrst_pmu_reg {
+ u32 pm_sts; /* 0x00 */
+ u32 pm_cmd; /* 0x04 */
+ u32 pm_ics; /* 0x08 */
+ u32 _resv1;
+ u32 pm_wkc[2]; /* 0x10 */
+ u32 pm_wks[2]; /* 0x18 */
+ u32 pm_ssc[4]; /* 0x20 */
+ u32 pm_sss[4]; /* 0x30 */
+ u32 pm_wssc[4]; /* 0x40 */
+ u32 pm_c3c4; /* 0x50 */
+ u32 pm_c5c6; /* 0x54 */
+ u32 pm_msic; /* 0x58 */
+};
+
static void do_s0i3(void);
-static volatile u32 *pmu_reg;
+static volatile struct mrst_pmu_reg *pmu_reg;
static struct pci_dev *pmu_dev; /* South Complex PMU unit */
static u64 *wakeup_ptr;
@@ -193,32 +209,29 @@ static void s0i3_poke_other_cpu(void)
/*
* Send a command to the PMU to shut down the south complex
*/
-enum pmu_regs {
- PM_STS = 0,
- PM_CMD = 1,
- PM_ICS = 2,
- PM_WKC0 = 4,
- PM_WKS0 = 6,
- PM_SSC0 = 8,
- PM_SSS0 = 12,
- PM_WSSC0 = 16,
- PM_MSIC = 22
-};
#define WAKE_CAPABLE 0x80000000
-#define AUTO_CLK_GATE_VALUE 0x555551
-#define SUB_SYS_D0I2_VALUE 0xaaaaaa
-#define WAKE_ENABLE_VALUE 0x4786
+#define AUTO_CLK_GATE_VALUE 0x00555551
+#define SUB_SYS_D0I2_VALUE 0x00aaaaaa
+#define WAKE_ENABLE_VALUE 0x0786 /* 0x4786? */
#define SUSPEND_GFX 0xc
+static void s0i3_wait_for_pmu(void)
+{
+ while (readl(&pmu_reg->pm_sts) & (1 << 8))
+ cpu_relax();
+}
+
static void s0i3_prep_pmu(void)
{
+ s0i3_wait_for_pmu();
+
/* Program the wakeup */
- writel(WAKE_ENABLE_VALUE, &pmu_reg[PM_WKC0]);
- writel(AUTO_CLK_GATE_VALUE, &pmu_reg[PM_WSSC0]);
+ writel(WAKE_ENABLE_VALUE, &pmu_reg->pm_wkc[0]);
+ writel(AUTO_CLK_GATE_VALUE, &pmu_reg->pm_wssc[0]);
/* Clock gate Langwell */
- writel(SUB_SYS_D0I2_VALUE, &pmu_reg[PM_SSC0]);
+ writel(SUB_SYS_D0I2_VALUE, &pmu_reg->pm_ssc[0]);
/* Do this when S0i3 is entered */
writel((0 << 31) | /* Reserved */
@@ -226,17 +239,11 @@ static void s0i3_prep_pmu(void)
(0xc2 << 22) | /* ACK C6 trigger */
(3 << 19) | /* Trigger on DMI message */
(3 << 16) | /* Enter S0i3 */
- (3 << 13) | /* Numeric mode ID (sw) */
+ (0 << 13) | /* Numeric mode ID (sw) */
(3 << 9) | /* Trigger mode */
(0 << 8) | /* Do not interrupt */
(1 << 0), /* Set configuration */
- &pmu_reg[PM_CMD]);
-}
-
-static void s0i3_wait_for_pmu(void)
-{
- while (readl(&pmu_reg[PM_STS]) & (1 << 8))
- cpu_relax();
+ &pmu_reg->pm_cmd);
}
static inline void s0i3_update_wake_pointer(void)
@@ -250,7 +257,6 @@ static noinline void do_s0i3(void)
s0i3_save_lapic();
s0i3_save_msrs();
save_processor_state();
- s0i3_wait_for_pmu();
s0i3_prep_pmu();
if (mrst_s0i3_entry()) {
s0i3_restore_msrs();
@@ -258,7 +264,7 @@ static noinline void do_s0i3(void)
s0i3_restore_lapic();
/* HACK HACK HACK Enable MSI interrupts again */
- writel(0x0, &pmu_dev[PM_MSIC]);
+ writel(0x0, &pmu_reg->pm_msic);
s0i3_poke_other_cpu();
} else {
@@ -288,10 +294,33 @@ static int s0i3_sfi_parse_wake(struct sfi_table_header *table)
return wakeup_ptr ? 0 : -ENOMEM;
}
+/*
+ * Interrupt handler. The purpose of the interrupt is to break us
+ * out of an error condition where we ended up in C6 rather than S0i3;
+ * the cpuidle loop will then retry the S0i3 condition at a later time.
+ */
+static irqreturn_t s0i3_pmu_irq(int irq, void *dummy)
+{
+ u32 status;
+
+ (void)dummy;
+
+ status = readl(&pmu_reg->pm_ics);
+
+ if (!(status & 0x200))
+ return IRQ_NONE; /* Not ours */
+
+ /* Clear the status */
+ writel(status, &pmu_reg->pm_ics);
+ return IRQ_HANDLED;
+}
+
/* Hacky! */
static int s0i3_prepare(void)
{
int err;
+ volatile struct mrst_pmu_reg *pmu;
+ u32 tmp;
/* Map the PMU unit */
pmu_dev = pci_get_device(0x8086, 0x0810, NULL);
@@ -311,12 +340,23 @@ static int s0i3_prepare(void)
if (err)
goto err_disable_pdev;
- pmu_reg = pci_iomap(pmu_dev, 0, 0);
- if (!pmu_reg) {
+ pmu = pci_iomap(pmu_dev, 0, 0);
+ if (!pmu) {
err = -ENOMEM;
goto err_disable_pdev;
}
+ err = request_irq(pmu_dev->irq, s0i3_pmu_irq, 0, "mrst_s0i3", NULL);
+ if (err)
+ goto err_disable_pdev;
+
+ pmu_reg = pmu;
+
+ /* Enable the hardware interrupt */
+ tmp = readl(&pmu_reg->pm_ics);
+ tmp |= 0x100; /* Enable interrupts */
+ writel(tmp, &pmu_reg->pm_ics);
+
return 0;
err_disable_pdev: