summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRajneesh Bhardwaj <rajneesh.bhardwaj@intel.com>2016-05-26 05:11:19 -0400
committerDarren Hart <dvhart@linux.intel.com>2016-05-27 14:47:56 -0400
commitb740d2e9233cb33626d3b62210bcfc6a34baa839 (patch)
treebb0ed870f77fef2bffaded52040e174578bd6caa
parentff8651237f39cea60dc89b2d9f25d9ede3fc82c0 (diff)
platform/x86: Add PMC Driver for Intel Core SoC
This patch adds the Power Management Controller driver as a PCI driver for Intel Core SoC architecture. This driver can utilize debugging capabilities and supported features as exposed by the Power Management Controller. Please refer to the below specification for more details on PMC features. http://www.intel.in/content/www/in/en/chipsets/100-series-chipset-datasheet-vol-2.html The current version of this driver exposes SLP_S0_RESIDENCY counter. This counter can be used for detecting fragile SLP_S0 signal related failures and take corrective actions when PCH SLP_S0 signal is not asserted after kernel freeze as part of suspend to idle flow (echo freeze > /sys/power/state). Intel Platform Controller Hub (PCH) asserts SLP_S0 signal when it detects favorable conditions to enter its low power mode. As a pre-requisite the SoC should be in deepest possible Package C-State and devices should be in low power mode. For example, on Skylake SoC the deepest Package C-State is Package C10 or PC10. Suspend to idle flow generally leads to PC10 state but PC10 state may not be sufficient for realizing the platform wide power potential which SLP_S0 signal assertion can provide. SLP_S0 signal is often connected to the Embedded Controller (EC) and the Power Management IC (PMIC) for other platform power management related optimizations. In general, SLP_S0 assertion == PC10 + PCH low power mode + ModPhy Lanes power gated + PLL Idle. As part of this driver, a mechanism to read the SLP_S0_RESIDENCY is exposed as an API and also debugfs features are added to indicate SLP_S0 signal assertion residency in microseconds. echo freeze > /sys/power/state wake the system cat /sys/kernel/debug/pmc_core/slp_s0_residency_usec Signed-off-by: Rajneesh Bhardwaj <rajneesh.bhardwaj@intel.com> Signed-off-by: Vishwanath Somayaji <vishwanath.somayaji@intel.com> Reviewed-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com> Signed-off-by: Darren Hart <dvhart@linux.intel.com>
-rw-r--r--MAINTAINERS8
-rw-r--r--arch/x86/include/asm/pmc_core.h27
-rw-r--r--drivers/platform/x86/Kconfig12
-rw-r--r--drivers/platform/x86/Makefile1
-rw-r--r--drivers/platform/x86/intel_pmc_core.c200
-rw-r--r--drivers/platform/x86/intel_pmc_core.h51
6 files changed, 299 insertions, 0 deletions
diff --git a/MAINTAINERS b/MAINTAINERS
index 1c32f8a3d6c4..32a57926cd04 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -5879,6 +5879,14 @@ S: Maintained
5879F: arch/x86/include/asm/intel_telemetry.h 5879F: arch/x86/include/asm/intel_telemetry.h
5880F: drivers/platform/x86/intel_telemetry* 5880F: drivers/platform/x86/intel_telemetry*
5881 5881
5882INTEL PMC CORE DRIVER
5883M: Rajneesh Bhardwaj <rajneesh.bhardwaj@intel.com>
5884M: Vishwanath Somayaji <vishwanath.somayaji@intel.com>
5885L: platform-driver-x86@vger.kernel.org
5886S: Maintained
5887F: arch/x86/include/asm/pmc_core.h
5888F: drivers/platform/x86/intel_pmc_core*
5889
5882IOC3 ETHERNET DRIVER 5890IOC3 ETHERNET DRIVER
5883M: Ralf Baechle <ralf@linux-mips.org> 5891M: Ralf Baechle <ralf@linux-mips.org>
5884L: linux-mips@linux-mips.org 5892L: linux-mips@linux-mips.org
diff --git a/arch/x86/include/asm/pmc_core.h b/arch/x86/include/asm/pmc_core.h
new file mode 100644
index 000000000000..d4855f11136d
--- /dev/null
+++ b/arch/x86/include/asm/pmc_core.h
@@ -0,0 +1,27 @@
1/*
2 * Intel Core SoC Power Management Controller Header File
3 *
4 * Copyright (c) 2016, Intel Corporation.
5 * All Rights Reserved.
6 *
7 * Authors: Rajneesh Bhardwaj <rajneesh.bhardwaj@intel.com>
8 * Vishwanath Somayaji <vishwanath.somayaji@intel.com>
9 *
10 * This program is free software; you can redistribute it and/or modify it
11 * under the terms and conditions of the GNU General Public License,
12 * version 2, as published by the Free Software Foundation.
13 *
14 * This program is distributed in the hope it will be useful, but WITHOUT
15 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
16 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
17 * more details.
18 *
19 */
20
21#ifndef _ASM_PMC_CORE_H
22#define _ASM_PMC_CORE_H
23
24/* API to read SLP_S0_RESIDENCY counter */
25int intel_pmc_slp_s0_counter_read(u32 *data);
26
27#endif /* _ASM_PMC_CORE_H */
diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig
index ed2004be13cf..c06bb85c2839 100644
--- a/drivers/platform/x86/Kconfig
+++ b/drivers/platform/x86/Kconfig
@@ -846,6 +846,18 @@ config INTEL_IMR
846 846
847 If you are running on a Galileo/Quark say Y here. 847 If you are running on a Galileo/Quark say Y here.
848 848
849config INTEL_PMC_CORE
850 bool "Intel PMC Core driver"
851 depends on X86 && PCI
852 ---help---
853 The Intel Platform Controller Hub for Intel Core SoCs provides access
854 to Power Management Controller registers via a PCI interface. This
855 driver can utilize debugging capabilities and supported features as
856 exposed by the Power Management Controller.
857
858 Supported features:
859 - SLP_S0_RESIDENCY counter.
860
849config IBM_RTL 861config IBM_RTL
850 tristate "Device driver to enable PRTL support" 862 tristate "Device driver to enable PRTL support"
851 depends on X86 && PCI 863 depends on X86 && PCI
diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile
index 448443c3baba..9b11b4073e03 100644
--- a/drivers/platform/x86/Makefile
+++ b/drivers/platform/x86/Makefile
@@ -69,3 +69,4 @@ obj-$(CONFIG_INTEL_PUNIT_IPC) += intel_punit_ipc.o
69obj-$(CONFIG_INTEL_TELEMETRY) += intel_telemetry_core.o \ 69obj-$(CONFIG_INTEL_TELEMETRY) += intel_telemetry_core.o \
70 intel_telemetry_pltdrv.o \ 70 intel_telemetry_pltdrv.o \
71 intel_telemetry_debugfs.o 71 intel_telemetry_debugfs.o
72obj-$(CONFIG_INTEL_PMC_CORE) += intel_pmc_core.o
diff --git a/drivers/platform/x86/intel_pmc_core.c b/drivers/platform/x86/intel_pmc_core.c
new file mode 100644
index 000000000000..2776bec89c88
--- /dev/null
+++ b/drivers/platform/x86/intel_pmc_core.c
@@ -0,0 +1,200 @@
1/*
2 * Intel Core SoC Power Management Controller Driver
3 *
4 * Copyright (c) 2016, Intel Corporation.
5 * All Rights Reserved.
6 *
7 * Authors: Rajneesh Bhardwaj <rajneesh.bhardwaj@intel.com>
8 * Vishwanath Somayaji <vishwanath.somayaji@intel.com>
9 *
10 * This program is free software; you can redistribute it and/or modify it
11 * under the terms and conditions of the GNU General Public License,
12 * version 2, as published by the Free Software Foundation.
13 *
14 * This program is distributed in the hope it will be useful, but WITHOUT
15 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
16 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
17 * more details.
18 *
19 */
20
21#include <linux/debugfs.h>
22#include <linux/device.h>
23#include <linux/init.h>
24#include <linux/io.h>
25#include <linux/pci.h>
26#include <linux/seq_file.h>
27
28#include <asm/cpu_device_id.h>
29#include <asm/pmc_core.h>
30
31#include "intel_pmc_core.h"
32
33static struct pmc_dev pmc;
34
35static const struct pci_device_id pmc_pci_ids[] = {
36 { PCI_VDEVICE(INTEL, SPT_PMC_PCI_DEVICE_ID), (kernel_ulong_t)NULL },
37 { 0, },
38};
39
40static inline u32 pmc_core_reg_read(struct pmc_dev *pmcdev, int reg_offset)
41{
42 return readl(pmcdev->regbase + reg_offset);
43}
44
45static inline u32 pmc_core_adjust_slp_s0_step(u32 value)
46{
47 return value * SPT_PMC_SLP_S0_RES_COUNTER_STEP;
48}
49
50/**
51 * intel_pmc_slp_s0_counter_read() - Read SLP_S0 residency.
52 * @data: Out param that contains current SLP_S0 count.
53 *
54 * This API currently supports Intel Skylake SoC and Sunrise
55 * Point Platform Controller Hub. Future platform support
56 * should be added for platforms that support low power modes
57 * beyond Package C10 state.
58 *
59 * SLP_S0_RESIDENCY counter counts in 100 us granularity per
60 * step hence function populates the multiplied value in out
61 * parameter @data.
62 *
63 * Return: an error code or 0 on success.
64 */
65int intel_pmc_slp_s0_counter_read(u32 *data)
66{
67 struct pmc_dev *pmcdev = &pmc;
68 u32 value;
69
70 if (!pmcdev->has_slp_s0_res)
71 return -EACCES;
72
73 value = pmc_core_reg_read(pmcdev, SPT_PMC_SLP_S0_RES_COUNTER_OFFSET);
74 *data = pmc_core_adjust_slp_s0_step(value);
75
76 return 0;
77}
78EXPORT_SYMBOL_GPL(intel_pmc_slp_s0_counter_read);
79
80#if IS_ENABLED(CONFIG_DEBUG_FS)
81static int pmc_core_dev_state_show(struct seq_file *s, void *unused)
82{
83 struct pmc_dev *pmcdev = s->private;
84 u32 counter_val;
85
86 counter_val = pmc_core_reg_read(pmcdev,
87 SPT_PMC_SLP_S0_RES_COUNTER_OFFSET);
88 seq_printf(s, "%u\n", pmc_core_adjust_slp_s0_step(counter_val));
89
90 return 0;
91}
92
93static int pmc_core_dev_state_open(struct inode *inode, struct file *file)
94{
95 return single_open(file, pmc_core_dev_state_show, inode->i_private);
96}
97
98static const struct file_operations pmc_core_dev_state_ops = {
99 .open = pmc_core_dev_state_open,
100 .read = seq_read,
101 .llseek = seq_lseek,
102 .release = single_release,
103};
104
105static void pmc_core_dbgfs_unregister(struct pmc_dev *pmcdev)
106{
107 debugfs_remove_recursive(pmcdev->dbgfs_dir);
108}
109
110static int pmc_core_dbgfs_register(struct pmc_dev *pmcdev)
111{
112 struct dentry *dir, *file;
113
114 dir = debugfs_create_dir("pmc_core", NULL);
115 if (!dir)
116 return -ENOMEM;
117
118 pmcdev->dbgfs_dir = dir;
119 file = debugfs_create_file("slp_s0_residency_usec", S_IFREG | S_IRUGO,
120 dir, pmcdev, &pmc_core_dev_state_ops);
121
122 if (!file) {
123 pmc_core_dbgfs_unregister(pmcdev);
124 return -ENODEV;
125 }
126
127 return 0;
128}
129#else
130static inline int pmc_core_dbgfs_register(struct pmc_dev *pmcdev)
131{
132 return 0;
133}
134
135static inline void pmc_core_dbgfs_unregister(struct pmc_dev *pmcdev)
136{
137}
138#endif /* CONFIG_DEBUG_FS */
139
140static const struct x86_cpu_id intel_pmc_core_ids[] = {
141 { X86_VENDOR_INTEL, 6, 0x4e, X86_FEATURE_MWAIT,
142 (kernel_ulong_t)NULL}, /* Skylake CPUID Signature */
143 { X86_VENDOR_INTEL, 6, 0x5e, X86_FEATURE_MWAIT,
144 (kernel_ulong_t)NULL}, /* Skylake CPUID Signature */
145 {}
146};
147
148static int pmc_core_probe(struct pci_dev *dev, const struct pci_device_id *id)
149{
150 struct device *ptr_dev = &dev->dev;
151 struct pmc_dev *pmcdev = &pmc;
152 const struct x86_cpu_id *cpu_id;
153 int err;
154
155 cpu_id = x86_match_cpu(intel_pmc_core_ids);
156 if (!cpu_id) {
157 dev_dbg(&dev->dev, "PMC Core: cpuid mismatch.\n");
158 return -EINVAL;
159 }
160
161 err = pcim_enable_device(dev);
162 if (err < 0) {
163 dev_dbg(&dev->dev, "PMC Core: failed to enable Power Management Controller.\n");
164 return err;
165 }
166
167 err = pci_read_config_dword(dev,
168 SPT_PMC_BASE_ADDR_OFFSET,
169 &pmcdev->base_addr);
170 if (err < 0) {
171 dev_dbg(&dev->dev, "PMC Core: failed to read PCI config space.\n");
172 return err;
173 }
174 dev_dbg(&dev->dev, "PMC Core: PWRMBASE is %#x\n", pmcdev->base_addr);
175
176 pmcdev->regbase = devm_ioremap_nocache(ptr_dev,
177 pmcdev->base_addr,
178 SPT_PMC_MMIO_REG_LEN);
179 if (!pmcdev->regbase) {
180 dev_dbg(&dev->dev, "PMC Core: ioremap failed.\n");
181 return -ENOMEM;
182 }
183
184 err = pmc_core_dbgfs_register(pmcdev);
185 if (err < 0) {
186 dev_err(&dev->dev, "PMC Core: debugfs register failed.\n");
187 return err;
188 }
189
190 pmc.has_slp_s0_res = true;
191 return 0;
192}
193
194static struct pci_driver intel_pmc_core_driver = {
195 .name = "intel_pmc_core",
196 .id_table = pmc_pci_ids,
197 .probe = pmc_core_probe,
198};
199
200builtin_pci_driver(intel_pmc_core_driver);
diff --git a/drivers/platform/x86/intel_pmc_core.h b/drivers/platform/x86/intel_pmc_core.h
new file mode 100644
index 000000000000..a9dadaf787c1
--- /dev/null
+++ b/drivers/platform/x86/intel_pmc_core.h
@@ -0,0 +1,51 @@
1/*
2 * Intel Core SoC Power Management Controller Header File
3 *
4 * Copyright (c) 2016, Intel Corporation.
5 * All Rights Reserved.
6 *
7 * Authors: Rajneesh Bhardwaj <rajneesh.bhardwaj@intel.com>
8 * Vishwanath Somayaji <vishwanath.somayaji@intel.com>
9 *
10 * This program is free software; you can redistribute it and/or modify it
11 * under the terms and conditions of the GNU General Public License,
12 * version 2, as published by the Free Software Foundation.
13 *
14 * This program is distributed in the hope it will be useful, but WITHOUT
15 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
16 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
17 * more details.
18 *
19 */
20
21#ifndef PMC_CORE_H
22#define PMC_CORE_H
23
24/* Sunrise Point Power Management Controller PCI Device ID */
25#define SPT_PMC_PCI_DEVICE_ID 0x9d21
26#define SPT_PMC_BASE_ADDR_OFFSET 0x48
27#define SPT_PMC_SLP_S0_RES_COUNTER_OFFSET 0x13c
28#define SPT_PMC_MMIO_REG_LEN 0x100
29#define SPT_PMC_SLP_S0_RES_COUNTER_STEP 0x64
30
31/**
32 * struct pmc_dev - pmc device structure
33 * @base_addr: comtains pmc base address
34 * @regbase: pointer to io-remapped memory location
35 * @dbgfs_dir: path to debug fs interface
36 * @feature_available: flag to indicate whether
37 * the feature is available
38 * on a particular platform or not.
39 *
40 * pmc_dev contains info about power management controller device.
41 */
42struct pmc_dev {
43 u32 base_addr;
44 void __iomem *regbase;
45#if IS_ENABLED(CONFIG_DEBUG_FS)
46 struct dentry *dbgfs_dir;
47#endif /* CONFIG_DEBUG_FS */
48 bool has_slp_s0_res;
49};
50
51#endif /* PMC_CORE_H */