Bug 7463 - CPU frequency scaling(cpufreq) does not work. speedstep-centrino.c problem
Summary: CPU frequency scaling(cpufreq) does not work. speedstep-centrino.c problem
Status: REJECTED INVALID
Alias: None
Product: Power Management
Classification: Unclassified
Component: cpufreq (show other bugs)
Hardware: i386 Linux
: P2 normal
Assignee: cpufreq
URL:
Keywords:
: 7607 (view as bug list)
Depends on:
Blocks:
 
Reported: 2006-11-05 13:33 UTC by Oldrich Plchot
Modified: 2008-05-28 18:37 UTC (History)
9 users (show)

See Also:
Kernel Version: 2.6.18.1
Subsystem:
Regression: ---
Bisected commit-id:


Attachments

Description Oldrich Plchot 2006-11-05 13:33:24 UTC
Most recent kernel where this bug did not occur:
Distribution: Fedora core 6

Hardware Environment:
Laptop Prestigio Nobile 157
#cat /proc/cpuinfo
[olda@Prestigio ~]$ cat /proc/cpuinfo 
processor       : 0
vendor_id       : GenuineIntel
cpu family      : 6
model           : 13
model name      : Intel(R) Pentium(R) M processor 1.70GHz
stepping        : 6
cpu MHz         : 600.000
cache size      : 2048 KB
fdiv_bug        : no
hlt_bug         : no
f00f_bug        : no
coma_bug        : no
fpu             : yes
fpu_exception   : yes
cpuid level     : 2
wp              : yes
flags           : fpu vme de pse tsc msr mce cx8 sep mtrr pge mca cmov pat
clflush dts acpi mmx fxsr sse sse2 ss tm pbe est tm2
bogomips        : 1197.27

[olda@Prestigio ~]$ cpuid
CPU:
   vendor_id = "GenuineIntel"
   version information (1/eax):
      processor type  = primary processor (0)
      family          = Intel Pentium Pro/II/III/Celeron, AMD Athlon/Duron,
Cyrix M2, VIA C3 (6)
      model           = 0xd (13)
      stepping id     = 0x6 (6)
      extended family = 0x0 (0)
      extended model  = 0x0 (0)
      (simple synth)  = Intel Pentium M (Dothan B1) / Celeron M (Dothan B1), 90nm
   miscellaneous (1/ebx):
      process local APIC physical ID = 0x0 (0)
      cpu count                      = 0x0 (0)
      CLFLUSH line size              = 0x8 (8)
      brand index                    = 0x16 (22)
   brand id = 0x16 (22): Intel Pentium M, .13um
   feature information (1/edx):
      x87 FPU on chip                        = true
      virtual-8086 mode enhancement          = true
      debugging extensions                   = true
      page size extensions                   = true
      time stamp counter                     = true
      RDMSR and WRMSR support                = true
      physical address extensions            = false
      machine check exception                = true
      CMPXCHG8B inst.                        = true
      APIC on chip                           = false
      SYSENTER and SYSEXIT                   = true
      memory type range registers            = true
      PTE global bit                         = true
      machine check architecture             = true
      conditional move/compare instruction   = true
      page attribute table                   = true
      page size extension                    = false
      processor serial number                = false
      CLFLUSH instruction                    = true
      debug store                            = true
      thermal monitor and clock ctrl         = true
      MMX Technology                         = true
      FXSAVE/FXRSTOR                         = true
      SSE extensions                         = true
      SSE2 extensions                        = true
      self snoop                             = true
      hyper-threading / multi-core supported = false
      therm. monitor                         = true
      IA64                                   = false
      pending break event                    = true
   feature information (1/ecx):
      PNI/SSE3: Prescott New Instructions    = false
      MONITOR/MWAIT                          = false
      CPL-qualified debug store              = false
      VMX: virtual machine extensions        = false
      Enhanced Intel SpeedStep Technology    = true
      thermal monitor 2                      = true
      context ID: adaptive or shared L1 data = false
      cmpxchg16b available                   = false
      xTPR disable                           = false
   cache and TLB information (2):
      0xb0: instruction TLB: 4K, 4-way, 128 entries
      0xb3: data TLB: 4K, 4-way, 128 entries
      0x02: instruction TLB: 4M pages, 4-way, 2 entries
      0xf0: 64 byte prefetching
      0x7d: L2 cache: 2M, 8-way, sectored, 64 byte lines
      0x30: L1 cache: 32K, 8-way, 64 byte lines
      0x04: data TLB: 4M pages, 4-way, 8 entries
      0x2c: L1 data cache: 32K, 8-way, 64 byte lines
   extended feature flags (0x80000001/edx):
      SYSCALL and SYSRET instructions        = false
      execution disable                      = false
      64-bit extensions technology available = false
   Intel feature flags (0x80000001/ecx):
      LAHF/SAHF supported in 64-bit mode = false
   brand = "        Intel(R) Pentium(R) M processor 1.70GHz"
   (multi-processing synth): none
   (synth) = Intel Pentium M (Dothan B1), 90nm


Software Environment:
standard kernel without any proprietary modules

Problem Description:
CPU frequency scaling (cpufreq) does not work if I use vanilla kernel or kernel
from distribution. I had this problem for quiet a long time. Once I found a file
from Jeremy Fitzhardinge which solved the problem. There are defined exact
processor types and one of them is mine. That file only defines
cpufreq_frequency_table based on the document
http://www.intel.com/design/mobile/datashts/302189.htm


/* Intel Pentium M processor 735 / 1.70GHz (Dothan) */
static struct cpufreq_frequency_table dothan_1700[] =
{
	OP( 600,  988,  988,  988,  988),
	OP( 800, 1052, 1052, 1052, 1052),
	OP(1000, 1116, 1116, 1116, 1100),
	OP(1200, 1180, 1180, 1164, 1148),
	OP(1400, 1244, 1244, 1228, 1212),
	OP(1700, 1340, 1324, 1308, 1276),
	{ .frequency = CPUFREQ_TABLE_END }
};

