| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1 | /* | 
 | 2 |  * (C) 2002 - 2003 Dominik Brodowski <linux@brodo.de> | 
 | 3 |  * | 
 | 4 |  *  Licensed under the terms of the GNU GPL License version 2. | 
 | 5 |  * | 
 | 6 |  *  Library for common functions for Intel SpeedStep v.1 and v.2 support | 
 | 7 |  * | 
 | 8 |  *  BIG FAT DISCLAIMER: Work in progress code. Possibly *dangerous* | 
 | 9 |  */ | 
 | 10 |  | 
 | 11 | #include <linux/kernel.h> | 
| Dave Jones | 32ee8c3 | 2006-02-28 00:43:23 -0500 | [diff] [blame] | 12 | #include <linux/module.h> | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 13 | #include <linux/moduleparam.h> | 
 | 14 | #include <linux/init.h> | 
 | 15 | #include <linux/cpufreq.h> | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 16 |  | 
 | 17 | #include <asm/msr.h> | 
| Matthias-Christian Ott | 199785e | 2009-02-20 20:52:17 -0500 | [diff] [blame] | 18 | #include <asm/tsc.h> | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 19 | #include "speedstep-lib.h" | 
 | 20 |  | 
| Dave Jones | bbfebd6 | 2009-01-17 23:55:22 -0500 | [diff] [blame] | 21 | #define PFX "speedstep-lib: " | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 22 |  | 
 | 23 | #ifdef CONFIG_X86_SPEEDSTEP_RELAXED_CAP_CHECK | 
| Dave Jones | bbfebd6 | 2009-01-17 23:55:22 -0500 | [diff] [blame] | 24 | static int relaxed_check; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 25 | #else | 
 | 26 | #define relaxed_check 0 | 
 | 27 | #endif | 
 | 28 |  | 
 | 29 | /********************************************************************* | 
 | 30 |  *                   GET PROCESSOR CORE SPEED IN KHZ                 * | 
 | 31 |  *********************************************************************/ | 
 | 32 |  | 
