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 
43 enum amd_fam14h_states {NON_PC0 = 0, PC1, PC6, NBP1,
44 			AMD_FAM14H_STATE_NUM};
45 
46 static int fam14h_get_count_percent(unsigned int self_id, double *percent,
47 				    unsigned int cpu);
48 static int fam14h_nbp1_count(unsigned int id, unsigned long long *count,
49 			     unsigned int cpu);
50 
51 static 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 
82 static struct pci_access *pci_acc;
83 static struct pci_dev *amd_fam14h_pci_dev;
84 static int nbp1_entered;
85 
86 struct timespec start_time;
87 static unsigned long long timediff;
88 
89 #ifdef DEBUG
90 struct timespec dbg_time;
91 long dbg_timediff;
92 #endif
93 
94 static unsigned long long *previous_count[AMD_FAM14H_STATE_NUM];
95 static unsigned long long *current_count[AMD_FAM14H_STATE_NUM];
96 
97 static 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 
125 static 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 
160 static 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 
196 static 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 }
208 static 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 
229 static 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 
246 static 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 
270 static 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 
277 struct 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 
314 static 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 
324 struct 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