With this speedstep-centrino.c everything works great without any problem. So I
have to compile kernel every time I upgrade distro or want a new functionality
from kernel. I will include the source of sppedstep-centrino.c, kernel compiles
and works just fine if I replace original speedstep-centrino.c with the file I use.


/*
 * cpufreq driver for Enhanced SpeedStep, as found in Intel's Pentium
 * M (part of the Centrino chipset).
 *
 * Despite the "SpeedStep" in the name, this is almost entirely unlike
 * traditional SpeedStep.
 *
 * Modelled on speedstep.c
 *
 * Copyright (C) 2003 Jeremy Fitzhardinge <jeremy@goop.org>
 *
 * WARNING WARNING WARNING
 *
 * This driver manipulates the PERF_CTL MSR, which is only somewhat
 * documented.  While it seems to work on my laptop, it has not been
 * tested anywhere else, and it may not work for you, do strange
 * things or simply crash.
 */

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/cpufreq.h>
#include <linux/config.h>
#include <linux/delay.h>
#include <linux/compiler.h>

#ifdef CONFIG_X86_SPEEDSTEP_CENTRINO_ACPI
#include <linux/acpi.h>
#include <acpi/processor.h>
#endif

#include <asm/msr.h>
#include <asm/processor.h>
#include <asm/cpufeature.h>

#include "speedstep-est-common.h"

#define PFX		"speedstep-centrino: "
#define MAINTAINER	"Jeremy Fitzhardinge <jeremy@goop.org>"

#define dprintk(msg...) cpufreq_debug_printk(CPUFREQ_DEBUG_DRIVER,
"speedstep-centrino", msg)


struct cpu_id
{
	__u8	x86;            /* CPU family */
	__u8	x86_model;	/* model */
	__u8	x86_mask;	/* stepping */
};

enum {
	CPU_BANIAS,
	CPU_DOTHAN_A1,
	CPU_DOTHAN_A2,
	CPU_DOTHAN_B0,
};

static const struct cpu_id cpu_ids[] = {
	[CPU_BANIAS]	= { 6,  9, 5 },
	[CPU_DOTHAN_A1]	= { 6, 13, 1 },
	[CPU_DOTHAN_A2]	= { 6, 13, 2 },
	[CPU_DOTHAN_B0]	= { 6, 13, 6 },
};
#define N_IDS	(sizeof(cpu_ids)/sizeof(cpu_ids[0]))

struct cpu_model
{
	const struct cpu_id *cpu_id;
	const char	*model_name;
	unsigned	max_freq; /* max clock in kHz */

	struct cpufreq_frequency_table *op_points; /* clock/voltage pairs */
};
static int centrino_verify_cpu_id(const struct cpuinfo_x86 *c, const struct
cpu_id *x);

/* Operating points for current CPU */
static struct cpu_model *centrino_model[NR_CPUS];
static const struct cpu_id *centrino_cpu[NR_CPUS];

static struct cpufreq_driver centrino_driver;

#ifdef CONFIG_X86_SPEEDSTEP_CENTRINO_TABLE

/* Computes the correct form for IA32_PERF_CTL MSR for a particular
   frequency/voltage operating point; frequency in MHz, volts in mV.
   This is stored as "index" in the structure. */
#define OP(mhz, mv)							\
	{								\
		.frequency = (mhz) * 1000,				\
		.index = (((mhz)/100) << 8) | ((mv - 700) / 16)		\
	}

/*
 * These voltage tables were derived from the Intel Pentium M
 * datasheet, document 25261202.pdf, Table 5.  I have verified they
 * are consistent with my IBM ThinkPad X31, which has a 1.3GHz Pentium
 * M.
 */

/* Ultra Low Voltage Intel Pentium M processor 900MHz (Banias) */
static struct cpufreq_frequency_table banias_900[] =
{
	OP(600,  844),
	OP(800,  988),
	OP(900, 1004),
	{ .frequency = CPUFREQ_TABLE_END }
};

/* Ultra Low Voltage Intel Pentium M processor 1000MHz (Banias) */
static struct cpufreq_frequency_table banias_1000[] =
{
	OP(600,   844),
	OP(800,   972),
	OP(900,   988),
	OP(1000, 1004),
	{ .frequency = CPUFREQ_TABLE_END }
};

/* Low Voltage Intel Pentium M processor 1.10GHz (Banias) */
static struct cpufreq_frequency_table banias_1100[] =
{
	OP( 600,  956),
	OP( 800, 1020),
	OP( 900, 1100),
	OP(1000, 1164),
	OP(1100, 1180),
	{ .frequency = CPUFREQ_TABLE_END }
};


/* Low Voltage Intel Pentium M processor 1.20GHz (Banias) */
static struct cpufreq_frequency_table banias_1200[] =
{
	OP( 600,  956),
	OP( 800, 1004),
	OP( 900, 1020),
	OP(1000, 1100),
	OP(1100, 1164),
	OP(1200, 1180),
	{ .frequency = CPUFREQ_TABLE_END }
};

/* Intel Pentium M processor 1.30GHz (Banias) */
static struct cpufreq_frequency_table banias_1300[] =
{
	OP( 600,  956),
	OP( 800, 1260),
	OP(1000, 1292),
	OP(1200, 1356),
	OP(1300, 1388),
	{ .frequency = CPUFREQ_TABLE_END }
};

/* Intel Pentium M processor 1.40GHz (Banias) */
static struct cpufreq_frequency_table banias_1400[] =
{
	OP( 600,  956),
	OP( 800, 1180),
	OP(1000, 1308),
	OP(1200, 1436),
	OP(1400, 1484),
	{ .frequency = CPUFREQ_TABLE_END }
};

