p4-clockmod.c 7.8 KB
Newer Older
L
Linus Torvalds 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
/*
 *	Pentium 4/Xeon CPU on demand clock modulation/speed scaling
 *	(C) 2002 - 2003 Dominik Brodowski <linux@brodo.de>
 *	(C) 2002 Zwane Mwaikambo <zwane@commfireservices.com>
 *	(C) 2002 Arjan van de Ven <arjanv@redhat.com>
 *	(C) 2002 Tora T. Engstad
 *	All Rights Reserved
 *
 *	This program is free software; you can redistribute it and/or
 *      modify it under the terms of the GNU General Public License
 *      as published by the Free Software Foundation; either version
 *      2 of the License, or (at your option) any later version.
 *
 *      The author(s) of this software shall not be held liable for damages
 *      of any nature resulting due to the use of this software. This
 *      software is provided AS-IS with no warranties.
17
 *
L
Linus Torvalds 已提交
18 19 20 21 22 23
 *	Date		Errata			Description
 *	20020525	N44, O17	12.5% or 25% DC causes lockup
 *
 */

#include <linux/kernel.h>
24
#include <linux/module.h>
L
Linus Torvalds 已提交
25 26 27 28
#include <linux/init.h>
#include <linux/smp.h>
#include <linux/cpufreq.h>
#include <linux/cpumask.h>
29
#include <linux/timex.h>
L
Linus Torvalds 已提交
30

31
#include <asm/processor.h>
L
Linus Torvalds 已提交
32
#include <asm/msr.h>
33
#include <asm/timer.h>
34
#include <asm/cpu_device_id.h>
L
Linus Torvalds 已提交
35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60

#include "speedstep-lib.h"

#define PFX	"p4-clockmod: "

/*
 * Duty Cycle (3bits), note DC_DISABLE is not specified in
 * intel docs i just use it to mean disable
 */
enum {
	DC_RESV, DC_DFLT, DC_25PT, DC_38PT, DC_50PT,
	DC_64PT, DC_75PT, DC_88PT, DC_DISABLE
};

#define DC_ENTRIES	8


static int has_N44_O17_errata[NR_CPUS];
static unsigned int stock_freq;
static struct cpufreq_driver p4clockmod_driver;
static unsigned int cpufreq_p4_get(unsigned int cpu);

static int cpufreq_p4_setdc(unsigned int cpu, unsigned int newstate)
{
	u32 l, h;

61
	if ((newstate > DC_DISABLE) || (newstate == DC_RESV))
L
Linus Torvalds 已提交
62 63
		return -EINVAL;

64
	rdmsr_on_cpu(cpu, MSR_IA32_THERM_STATUS, &l, &h);
L
Linus Torvalds 已提交
65 66

	if (l & 0x01)
67
		pr_debug("CPU#%d currently thermal throttled\n", cpu);
L
Linus Torvalds 已提交
68

69 70
	if (has_N44_O17_errata[cpu] &&
	    (newstate == DC_25PT || newstate == DC_DFLT))
L
Linus Torvalds 已提交
71 72
		newstate = DC_38PT;

73
	rdmsr_on_cpu(cpu, MSR_IA32_THERM_CONTROL, &l, &h);
L
Linus Torvalds 已提交
74
	if (newstate == DC_DISABLE) {
75
		pr_debug("CPU#%d disabling modulation\n", cpu);
76
		wrmsr_on_cpu(cpu, MSR_IA32_THERM_CONTROL, l & ~(1<<4), h);
L
Linus Torvalds 已提交
77
	} else {
78
		pr_debug("CPU#%d setting duty cycle to %d%%\n",
L
Linus Torvalds 已提交
79
			cpu, ((125 * newstate) / 10));
80
		/* bits 63 - 5	: reserved
L
Linus Torvalds 已提交
81 82 83 84 85 86
		 * bit  4	: enable/disable
		 * bits 3-1	: duty cycle
		 * bit  0	: reserved
		 */
		l = (l & ~14);
		l = l | (1<<4) | ((newstate & 0x7)<<1);
87
		wrmsr_on_cpu(cpu, MSR_IA32_THERM_CONTROL, l, h);
L
Linus Torvalds 已提交
88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115
	}

	return 0;
}


static struct cpufreq_frequency_table p4clockmod_table[] = {
	{DC_RESV, CPUFREQ_ENTRY_INVALID},
	{DC_DFLT, 0},
	{DC_25PT, 0},
	{DC_38PT, 0},
	{DC_50PT, 0},
	{DC_64PT, 0},
	{DC_75PT, 0},
	{DC_88PT, 0},
	{DC_DISABLE, 0},
	{DC_RESV, CPUFREQ_TABLE_END},
};


