Path: blob/master/tools/power/cpupower/debug/kernel/cpufreq-test_tsc.c
26292 views
// SPDX-License-Identifier: GPL-2.0-only1/*2* test module to check whether the TSC-based delay routine continues3* to work properly after cpufreq transitions. Needs ACPI to work4* properly.5*6* Based partly on the Power Management Timer (PMTMR) code to be found7* in arch/i386/kernel/timers/timer_pm.c on recent 2.6. kernels, especially8* code written by John Stultz. The read_pmtmr function was copied verbatim9* from that file.10*11* (C) 2004 Dominik Brodowski12*13* To use:14* 1.) pass clock=tsc to the kernel on your bootloader15* 2.) modprobe this module (it'll fail)16* 3.) change CPU frequency17* 4.) modprobe this module again18* 5.) if the third value, "diff_pmtmr", changes between 2. and 4., the19* TSC-based delay routine on the Linux kernel does not correctly20* handle the cpufreq transition. Please report this to21* [email protected]22*/2324#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>3031static int pm_tmr_ioport = 0;3233/*helper function to safely read acpi pm timesource*/34static u32 read_pmtmr(void)35{36u32 v1=0,v2=0,v3=0;37/* It has been reported that because of various broken38* chipsets (ICH4, PIIX4 and PIIX4E) where the ACPI PM time39* source is not latched, so you must read it multiple40* times to insure a safe value is read.41*/42do {43v1 = inl(pm_tmr_ioport);44v2 = inl(pm_tmr_ioport);45v3 = inl(pm_tmr_ioport);46} while ((v1 > v2 && v1 < v3) || (v2 > v3 && v2 < v1)47|| (v3 > v1 && v3 < v2));4849/* mask the output to 24 bits */50return (v2 & 0xFFFFFF);51}5253static int __init cpufreq_test_tsc(void)54{55u32 now, then, diff;56u64 now_tsc, then_tsc, diff_tsc;57int i;5859/* the following code snipped is copied from arch/x86/kernel/acpi/boot.c60of Linux v2.6.25. */6162/* detect the location of the ACPI PM Timer */63if (acpi_gbl_FADT.header.revision >= FADT2_REVISION_ID) {64/* FADT rev. 2 */65if (acpi_gbl_FADT.xpm_timer_block.space_id !=66ACPI_ADR_SPACE_SYSTEM_IO)67return 0;6869pm_tmr_ioport = acpi_gbl_FADT.xpm_timer_block.address;70/*71* "X" fields are optional extensions to the original V1.072* fields, so we must selectively expand V1.0 fields if the73* corresponding X field is zero.74*/75if (!pm_tmr_ioport)76pm_tmr_ioport = acpi_gbl_FADT.pm_timer_block;77} else {78/* FADT rev. 1 */79pm_tmr_ioport = acpi_gbl_FADT.pm_timer_block;80}8182printk(KERN_DEBUG "start--> \n");83then = read_pmtmr();84then_tsc = rdtsc();85for (i=0;i<20;i++) {86mdelay(100);87now = read_pmtmr();88now_tsc = rdtsc();89diff = (now - then) & 0xFFFFFF;90diff_tsc = now_tsc - then_tsc;91printk(KERN_DEBUG "t1: %08u t2: %08u diff_pmtmr: %08u diff_tsc: %016llu\n", then, now, diff, diff_tsc);92then = now;93then_tsc = now_tsc;94}95printk(KERN_DEBUG "<-- end \n");96return -ENODEV;97}9899static void __exit cpufreq_none(void)100{101return;102}103104module_init(cpufreq_test_tsc)105module_exit(cpufreq_none)106107108MODULE_AUTHOR("Dominik Brodowski");109MODULE_DESCRIPTION("Verify the TSC cpufreq notifier working correctly -- needs ACPI-enabled system");110MODULE_LICENSE ("GPL");111112113