/* Intel Pentium M processor 1.50GHz (Banias) */
static struct cpufreq_frequency_table banias_1500[] =
{
	OP( 600,  956),
	OP( 800, 1116),
	OP(1000, 1228),
	OP(1200, 1356),
	OP(1400, 1452),
	OP(1500, 1484),
	{ .frequency = CPUFREQ_TABLE_END }
};

/* Intel Pentium M processor 1.60GHz (Banias) */
static struct cpufreq_frequency_table banias_1600[] =
{
	OP( 600,  956),
	OP( 800, 1036),
	OP(1000, 1164),
	OP(1200, 1276),
	OP(1400, 1420),
	OP(1600, 1484),
	{ .frequency = CPUFREQ_TABLE_END }
};

/* Intel Pentium M processor 1.70GHz (Banias) */
static struct cpufreq_frequency_table banias_1700[] =
{
	OP( 600,  956),
	OP( 800, 1004),
	OP(1000, 1116),
	OP(1200, 1228),
	OP(1400, 1308),
	OP(1700, 1484),
	{ .frequency = CPUFREQ_TABLE_END }
};

#undef OP

/* Dothan processor datasheet 30218903.pdf defines 4 voltages for each
   frequency (VID#A through VID#D) - this macro allows us to define all
   of these but we only use the VID#C voltages at compile time - this may
   need some work if we want to select the voltage profile at runtime. */

#define OP(mhz, mva, mvb, mvc, mvd)					\
	{								\
		.frequency = (mhz) * 1000,				\
		.index = (((mhz)/100) << 8) | ((mvc - 700) / 16)       	\
	}

/* Intel Pentium M processor 715 / 1.50GHz (Dothan) */
static struct cpufreq_frequency_table dothan_1500[] =
{
	OP( 600,  988,  988,  988,  988),
	OP( 800, 1068, 1068, 1068, 1052),
	OP(1000, 1148, 1148, 1132, 1116),
	OP(1200, 1228, 1212, 1212, 1180),
	OP(1500, 1340, 1324, 1308, 1276),
	{ .frequency = CPUFREQ_TABLE_END }
};

/* Intel Pentium M processor 725 / 1.60GHz (Dothan) */
static struct cpufreq_frequency_table dothan_1600[] =
{
	OP( 600,  988,  988,  988,  988),
	OP( 800, 1068, 1068, 1052, 1052),
	OP(1000, 1132, 1132, 1116, 1116),
	OP(1200, 1212, 1196, 1180, 1164),
	OP(1400, 1276, 1260, 1244, 1228),
	OP(1600, 1340, 1324, 1308, 1276),
	{ .frequency = CPUFREQ_TABLE_END }
};

/* Intel Pentium M processor 735 / 1.70GHz (Dothan) */
static struct cpufreq_frequency_table dothan_1700[] =
{
	OP( 600,  988,  988,  988,  988),
	OP( 800, 1052, 1052, 1052, 1052),
	OP(1000, 1116, 1116, 1116, 1100),
	OP(1200, 1180, 1180, 1164, 1148),
	OP(1400, 1244, 1244, 1228, 1212),
	OP(1700, 1340, 1324, 1308, 1276),
	{ .frequency = CPUFREQ_TABLE_END }
};

/* Intel Pentium M processor 745 / 1.80GHz (Dothan) */
static struct cpufreq_frequency_table dothan_1800[] =
{
	OP( 600,  988,  988,  988,  988),
	OP( 800, 1052, 1052, 1052, 1036),
	OP(1000, 1116, 1100, 1100, 1084),
	OP(1200, 1164, 1164, 1148, 1132),
	OP(1400, 1228, 1212, 1212, 1180),
	OP(1600, 1292, 1276, 1260, 1228),
	OP(1800, 1340, 1324, 1308, 1276),
	{ .frequency = CPUFREQ_TABLE_END }
};

/* Intel Pentium M processor 755 / 2.00GHz (Dothan) */
static struct cpufreq_frequency_table dothan_2000[] =
{
	OP( 600,  988,  988,  988,  988),
	OP( 800, 1052, 1036, 1036, 1036),
	OP(1000, 1100, 1084, 1084, 1084),
	OP(1200, 1148, 1132, 1132, 1116),
	OP(1400, 1196, 1180, 1180, 1164),
	OP(1600, 1244, 1228, 1228, 1196),
	OP(1800, 1292, 1276, 1276, 1244),
	OP(2000, 1340, 1324, 1308, 1276),
	{ .frequency = CPUFREQ_TABLE_END }
};

#undef OP

#define _BANIAS(cpuid, max, name)	\
{	.cpu_id		= cpuid,	\
	.model_name	= "Intel(R) Pentium(R) M processor " name "MHz", \
	.max_freq	= (max)*1000,	\
	.op_points	= banias_##max,	\
}
#define BANIAS(max)	_BANIAS(&cpu_ids[CPU_BANIAS], max, #max)

#define DOTHAN(cpuid, max, name)	\
{	.cpu_id		= cpuid,	\
	.model_name	= "Intel(R) Pentium(R) M processor " name "GHz", \
	.max_freq	= (max)*1000,	\
	.op_points	= dothan_##max,	\
}

/* CPU models, their operating frequency range, and freq/voltage
   operating points */
static struct cpu_model models[] =
{
	_BANIAS(&cpu_ids[CPU_BANIAS], 900, " 900"),
	BANIAS(1000),
	BANIAS(1100),
	BANIAS(1200),
	BANIAS(1300),
	BANIAS(1400),
	BANIAS(1500),
	BANIAS(1600),
	BANIAS(1700),
	DOTHAN(&cpu_ids[CPU_DOTHAN_B0], 1500, "1.50"),
	DOTHAN(&cpu_ids[CPU_DOTHAN_B0], 1600, "1.60"),
	DOTHAN(&cpu_ids[CPU_DOTHAN_B0], 1700, "1.70"),
	DOTHAN(&cpu_ids[CPU_DOTHAN_B0], 1800, "1.80"),
	DOTHAN(&cpu_ids[CPU_DOTHAN_B0], 2000, "2.00"),

