cregit-Linux how code gets into the kernel

Release 4.11 tools/power/cpupower/utils/idle_monitor/amd_fam14h_idle.c

/*
 *  (C) 2010,2011      Thomas Renninger <trenn@suse.de>, Novell Inc.
 *
 *  Licensed under the terms of the GNU GPL License version 2.
 *
 *  PCI initialization based on example code from:
 *  Andreas Herrmann <andreas.herrmann3@amd.com>
 */

#if defined(__i386__) || defined(__x86_64__)

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <time.h>
#include <string.h>

#include <pci/pci.h>

#include "idle_monitor/cpupower-monitor.h"
#include "helpers/helpers.h"


#define PCI_NON_PC0_OFFSET	0xb0

#define PCI_PC1_OFFSET		0xb4

#define PCI_PC6_OFFSET		0xb8


#define PCI_MONITOR_ENABLE_REG  0xe0


#define PCI_NON_PC0_ENABLE_BIT	0

#define PCI_PC1_ENABLE_BIT	1

#define PCI_PC6_ENABLE_BIT	2


#define PCI_NBP1_STAT_OFFSET	0x98

#define PCI_NBP1_ACTIVE_BIT	2

#define PCI_NBP1_ENTERED_BIT	1


#define PCI_NBP1_CAP_OFFSET	0x90

#define PCI_NBP1_CAPABLE_BIT    31


#define OVERFLOW_MS		343597 
/* 32 bit register filled at 12500 HZ
                                          (1 tick per 80ns) */






enum amd_fam14h_states {NON_PC0 = 0, PC1, PC6, NBP1,
			
AMD_FAM14H_STATE_NUM};

static int fam14h_get_count_percent(unsigned int self_id, double *percent,
				    unsigned int cpu);
static int fam14h_nbp1_count(unsigned int id, unsigned long long *count,
			     unsigned int cpu);


static cstate_t amd_fam14h_cstates[AMD_FAM14H_STATE_NUM] = {
	{
		.name			= "!PC0",
		.desc			= N_("Package in sleep state (PC1 or deeper)"),
		.id			= NON_PC0,
		.range			= RANGE_PACKAGE,
		.get_count_percent	= fam14h_get_count_percent,
        },
	{
		.name			= "PC1",
		.desc			= N_("Processor Package C1"),
		.id			= PC1,
		.range			= RANGE_PACKAGE,
		.get_count_percent	= fam14h_get_count_percent,
        },
	{
		.name			= "PC6",
		.desc			= N_("Processor Package C6"),
		.id			= PC6,
		.range			= RANGE_PACKAGE,
		.get_count_percent	= fam14h_get_count_percent,
        },
	{
		.name			= "NBP1",
		.desc			= N_("North Bridge P1 boolean counter (returns 0 or 1)"),
		.id			= NBP1,
		.range			= RANGE_PACKAGE,
		.get_count		= fam14h_nbp1_count,
        },
};


static struct pci_access *pci_acc;

static struct pci_dev *amd_fam14h_pci_dev;

static int nbp1_entered;


struct timespec start_time;

static unsigned long long timediff;

#ifdef DEBUG

struct timespec dbg_time;

long dbg_timediff;
#endif


static unsigned long long *previous_count[AMD_FAM14H_STATE_NUM];

static unsigned long long *current_count[AMD_FAM14H_STATE_NUM];


