2 * (C) 2010,2011 Thomas Renninger <trenn@suse.de>, Novell Inc.
4 * Licensed under the terms of the GNU GPL License version 2.
6 * PCI initialization based on example code from:
7 * Andreas Herrmann <andreas.herrmann3@amd.com>
10 #if defined(__i386__) || defined(__x86_64__)
20 #include "idle_monitor/cpupower-monitor.h"
21 #include "helpers/helpers.h"
23 #define PCI_NON_PC0_OFFSET 0xb0
24 #define PCI_PC1_OFFSET 0xb4
25 #define PCI_PC6_OFFSET 0xb8
27 #define PCI_MONITOR_ENABLE_REG 0xe0
29 #define PCI_NON_PC0_ENABLE_BIT 0
30 #define PCI_PC1_ENABLE_BIT 1
31 #define PCI_PC6_ENABLE_BIT 2
33 #define PCI_NBP1_STAT_OFFSET 0x98
34 #define PCI_NBP1_ACTIVE_BIT 2
35 #define PCI_NBP1_ENTERED_BIT 1
37 #define PCI_NBP1_CAP_OFFSET 0x90
38 #define PCI_NBP1_CAPABLE_BIT 31
40 #define OVERFLOW_MS 343597 /* 32 bit register filled at 12500 HZ
43 enum amd_fam14h_states
{NON_PC0
= 0, PC1
, PC6
, NBP1
,
44 AMD_FAM14H_STATE_NUM
};
46 static int fam14h_get_count_percent(unsigned int self_id
, double *percent
,
48 static int fam14h_nbp1_count(unsigned int id
, unsigned long long *count
,
51 static cstate_t amd_fam14h_cstates
[AMD_FAM14H_STATE_NUM
] = {
54 .desc
= N_("Package in sleep state (PC1 or deeper)"),
56 .range
= RANGE_PACKAGE
,
57 .get_count_percent
= fam14h_get_count_percent
,
61 .desc
= N_("Processor Package C1"),
63 .range
= RANGE_PACKAGE
,
64 .get_count_percent
= fam14h_get_count_percent
,
68 .desc
= N_("Processor Package C6"),
70 .range
= RANGE_PACKAGE
,
71 .get_count_percent
= fam14h_get_count_percent
,
75 .desc
= N_("North Bridge P1 boolean counter (returns 0 or 1)"),
77 .range
= RANGE_PACKAGE
,
78 .get_count
= fam14h_nbp1_count
,
82 static struct pci_access
*pci_acc
;
83 static struct pci_dev
*amd_fam14h_pci_dev
;
84 static int nbp1_entered
;
86 struct timespec start_time
;
87 static unsigned long long timediff
;
90 struct timespec dbg_time
;
94 static unsigned long long *previous_count
[AMD_FAM14H_STATE_NUM
];
95 static unsigned long long *current_count
[AMD_FAM14H_STATE_NUM
];
97 static int amd_fam14h_get_pci_info(struct cstate
*state
,
98 unsigned int *pci_offset
,
99 unsigned int *enable_bit
,
104 *enable_bit
= PCI_NON_PC0_ENABLE_BIT
;
105 *pci_offset
= PCI_NON_PC0_OFFSET
;
108 *enable_bit
= PCI_PC1_ENABLE_BIT
;
109 *pci_offset
= PCI_PC1_OFFSET
;
112 *enable_bit
= PCI_PC6_ENABLE_BIT
;
113 *pci_offset
= PCI_PC6_OFFSET
;
116 *enable_bit
= PCI_NBP1_ENTERED_BIT
;
117 *pci_offset
= PCI_NBP1_STAT_OFFSET
;
125 static int amd_fam14h_init(cstate_t
*state
, unsigned int cpu
)
127 int enable_bit
, pci_offset
, ret
;
130 ret
= amd_fam14h_get_pci_info(state
, &pci_offset
, &enable_bit
, cpu
);
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
);
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
);
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
);
153 /* Set counter to zero */
154 pci_write_long(amd_fam14h_pci_dev
, pci_offset
, 0);
155 previous_count
[state
->id
][cpu
] = 0;
160 static int amd_fam14h_disable(cstate_t
*state
, unsigned int cpu
)
162 int enable_bit
, pci_offset
, ret
;
165 ret
= amd_fam14h_get_pci_info(state
, &pci_offset
, &enable_bit
, cpu
);
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
));
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
);
182 current_count
[state
->id
][cpu
] = val
;
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
);
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
);
196 static int fam14h_nbp1_count(unsigned int id
, unsigned long long *count
,
208 static int fam14h_get_count_percent(unsigned int id
, double *percent
,
213 if (id
>= AMD_FAM14H_STATE_NUM
)
215 /* residency count in 80ns -> divide through 12.5 to get us residency */
216 diff
= current_count
[id
][cpu
] - previous_count
[id
][cpu
];
221 *percent
= 100.0 * diff
/ timediff
/ 12.5;
223 dprint("Timediff: %llu - res~: %lu us - percent: %.2f %%\n",
224 timediff
, diff
* 10 / 125, *percent
);
229 static int amd_fam14h_start(void)
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
);
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",
246 static int amd_fam14h_stop(void)
249 struct timespec end_time
;
251 clock_gettime(CLOCK_REALTIME
, &end_time
);
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
);
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
);
262 timediff
= timespec_diff_us(start_time
, end_time
);
263 if (timediff
/ 1000 > OVERFLOW_MS
)
264 print_overflow_err((unsigned int)timediff
/ 1000000,
270 static int is_nbp1_capable(void)
273 val
= pci_read_long(amd_fam14h_pci_dev
, PCI_NBP1_CAP_OFFSET
);
274 return val
& (1 << 31);
277 struct cpuidle_monitor
*amd_fam14h_register(void)
281 if (cpupower_cpu_info
.vendor
!= X86_VENDOR_AMD
)
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);
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));
301 /* We need PCI device: Slot 18, Func 6, compare with BKDG
303 amd_fam14h_pci_dev
= pci_slot_func_init(&pci_acc
, 0x18, 6);
304 if (amd_fam14h_pci_dev
== NULL
|| pci_acc
== NULL
)
307 if (!is_nbp1_capable())
308 amd_fam14h_monitor
.hw_states_num
= AMD_FAM14H_STATE_NUM
- 1;
310 amd_fam14h_monitor
.name_len
= strlen(amd_fam14h_monitor
.name
);
311 return &amd_fam14h_monitor
;
314 static void amd_fam14h_unregister(void)
317 for (num
= 0; num
< AMD_FAM14H_STATE_NUM
- 1; num
++) {
318 free(previous_count
[num
]);
319 free(current_count
[num
]);
321 pci_cleanup(pci_acc
);
324 struct cpuidle_monitor amd_fam14h_monitor
= {
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
,
333 .overflow_s
= OVERFLOW_MS
/ 1000,
335 #endif /* #if defined(__i386__) || defined(__x86_64__) */