	/* NULL model_name is a wildcard */
	{ &cpu_ids[CPU_DOTHAN_A1], NULL, 0, NULL },
	{ &cpu_ids[CPU_DOTHAN_A2], NULL, 0, NULL },
	{ &cpu_ids[CPU_DOTHAN_B0], NULL, 0, NULL },

	{ NULL, }
};
#undef _BANIAS
#undef BANIAS
#undef DOTHAN

static int centrino_cpu_init_table(struct cpufreq_policy *policy)
{
	struct cpuinfo_x86 *cpu = &cpu_data[policy->cpu];
	struct cpu_model *model;

	for(model = models; model->cpu_id != NULL; model++)
		if (centrino_verify_cpu_id(cpu, model->cpu_id) &&
		    (model->model_name == NULL ||
		     strcmp(cpu->x86_model_id, model->model_name) == 0))
			break;

	if (model->cpu_id == NULL) {
		/* No match at all */
		dprintk(KERN_INFO PFX "no support for CPU model \"%s\": "
		       "send /proc/cpuinfo to " MAINTAINER "\n",
		       cpu->x86_model_id);
		return -ENOENT;
	}

	if (model->op_points == NULL) {
		/* Matched a non-match */
		dprintk(KERN_INFO PFX "no table support for CPU model \"%s\": \n",
		       cpu->x86_model_id);
#ifndef CONFIG_X86_SPEEDSTEP_CENTRINO_ACPI
		dprintk(KERN_INFO PFX "try compiling with CONFIG_X86_SPEEDSTEP_CENTRINO_ACPI
enabled\n");
#endif
		return -ENOENT;
	}

	centrino_model[policy->cpu] = model;

	dprintk("found \"%s\": max frequency: %dkHz\n",
	       model->model_name, model->max_freq);

	return 0;
}

#else
static inline int centrino_cpu_init_table(struct cpufreq_policy *policy) {
return -ENODEV; }
#endif /* CONFIG_X86_SPEEDSTEP_CENTRINO_TABLE */

static int centrino_verify_cpu_id(const struct cpuinfo_x86 *c, const struct
cpu_id *x)
{
	if ((c->x86 == x->x86) &&
	    (c->x86_model == x->x86_model) &&
	    (c->x86_mask == x->x86_mask))
		return 1;
	return 0;
}

/* To be called only after centrino_model is initialized */
static unsigned extract_clock(unsigned msr, unsigned int cpu, int failsafe)
{
	int i;

	/*
	 * Extract clock in kHz from PERF_CTL value
	 * for centrino, as some DSDTs are buggy.
	 * Ideally, this can be done using the acpi_data structure.
	 */
	if ((centrino_cpu[cpu] == &cpu_ids[CPU_BANIAS]) ||
	    (centrino_cpu[cpu] == &cpu_ids[CPU_DOTHAN_A1]) ||
	    (centrino_cpu[cpu] == &cpu_ids[CPU_DOTHAN_B0])) {
		msr = (msr >> 8) & 0xff;
		return msr * 100000;
	}

	if ((!centrino_model[cpu]) || (!centrino_model[cpu]->op_points))
		return 0;

	msr &= 0xffff;
	for (i=0;centrino_model[cpu]->op_points[i].frequency != CPUFREQ_TABLE_END; i++) {
		if (msr == centrino_model[cpu]->op_points[i].index)
			return centrino_model[cpu]->op_points[i].frequency;
	}
	if (failsafe)
		return centrino_model[cpu]->op_points[i-1].frequency;
	else
		return 0;
}

/* Return the current CPU frequency in kHz */
static unsigned int get_cur_freq(unsigned int cpu)
{
	unsigned l, h;
	unsigned clock_freq;
	cpumask_t saved_mask;

	saved_mask = current->cpus_allowed;
	set_cpus_allowed(current, cpumask_of_cpu(cpu));
	if (smp_processor_id() != cpu)
		return 0;

	rdmsr(MSR_IA32_PERF_STATUS, l, h);
	clock_freq = extract_clock(l, cpu, 0);

	if (unlikely(clock_freq == 0)) {
		/*
		 * On some CPUs, we can see transient MSR values (which are
		 * not present in _PSS), while CPU is doing some automatic
		 * P-state transition (like TM2). Get the last freq set 
		 * in PERF_CTL.
		 */
		rdmsr(MSR_IA32_PERF_CTL, l, h);
		clock_freq = extract_clock(l, cpu, 1);
	}

	set_cpus_allowed(current, saved_mask);
	return clock_freq;
}


#ifdef CONFIG_X86_SPEEDSTEP_CENTRINO_ACPI

static struct acpi_processor_performance p;

/*
 * centrino_cpu_init_acpi - register with ACPI P-States library
 *
 * Register with the ACPI P-States library (part of drivers/acpi/processor.c)
 * in order to determine correct frequency and voltage pairings by reading
 * the _PSS of the ACPI DSDT or SSDT tables.
 */
static int centrino_cpu_init_acpi(struct cpufreq_policy *policy)
{
	union acpi_object		arg0 = {ACPI_TYPE_BUFFER};
	u32				arg0_buf[3];
	struct acpi_object_list		arg_list = {1, &arg0};
	unsigned long			cur_freq;
	int				result = 0, i;
	unsigned int			cpu = policy->cpu;

	/* _PDC settings */
	arg0.buffer.length = 12;
	arg0.buffer.pointer = (u8 *) arg0_buf;
	arg0_buf[0] = ACPI_PDC_REVISION_ID;
	arg0_buf[1] = 1;
	arg0_buf[2] = ACPI_PDC_EST_CAPABILITY_SMP_MSR; //  | ACPI_PDC_EST_CAPABILITY_MSR;

	p.pdc = &arg_list;

	/* register with ACPI core */
	if (acpi_processor_register_performance(&p, cpu)) {
		dprintk(KERN_INFO PFX "obtaining ACPI data failed\n");
		return -EIO;
	}

	/* verify the acpi_data */
	if (p.state_count <= 1) {
		dprintk("No P-States\n");
		result = -ENODEV;
		goto err_unreg;
	}

	if ((p.control_register.space_id != ACPI_ADR_SPACE_FIXED_HARDWARE) ||
	    (p.status_register.space_id != ACPI_ADR_SPACE_FIXED_HARDWARE)) {
		dprintk("Invalid control/status registers (%x - %x)\n",
			p.control_register.space_id, p.status_register.space_id);
		result = -EIO;
		goto err_unreg;
	}

	for (i=0; i<p.state_count; i++) {
		if (p.states[i].control != p.states[i].status) {
			dprintk("Different control (%x) and status values (%x)\n",
				p.states[i].control, p.states[i].status);
			result = -EINVAL;
			goto err_unreg;
		}

		if (!p.states[i].core_frequency) {
			dprintk("Zero core frequency for state %u\n", i);
			result = -EINVAL;
			goto err_unreg;
		}

		if (p.states[i].core_frequency > p.states[0].core_frequency) {
			dprintk("P%u has larger frequency (%u) than P0 (%u), skipping\n", i,
				p.states[i].core_frequency, p.states[0].core_frequency);
			p.states[i].core_frequency = 0;
			continue;
		}
	}

	centrino_model[cpu] = kmalloc(sizeof(struct cpu_model), GFP_KERNEL);
	if (!centrino_model[cpu]) {
		result = -ENOMEM;
		goto err_unreg;
	}
	memset(centrino_model[cpu], 0, sizeof(struct cpu_model));

	centrino_model[cpu]->model_name=NULL;
	centrino_model[cpu]->max_freq = p.states[0].core_frequency * 1000;
	centrino_model[cpu]->op_points =  kmalloc(sizeof(struct cpufreq_frequency_table) *
					     (p.state_count + 1), GFP_KERNEL);
        if (!centrino_model[cpu]->op_points) {
                result = -ENOMEM;
                goto err_kfree;
        }

        for (i=0; i<p.state_count; i++) {
		centrino_model[cpu]->op_points[i].index = p.states[i].control;
		centrino_model[cpu]->op_points[i].frequency = p.states[i].core_frequency * 1000;
		dprintk("adding state %i with frequency %u and control value %04x\n", 
			i, centrino_model[cpu]->op_points[i].frequency,
centrino_model[cpu]->op_points[i].index);
	}
	centrino_model[cpu]->op_points[p.state_count].frequency = CPUFREQ_TABLE_END;

	cur_freq = get_cur_freq(cpu);

	for (i=0; i<p.state_count; i++) {
		if (!p.states[i].core_frequency) {
			dprintk("skipping state %u\n", i);
			centrino_model[cpu]->op_points[i].frequency = CPUFREQ_ENTRY_INVALID;
			continue;
		}
		
		if (extract_clock(centrino_model[cpu]->op_points[i].index, cpu, 0) !=
		    (centrino_model[cpu]->op_points[i].frequency)) {
			dprintk("Invalid encoded frequency (%u vs. %u)\n",
				extract_clock(centrino_model[cpu]->op_points[i].index, cpu, 0),
				centrino_model[cpu]->op_points[i].frequency);
			result = -EINVAL;
			goto err_kfree_all;
		}

		if (cur_freq == centrino_model[cpu]->op_points[i].frequency)
			p.state = i;
	}

	/* notify BIOS that we exist */
	acpi_processor_notify_smm(THIS_MODULE);

	return 0;

 err_kfree_all:
	kfree(centrino_model[cpu]->op_points);
 err_kfree:
	kfree(centrino_model[cpu]);
 err_unreg:
	acpi_processor_unregister_performance(&p, cpu);
	dprintk(KERN_INFO PFX "invalid ACPI data\n");
	return (result);
}
#else
static inline int centrino_cpu_init_acpi(struct cpufreq_policy *policy) { return
-ENODEV; }
#endif

static int centrino_cpu_init(struct cpufreq_policy *policy)
{
	struct cpuinfo_x86 *cpu = &cpu_data[policy->cpu];
	unsigned freq;
	unsigned l, h;
	int ret;
	int i;

	/* Only Intel makes Enhanced Speedstep-capable CPUs */
	if (cpu->x86_vendor != X86_VENDOR_INTEL || !cpu_has(cpu, X86_FEATURE_EST))
		return -ENODEV;

	for (i = 0; i < N_IDS; i++)
		if (centrino_verify_cpu_id(cpu, &cpu_ids[i]))
			break;

	if (i != N_IDS)
		centrino_cpu[policy->cpu] = &cpu_ids[i];

	if (is_const_loops_cpu(policy->cpu)) {
		centrino_driver.flags |= CPUFREQ_CONST_LOOPS;
	}

	if (centrino_cpu_init_acpi(policy)) {
		if (policy->cpu != 0)
			return -ENODEV;

		if (!centrino_cpu[policy->cpu]) {
			dprintk(KERN_INFO PFX "found unsupported CPU with "
			"Enhanced SpeedStep: send /proc/cpuinfo to "
			MAINTAINER "\n");
			return -ENODEV;
		}

		if (centrino_cpu_init_table(policy)) {
			return -ENODEV;
		}
	}

	/* Check to see if Enhanced SpeedStep is enabled, and try to
	   enable it if not. */
	rdmsr(MSR_IA32_MISC_ENABLE, l, h);

	if (!(l & (1<<16))) {
		l |= (1<<16);
		dprintk("trying to enable Enhanced SpeedStep (%x)\n", l);
		wrmsr(MSR_IA32_MISC_ENABLE, l, h);

		/* check to see if it stuck */
		rdmsr(MSR_IA32_MISC_ENABLE, l, h);
		if (!(l & (1<<16))) {
			printk(KERN_INFO PFX "couldn't enable Enhanced SpeedStep\n");
			return -ENODEV;
		}
	}

	freq = get_cur_freq(policy->cpu);

	policy->governor = CPUFREQ_DEFAULT_GOVERNOR;
	policy->cpuinfo.transition_latency = 10000; /* 10uS transition latency */
	policy->cur = freq;

	dprintk("centrino_cpu_init: cur=%dkHz\n", policy->cur);

	ret = cpufreq_frequency_table_cpuinfo(policy,
centrino_model[policy->cpu]->op_points);
	if (ret)
		return (ret);

	cpufreq_frequency_table_get_attr(centrino_model[policy->cpu]->op_points,
policy->cpu);

	return 0;
}

static int centrino_cpu_exit(struct cpufreq_policy *policy)
{
	unsigned int cpu = policy->cpu;

	if (!centrino_model[cpu])
		return -ENODEV;

	cpufreq_frequency_table_put_attr(cpu);

#ifdef CONFIG_X86_SPEEDSTEP_CENTRINO_ACPI
	if (!centrino_model[cpu]->model_name) {
		dprintk("unregistering and freeing ACPI data\n");
		acpi_processor_unregister_performance(&p, cpu);
		kfree(centrino_model[cpu]->op_points);
		kfree(centrino_model[cpu]);
	}
#endif

	centrino_model[cpu] = NULL;

	return 0;
}

/**
 * centrino_verify - verifies a new CPUFreq policy
 * @policy: new policy
 *
 * Limit must be within this model's frequency range at least one
 * border included.
 */
static int centrino_verify (struct cpufreq_policy *policy)
{
	return cpufreq_frequency_table_verify(policy,
centrino_model[policy->cpu]->op_points);
}

/**
 * centrino_setpolicy - set a new CPUFreq policy
 * @policy: new policy
 * @target_freq: the target frequency
 * @relation: how that frequency relates to achieved frequency
(CPUFREQ_RELATION_L or CPUFREQ_RELATION_H)
 *
 * Sets a new CPUFreq policy.
 */
static int centrino_target (struct cpufreq_policy *policy,
			    unsigned int target_freq,
			    unsigned int relation)
{
	unsigned int    newstate = 0;
	unsigned int	msr, oldmsr, h, cpu = policy->cpu;
	struct cpufreq_freqs	freqs;
	cpumask_t		saved_mask;
	int			retval;

	if (centrino_model[cpu] == NULL)
		return -ENODEV;

	/*
	 * Support for SMP systems.
	 * Make sure we are running on the CPU that wants to change frequency
	 */
	saved_mask = current->cpus_allowed;
	set_cpus_allowed(current, policy->cpus);
	if (!cpu_isset(smp_processor_id(), policy->cpus)) {
		dprintk("couldn't limit to CPUs in this domain\n");
		return(-EAGAIN);
	}

	if (cpufreq_frequency_table_target(policy, centrino_model[cpu]->op_points,
target_freq,
					   relation, &newstate)) {
		retval = -EINVAL;
		goto migrate_end;
	}

	msr = centrino_model[cpu]->op_points[newstate].index;
	rdmsr(MSR_IA32_PERF_CTL, oldmsr, h);

	if (msr == (oldmsr & 0xffff)) {
		retval = 0;
		dprintk("no change needed - msr was and needs to be %x\n", oldmsr);
		goto migrate_end;
	}

	freqs.cpu = cpu;
	freqs.old = extract_clock(oldmsr, cpu, 0);
	freqs.new = extract_clock(msr, cpu, 0);

	dprintk("target=%dkHz old=%d new=%d msr=%04x\n",
		target_freq, freqs.old, freqs.new, msr);

	cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);

	/* all but 16 LSB are "reserved", so treat them with
	   care */
	oldmsr &= ~0xffff;
	msr &= 0xffff;
	oldmsr |= msr;

	wrmsr(MSR_IA32_PERF_CTL, oldmsr, h);

	cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);

	retval = 0;
