File Index
Files and directories
Sign in
FILE

amd_fam14h_idle.c

File preview with syntax highlighting.

linux-6.19.14/tools/power/cpupower/utils/idle_monitor/amd_fam14h_idle.c C
  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__) */
~/ Read-only file preview.