brintos

brintos / linux-shallow public Read only

0
0
Text · 8.4 KiB · 5edd35b Raw
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