static int amd_fam14h_get_pci_info(struct cstate *state, unsigned int *pci_offset, unsigned int *enable_bit, unsigned int cpu) { switch (state->id) { case NON_PC0: *enable_bit = PCI_NON_PC0_ENABLE_BIT; *pci_offset = PCI_NON_PC0_OFFSET; break; case PC1: *enable_bit = PCI_PC1_ENABLE_BIT; *pci_offset = PCI_PC1_OFFSET; break; case PC6: *enable_bit = PCI_PC6_ENABLE_BIT; *pci_offset = PCI_PC6_OFFSET; break; case NBP1: *enable_bit = PCI_NBP1_ENTERED_BIT; *pci_offset = PCI_NBP1_STAT_OFFSET; break; default: return -1; }; return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Dominik Brodowski98100.00%1100.00%
Total98100.00%1100.00%


static int amd_fam14h_init(cstate_t *state, unsigned int cpu) { int enable_bit, pci_offset, ret; uint32_t val; ret = amd_fam14h_get_pci_info(state, &pci_offset, &enable_bit, cpu); if (ret) return ret; /* NBP1 needs extra treating -> write 1 to D18F6x98 bit 1 for init */ if (state->id == NBP1) { val = pci_read_long(amd_fam14h_pci_dev, pci_offset); val |= 1 << enable_bit; val = pci_write_long(amd_fam14h_pci_dev, pci_offset, val); return ret; } /* Enable monitor */ val = pci_read_long(amd_fam14h_pci_dev, PCI_MONITOR_ENABLE_REG); dprint("Init %s: read at offset: 0x%x val: %u\n", state->name, PCI_MONITOR_ENABLE_REG, (unsigned int) val); val |= 1 << enable_bit; pci_write_long(amd_fam14h_pci_dev, PCI_MONITOR_ENABLE_REG, val); dprint("Init %s: offset: 0x%x enable_bit: %d - val: %u (%u)\n", state->name, PCI_MONITOR_ENABLE_REG, enable_bit, (unsigned int) val, cpu); /* Set counter to zero */ pci_write_long(amd_fam14h_pci_dev, pci_offset, 0); previous_count[state->id][cpu] = 0; return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Dominik Brodowski174100.00%1100.00%
Total174100.00%1100.00%


static int amd_fam14h_disable(cstate_t *state, unsigned int cpu) { int enable_bit, pci_offset, ret; uint32_t val; ret = amd_fam14h_get_pci_info(state, &pci_offset, &enable_bit, cpu); if (ret) return ret; val = pci_read_long(amd_fam14h_pci_dev, pci_offset); dprint("%s: offset: 0x%x %u\n", state->name, pci_offset, val); if (state->id == NBP1) { /* was the bit whether NBP1 got entered set? */ nbp1_entered = (val & (1 << PCI_NBP1_ACTIVE_BIT)) | (val & (1 << PCI_NBP1_ENTERED_BIT)); dprint("NBP1 was %sentered - 0x%x - enable_bit: " "%d - pci_offset: 0x%x\n", nbp1_entered ? "" : "not ", val, enable_bit, pci_offset); return ret; } current_count[state->id][cpu] = val; dprint("%s: Current - %llu (%u)\n", state->name, current_count[state->id][cpu], cpu); dprint("%s: Previous - %llu (%u)\n", state->name, previous_count[state->id][cpu], cpu); val = pci_read_long(amd_fam14h_pci_dev, PCI_MONITOR_ENABLE_REG); val &= ~(1 << enable_bit); pci_write_long(amd_fam14h_pci_dev, PCI_MONITOR_ENABLE_REG, val); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Dominik Brodowski206100.00%1100.00%
Total206100.00%1100.00%


static int fam14h_nbp1_count(unsigned int id, unsigned long long *count, unsigned int cpu) { if (id == NBP1) { if (nbp1_entered) *count = 1; else *count = 0; return 0; } return -1; }

Contributors

PersonTokensPropCommitsCommitProp
Dominik Brodowski50100.00%1100.00%
Total50100.00%1100.00%


static int fam14h_get_count_percent(unsigned int id, double *percent, unsigned int cpu) { unsigned long diff; if (id >= AMD_FAM14H_STATE_NUM) return -1; /* residency count in 80ns -> divide through 12.5 to get us residency */ diff = current_count[id][cpu] - previous_count[id][cpu]; if (timediff == 0) *percent = 0.0; else *percent = 100.0 * diff / timediff / 12.5; dprint("Timediff: %llu - res~: %lu us - percent: %.2f %%\n", timediff, diff * 10 / 125, *percent); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Dominik Brodowski93100.00%1100.00%
Total93100.00%1100.00%


static int amd_fam14h_start(void) { int num, cpu; clock_gettime(CLOCK_REALTIME, &start_time); for (num = 0; num < AMD_FAM14H_STATE_NUM; num++) { for (cpu = 0; cpu < cpu_count; cpu++) amd_fam14h_init(&amd_fam14h_cstates[num], cpu); } #ifdef DEBUG clock_gettime(CLOCK_REALTIME, &dbg_time); dbg_timediff = timespec_diff_us(start_time, dbg_time); dprint("Enabling counters took: %lu us\n", dbg_timediff); #endif return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Dominik Brodowski92100.00%1100.00%
Total92100.00%1100.00%


static int amd_fam14h_stop(void) { int num, cpu; struct timespec end_time; clock_gettime(CLOCK_REALTIME, &end_time); for (num = 0; num < AMD_FAM14H_STATE_NUM; num++) { for (cpu = 0; cpu < cpu_count; cpu++) amd_fam14h_disable(&amd_fam14h_cstates[num], cpu); } #ifdef DEBUG clock_gettime(CLOCK_REALTIME, &dbg_time); dbg_timediff = timespec_diff_us(end_time, dbg_time); dprint("Disabling counters took: %lu ns\n", dbg_timediff); #endif timediff = timespec_diff_us(start_time, end_time); if (timediff / 1000 > OVERFLOW_MS) print_overflow_err((unsigned int)timediff / 1000000, OVERFLOW_MS / 1000); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Dominik Brodowski128100.00%1100.00%
Total128100.00%1100.00%


static int is_nbp1_capable(void) { uint32_t val; val = pci_read_long(amd_fam14h_pci_dev, PCI_NBP1_CAP_OFFSET); return val & (1 << 31); }

Contributors

PersonTokensPropCommitsCommitProp
Dominik Brodowski29100.00%1100.00%
Total29100.00%1100.00%


struct cpuidle_monitor *amd_fam14h_register(void) { int num; if (cpupower_cpu_info.vendor != X86_VENDOR_AMD) return NULL; if (cpupower_cpu_info.family == 0x14) strncpy(amd_fam14h_monitor.name, "Fam_14h", MONITOR_NAME_LEN - 1); else if (cpupower_cpu_info.family == 0x12) strncpy(amd_fam14h_monitor.name, "Fam_12h", MONITOR_NAME_LEN - 1); else return NULL; /* We do not alloc for nbp1 machine wide counter */ for (num = 0; num < AMD_FAM14H_STATE_NUM - 1; num++) { previous_count[num] = calloc(cpu_count, sizeof(unsigned long long)); current_count[num] = calloc(cpu_count, sizeof(unsigned long long)); } /* We need PCI device: Slot 18, Func 6, compare with BKDG for fam 12h/14h */ amd_fam14h_pci_dev = pci_slot_func_init(&pci_acc, 0x18, 6); if (amd_fam14h_pci_dev == NULL || pci_acc == NULL) return NULL; if (!is_nbp1_capable()) amd_fam14h_monitor.hw_states_num = AMD_FAM14H_STATE_NUM - 1; amd_fam14h_monitor.name_len = strlen(amd_fam14h_monitor.name); return &amd_fam14h_monitor; }

Contributors

PersonTokensPropCommitsCommitProp
Dominik Brodowski14682.49%133.33%
Thomas Renninger3117.51%266.67%
Total177100.00%3100.00%


static void amd_fam14h_unregister(void) { int num; for (num = 0; num < AMD_FAM14H_STATE_NUM - 1; num++) { free(previous_count[num]); free(current_count[num]); } pci_cleanup(pci_acc); }

Contributors

PersonTokensPropCommitsCommitProp
Dominik Brodowski49100.00%1100.00%
Total49100.00%1100.00%

struct cpuidle_monitor amd_fam14h_monitor = { .name = "", .hw_states = amd_fam14h_cstates, .hw_states_num = AMD_FAM14H_STATE_NUM, .start = amd_fam14h_start, .stop = amd_fam14h_stop, .do_register = amd_fam14h_register, .unregister = amd_fam14h_unregister, .needs_root = 1, .overflow_s = OVERFLOW_MS / 1000, }; #endif /* #if defined(__i386__) || defined(__x86_64__) */

Overall Contributors

PersonTokensPropCommitsCommitProp
Dominik Brodowski144897.84%133.33%
Thomas Renninger322.16%266.67%
Total1480100.00%3100.00%
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.
Created with cregit.