Commit | Line | Data |
---|---|---|
b740d2e9 RB |
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> | |
b740d2e9 RB |
26 | |
27 | #include <asm/cpu_device_id.h> | |
70e0d117 | 28 | #include <asm/intel-family.h> |
b740d2e9 RB |
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 | ||
df2294fb | 80 | static int pmc_core_dev_state_get(void *data, u64 *val) |
b740d2e9 | 81 | { |
df2294fb AS |
82 | struct pmc_dev *pmcdev = data; |
83 | u32 value; | |
b740d2e9 | 84 | |
df2294fb AS |
85 | value = pmc_core_reg_read(pmcdev, SPT_PMC_SLP_S0_RES_COUNTER_OFFSET); |
86 | *val = pmc_core_adjust_slp_s0_step(value); | |
b740d2e9 RB |
87 | |
88 | return 0; | |
89 | } | |
90 | ||
df2294fb | 91 | DEFINE_DEBUGFS_ATTRIBUTE(pmc_core_dev_state, pmc_core_dev_state_get, NULL, "%llu\n"); |
b740d2e9 RB |
92 | |
93 | static void pmc_core_dbgfs_unregister(struct pmc_dev *pmcdev) | |
94 | { | |
95 | debugfs_remove_recursive(pmcdev->dbgfs_dir); | |
96 | } | |
97 | ||
98 | static int pmc_core_dbgfs_register(struct pmc_dev *pmcdev) | |
99 | { | |
100 | struct dentry *dir, *file; | |
101 | ||
102 | dir = debugfs_create_dir("pmc_core", NULL); | |
df2294fb | 103 | if (IS_ERR_OR_NULL(dir)) |
b740d2e9 RB |
104 | return -ENOMEM; |
105 | ||
106 | pmcdev->dbgfs_dir = dir; | |
107 | file = debugfs_create_file("slp_s0_residency_usec", S_IFREG | S_IRUGO, | |
df2294fb | 108 | dir, pmcdev, &pmc_core_dev_state); |
b740d2e9 RB |
109 | |
110 | if (!file) { | |
111 | pmc_core_dbgfs_unregister(pmcdev); | |
112 | return -ENODEV; | |
113 | } | |
114 | ||
115 | return 0; | |
116 | } | |
b740d2e9 RB |
117 | |
118 | static const struct x86_cpu_id intel_pmc_core_ids[] = { | |
70e0d117 DH |
119 | { X86_VENDOR_INTEL, 6, INTEL_FAM6_SKYLAKE_MOBILE, X86_FEATURE_MWAIT, |
120 | (kernel_ulong_t)NULL}, | |
121 | { X86_VENDOR_INTEL, 6, INTEL_FAM6_SKYLAKE_DESKTOP, X86_FEATURE_MWAIT, | |
122 | (kernel_ulong_t)NULL}, | |
b740d2e9 RB |
123 | {} |
124 | }; | |
125 | ||
126 | static int pmc_core_probe(struct pci_dev *dev, const struct pci_device_id *id) | |
127 | { | |
128 | struct device *ptr_dev = &dev->dev; | |
129 | struct pmc_dev *pmcdev = &pmc; | |
130 | const struct x86_cpu_id *cpu_id; | |
131 | int err; | |
132 | ||
133 | cpu_id = x86_match_cpu(intel_pmc_core_ids); | |
134 | if (!cpu_id) { | |
135 | dev_dbg(&dev->dev, "PMC Core: cpuid mismatch.\n"); | |
136 | return -EINVAL; | |
137 | } | |
138 | ||
139 | err = pcim_enable_device(dev); | |
140 | if (err < 0) { | |
141 | dev_dbg(&dev->dev, "PMC Core: failed to enable Power Management Controller.\n"); | |
142 | return err; | |
143 | } | |
144 | ||
145 | err = pci_read_config_dword(dev, | |
146 | SPT_PMC_BASE_ADDR_OFFSET, | |
147 | &pmcdev->base_addr); | |
148 | if (err < 0) { | |
149 | dev_dbg(&dev->dev, "PMC Core: failed to read PCI config space.\n"); | |
150 | return err; | |
151 | } | |
152 | dev_dbg(&dev->dev, "PMC Core: PWRMBASE is %#x\n", pmcdev->base_addr); | |
153 | ||
154 | pmcdev->regbase = devm_ioremap_nocache(ptr_dev, | |
155 | pmcdev->base_addr, | |
156 | SPT_PMC_MMIO_REG_LEN); | |
157 | if (!pmcdev->regbase) { | |
158 | dev_dbg(&dev->dev, "PMC Core: ioremap failed.\n"); | |
159 | return -ENOMEM; | |
160 | } | |
161 | ||
162 | err = pmc_core_dbgfs_register(pmcdev); | |
df2294fb AS |
163 | if (err < 0) |
164 | dev_warn(&dev->dev, "PMC Core: debugfs register failed.\n"); | |
b740d2e9 RB |
165 | |
166 | pmc.has_slp_s0_res = true; | |
167 | return 0; | |
168 | } | |
169 | ||
170 | static struct pci_driver intel_pmc_core_driver = { | |
171 | .name = "intel_pmc_core", | |
172 | .id_table = pmc_pci_ids, | |
173 | .probe = pmc_core_probe, | |
174 | }; | |
175 | ||
176 | builtin_pci_driver(intel_pmc_core_driver); |