diff options
Diffstat (limited to 'tools/power/cpupower/debug/kernel')
-rw-r--r-- | tools/power/cpupower/debug/kernel/Makefile | 23 | ||||
-rw-r--r-- | tools/power/cpupower/debug/kernel/cpufreq-test_tsc.c | 113 |
2 files changed, 136 insertions, 0 deletions
diff --git a/tools/power/cpupower/debug/kernel/Makefile b/tools/power/cpupower/debug/kernel/Makefile new file mode 100644 index 00000000000..96b146fe6f8 --- /dev/null +++ b/tools/power/cpupower/debug/kernel/Makefile | |||
@@ -0,0 +1,23 @@ | |||
1 | obj-m := | ||
2 | |||
3 | KDIR := /lib/modules/$(shell uname -r)/build | ||
4 | PWD := $(shell pwd) | ||
5 | KMISC := /lib/modules/$(shell uname -r)/cpufrequtils/ | ||
6 | |||
7 | ifeq ("$(CONFIG_X86_TSC)", "y") | ||
8 | obj-m += cpufreq-test_tsc.o | ||
9 | endif | ||
10 | |||
11 | default: | ||
12 | $(MAKE) -C $(KDIR) M=$(PWD) | ||
13 | |||
14 | clean: | ||
15 | - rm -rf *.o *.ko .tmp-versions .*.cmd .*.mod.* *.mod.c | ||
16 | - rm -rf .tmp_versions* Module.symvers modules.order | ||
17 | |||
18 | install: default | ||
19 | install -d $(KMISC) | ||
20 | install -m 644 -c *.ko $(KMISC) | ||
21 | /sbin/depmod -a | ||
22 | |||
23 | all: default | ||
diff --git a/tools/power/cpupower/debug/kernel/cpufreq-test_tsc.c b/tools/power/cpupower/debug/kernel/cpufreq-test_tsc.c new file mode 100644 index 00000000000..66cace601e5 --- /dev/null +++ b/tools/power/cpupower/debug/kernel/cpufreq-test_tsc.c | |||
@@ -0,0 +1,113 @@ | |||
1 | /* | ||
2 | * test module to check whether the TSC-based delay routine continues | ||
3 | * to work properly after cpufreq transitions. Needs ACPI to work | ||
4 | * properly. | ||
5 | * | ||
6 | * Based partly on the Power Management Timer (PMTMR) code to be found | ||
7 | * in arch/i386/kernel/timers/timer_pm.c on recent 2.6. kernels, especially | ||
8 | * code written by John Stultz. The read_pmtmr function was copied verbatim | ||
9 | * from that file. | ||
10 | * | ||
11 | * (C) 2004 Dominik Brodowski | ||
12 | * | ||
13 | * To use: | ||
14 | * 1.) pass clock=tsc to the kernel on your bootloader | ||
15 | * 2.) modprobe this module (it'll fail) | ||
16 | * 3.) change CPU frequency | ||
17 | * 4.) modprobe this module again | ||
18 | * 5.) if the third value, "diff_pmtmr", changes between 2. and 4., the | ||
19 | * TSC-based delay routine on the Linux kernel does not correctly | ||
20 | * handle the cpufreq transition. Please report this to | ||
21 | * cpufreq@vger.kernel.org | ||
22 | */ | ||
23 | |||
24 | #include <linux/kernel.h> | ||
25 | #include <linux/module.h> | ||
26 | #include <linux/init.h> | ||
27 | #include <linux/delay.h> | ||
28 | |||
29 | #include <asm/io.h> | ||
30 | |||
31 | #include <acpi/acpi_bus.h> | ||
32 | #include <acpi/acpi_drivers.h> | ||
33 | |||
34 | static int pm_tmr_ioport = 0; | ||
35 | |||
36 | /*helper function to safely read acpi pm timesource*/ | ||
37 | static u32 read_pmtmr(void) | ||
38 | { | ||
39 | u32 v1=0,v2=0,v3=0; | ||
40 | /* It has been reported that because of various broken | ||
41 | * chipsets (ICH4, PIIX4 and PIIX4E) where the ACPI PM time | ||
42 | * source is not latched, so you must read it multiple | ||
43 | * times to insure a safe value is read. | ||
44 | */ | ||
45 | do { | ||
46 | v1 = inl(pm_tmr_ioport); | ||
47 | v2 = inl(pm_tmr_ioport); | ||
48 | v3 = inl(pm_tmr_ioport); | ||
49 | } while ((v1 > v2 && v1 < v3) || (v2 > v3 && v2 < v1) | ||
50 | || (v3 > v1 && v3 < v2)); | ||
51 | |||
52 | /* mask the output to 24 bits */ | ||
53 | return (v2 & 0xFFFFFF); | ||
54 | } | ||
55 | |||
56 | static int __init cpufreq_test_tsc(void) | ||
57 | { | ||
58 | u32 now, then, diff; | ||
59 | u64 now_tsc, then_tsc, diff_tsc; | ||
60 | int i; | ||
61 | |||
62 | /* the following code snipped is copied from arch/x86/kernel/acpi/boot.c | ||
63 | of Linux v2.6.25. */ | ||
64 | |||
65 | /* detect the location of the ACPI PM Timer */ | ||
66 | if (acpi_gbl_FADT.header.revision >= FADT2_REVISION_ID) { | ||
67 | /* FADT rev. 2 */ | ||
68 | if (acpi_gbl_FADT.xpm_timer_block.space_id != | ||
69 | ACPI_ADR_SPACE_SYSTEM_IO) | ||
70 | return 0; | ||
71 | |||
72 | pm_tmr_ioport = acpi_gbl_FADT.xpm_timer_block.address; | ||
73 | /* | ||
74 | * "X" fields are optional extensions to the original V1.0 | ||
75 | * fields, so we must selectively expand V1.0 fields if the | ||
76 | * corresponding X field is zero. | ||
77 | */ | ||
78 | if (!pm_tmr_ioport) | ||
79 | pm_tmr_ioport = acpi_gbl_FADT.pm_timer_block; | ||
80 | } else { | ||
81 | /* FADT rev. 1 */ | ||
82 | pm_tmr_ioport = acpi_gbl_FADT.pm_timer_block; | ||
83 | } | ||
84 | |||
85 | printk(KERN_DEBUG "start--> \n"); | ||
86 | then = read_pmtmr(); | ||
87 | rdtscll(then_tsc); | ||
88 | for (i=0;i<20;i++) { | ||
89 | mdelay(100); | ||
90 | now = read_pmtmr(); | ||
91 | rdtscll(now_tsc); | ||
92 | diff = (now - then) & 0xFFFFFF; | ||
93 | diff_tsc = now_tsc - then_tsc; | ||
94 | printk(KERN_DEBUG "t1: %08u t2: %08u diff_pmtmr: %08u diff_tsc: %016llu\n", then, now, diff, diff_tsc); | ||
95 | then = now; | ||
96 | then_tsc = now_tsc; | ||
97 | } | ||
98 | printk(KERN_DEBUG "<-- end \n"); | ||
99 | return -ENODEV; | ||
100 | } | ||
101 | |||
102 | static void __exit cpufreq_none(void) | ||
103 | { | ||
104 | return; | ||
105 | } | ||
106 | |||
107 | module_init(cpufreq_test_tsc) | ||
108 | module_exit(cpufreq_none) | ||
109 | |||
110 | |||
111 | MODULE_AUTHOR("Dominik Brodowski"); | ||
112 | MODULE_DESCRIPTION("Verify the TSC cpufreq notifier working correctly -- needs ACPI-enabled system"); | ||
113 | MODULE_LICENSE ("GPL"); | ||