| Rusty Russell | 1cce76c | 2009-11-17 14:39:53 -0800 | [diff] [blame] | 33 | static unsigned int pentium3_get_frequency(enum speedstep_processor processor) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 34 | { | 
| Dave Jones | bbfebd6 | 2009-01-17 23:55:22 -0500 | [diff] [blame] | 35 | 	/* See table 14 of p3_ds.pdf and table 22 of 29834003.pdf */ | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 36 | 	struct { | 
 | 37 | 		unsigned int ratio;	/* Frequency Multiplier (x10) */ | 
| Dave Jones | 32ee8c3 | 2006-02-28 00:43:23 -0500 | [diff] [blame] | 38 | 		u8 bitmap;		/* power on configuration bits | 
 | 39 | 					[27, 25:22] (in MSR 0x2a) */ | 
| Dave Jones | bbfebd6 | 2009-01-17 23:55:22 -0500 | [diff] [blame] | 40 | 	} msr_decode_mult[] = { | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 41 | 		{ 30, 0x01 }, | 
 | 42 | 		{ 35, 0x05 }, | 
 | 43 | 		{ 40, 0x02 }, | 
 | 44 | 		{ 45, 0x06 }, | 
 | 45 | 		{ 50, 0x00 }, | 
 | 46 | 		{ 55, 0x04 }, | 
 | 47 | 		{ 60, 0x0b }, | 
 | 48 | 		{ 65, 0x0f }, | 
 | 49 | 		{ 70, 0x09 }, | 
 | 50 | 		{ 75, 0x0d }, | 
 | 51 | 		{ 80, 0x0a }, | 
 | 52 | 		{ 85, 0x26 }, | 
 | 53 | 		{ 90, 0x20 }, | 
 | 54 | 		{ 100, 0x2b }, | 
| Dave Jones | bbfebd6 | 2009-01-17 23:55:22 -0500 | [diff] [blame] | 55 | 		{ 0, 0xff }	/* error or unknown value */ | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 56 | 	}; | 
 | 57 |  | 
 | 58 | 	/* PIII(-M) FSB settings: see table b1-b of 24547206.pdf */ | 
 | 59 | 	struct { | 
| Dave Jones | 32ee8c3 | 2006-02-28 00:43:23 -0500 | [diff] [blame] | 60 | 		unsigned int value;	/* Front Side Bus speed in MHz */ | 
 | 61 | 		u8 bitmap;		/* power on configuration bits [18: 19] | 
 | 62 | 					(in MSR 0x2a) */ | 
| Dave Jones | bbfebd6 | 2009-01-17 23:55:22 -0500 | [diff] [blame] | 63 | 	} msr_decode_fsb[] = { | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 64 | 		{  66, 0x0 }, | 
 | 65 | 		{ 100, 0x2 }, | 
 | 66 | 		{ 133, 0x1 }, | 
 | 67 | 		{   0, 0xff} | 
 | 68 | 	}; | 
 | 69 |  | 
| Dave Jones | 32ee8c3 | 2006-02-28 00:43:23 -0500 | [diff] [blame] | 70 | 	u32 msr_lo, msr_tmp; | 
 | 71 | 	int i = 0, j = 0; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 72 |  | 
 | 73 | 	/* read MSR 0x2a - we only need the low 32 bits */ | 
 | 74 | 	rdmsr(MSR_IA32_EBL_CR_POWERON, msr_lo, msr_tmp); | 
| Dominik Brodowski | 2d06d8c | 2011-03-27 15:04:46 +0200 | [diff] [blame] | 75 | 	pr_debug("P3 - MSR_IA32_EBL_CR_POWERON: 0x%x 0x%x\n", msr_lo, msr_tmp); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 76 | 	msr_tmp = msr_lo; | 
 | 77 |  | 
 | 78 | 	/* decode the FSB */ | 
 | 79 | 	msr_tmp &= 0x00c0000; | 
 | 80 | 	msr_tmp >>= 18; | 
 | 81 | 	while (msr_tmp != msr_decode_fsb[i].bitmap) { | 
 | 82 | 		if (msr_decode_fsb[i].bitmap == 0xff) | 
 | 83 | 			return 0; | 
 | 84 | 		i++; | 
 | 85 | 	} | 
 | 86 |  | 
 | 87 | 	/* decode the multiplier */ | 
| Dave Jones | bbfebd6 | 2009-01-17 23:55:22 -0500 | [diff] [blame] | 88 | 	if (processor == SPEEDSTEP_CPU_PIII_C_EARLY) { | 
| Dominik Brodowski | 2d06d8c | 2011-03-27 15:04:46 +0200 | [diff] [blame] | 89 | 		pr_debug("workaround for early PIIIs\n"); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 90 | 		msr_lo &= 0x03c00000; | 
 | 91 | 	} else | 
 | 92 | 		msr_lo &= 0x0bc00000; | 
 | 93 | 	msr_lo >>= 22; | 
 | 94 | 	while (msr_lo != msr_decode_mult[j].bitmap) { | 
 | 95 | 		if (msr_decode_mult[j].bitmap == 0xff) | 
 | 96 | 			return 0; | 
 | 97 | 		j++; | 
 | 98 | 	} | 
 | 99 |  | 
| Dominik Brodowski | 2d06d8c | 2011-03-27 15:04:46 +0200 | [diff] [blame] | 100 | 	pr_debug("speed is %u\n", | 
| Dave Jones | bbfebd6 | 2009-01-17 23:55:22 -0500 | [diff] [blame] | 101 | 		(msr_decode_mult[j].ratio * msr_decode_fsb[i].value * 100)); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 102 |  | 
| Dave Jones | bbfebd6 | 2009-01-17 23:55:22 -0500 | [diff] [blame] | 103 | 	return msr_decode_mult[j].ratio * msr_decode_fsb[i].value * 100; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 104 | } | 
 | 105 |  | 
 | 106 |  | 
 | 107 | static unsigned int pentiumM_get_frequency(void) | 
 | 108 | { | 
| Dave Jones | 32ee8c3 | 2006-02-28 00:43:23 -0500 | [diff] [blame] | 109 | 	u32 msr_lo, msr_tmp; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 110 |  | 
 | 111 | 	rdmsr(MSR_IA32_EBL_CR_POWERON, msr_lo, msr_tmp); | 
| Dominik Brodowski | 2d06d8c | 2011-03-27 15:04:46 +0200 | [diff] [blame] | 112 | 	pr_debug("PM - MSR_IA32_EBL_CR_POWERON: 0x%x 0x%x\n", msr_lo, msr_tmp); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 113 |  | 
 | 114 | 	/* see table B-2 of 24547212.pdf */ | 
 | 115 | 	if (msr_lo & 0x00040000) { | 
| Dave Jones | bbfebd6 | 2009-01-17 23:55:22 -0500 | [diff] [blame] | 116 | 		printk(KERN_DEBUG PFX "PM - invalid FSB: 0x%x 0x%x\n", | 
 | 117 | 				msr_lo, msr_tmp); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 118 | 		return 0; | 
 | 119 | 	} | 
 | 120 |  | 
 | 121 | 	msr_tmp = (msr_lo >> 22) & 0x1f; | 
| Dominik Brodowski | 2d06d8c | 2011-03-27 15:04:46 +0200 | [diff] [blame] | 122 | 	pr_debug("bits 22-26 are 0x%x, speed is %u\n", | 
| Dave Jones | bbfebd6 | 2009-01-17 23:55:22 -0500 | [diff] [blame] | 123 | 			msr_tmp, (msr_tmp * 100 * 1000)); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 124 |  | 
| Dave Jones | bbfebd6 | 2009-01-17 23:55:22 -0500 | [diff] [blame] | 125 | 	return msr_tmp * 100 * 1000; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 126 | } | 
 | 127 |  | 