migrate_end:
	set_cpus_allowed(current, saved_mask);
	return (retval);
}

static struct freq_attr* centrino_attr[] = {
	&cpufreq_freq_attr_scaling_available_freqs,
	NULL,
};

static struct cpufreq_driver centrino_driver = {
	.name		= "centrino", /* should be speedstep-centrino,
					 but there's a 16 char limit */
	.init		= centrino_cpu_init,
	.exit		= centrino_cpu_exit,
	.verify		= centrino_verify,
	.target		= centrino_target,
	.get		= get_cur_freq,
	.attr           = centrino_attr,
	.owner		= THIS_MODULE,
};


/**
 * centrino_init - initializes the Enhanced SpeedStep CPUFreq driver
 *
 * Initializes the Enhanced SpeedStep support. Returns -ENODEV on
 * unsupported devices, -ENOENT if there's no voltage table for this
 * particular CPU model, -EINVAL on problems during initiatization,
 * and zero on success.
 *
 * This is quite picky.  Not only does the CPU have to advertise the
 * "est" flag in the cpuid capability flags, we look for a specific
 * CPU model and stepping, and we need to have the exact model name in
 * our voltage tables.  That is, be paranoid about not releasing
 * someone's valuable magic smoke.
 */
static int __init centrino_init(void)
{
	struct cpuinfo_x86 *cpu = cpu_data;

	if (!cpu_has(cpu, X86_FEATURE_EST))
		return -ENODEV;

	return cpufreq_register_driver(&centrino_driver);
}

