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