| Dominik Brodowski | 4e74663 | 2006-10-31 12:44:08 -0500 | [diff] [blame] | 128 | static unsigned int pentium_core_get_frequency(void) | 
 | 129 | { | 
 | 130 | 	u32 fsb = 0; | 
 | 131 | 	u32 msr_lo, msr_tmp; | 
| Dave Jones | bbfebd6 | 2009-01-17 23:55:22 -0500 | [diff] [blame] | 132 | 	int ret; | 
| Dominik Brodowski | 4e74663 | 2006-10-31 12:44:08 -0500 | [diff] [blame] | 133 |  | 
 | 134 | 	rdmsr(MSR_FSB_FREQ, msr_lo, msr_tmp); | 
| Dominik Brodowski | e11952b | 2006-12-04 20:39:16 -0500 | [diff] [blame] | 135 | 	/* see table B-2 of 25366920.pdf */ | 
| Dominik Brodowski | 4e74663 | 2006-10-31 12:44:08 -0500 | [diff] [blame] | 136 | 	switch (msr_lo & 0x07) { | 
 | 137 | 	case 5: | 
| Dominik Brodowski | e11952b | 2006-12-04 20:39:16 -0500 | [diff] [blame] | 138 | 		fsb = 100000; | 
| Dominik Brodowski | 4e74663 | 2006-10-31 12:44:08 -0500 | [diff] [blame] | 139 | 		break; | 
 | 140 | 	case 1: | 
| Dominik Brodowski | e11952b | 2006-12-04 20:39:16 -0500 | [diff] [blame] | 141 | 		fsb = 133333; | 
| Dominik Brodowski | 4e74663 | 2006-10-31 12:44:08 -0500 | [diff] [blame] | 142 | 		break; | 
 | 143 | 	case 3: | 
| Dominik Brodowski | e11952b | 2006-12-04 20:39:16 -0500 | [diff] [blame] | 144 | 		fsb = 166667; | 
| Dominik Brodowski | 4e74663 | 2006-10-31 12:44:08 -0500 | [diff] [blame] | 145 | 		break; | 
| Herton Ronaldo Krzesinski | c60e19e | 2008-11-15 17:02:46 -0200 | [diff] [blame] | 146 | 	case 2: | 
 | 147 | 		fsb = 200000; | 
 | 148 | 		break; | 
 | 149 | 	case 0: | 
 | 150 | 		fsb = 266667; | 
 | 151 | 		break; | 
 | 152 | 	case 4: | 
 | 153 | 		fsb = 333333; | 
 | 154 | 		break; | 
| Dominik Brodowski | 4e74663 | 2006-10-31 12:44:08 -0500 | [diff] [blame] | 155 | 	default: | 
 | 156 | 		printk(KERN_ERR "PCORE - MSR_FSB_FREQ undefined value"); | 
 | 157 | 	} | 
 | 158 |  | 
 | 159 | 	rdmsr(MSR_IA32_EBL_CR_POWERON, msr_lo, msr_tmp); | 
| Dominik Brodowski | 2d06d8c | 2011-03-27 15:04:46 +0200 | [diff] [blame] | 160 | 	pr_debug("PCORE - MSR_IA32_EBL_CR_POWERON: 0x%x 0x%x\n", | 
| Dave Jones | bbfebd6 | 2009-01-17 23:55:22 -0500 | [diff] [blame] | 161 | 			msr_lo, msr_tmp); | 
| Dominik Brodowski | 4e74663 | 2006-10-31 12:44:08 -0500 | [diff] [blame] | 162 |  | 
 | 163 | 	msr_tmp = (msr_lo >> 22) & 0x1f; | 
| Dominik Brodowski | 2d06d8c | 2011-03-27 15:04:46 +0200 | [diff] [blame] | 164 | 	pr_debug("bits 22-26 are 0x%x, speed is %u\n", | 
| Dave Jones | bbfebd6 | 2009-01-17 23:55:22 -0500 | [diff] [blame] | 165 | 			msr_tmp, (msr_tmp * fsb)); | 
| Dominik Brodowski | 4e74663 | 2006-10-31 12:44:08 -0500 | [diff] [blame] | 166 |  | 
| Dave Jones | bbfebd6 | 2009-01-17 23:55:22 -0500 | [diff] [blame] | 167 | 	ret = (msr_tmp * fsb); | 
 | 168 | 	return ret; | 
| Dominik Brodowski | 4e74663 | 2006-10-31 12:44:08 -0500 | [diff] [blame] | 169 | } | 
| Dominik Brodowski | e11952b | 2006-12-04 20:39:16 -0500 | [diff] [blame] | 170 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 171 |  | 
 | 172 | static unsigned int pentium4_get_frequency(void) | 
 | 173 | { | 
 | 174 | 	struct cpuinfo_x86 *c = &boot_cpu_data; | 
 | 175 | 	u32 msr_lo, msr_hi, mult; | 
 | 176 | 	unsigned int fsb = 0; | 
| Dave Jones | bbfebd6 | 2009-01-17 23:55:22 -0500 | [diff] [blame] | 177 | 	unsigned int ret; | 
| Matthias-Christian Ott | 199785e | 2009-02-20 20:52:17 -0500 | [diff] [blame] | 178 | 	u8 fsb_code; | 
 | 179 |  | 
 | 180 | 	/* Pentium 4 Model 0 and 1 do not have the Core Clock Frequency | 
 | 181 | 	 * to System Bus Frequency Ratio Field in the Processor Frequency | 
 | 182 | 	 * Configuration Register of the MSR. Therefore the current | 
 | 183 | 	 * frequency cannot be calculated and has to be measured. | 
 | 184 | 	 */ | 
 | 185 | 	if (c->x86_model < 2) | 
 | 186 | 		return cpu_khz; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 187 |  | 
 | 188 | 	rdmsr(0x2c, msr_lo, msr_hi); | 
 | 189 |  | 
| Dominik Brodowski | 2d06d8c | 2011-03-27 15:04:46 +0200 | [diff] [blame] | 190 | 	pr_debug("P4 - MSR_EBC_FREQUENCY_ID: 0x%x 0x%x\n", msr_lo, msr_hi); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 191 |  | 
| Dave Jones | 32ee8c3 | 2006-02-28 00:43:23 -0500 | [diff] [blame] | 192 | 	/* decode the FSB: see IA-32 Intel (C) Architecture Software | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 193 | 	 * Developer's Manual, Volume 3: System Prgramming Guide, | 
 | 194 | 	 * revision #12 in Table B-1: MSRs in the Pentium 4 and | 
 | 195 | 	 * Intel Xeon Processors, on page B-4 and B-5. | 
 | 196 | 	 */ | 
| Matthias-Christian Ott | 199785e | 2009-02-20 20:52:17 -0500 | [diff] [blame] | 197 | 	fsb_code = (msr_lo >> 16) & 0x7; | 
 | 198 | 	switch (fsb_code) { | 
 | 199 | 	case 0: | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 200 | 		fsb = 100 * 1000; | 
| Matthias-Christian Ott | 199785e | 2009-02-20 20:52:17 -0500 | [diff] [blame] | 201 | 		break; | 
 | 202 | 	case 1: | 
 | 203 | 		fsb = 13333 * 10; | 
 | 204 | 		break; | 
 | 205 | 	case 2: | 
 | 206 | 		fsb = 200 * 1000; | 
 | 207 | 		break; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 208 | 	} | 
 | 209 |  | 
 | 210 | 	if (!fsb) | 
| Dave Jones | bbfebd6 | 2009-01-17 23:55:22 -0500 | [diff] [blame] | 211 | 		printk(KERN_DEBUG PFX "couldn't detect FSB speed. " | 
 | 212 | 				"Please send an e-mail to <linux@brodo.de>\n"); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 213 |  | 
 | 214 | 	/* Multiplier. */ | 
| Zhao Yakui | ed9cbcd | 2007-11-20 14:20:21 -0500 | [diff] [blame] | 215 | 	mult = msr_lo >> 24; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 216 |  | 
| Dominik Brodowski | 2d06d8c | 2011-03-27 15:04:46 +0200 | [diff] [blame] | 217 | 	pr_debug("P4 - FSB %u kHz; Multiplier %u; Speed %u kHz\n", | 
| Dave Jones | bbfebd6 | 2009-01-17 23:55:22 -0500 | [diff] [blame] | 218 | 			fsb, mult, (fsb * mult)); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 219 |  | 
| Dave Jones | bbfebd6 | 2009-01-17 23:55:22 -0500 | [diff] [blame] | 220 | 	ret = (fsb * mult); | 
 | 221 | 	return ret; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 222 | } | 
 | 223 |  | 
