]> Git Repo - J-linux.git/blob - drivers/platform/x86/intel/tpmi_power_domains.c
Merge tag 'vfs-6.13-rc7.fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs
[J-linux.git] / drivers / platform / x86 / intel / tpmi_power_domains.c
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * Mapping of TPMI power domains CPU mapping
4  *
5  * Copyright (c) 2024, Intel Corporation.
6  */
7
8 #include <linux/bitfield.h>
9 #include <linux/cleanup.h>
10 #include <linux/cpuhotplug.h>
11 #include <linux/cpumask.h>
12 #include <linux/errno.h>
13 #include <linux/export.h>
14 #include <linux/hashtable.h>
15 #include <linux/module.h>
16 #include <linux/mutex.h>
17 #include <linux/overflow.h>
18 #include <linux/slab.h>
19 #include <linux/topology.h>
20 #include <linux/types.h>
21
22 #include <asm/cpu_device_id.h>
23 #include <asm/intel-family.h>
24 #include <asm/msr.h>
25
26 #include "tpmi_power_domains.h"
27
28 #define MSR_PM_LOGICAL_ID       0x54
29
30 /*
31  * Struct of MSR 0x54
32  * [15:11] PM_DOMAIN_ID
33  * [10:3] MODULE_ID (aka IDI_AGENT_ID)
34  * [2:0] LP_ID
35  * For Atom:
36  *   [2] Always 0
37  *   [1:0] core ID within module
38  * For Core
39  *   [2:1] Always 0
40  *   [0] thread ID
41  */
42
43 #define LP_ID_MASK              GENMASK_ULL(2, 0)
44 #define MODULE_ID_MASK          GENMASK_ULL(10, 3)
45 #define PM_DOMAIN_ID_MASK       GENMASK_ULL(15, 11)
46
47 /**
48  * struct tpmi_cpu_info - Mapping information for a CPU
49  * @hnode: Used to add mapping information to hash list
50  * @linux_cpu:  Linux CPU number
51  * @pkg_id: Package ID of this CPU
52  * @punit_thread_id: Punit thread id of this CPU
53  * @punit_core_id: Punit core id
54  * @punit_domain_id: Power domain id from Punit
55  *
56  * Structure to store mapping information for a Linux CPU
57  * to a Punit core, thread and power domain.
58  */
59 struct tpmi_cpu_info {
60         struct hlist_node hnode;
61         int linux_cpu;
62         u8 pkg_id;
63         u8 punit_thread_id;
64         u8 punit_core_id;
65         u8 punit_domain_id;
66 };
67
68 static DEFINE_PER_CPU(struct tpmi_cpu_info, tpmi_cpu_info);
69
70 /* The dynamically assigned cpu hotplug state to free later */
71 static enum cpuhp_state tpmi_hp_state __read_mostly;
72
73 #define MAX_POWER_DOMAINS       8
74
75 static cpumask_t *tpmi_power_domain_mask;
76
77 /* Lock to protect tpmi_power_domain_mask and tpmi_cpu_hash */
78 static DEFINE_MUTEX(tpmi_lock);
79
80 static const struct x86_cpu_id tpmi_cpu_ids[] = {
81         X86_MATCH_VFM(INTEL_GRANITERAPIDS_X,    NULL),
82         X86_MATCH_VFM(INTEL_ATOM_CRESTMONT_X,   NULL),
83         X86_MATCH_VFM(INTEL_ATOM_CRESTMONT,     NULL),
84         X86_MATCH_VFM(INTEL_GRANITERAPIDS_D,    NULL),
85         X86_MATCH_VFM(INTEL_PANTHERCOVE_X,      NULL),
86         {}
87 };
88 MODULE_DEVICE_TABLE(x86cpu, tpmi_cpu_ids);
89
90 static DECLARE_HASHTABLE(tpmi_cpu_hash, 8);
91
92 static bool tpmi_domain_is_valid(struct tpmi_cpu_info *info)
93 {
94         return info->pkg_id < topology_max_packages() &&
95                 info->punit_domain_id < MAX_POWER_DOMAINS;
96 }
97
98 int tpmi_get_linux_cpu_number(int package_id, int domain_id, int punit_core_id)
99 {
100         struct tpmi_cpu_info *info;
101         int ret = -EINVAL;
102
103         guard(mutex)(&tpmi_lock);
104         hash_for_each_possible(tpmi_cpu_hash, info, hnode, punit_core_id) {
105                 if (info->punit_domain_id == domain_id && info->pkg_id == package_id) {
106                         ret = info->linux_cpu;
107                         break;
108                 }
109         }
110
111         return ret;
112 }
113 EXPORT_SYMBOL_NS_GPL(tpmi_get_linux_cpu_number, "INTEL_TPMI_POWER_DOMAIN");
114
115 int tpmi_get_punit_core_number(int cpu_no)
116 {
117         if (cpu_no >= num_possible_cpus())
118                 return -EINVAL;
119
120         return per_cpu(tpmi_cpu_info, cpu_no).punit_core_id;
121 }
122 EXPORT_SYMBOL_NS_GPL(tpmi_get_punit_core_number, "INTEL_TPMI_POWER_DOMAIN");
123
124 int tpmi_get_power_domain_id(int cpu_no)
125 {
126         if (cpu_no >= num_possible_cpus())
127                 return -EINVAL;
128
129         return per_cpu(tpmi_cpu_info, cpu_no).punit_domain_id;
130 }
131 EXPORT_SYMBOL_NS_GPL(tpmi_get_power_domain_id, "INTEL_TPMI_POWER_DOMAIN");
132
133 cpumask_t *tpmi_get_power_domain_mask(int cpu_no)
134 {
135         struct tpmi_cpu_info *info;
136         cpumask_t *mask;
137         int index;
138
139         if (cpu_no >= num_possible_cpus())
140                 return NULL;
141
142         info = &per_cpu(tpmi_cpu_info, cpu_no);
143         if (!tpmi_domain_is_valid(info))
144                 return NULL;
145
146         index = info->pkg_id * MAX_POWER_DOMAINS + info->punit_domain_id;
147         guard(mutex)(&tpmi_lock);
148         mask = &tpmi_power_domain_mask[index];
149
150         return mask;
151 }
152 EXPORT_SYMBOL_NS_GPL(tpmi_get_power_domain_mask, "INTEL_TPMI_POWER_DOMAIN");
153
154 static int tpmi_get_logical_id(unsigned int cpu, struct tpmi_cpu_info *info)
155 {
156         u64 data;
157         int ret;
158
159         ret = rdmsrl_safe(MSR_PM_LOGICAL_ID, &data);
160         if (ret)
161                 return ret;
162
163         info->punit_domain_id = FIELD_GET(PM_DOMAIN_ID_MASK, data);
164         if (info->punit_domain_id >= MAX_POWER_DOMAINS)
165                 return -EINVAL;
166
167         info->punit_thread_id = FIELD_GET(LP_ID_MASK, data);
168         info->punit_core_id = FIELD_GET(MODULE_ID_MASK, data);
169         info->pkg_id = topology_physical_package_id(cpu);
170         info->linux_cpu = cpu;
171
172         return 0;
173 }
174
175 static int tpmi_cpu_online(unsigned int cpu)
176 {
177         struct tpmi_cpu_info *info = &per_cpu(tpmi_cpu_info, cpu);
178         int ret, index;
179
180         /* Don't fail CPU online for some bad mapping of CPUs */
181         ret = tpmi_get_logical_id(cpu, info);
182         if (ret)
183                 return 0;
184
185         index = info->pkg_id * MAX_POWER_DOMAINS + info->punit_domain_id;
186
187         guard(mutex)(&tpmi_lock);
188         cpumask_set_cpu(cpu, &tpmi_power_domain_mask[index]);
189         hash_add(tpmi_cpu_hash, &info->hnode, info->punit_core_id);
190
191         return 0;
192 }
193
194 static int __init tpmi_init(void)
195 {
196         const struct x86_cpu_id *id;
197         u64 data;
198         int ret;
199
200         id = x86_match_cpu(tpmi_cpu_ids);
201         if (!id)
202                 return -ENODEV;
203
204         /* Check for MSR 0x54 presence */
205         ret = rdmsrl_safe(MSR_PM_LOGICAL_ID, &data);
206         if (ret)
207                 return ret;
208
209         tpmi_power_domain_mask = kcalloc(size_mul(topology_max_packages(), MAX_POWER_DOMAINS),
210                                          sizeof(*tpmi_power_domain_mask), GFP_KERNEL);
211         if (!tpmi_power_domain_mask)
212                 return -ENOMEM;
213
214         ret = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN,
215                                 "platform/x86/tpmi_power_domains:online",
216                                 tpmi_cpu_online, NULL);
217         if (ret < 0) {
218                 kfree(tpmi_power_domain_mask);
219                 return ret;
220         }
221
222         tpmi_hp_state = ret;
223
224         return 0;
225 }
226 module_init(tpmi_init)
227
228 static void __exit tpmi_exit(void)
229 {
230         cpuhp_remove_state(tpmi_hp_state);
231         kfree(tpmi_power_domain_mask);
232 }
233 module_exit(tpmi_exit)
234
235 MODULE_DESCRIPTION("TPMI Power Domains Mapping");
236 MODULE_LICENSE("GPL");
This page took 0.041319 seconds and 4 git commands to generate.