summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--arch/x86/include/asm/cpu.h1
-rw-r--r--arch/x86/include/asm/pmu.h11
-rw-r--r--arch/x86/lib/Makefile1
-rw-r--r--arch/x86/lib/pmu.c117
4 files changed, 130 insertions, 0 deletions
diff --git a/arch/x86/include/asm/cpu.h b/arch/x86/include/asm/cpu.h
index 0ee13b1eb1..c00687a20a 100644
--- a/arch/x86/include/asm/cpu.h
+++ b/arch/x86/include/asm/cpu.h
@@ -54,6 +54,7 @@ enum {
X86_NONE,
X86_SYSCON_ME, /* Intel Management Engine */
X86_SYSCON_PINCONF, /* Intel x86 pin configuration */
+ X86_SYSCON_PMU, /* Power Management Unit */
X86_SYSCON_SCU, /* System Controller Unit */
};
diff --git a/arch/x86/include/asm/pmu.h b/arch/x86/include/asm/pmu.h
new file mode 100644
index 0000000000..96b968ff8f
--- /dev/null
+++ b/arch/x86/include/asm/pmu.h
@@ -0,0 +1,11 @@
+/*
+ * Copyright (c) 2017 Intel Corporation
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+#ifndef _X86_ASM_PMU_IPC_H_
+#define _X86_ASM_PMU_IPC_H_
+
+int pmu_turn_power(unsigned int lss, bool on);
+
+#endif /* _X86_ASM_PMU_IPC_H_ */
diff --git a/arch/x86/lib/Makefile b/arch/x86/lib/Makefile
index 320e45e4a0..d1ad37af64 100644
--- a/arch/x86/lib/Makefile
+++ b/arch/x86/lib/Makefile
@@ -31,6 +31,7 @@ obj-y += pinctrl_ich6.o
obj-y += pirq_routing.o
obj-y += relocate.o
obj-y += physmem.o
+obj-$(CONFIG_INTEL_MID) += pmu.o
obj-$(CONFIG_X86_RAMTEST) += ramtest.o
obj-$(CONFIG_INTEL_MID) += scu.o
obj-y += sections.o
diff --git a/arch/x86/lib/pmu.c b/arch/x86/lib/pmu.c
new file mode 100644
index 0000000000..4ceab8dc64
--- /dev/null
+++ b/arch/x86/lib/pmu.c
@@ -0,0 +1,117 @@
+/*
+ * Copyright (c) 2017 Intel Corporation
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+#include <common.h>
+#include <dm.h>
+#include <regmap.h>
+#include <syscon.h>
+#include <asm/cpu.h>
+#include <asm/pmu.h>
+#include <linux/errno.h>
+#include <linux/io.h>
+
+/* Registers */
+struct pmu_regs {
+ u32 sts;
+ u32 cmd;
+ u32 ics;
+ u32 reserved;
+ u32 wkc[4];
+ u32 wks[4];
+ u32 ssc[4];
+ u32 sss[4];
+};
+
+/* Bits in PMU_REGS_STS */
+#define PMU_REGS_STS_BUSY (1 << 8)
+
+struct pmu_mid {
+ struct pmu_regs *regs;
+};
+
+static int pmu_read_status(struct pmu_regs *regs)
+{
+ int retry = 500000;
+ u32 val;
+
+ do {
+ val = readl(&regs->sts);
+ if (!(val & PMU_REGS_STS_BUSY))
+ return 0;
+
+ udelay(1);
+ } while (--retry);
+
+ printf("WARNING: PMU still busy\n");
+ return -EBUSY;
+}
+
+static int pmu_power_lss(struct pmu_regs *regs, unsigned int lss, bool on)
+{
+ unsigned int offset = (lss * 2) / 32;
+ unsigned int shift = (lss * 2) % 32;
+ u32 ssc;
+ int ret;
+
+ /* Check PMU status */
+ ret = pmu_read_status(regs);
+ if (ret)
+ return ret;
+
+ /* Read PMU values */
+ ssc = readl(&regs->sss[offset]);
+
+ /* Modify PMU values */
+ if (on)
+ ssc &= ~(0x3 << shift); /* D0 */
+ else
+ ssc |= 0x3 << shift; /* D3hot */
+
+ /* Write modified PMU values */
+ writel(ssc, &regs->ssc[offset]);
+
+ /* Update modified PMU values */
+ writel(0x00002201, &regs->cmd);
+
+ /* Check PMU status */
+ return pmu_read_status(regs);
+}
+
+int pmu_turn_power(unsigned int lss, bool on)
+{
+ struct pmu_mid *pmu;
+ struct udevice *dev;
+ int ret;
+
+ ret = syscon_get_by_driver_data(X86_SYSCON_PMU, &dev);
+ if (ret)
+ return ret;
+
+ pmu = dev_get_priv(dev);
+
+ return pmu_power_lss(pmu->regs, lss, on);
+}
+
+static int pmu_mid_probe(struct udevice *dev)
+{
+ struct pmu_mid *pmu = dev_get_priv(dev);
+
+ pmu->regs = syscon_get_first_range(X86_SYSCON_PMU);
+
+ return 0;
+}
+
+static const struct udevice_id pmu_mid_match[] = {
+ { .compatible = "intel,pmu-mid", .data = X86_SYSCON_PMU },
+ { /* sentinel */ }
+};
+
+U_BOOT_DRIVER(intel_mid_pmu) = {
+ .name = "pmu_mid",
+ .id = UCLASS_SYSCON,
+ .of_match = pmu_mid_match,
+ .probe = pmu_mid_probe,
+ .priv_auto_alloc_size = sizeof(struct pmu_mid),
+};