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