amd_fam14h_idle.c (8630B)
1// SPDX-License-Identifier: GPL-2.0-only 2/* 3 * (C) 2010,2011 Thomas Renninger <trenn@suse.de>, Novell Inc. 4 * 5 * PCI initialization based on example code from: 6 * Andreas Herrmann <andreas.herrmann3@amd.com> 7 */ 8 9#if defined(__i386__) || defined(__x86_64__) 10 11#include <stdio.h> 12#include <stdlib.h> 13#include <stdint.h> 14#include <time.h> 15#include <string.h> 16 17#include <pci/pci.h> 18 19#include "idle_monitor/cpupower-monitor.h" 20#include "helpers/helpers.h" 21 22#define PCI_NON_PC0_OFFSET 0xb0 23#define PCI_PC1_OFFSET 0xb4 24#define PCI_PC6_OFFSET 0xb8 25 26#define PCI_MONITOR_ENABLE_REG 0xe0 27 28#define PCI_NON_PC0_ENABLE_BIT 0 29#define PCI_PC1_ENABLE_BIT 1 30#define PCI_PC6_ENABLE_BIT 2 31 32#define PCI_NBP1_STAT_OFFSET 0x98 33#define PCI_NBP1_ACTIVE_BIT 2 34#define PCI_NBP1_ENTERED_BIT 1 35 36#define PCI_NBP1_CAP_OFFSET 0x90 37#define PCI_NBP1_CAPABLE_BIT 31 38 39#define OVERFLOW_MS 343597 /* 32 bit register filled at 12500 HZ 40 (1 tick per 80ns) */ 41 42enum amd_fam14h_states {NON_PC0 = 0, PC1, PC6, NBP1, 43 AMD_FAM14H_STATE_NUM}; 44 45static int fam14h_get_count_percent(unsigned int self_id, double *percent, 46 unsigned int cpu); 47static int fam14h_nbp1_count(unsigned int id, unsigned long long *count, 48 unsigned int cpu); 49 50static cstate_t amd_fam14h_cstates[AMD_FAM14H_STATE_NUM] = { 51 { 52 .name = "!PC0", 53 .desc = N_("Package in sleep state (PC1 or deeper)"), 54 .id = NON_PC0, 55 .range = RANGE_PACKAGE, 56 .get_count_percent = fam14h_get_count_percent, 57 }, 58 { 59 .name = "PC1", 60 .desc = N_("Processor Package C1"), 61 .id = PC1, 62 .range = RANGE_PACKAGE, 63 .get_count_percent = fam14h_get_count_percent, 64 }, 65 { 66 .name = "PC6", 67 .desc = N_("Processor Package C6"), 68 .id = PC6, 69 .range = RANGE_PACKAGE, 70 .get_count_percent = fam14h_get_count_percent, 71 }, 72 { 73 .name = "NBP1", 74 .desc = N_("North Bridge P1 boolean counter (returns 0 or 1)"), 75 .id = NBP1, 76 .range = RANGE_PACKAGE, 77 .get_count = fam14h_nbp1_count, 78 }, 79}; 80 81static struct pci_access *pci_acc; 82static struct pci_dev *amd_fam14h_pci_dev; 83static int nbp1_entered; 84 85static struct timespec start_time; 86static unsigned long long timediff; 87 88#ifdef DEBUG 89struct timespec dbg_time; 90long dbg_timediff; 91#endif 92 93static unsigned long long *previous_count[AMD_FAM14H_STATE_NUM]; 94static unsigned long long *current_count[AMD_FAM14H_STATE_NUM]; 95 96static int amd_fam14h_get_pci_info(struct cstate *state, 97 unsigned int *pci_offset, 98 unsigned int *enable_bit, 99 unsigned int cpu) 100{ 101 switch (state->id) { 102 case NON_PC0: 103 *enable_bit = PCI_NON_PC0_ENABLE_BIT; 104 *pci_offset = PCI_NON_PC0_OFFSET; 105 break; 106 case PC1: 107 *enable_bit = PCI_PC1_ENABLE_BIT; 108 *pci_offset = PCI_PC1_OFFSET; 109 break; 110 case PC6: 111 *enable_bit = PCI_PC6_ENABLE_BIT; 112 *pci_offset = PCI_PC6_OFFSET; 113 break; 114 case NBP1: 115 *enable_bit = PCI_NBP1_ENTERED_BIT; 116 *pci_offset = PCI_NBP1_STAT_OFFSET; 117 break; 118 default: 119 return -1; 120 } 121 return 0; 122} 123 124static int amd_fam14h_init(cstate_t *state, unsigned int cpu) 125{ 126 int enable_bit, pci_offset, ret; 127 uint32_t val; 128 129 ret = amd_fam14h_get_pci_info(state, &pci_offset, &enable_bit, cpu); 130 if (ret) 131 return ret; 132 133 /* NBP1 needs extra treating -> write 1 to D18F6x98 bit 1 for init */ 134 if (state->id == NBP1) { 135 val = pci_read_long(amd_fam14h_pci_dev, pci_offset); 136 val |= 1 << enable_bit; 137 val = pci_write_long(amd_fam14h_pci_dev, pci_offset, val); 138 return ret; 139 } 140 141 /* Enable monitor */ 142 val = pci_read_long(amd_fam14h_pci_dev, PCI_MONITOR_ENABLE_REG); 143 dprint("Init %s: read at offset: 0x%x val: %u\n", state->name, 144 PCI_MONITOR_ENABLE_REG, (unsigned int) val); 145 val |= 1 << enable_bit; 146 pci_write_long(amd_fam14h_pci_dev, PCI_MONITOR_ENABLE_REG, val); 147 148 dprint("Init %s: offset: 0x%x enable_bit: %d - val: %u (%u)\n", 149 state->name, PCI_MONITOR_ENABLE_REG, enable_bit, 150 (unsigned int) val, cpu); 151 152 /* Set counter to zero */ 153 pci_write_long(amd_fam14h_pci_dev, pci_offset, 0); 154 previous_count[state->id][cpu] = 0; 155 156 return 0; 157} 158 159static int amd_fam14h_disable(cstate_t *state, unsigned int cpu) 160{ 161 int enable_bit, pci_offset, ret; 162 uint32_t val; 163 164 ret = amd_fam14h_get_pci_info(state, &pci_offset, &enable_bit, cpu); 165 if (ret) 166 return ret; 167 168 val = pci_read_long(amd_fam14h_pci_dev, pci_offset); 169 dprint("%s: offset: 0x%x %u\n", state->name, pci_offset, val); 170 if (state->id == NBP1) { 171 /* was the bit whether NBP1 got entered set? */ 172 nbp1_entered = (val & (1 << PCI_NBP1_ACTIVE_BIT)) | 173 (val & (1 << PCI_NBP1_ENTERED_BIT)); 174 175 dprint("NBP1 was %sentered - 0x%x - enable_bit: " 176 "%d - pci_offset: 0x%x\n", 177 nbp1_entered ? "" : "not ", 178 val, enable_bit, pci_offset); 179 return ret; 180 } 181 current_count[state->id][cpu] = val; 182 183 dprint("%s: Current - %llu (%u)\n", state->name, 184 current_count[state->id][cpu], cpu); 185 dprint("%s: Previous - %llu (%u)\n", state->name, 186 previous_count[state->id][cpu], cpu); 187 188 val = pci_read_long(amd_fam14h_pci_dev, PCI_MONITOR_ENABLE_REG); 189 val &= ~(1 << enable_bit); 190 pci_write_long(amd_fam14h_pci_dev, PCI_MONITOR_ENABLE_REG, val); 191 192 return 0; 193} 194 195static int fam14h_nbp1_count(unsigned int id, unsigned long long *count, 196 unsigned int cpu) 197{ 198 if (id == NBP1) { 199 if (nbp1_entered) 200 *count = 1; 201 else 202 *count = 0; 203 return 0; 204 } 205 return -1; 206} 207static int fam14h_get_count_percent(unsigned int id, double *percent, 208 unsigned int cpu) 209{ 210 unsigned long diff; 211 212 if (id >= AMD_FAM14H_STATE_NUM) 213 return -1; 214 /* residency count in 80ns -> divide through 12.5 to get us residency */ 215 diff = current_count[id][cpu] - previous_count[id][cpu]; 216 217 if (timediff == 0) 218 *percent = 0.0; 219 else 220 *percent = 100.0 * diff / timediff / 12.5; 221 222 dprint("Timediff: %llu - res~: %lu us - percent: %.2f %%\n", 223 timediff, diff * 10 / 125, *percent); 224 225 return 0; 226} 227 228static int amd_fam14h_start(void) 229{ 230 int num, cpu; 231 clock_gettime(CLOCK_REALTIME, &start_time); 232 for (num = 0; num < AMD_FAM14H_STATE_NUM; num++) { 233 for (cpu = 0; cpu < cpu_count; cpu++) 234 amd_fam14h_init(&amd_fam14h_cstates[num], cpu); 235 } 236#ifdef DEBUG 237 clock_gettime(CLOCK_REALTIME, &dbg_time); 238 dbg_timediff = timespec_diff_us(start_time, dbg_time); 239 dprint("Enabling counters took: %lu us\n", 240 dbg_timediff); 241#endif 242 return 0; 243} 244 245static int amd_fam14h_stop(void) 246{ 247 int num, cpu; 248 struct timespec end_time; 249 250 clock_gettime(CLOCK_REALTIME, &end_time); 251 252 for (num = 0; num < AMD_FAM14H_STATE_NUM; num++) { 253 for (cpu = 0; cpu < cpu_count; cpu++) 254 amd_fam14h_disable(&amd_fam14h_cstates[num], cpu); 255 } 256#ifdef DEBUG 257 clock_gettime(CLOCK_REALTIME, &dbg_time); 258 dbg_timediff = timespec_diff_us(end_time, dbg_time); 259 dprint("Disabling counters took: %lu ns\n", dbg_timediff); 260#endif 261 timediff = timespec_diff_us(start_time, end_time); 262 if (timediff / 1000 > OVERFLOW_MS) 263 print_overflow_err((unsigned int)timediff / 1000000, 264 OVERFLOW_MS / 1000); 265 266 return 0; 267} 268 269static int is_nbp1_capable(void) 270{ 271 uint32_t val; 272 val = pci_read_long(amd_fam14h_pci_dev, PCI_NBP1_CAP_OFFSET); 273 return val & (1 << 31); 274} 275 276struct cpuidle_monitor *amd_fam14h_register(void) 277{ 278 int num; 279 280 if (cpupower_cpu_info.vendor != X86_VENDOR_AMD) 281 return NULL; 282 283 if (cpupower_cpu_info.family == 0x14) 284 strncpy(amd_fam14h_monitor.name, "Fam_14h", 285 MONITOR_NAME_LEN - 1); 286 else if (cpupower_cpu_info.family == 0x12) 287 strncpy(amd_fam14h_monitor.name, "Fam_12h", 288 MONITOR_NAME_LEN - 1); 289 else 290 return NULL; 291 292 /* We do not alloc for nbp1 machine wide counter */ 293 for (num = 0; num < AMD_FAM14H_STATE_NUM - 1; num++) { 294 previous_count[num] = calloc(cpu_count, 295 sizeof(unsigned long long)); 296 current_count[num] = calloc(cpu_count, 297 sizeof(unsigned long long)); 298 } 299 300 /* We need PCI device: Slot 18, Func 6, compare with BKDG 301 for fam 12h/14h */ 302 amd_fam14h_pci_dev = pci_slot_func_init(&pci_acc, 0x18, 6); 303 if (amd_fam14h_pci_dev == NULL || pci_acc == NULL) 304 return NULL; 305 306 if (!is_nbp1_capable()) 307 amd_fam14h_monitor.hw_states_num = AMD_FAM14H_STATE_NUM - 1; 308 309 amd_fam14h_monitor.name_len = strlen(amd_fam14h_monitor.name); 310 return &amd_fam14h_monitor; 311} 312 313static void amd_fam14h_unregister(void) 314{ 315 int num; 316 for (num = 0; num < AMD_FAM14H_STATE_NUM - 1; num++) { 317 free(previous_count[num]); 318 free(current_count[num]); 319 } 320 pci_cleanup(pci_acc); 321} 322 323struct cpuidle_monitor amd_fam14h_monitor = { 324 .name = "", 325 .hw_states = amd_fam14h_cstates, 326 .hw_states_num = AMD_FAM14H_STATE_NUM, 327 .start = amd_fam14h_start, 328 .stop = amd_fam14h_stop, 329 .do_register = amd_fam14h_register, 330 .unregister = amd_fam14h_unregister, 331 .flags.needs_root = 1, 332 .overflow_s = OVERFLOW_MS / 1000, 333}; 334#endif /* #if defined(__i386__) || defined(__x86_64__) */