| Dave Jones | 32ee8c3 | 2006-02-28 00:43:23 -0500 | [diff] [blame] | 224 |  | 
| Rusty Russell | 394122a | 2009-06-11 22:59:58 +0930 | [diff] [blame] | 225 | /* Warning: may get called from smp_call_function_single. */ | 
| Rusty Russell | 1cce76c | 2009-11-17 14:39:53 -0800 | [diff] [blame] | 226 | unsigned int speedstep_get_frequency(enum speedstep_processor processor) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 227 | { | 
 | 228 | 	switch (processor) { | 
| Dave Jones | bbfebd6 | 2009-01-17 23:55:22 -0500 | [diff] [blame] | 229 | 	case SPEEDSTEP_CPU_PCORE: | 
| Dominik Brodowski | 4e74663 | 2006-10-31 12:44:08 -0500 | [diff] [blame] | 230 | 		return pentium_core_get_frequency(); | 
| Dave Jones | bbfebd6 | 2009-01-17 23:55:22 -0500 | [diff] [blame] | 231 | 	case SPEEDSTEP_CPU_PM: | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 232 | 		return pentiumM_get_frequency(); | 
| Dave Jones | bbfebd6 | 2009-01-17 23:55:22 -0500 | [diff] [blame] | 233 | 	case SPEEDSTEP_CPU_P4D: | 
 | 234 | 	case SPEEDSTEP_CPU_P4M: | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 235 | 		return pentium4_get_frequency(); | 
| Dave Jones | bbfebd6 | 2009-01-17 23:55:22 -0500 | [diff] [blame] | 236 | 	case SPEEDSTEP_CPU_PIII_T: | 
 | 237 | 	case SPEEDSTEP_CPU_PIII_C: | 
 | 238 | 	case SPEEDSTEP_CPU_PIII_C_EARLY: | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 239 | 		return pentium3_get_frequency(processor); | 
 | 240 | 	default: | 
 | 241 | 		return 0; | 
 | 242 | 	}; | 
 | 243 | 	return 0; | 
 | 244 | } | 
