diff options
-rw-r--r-- | MAINTAINERS | 8 | ||||
-rw-r--r-- | arch/x86/include/asm/pmc_core.h | 27 | ||||
-rw-r--r-- | drivers/platform/x86/Kconfig | 12 | ||||
-rw-r--r-- | drivers/platform/x86/Makefile | 1 | ||||
-rw-r--r-- | drivers/platform/x86/intel_pmc_core.c | 200 | ||||
-rw-r--r-- | drivers/platform/x86/intel_pmc_core.h | 51 |
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 | |||
5879 | F: arch/x86/include/asm/intel_telemetry.h | 5879 | F: arch/x86/include/asm/intel_telemetry.h |
5880 | F: drivers/platform/x86/intel_telemetry* | 5880 | F: drivers/platform/x86/intel_telemetry* |
5881 | 5881 | ||
5882 | INTEL PMC CORE DRIVER | ||
5883 | M: Rajneesh Bhardwaj <rajneesh.bhardwaj@intel.com> | ||
5884 | M: Vishwanath Somayaji <vishwanath.somayaji@intel.com> | ||
5885 | L: platform-driver-x86@vger.kernel.org | ||
5886 | S: Maintained | ||
5887 | F: arch/x86/include/asm/pmc_core.h | ||
5888 | F: drivers/platform/x86/intel_pmc_core* | ||
5889 | |||
5882 | IOC3 ETHERNET DRIVER | 5890 | IOC3 ETHERNET DRIVER |
5883 | M: Ralf Baechle <ralf@linux-mips.org> | 5891 | M: Ralf Baechle <ralf@linux-mips.org> |
5884 | L: linux-mips@linux-mips.org | 5892 | L: 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 */ | ||
25 | int 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 | ||
849 | config 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 | |||
849 | config IBM_RTL | 861 | config 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 | |||
69 | obj-$(CONFIG_INTEL_TELEMETRY) += intel_telemetry_core.o \ | 69 | obj-$(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 |
72 | obj-$(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 | |||
33 | static struct pmc_dev pmc; | ||
34 | |||
35 | static 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 | |||
40 | static inline u32 pmc_core_reg_read(struct pmc_dev *pmcdev, int reg_offset) | ||
41 | { | ||
42 | return readl(pmcdev->regbase + reg_offset); | ||
43 | } | ||
44 | |||
45 | static 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 | */ | ||
65 | int 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 | } | ||
78 | EXPORT_SYMBOL_GPL(intel_pmc_slp_s0_counter_read); | ||
79 | |||
80 | #if IS_ENABLED(CONFIG_DEBUG_FS) | ||
81 | static 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 | |||
93 | static 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 | |||
98 | static 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 | |||
105 | static void pmc_core_dbgfs_unregister(struct pmc_dev *pmcdev) | ||
106 | { | ||
107 | debugfs_remove_recursive(pmcdev->dbgfs_dir); | ||
108 | } | ||
109 | |||
110 | static 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 | ||
130 | static inline int pmc_core_dbgfs_register(struct pmc_dev *pmcdev) | ||
131 | { | ||
132 | return 0; | ||
133 | } | ||
134 | |||
135 | static inline void pmc_core_dbgfs_unregister(struct pmc_dev *pmcdev) | ||
136 | { | ||
137 | } | ||
138 | #endif /* CONFIG_DEBUG_FS */ | ||
139 | |||
140 | static 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 | |||
148 | static 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 | |||
194 | static 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 | |||
200 | builtin_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 | */ | ||
42 | struct 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 */ | ||