static int cpufreq_p4_target(struct cpufreq_policy *policy,
			     unsigned int target_freq,
			     unsigned int relation)
{
	unsigned int    newstate = DC_RESV;
	struct cpufreq_freqs freqs;
	int i;

116 117
	if (cpufreq_frequency_table_target(policy, &p4clockmod_table[0],
				target_freq, relation, &newstate))
L
Linus Torvalds 已提交
118 119 120
		return -EINVAL;

	freqs.old = cpufreq_p4_get(policy->cpu);
121
	freqs.new = stock_freq * p4clockmod_table[newstate].driver_data / 8;
L
Linus Torvalds 已提交
122 123 124 125 126

	if (freqs.new == freqs.old)
		return 0;

	/* notifiers */
127
	cpufreq_notify_transition(policy, &freqs, CPUFREQ_PRECHANGE);
L
Linus Torvalds 已提交
128

129 130
	/* run on each logical CPU,
	 * see section 13.15.3 of IA32 Intel Architecture Software
131
	 * Developer's Manual, Volume 3
L
Linus Torvalds 已提交
132
	 */
133
	for_each_cpu(i, policy->cpus)
134
		cpufreq_p4_setdc(i, p4clockmod_table[newstate].driver_data);
L
Linus Torvalds 已提交
135 136

	/* notifiers */
137
	cpufreq_notify_transition(policy, &freqs, CPUFREQ_POSTCHANGE);
L
Linus Torvalds 已提交
138 139 140 141 142 143 144

	return 0;
}


static unsigned int cpufreq_p4_get_frequency(struct cpuinfo_x86 *c)
{
145 146
	if (c->x86 == 0x06) {
		if (cpu_has(c, X86_FEATURE_EST))
147 148 149
			printk_once(KERN_WARNING PFX "Warning: EST-capable "
			       "CPU detected. The acpi-cpufreq module offers "
			       "voltage scaling in addition to frequency "
150 151
			       "scaling. You should use that instead of "
			       "p4-clockmod, if possible.\n");
152 153 154
		switch (c->x86_model) {
		case 0x0E: /* Core */
		case 0x0F: /* Core Duo */
155
		case 0x16: /* Celeron Core */
156
		case 0x1C: /* Atom */
157
			p4clockmod_driver.flags |= CPUFREQ_CONST_LOOPS;
158
			return speedstep_get_frequency(SPEEDSTEP_CPU_PCORE);
159 160 161 162
		case 0x0D: /* Pentium M (Dothan) */
			p4clockmod_driver.flags |= CPUFREQ_CONST_LOOPS;
			/* fall through */
		case 0x09: /* Pentium M (Banias) */
163
			return speedstep_get_frequency(SPEEDSTEP_CPU_PM);
164
		}
L
Linus Torvalds 已提交
165 166
	}

167
	if (c->x86 != 0xF)
L
Linus Torvalds 已提交
168 169 170 171 172 173
		return 0;

	/* on P-4s, the TSC runs with constant frequency independent whether
	 * throttling is active or not. */
	p4clockmod_driver.flags |= CPUFREQ_CONST_LOOPS;

174
	if (speedstep_detect_processor() == SPEEDSTEP_CPU_P4M) {
L
Linus Torvalds 已提交
175 176 177 178 179
		printk(KERN_WARNING PFX "Warning: Pentium 4-M detected. "
		       "The speedstep-ich or acpi cpufreq modules offer "
		       "voltage scaling in addition of frequency scaling. "
		       "You should use either one instead of p4-clockmod, "
		       "if possible.\n");
180
		return speedstep_get_frequency(SPEEDSTEP_CPU_P4M);
L
Linus Torvalds 已提交
181 182
	}

183
	return speedstep_get_frequency(SPEEDSTEP_CPU_P4D);
L
Linus Torvalds 已提交
184 185
}

186

L
Linus Torvalds 已提交
187 188 189