| Dave Jones | bbfebd6 | 2009-01-17 23:55:22 -0500 | [diff] [blame] | 245 | EXPORT_SYMBOL_GPL(speedstep_get_frequency); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 246 |  | 
 | 247 |  | 
 | 248 | /********************************************************************* | 
 | 249 |  *                 DETECT SPEEDSTEP-CAPABLE PROCESSOR                * | 
 | 250 |  *********************************************************************/ | 
 | 251 |  | 
| Andi Kleen | fa8031a | 2012-01-26 00:09:12 +0100 | [diff] [blame] | 252 | /* Keep in sync with the x86_cpu_id tables in the different modules */ | 
| Dave Jones | bbfebd6 | 2009-01-17 23:55:22 -0500 | [diff] [blame] | 253 | unsigned int speedstep_detect_processor(void) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 254 | { | 
| Mike Travis | 92cb761 | 2007-10-19 20:35:04 +0200 | [diff] [blame] | 255 | 	struct cpuinfo_x86 *c = &cpu_data(0); | 
| Dave Jones | 32ee8c3 | 2006-02-28 00:43:23 -0500 | [diff] [blame] | 256 | 	u32 ebx, msr_lo, msr_hi; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 257 |  | 
| Dominik Brodowski | 2d06d8c | 2011-03-27 15:04:46 +0200 | [diff] [blame] | 258 | 	pr_debug("x86: %x, model: %x\n", c->x86, c->x86_model); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 259 |  | 
| Dave Jones | 32ee8c3 | 2006-02-28 00:43:23 -0500 | [diff] [blame] | 260 | 	if ((c->x86_vendor != X86_VENDOR_INTEL) || | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 261 | 	    ((c->x86 != 6) && (c->x86 != 0xF))) | 
 | 262 | 		return 0; | 
 | 263 |  | 
 | 264 | 	if (c->x86 == 0xF) { | 
 | 265 | 		/* Intel Mobile Pentium 4-M | 
 | 266 | 		 * or Intel Mobile Pentium 4 with 533 MHz FSB */ | 
 | 267 | 		if (c->x86_model != 2) | 
 | 268 | 			return 0; | 
 | 269 |  | 
 | 270 | 		ebx = cpuid_ebx(0x00000001); | 
 | 271 | 		ebx &= 0x000000FF; | 
 | 272 |  | 
| Dominik Brodowski | 2d06d8c | 2011-03-27 15:04:46 +0200 | [diff] [blame] | 273 | 		pr_debug("ebx value is %x, x86_mask is %x\n", ebx, c->x86_mask); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 274 |  | 
 | 275 | 		switch (c->x86_mask) { | 
| Dave Jones | 32ee8c3 | 2006-02-28 00:43:23 -0500 | [diff] [blame] | 276 | 		case 4: | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 277 | 			/* | 
| Dave Jones | 32ee8c3 | 2006-02-28 00:43:23 -0500 | [diff] [blame] | 278 | 			 * B-stepping [M-P4-M] | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 279 | 			 * sample has ebx = 0x0f, production has 0x0e. | 
 | 280 | 			 */ | 
 | 281 | 			if ((ebx == 0x0e) || (ebx == 0x0f)) | 
| Dave Jones | bbfebd6 | 2009-01-17 23:55:22 -0500 | [diff] [blame] | 282 | 				return SPEEDSTEP_CPU_P4M; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 283 | 			break; | 
| Dave Jones | 32ee8c3 | 2006-02-28 00:43:23 -0500 | [diff] [blame] | 284 | 		case 7: | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 285 | 			/* | 
 | 286 | 			 * C-stepping [M-P4-M] | 
 | 287 | 			 * needs to have ebx=0x0e, else it's a celeron: | 
 | 288 | 			 * cf. 25130917.pdf / page 7, footnote 5 even | 
 | 289 | 			 * though 25072120.pdf / page 7 doesn't say | 
 | 290 | 			 * samples are only of B-stepping... | 
 | 291 | 			 */ | 
 | 292 | 			if (ebx == 0x0e) | 
| Dave Jones | bbfebd6 | 2009-01-17 23:55:22 -0500 | [diff] [blame] | 293 | 				return SPEEDSTEP_CPU_P4M; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 294 | 			break; | 
 | 295 | 		case 9: | 
 | 296 | 			/* | 
 | 297 | 			 * D-stepping [M-P4-M or M-P4/533] | 
 | 298 | 			 * | 
 | 299 | 			 * this is totally strange: CPUID 0x0F29 is | 
 | 300 | 			 * used by M-P4-M, M-P4/533 and(!) Celeron CPUs. | 
 | 301 | 			 * The latter need to be sorted out as they don't | 
 | 302 | 			 * support speedstep. | 
 | 303 | 			 * Celerons with CPUID 0x0F29 may have either | 
 | 304 | 			 * ebx=0x8 or 0xf -- 25130917.pdf doesn't say anything | 
 | 305 | 			 * specific. | 
 | 306 | 			 * M-P4-Ms may have either ebx=0xe or 0xf [see above] | 
 | 307 | 			 * M-P4/533 have either ebx=0xe or 0xf. [25317607.pdf] | 
 | 308 | 			 * also, M-P4M HTs have ebx=0x8, too | 
| Dave Jones | bbfebd6 | 2009-01-17 23:55:22 -0500 | [diff] [blame] | 309 | 			 * For now, they are distinguished by the model_id | 
 | 310 | 			 * string | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 311 | 			 */ | 
| Dave Jones | bbfebd6 | 2009-01-17 23:55:22 -0500 | [diff] [blame] | 312 | 			if ((ebx == 0x0e) || | 
 | 313 | 				(strstr(c->x86_model_id, | 
 | 314 | 				    "Mobile Intel(R) Pentium(R) 4") != NULL)) | 
 | 315 | 				return SPEEDSTEP_CPU_P4M; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 316 | 			break; | 
 | 317 | 		default: | 
 | 318 | 			break; | 
 | 319 | 		} | 
 | 320 | 		return 0; | 
 | 321 | 	} | 
 | 322 |  | 
 | 323 | 	switch (c->x86_model) { | 
 | 324 | 	case 0x0B: /* Intel PIII [Tualatin] */ | 
| Dave Jones | bbfebd6 | 2009-01-17 23:55:22 -0500 | [diff] [blame] | 325 | 		/* cpuid_ebx(1) is 0x04 for desktop PIII, | 
 | 326 | 		 * 0x06 for mobile PIII-M */ | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 327 | 		ebx = cpuid_ebx(0x00000001); | 
| Dominik Brodowski | 2d06d8c | 2011-03-27 15:04:46 +0200 | [diff] [blame] | 328 | 		pr_debug("ebx is %x\n", ebx); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 329 |  | 
 | 330 | 		ebx &= 0x000000FF; | 
 | 331 |  | 
 | 332 | 		if (ebx != 0x06) | 
 | 333 | 			return 0; | 
 | 334 |  | 
 | 335 | 		/* So far all PIII-M processors support SpeedStep. See | 
| Dave Jones | 32ee8c3 | 2006-02-28 00:43:23 -0500 | [diff] [blame] | 336 | 		 * Intel's 24540640.pdf of June 2003 | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 337 | 		 */ | 
| Dave Jones | bbfebd6 | 2009-01-17 23:55:22 -0500 | [diff] [blame] | 338 | 		return SPEEDSTEP_CPU_PIII_T; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 339 |  | 
 | 340 | 	case 0x08: /* Intel PIII [Coppermine] */ | 
 | 341 |  | 
 | 342 | 		/* all mobile PIII Coppermines have FSB 100 MHz | 
 | 343 | 		 * ==> sort out a few desktop PIIIs. */ | 
 | 344 | 		rdmsr(MSR_IA32_EBL_CR_POWERON, msr_lo, msr_hi); | 
| Dominik Brodowski | 2d06d8c | 2011-03-27 15:04:46 +0200 | [diff] [blame] | 345 | 		pr_debug("Coppermine: MSR_IA32_EBL_CR_POWERON is 0x%x, 0x%x\n", | 
| Dave Jones | bbfebd6 | 2009-01-17 23:55:22 -0500 | [diff] [blame] | 346 | 				msr_lo, msr_hi); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 347 | 		msr_lo &= 0x00c0000; | 
 | 348 | 		if (msr_lo != 0x0080000) | 
 | 349 | 			return 0; | 
 | 350 |  | 
 | 351 | 		/* | 
 | 352 | 		 * If the processor is a mobile version, | 
 | 353 | 		 * platform ID has bit 50 set | 
 | 354 | 		 * it has SpeedStep technology if either | 
 | 355 | 		 * bit 56 or 57 is set | 
 | 356 | 		 */ | 
 | 357 | 		rdmsr(MSR_IA32_PLATFORM_ID, msr_lo, msr_hi); | 
| Dominik Brodowski | 2d06d8c | 2011-03-27 15:04:46 +0200 | [diff] [blame] | 358 | 		pr_debug("Coppermine: MSR_IA32_PLATFORM ID is 0x%x, 0x%x\n", | 
| Dave Jones | bbfebd6 | 2009-01-17 23:55:22 -0500 | [diff] [blame] | 359 | 				msr_lo, msr_hi); | 
 | 360 | 		if ((msr_hi & (1<<18)) && | 
 | 361 | 		    (relaxed_check ? 1 : (msr_hi & (3<<24)))) { | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 362 | 			if (c->x86_mask == 0x01) { | 
| Dominik Brodowski | 2d06d8c | 2011-03-27 15:04:46 +0200 | [diff] [blame] | 363 | 				pr_debug("early PIII version\n"); | 
| Dave Jones | bbfebd6 | 2009-01-17 23:55:22 -0500 | [diff] [blame] | 364 | 				return SPEEDSTEP_CPU_PIII_C_EARLY; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 365 | 			} else | 
| Dave Jones | bbfebd6 | 2009-01-17 23:55:22 -0500 | [diff] [blame] | 366 | 				return SPEEDSTEP_CPU_PIII_C; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 367 | 		} | 
 | 368 |  | 
 | 369 | 	default: | 
 | 370 | 		return 0; | 
 | 371 | 	} | 
 | 372 | } | 
 | 373 | EXPORT_SYMBOL_GPL(speedstep_detect_processor); | 
 | 374 |  | 
 | 375 |  | 
 | 376 | /********************************************************************* | 
 | 377 |  *                     DETECT SPEEDSTEP SPEEDS                       * | 
 | 378 |  *********************************************************************/ | 
 | 379 |  | 