static void __exit centrino_exit(void)
{
	cpufreq_unregister_driver(&centrino_driver);
}

MODULE_AUTHOR ("Jeremy Fitzhardinge <jeremy@goop.org>");
MODULE_DESCRIPTION ("Enhanced SpeedStep driver for Intel Pentium M processors.");
MODULE_LICENSE ("GPL");

late_initcall(centrino_init);
module_exit(centrino_exit);




Steps to reproduce:
Everytime with standard kernel and my notebook. Maybe this can be a notebook
related problem, but I think it should be solvable and adjust the
speedstep-centrino.c I have found many discussions over internet forums and I
think this could help many people.
Comment 1 Andrew Morton 2007-01-31 01:31:39 UTC
Jeremy, could you please take a look, see if we can get a patch out of this?
Comment 2 Oldrich Plchot 2007-01-31 06:14:50 UTC
Hello again,
  I think it could be ACPI related problem. Maybe some types of BIOS does not
provide that frequency tables in a correct way. I heard some people resolved
this problem by upgrading BIOS. Maybe we could chceck if we obtained correct
values and if not, we could try values coded in the speedstep-centrino.c, which
are based on the Intel datasheets.

Olda
Comment 3 Venkatesh Pallipadi 2007-01-31 11:21:58 UTC
Yes. Please check whether there are any BIOS updates (and also any special 
options inside you BIOS to enable this feature).

