1 // SPDX-License-Identifier: GPL-2.0-only
3 * Mapping of TPMI power domains CPU mapping
5 * Copyright (c) 2024, Intel Corporation.
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>
22 #include <asm/cpu_device_id.h>
23 #include <asm/intel-family.h>
26 #include "tpmi_power_domains.h"
28 #define MSR_PM_LOGICAL_ID 0x54
32 * [15:11] PM_DOMAIN_ID
33 * [10:3] MODULE_ID (aka IDI_AGENT_ID)
37 * [1:0] core ID within module
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)
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
56 * Structure to store mapping information for a Linux CPU
57 * to a Punit core, thread and power domain.
59 struct tpmi_cpu_info
{
60 struct hlist_node hnode
;
68 static DEFINE_PER_CPU(struct tpmi_cpu_info
, tpmi_cpu_info
);
70 /* The dynamically assigned cpu hotplug state to free later */
71 static enum cpuhp_state tpmi_hp_state __read_mostly
;
73 #define MAX_POWER_DOMAINS 8
75 static cpumask_t
*tpmi_power_domain_mask
;
77 /* Lock to protect tpmi_power_domain_mask and tpmi_cpu_hash */
78 static DEFINE_MUTEX(tpmi_lock
);
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
),
88 MODULE_DEVICE_TABLE(x86cpu
, tpmi_cpu_ids
);
90 static DECLARE_HASHTABLE(tpmi_cpu_hash
, 8);
92 static bool tpmi_domain_is_valid(struct tpmi_cpu_info
*info
)
94 return info
->pkg_id
< topology_max_packages() &&
95 info
->punit_domain_id
< MAX_POWER_DOMAINS
;
98 int tpmi_get_linux_cpu_number(int package_id
, int domain_id
, int punit_core_id
)
100 struct tpmi_cpu_info
*info
;
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
;
113 EXPORT_SYMBOL_NS_GPL(tpmi_get_linux_cpu_number
, "INTEL_TPMI_POWER_DOMAIN");
115 int tpmi_get_punit_core_number(int cpu_no
)
117 if (cpu_no
>= num_possible_cpus())
120 return per_cpu(tpmi_cpu_info
, cpu_no
).punit_core_id
;
122 EXPORT_SYMBOL_NS_GPL(tpmi_get_punit_core_number
, "INTEL_TPMI_POWER_DOMAIN");
124 int tpmi_get_power_domain_id(int cpu_no
)
126 if (cpu_no
>= num_possible_cpus())
129 return per_cpu(tpmi_cpu_info
, cpu_no
).punit_domain_id
;
131 EXPORT_SYMBOL_NS_GPL(tpmi_get_power_domain_id
, "INTEL_TPMI_POWER_DOMAIN");
133 cpumask_t
*tpmi_get_power_domain_mask(int cpu_no
)
135 struct tpmi_cpu_info
*info
;
139 if (cpu_no
>= num_possible_cpus())
142 info
= &per_cpu(tpmi_cpu_info
, cpu_no
);
143 if (!tpmi_domain_is_valid(info
))
146 index
= info
->pkg_id
* MAX_POWER_DOMAINS
+ info
->punit_domain_id
;
147 guard(mutex
)(&tpmi_lock
);
148 mask
= &tpmi_power_domain_mask
[index
];
152 EXPORT_SYMBOL_NS_GPL(tpmi_get_power_domain_mask
, "INTEL_TPMI_POWER_DOMAIN");
154 static int tpmi_get_logical_id(unsigned int cpu
, struct tpmi_cpu_info
*info
)
159 ret
= rdmsrl_safe(MSR_PM_LOGICAL_ID
, &data
);
163 info
->punit_domain_id
= FIELD_GET(PM_DOMAIN_ID_MASK
, data
);
164 if (info
->punit_domain_id
>= MAX_POWER_DOMAINS
)
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
;
175 static int tpmi_cpu_online(unsigned int cpu
)
177 struct tpmi_cpu_info
*info
= &per_cpu(tpmi_cpu_info
, cpu
);
180 /* Don't fail CPU online for some bad mapping of CPUs */
181 ret
= tpmi_get_logical_id(cpu
, info
);
185 index
= info
->pkg_id
* MAX_POWER_DOMAINS
+ info
->punit_domain_id
;
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
);
194 static int __init
tpmi_init(void)
196 const struct x86_cpu_id
*id
;
200 id
= x86_match_cpu(tpmi_cpu_ids
);
204 /* Check for MSR 0x54 presence */
205 ret
= rdmsrl_safe(MSR_PM_LOGICAL_ID
, &data
);
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
)
214 ret
= cpuhp_setup_state(CPUHP_AP_ONLINE_DYN
,
215 "platform/x86/tpmi_power_domains:online",
216 tpmi_cpu_online
, NULL
);
218 kfree(tpmi_power_domain_mask
);
226 module_init(tpmi_init
)
228 static void __exit
tpmi_exit(void)
230 cpuhp_remove_state(tpmi_hp_state
);
231 kfree(tpmi_power_domain_mask
);
233 module_exit(tpmi_exit
)
235 MODULE_DESCRIPTION("TPMI Power Domains Mapping");
236 MODULE_LICENSE("GPL");