| Rusty Russell | 1cce76c | 2009-11-17 14:39:53 -0800 | [diff] [blame] | 380 | unsigned int speedstep_get_freqs(enum speedstep_processor processor, | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 381 | 				  unsigned int *low_speed, | 
 | 382 | 				  unsigned int *high_speed, | 
| Mattia Dongili | 1a10760 | 2005-12-02 21:59:41 +0100 | [diff] [blame] | 383 | 				  unsigned int *transition_latency, | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 384 | 				  void (*set_state) (unsigned int state)) | 
 | 385 | { | 
 | 386 | 	unsigned int prev_speed; | 
 | 387 | 	unsigned int ret = 0; | 
 | 388 | 	unsigned long flags; | 
| Mattia Dongili | 1a10760 | 2005-12-02 21:59:41 +0100 | [diff] [blame] | 389 | 	struct timeval tv1, tv2; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 390 |  | 
 | 391 | 	if ((!processor) || (!low_speed) || (!high_speed) || (!set_state)) | 
 | 392 | 		return -EINVAL; | 
 | 393 |  | 
| Dominik Brodowski | 2d06d8c | 2011-03-27 15:04:46 +0200 | [diff] [blame] | 394 | 	pr_debug("trying to determine both speeds\n"); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 395 |  | 
 | 396 | 	/* get current speed */ | 
| Dave Jones | bbfebd6 | 2009-01-17 23:55:22 -0500 | [diff] [blame] | 397 | 	prev_speed = speedstep_get_frequency(processor); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 398 | 	if (!prev_speed) | 
 | 399 | 		return -EIO; | 
 | 400 |  | 
| Dominik Brodowski | 2d06d8c | 2011-03-27 15:04:46 +0200 | [diff] [blame] | 401 | 	pr_debug("previous speed is %u\n", prev_speed); | 
| Mattia Dongili | 1a10760 | 2005-12-02 21:59:41 +0100 | [diff] [blame] | 402 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 403 | 	local_irq_save(flags); | 
 | 404 |  | 
 | 405 | 	/* switch to low state */ | 
 | 406 | 	set_state(SPEEDSTEP_LOW); | 
| Dave Jones | bbfebd6 | 2009-01-17 23:55:22 -0500 | [diff] [blame] | 407 | 	*low_speed = speedstep_get_frequency(processor); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 408 | 	if (!*low_speed) { | 
 | 409 | 		ret = -EIO; | 
 | 410 | 		goto out; | 
 | 411 | 	} | 
 | 412 |  | 
| Dominik Brodowski | 2d06d8c | 2011-03-27 15:04:46 +0200 | [diff] [blame] | 413 | 	pr_debug("low speed is %u\n", *low_speed); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 414 |  | 
| Mattia Dongili | 1a10760 | 2005-12-02 21:59:41 +0100 | [diff] [blame] | 415 | 	/* start latency measurement */ | 
 | 416 | 	if (transition_latency) | 
 | 417 | 		do_gettimeofday(&tv1); | 
 | 418 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 419 | 	/* switch to high state */ | 
 | 420 | 	set_state(SPEEDSTEP_HIGH); | 
| Mattia Dongili | 1a10760 | 2005-12-02 21:59:41 +0100 | [diff] [blame] | 421 |  | 
 | 422 | 	/* end latency measurement */ | 
 | 423 | 	if (transition_latency) | 
 | 424 | 		do_gettimeofday(&tv2); | 
 | 425 |  | 
| Dave Jones | bbfebd6 | 2009-01-17 23:55:22 -0500 | [diff] [blame] | 426 | 	*high_speed = speedstep_get_frequency(processor); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 427 | 	if (!*high_speed) { | 
 | 428 | 		ret = -EIO; | 
 | 429 | 		goto out; | 
 | 430 | 	} | 
 | 431 |  | 
| Dominik Brodowski | 2d06d8c | 2011-03-27 15:04:46 +0200 | [diff] [blame] | 432 | 	pr_debug("high speed is %u\n", *high_speed); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 433 |  | 
 | 434 | 	if (*low_speed == *high_speed) { | 
 | 435 | 		ret = -ENODEV; | 
 | 436 | 		goto out; | 
 | 437 | 	} | 
 | 438 |  | 
 | 439 | 	/* switch to previous state, if necessary */ | 
 | 440 | 	if (*high_speed != prev_speed) | 
 | 441 | 		set_state(SPEEDSTEP_LOW); | 
 | 442 |  | 
| Mattia Dongili | 1a10760 | 2005-12-02 21:59:41 +0100 | [diff] [blame] | 443 | 	if (transition_latency) { | 
 | 444 | 		*transition_latency = (tv2.tv_sec - tv1.tv_sec) * USEC_PER_SEC + | 
 | 445 | 			tv2.tv_usec - tv1.tv_usec; | 
| Dominik Brodowski | 2d06d8c | 2011-03-27 15:04:46 +0200 | [diff] [blame] | 446 | 		pr_debug("transition latency is %u uSec\n", *transition_latency); | 
| Mattia Dongili | 1a10760 | 2005-12-02 21:59:41 +0100 | [diff] [blame] | 447 |  | 
 | 448 | 		/* convert uSec to nSec and add 20% for safety reasons */ | 
 | 449 | 		*transition_latency *= 1200; | 
 | 450 |  | 
 | 451 | 		/* check if the latency measurement is too high or too low | 
 | 452 | 		 * and set it to a safe value (500uSec) in that case | 
 | 453 | 		 */ | 
| Dave Jones | bbfebd6 | 2009-01-17 23:55:22 -0500 | [diff] [blame] | 454 | 		if (*transition_latency > 10000000 || | 
 | 455 | 		    *transition_latency < 50000) { | 
 | 456 | 			printk(KERN_WARNING PFX "frequency transition " | 
 | 457 | 					"measured seems out of range (%u " | 
 | 458 | 					"nSec), falling back to a safe one of" | 
 | 459 | 					"%u nSec.\n", | 
| Mattia Dongili | 1a10760 | 2005-12-02 21:59:41 +0100 | [diff] [blame] | 460 | 					*transition_latency, 500000); | 
 | 461 | 			*transition_latency = 500000; | 
 | 462 | 		} | 
 | 463 | 	} | 
 | 464 |  | 
| Dave Jones | 32ee8c3 | 2006-02-28 00:43:23 -0500 | [diff] [blame] | 465 | out: | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 466 | 	local_irq_restore(flags); | 
| Dave Jones | bbfebd6 | 2009-01-17 23:55:22 -0500 | [diff] [blame] | 467 | 	return ret; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 468 | } | 
 | 469 | EXPORT_SYMBOL_GPL(speedstep_get_freqs); | 
 | 470 |  | 
 | 471 | #ifdef CONFIG_X86_SPEEDSTEP_RELAXED_CAP_CHECK | 
 | 472 | module_param(relaxed_check, int, 0444); | 
| Dave Jones | bbfebd6 | 2009-01-17 23:55:22 -0500 | [diff] [blame] | 473 | MODULE_PARM_DESC(relaxed_check, | 
 | 474 | 		"Don't do all checks for speedstep capability."); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 475 | #endif | 
 | 476 |  | 
| Dave Jones | bbfebd6 | 2009-01-17 23:55:22 -0500 | [diff] [blame] | 477 | MODULE_AUTHOR("Dominik Brodowski <linux@brodo.de>"); | 
 | 478 | MODULE_DESCRIPTION("Library for Intel SpeedStep 1 or 2 cpufreq drivers."); | 
 | 479 | MODULE_LICENSE("GPL"); |