And try having both acpi-cpufreq and speedstep-centrino driver (preferably 
with latest base kernel). Sometimes BIOS exports information in such a way 
that acpi-cpufreq driver can be enabled on Centrino platforms.

Thanks,
Venki
Comment 4 Jeremy Fitzhardinge 2007-02-02 14:06:52 UTC
bugme-daemon@bugzilla.kernel.org wrote:
> Jeremy, could you please take a look, see if we can get a patch out of this?
>   

As others said, this is something that should be fixable by getting info
from ACPI rather than patching the driver.

    J

Comment 5 Natalie Protasevich 2007-06-15 11:17:24 UTC
Any updates on this problem? Does the CPU frequency still need to be hardcoded?
Adding ACPI contacts.
Comment 6 Oldrich Plchot 2007-06-15 14:00:53 UTC
Hello,
  latest beta version of bios for my notebook resolved this issue, but I heard that not all manufacturers provide bios updates for older models and the problem still exists. If the bios is unable to pass correct values to kernel the only way is to use the patched speedstep-centrino.c with hard-coded values. That is a bad situation for the owners of the older notebooks where this problem exists.  I wonder how FreeBSD does this, because when I tried my notebook with old BIOS with FreeBSD 7.0 Current it worked fine, maybe they are not relying on ACPI. If we rely only on ACPI and BIOS we have a problem as we are not able to push all vendors to release correct BIOS update:-( But the situeation improved at least for me... I am the lucky one.

Best regards,
             Olda
Comment 7 Natalie Protasevich 2007-06-15 14:14:12 UTC
Then I guess we can ask Intel developers if it is possible to have "generic" table that fits one and for all and not necessarily does scaling optimal way but at least gives reasonable set of values.
Any recommendations?
Thanks.
Comment 8 Renato S. Yamane 2007-06-30 19:19:24 UTC
speedstep-centrino module don't work to me too!

Can someone provide a cpufreq_frequency_table to my Pentium M 750 (1.86Ghz)?
Datasheet available in:
<http://download.intel.com/design/mobile/datashts/30526202.pdf>

Best regards,
Renato
Comment 9 Jeremy Fitzhardinge 2007-07-01 09:31:44 UTC
Subject: Re:  CPU frequency scaling(cpufreq) does not work.  speedstep-centrino.c
 problem

bugme-daemon@bugzilla.kernel.org wrote:
> Can someone provide a cpufreq_frequency_table to my Pentium M 750 (1.86Ghz)?
> Datasheet available in:
> <http://download.intel.com/design/mobile/datashts/30526202.pdf>
>   

The trouble with newer Pentium M's (Dothan) is that they have 4 voltage 
grades (similar to different speed grades), and you need 4 tables to 
match.  The tricky part is that it doesn't appear to be possible to tell 
what voltage grade your chip is without digging the table out of ACPI 
anyway.

    J
Comment 10 Renato S. Yamane 2007-07-01 10:56:59 UTC
Hi Len, can you help us?

Our problem is that speedstep-centrino don't work with some Pentium M models, as all models available in this documents below:
<http://download.intel.com/design/mobile/datashts/30526202.pdf>
<http://download.intel.com/design/mobile/datashts/30218908.pdf>

To this models is necessary use acpi_cpufreq, installing powersaved and configure parameter CPUFREQD_MODULE="acpi_cpufreq" (/etc/powersave/cpufreq)

Currently, my .config in 2.6.21.1 is to my machine with Pentium M 750 Model (1.86Ghz) is:

# CPUFreq processor drivers
#
CONFIG_X86_ACPI_CPUFREQ=m
# CONFIG_X86_POWERNOW_K6 is not set
# CONFIG_X86_POWERNOW_K7 is not set
# CONFIG_X86_POWERNOW_K8 is not set
# CONFIG_X86_GX_SUSPMOD is not set
CONFIG_X86_SPEEDSTEP_CENTRINO=y
CONFIG_X86_SPEEDSTEP_CENTRINO_TABLE=y
CONFIG_X86_SPEEDSTEP_ICH=m
# CONFIG_X86_SPEEDSTEP_SMI is not set
# CONFIG_X86_P4_CLOCKMOD is not set
# CONFIG_X86_CPUFREQ_NFORCE2 is not set
# CONFIG_X86_LONGRUN is not set
# CONFIG_X86_LONGHAUL is not set
# CONFIG_X86_E_POWERSAVER is not set

#
# shared options
#
# CONFIG_X86_ACPI_CPUFREQ_PROC_INTF is not set
CONFIG_X86_SPEEDSTEP_LIB=m
CONFIG_X86_SPEEDSTEP_RELAXED_CAP_CHECK=y

Best regards,
Renato
Comment 11 Len Brown 2007-08-22 17:51:57 UTC
acpi-cpufreq works on the system at hand,
and so I advocate that this sighting be closed as WILL_NOT_FIX.

Nobody is maintaining speedstep-centrino, and I do not advocate
that it be enhanced.
Comment 12 Leszek Tarkowski 2007-10-18 13:01:58 UTC
I have laptop (MAxdata 8100X) with dothan 1.6GHz and no luck - both speedstep-centrino and acpi-cpufreq (kernel 2.6.22 from gutsy). So there are STILL some old laptops with are in need for hardcoded freq tables (everyting was working on old ubuntu kernels with were pathed this way). Please don't ignore old machines...
Comment 13 Andrew Morton 2007-10-18 13:44:59 UTC
I asked davej about this and he said:

The only safe way to use scaling on 'dothan' cores is with ACPI.
We just have no way to tell which of the four sets of voltages
is in use.  People keep proposing patches to hardcode a set of
tables for them which "works for them", but could do *anything*
on someone elses system.
Even if we added it as a CONFIG option, I'm worried that
people will turn it on not understanding the consequences
and either plague us with crappy bug reports of flaky kernels (or worse).
(And you guarantee some distros will turn it on to
 "make hardware 'just work'")

All-round crappy situation really, we're between a rock
and a hard place, and ACPI is the best hope we've got.
Comment 14 Venkatesh Pallipadi 2007-10-18 14:08:12 UTC
*** Bug 7607 has been marked as a duplicate of this bug. ***
Comment 15 Natalie Protasevich 2008-04-04 08:45:28 UTC
Oldrich and other reporters, were you able to use acpi_cpufreq? Has the problem been resolved for you?
Thanks.
Comment 16 Renato S. Yamane 2008-04-04 08:50:02 UTC
This work fine to me:

grep -i acpi ../linux-2.6.23.16/.config

# Power management options (ACPI, APM)
CONFIG_ACPI=y
CONFIG_ACPI_SLEEP=y
CONFIG_ACPI_PROCFS=y
CONFIG_ACPI_PROC_EVENT=y
CONFIG_ACPI_AC=m
CONFIG_ACPI_BATTERY=m
CONFIG_ACPI_BUTTON=m
CONFIG_ACPI_VIDEO=m
CONFIG_ACPI_FAN=m
CONFIG_ACPI_DOCK=m
CONFIG_ACPI_BAY=m
CONFIG_ACPI_PROCESSOR=m
CONFIG_ACPI_HOTPLUG_CPU=y
CONFIG_ACPI_THERMAL=m
# CONFIG_ACPI_ASUS is not set
CONFIG_ACPI_TOSHIBA=m
CONFIG_ACPI_BLACKLIST_YEAR=0
# CONFIG_ACPI_DEBUG is not set
CONFIG_ACPI_EC=y
CONFIG_ACPI_POWER=y
CONFIG_ACPI_SYSTEM=y
CONFIG_ACPI_CONTAINER=m
CONFIG_ACPI_SBS=m
CONFIG_X86_ACPI_CPUFREQ=m
# CONFIG_X86_ACPI_CPUFREQ_PROC_INTF is not set
CONFIG_PNPACPI=y
CONFIG_THINKPAD_ACPI=m
# CONFIG_THINKPAD_ACPI_DEBUG is not set
CONFIG_THINKPAD_ACPI_BAY=y
CONFIG_BLK_DEV_IDEACPI=y
CONFIG_ATA_ACPI=y

Regards,
Renato S. Yamane
Comment 17 Oldrich Plchot 2008-04-18 01:23:20 UTC
Hello,
  since i have upgraded my notebook with a latest beta of available bios, everything started to work. From then, also acpi_cpufreq works. I think we would need info from someone whos bios still does not handle the CPU correctly. I am sorry, I can not post more relevant results.

Thanks a lot,
              Oldrich Plchot
Comment 18 Dominik Brodowski 2008-05-28 06:08:02 UTC
Can't we close this bug then, if upgrading the BIOS fixes this issue?
Comment 19 Renato S. Yamane 2008-05-28 11:07:01 UTC
I don't have any problem with Kernel available in Debian Lenny or other release >=2.6.23.16

# hwinfo --bios
01: None 00.0: 10105 BIOS
  [Created at bios.174]
  Unique ID: rdCR.lZF+r4EgHp4
  Hardware Class: bios
  BIOS Keyboard LED Status:
    Scroll Lock: off
    Num Lock: off
    Caps Lock: off
  Base Memory: 639 kB
  PnP BIOS: SST2400
  BIOS: extended read supported
  BIOS32 Service Directory Entry: 0xea7c0
  SMBIOS Version: 2.3
  BIOS Info: #0
    Vendor: "TOSHIBA"
    Version: "Version 2.00"
    Date: "02/07/2006"
    Start Address: 0xeb000
    ROM Size: 512 kB
    Features: 0x02b3000000007f1a9f90
      ISA supported
      PCI supported
      PCMCIA supported
      PnP supported
      APM supported
      BIOS flashable
      BIOS shadowing allowed
      CD boot supported
      BIOS ROM socketed
      EDD spec supported
      1.2MB NEC 9800 Japanese Floppy supported
      720kB Floppy supported
      2.88MB Floppy supported
      Print Screen supported
      8042 Keyboard Services supported
      Serial Services supported
      Printer Services supported
      CGA/Mono Video supported
      ACPI supported
      USB Legacy supported
      LS-120 boot supported
      ATAPI ZIP boot supported
      Smart Battery supported
      F12 Network boot supported

In my case, this bug can be closed.

Best regards,
Renato S. Yamane

Note You need to log in before you can comment on or make changes to this bug.