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 /******** PCI parts could go into own file and get shared ***************/
25 #define PCI_NON_PC0_OFFSET 0xb0
26 #define PCI_PC1_OFFSET 0xb4
27 #define PCI_PC6_OFFSET 0xb8
29 #define PCI_MONITOR_ENABLE_REG 0xe0
31 #define PCI_NON_PC0_ENABLE_BIT 0
32 #define PCI_PC1_ENABLE_BIT 1
33 #define PCI_PC6_ENABLE_BIT 2
35 #define PCI_NBP1_STAT_OFFSET 0x98
36 #define PCI_NBP1_ACTIVE_BIT 2
37 #define PCI_NBP1_ENTERED_BIT 1
39 #define PCI_NBP1_CAP_OFFSET 0x90
40 #define PCI_NBP1_CAPABLE_BIT 31
42 #define OVERFLOW_MS 343597 /* 32 bit register filled at 12500 HZ
45 enum amd_fam14h_states
{NON_PC0
= 0, PC1
, PC6
, NBP1
,
46 AMD_FAM14H_STATE_NUM
};
48 static int fam14h_get_count_percent(unsigned int self_id
, double *percent
,
50 static int fam14h_nbp1_count(unsigned int id
, unsigned long long *count
,
53 static cstate_t amd_fam14h_cstates
[AMD_FAM14H_STATE_NUM
] = {
56 .desc
= N_("Package in sleep state (PC1 or deeper)"),
58 .range
= RANGE_PACKAGE
,
59 .get_count_percent
= fam14h_get_count_percent
,
63 .desc
= N_("Processor Package C1"),
65 .range
= RANGE_PACKAGE
,
66 .get_count_percent
= fam14h_get_count_percent
,
70 .desc
= N_("Processor Package C6"),
72 .range
= RANGE_PACKAGE
,
73 .get_count_percent
= fam14h_get_count_percent
,
77 .desc
= N_("North Bridge P1 boolean counter (returns 0 or 1)"),
79 .range
= RANGE_PACKAGE
,
80 .get_count
= fam14h_nbp1_count
,
84 static struct pci_access
*pci_acc
;
85 static int pci_vendor_id
= 0x1022;
86 static int pci_dev_ids
[2] = {0x1716, 0};
87 static struct pci_dev
*amd_fam14h_pci_dev
;
89 static int nbp1_entered
;
91 struct timespec start_time
;
92 static unsigned long long timediff
;
95 struct timespec dbg_time
;
99 static unsigned long long *previous_count
[AMD_FAM14H_STATE_NUM
];
100 static unsigned long long *current_count
[AMD_FAM14H_STATE_NUM
];
102 static int amd_fam14h_get_pci_info(struct cstate
*state
,
103 unsigned int *pci_offset
,
104 unsigned int *enable_bit
,
109 *enable_bit
= PCI_NON_PC0_ENABLE_BIT
;
110 *pci_offset
= PCI_NON_PC0_OFFSET
;
113 *enable_bit
= PCI_PC1_ENABLE_BIT
;
114 *pci_offset
= PCI_PC1_OFFSET
;
117 *enable_bit
= PCI_PC6_ENABLE_BIT
;
118 *pci_offset
= PCI_PC6_OFFSET
;
121 *enable_bit
= PCI_NBP1_ENTERED_BIT
;
122 *pci_offset
= PCI_NBP1_STAT_OFFSET
;
130 static int amd_fam14h_init(cstate_t
*state
, unsigned int cpu
)
132 int enable_bit
, pci_offset
, ret
;
135 ret
= amd_fam14h_get_pci_info(state
, &pci_offset
, &enable_bit
, cpu
);
139 /* NBP1 needs extra treating -> write 1 to D18F6x98 bit 1 for init */
140 if (state
->id
== NBP1
) {
141 val
= pci_read_long(amd_fam14h_pci_dev
, pci_offset
);
142 val
|= 1 << enable_bit
;
143 val
= pci_write_long(amd_fam14h_pci_dev
, pci_offset
, val
);
148 val
= pci_read_long(amd_fam14h_pci_dev
, PCI_MONITOR_ENABLE_REG
);
149 dprint("Init %s: read at offset: 0x%x val: %u\n", state
->name
,
150 PCI_MONITOR_ENABLE_REG
, (unsigned int) val
);
151 val
|= 1 << enable_bit
;
152 pci_write_long(amd_fam14h_pci_dev
, PCI_MONITOR_ENABLE_REG
, val
);
154 dprint("Init %s: offset: 0x%x enable_bit: %d - val: %u (%u)\n",
155 state
->name
, PCI_MONITOR_ENABLE_REG
, enable_bit
,
156 (unsigned int) val
, cpu
);
158 /* Set counter to zero */
159 pci_write_long(amd_fam14h_pci_dev
, pci_offset
, 0);
160 previous_count
[state
->id
][cpu
] = 0;
165 static int amd_fam14h_disable(cstate_t
*state
, unsigned int cpu
)
167 int enable_bit
, pci_offset
, ret
;
170 ret
= amd_fam14h_get_pci_info(state
, &pci_offset
, &enable_bit
, cpu
);
174 val
= pci_read_long(amd_fam14h_pci_dev
, pci_offset
);
175 dprint("%s: offset: 0x%x %u\n", state
->name
, pci_offset
, val
);
176 if (state
->id
== NBP1
) {
177 /* was the bit whether NBP1 got entered set? */
178 nbp1_entered
= (val
& (1 << PCI_NBP1_ACTIVE_BIT
)) |
179 (val
& (1 << PCI_NBP1_ENTERED_BIT
));
181 dprint("NBP1 was %sentered - 0x%x - enable_bit: "
182 "%d - pci_offset: 0x%x\n",
183 nbp1_entered
? "" : "not ",
184 val
, enable_bit
, pci_offset
);
187 current_count
[state
->id
][cpu
] = val
;
189 dprint("%s: Current - %llu (%u)\n", state
->name
,
190 current_count
[state
->id
][cpu
], cpu
);
191 dprint("%s: Previous - %llu (%u)\n", state
->name
,
192 previous_count
[state
->id
][cpu
], cpu
);
194 val
= pci_read_long(amd_fam14h_pci_dev
, PCI_MONITOR_ENABLE_REG
);
195 val
&= ~(1 << enable_bit
);
196 pci_write_long(amd_fam14h_pci_dev
, PCI_MONITOR_ENABLE_REG
, val
);
201 static int fam14h_nbp1_count(unsigned int id
, unsigned long long *count
,
213 static int fam14h_get_count_percent(unsigned int id
, double *percent
,
218 if (id
>= AMD_FAM14H_STATE_NUM
)
220 /* residency count in 80ns -> divide through 12.5 to get us residency */
221 diff
= current_count
[id
][cpu
] - previous_count
[id
][cpu
];
226 *percent
= 100.0 * diff
/ timediff
/ 12.5;
228 dprint("Timediff: %llu - res~: %lu us - percent: %.2f %%\n",
229 timediff
, diff
* 10 / 125, *percent
);
234 static int amd_fam14h_start(void)
237 clock_gettime(CLOCK_REALTIME
, &start_time
);
238 for (num
= 0; num
< AMD_FAM14H_STATE_NUM
; num
++) {
239 for (cpu
= 0; cpu
< cpu_count
; cpu
++)
240 amd_fam14h_init(&amd_fam14h_cstates
[num
], cpu
);
243 clock_gettime(CLOCK_REALTIME
, &dbg_time
);
244 dbg_timediff
= timespec_diff_us(start_time
, dbg_time
);
245 dprint("Enabling counters took: %lu us\n",
251 static int amd_fam14h_stop(void)
254 struct timespec end_time
;
256 clock_gettime(CLOCK_REALTIME
, &end_time
);
258 for (num
= 0; num
< AMD_FAM14H_STATE_NUM
; num
++) {
259 for (cpu
= 0; cpu
< cpu_count
; cpu
++)
260 amd_fam14h_disable(&amd_fam14h_cstates
[num
], cpu
);
263 clock_gettime(CLOCK_REALTIME
, &dbg_time
);
264 dbg_timediff
= timespec_diff_us(end_time
, dbg_time
);
265 dprint("Disabling counters took: %lu ns\n", dbg_timediff
);
267 timediff
= timespec_diff_us(start_time
, end_time
);
268 if (timediff
/ 1000 > OVERFLOW_MS
)
269 print_overflow_err((unsigned int)timediff
/ 1000000,
275 static int is_nbp1_capable(void)
278 val
= pci_read_long(amd_fam14h_pci_dev
, PCI_NBP1_CAP_OFFSET
);
279 return val
& (1 << 31);
282 struct cpuidle_monitor
*amd_fam14h_register(void)
286 if (cpupower_cpu_info
.vendor
!= X86_VENDOR_AMD
)
289 if (cpupower_cpu_info
.family
== 0x14) {
290 if (cpu_count
<= 0 || cpu_count
> 2) {
291 fprintf(stderr
, "AMD fam14h: Invalid cpu count: %d\n",
298 /* We do not alloc for nbp1 machine wide counter */
299 for (num
= 0; num
< AMD_FAM14H_STATE_NUM
- 1; num
++) {
300 previous_count
[num
] = calloc(cpu_count
,
301 sizeof(unsigned long long));
302 current_count
[num
] = calloc(cpu_count
,
303 sizeof(unsigned long long));
306 amd_fam14h_pci_dev
= pci_acc_init(&pci_acc
, pci_vendor_id
, pci_dev_ids
);
307 if (amd_fam14h_pci_dev
== NULL
|| pci_acc
== NULL
)
310 if (!is_nbp1_capable())
311 amd_fam14h_monitor
.hw_states_num
= AMD_FAM14H_STATE_NUM
- 1;
313 amd_fam14h_monitor
.name_len
= strlen(amd_fam14h_monitor
.name
);
314 return &amd_fam14h_monitor
;
317 static void amd_fam14h_unregister(void)
320 for (num
= 0; num
< AMD_FAM14H_STATE_NUM
- 1; num
++) {
321 free(previous_count
[num
]);
322 free(current_count
[num
]);
324 pci_cleanup(pci_acc
);
327 struct cpuidle_monitor amd_fam14h_monitor
= {
329 .hw_states
= amd_fam14h_cstates
,
330 .hw_states_num
= AMD_FAM14H_STATE_NUM
,
331 .start
= amd_fam14h_start
,
332 .stop
= amd_fam14h_stop
,
333 .do_register
= amd_fam14h_register
,
334 .unregister
= amd_fam14h_unregister
,
336 .overflow_s
= OVERFLOW_MS
/ 1000,
338 #endif /* #if defined(__i386__) || defined(__x86_64__) */