1 /* SPDX-License-Identifier: BSD-3-Clause 2 * Copyright(c) 2024 Advanced Micro Devices, Inc. 3 */ 4 5 #include <errno.h> 6 #include <dirent.h> 7 #include <fnmatch.h> 8 9 #include <rte_memcpy.h> 10 11 #include "amd_uncore.h" 12 #include "power_common.h" 13 #include "e_smi/e_smi.h" 14 15 #define MAX_NUMA_DIE 8 16 17 struct __rte_cache_aligned uncore_power_info { 18 unsigned int die; /* Core die id */ 19 unsigned int pkg; /* Package id */ 20 uint32_t freqs[RTE_MAX_UNCORE_FREQS]; /* Frequency array */ 21 uint32_t nb_freqs; /* Number of available freqs */ 22 uint32_t curr_idx; /* Freq index in freqs array */ 23 uint32_t max_freq; /* System max uncore freq */ 24 uint32_t min_freq; /* System min uncore freq */ 25 }; 26 27 static struct uncore_power_info uncore_info[RTE_MAX_NUMA_NODES][MAX_NUMA_DIE]; 28 static int esmi_initialized; 29 static unsigned int hsmp_proto_ver; 30 31 static int 32 set_uncore_freq_internal(struct uncore_power_info *ui, uint32_t idx) 33 { 34 int ret; 35 36 if (idx >= RTE_MAX_UNCORE_FREQS || idx >= ui->nb_freqs) { 37 POWER_LOG(DEBUG, "Invalid uncore frequency index %u, which " 38 "should be less than %u", idx, ui->nb_freqs); 39 return -1; 40 } 41 42 ret = esmi_apb_disable(ui->pkg, idx); 43 if (ret != ESMI_SUCCESS) { 44 POWER_LOG(ERR, "DF P-state '%u' set failed for pkg %02u", 45 idx, ui->pkg); 46 return -1; 47 } 48 49 POWER_DEBUG_LOG("DF P-state '%u' to be set for pkg %02u die %02u", 50 idx, ui->pkg, ui->die); 51 52 /* write the minimum value first if the target freq is less than current max */ 53 ui->curr_idx = idx; 54 55 return 0; 56 } 57 58 static int 59 power_init_for_setting_uncore_freq(struct uncore_power_info *ui) 60 { 61 switch (hsmp_proto_ver) { 62 case HSMP_PROTO_VER5: 63 ui->max_freq = 1800000; /* Hz */ 64 ui->min_freq = 1200000; /* Hz */ 65 break; 66 case HSMP_PROTO_VER2: 67 default: 68 ui->max_freq = 1600000; /* Hz */ 69 ui->min_freq = 1200000; /* Hz */ 70 } 71 72 return 0; 73 } 74 75 /* 76 * Get the available uncore frequencies of the specific die. 77 */ 78 static int 79 power_get_available_uncore_freqs(struct uncore_power_info *ui) 80 { 81 ui->nb_freqs = 3; 82 if (ui->nb_freqs >= RTE_MAX_UNCORE_FREQS) { 83 POWER_LOG(ERR, "Too many available uncore frequencies: %d", 84 ui->nb_freqs); 85 return -1; 86 } 87 88 /* Generate the uncore freq bucket array. */ 89 switch (hsmp_proto_ver) { 90 case HSMP_PROTO_VER5: 91 ui->freqs[0] = 1800000; 92 ui->freqs[1] = 1440000; 93 ui->freqs[2] = 1200000; 94 break; 95 case HSMP_PROTO_VER2: 96 default: 97 ui->freqs[0] = 1600000; 98 ui->freqs[1] = 1333000; 99 ui->freqs[2] = 1200000; 100 } 101 102 POWER_DEBUG_LOG("%d frequency(s) of pkg %02u die %02u are available", 103 ui->num_uncore_freqs, ui->pkg, ui->die); 104 105 return 0; 106 } 107 108 static int 109 check_pkg_die_values(unsigned int pkg, unsigned int die) 110 { 111 unsigned int max_pkgs, max_dies; 112 max_pkgs = power_amd_uncore_get_num_pkgs(); 113 if (max_pkgs == 0) 114 return -1; 115 if (pkg >= max_pkgs) { 116 POWER_LOG(DEBUG, "Package number %02u can not exceed %u", 117 pkg, max_pkgs); 118 return -1; 119 } 120 121 max_dies = power_amd_uncore_get_num_dies(pkg); 122 if (max_dies == 0) 123 return -1; 124 if (die >= max_dies) { 125 POWER_LOG(DEBUG, "Die number %02u can not exceed %u", 126 die, max_dies); 127 return -1; 128 } 129 130 return 0; 131 } 132 133 static void 134 power_amd_uncore_esmi_init(void) 135 { 136 if (esmi_init() == ESMI_SUCCESS) { 137 if (esmi_hsmp_proto_ver_get(&hsmp_proto_ver) == 138 ESMI_SUCCESS) 139 esmi_initialized = 1; 140 } 141 } 142 143 int 144 power_amd_uncore_init(unsigned int pkg, unsigned int die) 145 { 146 struct uncore_power_info *ui; 147 int ret; 148 149 if (!esmi_initialized) { 150 ret = esmi_init(); 151 if (ret != ESMI_SUCCESS) { 152 POWER_LOG(DEBUG, "ESMI Not initialized, drivers not found"); 153 return -1; 154 } 155 ret = esmi_hsmp_proto_ver_get(&hsmp_proto_ver); 156 if (ret != ESMI_SUCCESS) { 157 POWER_LOG(DEBUG, "HSMP Proto Version Get failed with " 158 "error %s", esmi_get_err_msg(ret)); 159 esmi_exit(); 160 return -1; 161 } 162 esmi_initialized = 1; 163 } 164 165 ret = check_pkg_die_values(pkg, die); 166 if (ret < 0) 167 return -1; 168 169 ui = &uncore_info[pkg][die]; 170 ui->die = die; 171 ui->pkg = pkg; 172 173 /* Init for setting uncore die frequency */ 174 if (power_init_for_setting_uncore_freq(ui) < 0) { 175 POWER_LOG(DEBUG, "Cannot init for setting uncore frequency for " 176 "pkg %02u die %02u", pkg, die); 177 return -1; 178 } 179 180 /* Get the available frequencies */ 181 if (power_get_available_uncore_freqs(ui) < 0) { 182 POWER_LOG(DEBUG, "Cannot get available uncore frequencies of " 183 "pkg %02u die %02u", pkg, die); 184 return -1; 185 } 186 187 return 0; 188 } 189 190 int 191 power_amd_uncore_exit(unsigned int pkg, unsigned int die) 192 { 193 struct uncore_power_info *ui; 194 195 int ret = check_pkg_die_values(pkg, die); 196 if (ret < 0) 197 return -1; 198 199 ui = &uncore_info[pkg][die]; 200 ui->nb_freqs = 0; 201 202 if (esmi_initialized) { 203 esmi_exit(); 204 esmi_initialized = 0; 205 } 206 207 return 0; 208 } 209 210 uint32_t 211 power_get_amd_uncore_freq(unsigned int pkg, unsigned int die) 212 { 213 int ret = check_pkg_die_values(pkg, die); 214 if (ret < 0) 215 return -1; 216 217 return uncore_info[pkg][die].curr_idx; 218 } 219 220 int 221 power_set_amd_uncore_freq(unsigned int pkg, unsigned int die, uint32_t index) 222 { 223 int ret = check_pkg_die_values(pkg, die); 224 if (ret < 0) 225 return -1; 226 227 return set_uncore_freq_internal(&(uncore_info[pkg][die]), index); 228 } 229 230 int 231 power_amd_uncore_freq_max(unsigned int pkg, unsigned int die) 232 { 233 int ret = check_pkg_die_values(pkg, die); 234 if (ret < 0) 235 return -1; 236 237 return set_uncore_freq_internal(&(uncore_info[pkg][die]), 0); 238 } 239 240 241 int 242 power_amd_uncore_freq_min(unsigned int pkg, unsigned int die) 243 { 244 int ret = check_pkg_die_values(pkg, die); 245 if (ret < 0) 246 return -1; 247 248 struct uncore_power_info *ui = &uncore_info[pkg][die]; 249 250 return set_uncore_freq_internal(&(uncore_info[pkg][die]), ui->nb_freqs - 1); 251 } 252 253 int 254 power_amd_uncore_freqs(unsigned int pkg, unsigned int die, uint32_t *freqs, uint32_t num) 255 { 256 struct uncore_power_info *ui; 257 258 int ret = check_pkg_die_values(pkg, die); 259 if (ret < 0) 260 return -1; 261 262 if (freqs == NULL) { 263 POWER_LOG(ERR, "NULL buffer supplied"); 264 return 0; 265 } 266 267 ui = &uncore_info[pkg][die]; 268 if (num < ui->nb_freqs) { 269 POWER_LOG(ERR, "Buffer size is not enough"); 270 return 0; 271 } 272 rte_memcpy(freqs, ui->freqs, ui->nb_freqs * sizeof(uint32_t)); 273 274 return ui->nb_freqs; 275 } 276 277 int 278 power_amd_uncore_get_num_freqs(unsigned int pkg, unsigned int die) 279 { 280 int ret = check_pkg_die_values(pkg, die); 281 if (ret < 0) 282 return -1; 283 284 return uncore_info[pkg][die].nb_freqs; 285 } 286 287 unsigned int 288 power_amd_uncore_get_num_pkgs(void) 289 { 290 uint32_t num_pkgs = 0; 291 int ret; 292 293 if (esmi_initialized) { 294 ret = esmi_number_of_sockets_get(&num_pkgs); 295 if (ret != ESMI_SUCCESS) { 296 POWER_LOG(ERR, "Failed to get number of sockets"); 297 num_pkgs = 0; 298 } 299 } 300 return num_pkgs; 301 } 302 303 unsigned int 304 power_amd_uncore_get_num_dies(unsigned int pkg) 305 { 306 if (pkg >= power_amd_uncore_get_num_pkgs()) { 307 POWER_LOG(ERR, "Invalid package ID"); 308 return 0; 309 } 310 311 return 1; 312 } 313 314 static struct rte_power_uncore_ops amd_uncore_ops = { 315 .name = "amd-hsmp", 316 .cb = power_amd_uncore_esmi_init, 317 .init = power_amd_uncore_init, 318 .exit = power_amd_uncore_exit, 319 .get_avail_freqs = power_amd_uncore_freqs, 320 .get_num_pkgs = power_amd_uncore_get_num_pkgs, 321 .get_num_dies = power_amd_uncore_get_num_dies, 322 .get_num_freqs = power_amd_uncore_get_num_freqs, 323 .get_freq = power_get_amd_uncore_freq, 324 .set_freq = power_set_amd_uncore_freq, 325 .freq_max = power_amd_uncore_freq_max, 326 .freq_min = power_amd_uncore_freq_min, 327 }; 328 329 RTE_POWER_REGISTER_UNCORE_OPS(amd_uncore_ops); 330