Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v6.12-rc1 235 lines 5.6 kB view raw
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 */ 59struct 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 68static DEFINE_PER_CPU(struct tpmi_cpu_info, tpmi_cpu_info); 69 70/* The dynamically assigned cpu hotplug state to free later */ 71static enum cpuhp_state tpmi_hp_state __read_mostly; 72 73#define MAX_POWER_DOMAINS 8 74 75static cpumask_t *tpmi_power_domain_mask; 76 77/* Lock to protect tpmi_power_domain_mask and tpmi_cpu_hash */ 78static DEFINE_MUTEX(tpmi_lock); 79 80static 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 {} 86}; 87MODULE_DEVICE_TABLE(x86cpu, tpmi_cpu_ids); 88 89static DECLARE_HASHTABLE(tpmi_cpu_hash, 8); 90 91static bool tpmi_domain_is_valid(struct tpmi_cpu_info *info) 92{ 93 return info->pkg_id < topology_max_packages() && 94 info->punit_domain_id < MAX_POWER_DOMAINS; 95} 96 97int tpmi_get_linux_cpu_number(int package_id, int domain_id, int punit_core_id) 98{ 99 struct tpmi_cpu_info *info; 100 int ret = -EINVAL; 101 102 guard(mutex)(&tpmi_lock); 103 hash_for_each_possible(tpmi_cpu_hash, info, hnode, punit_core_id) { 104 if (info->punit_domain_id == domain_id && info->pkg_id == package_id) { 105 ret = info->linux_cpu; 106 break; 107 } 108 } 109 110 return ret; 111} 112EXPORT_SYMBOL_NS_GPL(tpmi_get_linux_cpu_number, INTEL_TPMI_POWER_DOMAIN); 113 114int tpmi_get_punit_core_number(int cpu_no) 115{ 116 if (cpu_no >= num_possible_cpus()) 117 return -EINVAL; 118 119 return per_cpu(tpmi_cpu_info, cpu_no).punit_core_id; 120} 121EXPORT_SYMBOL_NS_GPL(tpmi_get_punit_core_number, INTEL_TPMI_POWER_DOMAIN); 122 123int tpmi_get_power_domain_id(int cpu_no) 124{ 125 if (cpu_no >= num_possible_cpus()) 126 return -EINVAL; 127 128 return per_cpu(tpmi_cpu_info, cpu_no).punit_domain_id; 129} 130EXPORT_SYMBOL_NS_GPL(tpmi_get_power_domain_id, INTEL_TPMI_POWER_DOMAIN); 131 132cpumask_t *tpmi_get_power_domain_mask(int cpu_no) 133{ 134 struct tpmi_cpu_info *info; 135 cpumask_t *mask; 136 int index; 137 138 if (cpu_no >= num_possible_cpus()) 139 return NULL; 140 141 info = &per_cpu(tpmi_cpu_info, cpu_no); 142 if (!tpmi_domain_is_valid(info)) 143 return NULL; 144 145 index = info->pkg_id * MAX_POWER_DOMAINS + info->punit_domain_id; 146 guard(mutex)(&tpmi_lock); 147 mask = &tpmi_power_domain_mask[index]; 148 149 return mask; 150} 151EXPORT_SYMBOL_NS_GPL(tpmi_get_power_domain_mask, INTEL_TPMI_POWER_DOMAIN); 152 153static int tpmi_get_logical_id(unsigned int cpu, struct tpmi_cpu_info *info) 154{ 155 u64 data; 156 int ret; 157 158 ret = rdmsrl_safe(MSR_PM_LOGICAL_ID, &data); 159 if (ret) 160 return ret; 161 162 info->punit_domain_id = FIELD_GET(PM_DOMAIN_ID_MASK, data); 163 if (info->punit_domain_id >= MAX_POWER_DOMAINS) 164 return -EINVAL; 165 166 info->punit_thread_id = FIELD_GET(LP_ID_MASK, data); 167 info->punit_core_id = FIELD_GET(MODULE_ID_MASK, data); 168 info->pkg_id = topology_physical_package_id(cpu); 169 info->linux_cpu = cpu; 170 171 return 0; 172} 173 174static int tpmi_cpu_online(unsigned int cpu) 175{ 176 struct tpmi_cpu_info *info = &per_cpu(tpmi_cpu_info, cpu); 177 int ret, index; 178 179 /* Don't fail CPU online for some bad mapping of CPUs */ 180 ret = tpmi_get_logical_id(cpu, info); 181 if (ret) 182 return 0; 183 184 index = info->pkg_id * MAX_POWER_DOMAINS + info->punit_domain_id; 185 186 guard(mutex)(&tpmi_lock); 187 cpumask_set_cpu(cpu, &tpmi_power_domain_mask[index]); 188 hash_add(tpmi_cpu_hash, &info->hnode, info->punit_core_id); 189 190 return 0; 191} 192 193static int __init tpmi_init(void) 194{ 195 const struct x86_cpu_id *id; 196 u64 data; 197 int ret; 198 199 id = x86_match_cpu(tpmi_cpu_ids); 200 if (!id) 201 return -ENODEV; 202 203 /* Check for MSR 0x54 presence */ 204 ret = rdmsrl_safe(MSR_PM_LOGICAL_ID, &data); 205 if (ret) 206 return ret; 207 208 tpmi_power_domain_mask = kcalloc(size_mul(topology_max_packages(), MAX_POWER_DOMAINS), 209 sizeof(*tpmi_power_domain_mask), GFP_KERNEL); 210 if (!tpmi_power_domain_mask) 211 return -ENOMEM; 212 213 ret = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, 214 "platform/x86/tpmi_power_domains:online", 215 tpmi_cpu_online, NULL); 216 if (ret < 0) { 217 kfree(tpmi_power_domain_mask); 218 return ret; 219 } 220 221 tpmi_hp_state = ret; 222 223 return 0; 224} 225module_init(tpmi_init) 226 227static void __exit tpmi_exit(void) 228{ 229 cpuhp_remove_state(tpmi_hp_state); 230 kfree(tpmi_power_domain_mask); 231} 232module_exit(tpmi_exit) 233 234MODULE_DESCRIPTION("TPMI Power Domains Mapping"); 235MODULE_LICENSE("GPL");