static int cpufreq_p4_cpu_init(struct cpufreq_policy *policy)
{
190
	struct cpuinfo_x86 *c = &cpu_data(policy->cpu);
L
Linus Torvalds 已提交
191 192 193 194
	int cpuid = 0;
	unsigned int i;

#ifdef CONFIG_SMP
195
	cpumask_copy(policy->cpus, cpu_sibling_mask(policy->cpu));
L
Linus Torvalds 已提交
196 197 198 199 200 201 202 203 204 205
#endif

	/* Errata workaround */
	cpuid = (c->x86 << 8) | (c->x86_model << 4) | c->x86_mask;
	switch (cpuid) {
	case 0x0f07:
	case 0x0f0a:
	case 0x0f11:
	case 0x0f12:
		has_N44_O17_errata[policy->cpu] = 1;
206
		pr_debug("has errata -- disabling low frequencies\n");
L
Linus Torvalds 已提交
207
	}
208

209 210 211 212 213 214
	if (speedstep_detect_processor() == SPEEDSTEP_CPU_P4D &&
	    c->x86_model < 2) {
		/* switch to maximum frequency and measure result */
		cpufreq_p4_setdc(policy->cpu, DC_DISABLE);
		recalibrate_cpu_khz();
	}
L
Linus Torvalds 已提交
215 216 217 218 219 220
	/* get max frequency */
	stock_freq = cpufreq_p4_get_frequency(c);
	if (!stock_freq)
		return -EINVAL;

	/* table init */
221 222
	for (i = 1; (p4clockmod_table[i].frequency != CPUFREQ_TABLE_END); i++) {
		if ((i < 2) && (has_N44_O17_errata[policy->cpu]))
L
Linus Torvalds 已提交
223 224 225 226
			p4clockmod_table[i].frequency = CPUFREQ_ENTRY_INVALID;
		else
			p4clockmod_table[i].frequency = (stock_freq * i)/8;
	}
227

L
Linus Torvalds 已提交
228
	/* cpuinfo and default policy values */
229 230 231 232

	/* the transition latency is set to be 1 higher than the maximum
	 * transition latency of the ondemand governor */
	policy->cpuinfo.transition_latency = 10000001;
L
Linus Torvalds 已提交
233 234
	policy->cur = stock_freq;

235
	return cpufreq_table_validate_and_show(policy, &p4clockmod_table[0]);
L
Linus Torvalds 已提交
236 237 238 239 240 241 242
}


static unsigned int cpufreq_p4_get(unsigned int cpu)
{
	u32 l, h;

243
	rdmsr_on_cpu(cpu, MSR_IA32_THERM_CONTROL, &l, &h);
L
Linus Torvalds 已提交
244 245 246 247 248 249 250 251

	if (l & 0x10) {
		l = l >> 1;
		l &= 0x7;
	} else
		l = DC_DISABLE;

	if (l != DC_DISABLE)
252
		return stock_freq * l / 8;
L
Linus Torvalds 已提交
253 254 255 256 257

	return stock_freq;
}

static struct cpufreq_driver p4clockmod_driver = {
258
	.verify		= cpufreq_generic_frequency_table_verify,
L
Linus Torvalds 已提交
259 260
	.target		= cpufreq_p4_target,
	.init		= cpufreq_p4_cpu_init,
261
	.exit		= cpufreq_generic_exit,
L
Linus Torvalds 已提交
262 263
	.get		= cpufreq_p4_get,
	.name		= "p4-clockmod",
264
	.attr		= cpufreq_generic_attr,
L
Linus Torvalds 已提交
265 266
};

267 268 269 270 271 272 273 274 275
static const struct x86_cpu_id cpufreq_p4_id[] = {
	{ X86_VENDOR_INTEL, X86_FAMILY_ANY, X86_MODEL_ANY, X86_FEATURE_ACC },
	{}
};

/*
 * Intentionally no MODULE_DEVICE_TABLE here: this driver should not
 * be auto loaded.  Please don't add one.
 */
L
Linus Torvalds 已提交
276 277

static int __init cpufreq_p4_init(void)
278
{
L
Linus Torvalds 已提交
279 280 281
	int ret;

	/*
282
	 * THERM_CONTROL is architectural for IA32 now, so
L
Linus Torvalds 已提交
283 284
	 * we can rely on the capability checks
	 */
285
	if (!x86_match_cpu(cpufreq_p4_id) || !boot_cpu_has(X86_FEATURE_ACPI))
L
Linus Torvalds 已提交
286 287 288 289
		return -ENODEV;

	ret = cpufreq_register_driver(&p4clockmod_driver);
	if (!ret)
290 291
		printk(KERN_INFO PFX "P4/Xeon(TM) CPU On-Demand Clock "
				"Modulation available\n");
L
Linus Torvalds 已提交
292

293
	return ret;
L
Linus Torvalds 已提交
294 295 296 297 298 299 300 301 302
}


static void __exit cpufreq_p4_exit(void)
{
	cpufreq_unregister_driver(&p4clockmod_driver);
}


303 304 305
MODULE_AUTHOR("Zwane Mwaikambo <zwane@commfireservices.com>");
MODULE_DESCRIPTION("cpufreq driver for Pentium(TM) 4/Xeon(TM)");
MODULE_LICENSE("GPL");
L
Linus Torvalds 已提交
306 307 308

late_initcall(cpufreq_p4_init);
module_exit(cpufreq_p4_exit);