• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
42 enum amd_fam14h_states {NON_PC0 = 0, PC1, PC6, NBP1,
43 			AMD_FAM14H_STATE_NUM};
44 
45 static int fam14h_get_count_percent(unsigned int self_id, double *percent,
46 				    unsigned int cpu);
47 static int fam14h_nbp1_count(unsigned int id, unsigned long long *count,
48 			     unsigned int cpu);
49 
50 static 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 
81 static struct pci_access *pci_acc;
82 static struct pci_dev *amd_fam14h_pci_dev;
83 static int nbp1_entered;
84 
85 static struct timespec start_time;
86 static unsigned long long timediff;
87 
88 #ifdef DEBUG
89 struct timespec dbg_time;
90 long dbg_timediff;
91 #endif
92 
93 static unsigned long long *previous_count[AMD_FAM14H_STATE_NUM];
94 static unsigned long long *current_count[AMD_FAM14H_STATE_NUM];
95 
amd_fam14h_get_pci_info(struct cstate * state,unsigned int * pci_offset,unsigned int * enable_bit,unsigned int cpu)96 static 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 
amd_fam14h_init(cstate_t * state,unsigned int cpu)124 static 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 
amd_fam14h_disable(cstate_t * state,unsigned int cpu)159 static 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 
fam14h_nbp1_count(unsigned int id,unsigned long long * count,unsigned int cpu)195 static 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 }
fam14h_get_count_percent(unsigned int id,double * percent,unsigned int cpu)207 static 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 
amd_fam14h_start(void)228 static 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 
amd_fam14h_stop(void)245 static 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 
is_nbp1_capable(void)269 static 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 
amd_fam14h_register(void)276 struct 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 
amd_fam14h_unregister(void)313 static 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 
323 struct 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