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"); 114