1 // SPDX-License-Identifier: GPL-2.0-only
3 * (C) 2010,2011 Thomas Renninger <trenn@suse.de>, Novell Inc.
5 * PCI initialization based on example code from:
6 * Andreas Herrmann <andreas.herrmann3@amd.com>
9 #if defined(__i386__) || defined(__x86_64__)
19 #include "idle_monitor/cpupower-monitor.h"
20 #include "helpers/helpers.h"
22 #define PCI_NON_PC0_OFFSET 0xb0
23 #define PCI_PC1_OFFSET 0xb4
24 #define PCI_PC6_OFFSET 0xb8
26 #define PCI_MONITOR_ENABLE_REG 0xe0
28 #define PCI_NON_PC0_ENABLE_BIT 0
29 #define PCI_PC1_ENABLE_BIT 1
30 #define PCI_PC6_ENABLE_BIT 2
32 #define PCI_NBP1_STAT_OFFSET 0x98
33 #define PCI_NBP1_ACTIVE_BIT 2
34 #define PCI_NBP1_ENTERED_BIT 1
36 #define PCI_NBP1_CAP_OFFSET 0x90
37 #define PCI_NBP1_CAPABLE_BIT 31
39 #define OVERFLOW_MS 343597 /* 32 bit register filled at 12500 HZ
42 enum amd_fam14h_states
{NON_PC0
= 0, PC1
, PC6
, NBP1
,
43 AMD_FAM14H_STATE_NUM
};
45 static int fam14h_get_count_percent(unsigned int self_id
, double *percent
,
47 static int fam14h_nbp1_count(unsigned int id
, unsigned long long *count
,
50 static cstate_t amd_fam14h_cstates
[AMD_FAM14H_STATE_NUM
] = {
53 .desc
= N_("Package in sleep state (PC1 or deeper)"),
55 .range
= RANGE_PACKAGE
,
56 .get_count_percent
= fam14h_get_count_percent
,
60 .desc
= N_("Processor Package C1"),
62 .range
= RANGE_PACKAGE
,
63 .get_count_percent
= fam14h_get_count_percent
,
67 .desc
= N_("Processor Package C6"),
69 .range
= RANGE_PACKAGE
,
70 .get_count_percent
= fam14h_get_count_percent
,
74 .desc
= N_("North Bridge P1 boolean counter (returns 0 or 1)"),
76 .range
= RANGE_PACKAGE
,
77 .get_count
= fam14h_nbp1_count
,
81 static struct pci_access
*pci_acc
;
82 static struct pci_dev
*amd_fam14h_pci_dev
;
83 static int nbp1_entered
;
85 static struct timespec start_time
;
86 static unsigned long long timediff
;
89 struct timespec dbg_time
;
93 static unsigned long long *previous_count
[AMD_FAM14H_STATE_NUM
];
94 static unsigned long long *current_count
[AMD_FAM14H_STATE_NUM
];
96 static int amd_fam14h_get_pci_info(struct cstate
*state
,
97 unsigned int *pci_offset
,
98 unsigned int *enable_bit
,
103 *enable_bit
= PCI_NON_PC0_ENABLE_BIT
;
104 *pci_offset
= PCI_NON_PC0_OFFSET
;
107 *enable_bit
= PCI_PC1_ENABLE_BIT
;
108 *pci_offset
= PCI_PC1_OFFSET
;
111 *enable_bit
= PCI_PC6_ENABLE_BIT
;
112 *pci_offset
= PCI_PC6_OFFSET
;
115 *enable_bit
= PCI_NBP1_ENTERED_BIT
;
116 *pci_offset
= PCI_NBP1_STAT_OFFSET
;
124 static int amd_fam14h_init(cstate_t
*state
, unsigned int cpu
)
126 int enable_bit
, pci_offset
, ret
;
129 ret
= amd_fam14h_get_pci_info(state
, &pci_offset
, &enable_bit
, cpu
);
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
);
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
);
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
);
152 /* Set counter to zero */
153 pci_write_long(amd_fam14h_pci_dev
, pci_offset
, 0);
154 previous_count
[state
->id
][cpu
] = 0;
159 static int amd_fam14h_disable(cstate_t
*state
, unsigned int cpu
)
161 int enable_bit
, pci_offset
, ret
;
164 ret
= amd_fam14h_get_pci_info(state
, &pci_offset
, &enable_bit
, cpu
);
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
));
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
);
181 current_count
[state
->id
][cpu
] = val
;
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
);
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
);
195 static int fam14h_nbp1_count(unsigned int id
, unsigned long long *count
,
207 static int fam14h_get_count_percent(unsigned int id
, double *percent
,
212 if (id
>= AMD_FAM14H_STATE_NUM
)
214 /* residency count in 80ns -> divide through 12.5 to get us residency */
215 diff
= current_count
[id
][cpu
] - previous_count
[id
][cpu
];
220 *percent
= 100.0 * diff
/ timediff
/ 12.5;
222 dprint("Timediff: %llu - res~: %lu us - percent: %.2f %%\n",
223 timediff
, diff
* 10 / 125, *percent
);
228 static int amd_fam14h_start(void)
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
);
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",
245 static int amd_fam14h_stop(void)
248 struct timespec end_time
;
250 clock_gettime(CLOCK_REALTIME
, &end_time
);
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
);
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
);
261 timediff
= timespec_diff_us(start_time
, end_time
);
262 if (timediff
/ 1000 > OVERFLOW_MS
)
263 print_overflow_err((unsigned int)timediff
/ 1000000,
269 static int is_nbp1_capable(void)
272 val
= pci_read_long(amd_fam14h_pci_dev
, PCI_NBP1_CAP_OFFSET
);
273 return val
& (1 << 31);
276 struct cpuidle_monitor
*amd_fam14h_register(void)
280 if (cpupower_cpu_info
.vendor
!= X86_VENDOR_AMD
)
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);
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));
300 /* We need PCI device: Slot 18, Func 6, compare with BKDG
302 amd_fam14h_pci_dev
= pci_slot_func_init(&pci_acc
, 0x18, 6);
303 if (amd_fam14h_pci_dev
== NULL
|| pci_acc
== NULL
)
306 if (!is_nbp1_capable())
307 amd_fam14h_monitor
.hw_states_num
= AMD_FAM14H_STATE_NUM
- 1;
309 amd_fam14h_monitor
.name_len
= strlen(amd_fam14h_monitor
.name
);
310 return &amd_fam14h_monitor
;
313 static void amd_fam14h_unregister(void)
316 for (num
= 0; num
< AMD_FAM14H_STATE_NUM
- 1; num
++) {
317 free(previous_count
[num
]);
318 free(current_count
[num
]);
320 pci_cleanup(pci_acc
);
323 struct cpuidle_monitor amd_fam14h_monitor
= {
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,
334 #endif /* #if defined(__i386__) || defined(__x86_64__) */