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 * linux-pm@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 #include <linux/acpi.h> 29 #include <asm/io.h> 30 31 static int pm_tmr_ioport = 0; 32 33 /*helper function to safely read acpi pm timesource*/ 34 static u32 read_pmtmr(void) 35 { 36 u32 v1=0,v2=0,v3=0; 37 /* It has been reported that because of various broken 38 * chipsets (ICH4, PIIX4 and PIIX4E) where the ACPI PM time 39 * source is not latched, so you must read it multiple 40 * times to insure a safe value is read. 41 */ 42 do { 43 v1 = inl(pm_tmr_ioport); 44 v2 = inl(pm_tmr_ioport); 45 v3 = inl(pm_tmr_ioport); 46 } while ((v1 > v2 && v1 < v3) || (v2 > v3 && v2 < v1) 47 || (v3 > v1 && v3 < v2)); 48 49 /* mask the output to 24 bits */ 50 return (v2 & 0xFFFFFF); 51 } 52 53 static int __init cpufreq_test_tsc(void) 54 { 55 u32 now, then, diff; 56 u64 now_tsc, then_tsc, diff_tsc; 57 int i; 58 59 /* the following code snipped is copied from arch/x86/kernel/acpi/boot.c 60 of Linux v2.6.25. */ 61 62 /* detect the location of the ACPI PM Timer */ 63 if (acpi_gbl_FADT.header.revision >= FADT2_REVISION_ID) { 64 /* FADT rev. 2 */ 65 if (acpi_gbl_FADT.xpm_timer_block.space_id != 66 ACPI_ADR_SPACE_SYSTEM_IO) 67 return 0; 68 69 pm_tmr_ioport = acpi_gbl_FADT.xpm_timer_block.address; 70 /* 71 * "X" fields are optional extensions to the original V1.0 72 * fields, so we must selectively expand V1.0 fields if the 73 * corresponding X field is zero. 74 */ 75 if (!pm_tmr_ioport) 76 pm_tmr_ioport = acpi_gbl_FADT.pm_timer_block; 77 } else { 78 /* FADT rev. 1 */ 79 pm_tmr_ioport = acpi_gbl_FADT.pm_timer_block; 80 } 81 82 printk(KERN_DEBUG "start--> \n"); 83 then = read_pmtmr(); 84 rdtscll(then_tsc); 85 for (i=0;i<20;i++) { 86 mdelay(100); 87 now = read_pmtmr(); 88 rdtscll(now_tsc); 89 diff = (now - then) & 0xFFFFFF; 90 diff_tsc = now_tsc - then_tsc; 91 printk(KERN_DEBUG "t1: %08u t2: %08u diff_pmtmr: %08u diff_tsc: %016llu\n", then, now, diff, diff_tsc); 92 then = now; 93 then_tsc = now_tsc; 94 } 95 printk(KERN_DEBUG "<-- end \n"); 96 return -ENODEV; 97 } 98 99 static void __exit cpufreq_none(void) 100 { 101 return; 102 } 103 104 module_init(cpufreq_test_tsc) 105 module_exit(cpufreq_none) 106 107 108 MODULE_AUTHOR("Dominik Brodowski"); 109 MODULE_DESCRIPTION("Verify the TSC cpufreq notifier working correctly -- needs ACPI-enabled system"); 110 MODULE_LICENSE ("GPL"); 111