335 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*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 0xb023#define PCI_PC1_OFFSET 0xb424#define PCI_PC6_OFFSET 0xb825 26#define PCI_MONITOR_ENABLE_REG 0xe027 28#define PCI_NON_PC0_ENABLE_BIT 029#define PCI_PC1_ENABLE_BIT 130#define PCI_PC6_ENABLE_BIT 231 32#define PCI_NBP1_STAT_OFFSET 0x9833#define PCI_NBP1_ACTIVE_BIT 234#define PCI_NBP1_ENTERED_BIT 135 36#define PCI_NBP1_CAP_OFFSET 0x9037#define PCI_NBP1_CAPABLE_BIT 3138 39#define OVERFLOW_MS 343597 /* 32 bit register filled at 12500 HZ40 (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 DEBUG89struct timespec dbg_time;90long dbg_timediff;91#endif92 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 else202 *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 else220 *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 DEBUG237 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#endif242 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 DEBUG257 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#endif261 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 else290 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 BKDG301 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__) */335