CPUFreq patch for kernel 2.4.23 [Was : Kernel 2.4.23]

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Since Alan Cox is busy with some _other_ stuffs, and the last -ac series was
the 2.4.22-ac4, and I wanted the New "Laptop_mode" I did some hacking of my
own.

So.. Here's the patch for cpufreq (taken from a site in
http://ftp.linuk.org.uk) against the vanilla 2.4.23 kernel (taken from
http://kernel.org) (why is it called vanilla.. why can't it be
rum-&-raisin?? yumm)

I've tested it on my Laptop and CPUFreq compiled in without any problems and
I'm using it. (NO extensive testing. I just made the patch like yesterday
_night_. It was a long night...)

So.. I guess now, I have the best of both...CPUfreq and Laptop_mode
support.. Yeh!!!

The diffs are made as "diff -uNr linux-2.4.23-vanilla/ linux'.. Tried
patching it using 'patch -p1 -i ../patch-2.4.23-mh5 --dry-run' with no
problems (mh5 : where mh is my name and 5, the number of iterations before I
finally got it right! Man.. That was a happy moment!)

Here it is.. For all it's glory and whatnots.. (I'm Very proud of myself my
very 1st patch and my very 1st contribution to the Linux community.. So..
let me boast it out a bit okay???)

Er.. if anyone wants this as an attachment.. Drop me a email

PS: somewhere embedded in there is an EXTRAVERSION of -dec2 (to signify when
I last compiled the kernel. Be sure to change yours. I was bored and tired
to re-do the patch to change 1 thing. :) )

& let me know if the patch busted the 2.4.23-vanilla kernel.

===================Cut Here
patch-2.4.23-mh5======================================

diff -uNr linux-2.4.23-vanilla/arch/i386/boot/setup.S
linux/arch/i386/boot/setup.S
--- linux-2.4.23-vanilla/arch/i386/boot/setup.S	2003-11-29
02:26:19.000000000 +0800
+++ linux/arch/i386/boot/setup.S	2003-12-01 20:38:29.000000000 +0800
@@ -488,6 +488,18 @@
 	movw	$0xAA, (0x1ff)			# device present
 no_psmouse:
 
+#if defined(CONFIG_X86_SPEEDSTEP_SMI) ||
defined(CONFIG_X86_SPEEDSTEP_SMI_MODULE)
+	movl	$0x0000E980, %eax		# IST Support 
+	movl	$0x47534943, %edx		# Request value
+	int	$0x15
+
+	movl	%eax, (96)
+	movl	%ebx, (100)
+	movl	%ecx, (104)
+	movl	%edx, (108)
+#endif
+
+
 #if defined(CONFIG_APM) || defined(CONFIG_APM_MODULE)
 # Then check for an APM BIOS...
 						# %ds points to the
bootsector
diff -uNr linux-2.4.23-vanilla/arch/i386/config.in linux/arch/i386/config.in
--- linux-2.4.23-vanilla/arch/i386/config.in	2003-11-29
02:26:19.000000000 +0800
+++ linux/arch/i386/config.in	2003-12-01 20:38:29.000000000 +0800
@@ -194,6 +194,33 @@
 
 bool 'Machine Check Exception' CONFIG_X86_MCE
 
+mainmenu_option next_comment
+comment 'CPU Frequency scaling'
+bool 'CPU Frequency scaling' CONFIG_CPU_FREQ
+if [ "$CONFIG_CPU_FREQ" = "y" ]; then
+   bool ' CPU frequency table helpers' CONFIG_CPU_FREQ_TABLE
+   define_bool CONFIG_CPU_FREQ_PROC_INTF y
+   comment 'CPUFreq governors'
+   bool ' Support for governing from userspace'
CONFIG_CPU_FREQ_GOV_USERSPACE
+   define_bool CONFIG_CPU_FREQ_24_API y
+   comment 'CPUFreq processor drivers'
+   dep_tristate ' AMD Mobile K6-2/K6-3 PowerNow!' CONFIG_X86_POWERNOW_K6
$CONFIG_CPU_FREQ_TABLE
+   dep_tristate ' AMD Mobile Athlon/Duron K7 PowerNow!'
CONFIG_X86_POWERNOW_K7 $CONFIG_CPU_FREQ_TABLE
+   dep_tristate ' AMD Opteron/Athlon64 PowerNow!' CONFIG_X86_POWERNOW_K8
$CONFIG_CPU_FREQ_TABLE
+   if [ "$CONFIG_MELAN" = "y" ]; then
+       dep_tristate ' AMD Elan' CONFIG_ELAN_CPUFREQ $CONFIG_CPU_FREQ_TABLE
+   fi
+   dep_tristate ' VIA Cyrix III Longhaul' CONFIG_X86_LONGHAUL
$CONFIG_CPU_FREQ_TABLE
+   dep_tristate ' Intel Speedstep (PIIX4)' CONFIG_X86_SPEEDSTEP_PIIX4
$CONFIG_CPU_FREQ_TABLE
+   dep_tristate ' Intel SpeedStep on 440BX/ZX/MX chipsets (SMI interface)'
CONFIG_X86_SPEEDSTEP_SMI  $CONFIG_CPU_FREQ_TABLE
+   dep_tristate ' Intel Speedstep (ICH)' CONFIG_X86_SPEEDSTEP_ICH
$CONFIG_CPU_FREQ_TABLE
+   dep_tristate ' Intel Pentium-M Enhanced SpeedStep'
CONFIG_X86_SPEEDSTEP_CENTRINO $CONFIG_CPU_FREQ_TABLE
+   dep_tristate ' Intel Pentium 4 clock modulation' CONFIG_X86_P4_CLOCKMOD
$CONFIG_CPU_FREQ_TABLE
+   tristate ' Transmeta LongRun' CONFIG_X86_LONGRUN
+   tristate ' Cyrix MediaGX/NatSemi Geode Suspend Modulation'
CONFIG_X86_GX_SUSPMOD
+fi
+endmenu
+
 tristate 'Toshiba Laptop support' CONFIG_TOSHIBA
 tristate 'Dell laptop support' CONFIG_I8K
 
diff -uNr linux-2.4.23-vanilla/arch/i386/kernel/elanfreq.c
linux/arch/i386/kernel/elanfreq.c
--- linux-2.4.23-vanilla/arch/i386/kernel/elanfreq.c	1970-01-01
07:30:00.000000000 +0730
+++ linux/arch/i386/kernel/elanfreq.c	2003-08-25 22:58:46.000000000 +0800
@@ -0,0 +1,286 @@
+/*
+ * 	elanfreq: 	cpufreq driver for the AMD ELAN family
+ *
+ *	(c) Copyright 2002 Robert Schwebel <r.schwebel@xxxxxxxxxxxxxx>
+ *
+ *	Parts of this code are (c) Sven Geggus <sven@xxxxxxxxxx> 
+ *
+ *      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. 
+ *
+ *	2002-02-13: - initial revision for 2.4.18-pre9 by Robert Schwebel
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/cpufreq.h>
+
+#include <asm/msr.h>
+#include <asm/timex.h>
+#include <asm/io.h>
+
+#define REG_CSCIR 0x22 		/* Chip Setup and Control Index
Register    */
+#define REG_CSCDR 0x23		/* Chip Setup and Control Data  Register
*/
+
+/* Module parameter */
+static int max_freq;
+
+struct s_elan_multiplier {
+	int clock;		/* frequency in kHz
*/
+	int val40h;		/* PMU Force Mode register
*/
+	int val80h;		/* CPU Clock Speed Register
*/
+};
+
+/*
+ * It is important that the frequencies 
+ * are listed in ascending order here!
+ */
+struct s_elan_multiplier elan_multiplier[] = {
+	{1000,	0x02,	0x18},
+	{2000,	0x02,	0x10},
+	{4000,	0x02,	0x08},
+	{8000,	0x00,	0x00},
+	{16000,	0x00,	0x02},
+	{33000,	0x00,	0x04},
+	{66000,	0x01,	0x04},
+	{99000,	0x01,	0x05}
+};
+
+static struct cpufreq_frequency_table elanfreq_table[] = {
+	{0,	1000},
+	{1,	2000},
+	{2,	4000},
+	{3,	8000},
+	{4,	16000},
+	{5,	33000},
+	{6,	66000},
+	{7,	99000},
+	{0,	CPUFREQ_TABLE_END},
+};
+
+
+/**
+ *	elanfreq_get_cpu_frequency: determine current cpu speed
+ *
+ *	Finds out at which frequency the CPU of the Elan SOC runs
+ *	at the moment. Frequencies from 1 to 33 MHz are generated 
+ *	the normal way, 66 and 99 MHz are called "Hyperspeed Mode"
+ *	and have the rest of the chip running with 33 MHz. 
+ */
+
+static unsigned int elanfreq_get_cpu_frequency(void)
+{
+        u8 clockspeed_reg;    /* Clock Speed Register */
+	
+	local_irq_disable();
+        outb_p(0x80,REG_CSCIR);
+        clockspeed_reg = inb_p(REG_CSCDR);
+	local_irq_enable();
+
+        if ((clockspeed_reg & 0xE0) == 0xE0) { return 0; }
+
+        /* Are we in CPU clock multiplied mode (66/99 MHz)? */
+        if ((clockspeed_reg & 0xE0) == 0xC0) {
+                if ((clockspeed_reg & 0x01) == 0) {
+			return 66000;
+		} else {
+			return 99000;             
+		}
+        }
+
+	/* 33 MHz is not 32 MHz... */
+	if ((clockspeed_reg & 0xE0)==0xA0)
+		return 33000;
+
+        return ((1<<((clockspeed_reg & 0xE0) >> 5)) * 1000);
+}
+
+
+/**
+ *      elanfreq_set_cpu_frequency: Change the CPU core frequency
+ * 	@cpu: cpu number
+ *	@freq: frequency in kHz
+ *
+ *      This function takes a frequency value and changes the CPU frequency

+ *	according to this. Note that the frequency has to be checked by
+ *	elanfreq_validatespeed() for correctness!
+ *	
+ *	There is no return value. 
+ */
+
+static void elanfreq_set_cpu_state (unsigned int state) {
+
+	struct cpufreq_freqs    freqs;
+
+	freqs.old = elanfreq_get_cpu_frequency();
+	freqs.new = elan_multiplier[state].clock;
+	freqs.cpu = 0; /* elanfreq.c is UP only driver */
+	
+	cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
+
+	printk(KERN_INFO "elanfreq: attempting to set frequency to %i
kHz\n",elan_multiplier[state].clock);
+
+
+	/* 
+	 * Access to the Elan's internal registers is indexed via    
+	 * 0x22: Chip Setup & Control Register Index Register (CSCI) 
+	 * 0x23: Chip Setup & Control Register Data  Register (CSCD) 
+	 *
+	 */
+
+	/* 
+	 * 0x40 is the Power Management Unit's Force Mode Register. 
+	 * Bit 6 enables Hyperspeed Mode (66/100 MHz core frequency)
+	 */
+
+	local_irq_disable();
+	outb_p(0x40,REG_CSCIR); 	/* Disable hyperspeed mode
*/
+	outb_p(0x00,REG_CSCDR);
+	local_irq_enable();		/* wait till internal pipelines and
*/
+	udelay(1000);			/* buffers have cleaned up
*/
+
+	local_irq_disable();
+
+	/* now, set the CPU clock speed register (0x80) */
+	outb_p(0x80,REG_CSCIR);
+	outb_p(elan_multiplier[state].val80h,REG_CSCDR);
+
+	/* now, the hyperspeed bit in PMU Force Mode Register (0x40) */
+	outb_p(0x40,REG_CSCIR);
+	outb_p(elan_multiplier[state].val40h,REG_CSCDR);
+	udelay(10000);
+	local_irq_enable();
+
+	cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
+};
+
+
+/**
+ *	elanfreq_validatespeed: test if frequency range is valid 
+ *
+ *	This function checks if a given frequency range in kHz is valid 
+ *      for the hardware supported by the driver. 
+ */
+
+static int elanfreq_verify (struct cpufreq_policy *policy)
+{
+	return cpufreq_frequency_table_verify(policy, &elanfreq_table[0]);
+}
+
+static int elanfreq_target (struct cpufreq_policy *policy, 
+			    unsigned int target_freq, 
+			    unsigned int relation)
+{
+	unsigned int    newstate = 0;
+
+	if (cpufreq_frequency_table_target(policy, &elanfreq_table[0],
target_freq, relation, &newstate))
+		return -EINVAL;
+
+	elanfreq_set_cpu_state(newstate);
+
+	return 0;
+}
+
+
+/*
+ *	Module init and exit code
+ */
+
+static int elanfreq_cpu_init(struct cpufreq_policy *policy)
+{
+	struct cpuinfo_x86 *c = cpu_data;
+	unsigned int i;
+
+	/* capability check */
+	if ((c->x86_vendor != X86_VENDOR_AMD) ||
+	    (c->x86 != 4) || (c->x86_model!=10))
+		return -ENODEV;
+
+	/* max freq */
+	if (!max_freq)
+		max_freq = elanfreq_get_cpu_frequency();
+
+	/* table init */
+ 	for (i=0; (elanfreq_table[i].frequency != CPUFREQ_TABLE_END); i++) {
+		if (elanfreq_table[i].frequency > max_freq)
+			elanfreq_table[i].frequency = CPUFREQ_ENTRY_INVALID;
+	}
+
+	/* cpuinfo and default policy values */
+	policy->policy = CPUFREQ_POLICY_PERFORMANCE;
+	policy->cpuinfo.transition_latency = CPUFREQ_ETERNAL;
+	policy->cur = elanfreq_get_cpu_frequency();
+
+	return cpufreq_frequency_table_cpuinfo(policy, &elanfreq_table[0]);;
+}
+
+
+#ifndef MODULE
+/**
+ * elanfreq_setup - elanfreq command line parameter parsing
+ *
+ * elanfreq command line parameter.  Use:
+ *  elanfreq=66000
+ * to set the maximum CPU frequency to 66 MHz. Note that in
+ * case you do not give this boot parameter, the maximum
+ * frequency will fall back to _current_ CPU frequency which
+ * might be lower. If you build this as a module, use the
+ * max_freq module parameter instead.
+ */
+static int __init elanfreq_setup(char *str)
+{
+	max_freq = simple_strtoul(str, &str, 0);
+	return 1;
+}
+__setup("elanfreq=", elanfreq_setup);
+#endif
+
+
+static struct cpufreq_driver elanfreq_driver = {
+	.verify 	= elanfreq_verify,
+	.target 	= elanfreq_target,
+	.init		= elanfreq_cpu_init,
+	.name		= "elanfreq",
+};
+
+
+static int __init elanfreq_init(void) 
+{	
+	struct cpuinfo_x86 *c = cpu_data;
+
+	/* Test if we have the right hardware */
+	if ((c->x86_vendor != X86_VENDOR_AMD) ||
+		(c->x86 != 4) || (c->x86_model!=10))
+	{
+		printk(KERN_INFO "elanfreq: error: no Elan processor
found!\n");
+                return -ENODEV;
+	}
+	
+	return cpufreq_register_driver(&elanfreq_driver);
+}
+
+
+static void __exit elanfreq_exit(void) 
+{
+	cpufreq_unregister_driver(&elanfreq_driver);
+}
+
+
+MODULE_PARM (max_freq, "i");
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Robert Schwebel <r.schwebel@xxxxxxxxxxxxxx>, Sven Geggus
<sven@xxxxxxxxxx>");
+MODULE_DESCRIPTION("cpufreq driver for AMD's Elan CPUs");
+
+module_init(elanfreq_init);
+module_exit(elanfreq_exit);
+
diff -uNr linux-2.4.23-vanilla/arch/i386/kernel/gx-suspmod.c
linux/arch/i386/kernel/gx-suspmod.c
--- linux-2.4.23-vanilla/arch/i386/kernel/gx-suspmod.c	1970-01-01
07:30:00.000000000 +0730
+++ linux/arch/i386/kernel/gx-suspmod.c	2003-08-25 22:58:46.000000000 +0800
@@ -0,0 +1,510 @@
+/*
+ *	Cyrix MediaGX and NatSemi Geode Suspend Modulation
+ *	(C) 2002 Zwane Mwaikambo <zwane@xxxxxxxxxxxxxxxxxxxx>
+ *	(C) 2002 Hiroshi Miura   <miura@xxxxxxxxxx>
+ *	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
+ *      version 2 as published by the Free Software Foundation 
+ *
+ *      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.
+ *	
+ * Theoritical note:
+ *
+ *	(see Geode(tm) CS5530 manual (rev.4.1) page.56)
+ *
+ *	CPU frequency control on NatSemi Geode GX1/GXLV processor and CS55x0
+ *	are based on Suspend Moduration.
+ *
+ *	Suspend Modulation works by asserting and de-asserting the SUSP# pin
+ *	to CPU(GX1/GXLV) for configurable durations. When asserting SUSP#
+ *	the CPU enters an idle state. GX1 stops its core clock when SUSP# is

+ *	asserted then power consumption is reduced.
+ *
+ *	Suspend Modulation's OFF/ON duration are configurable 
+ *	with 'Suspend Modulation OFF Count Register'
+ *	and 'Suspend Modulation ON Count Register'.
+ *	These registers are 8bit counters that represent the number of 
+ *	32us intervals which the SUSP# pin is asserted/de-asserted to the 
+ *	processor.
+ *
+ *	These counters define a ratio which is the effective frequency 
+ * 	of operation of the system.
+ *
+ *			       On Count
+ *	F_eff = Fgx * ----------------------
+ *	                On Count + Off Count
+ *
+ *	0 <= On Count, Off Count <= 255
+ *
+ *	From these limits, we can get register values 
+ *
+ *	on_duration + off_duration <= MAX_DURATION
+ *	off_duration = on_duration * (stock_freq - freq) / freq
+ *
+ *      on_duration  =  (freq * DURATION) / stock_freq 
+ *      off_duration = DURATION - on_duration 
+ *
+ *
+
*---------------------------------------------------------------------------
+ *
+ * ChangeLog:
+ *	Dec. 11, 2002 	Hiroshi Miura <miura@xxxxxxxxxx>
+ *		- rewrite for Cyrix MediaGX Cx5510/5520 and 
+ *		  NatSemi Geode Cs5530(A).
+ *
+ *	Jul. ??, 2002  Zwane Mwaikambo <zwane@xxxxxxxxxxxxxxxxxxxx>
+ *		- cs5530_mod patch for 2.4.19-rc1.
+ *
+
*---------------------------------------------------------------------------
+ *
+ * Todo
+ *	Test on machines with 5510, 5530, 5530A
+ */
+
+/************************************************************************
+ *			Suspend Modulation - Definitions		*
+ ************************************************************************/
+
+#include <linux/kernel.h>
+#include <linux/module.h> 
+#include <linux/sched.h>
+#include <linux/init.h>
+#include <linux/smp.h>
+#include <linux/cpufreq.h>
+#include <linux/pci.h>
+#include <asm/processor.h> 
+#include <asm/errno.h>
+
+/* PCI config registers, all at F0 */
+#define PCI_PMER1              0x80    /* power management enable register
1 */
+#define PCI_PMER2              0x81    /* power management enable register
2 */
+#define PCI_PMER3              0x82    /* power management enable register
3 */
+#define PCI_IRQTC              0x8c    /* irq speedup timer counter
register:typical 2 to 4ms */
+#define PCI_VIDTC              0x8d    /* video speedup timer counter
register: typical 50 to 100ms */
+#define PCI_MODOFF             0x94    /* suspend modulation OFF counter
register, 1 = 32us */
+#define PCI_MODON              0x95    /* suspend modulation ON counter
register */
+#define PCI_SUSCFG             0x96    /* suspend configuration register */
+
+/* PMER1 bits */
+#define GPM                    (1<<0)  /* global power management */
+#define GIT                    (1<<1)  /* globally enable PM device idle
timers */
+#define GTR                    (1<<2)  /* globally enable IO traps */
+#define IRQ_SPDUP              (1<<3)  /* disable clock throttle during
interrupt handling */
+#define VID_SPDUP              (1<<4)  /* disable clock throttle during vga
video handling */
+
+/* SUSCFG bits */
+#define SUSMOD                 (1<<0)  /* enable/disable suspend modulation
*/
+/* the belows support only with cs5530 (after rev.1.2)/cs5530A */ 
+#define SMISPDUP               (1<<1)  /* select how SMI re-enable suspend
modulation: */
+                                       /* IRQTC timer or read SMI speedup
disable reg.(F1BAR[08-09h]) */
+#define SUSCFG                 (1<<2)  /* enable powering down a GXLV
processor. "Special 3Volt Suspend" mode */
+/* the belows support only with cs5530A */ 
+#define PWRSVE_ISA             (1<<3)  /* stop ISA clock  */
+#define PWRSVE                 (1<<4)  /* active idle */
+
+struct gxfreq_params {
+	u8 on_duration;
+	u8 off_duration;
+	u8 pci_suscfg;
+	u8 pci_pmer1;
+	u8 pci_pmer2;
+	u8 pci_rev;
+	struct pci_dev *cs55x0;
+};
+
+static struct gxfreq_params *gx_params;
+static int stock_freq;
+
+/* PCI bus clock - defaults to 30.000 if cpu_khz is not available */
+static int pci_busclk = 0;
+MODULE_PARM(pci_busclk, "i");
+
+/* maximum duration for which the cpu may be suspended
+ * (32us * MAX_DURATION). If no parameter is given, this defaults
+ * to 255. 
+ * Note that this leads to a maximum of 8 ms(!) where the CPU clock
+ * is suspended -- processing power is just 0.39% of what it used to be,
+ * though. 781.25 kHz(!) for a 200 MHz processor -- wow. */
+static int max_duration = 255;
+MODULE_PARM(max_duration, "i");
+
+/* For the default policy, we want at least some processing power
+ * - let's say 5%. (min = maxfreq / POLICY_MIN_DIV)
+ */
+#define POLICY_MIN_DIV 20
+
+
+/* DEBUG
+ *   Define it if you want verbose debug output
+ */
+
+#define SUSPMOD_DEBUG 1
+
+#ifdef SUSPMOD_DEBUG
+#define dprintk(msg...) printk(KERN_DEBUG "cpufreq:" msg)
+#else
+#define dprintk(msg...) do { } while(0)
+#endif
+
+/**
+ *      we can detect a core multipiler from dir0_lsb 
+ *      from GX1 datasheet p.56, 
+ *	   MULT[3:0]:
+ *	   0000 = SYSCLK multiplied by 4 (test only)
+ *	   0001 = SYSCLK multiplied by 10
+ *	   0010 = SYSCLK multiplied by 4
+ *	   0011 = SYSCLK multiplied by 6
+ *	   0100 = SYSCLK multiplied by 9
+ *	   0101 = SYSCLK multiplied by 5
+ *	   0110 = SYSCLK multiplied by 7
+ *	   0111 = SYSCLK multiplied by 8
+ *              of 33.3MHz
+ **/
+static int gx_freq_mult[16] = {
+		4, 10, 4, 6, 9, 5, 7, 8,
+		0, 0, 0, 0, 0, 0, 0, 0
+};
+
+
+/****************************************************************
+ * 	Low Level chipset interface				*
+ ****************************************************************/
+static struct pci_device_id gx_chipset_tbl[] __initdata = {
+        { PCI_VENDOR_ID_CYRIX, PCI_DEVICE_ID_CYRIX_5530_LEGACY, PCI_ANY_ID,
PCI_ANY_ID },
+        { PCI_VENDOR_ID_CYRIX, PCI_DEVICE_ID_CYRIX_5520, PCI_ANY_ID,
PCI_ANY_ID },
+        { PCI_VENDOR_ID_CYRIX, PCI_DEVICE_ID_CYRIX_5510, PCI_ANY_ID,
PCI_ANY_ID },
+        { 0, },
+};
+
+/**
+ *     gx_detect_chipset:
+ *
+ **/
+static __init struct pci_dev *gx_detect_chipset(void)
+{
+	struct pci_dev *gx_pci = NULL;
+
+	/* check if CPU is a MediaGX or a Geode. */
+        if ((current_cpu_data.x86_vendor != X86_VENDOR_NSC) && 
+	    (current_cpu_data.x86_vendor != X86_VENDOR_CYRIX)) {
+		printk(KERN_INFO "gx-suspmod: error: no MediaGX/Geode
processor found!\n");
+		return NULL;		
+	}
+
+	/* detect which companion chip is used */
+	while ((gx_pci = pci_find_device(PCI_ANY_ID, PCI_ANY_ID, gx_pci)) !=
NULL) {
+		if ((pci_match_device (gx_chipset_tbl, gx_pci)) != NULL) {
+			return gx_pci;
+		}
+	}
+
+	dprintk(KERN_INFO "gx-suspmod: error: no supported chipset
found!\n");
+	return NULL;
+}
+
+/**
+ *      gx_get_cpuspeed:
+ *
+ * Finds out at which efficient frequency the Cyrix MediaGX/NatSemi Geode
CPU runs.
+ */
+static int gx_get_cpuspeed(void)
+{
+	if ((gx_params->pci_suscfg & SUSMOD) == 0) 
+		return stock_freq;
+
+	return (stock_freq * gx_params->on_duration) 
+		/ (gx_params->on_duration + gx_params->off_duration);
+}
+
+/**
+ *      gx_validate_speed:
+ *      determine current cpu speed
+ *       
+**/
+
+static unsigned int gx_validate_speed(unsigned int khz, u8 *on_duration, u8
*off_duration)
+{
+	unsigned int i;
+	u8 tmp_on, tmp_off;
+	int old_tmp_freq = stock_freq;
+	int tmp_freq;
+
+	*on_duration=1;
+	*off_duration=0;
+
+	for (i=max_duration; i>0; i--) {
+		tmp_on = ((khz * i) / stock_freq) & 0xff; 
+		tmp_off = i - tmp_on;
+		tmp_freq = (stock_freq * tmp_on) / i;
+		/* if this relation is closer to khz, use this. If it's
equal,
+		 * prefer it, too - lower latency */
+		if (abs(tmp_freq - khz) <= abs(old_tmp_freq - khz)) {
+			*on_duration = tmp_on;
+			*off_duration = tmp_off;
+			old_tmp_freq = tmp_freq;
+		}
+	}
+
+	return old_tmp_freq;
+}
+
+
+/**
+ * 	gx_set_cpuspeed:
+ *		set cpu speed in khz.
+ **/
+
+static void gx_set_cpuspeed(unsigned int khz)
+{
+        u8 suscfg, pmer1;
+	unsigned int new_khz;
+	unsigned long flags;
+	struct cpufreq_freqs freqs;
+
+
+	freqs.cpu = 0;
+	freqs.old = gx_get_cpuspeed();
+
+	new_khz = gx_validate_speed(khz, &gx_params->on_duration,
&gx_params->off_duration);
+
+	freqs.new = new_khz;
+
+	if (new_khz == stock_freq) {  /* if new khz == 100% of CPU speed, it
is special case */
+		local_irq_save(flags);
+		cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
+		pci_write_config_byte(gx_params->cs55x0, PCI_SUSCFG,
(gx_params->pci_suscfg & ~(SUSMOD)));
+		pci_read_config_byte(gx_params->cs55x0, PCI_SUSCFG,
&(gx_params->pci_suscfg));
+		local_irq_restore(flags);
+		dprintk("suspend modulation disabled: cpu runs 100 percent
speed.\n");
+		cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
+		return;
+	}
+
+	cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
+
+	local_irq_save(flags);
+	switch (gx_params->cs55x0->device) {
+	case PCI_DEVICE_ID_CYRIX_5530_LEGACY:
+		pmer1 = gx_params->pci_pmer1 | IRQ_SPDUP | VID_SPDUP;
+		/* FIXME: need to test other values -- Zwane,Miura */
+		pci_write_config_byte(gx_params->cs55x0, PCI_IRQTC, 4); /*
typical 2 to 4ms */
+		pci_write_config_byte(gx_params->cs55x0, PCI_VIDTC, 100);/*
typical 50 to 100ms */
+		pci_write_config_byte(gx_params->cs55x0, PCI_PMER1, pmer1);
+
+		if (gx_params->pci_rev < 0x10) {   /* CS5530(rev 1.2, 1.3)
*/
+			suscfg = gx_params->pci_suscfg | SUSMOD;
+		} else {                           /* CS5530A,B.. */
+			suscfg = gx_params->pci_suscfg | SUSMOD | PWRSVE;
+		}
+		break;
+	case PCI_DEVICE_ID_CYRIX_5520:
+	case PCI_DEVICE_ID_CYRIX_5510:
+		suscfg = gx_params->pci_suscfg | SUSMOD;
+		break;
+	default:
+		local_irq_restore(flags);
+		dprintk("fatal: try to set unknown chipset.\n");
+		return;
+	}
+
+	pci_write_config_byte(gx_params->cs55x0, PCI_MODOFF,
gx_params->off_duration);
+	pci_write_config_byte(gx_params->cs55x0, PCI_MODON,
gx_params->on_duration);
+
+        pci_write_config_byte(gx_params->cs55x0, PCI_SUSCFG, suscfg);
+        pci_read_config_byte(gx_params->cs55x0, PCI_SUSCFG, &suscfg);
+
+        local_irq_restore(flags);
+
+	gx_params->pci_suscfg = suscfg;
+
+	cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
+
+        dprintk("suspend modulation w/ duration of ON:%d us, OFF:%d us\n",
+                gx_params->on_duration * 32, gx_params->off_duration * 32);
+	dprintk("suspend modulation w/ clock speed: %d kHz.\n", freqs.new); 
+}
+
+/****************************************************************
+ *             High level functions                             *
+ ****************************************************************/
+
+/*
+ *	cpufreq_gx_verify: test if frequency range is valid 
+ *
+ *	This function checks if a given frequency range in kHz is valid 
+ *      for the hardware supported by the driver. 
+ */
+
+static int cpufreq_gx_verify(struct cpufreq_policy *policy)
+{
+	unsigned int tmp_freq = 0;
+	u8 tmp1, tmp2;
+
+        if (!stock_freq || !policy)
+                return -EINVAL;
+
+	policy->cpu = 0;
+	cpufreq_verify_within_limits(policy, (stock_freq / max_duration),
stock_freq);
+
+	/* it needs to be assured that at least one supported frequency is
+	 * within policy->min and policy->max. If it is not, policy->max
+	 * needs to be increased until one freuqency is supported.
+	 * policy->min may not be decreased, though. This way we guarantee a

+	 * specific processing capacity.
+	 */
+	tmp_freq = gx_validate_speed(policy->min, &tmp1, &tmp2);
+	if (tmp_freq < policy->min) 
+		tmp_freq += stock_freq / max_duration;
+	policy->min = tmp_freq;
+	if (policy->min > policy->max) 
+		policy->max = tmp_freq;
+	tmp_freq = gx_validate_speed(policy->max, &tmp1, &tmp2);
+	if (tmp_freq > policy->max)
+		tmp_freq -= stock_freq / max_duration;
+	policy->max = tmp_freq;
+	if (policy->max < policy->min)
+		policy->max = policy->min;
+	cpufreq_verify_within_limits(policy, (stock_freq / max_duration),
stock_freq);
+	
+	return 0;
+}
+
+/*
+ *      cpufreq_gx_target:  
+ *
+ */
+static int cpufreq_gx_target(struct cpufreq_policy *policy,
+			     unsigned int target_freq,
+			     unsigned int relation)
+{
+	u8 tmp1, tmp2;
+	unsigned int tmp_freq;
+
+        if (!stock_freq || !policy)
+                return -EINVAL;
+
+	policy->cpu = 0;
+
+	tmp_freq = gx_validate_speed(target_freq, &tmp1, &tmp2);
+	while (tmp_freq < policy->min) {
+		tmp_freq += stock_freq / max_duration;
+		tmp_freq = gx_validate_speed(tmp_freq, &tmp1, &tmp2);
+	}
+	while (tmp_freq > policy->max) {
+		tmp_freq -= stock_freq / max_duration;
+		tmp_freq = gx_validate_speed(tmp_freq, &tmp1, &tmp2);
+	}
+
+	gx_set_cpuspeed(tmp_freq);
+
+	return 0;
+}
+
+static int cpufreq_gx_cpu_init(struct cpufreq_policy *policy)
+{
+	int maxfreq, curfreq;
+
+	if (!policy || policy->cpu != 0)
+		return -ENODEV;
+
+	/* determine maximum frequency */
+	if (pci_busclk) {
+		maxfreq = pci_busclk * gx_freq_mult[getCx86(CX86_DIR1) &
0x0f];
+	} else if (cpu_khz) {
+		maxfreq = cpu_khz;
+	} else {
+		maxfreq = 30000 * gx_freq_mult[getCx86(CX86_DIR1) & 0x0f];
+	}
+	stock_freq = maxfreq;
+	curfreq = gx_get_cpuspeed();
+
+	dprintk("cpu max frequency is %d.\n", maxfreq);
+	dprintk("cpu current frequency is %dkHz.\n",curfreq);
+
+	/* setup basic struct for cpufreq API */
+	policy->cpu = 0;
+
+	if (max_duration < POLICY_MIN_DIV)
+		policy->min = maxfreq / max_duration;
+	else
+		policy->min = maxfreq / POLICY_MIN_DIV;
+	policy->max = maxfreq;
+	policy->cur = curfreq;
+	policy->policy = CPUFREQ_POLICY_PERFORMANCE;
+	policy->cpuinfo.min_freq = maxfreq / max_duration;
+	policy->cpuinfo.max_freq = maxfreq;
+	policy->cpuinfo.transition_latency = CPUFREQ_ETERNAL;
+
+	return 0;
+}
+
+/* 
+ * cpufreq_gx_init:
+ *   MediaGX/Geode GX initialize cpufreq driver
+ */
+static struct cpufreq_driver gx_suspmod_driver = {
+	.verify		= cpufreq_gx_verify,
+	.target		= cpufreq_gx_target,
+	.init		= cpufreq_gx_cpu_init,
+	.name		= "gx-suspmod",
+};
+
+static int __init cpufreq_gx_init(void)
+{
+	int ret;
+	struct gxfreq_params *params;
+	struct pci_dev *gx_pci;
+	u32 class_rev;
+
+	/* Test if we have the right hardware */
+	if ((gx_pci = gx_detect_chipset()) == NULL) 
+		return -ENODEV;
+
+	/* check whether module parameters are sane */
+	if (max_duration > 0xff)
+		max_duration = 0xff;
+
+	dprintk("geode suspend modulation available.\n");
+
+	params = kmalloc(sizeof(struct gxfreq_params), GFP_KERNEL);
+	if (params == NULL)
+		return -ENOMEM;
+	memset(params, 0, sizeof(struct gxfreq_params));
+
+	params->cs55x0 = gx_pci;
+	gx_params = params;
+
+	/* keep cs55x0 configurations */
+	pci_read_config_byte(params->cs55x0, PCI_SUSCFG,
&(params->pci_suscfg));
+	pci_read_config_byte(params->cs55x0, PCI_PMER1,
&(params->pci_pmer1));
+	pci_read_config_byte(params->cs55x0, PCI_PMER2,
&(params->pci_pmer2));
+	pci_read_config_byte(params->cs55x0, PCI_MODON,
&(params->on_duration));
+	pci_read_config_byte(params->cs55x0, PCI_MODOFF,
&(params->off_duration));
+        pci_read_config_dword(params->cs55x0, PCI_CLASS_REVISION,
&class_rev);
+	params->pci_rev = class_rev && 0xff;
+
+	if ((ret = cpufreq_register_driver(&gx_suspmod_driver))) { 
+		kfree(params);
+		return ret;                   /* register error! */
+	}
+
+	return 0;
+}
+
+static void __exit cpufreq_gx_exit(void)
+{
+	cpufreq_unregister_driver(&gx_suspmod_driver);
+	kfree(gx_params);
+}
+
+MODULE_AUTHOR ("Hiroshi Miura <miura@xxxxxxxxxx>");
+MODULE_DESCRIPTION ("Cpufreq driver for Cyrix MediaGX and NatSemi Geode");
+MODULE_LICENSE ("GPL");
+
+module_init(cpufreq_gx_init);
+module_exit(cpufreq_gx_exit);
+
diff -uNr linux-2.4.23-vanilla/arch/i386/kernel/i386_ksyms.c
linux/arch/i386/kernel/i386_ksyms.c
--- linux-2.4.23-vanilla/arch/i386/kernel/i386_ksyms.c	2003-11-29
02:26:19.000000000 +0800
+++ linux/arch/i386/kernel/i386_ksyms.c	2003-12-01 21:04:21.000000000 +0800
@@ -29,6 +29,7 @@
 #include <asm/pgtable.h>
 #include <asm/pgalloc.h>
 #include <asm/edd.h>
+#include <asm/ist.h>
 
 extern void dump_thread(struct pt_regs *, struct user *);
 extern spinlock_t rtc_lock;
@@ -50,6 +51,7 @@
 EXPORT_SYMBOL(drive_info);
 #endif
 
+extern unsigned long cpu_khz;
 extern unsigned long get_cmos_time(void);
 
 /* platform dependent support */
@@ -72,7 +74,9 @@
 EXPORT_SYMBOL(pm_idle);
 EXPORT_SYMBOL(pm_power_off);
 EXPORT_SYMBOL(get_cmos_time);
+EXPORT_SYMBOL(cpu_khz);
 EXPORT_SYMBOL(apm_info);
+EXPORT_SYMBOL(ist_info);
 EXPORT_SYMBOL(gdt);
 EXPORT_SYMBOL(empty_zero_page);
 
@@ -131,7 +135,9 @@
 EXPORT_SYMBOL(cpu_data);
 EXPORT_SYMBOL(kernel_flag_cacheline);
 EXPORT_SYMBOL(smp_num_cpus);
+EXPORT_SYMBOL(smp_num_siblings);
 EXPORT_SYMBOL(cpu_online_map);
+EXPORT_SYMBOL(cpu_sibling_map);
 EXPORT_SYMBOL_NOVERS(__write_lock_failed);
 EXPORT_SYMBOL_NOVERS(__read_lock_failed);
 
diff -uNr linux-2.4.23-vanilla/arch/i386/kernel/longhaul.c
linux/arch/i386/kernel/longhaul.c
--- linux-2.4.23-vanilla/arch/i386/kernel/longhaul.c	1970-01-01
07:30:00.000000000 +0730
+++ linux/arch/i386/kernel/longhaul.c	2003-09-30 20:30:04.000000000 +0800
@@ -0,0 +1,478 @@
+/*
+ *  (C) 2001-2003  Dave Jones. <davej@xxxxxxxxxxxxxxxxx>
+ *  (C) 2002  Padraig Brady. <padraig@xxxxxxxxxxxxx>
+ *
+ *  Licensed under the terms of the GNU GPL License version 2.
+ *  Based upon datasheets & sample CPUs kindly provided by VIA.
+ *
+ *  VIA have currently 3 different versions of Longhaul.
+ *
+ *  +---------------------+----------+---------------------------------+
+ *  | Marketing name      | Codename | longhaul version / features.    |
+ *  +---------------------+----------+---------------------------------+
+ *  |  Samuel/CyrixIII    | C5A      | v1 : multipliers only           |
+ *  |  Samuel2/C3         | C3E/C5B  | v1 : multiplier only            |
+ *  |  Ezra               | C5C      | v2 : multipliers & voltage      |
+ *  |  Ezra-T             | C5M      | v3 : multipliers, voltage & FSB |
+ *  |  Nehemiah           | C5N      | v3 : multipliers, voltage & FSB |
+ *  +---------------------+----------+---------------------------------+
+ *
+ *  BIG FAT DISCLAIMER: Work in progress code. Possibly *dangerous*
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h> 
+#include <linux/init.h>
+#include <linux/cpufreq.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+
+#include <asm/msr.h>
+#include <asm/timex.h>
+#include <asm/io.h>
+
+#include "longhaul.h"
+
+#define DEBUG
+
+#ifdef DEBUG
+#define dprintk(msg...) printk(msg)
+#else
+#define dprintk(msg...) do { } while(0)
+#endif
+
+#define PFX "longhaul: "
+
+static unsigned int numscales=16, numvscales;
+static int minvid, maxvid;
+static int can_scale_voltage;
+static int vrmrev;
+
+
+/* Module parameters */
+static int dont_scale_voltage;
+static unsigned int fsb;
+
+#define __hlt()     __asm__ __volatile__("hlt": : :"memory")
+
+/* Clock ratios multiplied by 10 */
+static int clock_ratio[32];
+static int eblcr_table[32];
+static int voltage_table[32];
+static unsigned int highest_speed, lowest_speed; /* kHz */
+static int longhaul_version;
+static struct cpufreq_frequency_table *longhaul_table;
+
+
+static unsigned int calc_speed (int mult, int fsb)
+{
+	return ((mult/10)*fsb) + ((mult%10)*(fsb/2));
+}
+
+
+static int longhaul_get_cpu_mult (void)
+{
+	unsigned long invalue=0,lo, hi;
+
+	rdmsr (MSR_IA32_EBL_CR_POWERON, lo, hi);
+	invalue = (lo & (1<<22|1<<23|1<<24|1<<25)) >>22;
+	if (longhaul_version==3) {
+		if (lo & (1<<27))
+			invalue+=16;
+	}
+	return eblcr_table[invalue];
+}
+
+
+/**
+ * longhaul_set_cpu_frequency()
+ * @clock_ratio_index : bitpattern of the new multiplier.
+ *
+ * Sets a new clock ratio, and -if applicable- a new Front Side Bus
+ */
+
+static void longhaul_setstate (unsigned int clock_ratio_index)
+{
+	int speed, mult;
+	struct cpufreq_freqs freqs;
+	union msr_longhaul longhaul;
+	union msr_bcr2 bcr2;
+
+	mult = clock_ratio[clock_ratio_index];
+	if (mult == -1)
+		return;
+
+	speed = calc_speed (mult, fsb);
+	if ((speed > highest_speed) || (speed < lowest_speed))
+		return;
+
+	freqs.old = calc_speed (longhaul_get_cpu_mult(), fsb);
+	freqs.new = speed;
+	freqs.cpu = 0; /* longhaul.c is UP only driver */
+
+	cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
+
+	dprintk (KERN_INFO PFX "FSB:%d Mult:%d.%dx\n", fsb,
+				mult/10, mult%10);
+
+	switch (longhaul_version) {
+	case 1:
+		rdmsrl (MSR_VIA_BCR2, bcr2.val);
+		/* Enable software clock multiplier */
+		bcr2.bits.ESOFTBF = 1;
+		bcr2.bits.CLOCKMUL = clock_ratio_index;
+		wrmsrl (MSR_VIA_BCR2, bcr2.val);
+
+		__hlt();
+
+		/* Disable software clock multiplier */
+		rdmsrl (MSR_VIA_BCR2, bcr2.val);
+		bcr2.bits.ESOFTBF = 0;
+		wrmsrl (MSR_VIA_BCR2, bcr2.val);
+		break;
+
+	/*
+	 * Longhaul v2. (Ezra [C5C])
+	 * We can scale voltage with this too, but that's currently
+	 * disabled until we come up with a decent 'match freq to voltage'
+	 * algorithm.
+	 * We also need to do the voltage/freq setting in order depending
+	 * on the direction of scaling (like we do in powernow-k7.c)
+	 */
+	case 2:
+		rdmsrl (MSR_VIA_LONGHAUL, longhaul.val);
+		longhaul.bits.SoftBusRatio = clock_ratio_index & 0xf;
+		longhaul.bits.SoftBusRatio4 = (clock_ratio_index & 0x10) >>
4;
+		longhaul.bits.EnableSoftBusRatio = 1;
+		/* We must program the revision key only with values we
+		 * know about, not blindly copy it from 0:3 */
+		longhaul.bits.RevisionKey = 1;
+		wrmsrl (MSR_VIA_LONGHAUL, longhaul.val);
+		__hlt();
+
+		break;
+
+	/*
+	 * Longhaul v3. (Ezra-T [C5M], Nehemiah [C5N])
+	 * This can also do voltage scaling, but see above.
+	 * Ezra-T was alleged to do FSB scaling too, but it never worked in
practice.
+	 */
+	case 3:
+		rdmsrl (MSR_VIA_LONGHAUL, longhaul.val);
+		longhaul.bits.SoftBusRatio = clock_ratio_index & 0xf;
+		longhaul.bits.SoftBusRatio4 = (clock_ratio_index & 0x10) >>
4;
+		longhaul.bits.EnableSoftBusRatio = 1;
+		/* We must program the revision key only with values we
+		 * know about, not blindly copy it from 0:3 */
+		longhaul.bits.RevisionKey = 3;	/* SoftVID & SoftBSEL */
+		wrmsrl(MSR_VIA_LONGHAUL, longhaul.val);
+		__hlt();
+
+		rdmsrl (MSR_VIA_LONGHAUL, longhaul.val);
+		longhaul.bits.EnableSoftBusRatio = 0;
+		longhaul.bits.RevisionKey = 3;
+		wrmsrl (MSR_VIA_LONGHAUL, longhaul.val);
+		break;
+	}
+
+	cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
+}
+
+/*
+ * Centaur decided to make life a little more tricky.
+ * Only longhaul v1 is allowed to read EBLCR BSEL[0:1].
+ * Samuel2 and above have to try and guess what the FSB is.
+ * We do this by assuming we booted at maximum multiplier, and interpolate
+ * between that value multiplied by possible FSBs and cpu_mhz which
+ * was calculated at boot time. Really ugly, but no other way to do this.
+ */
+static int _guess (int guess, int maxmult)
+{
+	int target;
+
+	target = ((maxmult/10)*guess);
+	if (maxmult%10 != 0)
+		target += (guess/2);
+	target &= ~0xf;
+	return target;
+}
+
+static int guess_fsb(int maxmult)
+{
+	int speed = (cpu_khz/1000) & ~0xf;
+	int i;
+	int speeds[3] = { 66, 100, 133 };
+
+	for (i=0; i<3; i++) {
+		if (_guess(speeds[i],maxmult) == speed)
+			return speeds[i];
+	}
+	return 0;
+}
+
+
+
+static int __init longhaul_get_ranges (void)
+{
+	unsigned long invalue;
+	unsigned int minmult=0, maxmult=0;
+	unsigned int multipliers[32]= {
+		50,30,40,100,55,35,45,95,90,70,80,60,120,75,85,65,
+		-1,110,120,-1,135,115,125,105,130,150,160,140,-1,155,-1,145
};
+	unsigned int j, k = 0;
+	union msr_longhaul longhaul;
+	unsigned long lo, hi;
+	unsigned int eblcr_fsb_table[] = { 66, 133, 100, -1 };
+
+	switch (longhaul_version) {
+	case 1:
+		/* Ugh, Longhaul v1 didn't have the min/max MSRs.
+		   Assume min=3.0x & max = whatever we booted at. */
+		minmult = 30;
+		maxmult = longhaul_get_cpu_mult();
+		rdmsr (MSR_IA32_EBL_CR_POWERON, lo, hi);
+		invalue = (lo & (1<<18|1<<19)) >>18;
+		fsb = eblcr_fsb_table[invalue];
+		break;
+
+	case 2 ... 3:
+		rdmsrl (MSR_VIA_LONGHAUL, longhaul.val);
+
+		invalue = longhaul.bits.MaxMHzBR;
+		if (longhaul.bits.MaxMHzBR4)
+			invalue += 16;
+		maxmult=multipliers[invalue];
+
+		invalue = longhaul.bits.MinMHzBR;
+		if (longhaul.bits.MinMHzBR4 == 1)
+			minmult = 30;
+		else
+			minmult = multipliers[invalue];
+
+		fsb = guess_fsb(maxmult);
+		break;
+	}
+
+	dprintk (KERN_INFO PFX "MinMult=%d.%dx MaxMult=%d.%dx\n",
+		 minmult/10, minmult%10, maxmult/10, maxmult%10);
+	highest_speed = calc_speed (maxmult, fsb);
+	lowest_speed = calc_speed (minmult,fsb);
+	dprintk (KERN_INFO PFX "FSB: %dMHz Lowestspeed=%dMHz
Highestspeed=%dMHz\n",
+		 fsb, lowest_speed, highest_speed);
+
+	longhaul_table = kmalloc((numscales + 1) * sizeof(struct
cpufreq_frequency_table), GFP_KERNEL);
+	if(!longhaul_table)
+		return -ENOMEM;
+
+	for (j=0; j < numscales; j++) {
+		unsigned int ratio;
+		ratio = clock_ratio[j];
+		if (ratio == -1)
+			continue;
+		if (ratio > maxmult || ratio < minmult)
+			continue;
+		longhaul_table[k].frequency = calc_speed (ratio, fsb);
+		longhaul_table[k].index	= (j << 8);
+		k++;
+	}
+
+	longhaul_table[k].frequency = CPUFREQ_TABLE_END;
+	if (!k) {
+		kfree (longhaul_table);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+
+static void __init longhaul_setup_voltagescaling(void)
+{
+	union msr_longhaul longhaul;
+
+	rdmsrl (MSR_VIA_LONGHAUL, longhaul.val);
+
+	if (!(longhaul.bits.RevisionID & 1))
+		return;
+
+	minvid = longhaul.bits.MinimumVID;
+	maxvid = longhaul.bits.MaximumVID;
+	vrmrev = longhaul.bits.VRMRev;
+
+	if (minvid == 0 || maxvid == 0) {
+		printk (KERN_INFO PFX "Bogus values Min:%d.%03d Max:%d.%03d.
"
+					"Voltage scaling disabled.\n",
+					minvid/1000, minvid%1000,
maxvid/1000, maxvid%1000);
+		return;
+	}
+
+	if (minvid == maxvid) {
+		printk (KERN_INFO PFX "Claims to support voltage scaling but
min & max are "
+				"both %d.%03d. Voltage scaling disabled\n",
+				maxvid/1000, maxvid%1000);
+		return;
+	}
+
+	if (vrmrev==0) {
+		dprintk (KERN_INFO PFX "VRM 8.5 : ");
+		memcpy (voltage_table, vrm85scales, sizeof(voltage_table));
+		numvscales =
(voltage_table[maxvid]-voltage_table[minvid])/25;
+	} else {
+		dprintk (KERN_INFO PFX "Mobile VRM : ");
+		memcpy (voltage_table, mobilevrmscales,
sizeof(voltage_table));
+		numvscales =
(voltage_table[maxvid]-voltage_table[minvid])/5;
+	}
+
+	/* Current voltage isn't readable at first, so we need to
+	   set it to a known value. The spec says to use maxvid */
+	longhaul.bits.RevisionKey = longhaul.bits.RevisionID;	/* FIXME:
This is bad. */
+	longhaul.bits.EnableSoftVID = 1;
+	longhaul.bits.SoftVID = maxvid;
+	wrmsrl (MSR_VIA_LONGHAUL, longhaul.val);
+
+	minvid = voltage_table[minvid];
+	maxvid = voltage_table[maxvid];
+
+	dprintk ("Min VID=%d.%03d Max VID=%d.%03d, %d possible voltage
scales\n",
+		maxvid/1000, maxvid%1000, minvid/1000, minvid%1000,
numvscales);
+
+	can_scale_voltage = 1;
+}
+
+
+static int longhaul_verify(struct cpufreq_policy *policy)
+{
+	return cpufreq_frequency_table_verify(policy, longhaul_table);
+}
+
+
+static int longhaul_target (struct cpufreq_policy *policy,
+			    unsigned int target_freq,
+			    unsigned int relation)
+{
+	unsigned int table_index = 0;
+ 	unsigned int new_clock_ratio = 0;
+
+	if (cpufreq_frequency_table_target(policy, longhaul_table,
target_freq, relation, &table_index))
+		return -EINVAL;
+
+	new_clock_ratio = longhaul_table[table_index].index & 0xFF;
+ 
+	longhaul_setstate(new_clock_ratio);
+
+	return 0;
+}
+
+static int longhaul_cpu_init (struct cpufreq_policy *policy)
+{
+	struct cpuinfo_x86 *c = cpu_data;
+	char *cpuname=NULL;
+	int ret;
+
+	switch (c->x86_model) {
+	case 6:
+		cpuname = "C3 'Samuel' [C5A]";
+		longhaul_version=1;
+		memcpy (clock_ratio, longhaul1_clock_ratio,
sizeof(longhaul1_clock_ratio));
+		memcpy (eblcr_table, samuel1_eblcr, sizeof(samuel1_eblcr));
+		break;
+
+	case 7:		/* C5B / C5C */
+		switch (c->x86_mask) {
+		case 0:
+			cpuname = "C3 'Samuel 2' [C5B]";
+			longhaul_version=1;
+			memcpy (clock_ratio, longhaul1_clock_ratio,
sizeof(longhaul1_clock_ratio));
+			memcpy (eblcr_table, samuel2_eblcr,
sizeof(samuel2_eblcr));
+			break;
+		case 1 ... 15:
+			cpuname = "C3 'Ezra' [C5C]";
+			longhaul_version=2;
+			memcpy (clock_ratio, longhaul2_clock_ratio,
sizeof(longhaul2_clock_ratio));
+			memcpy (eblcr_table, ezra_eblcr,
sizeof(ezra_eblcr));
+			break;
+		}
+		break;
+
+	case 8:
+		cpuname = "C3 'Ezra-T [C5M]";
+		longhaul_version=3;
+		numscales=32;
+		memcpy (clock_ratio, longhaul3_clock_ratio,
sizeof(longhaul3_clock_ratio));
+		memcpy (eblcr_table, c5m_eblcr, sizeof(c5m_eblcr));
+		break;
+	/*
+	case 9:
+		cpuname = "C3 'Nehemiah' [C5N]";
+		longhaul_version=3;
+		numscales=32;
+	*/
+	default:
+		cpuname = "Unknown";
+		break;
+	}
+
+	printk (KERN_INFO PFX "VIA %s CPU detected. Longhaul v%d
supported.\n",
+					cpuname, longhaul_version);
+
+	if ((longhaul_version==2 || longhaul_version==3) &&
(dont_scale_voltage==0))
+		longhaul_setup_voltagescaling();
+
+	ret = longhaul_get_ranges();
+	if (ret != 0)
+		return ret;
+
+ 	policy->policy = CPUFREQ_POLICY_PERFORMANCE;
+ 	policy->cpuinfo.transition_latency = CPUFREQ_ETERNAL;
+	policy->cur = calc_speed (longhaul_get_cpu_mult(), fsb);
+
+	return cpufreq_frequency_table_cpuinfo(policy, longhaul_table);
+}
+
+static struct cpufreq_driver longhaul_driver = {
+	.verify 	= longhaul_verify,
+	.target 	= longhaul_target,
+	.init		= longhaul_cpu_init,
+	.name		= "longhaul",
+};
+
+static int __init longhaul_init (void)
+{
+	struct cpuinfo_x86 *c = cpu_data;
+
+	if (c->x86_vendor != X86_VENDOR_CENTAUR || c->x86 != 6)
+		return -ENODEV;
+
+	switch (c->x86_model) {
+	case 6 ... 7:
+		return cpufreq_register_driver(&longhaul_driver);
+	case 8:
+		printk (KERN_INFO PFX "Ezra-T unsupported: Waiting on
updated docs "
+						"from VIA before this is
usable.\n");
+		break;
+	case 9:
+		printk (KERN_INFO PFX "Nehemiah unsupported: Waiting on
working silicon "
+						"from VIA before this is
usable.\n");
+		break;
+	default:
+		printk (KERN_INFO PFX "Unknown VIA CPU. Contact
davej@xxxxxxxxxxxxxxxxx\n");
+	}
+
+	return -ENODEV;
+}
+
+static void __exit longhaul_exit (void)
+{
+	cpufreq_unregister_driver(&longhaul_driver);
+	kfree(longhaul_table);
+}
+
+MODULE_PARM (dont_scale_voltage, "i");
+
+MODULE_AUTHOR ("Dave Jones <davej@xxxxxxxxxxxxxxxxx>");
+MODULE_DESCRIPTION ("Longhaul driver for VIA Cyrix processors.");
+MODULE_LICENSE ("GPL");
+
+module_init(longhaul_init);
+module_exit(longhaul_exit);
+
diff -uNr linux-2.4.23-vanilla/arch/i386/kernel/longhaul.h
linux/arch/i386/kernel/longhaul.h
--- linux-2.4.23-vanilla/arch/i386/kernel/longhaul.h	1970-01-01
07:30:00.000000000 +0730
+++ linux/arch/i386/kernel/longhaul.h	2003-09-30 19:52:08.000000000 +0800
@@ -0,0 +1,250 @@
+/*
+ *  longhaul.h
+ *  (C) 2003 Dave Jones.
+ *
+ *  Licensed under the terms of the GNU GPL License version 2.
+ *
+ *  VIA-specific information
+ */
+
+union msr_bcr2 {
+	struct {
+		unsigned Reseved:19,	// 18:0
+		ESOFTBF:1,		// 19
+		Reserved2:3,		// 22:20
+		CLOCKMUL:4,		// 26:23
+		Reserved3:5;		// 31:27
+	} bits;
+	unsigned long val;
+};
+
+union msr_longhaul {
+	struct {
+		unsigned RevisionID:4,	// 3:0
+		RevisionKey:4,		// 7:4
+		EnableSoftBusRatio:1,	// 8
+		EnableSoftVID:1,	// 9
+		EnableSoftBSEL:1,	// 10
+		Reserved:3,		// 11:13
+		SoftBusRatio4:1,	// 14
+		VRMRev:1,		// 15
+		SoftBusRatio:4,		// 19:16
+		SoftVID:5,		// 24:20
+		Reserved2:3,		// 27:25
+		SoftBSEL:2,		// 29:28
+		Reserved3:2,		// 31:30
+		MaxMHzBR:4,		// 35:32
+		MaximumVID:5,		// 40:36
+		MaxMHzFSB:2,		// 42:41
+		MaxMHzBR4:1,		// 43
+		Reserved4:4,		// 47:44
+		MinMHzBR:4,		// 51:48
+		MinimumVID:5,		// 56:52
+		MinMHzFSB:2,		// 58:57
+		MinMHzBR4:1,		// 59
+		Reserved5:4;		// 63:60
+	} bits;
+	unsigned long long val;
+};
+
+/*
+ * Clock ratio tables. Div/Mod by 10 to get ratio.
+ * The eblcr ones specify the ratio read from the CPU.
+ * The clock_ratio ones specify what to write to the CPU.
+ */
+
+/*
+ * VIA C3 Samuel 1  & Samuel 2 (stepping 0)
+ */
+static int __initdata longhaul1_clock_ratio[16] = {
+	-1, /* 0000 -> RESERVED */
+	30, /* 0001 ->  3.0x */
+	40, /* 0010 ->  4.0x */
+	-1, /* 0011 -> RESERVED */
+	-1, /* 0100 -> RESERVED */
+	35, /* 0101 ->  3.5x */
+	45, /* 0110 ->  4.5x */
+	55, /* 0111 ->  5.5x */
+	60, /* 1000 ->  6.0x */
+	70, /* 1001 ->  7.0x */
+	80, /* 1010 ->  8.0x */
+	50, /* 1011 ->  5.0x */
+	65, /* 1100 ->  6.5x */
+	75, /* 1101 ->  7.5x */
+	-1, /* 1110 -> RESERVED */
+	-1, /* 1111 -> RESERVED */
+};
+
+static int __initdata samuel1_eblcr[16] = {
+	50, /* 0000 -> RESERVED */
+	30, /* 0001 ->  3.0x */
+	40, /* 0010 ->  4.0x */
+	-1, /* 0011 -> RESERVED */
+	55, /* 0100 ->  5.5x */
+	35, /* 0101 ->  3.5x */
+	45, /* 0110 ->  4.5x */
+	-1, /* 0111 -> RESERVED */
+	-1, /* 1000 -> RESERVED */
+	70, /* 1001 ->  7.0x */
+	80, /* 1010 ->  8.0x */
+	60, /* 1011 ->  6.0x */
+	-1, /* 1100 -> RESERVED */
+	75, /* 1101 ->  7.5x */
+	-1, /* 1110 -> RESERVED */
+	65, /* 1111 ->  6.5x */
+};
+
+/*
+ * VIA C3 Samuel2 Stepping 1->15 & VIA C3 Ezra
+ */
+static int __initdata longhaul2_clock_ratio[16] = {
+	100, /* 0000 -> 10.0x */
+	30,  /* 0001 ->  3.0x */
+	40,  /* 0010 ->  4.0x */
+	90,  /* 0011 ->  9.0x */
+	95,  /* 0100 ->  9.5x */
+	35,  /* 0101 ->  3.5x */
+	45,  /* 0110 ->  4.5x */
+	55,  /* 0111 ->  5.5x */
+	60,  /* 1000 ->  6.0x */
+	70,  /* 1001 ->  7.0x */
+	80,  /* 1010 ->  8.0x */
+	50,  /* 1011 ->  5.0x */
+	65,  /* 1100 ->  6.5x */
+	75,  /* 1101 ->  7.5x */
+	85,  /* 1110 ->  8.5x */
+	120, /* 1111 -> 12.0x */
+};
+
+static int __initdata samuel2_eblcr[16] = {
+	50,  /* 0000 ->  5.0x */
+	30,  /* 0001 ->  3.0x */
+	40,  /* 0010 ->  4.0x */
+	100, /* 0011 -> 10.0x */
+	55,  /* 0100 ->  5.5x */
+	35,  /* 0101 ->  3.5x */
+	45,  /* 0110 ->  4.5x */
+	110, /* 0111 -> 11.0x */
+	90,  /* 1000 ->  9.0x */
+	70,  /* 1001 ->  7.0x */
+	80,  /* 1010 ->  8.0x */
+	60,  /* 1011 ->  6.0x */
+	120, /* 1100 -> 12.0x */
+	75,  /* 1101 ->  7.5x */
+	130, /* 1110 -> 13.0x */
+	65,  /* 1111 ->  6.5x */
+};
+
+static int __initdata ezra_eblcr[16] = {
+	50,  /* 0000 ->  5.0x */
+	30,  /* 0001 ->  3.0x */
+	40,  /* 0010 ->  4.0x */
+	100, /* 0011 -> 10.0x */
+	55,  /* 0100 ->  5.5x */
+	35,  /* 0101 ->  3.5x */
+	45,  /* 0110 ->  4.5x */
+	95,  /* 0111 ->  9.5x */
+	90,  /* 1000 ->  9.0x */
+	70,  /* 1001 ->  7.0x */
+	80,  /* 1010 ->  8.0x */
+	60,  /* 1011 ->  6.0x */
+	120, /* 1100 -> 12.0x */
+	75,  /* 1101 ->  7.5x */
+	85,  /* 1110 ->  8.5x */
+	65,  /* 1111 ->  6.5x */
+};
+
+/*
+ * VIA C3 (Ezra-T) [C5M].
+ */
+static int __initdata longhaul3_clock_ratio[32] = {
+	100, /* 0000 -> 10.0x */
+	30,  /* 0001 ->  3.0x */
+	40,  /* 0010 ->  4.0x */
+	90,  /* 0011 ->  9.0x */
+	95,  /* 0100 ->  9.5x */
+	35,  /* 0101 ->  3.5x */
+	45,  /* 0110 ->  4.5x */
+	55,  /* 0111 ->  5.5x */
+	60,  /* 1000 ->  6.0x */
+	70,  /* 1001 ->  7.0x */
+	80,  /* 1010 ->  8.0x */
+	50,  /* 1011 ->  5.0x */
+	65,  /* 1100 ->  6.5x */
+	75,  /* 1101 ->  7.5x */
+	85,  /* 1110 ->  8.5x */
+	120, /* 1111 ->  12.0x */
+
+	-1,  /* 0000 -> RESERVED (10.0x) */
+	110, /* 0001 -> 11.0x */
+	120, /* 0010 -> 12.0x */
+	-1,  /* 0011 -> RESERVED (9.0x)*/
+	105, /* 0100 -> 10.5x */
+	115, /* 0101 -> 11.5x */
+	125, /* 0110 -> 12.5x */
+	135, /* 0111 -> 13.5x */
+	140, /* 1000 -> 14.0x */
+	150, /* 1001 -> 15.0x */
+	160, /* 1010 -> 16.0x */
+	130, /* 1011 -> 13.0x */
+	145, /* 1100 -> 14.5x */
+	155, /* 1101 -> 15.5x */
+	-1,  /* 1110 -> RESERVED (13.0x) */
+	-1,  /* 1111 -> RESERVED (12.0x) */
+};
+
+static int __initdata c5m_eblcr[32] = {
+	50,  /* 0000 ->  5.0x */
+	30,  /* 0001 ->  3.0x */
+	40,  /* 0010 ->  4.0x */
+	100, /* 0011 -> 10.0x */
+	55,  /* 0100 ->  5.5x */
+	35,  /* 0101 ->  3.5x */
+	45,  /* 0110 ->  4.5x */
+	95,  /* 0111 ->  9.5x */
+	90,  /* 1000 ->  9.0x */
+	70,  /* 1001 ->  7.0x */
+	80,  /* 1010 ->  8.0x */
+	60,  /* 1011 ->  6.0x */
+	120, /* 1100 -> 12.0x */
+	75,  /* 1101 ->  7.5x */
+	85,  /* 1110 ->  8.5x */
+	65,  /* 1111 ->  6.5x */
+
+	-1,  /* 0000 -> RESERVED (9.0x) */
+	110, /* 0001 -> 11.0x */
+	120, /* 0010 -> 12.0x */
+	-1,  /* 0011 -> RESERVED (10.0x)*/
+	135, /* 0100 -> 13.5x */
+	115, /* 0101 -> 11.5x */
+	125, /* 0110 -> 12.5x */
+	105, /* 0111 -> 10.5x */
+	130, /* 1000 -> 13.0x */
+	150, /* 1001 -> 15.0x */
+	160, /* 1010 -> 16.0x */
+	140, /* 1011 -> 14.0x */
+	-1,  /* 1100 -> RESERVED (12.0x) */
+	155, /* 1101 -> 15.5x */
+	-1,  /* 1110 -> RESERVED (13.0x) */
+	145, /* 1111 -> 14.5x */
+};
+
+
+/* 
+ * Voltage scales. Div/Mod by 1000 to get actual voltage.
+ * Which scale to use depends on the VRM type in use.
+ */
+static int __initdata vrm85scales[32] = {
+	1250, 1200, 1150, 1100, 1050, 1800, 1750, 1700,
+	1650, 1600, 1550, 1500, 1450, 1400, 1350, 1300,
+	1275, 1225, 1175, 1125, 1075, 1825, 1775, 1725,
+	1675, 1625, 1575, 1525, 1475, 1425, 1375, 1325,
+};
+
+static int __initdata mobilevrmscales[32] = {
+	2000, 1950, 1900, 1850, 1800, 1750, 1700, 1650,
+	1600, 1550, 1500, 1450, 1500, 1350, 1300, -1,
+	1275, 1250, 1225, 1200, 1175, 1150, 1125, 1100,
+	1075, 1050, 1025, 1000, 975, 950, 925, -1,
+};
+
diff -uNr linux-2.4.23-vanilla/arch/i386/kernel/longrun.c
linux/arch/i386/kernel/longrun.c
--- linux-2.4.23-vanilla/arch/i386/kernel/longrun.c	1970-01-01
07:30:00.000000000 +0730
+++ linux/arch/i386/kernel/longrun.c	2003-08-25 22:58:48.000000000 +0800
@@ -0,0 +1,284 @@
+/*
+ * (C) 2002 - 2003  Dominik Brodowski <linux@xxxxxxxx>
+ *
+ *  Licensed under the terms of the GNU GPL License version 2.
+ *
+ *  BIG FAT DISCLAIMER: Work in progress code. Possibly *dangerous*
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h> 
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/cpufreq.h>
+
+#include <asm/msr.h>
+#include <asm/processor.h>
+#include <asm/timex.h>
+
+static struct cpufreq_driver	longrun_driver;
+
+/**
+ * longrun_{low,high}_freq is needed for the conversion of cpufreq kHz 
+ * values into per cent values. In TMTA microcode, the following is valid:
+ * performance_pctg = (current_freq - low_freq)/(high_freq - low_freq)
+ */
+static unsigned int longrun_low_freq, longrun_high_freq;
+
+
+/**
+ * longrun_get_policy - get the current LongRun policy
+ * @policy: struct cpufreq_policy where current policy is written into
+ *
+ * Reads the current LongRun policy by access to MSR_TMTA_LONGRUN_FLAGS
+ * and MSR_TMTA_LONGRUN_CTRL
+ */
+static void longrun_get_policy(struct cpufreq_policy *policy)
+{
+	u32 msr_lo, msr_hi;
+
+	rdmsr(MSR_TMTA_LONGRUN_FLAGS, msr_lo, msr_hi);
+	if (msr_lo & 0x01)
+		policy->policy = CPUFREQ_POLICY_PERFORMANCE;
+	else
+		policy->policy = CPUFREQ_POLICY_POWERSAVE;
+	
+	rdmsr(MSR_TMTA_LONGRUN_CTRL, msr_lo, msr_hi);
+	msr_lo &= 0x0000007F;
+	msr_hi &= 0x0000007F;
+
+	policy->min = longrun_low_freq + msr_lo * 
+		((longrun_high_freq - longrun_low_freq) / 100);
+	policy->max = longrun_low_freq + msr_hi * 
+		((longrun_high_freq - longrun_low_freq) / 100);
+	policy->cpu = 0;
+}
+
+
+/**
+ * longrun_set_policy - sets a new CPUFreq policy
+ * @policy - new policy
+ *
+ * Sets a new CPUFreq policy on LongRun-capable processors. This function
+ * has to be called with cpufreq_driver locked.
+ */
+static int longrun_set_policy(struct cpufreq_policy *policy)
+{
+	u32 msr_lo, msr_hi;
+	u32 pctg_lo, pctg_hi;
+
+	if (!policy)
+		return -EINVAL;
+
+	pctg_lo = (policy->min - longrun_low_freq) / 
+		((longrun_high_freq - longrun_low_freq) / 100);
+	pctg_hi = (policy->max - longrun_low_freq) / 
+		((longrun_high_freq - longrun_low_freq) / 100);
+
+	if (pctg_hi > 100)
+		pctg_hi = 100;
+	if (pctg_lo > pctg_hi)
+		pctg_lo = pctg_hi;
+
+	/* performance or economy mode */
+	rdmsr(MSR_TMTA_LONGRUN_FLAGS, msr_lo, msr_hi);
+	msr_lo &= 0xFFFFFFFE;
+	switch (policy->policy) {
+	case CPUFREQ_POLICY_PERFORMANCE:
+		msr_lo |= 0x00000001;
+		break;
+	case CPUFREQ_POLICY_POWERSAVE:
+		break;
+	}
+	wrmsr(MSR_TMTA_LONGRUN_FLAGS, msr_lo, msr_hi);
+
+	/* lower and upper boundary */
+	rdmsr(MSR_TMTA_LONGRUN_CTRL, msr_lo, msr_hi);
+	msr_lo &= 0xFFFFFF80;
+	msr_hi &= 0xFFFFFF80;
+	msr_lo |= pctg_lo;
+	msr_hi |= pctg_hi;
+	wrmsr(MSR_TMTA_LONGRUN_CTRL, msr_lo, msr_hi);
+
+	return 0;
+}
+
+
+/**
+ * longrun_verify_poliy - verifies a new CPUFreq policy
+ *
+ * Validates a new CPUFreq policy. This function has to be called with 
+ * cpufreq_driver locked.
+ */
+static int longrun_verify_policy(struct cpufreq_policy *policy)
+{
+	if (!policy)
+		return -EINVAL;
+
+	policy->cpu = 0;
+	cpufreq_verify_within_limits(policy, 
+		policy->cpuinfo.min_freq, 
+		policy->cpuinfo.max_freq);
+
+	if (policy->policy == CPUFREQ_POLICY_GOVERNOR)
+		return -EINVAL;
+
+	return 0;
+}
+
+
+/**
+ * longrun_determine_freqs - determines the lowest and highest possible
core frequency
+ *
+ * Determines the lowest and highest possible core frequencies on this CPU.
+ * This is necessary to calculate the performance percentage according to
+ * TMTA rules:
+ * performance_pctg = (target_freq - low_freq)/(high_freq - low_freq)
+ */
+static unsigned int __init longrun_determine_freqs(unsigned int *low_freq, 
+						   unsigned int *high_freq)
+{
+	u32 msr_lo, msr_hi;
+	u32 save_lo, save_hi;
+	u32 eax, ebx, ecx, edx;
+	struct cpuinfo_x86 *c = cpu_data;
+
+	if (!low_freq || !high_freq)
+		return -EINVAL;
+
+	if (cpu_has(c, X86_FEATURE_LRTI)) {
+		/* if the LongRun Table Interface is present, the
+		 * detection is a bit easier: 
+		 * For minimum frequency, read out the maximum
+		 * level (msr_hi), write that into "currently 
+		 * selected level", and read out the frequency.
+		 * For maximum frequency, read out level zero.
+		 */
+		/* minimum */
+		rdmsr(MSR_TMTA_LRTI_READOUT, msr_lo, msr_hi);
+		wrmsr(MSR_TMTA_LRTI_READOUT, msr_hi, msr_hi);
+		rdmsr(MSR_TMTA_LRTI_VOLT_MHZ, msr_lo, msr_hi);
+		*low_freq = msr_lo * 1000; /* to kHz */
+
+		/* maximum */
+		wrmsr(MSR_TMTA_LRTI_READOUT, 0, msr_hi);
+		rdmsr(MSR_TMTA_LRTI_VOLT_MHZ, msr_lo, msr_hi);
+		*high_freq = msr_lo * 1000; /* to kHz */
+
+		if (*low_freq > *high_freq)
+			*low_freq = *high_freq;
+		return 0;
+	}
+
+	/* set the upper border to the value determined during TSC init */
+	*high_freq = (cpu_khz / 1000);
+	*high_freq = *high_freq * 1000;
+
+	/* get current borders */
+	rdmsr(MSR_TMTA_LONGRUN_CTRL, msr_lo, msr_hi);
+	save_lo = msr_lo & 0x0000007F;
+	save_hi = msr_hi & 0x0000007F;
+
+	/* if current perf_pctg is larger than 90%, we need to decrease the
+	 * upper limit to make the calculation more accurate.
+	 */
+	cpuid(0x80860007, &eax, &ebx, &ecx, &edx);
+	if (ecx > 90) {
+		/* set to 0 to 80 perf_pctg */
+		msr_lo &= 0xFFFFFF80;
+		msr_hi &= 0xFFFFFF80;
+		msr_lo |= 0;
+		msr_hi |= 80;
+		wrmsr(MSR_TMTA_LONGRUN_CTRL, msr_lo, msr_hi);
+
+		/* read out current core MHz and current perf_pctg */
+		cpuid(0x80860007, &eax, &ebx, &ecx, &edx);
+
+		/* restore values */
+		wrmsr(MSR_TMTA_LONGRUN_CTRL, save_lo, save_hi);	
+	}
+
+	/* performance_pctg = (current_freq - low_freq)/(high_freq -
low_freq)
+	 * eqals
+	 * low_freq * ( 1 - perf_pctg) = (cur_freq - high_freq * perf_pctg)
+	 *
+	 * high_freq * perf_pctg is stored tempoarily into "ebx".
+	 */
+	ebx = (((cpu_khz / 1000) * ecx) / 100); /* to MHz */
+
+	if ((ecx > 95) || (ecx == 0) || (eax < ebx))
+		return -EIO;
+
+	edx = (eax - ebx) / (100 - ecx); 
+	*low_freq = edx * 1000; /* back to kHz */
+
+	if (*low_freq > *high_freq)
+		*low_freq = *high_freq;
+
+	return 0;
+}
+
+
+static int longrun_cpu_init(struct cpufreq_policy *policy)
+{
+	int                     result = 0;
+
+	/* capability check */
+	if (policy->cpu != 0)
+		return -ENODEV;
+
+	/* detect low and high frequency */
+	result = longrun_determine_freqs(&longrun_low_freq,
&longrun_high_freq);
+	if (result)
+		return result;
+
+	/* cpuinfo and default policy values */
+	policy->cpuinfo.min_freq = longrun_low_freq;
+	policy->cpuinfo.max_freq = longrun_high_freq;
+	policy->cpuinfo.transition_latency = CPUFREQ_ETERNAL;
+	longrun_get_policy(policy);
+	
+	return 0;
+}
+
+
+static struct cpufreq_driver longrun_driver = {
+	.verify 	= longrun_verify_policy,
+	.setpolicy 	= longrun_set_policy,
+	.init		= longrun_cpu_init,
+	.name		= "longrun",
+};
+
+
+/**
+ * longrun_init - initializes the Transmeta Crusoe LongRun CPUFreq driver
+ *
+ * Initializes the LongRun support.
+ */
+static int __init longrun_init(void)
+{
+	struct cpuinfo_x86 *c = cpu_data;
+
+	if (c->x86_vendor != X86_VENDOR_TRANSMETA || 
+	    !cpu_has(c, X86_FEATURE_LONGRUN))
+		return -ENODEV;
+
+	return cpufreq_register_driver(&longrun_driver);
+}
+
+
+/**
+ * longrun_exit - unregisters LongRun support
+ */
+static void __exit longrun_exit(void)
+{
+	cpufreq_unregister_driver(&longrun_driver);
+}
+
+
+MODULE_AUTHOR ("Dominik Brodowski <linux@xxxxxxxx>");
+MODULE_DESCRIPTION ("LongRun driver for Transmeta Crusoe processors.");
+MODULE_LICENSE ("GPL");
+
+module_init(longrun_init);
+module_exit(longrun_exit);
diff -uNr linux-2.4.23-vanilla/arch/i386/kernel/Makefile
linux/arch/i386/kernel/Makefile
--- linux-2.4.23-vanilla/arch/i386/kernel/Makefile	2003-11-29
02:26:19.000000000 +0800
+++ linux/arch/i386/kernel/Makefile	2003-12-01 23:37:33.000000000 +0800
@@ -14,7 +14,8 @@
 
 O_TARGET := kernel.o
 
-export-objs     := mca.o mtrr.o msr.o cpuid.o microcode.o i386_ksyms.o
time.o setup.o
+export-objs     := mca.o mtrr.o msr.o cpuid.o microcode.o i386_ksyms.o
time.o setup.o \
+       setup.o speedstep-lib.o
 
 obj-y	:= process.o semaphore.o signal.o entry.o traps.o irq.o vm86.o \
 		ptrace.o i8259.o ioport.o ldt.o setup.o time.o sys_i386.o \
@@ -43,5 +44,17 @@
 obj-$(CONFIG_X86_IO_APIC)	+= io_apic.o
 obj-$(CONFIG_X86_VISWS_APIC)	+= visws_apic.o
 obj-$(CONFIG_EDD)             	+= edd.o
+obj-$(CONFIG_X86_POWERNOW_K6)  += powernow-k6.o
+obj-$(CONFIG_X86_POWERNOW_K7)  += powernow-k7.o
+obj-$(CONFIG_X86_POWERNOW_K8)  += powernow-k8.o
+obj-$(CONFIG_X86_LONGHAUL) += longhaul.o
+obj-$(CONFIG_X86_SPEEDSTEP_CENTRINO)   += speedstep-centrino.o
+obj-$(CONFIG_X86_SPEEDSTEP_PIIX4)  += speedstep-piix4.o speedstep-lib.o
+obj-$(CONFIG_X86_SPEEDSTEP_SMI)    += speedstep-smi.o speedstep-lib.o
+obj-$(CONFIG_X86_SPEEDSTEP_ICH)    += speedstep-ich.o speedstep-lib.o
+obj-$(CONFIG_X86_P4_CLOCKMOD)  += p4-clockmod.o
+obj-$(CONFIG_ELAN_CPUFREQ) += elanfreq.o
+obj-$(CONFIG_X86_LONGRUN)  += longrun.o  
+obj-$(CONFIG_X86_GX_SUSPMOD)   += gx-suspmod.o
 
 include $(TOPDIR)/Rules.make
diff -uNr linux-2.4.23-vanilla/arch/i386/kernel/p4-clockmod.c
linux/arch/i386/kernel/p4-clockmod.c
--- linux-2.4.23-vanilla/arch/i386/kernel/p4-clockmod.c	1970-01-01
07:30:00.000000000 +0730
+++ linux/arch/i386/kernel/p4-clockmod.c	2003-08-25
22:58:48.000000000 +0800
@@ -0,0 +1,271 @@
+/*
+ *	Pentium 4/Xeon CPU on demand clock modulation/speed scaling
+ *	(C) 2002 - 2003 Dominik Brodowski <linux@xxxxxxxx>
+ *	(C) 2002 Zwane Mwaikambo <zwane@xxxxxxxxxxxxxxxxxxxx>
+ *	(C) 2002 Arjan van de Ven <arjanv@xxxxxxxxxx>
+ *	(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.
+ *	
+ *	Date		Errata			Description
+ *	20020525	N44, O17	12.5% or 25% DC causes lockup
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h> 
+#include <linux/init.h>
+#include <linux/smp.h>
+#include <linux/cpufreq.h>
+#include <linux/slab.h>
+#include <linux/sched.h>
+
+#include <asm/processor.h> 
+#include <asm/msr.h>
+#include <asm/timex.h>
+
+#define PFX	"cpufreq: "
+
+/*
+ * 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 int stock_freq;
+
+
+static int cpufreq_p4_setdc(unsigned int cpu, unsigned int newstate)
+{
+	u32 l, h;
+	unsigned long cpus_allowed;
+	struct cpufreq_freqs freqs;
+	int hyperthreading = 0;
+	int affected_cpu_map = 0;
+	int sibling = 0;
+
+	if (!cpu_online(cpu) || (newstate > DC_DISABLE) || 
+		(newstate == DC_RESV))
+		return -EINVAL;
+
+	/* switch to physical CPU where state is to be changed*/
+	cpus_allowed = current->cpus_allowed;
+
+	/* only run on CPU to be set, or on its sibling */
+	affected_cpu_map = 1 << cpu;
+#ifdef CONFIG_X86_HT
+	hyperthreading = ((cpu_has_ht) && (smp_num_siblings == 2));
+	if (hyperthreading) {
+		sibling = cpu_sibling_map[cpu];
+		affected_cpu_map |= (1 << sibling);
+	}
+#endif
+	set_cpus_allowed(current, affected_cpu_map);
+	BUG_ON(!(affected_cpu_map & (1 << smp_processor_id())));
+
+	/* get current state */
+	rdmsr(MSR_IA32_THERM_CONTROL, l, h);
+	if (l & 0x10) {
+		l = l >> 1;
+		l &= 0x7;
+	} else
+		l = DC_DISABLE;
+	
+	if (l == newstate) {
+		set_cpus_allowed(current, cpus_allowed);
+		return 0;
+	} else if (l == DC_RESV) {
+		printk(KERN_ERR PFX "BIG FAT WARNING: currently in invalid
setting\n");
+	}
+
+	/* notifiers */
+	freqs.old = stock_freq * l / 8;
+	freqs.new = stock_freq * newstate / 8;
+	freqs.cpu = cpu;
+	cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
+	if (hyperthreading) {
+		freqs.cpu = sibling;
+		cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
+	}
+
+	rdmsr(MSR_IA32_THERM_STATUS, l, h);
+#if 0
+	if (l & 0x01)
+		printk(KERN_DEBUG PFX "CPU#%d currently thermal
throttled\n", cpu);
+#endif
+	if (has_N44_O17_errata[cpu] && (newstate == DC_25PT || newstate ==
DC_DFLT))
+		newstate = DC_38PT;
+
+	rdmsr(MSR_IA32_THERM_CONTROL, l, h);
+	if (newstate == DC_DISABLE) {
+		/* printk(KERN_INFO PFX "CPU#%d disabling modulation\n",
cpu); */
+		wrmsr(MSR_IA32_THERM_CONTROL, l & ~(1<<4), h);
+	} else {
+		/* printk(KERN_INFO PFX "CPU#%d setting duty cycle to
%d%%\n",
+			cpu, ((125 * newstate) / 10)); */
+		/* bits 63 - 5	: reserved 
+		 * bit  4	: enable/disable
+		 * bits 3-1	: duty cycle
+		 * bit  0	: reserved
+		 */
+		l = (l & ~14);
+		l = l | (1<<4) | ((newstate & 0x7)<<1);
+		wrmsr(MSR_IA32_THERM_CONTROL, l, h);
+	}
+
+	set_cpus_allowed(current, cpus_allowed);
+
+	/* notifiers */
+	cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
+	if (hyperthreading) {
+		freqs.cpu = cpu;
+		cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
+	}
+
+	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;
+
+	if (cpufreq_frequency_table_target(policy, &p4clockmod_table[0],
target_freq, relation, &newstate))
+		return -EINVAL;
+
+	cpufreq_p4_setdc(policy->cpu, p4clockmod_table[newstate].index);
+
+	return 0;
+}
+
+
+static int cpufreq_p4_verify(struct cpufreq_policy *policy)
+{
+	return cpufreq_frequency_table_verify(policy, &p4clockmod_table[0]);
+}
+
+
+static int cpufreq_p4_cpu_init(struct cpufreq_policy *policy)
+{
+	struct cpuinfo_x86 *c = &cpu_data[policy->cpu];
+	int cpuid = 0;
+	unsigned int i;
+
+	/* 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;
+	}
+	
+	/* get frequency */
+	if (!stock_freq) {
+		if (cpu_khz)
+			stock_freq = cpu_khz;
+		else {
+			printk(KERN_INFO PFX "unknown core frequency -
please use module parameter 'stock_freq'\n");
+			return -EINVAL;
+		}
+	}
+
+	/* table init */
+	for (i=1; (p4clockmod_table[i].frequency != CPUFREQ_TABLE_END); i++)
{
+		if ((i<2) && (has_N44_O17_errata[policy->cpu]))
+			p4clockmod_table[i].frequency =
CPUFREQ_ENTRY_INVALID;
+		else
+			p4clockmod_table[i].frequency = (stock_freq * i)/8;
+	}
+	
+	/* cpuinfo and default policy values */
+	policy->policy = CPUFREQ_POLICY_PERFORMANCE;
+	policy->cpuinfo.transition_latency = 1000;
+	policy->cur = stock_freq;
+
+	return cpufreq_frequency_table_cpuinfo(policy,
&p4clockmod_table[0]);
+}
+
+
+static int cpufreq_p4_cpu_exit(struct cpufreq_policy *policy)
+{
+	return cpufreq_p4_setdc(policy->cpu, DC_DISABLE);
+}
+
+static struct cpufreq_driver p4clockmod_driver = {
+	.verify 	= cpufreq_p4_verify,
+	.target		= cpufreq_p4_target,
+	.init		= cpufreq_p4_cpu_init,
+	.exit		= cpufreq_p4_cpu_exit,
+	.name		= "p4-clockmod",
+};
+
+
+static int __init cpufreq_p4_init(void)
+{	
+	struct cpuinfo_x86 *c = cpu_data;
+
+	/*
+	 * THERM_CONTROL is architectural for IA32 now, so 
+	 * we can rely on the capability checks
+	 */
+	if (c->x86_vendor != X86_VENDOR_INTEL)
+		return -ENODEV;
+
+	if (!test_bit(X86_FEATURE_ACPI, c->x86_capability) ||
+		!test_bit(X86_FEATURE_ACC, c->x86_capability))
+		return -ENODEV;
+
+	printk(KERN_INFO PFX "P4/Xeon(TM) CPU On-Demand Clock Modulation
available\n");
+
+	return cpufreq_register_driver(&p4clockmod_driver);
+}
+
+
+static void __exit cpufreq_p4_exit(void)
+{
+	cpufreq_unregister_driver(&p4clockmod_driver);
+}
+
+
+MODULE_PARM(stock_freq, "i");
+
+MODULE_AUTHOR ("Zwane Mwaikambo <zwane@xxxxxxxxxxxxxxxxxxxx>");
+MODULE_DESCRIPTION ("cpufreq driver for Pentium(TM) 4/Xeon(TM)");
+MODULE_LICENSE ("GPL");
+
+module_init(cpufreq_p4_init);
+module_exit(cpufreq_p4_exit);
+
diff -uNr linux-2.4.23-vanilla/arch/i386/kernel/powernow-k6.c
linux/arch/i386/kernel/powernow-k6.c
--- linux-2.4.23-vanilla/arch/i386/kernel/powernow-k6.c	1970-01-01
07:30:00.000000000 +0730
+++ linux/arch/i386/kernel/powernow-k6.c	2003-08-25
22:58:48.000000000 +0800
@@ -0,0 +1,234 @@
+/*
+ *  This file was based upon code in Powertweak Linux
(http://powertweak.sf.net)
+ *  (C) 2000-2003  Dave Jones, Arjan van de Ven, Janne Pankala, Dominik
Brodowski.
+ *
+ *  Licensed under the terms of the GNU GPL License version 2.
+ *
+ *  BIG FAT DISCLAIMER: Work in progress code. Possibly *dangerous*
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h> 
+#include <linux/init.h>
+#include <linux/cpufreq.h>
+#include <linux/ioport.h>
+#include <linux/slab.h>
+
+#include <asm/msr.h>
+#include <asm/timex.h>
+#include <asm/io.h>
+
+
+#define POWERNOW_IOPORT 0xfff0         /* it doesn't matter where, as long
+					  as it is unused */
+
+static unsigned int                     busfreq;   /* FSB, in 10 kHz */
+static unsigned int                     max_multiplier;
+
+
+/* Clock ratio multiplied by 10 - see table 27 in AMD#23446 */
+static struct cpufreq_frequency_table clock_ratio[] = {
+	{45,  /* 000 -> 4.5x */ 0},
+	{50,  /* 001 -> 5.0x */ 0},
+	{40,  /* 010 -> 4.0x */ 0},
+	{55,  /* 011 -> 5.5x */ 0},
+	{20,  /* 100 -> 2.0x */ 0},
+	{30,  /* 101 -> 3.0x */ 0},
+	{60,  /* 110 -> 6.0x */ 0},
+	{35,  /* 111 -> 3.5x */ 0},
+	{0, CPUFREQ_TABLE_END}
+};
+
+
+/**
+ * powernow_k6_get_cpu_multiplier - returns the current FSB multiplier
+ *
+ *   Returns the current setting of the frequency multiplier. Core clock
+ * speed is frequency of the Front-Side Bus multiplied with this value.
+ */
+static int powernow_k6_get_cpu_multiplier(void)
+{
+	u64             invalue = 0;
+	u32             msrval;
+	
+	msrval = POWERNOW_IOPORT + 0x1;
+	wrmsr(MSR_K6_EPMR, msrval, 0); /* enable the PowerNow port */
+	invalue=inl(POWERNOW_IOPORT + 0x8);
+	msrval = POWERNOW_IOPORT + 0x0;
+	wrmsr(MSR_K6_EPMR, msrval, 0); /* disable it again */
+
+	return clock_ratio[(invalue >> 5)&7].index;
+}
+
+
+/**
+ * powernow_k6_set_state - set the PowerNow! multiplier
+ * @best_i: clock_ratio[best_i] is the target multiplier
+ *
+ *   Tries to change the PowerNow! multiplier
+ */
+static void powernow_k6_set_state (unsigned int best_i)
+{
+	unsigned long           outvalue=0, invalue=0;
+	unsigned long           msrval;
+	struct cpufreq_freqs    freqs;
+
+	if (clock_ratio[best_i].index > max_multiplier) {
+		printk(KERN_ERR "cpufreq: invalid target frequency\n");
+		return;
+	}
+
+	freqs.old = busfreq * powernow_k6_get_cpu_multiplier();
+	freqs.new = busfreq * clock_ratio[best_i].index;
+	freqs.cpu = 0; /* powernow-k6.c is UP only driver */
+	
+	cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
+
+	/* we now need to transform best_i to the BVC format, see AMD#23446
*/
+
+	outvalue = (1<<12) | (1<<10) | (1<<9) | (best_i<<5);
+
+	msrval = POWERNOW_IOPORT + 0x1;
+	wrmsr(MSR_K6_EPMR, msrval, 0); /* enable the PowerNow port */
+	invalue=inl(POWERNOW_IOPORT + 0x8);
+	invalue = invalue & 0xf;
+	outvalue = outvalue | invalue;
+	outl(outvalue ,(POWERNOW_IOPORT + 0x8));
+	msrval = POWERNOW_IOPORT + 0x0;
+	wrmsr(MSR_K6_EPMR, msrval, 0); /* disable it again */
+
+	cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
+
+	return;
+}
+
+
+/**
+ * powernow_k6_verify - verifies a new CPUfreq policy
+ * @policy: new policy
+ *
+ * Policy must be within lowest and highest possible CPU Frequency,
+ * and at least one possible state must be within min and max.
+ */
+static int powernow_k6_verify(struct cpufreq_policy *policy)
+{
+	return cpufreq_frequency_table_verify(policy, &clock_ratio[0]);
+}
+
+
+/**
+ * powernow_k6_setpolicy - sets a new CPUFreq policy
+ * @policy - new policy
+ *
+ * sets a new CPUFreq policy
+ */
+static int powernow_k6_target (struct cpufreq_policy *policy,
+			       unsigned int target_freq,
+			       unsigned int relation)
+{
+	unsigned int    newstate = 0;
+
+	if (cpufreq_frequency_table_target(policy, &clock_ratio[0],
target_freq, relation, &newstate))
+		return -EINVAL;
+
+	powernow_k6_set_state(newstate);
+
+	return 0;
+}
+
+
+static int powernow_k6_cpu_init(struct cpufreq_policy *policy)
+{
+	unsigned int i;
+
+	if (policy->cpu != 0)
+		return -ENODEV;
+
+	/* get frequencies */
+	max_multiplier = powernow_k6_get_cpu_multiplier();
+	busfreq = cpu_khz / max_multiplier;
+
+	/* table init */
+ 	for (i=0; (clock_ratio[i].frequency != CPUFREQ_TABLE_END); i++) {
+		if (clock_ratio[i].index > max_multiplier)
+			clock_ratio[i].frequency = CPUFREQ_ENTRY_INVALID;
+		else
+			clock_ratio[i].frequency = busfreq *
clock_ratio[i].index;
+	}
+
+	/* cpuinfo and default policy values */
+	policy->policy = CPUFREQ_POLICY_PERFORMANCE;
+	policy->cpuinfo.transition_latency = CPUFREQ_ETERNAL;
+	policy->cur = busfreq * max_multiplier;
+
+	return cpufreq_frequency_table_cpuinfo(policy, &clock_ratio[0]);
+}
+
+
+static int powernow_k6_cpu_exit(struct cpufreq_policy *policy)
+{
+	unsigned int i;
+	for (i=0; i<8; i++) {
+		if (i==max_multiplier)
+			powernow_k6_set_state(i);
+	}
+	return 0;
+}
+
+
+static struct cpufreq_driver powernow_k6_driver = {
+	.verify 	= powernow_k6_verify,
+	.target 	= powernow_k6_target,
+	.init		= powernow_k6_cpu_init,
+	.exit		= powernow_k6_cpu_exit,
+	.name		= "powernow-k6",
+};
+
+
+/**
+ * powernow_k6_init - initializes the k6 PowerNow! CPUFreq driver
+ *
+ *   Initializes the K6 PowerNow! support. Returns -ENODEV on unsupported
+ * devices, -EINVAL or -ENOMEM on problems during initiatization, and zero
+ * on success.
+ */
+static int __init powernow_k6_init(void)
+{	
+	struct cpuinfo_x86      *c = cpu_data;
+
+	if ((c->x86_vendor != X86_VENDOR_AMD) || (c->x86 != 5) ||
+		((c->x86_model != 12) && (c->x86_model != 13)))
+		return -ENODEV;
+
+	if (!request_region(POWERNOW_IOPORT, 16, "PowerNow!")) {
+		printk("cpufreq: PowerNow IOPORT region already used.\n");
+		return -EIO;
+	}
+
+	if (cpufreq_register_driver(&powernow_k6_driver)) {
+		release_region (POWERNOW_IOPORT, 16);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+
+/**
+ * powernow_k6_exit - unregisters AMD K6-2+/3+ PowerNow! support
+ *
+ *   Unregisters AMD K6-2+ / K6-3+ PowerNow! support.
+ */
+static void __exit powernow_k6_exit(void)
+{
+	cpufreq_unregister_driver(&powernow_k6_driver);
+	release_region (POWERNOW_IOPORT, 16);
+}
+
+
+MODULE_AUTHOR ("Arjan van de Ven <arjanv@xxxxxxxxxx>, Dave Jones
<davej@xxxxxxxxxxxxxxxxx>, Dominik Brodowski <linux@xxxxxxxx>");
+MODULE_DESCRIPTION ("PowerNow! driver for AMD K6-2+ / K6-3+ processors.");
+MODULE_LICENSE ("GPL");
+
+module_init(powernow_k6_init);
+module_exit(powernow_k6_exit);
diff -uNr linux-2.4.23-vanilla/arch/i386/kernel/powernow-k7.c
linux/arch/i386/kernel/powernow-k7.c
--- linux-2.4.23-vanilla/arch/i386/kernel/powernow-k7.c	1970-01-01
07:30:00.000000000 +0730
+++ linux/arch/i386/kernel/powernow-k7.c	2003-09-20
00:47:44.000000000 +0800
@@ -0,0 +1,422 @@
+/*
+ *  AMD K7 Powernow driver.
+ *  (C) 2003 Dave Jones <davej@xxxxxxxxxxxxxxxxx> on behalf of SuSE Labs.
+ *  (C) 2003 Dave Jones <davej@xxxxxxxxxx>
+ *
+ *  Licensed under the terms of the GNU GPL License version 2.
+ *  Based upon datasheets & sample CPUs kindly provided by AMD.
+ *
+ *  BIG FAT DISCLAIMER: Work in progress code. Possibly *dangerous*
+ *
+ * Errata 5: Processor may fail to execute a FID/VID change in presence of
interrupt.
+ * - We cli/sti on stepping A0 CPUs around the FID/VID transition.
+ * Errata 15: Processors with half frequency multipliers may hang upon
wakeup from disconnect.
+ * - We disable half multipliers if ACPI is used on A0 stepping CPUs.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h> 
+#include <linux/init.h>
+#include <linux/cpufreq.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+
+#include <asm/msr.h>
+#include <asm/timex.h>
+#include <asm/io.h>
+#include <asm/system.h>
+
+#include "powernow-k7.h"
+
+#define DEBUG
+
+#ifdef DEBUG
+#define dprintk(msg...) printk(msg)
+#else
+#define dprintk(msg...) do { } while(0)
+#endif
+
+#define PFX "powernow: "
+
+
+struct psb_s {
+	u8 signature[10];
+	u8 tableversion;
+	u8 flags;
+	u16 settlingtime;
+	u8 reserved1;
+	u8 numpst;
+};
+
+struct pst_s {
+	u32 cpuid;
+	u8 fsbspeed;
+	u8 maxfid;
+	u8 startvid;
+	u8 numpstates;
+};
+
+
+/* divide by 1000 to get VID. */
+static int mobile_vid_table[32] = {
+    2000, 1950, 1900, 1850, 1800, 1750, 1700, 1650,
+    1600, 1550, 1500, 1450, 1400, 1350, 1300, 0,
+    1275, 1250, 1225, 1200, 1175, 1150, 1125, 1100,
+    1075, 1050, 1024, 1000, 975, 950, 925, 0,
+};
+
+/* divide by 10 to get FID. */
+static int fid_codes[32] = {
+    110, 115, 120, 125, 50, 55, 60, 65,
+    70, 75, 80, 85, 90, 95, 100, 105, 
+    30, 190, 40, 200, 130, 135, 140, 210,
+    150, 225, 160, 165, 170, 180, -1, -1,
+};
+
+static struct cpufreq_frequency_table *powernow_table;
+
+static unsigned int can_scale_bus;
+static unsigned int can_scale_vid;
+static unsigned int minimum_speed=-1;
+static unsigned int maximum_speed;
+static unsigned int number_scales;
+static unsigned int fsb;
+static unsigned int latency;
+static char have_a0;
+
+
+static int check_powernow(void)
+{
+	struct cpuinfo_x86 *c = cpu_data;
+	unsigned int maxei, eax, ebx, ecx, edx;
+
+	if (c->x86_vendor != X86_VENDOR_AMD) {
+		printk (KERN_INFO PFX "AMD processor not detected.\n");
+		return 0;
+	}
+
+	if (c->x86 !=6) {
+		printk (KERN_INFO PFX "This module only works with AMD K7
CPUs\n");
+		return 0;
+	}
+
+	printk (KERN_INFO PFX "AMD K7 CPU detected.\n");
+
+	if ((c->x86_model == 6) && (c->x86_mask == 0)) {
+		printk (KERN_INFO PFX "K7 660[A0] core detected, enabling
errata workarounds\n");
+		have_a0 = 1;
+	}
+
+	/* Get maximum capabilities */
+	maxei = cpuid_eax (0x80000000);
+	if (maxei < 0x80000007) {	/* Any powernow info ? */
+		printk (KERN_INFO PFX "No powernow capabilities
detected\n");
+		return 0;
+	}
+
+	cpuid(0x80000007, &eax, &ebx, &ecx, &edx);
+	printk (KERN_INFO PFX "PowerNOW! Technology present. Can scale: ");
+
+	if (edx & 1 << 1) {
+		printk ("frequency");
+		can_scale_bus=1;
+	}
+
+	if ((edx & (1 << 1 | 1 << 2)) == 0x6)
+		printk (" and ");
+
+	if (edx & 1 << 2) {
+		printk ("voltage");
+		can_scale_vid=1;
+	}
+
+	if (!(edx & (1 << 1 | 1 << 2))) {
+		printk ("nothing.\n");
+		return 0;
+	}
+
+	printk (".\n");
+	return 1;
+}
+
+
+static int get_ranges (unsigned char *pst)
+{
+	unsigned int j, speed;
+	u8 fid, vid;
+
+	powernow_table = kmalloc((sizeof(struct cpufreq_frequency_table) *
(number_scales + 1)), GFP_KERNEL);
+	if (!powernow_table)
+		return -ENOMEM;
+	memset(powernow_table, 0, (sizeof(struct cpufreq_frequency_table) *
(number_scales + 1)));
+
+	for (j=0 ; j < number_scales; j++) {
+		fid = *pst++;
+
+		powernow_table[j].frequency = fsb * fid_codes[fid] * 100;
+		powernow_table[j].index = fid; /* lower 8 bits */
+
+		speed = fsb * (fid_codes[fid]/10);
+		if ((fid_codes[fid] % 10)==5) {
+			speed += fsb/2;
+#if defined(CONFIG_ACPI_PROCESSOR) || defined(CONFIG_ACPI_PROCESSOR_MODULE)
+			if (have_a0 == 1)
+				powernow_table[j].frequency =
CPUFREQ_ENTRY_INVALID;
+#endif
+		}
+
+		dprintk (KERN_INFO PFX "   FID: 0x%x (%d.%dx [%dMHz])\t",
fid,
+			fid_codes[fid] / 10, fid_codes[fid] % 10, speed);
+
+		if (speed < minimum_speed)
+			minimum_speed = speed;
+		if (speed > maximum_speed)
+			maximum_speed = speed;
+
+		vid = *pst++;
+		powernow_table[j].index |= (vid << 8); /* upper 8 bits */
+		dprintk ("VID: 0x%x (%d.%03dV)\n", vid,
mobile_vid_table[vid]/1000,
+			mobile_vid_table[vid]%1000);
+	}
+	dprintk ("\n");
+
+	powernow_table[number_scales].frequency = CPUFREQ_TABLE_END;
+	powernow_table[number_scales].index = 0;
+
+	return 0;
+}
+
+
+static void change_FID(int fid)
+{
+	union msr_fidvidctl fidvidctl;
+
+	rdmsrl (MSR_K7_FID_VID_CTL, fidvidctl.val);
+	if (fidvidctl.bits.FID != fid) {
+		fidvidctl.bits.SGTC = latency;
+		fidvidctl.bits.FID = fid;
+		fidvidctl.bits.VIDC = 0;
+		fidvidctl.bits.FIDC = 1;
+		wrmsrl (MSR_K7_FID_VID_CTL, fidvidctl.val);
+	}
+}
+
+
+static void change_VID(int vid)
+{
+	union msr_fidvidctl fidvidctl;
+
+	rdmsrl (MSR_K7_FID_VID_CTL, fidvidctl.val);
+	if (fidvidctl.bits.VID != vid) {
+		fidvidctl.bits.SGTC = latency;
+		fidvidctl.bits.VID = vid;
+		fidvidctl.bits.FIDC = 0;
+		fidvidctl.bits.VIDC = 1;
+		wrmsrl (MSR_K7_FID_VID_CTL, fidvidctl.val);
+	}
+}
+
+
+static void change_speed (unsigned int index)
+{
+	u8 fid, vid;
+	struct cpufreq_freqs freqs;
+	union msr_fidvidstatus fidvidstatus;
+	int cfid;
+
+	/* fid are the lower 8 bits of the index we stored into
+	 * the cpufreq frequency table in powernow_decode_bios,
+	 * vid are the upper 8 bits.
+	 */
+
+	fid = powernow_table[index].index & 0xFF;
+	vid = (powernow_table[index].index & 0xFF00) >> 8;
+
+	freqs.cpu = 0;
+
+	rdmsrl (MSR_K7_FID_VID_STATUS, fidvidstatus.val);
+	cfid = fidvidstatus.bits.CFID;
+	freqs.old = fsb * fid_codes[cfid] * 100;
+	freqs.new = powernow_table[index].frequency;
+
+	cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
+
+	/* Now do the magic poking into the MSRs.  */
+
+	if (have_a0 == 1)	/* A0 errata 5 */
+		local_irq_disable();
+
+	if (freqs.old > freqs.new) {
+		/* Going down, so change FID first */
+		change_FID(fid);
+		change_VID(vid);
+	} else {
+		/* Going up, so change VID first */
+		change_VID(vid);
+		change_FID(fid);
+	}
+	
+
+	if (have_a0 == 1)
+		local_irq_enable();
+
+	cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
+}
+
+
+static int powernow_decode_bios (int maxfid, int startvid)
+{
+	struct psb_s *psb;
+	struct pst_s *pst;
+	struct cpuinfo_x86 *c = cpu_data;
+	unsigned int i, j;
+	unsigned char *p;
+	unsigned int etuple;
+	unsigned int ret;
+
+	etuple = cpuid_eax(0x80000001);
+	etuple &= 0xf00;
+	etuple |= (c->x86_model<<4)|(c->x86_mask);
+
+	for (i=0xC0000; i < 0xffff0 ; i+=16) {
+
+		p = phys_to_virt(i);
+
+		if (memcmp(p, "AMDK7PNOW!",  10) == 0){
+			dprintk (KERN_INFO PFX "Found PSB header at %p\n",
p);
+			psb = (struct psb_s *) p;
+			dprintk (KERN_INFO PFX "Table version: 0x%x\n",
psb->tableversion);
+			if (psb->tableversion != 0x12) {
+				printk (KERN_INFO PFX "Sorry, only v1.2
tables supported right now\n");
+				return -ENODEV;
+			}
+
+			dprintk (KERN_INFO PFX "Flags: 0x%x (", psb->flags);
+			if ((psb->flags & 1)==0) {
+				dprintk ("Mobile");
+			} else {
+				dprintk ("Desktop");
+			}
+			dprintk (" voltage regulator)\n");
+
+			latency = psb->settlingtime;
+			if (latency < 100) {
+				printk (KERN_INFO PFX "BIOS set settling
time to %d microseconds."
+						"Should be at least 100.
Correcting.\n", latency);
+				latency = 100;
+			}
+			dprintk (KERN_INFO PFX "Settling Time: %d
microseconds.\n", psb->settlingtime);
+			dprintk (KERN_INFO PFX "Has %d PST tables. (Only
dumping ones relevant to this CPU).\n", psb->numpst);
+			latency *= 100;	/* SGTC needs to be in units of 10ns
*/
+
+			p += sizeof (struct psb_s);
+
+			pst = (struct pst_s *) p;
+
+			for (i = 0 ; i <psb->numpst; i++) {
+				pst = (struct pst_s *) p;
+				number_scales = pst->numpstates;
+
+				if ((etuple == pst->cpuid) &&
(maxfid==pst->maxfid) && (startvid==pst->startvid))
+				{
+					dprintk (KERN_INFO PFX "PST:%d
(@%p)\n", i, pst);
+					dprintk (KERN_INFO PFX " cpuid:
0x%x\t", pst->cpuid);
+					dprintk ("fsb: %d\t",
pst->fsbspeed);
+					dprintk ("maxFID: 0x%x\t",
pst->maxfid);
+					dprintk ("startvid: 0x%x\n",
pst->startvid);
+
+					fsb = pst->fsbspeed;
+					ret = get_ranges ((char *) pst +
sizeof (struct pst_s));
+					return ret;
+
+				} else {
+					p = (char *) pst + sizeof (struct
pst_s);
+					for (j=0 ; j < number_scales; j++)
+						p+=2;
+				}
+			}
+			printk (KERN_INFO PFX "No PST tables match this
cpuid (0x%x)\n", etuple);
+			printk ("This is indicative of a broken BIOS. Email
davej@xxxxxxxxxx\n");
+			return -EINVAL;
+		}
+		p++;
+	}
+
+	return -ENODEV;
+}
+
+
+static int powernow_target (struct cpufreq_policy *policy,
+			    unsigned int target_freq,
+			    unsigned int relation)
+{
+	unsigned int newstate;
+
+	if (cpufreq_frequency_table_target(policy, powernow_table,
target_freq, relation, &newstate))
+		return -EINVAL;
+
+	change_speed(newstate);
+
+	return 0;
+}
+
+
+static int powernow_verify (struct cpufreq_policy *policy)
+{
+	return cpufreq_frequency_table_verify(policy, powernow_table);
+}
+
+
+static int __init powernow_cpu_init (struct cpufreq_policy *policy)
+{
+	union msr_fidvidstatus fidvidstatus;
+	int result;
+
+	if (policy->cpu != 0)
+		return -ENODEV;
+
+	rdmsrl (MSR_K7_FID_VID_STATUS, fidvidstatus.val);
+
+	result = powernow_decode_bios(fidvidstatus.bits.MFID,
fidvidstatus.bits.SVID);
+	if (result)
+		return result;
+
+	printk (KERN_INFO PFX "Minimum speed %d MHz. Maximum speed %d
MHz.\n",
+				minimum_speed, maximum_speed);
+
+	policy->policy = CPUFREQ_POLICY_PERFORMANCE;
+	policy->cpuinfo.transition_latency = latency;
+	policy->cur = maximum_speed;
+
+	return cpufreq_frequency_table_cpuinfo(policy, powernow_table);
+}
+
+static struct cpufreq_driver powernow_driver = {
+	.verify 	= powernow_verify,
+	.target 	= powernow_target,
+	.init		= powernow_cpu_init,
+	.name		= "powernow-k7",
+};
+
+static int __init powernow_init (void)
+{
+	if (check_powernow()==0)
+		return -ENODEV;
+	return cpufreq_register_driver(&powernow_driver);
+}
+
+
+static void __exit powernow_exit (void)
+{
+	cpufreq_unregister_driver(&powernow_driver);
+	if (powernow_table)
+		kfree(powernow_table);
+}
+
+MODULE_AUTHOR ("Dave Jones <davej@xxxxxxxxxxxxxxxxx>");
+MODULE_DESCRIPTION ("Powernow driver for AMD K7 processors.");
+MODULE_LICENSE ("GPL");
+
+module_init(powernow_init);
+module_exit(powernow_exit);
+
diff -uNr linux-2.4.23-vanilla/arch/i386/kernel/powernow-k7.h
linux/arch/i386/kernel/powernow-k7.h
--- linux-2.4.23-vanilla/arch/i386/kernel/powernow-k7.h	1970-01-01
07:30:00.000000000 +0730
+++ linux/arch/i386/kernel/powernow-k7.h	2003-08-25
22:58:48.000000000 +0800
@@ -0,0 +1,44 @@
+/*
+ *  $Id: powernow-k7.h,v 1.1.1.1 2003/08/25 14:58:47 ducrot Exp $
+ *  (C) 2003 Dave Jones.
+ *
+ *  Licensed under the terms of the GNU GPL License version 2.
+ *
+ *  AMD-specific information
+ *
+ */
+
+union msr_fidvidctl {
+	struct {
+		unsigned FID:5,			// 4:0
+		reserved1:3,	// 7:5
+		VID:5,			// 12:8
+		reserved2:3,	// 15:13
+		FIDC:1,			// 16
+		VIDC:1,			// 17
+		reserved3:2,	// 19:18
+		FIDCHGRATIO:1,	// 20
+		reserved4:11,	// 31-21
+		SGTC:20,		// 32:51
+		reserved5:12;	// 63:52
+	} bits;
+	unsigned long long val;
+};
+
+union msr_fidvidstatus {
+	struct {
+		unsigned CFID:5,			// 4:0
+		reserved1:3,	// 7:5
+		SFID:5,			// 12:8
+		reserved2:3,	// 15:13
+		MFID:5,			// 20:16
+		reserved3:11,	// 31:21
+		CVID:5,			// 36:32
+		reserved4:3,	// 39:37
+		SVID:5,			// 44:40
+		reserved5:3,	// 47:45
+		MVID:5,			// 52:48
+		reserved6:11;	// 63:53
+	} bits;
+	unsigned long long val;
+};
diff -uNr linux-2.4.23-vanilla/arch/i386/kernel/powernow-k8.c
linux/arch/i386/kernel/powernow-k8.c
--- linux-2.4.23-vanilla/arch/i386/kernel/powernow-k8.c	1970-01-01
07:30:00.000000000 +0730
+++ linux/arch/i386/kernel/powernow-k8.c	2003-09-30
19:25:52.000000000 +0800
@@ -0,0 +1,1019 @@
+/*
+ *   (c) 2003 Advanced Micro Devices, Inc.
+ *  Your use of this code is subject to the terms and conditions of the
+ *  GNU general public license version 2. See "../../../COPYING" or
+ *  http://www.gnu.org/licenses/gpl.html
+ *
+ *  Support : paul.devriendt@xxxxxxx
+ *
+ *  Based on the powernow-k7.c module written by Dave Jones.
+ *  (C) 2003 Dave Jones <davej@xxxxxxxxxxxxxxxxx> on behalf of SuSE Labs
+ *  Licensed under the terms of the GNU GPL License version 2.
+ *  Based upon datasheets & sample CPUs kindly provided by AMD.
+ *
+ *  Processor information obtained from Chapter 9 (Power and Thermal
Management)
+ *  of the "BIOS and Kernel Developer's Guide for the AMD Athlon 64 and AMD
+ *  Opteron Processors", revision 3.03, available for download from
www.amd.com
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/smp.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/cpufreq.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+
+#include <asm/msr.h>
+#include <asm/io.h>
+#include <asm/delay.h>
+
+#define PFX "powernow-k8: "
+#define BFX PFX "BIOS error: "
+#define VERSION "version 1.00.08 - September 26, 2003"
+#include "powernow-k8.h"
+
+#ifdef CONFIG_PREEMPT
+#warning this driver has not been tested on a preempt system
+#endif
+
+static u32 vstable;	/* voltage stabalization time, from PSB, units 20 us
*/
+static u32 plllock;	/* pll lock time, from PSB, units 1 us */
+static u32 numps;	/* number of p-states, from PSB */
+static u32 rvo;		/* ramp voltage offset, from PSB */
+static u32 irt;		/* isochronous relief time, from PSB */
+static u32 vidmvs;	/* usable value calculated from mvs, from PSB */
+struct pst_s *ppst;	/* array of p states, valid for this part */
+static u32 currvid;	/* keep track of the current fid / vid */
+static u32 currfid;
+
+/*
+The PSB table supplied by BIOS allows for the definition of the number of
+p-states that can be used when running on a/c, and the number of p-states
+that can be used when running on battery. This allows laptop manufacturers
+to force the system to save power when running from battery. The
relationship 
+is :
+   1 <= number_of_battery_p_states <= maximum_number_of_p_states
+
+This driver does NOT have the support in it to detect transitions from
+a/c power to battery power, and thus trigger the transition to a lower
+p-state if required. This is because I need ACPI and the 2.6 kernel to do 
+this, and this is a 2.4 kernel driver. Check back for a new improved driver
+for the 2.6 kernel soon.
+
+This code therefore assumes it is on battery at all times, and thus
+restricts performance to number_of_battery_p_states. For desktops, 
+  number_of_battery_p_states == maximum_number_of_pstates, 
+so this is not actually a restriction.
+*/
+
+static u32 batps;	/* limit on the number of p states when on battery
*/
+			/* - set by BIOS in the PSB/PST
*/
+
+static struct cpufreq_driver cpufreq_amd64_driver = {
+	.verify = drv_verify,
+	.target = drv_target,
+	.init = drv_cpu_init,
+	.name = "cpufreq-amd64",
+};
+
+#define SEARCH_UP     1
+#define SEARCH_DOWN   0
+
+/* Return a frequency in MHz, given an input fid */
+u32
+find_freq_from_fid(u32 fid)
+{
+	return 800 + (fid * 100);
+}
+
+/* Return a fid matching an input frequency in MHz */
+u32
+find_fid_from_freq(u32 freq)
+{
+	return (freq - 800) / 100;
+}
+
+/* Return the vco fid for an input fid */
+static u32
+convert_fid_to_vco_fid(u32 fid)
+{
+	if (fid < HI_FID_TABLE_BOTTOM) {
+		return 8 + (2 * fid);
+	} else {
+		return fid;
+	}
+}
+
+/* Sort the fid/vid frequency table into ascending order by fid. The spec
*/
+/* implies that it will be sorted by BIOS, but, it only implies it, and I
*/
+/* prefer not to trust when I can check.
*/
+/* Yes, it is a simple bubble sort, but the PST is really small, so the
*/
+/* choice of algorithm is pretty irrelevant.
*/
+static inline void
+sort_pst(struct pst_s *ppst, u32 numpstates)
+{
+	u32 i;
+	u8 tempfid;
+	u8 tempvid;
+	int swaps = 1;
+
+	while (swaps) {
+		swaps = 0;
+		for (i = 0; i < (numpstates - 1); i++) {
+			if (ppst[i].fid > ppst[i + 1].fid) {
+				swaps = 1;
+				tempfid = ppst[i].fid;
+				tempvid = ppst[i].vid;
+				ppst[i].fid = ppst[i + 1].fid;
+				ppst[i].vid = ppst[i + 1].vid;
+				ppst[i + 1].fid = tempfid;
+				ppst[i + 1].vid = tempvid;
+			}
+		}
+	}
+
+	return;
+}
+
+/* Return 1 if the pending bit is set. Unless we are actually just told the
*/
+/* processor to transition a state, seeing this bit set is really bad news.
*/
+static inline int
+pending_bit_stuck(void)
+{
+	u32 lo;
+	u32 hi;
+
+	rdmsr(MSR_FIDVID_STATUS, lo, hi);
+	return lo & MSR_S_LO_CHANGE_PENDING ? 1 : 0;
+}
+
+/* Update the global current fid / vid values from the status msr. Returns
1 */
+/* on error.
*/
+static int
+query_current_values_with_pending_wait(void)
+{
+	u32 lo;
+	u32 hi;
+	u32 i = 0;
+
+	lo = MSR_S_LO_CHANGE_PENDING;
+	while (lo & MSR_S_LO_CHANGE_PENDING) {
+		if (i++ > 0x1000000) {
+			printk(KERN_ERR PFX "detected change pending
stuck\n");
+			return 1;
+		}
+		rdmsr(MSR_FIDVID_STATUS, lo, hi);
+	}
+
+	currvid = hi & MSR_S_HI_CURRENT_VID;
+	currfid = lo & MSR_S_LO_CURRENT_FID;
+
+	return 0;
+}
+
+/* the isochronous relief time */
+static inline void
+count_off_irt(void)
+{
+	udelay((1 << irt) * 10);
+	return;
+}
+
+/* the voltage stabalization time */
+static inline void
+count_off_vst(void)
+{
+	udelay(vstable * VST_UNITS_20US);
+	return;
+}
+
+/* write the new fid value along with the other control fields to the msr
*/
+static int
+write_new_fid(u32 fid)
+{
+	u32 lo;
+	u32 savevid = currvid;
+
+	if ((fid & INVALID_FID_MASK) || (currvid & INVALID_VID_MASK)) {
+		printk(KERN_ERR PFX "internal error - overflow on fid
write\n");
+		return 1;
+	}
+
+	lo = fid | (currvid << MSR_C_LO_VID_SHIFT) | MSR_C_LO_INIT_FID_VID;
+
+	dprintk(KERN_DEBUG PFX "writing fid %x, lo %x, hi %x\n",
+		fid, lo, plllock * PLL_LOCK_CONVERSION);
+
+	wrmsr(MSR_FIDVID_CTL, lo, plllock * PLL_LOCK_CONVERSION);
+
+	if (query_current_values_with_pending_wait())
+		return 1;
+
+	count_off_irt();
+
+	if (savevid != currvid) {
+		printk(KERN_ERR PFX
+		       "vid changed on fid transition, save %x, currvid
%x\n",
+		       savevid, currvid);
+		return 1;
+	}
+
+	if (fid != currfid) {
+		printk(KERN_ERR PFX
+		       "fid transition failed, fid %x, currfid %x\n",
+		        fid, currfid);
+		return 1;
+	}
+
+	return 0;
+}
+
+/* Write a new vid to the hardware */
+static int
+write_new_vid(u32 vid)
+{
+	u32 lo;
+	u32 savefid = currfid;
+
+	if ((currfid & INVALID_FID_MASK) || (vid & INVALID_VID_MASK)) {
+		printk(KERN_ERR PFX "internal error - overflow on vid
write\n");
+		return 1;
+	}
+
+	lo = currfid | (vid << MSR_C_LO_VID_SHIFT) | MSR_C_LO_INIT_FID_VID;
+
+	dprintk(KERN_DEBUG PFX "writing vid %x, lo %x, hi %x\n",
+		vid, lo, STOP_GRANT_5NS);
+
+	wrmsr(MSR_FIDVID_CTL, lo, STOP_GRANT_5NS);
+
+	if (query_current_values_with_pending_wait()) {
+		return 1;
+	}
+
+	if (savefid != currfid) {
+		printk(KERN_ERR PFX
+		       "fid changed on vid transition, save %x currfid
%x\n",
+		       savefid, currfid);
+		return 1;
+	}
+
+	if (vid != currvid) {
+		printk(KERN_ERR PFX
+		       "vid transition failed, vid %x, currvid %x\n",
+		       vid, currvid);
+		return 1;
+	}
+
+	return 0;
+}
+
+/* Reduce the vid by the max of step or reqvid.                   */
+/* Decreasing vid codes represent increasing voltages :           */
+/* vid of 0 is 1.550V, vid of 0x1e is 0.800V, vid of 0x1f is off. */
+static int
+decrease_vid_code_by_step(u32 reqvid, u32 step)
+{
+	if ((currvid - reqvid) > step)
+		reqvid = currvid - step;
+
+	if (write_new_vid(reqvid))
+		return 1;
+
+	count_off_vst();
+
+	return 0;
+}
+
+/* Change the fid and vid, by the 3 phases. */
+static inline int
+transition_fid_vid(u32 reqfid, u32 reqvid)
+{
+	if (core_voltage_pre_transition(reqvid))
+		return 1;
+
+	if (core_frequency_transition(reqfid))
+		return 1;
+
+	if (core_voltage_post_transition(reqvid))
+		return 1;
+
+	if (query_current_values_with_pending_wait())
+		return 1;
+
+	if ((reqfid != currfid) || (reqvid != currvid)) {
+		printk(KERN_ERR PFX "failed: req 0x%x 0x%x, curr 0x%x
0x%x\n",
+		       reqfid, reqvid, currfid, currvid);
+		return 1;
+	}
+
+	dprintk(KERN_INFO PFX
+		"transitioned: new fid 0x%x, vid 0x%x\n", currfid, currvid);
+
+	return 0;
+}
+
+/* Phase 1 - core voltage transition ... setup appropriate voltage for the
*/
+/* fid transition.
*/
+static inline int
+core_voltage_pre_transition(u32 reqvid)
+{
+	u32 rvosteps = rvo;
+	u32 savefid = currfid;
+
+	dprintk(KERN_DEBUG PFX
+		"ph1: start, currfid 0x%x, currvid 0x%x, reqvid 0x%x, rvo
%x\n",
+		currfid, currvid, reqvid, rvo);
+
+	while (currvid > reqvid) {
+		dprintk(KERN_DEBUG PFX "ph1: curr 0x%x, requesting vid
0x%x\n",
+			currvid, reqvid);
+		if (decrease_vid_code_by_step(reqvid, vidmvs))
+			return 1;
+	}
+
+	while (rvosteps > 0) {
+		if (currvid == 0) {
+			rvosteps = 0;
+		} else {
+			dprintk(KERN_DEBUG PFX
+				"ph1: changing vid for rvo, requesting
0x%x\n",
+				currvid - 1);
+			if (decrease_vid_code_by_step(currvid - 1, 1))
+				return 1;
+			rvosteps--;
+		}
+	}
+
+	if (query_current_values_with_pending_wait())
+		return 1;
+
+	if (savefid != currfid) {
+		printk(KERN_ERR PFX "ph1 err, currfid changed 0x%x\n",
currfid);
+		return 1;
+	}
+
+	dprintk(KERN_DEBUG PFX "ph1 complete, currfid 0x%x, currvid 0x%x\n",
+		currfid, currvid);
+
+	return 0;
+}
+
+/* Phase 2 - core frequency transition */
+static inline int
+core_frequency_transition(u32 reqfid)
+{
+	u32 vcoreqfid;
+	u32 vcocurrfid;
+	u32 vcofiddiff;
+	u32 savevid = currvid;
+
+	if ((reqfid < HI_FID_TABLE_BOTTOM) && (currfid <
HI_FID_TABLE_BOTTOM)) {
+		printk(KERN_ERR PFX "ph2 illegal lo-lo transition 0x%x
0x%x\n",
+		       reqfid, currfid);
+		return 1;
+	}
+
+	if (currfid == reqfid) {
+		printk(KERN_ERR PFX "ph2 null fid transition 0x%x\n",
currfid);
+		return 0;
+	}
+
+	dprintk(KERN_DEBUG PFX
+		"ph2 starting, currfid 0x%x, currvid 0x%x, reqfid 0x%x\n",
+		currfid, currvid, reqfid);
+
+	vcoreqfid = convert_fid_to_vco_fid(reqfid);
+	vcocurrfid = convert_fid_to_vco_fid(currfid);
+	vcofiddiff = vcocurrfid > vcoreqfid ? vcocurrfid - vcoreqfid
+	    : vcoreqfid - vcocurrfid;
+
+	while (vcofiddiff > 2) {
+		if (reqfid > currfid) {
+			if (currfid > LO_FID_TABLE_TOP) {
+				if (write_new_fid(currfid + 2)) {
+					return 1;
+				}
+			} else {
+				if (write_new_fid
+				    (2 + convert_fid_to_vco_fid(currfid))) {
+					return 1;
+				}
+			}
+		} else {
+			if (write_new_fid(currfid - 2))
+				return 1;
+		}
+
+		vcocurrfid = convert_fid_to_vco_fid(currfid);
+		vcofiddiff = vcocurrfid > vcoreqfid ? vcocurrfid - vcoreqfid
+		    : vcoreqfid - vcocurrfid;
+	}
+
+	if (write_new_fid(reqfid))
+		return 1;
+
+	if (query_current_values_with_pending_wait())
+		return 1;
+
+	if (currfid != reqfid) {
+		printk(KERN_ERR PFX
+		       "ph2 mismatch, failed fid transition, curr %x, req
%x\n",
+		       currfid, reqfid);
+		return 1;
+	}
+
+	if (savevid != currvid) {
+		printk(KERN_ERR PFX
+		       "ph2 vid changed, save %x, curr %x\n", savevid,
+		       currvid);
+		return 1;
+	}
+
+	dprintk(KERN_DEBUG PFX "ph2 complete, currfid 0x%x, currvid 0x%x\n",
+		currfid, currvid);
+
+	return 0;
+}
+
+/* Phase 3 - core voltage transition flow ... jump to the final vid. */
+static inline int
+core_voltage_post_transition(u32 reqvid)
+{
+	u32 savefid = currfid;
+	u32 savereqvid = reqvid;
+
+	dprintk(KERN_DEBUG PFX "ph3 starting, currfid 0x%x, currvid 0x%x\n",
+		currfid, currvid);
+
+	if (reqvid != currvid) {
+		if (write_new_vid(reqvid))
+			return 1;
+
+		if (savefid != currfid) {
+			printk(KERN_ERR PFX
+			       "ph3: bad fid change, save %x, curr %x\n",
+			       savefid, currfid);
+			return 1;
+		}
+
+		if (currvid != reqvid) {
+			printk(KERN_ERR PFX
+			       "ph3: failed vid transition\n, req %x, curr
%x",
+			       reqvid, currvid);
+			return 1;
+		}
+	}
+
+	if (query_current_values_with_pending_wait())
+		return 1;
+
+	if (savereqvid != currvid) {
+		dprintk(KERN_ERR PFX "ph3 failed, currvid 0x%x\n", currvid);
+		return 1;
+	}
+
+	if (savefid != currfid) {
+		dprintk(KERN_ERR PFX "ph3 failed, currfid changed 0x%x\n",
+			currfid);
+		return 1;
+	}
+
+	dprintk(KERN_DEBUG PFX "ph3 complete, currfid 0x%x, currvid 0x%x\n",
+		currfid, currvid);
+
+	return 0;
+}
+
+static inline int
+check_supported_cpu(void)
+{
+	struct cpuinfo_x86 *c = cpu_data;
+	u32 eax, ebx, ecx, edx;
+
+	if (smp_num_cpus != 1) {
+		printk(KERN_INFO PFX "multiprocessor systems not
supported\n");
+		return 0;
+	}
+
+	if (c->x86_vendor != X86_VENDOR_AMD) {
+		printk(KERN_INFO PFX "Not an AMD processor\n");
+		return 0;
+	}
+
+	eax = cpuid_eax(CPUID_PROCESSOR_SIGNATURE);
+	if ((eax & CPUID_XFAM_MOD) == ATHLON64_XFAM_MOD) {
+		dprintk(KERN_DEBUG PFX "AMD Althon 64 Processor found\n");
+		if ((eax & CPUID_F1_STEP) < ATHLON64_REV_C0) {
+			printk(KERN_INFO PFX "Revision C0 or better "
+			       "AMD Athlon 64 processor required\n");
+			return 0;
+		}
+	} else if ((eax & CPUID_XFAM_MOD) == OPTERON_XFAM_MOD) {
+		dprintk(KERN_DEBUG PFX "AMD Opteron Processor found\n");
+	} else {
+		printk(KERN_INFO PFX
+		       "AMD Athlon 64 or AMD Opteron processor required\n");
+		return 0;
+	}
+
+	eax = cpuid_eax(CPUID_GET_MAX_CAPABILITIES);
+	if (eax < CPUID_FREQ_VOLT_CAPABILITIES) {
+		printk(KERN_INFO PFX
+		       "No frequency change capabilities detected\n");
+		return 0;
+	}
+
+	cpuid(CPUID_FREQ_VOLT_CAPABILITIES, &eax, &ebx, &ecx, &edx);
+	if ((edx & P_STATE_TRANSITION_CAPABLE) !=
P_STATE_TRANSITION_CAPABLE) {
+		printk(KERN_INFO PFX "Power state transitions not
supported\n");
+		return 0;
+	}
+
+	printk(KERN_INFO PFX "Found AMD Athlon 64 / Opteron processor "
+	       "supporting p-state transitions\n");
+
+	return 1;
+}
+
+/* Find and validate the PSB/PST table in BIOS. */
+static inline int
+find_psb_table(void)
+{
+	struct psb_s *psb;
+	struct pst_s *pst;
+	unsigned i, j;
+	u32 lastfid;
+	u32 mvs;
+	u8 maxvid;
+
+	for (i = 0xc0000; i < 0xffff0; i += 0x10) {
+		/* Scan BIOS looking for the signature. */
+		/* It can not be at ffff0 - it is too big. */
+
+		psb = phys_to_virt(i);
+		if (memcmp(psb, PSB_ID_STRING, PSB_ID_STRING_LEN) != 0)
+			continue;
+
+		dprintk(KERN_DEBUG PFX "found PSB header at 0x%p\n", psb);
+
+		dprintk(KERN_DEBUG PFX "table vers: 0x%x\n",
psb->tableversion);
+		if (psb->tableversion != PSB_VERSION_1_4) {
+			printk(KERN_INFO BFX "PSB table is not v1.4\n");
+			return -ENODEV;
+		}
+
+		dprintk(KERN_DEBUG PFX "flags: 0x%x\n", psb->flags1);
+		if (psb->flags1) {
+			printk(KERN_ERR BFX "unknown flags\n");
+			return -ENODEV;
+		}
+
+		vstable = psb->voltagestabilizationtime;
+		printk(KERN_INFO PFX "voltage stable time: %d (units
20us)\n",
+		       vstable);
+
+		dprintk(KERN_DEBUG PFX "flags2: 0x%x\n", psb->flags2);
+		rvo = psb->flags2 & 3;
+		irt = ((psb->flags2) >> 2) & 3;
+		mvs = ((psb->flags2) >> 4) & 3;
+		vidmvs = 1 << mvs;
+		batps = ((psb->flags2) >> 6) & 3;
+		printk(KERN_INFO PFX "p states on battery: %d ", batps);
+		switch (batps) {
+		case 0:
+			printk("- all available\n");
+			break;
+		case 1:
+			printk("- only the minimum\n");
+			break;
+		case 2:
+			printk("- only the 2 lowest\n");
+			break;
+		case 3:
+			printk("- only the 3 lowest\n");
+			break;
+		}
+		printk(KERN_INFO PFX "ramp voltage offset: %d\n", rvo);
+		printk(KERN_INFO PFX "isochronous relief time: %d\n", irt);
+		printk(KERN_INFO PFX "maximum voltage step: %d\n", mvs);
+
+		dprintk(KERN_DEBUG PFX "numpst: 0x%x\n", psb->numpst);
+		if (psb->numpst != 1) {
+			printk(KERN_ERR BFX "numpst must be 1\n");
+			return -ENODEV;
+		}
+
+		dprintk(KERN_DEBUG PFX "cpuid: 0x%x\n", psb->cpuid);
+
+		plllock = psb->plllocktime;
+		printk(KERN_INFO PFX "pll lock time: 0x%x\n", plllock);
+
+		maxvid = psb->maxvid;
+		printk(KERN_INFO PFX "maxfid: 0x%x\n", psb->maxfid);
+		printk(KERN_INFO PFX "maxvid: 0x%x\n", maxvid);
+
+		numps = psb->numpstates;
+		printk(KERN_INFO PFX "numpstates: 0x%x\n", numps);
+		if (numps < 2) {
+			printk(KERN_ERR BFX "no p states to transition\n");
+			return -ENODEV;
+		}
+
+		if (batps == 0) {
+			batps = numps;
+		} else if (batps > numps) {
+			printk(KERN_ERR BFX "batterypstates >
numpstates\n");
+			batps = numps;
+		} else {
+			printk(KERN_ERR PFX
+			       "Restricting operation to %d p-states\n",
batps);
+			printk(KERN_ERR PFX
+			       "Check for an updated driver to access all "
+			       "%d p-states\n", numps);
+		}
+
+		if ((numps <= 1) || (batps <= 1)) {
+			printk(KERN_ERR PFX "only 1 p-state to
transition\n");
+			return -ENODEV;
+		}
+
+		ppst = kmalloc(sizeof (struct pst_s) * numps, GFP_KERNEL);
+		if (!ppst) {
+			printk(KERN_ERR PFX "ppst memory alloc failure\n");
+			return -ENOMEM;
+		}
+
+		pst = (struct pst_s *) (psb + 1);
+		for (j = 0; j < numps; j++) {
+			ppst[j].fid = pst[j].fid;
+			ppst[j].vid = pst[j].vid;
+			printk(KERN_INFO PFX
+			       "   %d : fid 0x%x, vid 0x%x\n", j,
+			       ppst[j].fid, ppst[j].vid);
+		}
+		sort_pst(ppst, numps);
+
+		lastfid = ppst[0].fid;
+		if (lastfid > LO_FID_TABLE_TOP)
+			printk(KERN_INFO BFX "first fid not in lo freq
tbl\n");
+
+		if ((lastfid > MAX_FID) || (lastfid & 1) || (ppst[0].vid >
LEAST_VID)) {
+			printk(KERN_ERR BFX "first fid/vid bad (0x%x -
0x%x)\n",
+			       lastfid, ppst[0].vid);
+			kfree(ppst);
+			return -ENODEV;
+		}
+
+		for (j = 1; j < numps; j++) {
+			if ((lastfid >= ppst[j].fid)
+			    || (ppst[j].fid & 1)
+			    || (ppst[j].fid < HI_FID_TABLE_BOTTOM)
+			    || (ppst[j].fid > MAX_FID)
+			    || (ppst[j].vid > LEAST_VID)) {
+				printk(KERN_ERR BFX
+				       "invalid fid/vid in pst(%x %x)\n",
+				       ppst[j].fid, ppst[j].vid);
+				kfree(ppst);
+				return -ENODEV;
+			}
+			lastfid = ppst[j].fid;
+		}
+
+		for (j = 0; j < numps; j++) {
+			if (ppst[j].vid < rvo) {	/* vid+rvo >= 0 */
+				printk(KERN_ERR BFX
+				       "0 vid exceeded with pstate %d\n",
j);
+				return -ENODEV;
+			}
+			if (ppst[j].vid < maxvid+rvo) { /* vid+rvo >= maxvid
*/
+				printk(KERN_ERR BFX
+				       "maxvid exceeded with pstate %d\n",
j);
+				return -ENODEV;
+			}
+		}
+
+		if (query_current_values_with_pending_wait()) {
+			kfree(ppst);
+			return -EIO;
+		}
+
+		printk(KERN_INFO PFX "currfid 0x%x, currvid 0x%x\n",
+		       currfid, currvid);
+
+		for (j = 0; j < numps; j++)
+			if ((ppst[j].fid==currfid) &&
(ppst[j].vid==currvid))
+				return (0);
+
+		printk(KERN_ERR BFX "currfid/vid do not match PST,
ignoring\n");
+		return 0;
+	}
+
+	printk(KERN_ERR BFX "no PSB\n");
+	return -ENODEV;
+}
+
+/* Converts a frequency (that might not necessarily be a multiple of 200)
*/
+/* to a fid.
*/
+u32
+find_closest_fid(u32 freq, int searchup)
+{
+	if (searchup == SEARCH_UP)
+		freq += MIN_FREQ_RESOLUTION - 1;
+
+	freq = (freq / MIN_FREQ_RESOLUTION) * MIN_FREQ_RESOLUTION;
+
+	if (freq < MIN_FREQ)
+		freq = MIN_FREQ;
+	else if (freq > MAX_FREQ)
+		freq = MAX_FREQ;
+
+	return find_fid_from_freq(freq);
+}
+
+static int
+find_match(u32 * ptargfreq, u32 * pmin, u32 * pmax, int searchup, u32 *
pfid,
+	   u32 * pvid)
+{
+	u32 availpstates = batps;
+	u32 targfid = find_closest_fid(*ptargfreq, searchup);
+	u32 minfid = find_closest_fid(*pmin, SEARCH_DOWN);
+	u32 maxfid = find_closest_fid(*pmax, SEARCH_UP);
+	u32 minidx = 0;
+	u32 maxidx = availpstates - 1;
+	u32 targidx = 0xffffffff;
+	int i;
+
+	dprintk(KERN_DEBUG PFX "find match: freq %d MHz, min %d, max %d\n",
+		*ptargfreq, *pmin, *pmax);
+
+	/* Restrict values to the frequency choices in the PST */
+	if (minfid < ppst[0].fid)
+		minfid = ppst[0].fid;
+	if (maxfid > ppst[maxidx].fid)
+		maxfid = ppst[maxidx].fid;
+
+	/* Find appropriate PST index for the minimim fid */
+	for (i = 0; i < (int) availpstates; i++) {
+		if (minfid >= ppst[i].fid)
+			minidx = i;
+	}
+
+	/* Find appropriate PST index for the maximum fid */
+	for (i = availpstates - 1; i >= 0; i--) {
+		if (maxfid <= ppst[i].fid)
+			maxidx = i;
+	}
+
+	if (minidx > maxidx)
+		maxidx = minidx;
+
+	/* Frequency ids are now constrained by limits matching PST entries
*/
+	minfid = ppst[minidx].fid;
+	maxfid = ppst[maxidx].fid;
+
+	/* Limit the target frequency to these limits */
+	if (targfid < minfid)
+		targfid = minfid;
+	else if (targfid > maxfid)
+		targfid = maxfid;
+
+	/* Find the best target index into the PST, contrained by the range
*/
+	if (searchup == SEARCH_UP) {
+		for (i = maxidx; i >= (int) minidx; i--) {
+			if (targfid <= ppst[i].fid)
+				targidx = i;
+		}
+	} else {
+		for (i = minidx; i <= (int) maxidx; i++) {
+			if (targfid >= ppst[i].fid)
+				targidx = i;
+		}
+	}
+
+	if (targidx == 0xffffffff) {
+		printk(KERN_ERR PFX "could not find target\n");
+		return 1;
+	}
+
+	*pmin = find_freq_from_fid(minfid);
+	*pmax = find_freq_from_fid(maxfid);
+	*ptargfreq = find_freq_from_fid(ppst[targidx].fid);
+
+	if (pfid)
+		*pfid = ppst[targidx].fid;
+	if (pvid)
+		*pvid = ppst[targidx].vid;
+
+	return 0;
+}
+
+/* Take a frequency, and issue the fid/vid transition command */
+static inline int
+transition_frequency(u32 * preq, u32 * pmin, u32 * pmax, u32 searchup)
+{
+	u32 fid;
+	u32 vid;
+	int res;
+	struct cpufreq_freqs freqs;
+
+	if (find_match(preq, pmin, pmax, searchup, &fid, &vid))
+		return 1;
+
+	dprintk(KERN_DEBUG PFX "table matched fid 0x%x, giving vid 0x%x\n",
+		fid, vid);
+
+	if (query_current_values_with_pending_wait())
+		return 1;
+
+	if ((currvid == vid) && (currfid == fid)) {
+		dprintk(KERN_DEBUG PFX
+			"target matches current values (fid 0x%x, vid
0x%x)\n",
+			fid, vid);
+		return 0;
+	}
+
+	if ((fid < HI_FID_TABLE_BOTTOM) && (currfid < HI_FID_TABLE_BOTTOM))
{
+		printk(KERN_ERR PFX
+		       "ignoring illegal change in lo freq table-%x to
%x\n",
+		       currfid, fid);
+		return 1;
+	}
+
+	dprintk(KERN_DEBUG PFX "changing to fid 0x%x, vid 0x%x\n", fid,
vid);
+
+	freqs.cpu = 0;	/* only true because SMP not supported */
+
+	freqs.old = find_freq_from_fid(currfid);
+	freqs.new = find_freq_from_fid(fid);
+	cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
+
+	res = transition_fid_vid(fid, vid);
+
+	freqs.new = find_freq_from_fid(currfid);
+	cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
+
+	return res;
+}
+
+/* Driver entry point to switch to the target frequency */
+static int
+drv_target(struct cpufreq_policy *pol, unsigned targfreq, unsigned
relation)
+{
+	u32 checkfid = currfid;
+	u32 checkvid = currvid;
+	u32 reqfreq = targfreq / 1000;
+	u32 minfreq = pol->min / 1000;
+	u32 maxfreq = pol->max / 1000;
+
+	if (ppst == 0) {
+		printk(KERN_ERR PFX "targ: ppst 0\n");
+		return -ENODEV;
+	}
+
+	if (pending_bit_stuck()) {
+		printk(KERN_ERR PFX "drv targ fail: change pending bit
set\n");
+		return -EIO;
+	}
+
+	dprintk(KERN_DEBUG PFX "targ: %d kHz, min %d, max %d, relation
%d\n",
+		targfreq, pol->min, pol->max, relation);
+
+	if (query_current_values_with_pending_wait())
+		return -EIO;
+
+	dprintk(KERN_DEBUG PFX "targ: curr fid 0x%x, vid 0x%x\n",
+		currfid, currvid);
+
+	if ((checkvid != currvid) || (checkfid != currfid)) {
+		printk(KERN_ERR PFX
+		       "error - out of sync, fid 0x%x 0x%x, vid 0x%x
0x%x\n",
+		       checkfid, currfid, checkvid, currvid);
+	}
+
+	if (transition_frequency(&reqfreq, &minfreq, &maxfreq,
+				 relation ==
+				 CPUFREQ_RELATION_H ? SEARCH_UP :
SEARCH_DOWN))
+	{
+		printk(KERN_ERR PFX "transition frequency failed\n");
+		return 1;
+	}
+
+	pol->cur = 1000 * find_freq_from_fid(currfid);
+
+	return 0;
+}
+
+/* Driver entry point to verify the policy and range of frequencies */
+static int
+drv_verify(struct cpufreq_policy *pol)
+{
+	u32 min = pol->min / 1000;
+	u32 max = pol->max / 1000;
+	u32 targ = min;
+	int res;
+
+	if (ppst == 0) {
+		printk(KERN_ERR PFX "verify - ppst 0\n");
+		return -ENODEV;
+	}
+
+	if (pending_bit_stuck()) {
+		printk(KERN_ERR PFX "failing verify, change pending bit
set\n");
+		return -EIO;
+	}
+
+	dprintk(KERN_DEBUG PFX
+		"ver: cpu%d, min %d, max %d, cur %d, pol %d\n", pol->cpu,
+		pol->min, pol->max, pol->cur, pol->policy);
+
+	if (pol->cpu != 0) {
+		printk(KERN_ERR PFX "verify - cpu not 0\n");
+		return -ENODEV;
+	}
+
+	res = find_match(&targ, &min, &max,
+			 pol->policy == CPUFREQ_POLICY_POWERSAVE ?
+			 SEARCH_DOWN : SEARCH_UP, 0, 0);
+	if (!res) {
+		pol->min = min * 1000;
+		pol->max = max * 1000;
+	}
+	return res;
+}
+
+/* per CPU init entry point to the driver */
+static int __init
+drv_cpu_init(struct cpufreq_policy *pol)
+{
+	if (pol->cpu != 0) {
+		printk(KERN_ERR PFX "init not cpu 0\n");
+		return -ENODEV;
+	}
+
+	pol->policy = CPUFREQ_POLICY_PERFORMANCE; /* boot as fast as we can
*/
+
+	/* Take a crude guess here. */
+	pol->cpuinfo.transition_latency = ((rvo + 8) * vstable *
VST_UNITS_20US)
+	    + (3 * (1 << irt) * 10);
+
+	if (query_current_values_with_pending_wait())
+		return -EIO;
+
+	pol->cur = 1000 * find_freq_from_fid(currfid);
+	dprintk(KERN_DEBUG PFX "policy current frequency %d kHz\n",
pol->cur);
+
+	/* min/max the cpu is capable of */
+	pol->cpuinfo.min_freq = 1000 * find_freq_from_fid(ppst[0].fid);
+	pol->cpuinfo.max_freq = 1000 *
find_freq_from_fid(ppst[numps-1].fid);
+	pol->min = 1000 * find_freq_from_fid(ppst[0].fid);
+	pol->max = 1000 * find_freq_from_fid(ppst[batps - 1].fid);
+
+	printk(KERN_INFO PFX "cpu_init done, current fid 0x%x, vid 0x%x\n",
+	       currfid, currvid);
+
+	return 0;
+}
+
+/* driver entry point for init */
+static int __init
+drv_init(void)
+{
+	int rc;
+
+	printk(KERN_INFO PFX VERSION "\n");
+
+	if (check_supported_cpu() == 0)
+		return -ENODEV;
+
+	rc = find_psb_table();
+	if (rc)
+		return rc;
+
+	if (pending_bit_stuck()) {
+		printk(KERN_ERR PFX "drv_init fail, change pending bit
set\n");
+		kfree(ppst);
+		return -EIO;
+	}
+
+	return cpufreq_register_driver(&cpufreq_amd64_driver);
+}
+
+/* driver entry point for term */
+static void __exit
+drv_exit(void)
+{
+	dprintk(KERN_INFO PFX "drv_exit\n");
+
+	cpufreq_unregister_driver(&cpufreq_amd64_driver);
+	kfree(ppst);
+}
+
+MODULE_AUTHOR("Paul Devriendt <paul.devriendt@xxxxxxx>");
+MODULE_DESCRIPTION("AMD Athlon 64 and Opteron processor frequency
driver.");
+MODULE_LICENSE("GPL");
+
+module_init(drv_init);
+module_exit(drv_exit);
diff -uNr linux-2.4.23-vanilla/arch/i386/kernel/powernow-k8.h
linux/arch/i386/kernel/powernow-k8.h
--- linux-2.4.23-vanilla/arch/i386/kernel/powernow-k8.h	1970-01-01
07:30:00.000000000 +0730
+++ linux/arch/i386/kernel/powernow-k8.h	2003-09-30
17:26:28.000000000 +0800
@@ -0,0 +1,126 @@
+/*
+ *   (c) 2003 Advanced Micro Devices, Inc.
+ *  Your use of this code is subject to the terms and conditions of the
+ *  GNU general public license version 2. See "../../../COPYING" or
+ *  http://www.gnu.org/licenses/gpl.html
+ */
+
+/* processor's cpuid instruction support */
+#define CPUID_PROCESSOR_SIGNATURE             1	/* function 1
*/
+#define CPUID_F1_FAM                 0x00000f00	/* family mask
*/
+#define CPUID_F1_XFAM                0x0ff00000	/* extended family
mask     */
+#define CPUID_F1_MOD                 0x000000f0	/* model mask
*/
+#define CPUID_F1_STEP                0x0000000f	/* stepping level
mask      */
+#define CPUID_XFAM_MOD               0x0ff00ff0	/* xtended fam, fam
+ model */
+#define ATHLON64_XFAM_MOD            0x00000f40	/* xtended fam, fam
+ model */
+#define OPTERON_XFAM_MOD             0x00000f50	/* xtended fam, fam
+ model */
+#define ATHLON64_REV_C0                       8
+#define CPUID_GET_MAX_CAPABILITIES   0x80000000
+#define CPUID_FREQ_VOLT_CAPABILITIES 0x80000007
+#define P_STATE_TRANSITION_CAPABLE            6
+
+/* Model Specific Registers for p-state transitions. MSRs are 64-bit. For
*/
+/* writes (wrmsr - opcode 0f 30), the register number is placed in ecx, and
*/
+/* the value to write is placed in edx:eax. For reads (rdmsr - opcode 0f
32), */
+/* the register number is placed in ecx, and the data is returned in
edx:eax. */
+
+#define MSR_FIDVID_CTL      0xc0010041
+#define MSR_FIDVID_STATUS   0xc0010042
+
+/* Field definitions within the FID VID Low Control MSR : */
+#define MSR_C_LO_INIT_FID_VID     0x00010000
+#define MSR_C_LO_NEW_VID          0x00001f00
+#define MSR_C_LO_NEW_FID          0x0000002f
+#define MSR_C_LO_VID_SHIFT        8
+
+/* Field definitions within the FID VID High Control MSR : */
+#define MSR_C_HI_STP_GNT_TO       0x000fffff
+
+/* Field definitions within the FID VID Low Status MSR : */
+#define MSR_S_LO_CHANGE_PENDING   0x80000000	/* cleared when completed */
+#define MSR_S_LO_MAX_RAMP_VID     0x1f000000
+#define MSR_S_LO_MAX_FID          0x003f0000
+#define MSR_S_LO_START_FID        0x00003f00
+#define MSR_S_LO_CURRENT_FID      0x0000003f
+
+/* Field definitions within the FID VID High Status MSR : */
+#define MSR_S_HI_MAX_WORKING_VID  0x001f0000
+#define MSR_S_HI_START_VID        0x00001f00
+#define MSR_S_HI_CURRENT_VID      0x0000001f
+
+/* fids (frequency identifiers) are arranged in 2 tables - lo and hi */
+#define LO_FID_TABLE_TOP     6
+#define HI_FID_TABLE_BOTTOM  8
+
+#define LO_VCOFREQ_TABLE_TOP    1400	/* corresponding vco frequency
values */
+#define HI_VCOFREQ_TABLE_BOTTOM 1600
+
+#define MIN_FREQ_RESOLUTION  200 /* fids jump by 2 matching freq jumps by
200 */
+
+#define MAX_FID 0x2a	/* Spec only gives FID values as far as 5 GHz */
+
+#define LEAST_VID 0x1e	/* Lowest (numerically highest) useful vid value */
+
+#define MIN_FREQ 800	/* Min and max freqs, per spec */
+#define MAX_FREQ 5000
+
+#define INVALID_FID_MASK 0xffffffc1  /* not a valid fid if these bits are
set */
+
+#define INVALID_VID_MASK 0xffffffe0  /* not a valid vid if these bits are
set */
+
+#define STOP_GRANT_5NS 1 /* min poss memory access latency for voltage
change */
+
+#define PLL_LOCK_CONVERSION (1000/5) /* ms to ns, then divide by clock
period */
+
+#define MAXIMUM_VID_STEPS 1  /* Current cpus only allow a single step of
25mV */
+
+#define VST_UNITS_20US 20   /* Voltage Stabalization Time is in units of
20us */
+
+/*
+Version 1.4 of the PSB table. This table is constructed by BIOS and is
+to tell the OS's power management driver which VIDs and FIDs are
+supported by this particular processor. This information is obtained from
+the data sheets for each processor model by the system vendor and
+incorporated into the BIOS.
+If the data in the PSB / PST is wrong, then this driver will program the
+wrong values into hardware, which is very likely to lead to a crash.
+*/
+
+#define PSB_ID_STRING      "AMDK7PNOW!"
+#define PSB_ID_STRING_LEN  10
+
+#define PSB_VERSION_1_4  0x14
+
+struct psb_s {
+	u8 signature[10];
+	u8 tableversion;
+	u8 flags1;
+	u16 voltagestabilizationtime;
+	u8 flags2;
+	u8 numpst;
+	u32 cpuid;
+	u8 plllocktime;
+	u8 maxfid;
+	u8 maxvid;
+	u8 numpstates;
+};
+
+/* Pairs of fid/vid values are appended to the version 1.4 PSB table. */
+struct pst_s {
+	u8 fid;
+	u8 vid;
+};
+
+#ifdef DEBUG
+#define dprintk(msg...) printk(msg)
+#else
+#define dprintk(msg...) do { } while(0)
+#endif
+
+static inline int core_voltage_pre_transition(u32 reqvid);
+static inline int core_voltage_post_transition(u32 reqvid);
+static inline int core_frequency_transition(u32 reqfid);
+static int drv_verify(struct cpufreq_policy *pol);
+static int drv_target(struct cpufreq_policy *pol, unsigned targfreq,
+		      unsigned relation);
+static int __init drv_cpu_init(struct cpufreq_policy *pol);
diff -uNr linux-2.4.23-vanilla/arch/i386/kernel/setup.c
linux/arch/i386/kernel/setup.c
--- linux-2.4.23-vanilla/arch/i386/kernel/setup.c	2003-11-29
02:26:19.000000000 +0800
+++ linux/arch/i386/kernel/setup.c	2003-12-01 21:06:13.000000000 +0800
@@ -120,6 +120,7 @@
 #include <asm/dma.h>
 #include <asm/mpspec.h>
 #include <asm/mmu_context.h>
+#include <asm/ist.h>
 #include <asm/io_apic.h>
 #include <asm/edd.h>
 /*
@@ -158,6 +159,7 @@
 struct drive_info_struct { char dummy[32]; } drive_info;
 struct screen_info screen_info;
 struct apm_info apm_info;
+struct ist_info ist_info;
 struct sys_desc_table_struct {
 	unsigned short length;
 	unsigned char table[0];
@@ -201,6 +203,7 @@
 #define E820_MAP_NR (*(char*) (PARAM+E820NR))
 #define E820_MAP    ((struct e820entry *) (PARAM+E820MAP))
 #define APM_BIOS_INFO (*(struct apm_bios_info *) (PARAM+0x40))
+#define IST_INFO (*(struct ist_info *) (PARAM+0x60))
 #define DRIVE_INFO (*(struct drive_info_struct *) (PARAM+0x80))
 #define SYS_DESC_TABLE (*(struct sys_desc_table_struct*)(PARAM+0xa0))
 #define MOUNT_ROOT_RDONLY (*(unsigned short *) (PARAM+0x1F2))
@@ -1160,6 +1163,7 @@
  	drive_info = DRIVE_INFO;
  	screen_info = SCREEN_INFO;
 	apm_info.bios = APM_BIOS_INFO;
+	ist_info = IST_INFO;
 	if( SYS_DESC_TABLE.length != 0 ) {
 		MCA_bus = SYS_DESC_TABLE.table[3] &0x2;
 		machine_id = SYS_DESC_TABLE.table[0];
diff -uNr linux-2.4.23-vanilla/arch/i386/kernel/speedstep-centrino.c
linux/arch/i386/kernel/speedstep-centrino.c
--- linux-2.4.23-vanilla/arch/i386/kernel/speedstep-centrino.c	1970-01-01
07:30:00.000000000 +0730
+++ linux/arch/i386/kernel/speedstep-centrino.c	2003-08-25
22:58:48.000000000 +0800
@@ -0,0 +1,382 @@
+/*
+ * 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@xxxxxxxx>
+ *
+ * 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/string.h> /* BACKPORT: for strcmp */
+#include <asm/msr.h>
+#include <asm/processor.h>
+#include <asm/cpufeature.h>
+
+#define PFX		"speedstep-centrino: "
+#define MAINTAINER	"Jeremy Fitzhardinge <jeremy@xxxxxxxx>"
+
+/*#define CENTRINO_DEBUG*/
+
+#ifdef CENTRINO_DEBUG
+#define dprintk(msg...) printk(msg)
+#else
+#define dprintk(msg...) do { } while(0)
+#endif
+
+struct cpu_model
+{
+	const char	*model_name;
+	unsigned	max_freq; /* max clock in kHz */
+
+	struct cpufreq_frequency_table *op_points; /* clock/voltage pairs */
+};
+
+/* Operating points for current CPU */
+static const struct cpu_model *centrino_model;
+
+/* 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 */
+static struct cpufreq_frequency_table op_900[] =
+{
+	OP(600,  844),
+	OP(800,  988),
+	OP(900, 1004),
+	{ .frequency = CPUFREQ_TABLE_END }
+};
+
+/* Low Voltage Intel Pentium M processor 1.10GHz */
+static struct cpufreq_frequency_table op_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 */
+static struct cpufreq_frequency_table op_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 */
+static struct cpufreq_frequency_table op_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 */
+static struct cpufreq_frequency_table op_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 */
+static struct cpufreq_frequency_table op_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 */
+static struct cpufreq_frequency_table op_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 */
+static struct cpufreq_frequency_table op_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
+
+#define _CPU(max, name)	\
+	{ "Intel(R) Pentium(R) M processor " name "MHz", (max)*1000,
op_##max }
+#define CPU(max)	_CPU(max, #max)
+
+/* CPU models, their operating frequency range, and freq/voltage
+   operating points */
+static const struct cpu_model models[] = 
+{
+       _CPU( 900, " 900"),
+	CPU(1100),
+	CPU(1200),
+	CPU(1300),
+	CPU(1400),
+	CPU(1500),
+	CPU(1600),
+	CPU(1700),
+	{ 0, }
+};
+#undef CPU
+
+/* Extract clock in kHz from PERF_CTL value */
+static unsigned extract_clock(unsigned msr)
+{
+	msr = (msr >> 8) & 0xff;
+	return msr * 100000;
+}
+
+/* Return the current CPU frequency in kHz */
+static unsigned get_cur_freq(void)
+{
+	unsigned l, h;
+
+	rdmsr(MSR_IA32_PERF_STATUS, l, h);
+	return extract_clock(l);
+}
+
+static int centrino_cpu_init(struct cpufreq_policy *policy)
+{
+	unsigned freq;
+
+	if (policy->cpu != 0 || centrino_model == NULL)
+		return -ENODEV;
+
+	freq = get_cur_freq();
+
+	policy->policy = (freq == centrino_model->max_freq) ? 
+		CPUFREQ_POLICY_PERFORMANCE : 
+		CPUFREQ_POLICY_POWERSAVE;
+	policy->cpuinfo.transition_latency = 10; /* 10uS transition latency
*/
+	policy->cur = freq;
+
+	dprintk(KERN_INFO PFX "centrino_cpu_init: policy=%d cur=%dkHz\n",
+		policy->policy, policy->cur);
+	
+	return cpufreq_frequency_table_cpuinfo(policy,
centrino_model->op_points);
+}
+
+/**
+ * centrino_verify - verifies a new CPUFreq policy
+ * @freq: 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->op_points);
+}
+
+/**
+ * centrino_setpolicy - set a new CPUFreq policy
+ * @policy: new policy
+ *
+ * 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;
+	struct cpufreq_freqs	freqs;
+
+	if (centrino_model == NULL)
+		return -ENODEV;
+
+	if (cpufreq_frequency_table_target(policy,
centrino_model->op_points, target_freq,
+					   relation, &newstate))
+		return -EINVAL;
+
+	msr = centrino_model->op_points[newstate].index;
+	rdmsr(MSR_IA32_PERF_CTL, oldmsr, h);
+
+	if (msr == (oldmsr & 0xffff))
+		return 0;
+
+	/* Hm, old frequency can either be the last value we put in
+	   PERF_CTL, or whatever it is now. The trouble is that TM2
+	   can change it behind our back, which means we never get to
+	   see the speed change.  Reading back the current speed would
+	   tell us something happened, but it may leave the things on
+	   the notifier chain confused; we therefore stick to using
+	   the last programmed speed rather than the current speed for
+	   "old". 
+
+	   TODO: work out how the TCC interrupts work, and try to
+	   catch the CPU changing things under us.
+	*/
+	freqs.cpu = 0;
+	freqs.old = extract_clock(oldmsr);
+	freqs.new = extract_clock(msr);
+	
+	dprintk(KERN_INFO PFX "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);
+
+	return 0;
+}
+
+static struct cpufreq_driver centrino_driver = {
+	.name		= "centrino", /* should be speedstep-centrino, 
+					 but there's a 16 char limit */
+	.init		= centrino_cpu_init,
+	.verify 	= centrino_verify,
+	.target 	= centrino_target,
+};
+
+
+/**
+ * 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;
+	const struct cpu_model *model;
+	unsigned l, h;
+	int dummy, ecx;
+
+	/* backport info: we can't use cpu_has here, as cpuid(1) isn't
+	 * stored in 2.4
+	 */
+	cpuid(1,&dummy,&dummy,&ecx,&dummy);
+	if (!(ecx & (1<<7)))
+		return -ENODEV;
+
+	/* Only Intel Pentium M stepping 5 for now - add new CPUs as
+	   they appear after making sure they use PERF_CTL in the same
+	   way. */
+	if (cpu->x86_vendor != X86_VENDOR_INTEL ||
+	    cpu->x86        != 6 ||
+	    cpu->x86_model  != 9 ||
+	    cpu->x86_mask   != 5) {
+		printk(KERN_INFO PFX "found unsupported CPU with Enhanced
SpeedStep: "
+		       "send /proc/cpuinfo to " MAINTAINER "\n");
+		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);
+		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;
+		}
+	}
+
+	for(model = models; model->model_name != NULL; model++)
+		if (strcmp(cpu->x86_model_id, model->model_name) == 0)
+			break;
+	if (model->model_name == NULL) {
+		printk(KERN_INFO PFX "no support for CPU model \"%s\": "
+		       "send /proc/cpuinfo to " MAINTAINER "\n",
+		       cpu->x86_model_id);
+		return -ENOENT;
+	}
+
+	centrino_model = model;
+		
+	printk(KERN_INFO PFX "found \"%s\": max frequency: %dkHz\n",
+	       model->model_name, model->max_freq);
+
+	return cpufreq_register_driver(&centrino_driver);
+}
+
+static void __exit centrino_exit(void)
+{
+	cpufreq_unregister_driver(&centrino_driver);
+}
+
+MODULE_AUTHOR ("Jeremy Fitzhardinge <jeremy@xxxxxxxx>");
+MODULE_DESCRIPTION ("Enhanced SpeedStep driver for Intel Pentium M
processors.");
+MODULE_LICENSE ("GPL");
+
+module_init(centrino_init);
+module_exit(centrino_exit);
diff -uNr linux-2.4.23-vanilla/arch/i386/kernel/speedstep-ich.c
linux/arch/i386/kernel/speedstep-ich.c
--- linux-2.4.23-vanilla/arch/i386/kernel/speedstep-ich.c	1970-01-01
07:30:00.000000000 +0730
+++ linux/arch/i386/kernel/speedstep-ich.c	2003-08-25
22:58:48.000000000 +0800
@@ -0,0 +1,363 @@
+/*
+ * (C) 2001  Dave Jones, Arjan van de ven.
+ * (C) 2002 - 2003  Dominik Brodowski <linux@xxxxxxxx>
+ *
+ *  Licensed under the terms of the GNU GPL License version 2.
+ *  Based upon reverse engineered information, and on Intel documentation
+ *  for chipsets ICH2-M and ICH3-M.
+ *
+ *  Many thanks to Ducrot Bruno for finding and fixing the last
+ *  "missing link" for ICH2-M/ICH3-M support, and to Thomas Winkler 
+ *  for extensive testing.
+ *
+ *  BIG FAT DISCLAIMER: Work in progress code. Possibly *dangerous*
+ */
+
+
+/*********************************************************************
+ *                        SPEEDSTEP - DEFINITIONS                    *
+ *********************************************************************/
+
+#include <linux/kernel.h>
+#include <linux/module.h> 
+#include <linux/init.h>
+#include <linux/cpufreq.h>
+#include <linux/pci.h>
+#include <linux/slab.h>
+
+#include "speedstep-lib.h"
+
+
+/* speedstep_chipset:
+ *   It is necessary to know which chipset is used. As accesses to 
+ * this device occur at various places in this module, we need a 
+ * static struct pci_dev * pointing to that device.
+ */
+static struct pci_dev			*speedstep_chipset_dev;
+
+
+/* speedstep_processor
+ */
+static unsigned int			speedstep_processor = 0;
+
+
+/* 
+ *   There are only two frequency states for each processor. Values
+ * are in kHz for the time being.
+ */
+static struct cpufreq_frequency_table speedstep_freqs[] = {
+	{SPEEDSTEP_HIGH, 	0},
+	{SPEEDSTEP_LOW,		0},
+	{0,			CPUFREQ_TABLE_END},
+};
+
+
+/* DEBUG
+ *   Define it if you want verbose debug output, e.g. for bug reporting
+ */
+//#define SPEEDSTEP_DEBUG
+
+#ifdef SPEEDSTEP_DEBUG
+#define dprintk(msg...) printk(msg)
+#else
+#define dprintk(msg...) do { } while(0)
+#endif
+
+
+/**
+ * speedstep_set_state - set the SpeedStep state
+ * @state: new processor frequency state (SPEEDSTEP_LOW or SPEEDSTEP_HIGH)
+ *
+ *   Tries to change the SpeedStep state. 
+ */
+static void speedstep_set_state (unsigned int state, unsigned int notify)
+{
+	u32			pmbase;
+	u8			pm2_blk;
+	u8			value;
+	unsigned long		flags;
+	struct cpufreq_freqs	freqs;
+
+	if (!speedstep_chipset_dev || (state > 0x1))
+		return;
+
+	freqs.old = speedstep_get_processor_frequency(speedstep_processor);
+	freqs.new = speedstep_freqs[state].frequency;
+	freqs.cpu = 0; /* speedstep.c is UP only driver */
+	
+	if (notify)
+		cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
+
+	/* get PMBASE */
+	pci_read_config_dword(speedstep_chipset_dev, 0x40, &pmbase);
+	if (!(pmbase & 0x01))
+	{
+		printk(KERN_ERR "cpufreq: could not find speedstep
register\n");
+		return;
+	}
+
+	pmbase &= 0xFFFFFFFE;
+	if (!pmbase) {
+		printk(KERN_ERR "cpufreq: could not find speedstep
register\n");
+		return;
+	}
+
+	/* Disable IRQs */
+	local_irq_save(flags);
+
+	/* read state */
+	value = inb(pmbase + 0x50);
+
+	dprintk(KERN_DEBUG "cpufreq: read at pmbase 0x%x + 0x50 returned
0x%x\n", pmbase, value);
+
+	/* write new state */
+	value &= 0xFE;
+	value |= state;
+
+	dprintk(KERN_DEBUG "cpufreq: writing 0x%x to pmbase 0x%x + 0x50\n",
value, pmbase);
+
+	/* Disable bus master arbitration */
+	pm2_blk = inb(pmbase + 0x20);
+	pm2_blk |= 0x01;
+	outb(pm2_blk, (pmbase + 0x20));
+
+	/* Actual transition */
+	outb(value, (pmbase + 0x50));
+
+	/* Restore bus master arbitration */
+	pm2_blk &= 0xfe;
+	outb(pm2_blk, (pmbase + 0x20));
+
+	/* check if transition was successful */
+	value = inb(pmbase + 0x50);
+
+	/* Enable IRQs */
+	local_irq_restore(flags);
+
+	dprintk(KERN_DEBUG "cpufreq: read at pmbase 0x%x + 0x50 returned
0x%x\n", pmbase, value);
+
+	if (state == (value & 0x1)) {
+		dprintk (KERN_INFO "cpufreq: change to %u MHz succeeded\n",
(speedstep_get_processor_frequency(speedstep_processor) / 1000));
+	} else {
+		printk (KERN_ERR "cpufreq: change failed - I/O error\n");
+	}
+
+	if (notify)
+		cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
+
+	return;
+}
+
+
+/**
+ * speedstep_activate - activate SpeedStep control in the chipset
+ *
+ *   Tries to activate the SpeedStep status and control registers.
+ * Returns -EINVAL on an unsupported chipset, and zero on success.
+ */
+static int speedstep_activate (void)
+{
+	u16		value = 0;
+
+	if (!speedstep_chipset_dev)
+		return -EINVAL;
+
+	pci_read_config_word(speedstep_chipset_dev, 
+			     0x00A0, &value);
+	if (!(value & 0x08)) {
+		value |= 0x08;
+		dprintk(KERN_DEBUG "cpufreq: activating SpeedStep (TM)
registers\n");
+		pci_write_config_word(speedstep_chipset_dev, 
+				      0x00A0, value);
+	}
+
+	return 0;
+}
+
+
+/**
+ * speedstep_detect_chipset - detect the Southbridge which contains
SpeedStep logic
+ *
+ *   Detects PIIX4, ICH2-M and ICH3-M so far. The pci_dev points to 
+ * the LPC bridge / PM module which contains all power-management 
+ * functions. Returns the SPEEDSTEP_CHIPSET_-number for the detected
+ * chipset, or zero on failure.
+ */
+static unsigned int speedstep_detect_chipset (void)
+{
+	speedstep_chipset_dev = pci_find_subsys(PCI_VENDOR_ID_INTEL,
+			      PCI_DEVICE_ID_INTEL_82801DB_12, 
+			      PCI_ANY_ID,
+			      PCI_ANY_ID,
+			      NULL);
+	if (speedstep_chipset_dev)
+		return 4; /* 4-M */
+
+	speedstep_chipset_dev = pci_find_subsys(PCI_VENDOR_ID_INTEL,
+			      PCI_DEVICE_ID_INTEL_82801CA_12, 
+			      PCI_ANY_ID,
+			      PCI_ANY_ID,
+			      NULL);
+	if (speedstep_chipset_dev)
+		return 3; /* 3-M */
+
+
+	speedstep_chipset_dev = pci_find_subsys(PCI_VENDOR_ID_INTEL,
+			      PCI_DEVICE_ID_INTEL_82801BA_10,
+			      PCI_ANY_ID,
+			      PCI_ANY_ID,
+			      NULL);
+	if (speedstep_chipset_dev) {
+		/* speedstep.c causes lockups on Dell Inspirons 8000 and
+		 * 8100 which use a pretty old revision of the 82815 
+		 * host brige. Abort on these systems.
+		 */
+		static struct pci_dev	*hostbridge;
+		u8			rev = 0;
+
+		hostbridge  = pci_find_subsys(PCI_VENDOR_ID_INTEL,
+			      PCI_DEVICE_ID_INTEL_82815_MC,
+			      PCI_ANY_ID,
+			      PCI_ANY_ID,
+			      NULL);
+
+		if (!hostbridge)
+			return 2; /* 2-M */
+			
+		pci_read_config_byte(hostbridge, PCI_REVISION_ID, &rev);
+		if (rev < 5) {
+			dprintk(KERN_INFO "cpufreq: hostbridge does not
support speedstep\n");
+			speedstep_chipset_dev = NULL;
+			return 0;
+		}
+
+		return 2; /* 2-M */
+	}
+
+	return 0;
+}
+
+
+/**
+ * speedstep_setpolicy - set a new CPUFreq policy
+ * @policy: new policy
+ *
+ * Sets a new CPUFreq policy.
+ */
+static int speedstep_target (struct cpufreq_policy *policy,
+			     unsigned int target_freq,
+			     unsigned int relation)
+{
+	unsigned int	newstate = 0;
+
+	if (cpufreq_frequency_table_target(policy, &speedstep_freqs[0],
target_freq, relation, &newstate))
+		return -EINVAL;
+
+	speedstep_set_state(newstate, 1);
+
+	return 0;
+}
+
+
+/**
+ * speedstep_verify - verifies a new CPUFreq policy
+ * @freq: new policy
+ *
+ * Limit must be within speedstep_low_freq and speedstep_high_freq, with
+ * at least one border included.
+ */
+static int speedstep_verify (struct cpufreq_policy *policy)
+{
+	return cpufreq_frequency_table_verify(policy, &speedstep_freqs[0]);
+}
+
+
+static int speedstep_cpu_init(struct cpufreq_policy *policy)
+{
+	int		result = 0;
+	unsigned int	speed;
+
+	/* capability check */
+	if (policy->cpu != 0)
+		return -ENODEV;
+
+	/* detect low and high frequency */
+	result = speedstep_get_freqs(speedstep_processor,
+
&speedstep_freqs[SPEEDSTEP_LOW].frequency,
+
&speedstep_freqs[SPEEDSTEP_HIGH].frequency,
+				     &speedstep_set_state);
+	if (result)
+		return result;
+
+	/* get current speed setting */
+	speed = speedstep_get_processor_frequency(speedstep_processor);
+	if (!speed)
+		return -EIO;
+
+	dprintk(KERN_INFO "cpufreq: currently at %s speed setting - %i
MHz\n", 
+		(speed == speedstep_freqs[SPEEDSTEP_LOW].frequency) ? "low"
: "high",
+		(speed / 1000));
+
+	/* cpuinfo and default policy values */
+	policy->policy = (speed == speedstep_freqs[SPEEDSTEP_LOW].frequency)
? 
+		CPUFREQ_POLICY_POWERSAVE : CPUFREQ_POLICY_PERFORMANCE;
+	policy->cpuinfo.transition_latency = CPUFREQ_ETERNAL;
+	policy->cur = speed;
+
+	return cpufreq_frequency_table_cpuinfo(policy, &speedstep_freqs[0]);
+}
+
+
+static struct cpufreq_driver speedstep_driver = {
+	.name		= "speedstep",
+	.verify 	= speedstep_verify,
+	.target 	= speedstep_target,
+	.init		= speedstep_cpu_init,
+};
+
+
+/**
+ * speedstep_init - initializes the SpeedStep CPUFreq driver
+ *
+ *   Initializes the SpeedStep support. Returns -ENODEV on unsupported
+ * devices, -EINVAL on problems during initiatization, and zero on
+ * success.
+ */
+static int __init speedstep_init(void)
+{
+	/* detect processor */
+	speedstep_processor = speedstep_detect_processor();
+	if (!speedstep_processor)
+		return -ENODEV;
+
+	/* detect chipset */
+	if (!speedstep_detect_chipset()) {
+		printk(KERN_INFO "cpufreq: Intel(R) SpeedStep(TM) for this
chipset not (yet) available.\n");
+		return -ENODEV;
+	}
+
+	/* activate speedstep support */
+	if (speedstep_activate())
+		return -EINVAL;
+
+	return cpufreq_register_driver(&speedstep_driver);
+}
+
+
+/**
+ * speedstep_exit - unregisters SpeedStep support
+ *
+ *   Unregisters SpeedStep support.
+ */
+static void __exit speedstep_exit(void)
+{
+	cpufreq_unregister_driver(&speedstep_driver);
+}
+
+
+MODULE_AUTHOR ("Dave Jones <davej@xxxxxxxxxxxxxxxxx>, Dominik Brodowski
<linux@xxxxxxxx>");
+MODULE_DESCRIPTION ("Speedstep driver for Intel mobile processors on
chipsets with ICH-M southbridges.");
+MODULE_LICENSE ("GPL");
+
+module_init(speedstep_init);
+module_exit(speedstep_exit);
diff -uNr linux-2.4.23-vanilla/arch/i386/kernel/speedstep-lib.c
linux/arch/i386/kernel/speedstep-lib.c
--- linux-2.4.23-vanilla/arch/i386/kernel/speedstep-lib.c	1970-01-01
07:30:00.000000000 +0730
+++ linux/arch/i386/kernel/speedstep-lib.c	2003-08-25
22:58:48.000000000 +0800
@@ -0,0 +1,275 @@
+/*
+ * (C) 2002 - 2003 Dominik Brodowski <linux@xxxxxxxx>
+ *
+ *  Licensed under the terms of the GNU GPL License version 2.
+ *
+ *  Library for common functions for Intel SpeedStep v.1 and v.2 support
+ *
+ *  BIG FAT DISCLAIMER: Work in progress code. Possibly *dangerous*
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h> 
+#include <linux/init.h>
+#include <linux/cpufreq.h>
+#include <linux/pci.h>
+#include <linux/slab.h>
+
+#include <asm/msr.h>
+#include "speedstep-lib.h"
+
+
+/* DEBUG
+ *   Define it if you want verbose debug output, e.g. for bug reporting
+ */
+//#define SPEEDSTEP_DEBUG
+
+#ifdef SPEEDSTEP_DEBUG
+#define dprintk(msg...) printk(msg)
+#else
+#define dprintk(msg...) do { } while(0)
+#endif
+
+/*********************************************************************
+ *                   GET PROCESSOR CORE SPEED IN KHZ                 *
+ *********************************************************************/
+
+static unsigned int pentium3_get_frequency (unsigned int processor)
+{
+        /* See table 14 of p3_ds.pdf and table 22 of 29834003.pdf */
+	struct {
+		unsigned int ratio;	/* Frequency Multiplier (x10) */
+		u8 bitmap;	        /* power on configuration bits
+					   [27, 25:22] (in MSR 0x2a) */
+	} msr_decode_mult [] = {
+		{ 30, 0x01 },
+		{ 35, 0x05 },
+		{ 40, 0x02 },
+		{ 45, 0x06 },
+		{ 50, 0x00 },
+		{ 55, 0x04 },
+		{ 60, 0x0b },
+		{ 65, 0x0f },
+		{ 70, 0x09 },
+		{ 75, 0x0d },
+		{ 80, 0x0a },
+		{ 85, 0x26 },
+		{ 90, 0x20 },
+		{ 100, 0x2b },
+		{ 0, 0xff }     /* error or unknown value */
+	};
+
+	/* PIII(-M) FSB settings: see table b1-b of 24547206.pdf */
+	struct {
+		unsigned int value;     /* Front Side Bus speed in MHz */
+		u8 bitmap;              /* power on configuration bits [18:
19]
+					   (in MSR 0x2a) */
+	} msr_decode_fsb [] = {
+		{  66, 0x0 },
+		{ 100, 0x2 },
+		{ 133, 0x1 },
+		{   0, 0xff}
+	};
+
+	u32     msr_lo, msr_tmp;
+	int     i = 0, j = 0;
+
+	/* read MSR 0x2a - we only need the low 32 bits */
+	rdmsr(MSR_IA32_EBL_CR_POWERON, msr_lo, msr_tmp);
+	dprintk(KERN_DEBUG "speedstep-lib: P3 - MSR_IA32_EBL_CR_POWERON:
0x%x 0x%x\n", msr_lo, msr_tmp);
+	msr_tmp = msr_lo;
+
+	/* decode the FSB */
+	msr_tmp &= 0x00c0000;
+	msr_tmp >>= 18;
+	while (msr_tmp != msr_decode_fsb[i].bitmap) {
+		if (msr_decode_fsb[i].bitmap == 0xff)
+			return 0;
+		i++;
+	}
+
+	/* decode the multiplier */
+	if (processor == SPEEDSTEP_PROCESSOR_PIII_C_EARLY)
+		msr_lo &= 0x03c00000;
+	else
+		msr_lo &= 0x0bc00000;
+	msr_lo >>= 22;
+	while (msr_lo != msr_decode_mult[j].bitmap) {
+		if (msr_decode_mult[j].bitmap == 0xff)
+			return 0;
+		j++;
+	}
+
+	return (msr_decode_mult[j].ratio * msr_decode_fsb[i].value * 100);
+}
+
+
+static unsigned int pentium4_get_frequency(void)
+{
+	u32 msr_lo, msr_hi;
+
+	rdmsr(0x2c, msr_lo, msr_hi);
+
+	dprintk(KERN_DEBUG "speedstep-lib: P4 - MSR_EBC_FREQUENCY_ID: 0x%x
0x%x\n", msr_lo, msr_hi);
+
+	msr_lo >>= 24;
+	return (msr_lo * 100000);
+}
+
+ 
+unsigned int speedstep_get_processor_frequency(unsigned int processor)
+{
+	switch (processor) {
+	case SPEEDSTEP_PROCESSOR_P4M:
+		return pentium4_get_frequency();
+	case SPEEDSTEP_PROCESSOR_PIII_T:
+	case SPEEDSTEP_PROCESSOR_PIII_C:
+	case SPEEDSTEP_PROCESSOR_PIII_C_EARLY:
+		return pentium3_get_frequency(processor);
+	default:
+		return 0;
+	};
+	return 0;
+}
+EXPORT_SYMBOL_GPL(speedstep_get_processor_frequency);
+
+
+/*********************************************************************
+ *                 DETECT SPEEDSTEP-CAPABLE PROCESSOR                *
+ *********************************************************************/
+
+unsigned int speedstep_detect_processor (void)
+{
+	struct cpuinfo_x86 *c = cpu_data;
+	u32			ebx, msr_lo, msr_hi;
+
+	if ((c->x86_vendor != X86_VENDOR_INTEL) || 
+	    ((c->x86 != 6) && (c->x86 != 0xF)))
+		return 0;
+
+	if (c->x86 == 0xF) {
+		/* Intel Mobile Pentium 4-M
+		 * or Intel Mobile Pentium 4 with 533 MHz FSB */
+		if (c->x86_model != 2)
+			return 0;
+
+		if ((c->x86_mask != 4) && /* B-stepping [M-P4-M] */
+			(c->x86_mask != 7) && /* C-stepping [M-P4-M] */
+			(c->x86_mask != 9))   /* D-stepping [M-P4-M or
M-P4/533] */
+			return 0;
+
+		ebx = cpuid_ebx(0x00000001);
+		ebx &= 0x000000FF;
+		if ((ebx != 0x0e) && (ebx != 0x0f))
+			return 0;
+
+		return SPEEDSTEP_PROCESSOR_P4M;
+	}
+
+	switch (c->x86_model) {
+	case 0x0B: /* Intel PIII [Tualatin] */
+		/* cpuid_ebx(1) is 0x04 for desktop PIII, 
+		                   0x06 for mobile PIII-M */
+		ebx = cpuid_ebx(0x00000001);
+
+		ebx &= 0x000000FF;
+		if (ebx != 0x06)
+			return 0;
+
+		/* So far all PIII-M processors support SpeedStep. See
+		 * Intel's 24540640.pdf of June 2003 
+		 */
+
+		return SPEEDSTEP_PROCESSOR_PIII_T;
+
+	case 0x08: /* Intel PIII [Coppermine] */
+
+		/* all mobile PIII Coppermines have FSB 100 MHz
+		 * ==> sort out a few desktop PIIIs. */
+		rdmsr(MSR_IA32_EBL_CR_POWERON, msr_lo, msr_hi);
+		dprintk(KERN_DEBUG "cpufreq: Coppermine:
MSR_IA32_EBL_CR_POWERON is 0x%x, 0x%x\n", msr_lo, msr_hi);
+		msr_lo &= 0x00c0000;
+		if (msr_lo != 0x0080000)
+			return 0;
+
+		/*
+		 * If the processor is a mobile version,
+		 * platform ID has bit 50 set
+		 * it has SpeedStep technology if either
+		 * bit 56 or 57 is set
+		 */
+		rdmsr(MSR_IA32_PLATFORM_ID, msr_lo, msr_hi);
+		dprintk(KERN_DEBUG "cpufreq: Coppermine: MSR_IA32_PLATFORM
ID is 0x%x, 0x%x\n", msr_lo, msr_hi);
+		if ((msr_hi & (1<<18)) && (msr_hi & (3<<24))) {
+			if (c->x86_mask == 0x01)
+				return SPEEDSTEP_PROCESSOR_PIII_C_EARLY;
+			else
+				return SPEEDSTEP_PROCESSOR_PIII_C;
+		}
+
+	default:
+		return 0;
+	}
+}
+EXPORT_SYMBOL_GPL(speedstep_detect_processor);
+
+
+/*********************************************************************
+ *                     DETECT SPEEDSTEP SPEEDS                       *
+ *********************************************************************/
+
+unsigned int speedstep_get_freqs(unsigned int processor,
+				  unsigned int *low_speed,
+				  unsigned int *high_speed,
+				  void (*set_state) (unsigned int state,
+						     unsigned int notify)
+				 )
+{
+	unsigned int prev_speed;
+	unsigned int ret = 0;
+	unsigned long flags;
+
+	if ((!processor) || (!low_speed) || (!high_speed) || (!set_state))
+		return EINVAL;
+
+	/* get current speed */
+	prev_speed = speedstep_get_processor_frequency(processor);
+	if (!prev_speed)
+		return EIO;
+	
+	local_irq_save(flags);
+
+	/* switch to low state */
+	set_state(SPEEDSTEP_LOW, 0);
+	*low_speed = speedstep_get_processor_frequency(processor);
+	if (!*low_speed) {
+		ret = EIO;
+		goto out;
+	}
+
+	/* switch to high state */
+	set_state(SPEEDSTEP_HIGH, 0);
+	*high_speed = speedstep_get_processor_frequency(processor);
+	if (!*high_speed) {
+		ret = EIO;
+		goto out;
+	}
+
+	if (*low_speed == *high_speed) {
+		ret = ENODEV;
+		goto out;
+	}
+
+	/* switch to previous state, if necessary */
+	if (*high_speed != prev_speed)
+		set_state(SPEEDSTEP_LOW, 0);
+
+ out:
+	local_irq_restore(flags);
+	return (ret);
+}
+EXPORT_SYMBOL_GPL(speedstep_get_freqs);
+
+MODULE_AUTHOR ("Dominik Brodowski <linux@xxxxxxxx>");
+MODULE_DESCRIPTION ("Library for Intel SpeedStep 1 or 2 cpufreq drivers.");
+MODULE_LICENSE ("GPL");
diff -uNr linux-2.4.23-vanilla/arch/i386/kernel/speedstep-lib.h
linux/arch/i386/kernel/speedstep-lib.h
--- linux-2.4.23-vanilla/arch/i386/kernel/speedstep-lib.h	1970-01-01
07:30:00.000000000 +0730
+++ linux/arch/i386/kernel/speedstep-lib.h	2003-08-25
22:58:48.000000000 +0800
@@ -0,0 +1,41 @@
+/*
+ * (C) 2002 - 2003 Dominik Brodowski <linux@xxxxxxxx>
+ *
+ *  Licensed under the terms of the GNU GPL License version 2.
+ *
+ *  Library for common functions for Intel SpeedStep v.1 and v.2 support
+ *
+ *  BIG FAT DISCLAIMER: Work in progress code. Possibly *dangerous*
+ */
+
+
+
+/* processors */
+
+#define SPEEDSTEP_PROCESSOR_PIII_C_EARLY	0x00000001  /* Coppermine
core */
+#define SPEEDSTEP_PROCESSOR_PIII_C		0x00000002  /* Coppermine
core */
+#define SPEEDSTEP_PROCESSOR_PIII_T 		0x00000003  /* Tualatin core
*/
+#define SPEEDSTEP_PROCESSOR_P4M			0x00000004  /* P4-M
with 100 MHz FSB */
+
+/* speedstep states -- only two of them */
+
+#define SPEEDSTEP_HIGH                  0x00000000
+#define SPEEDSTEP_LOW                   0x00000001
+
+
+/* detect a speedstep-capable processor */
+extern unsigned int speedstep_detect_processor (void);
+
+/* detect the current speed (in khz) of the processor */
+extern unsigned int speedstep_get_processor_frequency(unsigned int
processor);
+
+
+/* detect the low and high speeds of the processor. The callback 
+ * set_state"'s first argument is either SPEEDSTEP_HIGH or 
+ * SPEEDSTEP_LOW; the second argument is zero so that no 
+ * cpufreq_notify_transition calls are initiated.
+ */
+extern unsigned int speedstep_get_freqs(unsigned int processor,
+	  unsigned int *low_speed,
+	  unsigned int *high_speed,
+	  void (*set_state) (unsigned int state, unsigned int notify));
diff -uNr linux-2.4.23-vanilla/arch/i386/kernel/speedstep-piix4.c
linux/arch/i386/kernel/speedstep-piix4.c
--- linux-2.4.23-vanilla/arch/i386/kernel/speedstep-piix4.c	1970-01-01
07:30:00.000000000 +0730
+++ linux/arch/i386/kernel/speedstep-piix4.c	2003-08-28
21:32:50.000000000 +0800
@@ -0,0 +1,342 @@
+/*
+ * (C) 2001  Dave Jones, Arjan van de ven.
+ * (C) 2002 - 2003  Dominik Brodowski <linux@xxxxxxxx>
+ * (C) 2002 - 2003  Bruno Ducrot
+ *
+ *  Licensed under the terms of the GNU GPL License version 2.
+ *
+ *  BIG FAT DISCLAIMER: Work in progress code. Possibly *dangerous*
+ */
+
+
+/*********************************************************************
+ *                        SPEEDSTEP - DEFINITIONS                    *
+ *********************************************************************/
+
+#include <linux/kernel.h>
+#include <linux/module.h> 
+#include <linux/init.h>
+#include <linux/cpufreq.h>
+#include <linux/pci.h>
+#include <linux/slab.h>
+
+#include "speedstep-lib.h"
+
+
+/* speedstep_chipset:
+ *   It is necessary to know which chipset is used. As accesses to 
+ * this device occur at various places in this module, we need a 
+ * static struct pci_dev * pointing to that device.
+ */
+static struct pci_dev			*speedstep_chipset_dev;
+
+
+/* speedstep_processor
+ */
+static unsigned int			speedstep_processor = 0;
+
+
+/* the GPO line the Intel SpeedStep Control Logic is connected to */
+static int				gpo_hilo = -1;
+
+/* 
+ *   There are only two frequency states for each processor. Values
+ * are in kHz for the time being.
+ */
+static struct cpufreq_frequency_table speedstep_freqs[] = {
+	{SPEEDSTEP_HIGH, 	0},
+	{SPEEDSTEP_LOW,		0},
+	{0,			CPUFREQ_TABLE_END},
+};
+
+
+/* DEBUG
+ *   Define it if you want verbose debug output, e.g. for bug reporting
+ */
+//#define SPEEDSTEP_DEBUG
+
+#ifdef SPEEDSTEP_DEBUG
+#define dprintk(msg...) printk(msg)
+#else
+#define dprintk(msg...) do { } while(0)
+#endif
+
+
+/**
+ * speedstep_set_state - set the SpeedStep state
+ * @state: new processor frequency state (SPEEDSTEP_LOW or SPEEDSTEP_HIGH)
+ *
+ *   Tries to change the SpeedStep state. 
+ */
+static void speedstep_set_state (unsigned int state, unsigned int notify)
+{
+	u32			pmbase;
+	u32			pcntrl, pcntrl_save;
+	u32			cntb, cntb_save;
+	u32			val32;
+	u8			pm2_blk;
+	unsigned long		flags;
+	struct cpufreq_freqs	freqs;
+
+	if (!speedstep_chipset_dev || (state > 0x1))
+		return;
+
+	freqs.old = speedstep_get_processor_frequency(speedstep_processor);
+	freqs.new = speedstep_freqs[state].frequency;
+	freqs.cpu = 0; /* speedstep.c is UP only driver */
+	
+	if (notify)
+		cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
+
+	/* get PMBASE */
+	pci_read_config_dword(speedstep_chipset_dev, 0x40, &pmbase);
+	if (!(pmbase & 0x01)) {
+		printk(KERN_ERR "cpufreq: could not find speedstep
register\n");
+		return;
+	}
+
+	pmbase &= 0xFFFFFFFE;
+	if (!pmbase) {
+		printk(KERN_ERR "cpufreq: could not find speedstep
register\n");
+		return;
+	}
+
+	/* Disable IRQs */
+	local_irq_save(flags);
+
+	pci_read_config_dword(speedstep_chipset_dev, 0x48, &cntb);
+	cntb_save = cntb;
+
+	cntb &= ~(1 << 14);     /* ZZ_EN = 0 */
+	cntb &= ~(0x0000001f << 6);
+	cntb |= (0x1f << 6);    /* CPU_LCK */
+	cntb |= (1 << 5);       /* CPU_SEL = 1 */
+
+	dprintk(KERN_DEBUG "cntb before: 0x%.8x\n", cntb_save);
+	dprintk(KERN_DEBUG "cntb after:  0x%.8x\n", cntb);
+	pci_write_config_dword(speedstep_chipset_dev, 0x48, cntb);
+
+	/* read state */
+	val32 = inl_p(pmbase + 0x34);
+	dprintk(KERN_DEBUG "reading %.8x from %.4x\n", val32, pmbase +
0x34);
+
+	/* write new state */
+	val32 &= ~(1 << gpo_hilo);
+	val32 |= state << gpo_hilo;
+	dprintk(KERN_DEBUG "new gpo: %.8x\n", val32);
+
+	/* disable bus master arbitration */
+	pm2_blk = inb(0x22);
+	pm2_blk |= 0x01;
+	outb_p(pm2_blk, 0x22);
+
+	/* program southbridge for a deep sleep transition */
+	pcntrl_save = pcntrl = inl(pmbase + 0x10);
+
+	pcntrl &= ~(1 << 13);           /* CLKRUN_EN = 0 */
+	pcntrl |= 1 << 12;              /* STPCLK_EN = 1 */
+	pcntrl |= 1 << 11;              /* SLEEP_EN = 1 */
+	pcntrl &= ~(1 << 10);           /* BURST_EN = 0 */
+	pcntrl |= 1 << 9;               /* CC_EN = 1 */
+	pcntrl &= ~(1 << 4);            /* THT_EN = 0 */
+
+	dprintk(KERN_DEBUG "pcntrl before: 0x%.8x\n", pcntrl_save);
+	dprintk(KERN_DEBUG "pcntrl after:  0x%.8x\n", pcntrl);
+
+	outl_p(pcntrl, pmbase + 0x10);
+
+	/* Write to GPO (HI/LO)#, then enter C3. */
+	outl_p(val32, pmbase + 0x34);
+	wbinvd();
+	inb_p(pmbase + 0x15);
+
+	/* We may have to do a dummy read after C3. */
+	inb(0x80);
+
+	/* cleanup */
+
+	outl_p(pcntrl_save, pmbase + 0x10);
+	/* restore bus master arbitration */
+
+	pm2_blk &= 0xfe;
+	outb_p(pm2_blk, 0x22);
+
+	/* cleanup southbridge */
+	pci_write_config_dword(speedstep_chipset_dev, 0x48, cntb_save);
+
+	/* enable IRQs */
+
+	local_irq_restore(flags);
+
+	if (speedstep_get_processor_frequency(speedstep_processor) !=
freqs.old) {
+		dprintk(KERN_INFO "cpufreq: change to %u MHz succeded\n",
(freqs.new / 1000));
+	} else {
+		printk(KERN_ERR "cpufreq: change failed\n");
+	}
+
+	if (notify)
+		cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
+
+	return;
+}
+
+
+/**
+ * speedstep_detect_chipset - detect the Southbridge which contains
SpeedStep logic
+ *
+ *   Detects PIIX4. The pci_dev points to the PM module which contains all
+ * power-management functions. Returns the SPEEDSTEP_CHIPSET_-number for
+ * the detected chipset, or zero on failure.
+ */
+static unsigned int speedstep_detect_chipset (void)
+{
+	speedstep_chipset_dev = pci_find_subsys(PCI_VENDOR_ID_INTEL,
+			      PCI_DEVICE_ID_INTEL_82371AB_3,
+			      PCI_ANY_ID,
+			      PCI_ANY_ID,
+			      NULL);
+	if (speedstep_chipset_dev)
+		return 1; /* 440BX */
+
+	speedstep_chipset_dev = pci_find_subsys(PCI_VENDOR_ID_INTEL,
+			      PCI_DEVICE_ID_INTEL_82443MX_3,
+			      PCI_ANY_ID,
+			      PCI_ANY_ID,
+			      NULL);
+	if (speedstep_chipset_dev)
+		return 1; /* 440MX */
+
+	return 0;
+}
+
+
+/**
+ * speedstep_setpolicy - set a new CPUFreq policy
+ * @policy: new policy
+ *
+ * Sets a new CPUFreq policy.
+ */
+static int speedstep_target (struct cpufreq_policy *policy,
+			     unsigned int target_freq,
+			     unsigned int relation)
+{
+	unsigned int	newstate = 0;
+
+	if (cpufreq_frequency_table_target(policy, &speedstep_freqs[0],
target_freq, relation, &newstate))
+		return -EINVAL;
+
+	speedstep_set_state(newstate, 1);
+
+	return 0;
+}
+
+
+/**
+ * speedstep_verify - verifies a new CPUFreq policy
+ * @freq: new policy
+ *
+ * Limit must be within speedstep_low_freq and speedstep_high_freq, with
+ * at least one border included.
+ */
+static int speedstep_verify (struct cpufreq_policy *policy)
+{
+	return cpufreq_frequency_table_verify(policy, &speedstep_freqs[0]);
+}
+
+
+static int speedstep_cpu_init(struct cpufreq_policy *policy)
+{
+	int		result = 0;
+	unsigned int	speed;
+
+	/* capability check */
+	if (policy->cpu != 0)
+		return -ENODEV;
+
+	/* detect low and high frequency */
+	result = speedstep_get_freqs(speedstep_processor,
+
&speedstep_freqs[SPEEDSTEP_LOW].frequency,
+
&speedstep_freqs[SPEEDSTEP_HIGH].frequency,
+				     &speedstep_set_state);
+	if (result)
+		return result;
+
+	/* get current speed setting */
+	speed = speedstep_get_processor_frequency(speedstep_processor);
+	if (!speed)
+		return -EIO;
+
+	dprintk(KERN_INFO "cpufreq: currently at %s speed setting - %i
MHz\n", 
+		(speed == speedstep_freqs[SPEEDSTEP_LOW].frequency) ? "low"
: "high",
+		(speed / 1000));
+
+	/* cpuinfo and default policy values */
+	policy->policy = (speed == speedstep_freqs[SPEEDSTEP_LOW].frequency)
? 
+		CPUFREQ_POLICY_POWERSAVE : CPUFREQ_POLICY_PERFORMANCE;
+	policy->cpuinfo.transition_latency = CPUFREQ_ETERNAL;
+	policy->cur = speed;
+
+	return cpufreq_frequency_table_cpuinfo(policy, &speedstep_freqs[0]);
+}
+
+
+static struct cpufreq_driver speedstep_driver = {
+	.name		= "speedstep",
+	.verify 	= speedstep_verify,
+	.target 	= speedstep_target,
+	.init		= speedstep_cpu_init,
+};
+
+
+/**
+ * speedstep_init - initializes the SpeedStep CPUFreq driver
+ *
+ *   Initializes the SpeedStep support. Returns -ENODEV on unsupported
+ * devices, -EINVAL on problems during initiatization, and zero on
+ * success.
+ */
+static int __init speedstep_init(void)
+{
+	/* detect processor */
+	speedstep_processor = speedstep_detect_processor();
+	if (!speedstep_processor)
+		return -ENODEV;
+
+	/* detect chipset */
+	if (!speedstep_detect_chipset()) {
+		printk(KERN_INFO "cpufreq: Intel(R) SpeedStep(TM) for this
chipset not (yet) available.\n");
+		return -ENODEV;
+	}
+
+	/* activate speedstep support */
+	if (gpo_hilo < 0 || gpo_hilo > 32) {
+		printk(KERN_INFO "SpeedStep on PIIX4 southbridge need a
gpo_hilo option.\n");
+		return -EINVAL;
+	}
+
+#ifdef CONFIG_APM
+	printk(KERN_WARNING "SpeedStep and APM seems to be problematic.
Continuing anyway.\n");
+#endif
+
+	return cpufreq_register_driver(&speedstep_driver);
+}
+
+
+/**
+ * speedstep_exit - unregisters SpeedStep support
+ *
+ *   Unregisters SpeedStep support.
+ */
+static void __exit speedstep_exit(void)
+{
+	cpufreq_unregister_driver(&speedstep_driver);
+}
+
+MODULE_PARM (gpo_hilo, "i");
+
+MODULE_AUTHOR ("Bruno Ducrot");
+MODULE_DESCRIPTION ("Speedstep driver for Intel mobile processors on
chipsets with PIIX4 southbridges.");
+MODULE_LICENSE ("GPL");
+
+module_init(speedstep_init);
+module_exit(speedstep_exit);
diff -uNr linux-2.4.23-vanilla/arch/i386/kernel/speedstep-smi.c
linux/arch/i386/kernel/speedstep-smi.c
--- linux-2.4.23-vanilla/arch/i386/kernel/speedstep-smi.c	1970-01-01
07:30:00.000000000 +0730
+++ linux/arch/i386/kernel/speedstep-smi.c	2003-11-10
19:22:44.000000000 +0800
@@ -0,0 +1,365 @@
+/*
+ * Intel SpeedStep SMI driver.
+ *
+ * (C) 2003  Hiroshi Miura <miura@xxxxxxxxxx>
+ *
+ *  Licensed under the terms of the GNU GPL License version 2.
+ *
+ */
+
+
+/*********************************************************************
+ *                        SPEEDSTEP - DEFINITIONS                    *
+ *********************************************************************/
+
+#include <linux/kernel.h>
+#include <linux/module.h> 
+#include <linux/init.h>
+#include <linux/cpufreq.h>
+#include <linux/pci.h>
+#include <linux/slab.h>
+#include <asm/ist.h>
+
+#include "speedstep-lib.h"
+
+#define PFX "speedstep-smi: "
+
+/* speedstep system management interface port/command.
+ *
+ * These parameters are got from IST-SMI BIOS call.
+ * If user gives it, these are used.
+ * 
+ */
+static int		smi_port	= 0;
+static int		smi_cmd		= 0;
+static unsigned int	smi_sig		= 0;
+
+
+/* 
+ *   There are only two frequency states for each processor. Values
+ * are in kHz for the time being.
+ */
+static struct cpufreq_frequency_table speedstep_freqs[] = {
+	{SPEEDSTEP_HIGH, 	0},
+	{SPEEDSTEP_LOW,		0},
+	{0,			CPUFREQ_TABLE_END},
+};
+
+#define GET_SPEEDSTEP_OWNER 0
+#define GET_SPEEDSTEP_STATE 1
+#define SET_SPEEDSTEP_STATE 2
+#define GET_SPEEDSTEP_FREQS 4
+
+/* DEBUG
+ *   Define it if you want verbose debug output, e.g. for bug reporting
+ */
+#define SPEEDSTEP_DEBUG
+
+#ifdef SPEEDSTEP_DEBUG
+#define dprintk(msg...) printk(msg)
+#else
+#define dprintk(msg...) do { } while(0)
+#endif
+
+/**
+ * speedstep_smi_ownership
+ */
+static int speedstep_smi_ownership (void)
+{
+	u32 command, result, magic;
+	u32 function = GET_SPEEDSTEP_OWNER;
+	unsigned char magic_data[] = "Copyright (c) 1999 Intel Corporation";
+
+	command = (smi_sig & 0xffffff00) | (smi_cmd & 0xff);
+	magic = virt_to_phys(magic_data);
+
+	__asm__ __volatile__(
+		"out %%al, (%%dx)\n"
+		: "=D" (result)
+		: "a" (command), "b" (function), "c" (0), "d" (smi_port),
"D" (0), "S" (magic)
+	);
+
+	return result;
+}
+
+/**
+ * speedstep_smi_get_freqs - get SpeedStep preferred & current freq.
+ *
+ */
+static int speedstep_smi_get_freqs (unsigned int *low, unsigned int *high)
+{
+	u32 command, result, edi, high_mhz, low_mhz;
+	u32 state=0;
+	u32 function = GET_SPEEDSTEP_FREQS;
+
+	command = (smi_sig & 0xffffff00) | (smi_cmd & 0xff);
+
+	__asm__ __volatile__("movl $0, %%edi\n"
+		"out %%al, (%%dx)\n"
+		: "=a" (result), "=b" (high_mhz), "=c" (low_mhz), "=d"
(state), "=D" (edi)
+		: "a" (command), "b" (function), "c" (state), "d"
(smi_port), "S" (0)
+	);
+	*high = high_mhz * 1000;
+	*low  = low_mhz  * 1000;
+
+	return result;
+} 
+
+/**
+ * speedstep_get_state - set the SpeedStep state
+ * @state: processor frequency state (SPEEDSTEP_LOW or SPEEDSTEP_HIGH)
+ *
+ */
+static int speedstep_get_state (void)
+{
+	u32 function=GET_SPEEDSTEP_STATE;
+	u32 result, state, edi, command;
+
+	command = (smi_sig & 0xffffff00) | (smi_cmd & 0xff);
+
+	__asm__ __volatile__("movl $0, %%edi\n"
+		"out %%al, (%%dx)\n"
+		: "=a" (result), "=b" (state), "=D" (edi)
+		: "a" (command), "b" (function), "c" (0), "d" (smi_port),
"S" (0)
+	);
+
+	return state;
+}
+
+/**
+ * speedstep_set_state - set the SpeedStep state
+ * @state: new processor frequency state (SPEEDSTEP_LOW or SPEEDSTEP_HIGH)
+ *
+ */
+static void speedstep_set_state (unsigned int state, unsigned int notify)
+{
+	unsigned int old_state, result, command, new_state;
+	unsigned long flags;
+	struct cpufreq_freqs freqs;
+	unsigned int function=SET_SPEEDSTEP_STATE;
+
+	if (state > 0x1)
+		return;
+
+	old_state = speedstep_get_state();
+	freqs.old = speedstep_freqs[old_state].frequency;
+	freqs.new = speedstep_freqs[state].frequency;
+	freqs.cpu = 0; /* speedstep.c is UP only driver */
+
+	if (old_state == state)
+		return;
+
+	if (notify)
+		cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
+
+	/* Disable IRQs */
+	local_irq_save(flags);
+
+	command = (smi_sig & 0xffffff00) | (smi_cmd & 0xff);
+	__asm__ __volatile__(
+		"movl $0, %%edi\n"
+		"out %%al, (%%dx)\n"
+		: "=b" (new_state), "=D" (result)
+		: "a" (command), "b" (function), "c" (state), "d"
(smi_port), "S" (0)
+	);
+
+	/* enable IRQs */
+	local_irq_restore(flags);
+
+	if (new_state == state) {
+		dprintk(KERN_INFO "cpufreq: change to %u MHz succeded\n",
(freqs.new / 1000));
+	} else {
+		printk(KERN_ERR "cpufreq: change failed\n");
+	}
+
+	if (notify)
+		cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
+
+	return;
+}
+
+
+/**
+ * speedstep_target - set a new CPUFreq policy
+ * @policy: new policy
+ * @target_freq: new freq
+ * @relation: 
+ *
+ * Sets a new CPUFreq policy/freq.
+ */
+static int speedstep_target (struct cpufreq_policy *policy,
+			unsigned int target_freq, unsigned int relation)
+{
+	unsigned int newstate = 0;
+
+	if (cpufreq_frequency_table_target(policy, &speedstep_freqs[0],
target_freq, relation, &newstate))
+		return -EINVAL;
+
+	speedstep_set_state(newstate, 1);
+
+	return 0;
+}
+
+
+/**
+ * speedstep_verify - verifies a new CPUFreq policy
+ * @freq: new policy
+ *
+ * Limit must be within speedstep_low_freq and speedstep_high_freq, with
+ * at least one border included.
+ */
+static int speedstep_verify (struct cpufreq_policy *policy)
+{
+	return cpufreq_frequency_table_verify(policy, &speedstep_freqs[0]);
+}
+
+
+static int speedstep_cpu_init(struct cpufreq_policy *policy)
+{
+	int result;
+	unsigned int speed,state;
+
+	/* capability check */
+	if (policy->cpu != 0)
+		return -ENODEV;
+
+	result = speedstep_smi_ownership();
+
+	if (result)
+		dprintk(KERN_INFO "cpufreq: fails an aquiring ownership of a
SMI interface.\n");
+
+	/* detect low and high frequency */
+	result =
speedstep_smi_get_freqs(&speedstep_freqs[SPEEDSTEP_LOW].frequency,
+				&speedstep_freqs[SPEEDSTEP_HIGH].frequency);
+	if (result) {
+		/* fall back to speedstep_lib.c dection mechanism: try both
states out */
+		unsigned int speedstep_processor =
speedstep_detect_processor();
+
+		dprintk(KERN_INFO PFX "could not detect low and high
frequencies by SMI call.\n");
+		if (!speedstep_processor)
+			return -ENODEV;
+
+		result = speedstep_get_freqs(speedstep_processor,
+				&speedstep_freqs[SPEEDSTEP_LOW].frequency,
+				&speedstep_freqs[SPEEDSTEP_HIGH].frequency,
+				&speedstep_set_state);
+
+		if (result) {
+			dprintk(KERN_INFO PFX "could not detect two
different speeds -- aborting.\n");
+			return result;
+		} else
+			dprintk(KERN_INFO PFX "workaround worked.\n");
+	}
+
+	/* get current speed setting */
+	state = speedstep_get_state();
+	speed = speedstep_freqs[state].frequency;
+
+	dprintk(KERN_INFO "cpufreq: currently at %s speed setting - %i
MHz\n", 
+		(speed == speedstep_freqs[SPEEDSTEP_LOW].frequency) ? "low"
: "high",
+		(speed / 1000));
+
+	/* cpuinfo and default policy values */
+	/* TBD: clean up when the new core is commited. */
+	policy->policy = (speed == speedstep_freqs[SPEEDSTEP_LOW].frequency)
? 
+		CPUFREQ_POLICY_POWERSAVE : CPUFREQ_POLICY_PERFORMANCE;
+	policy->cpuinfo.transition_latency = CPUFREQ_ETERNAL;
+	policy->cur = speed;
+
+	return cpufreq_frequency_table_cpuinfo(policy, &speedstep_freqs[0]);
+}
+
+/* have to be fixed properly */
+#if 0
+static int speedstep_resume(struct cpufreq_policy *policy)
+{
+	int result = speedstep_smi_ownership();
+
+	if (result)
+		dprintk(KERN_INFO "cpufreq: fails an aquiring ownership of a
SMI interface.\n");
+
+	return result;
+}
+#endif
+
+
+static struct cpufreq_driver speedstep_driver = {
+	.name		= "speedstep-smi",
+	.verify 	= speedstep_verify,
+	.target 	= speedstep_target,
+	.init		= speedstep_cpu_init,
+	/* .resume		= speedstep_resume, */
+};
+
+/**
+ * speedstep_init - initializes the SpeedStep CPUFreq driver
+ *
+ *   Initializes the SpeedStep support. Returns -ENODEV on unsupported
+ * BIOS, -EINVAL on problems during initiatization, and zero on
+ * success.
+ */
+static int __init speedstep_init(void)
+{
+    struct cpuinfo_x86 *c = cpu_data;
+
+    if (c->x86_vendor != X86_VENDOR_INTEL) {
+		printk (KERN_INFO PFX "No Intel CPU detected.\n");
+		return -ENODEV;
+	}
+
+	dprintk(KERN_DEBUG PFX "signature:0x%.8lx, command:0x%.8lx,
event:0x%.8lx, perf_level:0x%.8lx.\n", 
+		ist_info.signature, ist_info.command, ist_info.event,
ist_info.perf_level);
+
+
+	/* Error if no IST-SMI BIOS or no PARM 
+		 sig= 'ISGE' aka 'Intel Speedstep Gate E' */
+	if ((ist_info.signature !=  0x47534943) && ( 
+	    (smi_port == 0) || (smi_cmd == 0)))
+		return -ENODEV;
+
+	if (smi_sig == 1)
+		smi_sig = 0x47534943;
+	else
+		smi_sig = ist_info.signature;
+
+	/* setup smi_port from MODLULE_PARM or BIOS */
+	if ((smi_port > 0xff) || (smi_port < 0)) {
+		return -EINVAL;
+	} else if (smi_port == 0) {
+		smi_port = ist_info.command & 0xff;
+	}
+
+	if ((smi_cmd > 0xff) || (smi_cmd < 0)) {
+		return -EINVAL;
+	} else if (smi_cmd == 0) {
+		smi_cmd = (ist_info.command >> 16) & 0xff;
+	}
+
+	return cpufreq_register_driver(&speedstep_driver);
+}
+
+
+/**
+ * speedstep_exit - unregisters SpeedStep support
+ *
+ *   Unregisters SpeedStep support.
+ */
+static void __exit speedstep_exit(void)
+{
+	cpufreq_unregister_driver(&speedstep_driver);
+}
+
+MODULE_PARM(smi_port, "i");
+MODULE_PARM(smi_cmd, "i");
+MODULE_PARM(smi_sig, "i");
+
+MODULE_PARM_DESC(smi_port, "Override the BIOS-given IST port with this
value -- Intel's default setting is 0xb2");
+MODULE_PARM_DESC(smi_cmd, "Override the BIOS-given IST command with this
value -- Intel's default setting is 0x82");
+MODULE_PARM_DESC(smi_sig, "Set to 1 to fake the IST signature when using
the SMI interface.");
+
+MODULE_AUTHOR ("Hiroshi Miura");
+MODULE_DESCRIPTION ("Speedstep driver for IST applet SMI interface.");
+MODULE_LICENSE ("GPL");
+
+module_init(speedstep_init);
+module_exit(speedstep_exit);
diff -uNr linux-2.4.23-vanilla/arch/i386/kernel/time.c
linux/arch/i386/kernel/time.c
--- linux-2.4.23-vanilla/arch/i386/kernel/time.c	2003-11-29
02:26:19.000000000 +0800
+++ linux/arch/i386/kernel/time.c	2003-12-01 20:38:29.000000000 +0800
@@ -55,6 +55,7 @@
 #include <linux/mc146818rtc.h>
 #include <linux/timex.h>
 #include <linux/config.h>
+#include <linux/cpufreq.h>
 
 #include <asm/fixmap.h>
 #include <asm/cobalt.h>
@@ -835,6 +836,49 @@
 	return 0;
 }
 
+#ifdef CONFIG_CPU_FREQ
+static unsigned int  ref_freq = 0;
+static unsigned long loops_per_jiffy_ref = 0;
+
+#ifndef CONFIG_SMP
+static unsigned long fast_gettimeoffset_ref = 0;
+static unsigned long cpu_khz_ref = 0;
+#endif
+
+static int
+time_cpufreq_notifier(struct notifier_block *nb, unsigned long val,
+		       void *data)
+{
+	struct cpufreq_freqs *freq = data;
+
+	if (!ref_freq) {
+		ref_freq = freq->old;
+		loops_per_jiffy_ref = cpu_data[freq->cpu].loops_per_jiffy;
+#ifndef CONFIG_SMP
+		fast_gettimeoffset_ref = fast_gettimeoffset_quotient;
+		cpu_khz_ref = cpu_khz;
+#endif
+	}
+
+	if ((val == CPUFREQ_PRECHANGE  && freq->old < freq->new) ||
+	    (val == CPUFREQ_POSTCHANGE && freq->old > freq->new)) {
+		cpu_data[freq->cpu].loops_per_jiffy =
cpufreq_scale(loops_per_jiffy_ref, ref_freq, freq->new);
+#ifndef CONFIG_SMP
+		if (use_tsc) {
+			fast_gettimeoffset_quotient =
cpufreq_scale(fast_gettimeoffset_ref, freq->new, ref_freq);
+			cpu_khz = cpufreq_scale(cpu_khz_ref, ref_freq,
freq->new);
+		}
+#endif
+	}
+
+	return 0;
+}
+
+static struct notifier_block time_cpufreq_notifier_block = {
+	.notifier_call	= time_cpufreq_notifier
+};
+#endif
+
 void __init time_init(void)
 {
 	extern int x86_udelay_tsc;
@@ -903,6 +947,9 @@
 	                	"0" (eax), "1" (edx));
 				printk("Detected %lu.%03lu MHz
processor.\n", cpu_khz / 1000, cpu_khz % 1000);
 			}
+#if defined(CONFIG_CPU_FREQ)
+
cpufreq_register_notifier(&time_cpufreq_notifier_block,
CPUFREQ_TRANSITION_NOTIFIER);
+#endif
 		}
 	}
 
diff -uNr linux-2.4.23-vanilla/config-2.4.23-cpufreq-patched
linux/config-2.4.23-cpufreq-patched
--- linux-2.4.23-vanilla/config-2.4.23-cpufreq-patched	1970-01-01
07:30:00.000000000 +0730
+++ linux/config-2.4.23-cpufreq-patched	2003-12-01 23:33:03.000000000 +0800
@@ -0,0 +1,1229 @@
+#
+# Automatically generated make config: don't edit
+#
+CONFIG_X86=y
+# CONFIG_SBUS is not set
+CONFIG_UID16=y
+
+#
+# Code maturity level options
+#
+CONFIG_EXPERIMENTAL=y
+
+#
+# Loadable module support
+#
+CONFIG_MODULES=y
+CONFIG_MODVERSIONS=y
+CONFIG_KMOD=y
+
+#
+# Processor type and features
+#
+# CONFIG_M386 is not set
+# CONFIG_M486 is not set
+# CONFIG_M586 is not set
+# CONFIG_M586TSC is not set
+# CONFIG_M586MMX is not set
+# CONFIG_M686 is not set
+# CONFIG_MPENTIUMIII is not set
+CONFIG_MPENTIUM4=y
+# CONFIG_MK6 is not set
+# CONFIG_MK7 is not set
+# CONFIG_MK8 is not set
+# CONFIG_MELAN is not set
+# CONFIG_MCRUSOE is not set
+# CONFIG_MWINCHIPC6 is not set
+# CONFIG_MWINCHIP2 is not set
+# CONFIG_MWINCHIP3D is not set
+# CONFIG_MCYRIXIII is not set
+# CONFIG_MVIAC3_2 is not set
+CONFIG_X86_WP_WORKS_OK=y
+CONFIG_X86_INVLPG=y
+CONFIG_X86_CMPXCHG=y
+CONFIG_X86_XADD=y
+CONFIG_X86_BSWAP=y
+CONFIG_X86_POPAD_OK=y
+# CONFIG_RWSEM_GENERIC_SPINLOCK is not set
+CONFIG_RWSEM_XCHGADD_ALGORITHM=y
+CONFIG_X86_L1_CACHE_SHIFT=7
+CONFIG_X86_HAS_TSC=y
+CONFIG_X86_GOOD_APIC=y
+CONFIG_X86_PGE=y
+CONFIG_X86_USE_PPRO_CHECKSUM=y
+CONFIG_X86_F00F_WORKS_OK=y
+CONFIG_X86_MCE=y
+
+#
+# CPU Frequency scaling
+#
+CONFIG_CPU_FREQ=y
+CONFIG_CPU_FREQ_TABLE=y
+CONFIG_CPU_FREQ_PROC_INTF=y
+
+#
+# CPUFreq governors
+#
+CONFIG_CPU_FREQ_GOV_USERSPACE=y
+CONFIG_CPU_FREQ_24_API=y
+
+#
+# CPUFreq processor drivers
+#
+# CONFIG_X86_POWERNOW_K6 is not set
+# CONFIG_X86_POWERNOW_K7 is not set
+# CONFIG_X86_POWERNOW_K8 is not set
+# CONFIG_X86_LONGHAUL is not set
+# CONFIG_X86_SPEEDSTEP_PIIX4 is not set
+# CONFIG_X86_SPEEDSTEP_SMI is not set
+# CONFIG_X86_SPEEDSTEP_ICH is not set
+CONFIG_X86_SPEEDSTEP_CENTRINO=y
+# CONFIG_X86_P4_CLOCKMOD is not set
+# CONFIG_X86_LONGRUN is not set
+# CONFIG_X86_GX_SUSPMOD is not set
+# CONFIG_TOSHIBA is not set
+CONFIG_I8K=m
+CONFIG_MICROCODE=m
+# CONFIG_X86_MSR is not set
+CONFIG_X86_CPUID=m
+# CONFIG_EDD is not set
+CONFIG_NOHIGHMEM=y
+# CONFIG_HIGHMEM4G is not set
+# CONFIG_HIGHMEM64G is not set
+# CONFIG_HIGHMEM is not set
+# CONFIG_MATH_EMULATION is not set
+CONFIG_MTRR=y
+# CONFIG_SMP is not set
+# CONFIG_X86_UP_APIC is not set
+# CONFIG_X86_TSC_DISABLE is not set
+CONFIG_X86_TSC=y
+
+#
+# General setup
+#
+CONFIG_NET=y
+CONFIG_PCI=y
+# CONFIG_PCI_GOBIOS is not set
+# CONFIG_PCI_GODIRECT is not set
+CONFIG_PCI_GOANY=y
+CONFIG_PCI_BIOS=y
+CONFIG_PCI_DIRECT=y
+CONFIG_ISA=y
+CONFIG_PCI_NAMES=y
+# CONFIG_EISA is not set
+# CONFIG_MCA is not set
+CONFIG_HOTPLUG=y
+
+#
+# PCMCIA/CardBus support
+#
+CONFIG_PCMCIA=y
+CONFIG_CARDBUS=y
+# CONFIG_TCIC is not set
+# CONFIG_I82092 is not set
+# CONFIG_I82365 is not set
+
+#
+# PCI Hotplug Support
+#
+CONFIG_HOTPLUG_PCI=m
+# CONFIG_HOTPLUG_PCI_COMPAQ is not set
+# CONFIG_HOTPLUG_PCI_IBM is not set
+CONFIG_HOTPLUG_PCI_ACPI=m
+CONFIG_SYSVIPC=y
+CONFIG_BSD_PROCESS_ACCT=y
+CONFIG_SYSCTL=y
+CONFIG_KCORE_ELF=y
+# CONFIG_KCORE_AOUT is not set
+CONFIG_BINFMT_AOUT=y
+CONFIG_BINFMT_ELF=y
+CONFIG_BINFMT_MISC=y
+CONFIG_PM=y
+# CONFIG_APM is not set
+
+#
+# ACPI Support
+#
+CONFIG_ACPI=y
+CONFIG_ACPI_BOOT=y
+CONFIG_ACPI_BUS=y
+CONFIG_ACPI_INTERPRETER=y
+CONFIG_ACPI_EC=y
+CONFIG_ACPI_POWER=y
+CONFIG_ACPI_PCI=y
+CONFIG_ACPI_SLEEP=y
+CONFIG_ACPI_SYSTEM=y
+CONFIG_ACPI_AC=y
+CONFIG_ACPI_BATTERY=y
+CONFIG_ACPI_BUTTON=y
+CONFIG_ACPI_FAN=y
+CONFIG_ACPI_PROCESSOR=y
+CONFIG_ACPI_THERMAL=y
+# CONFIG_ACPI_ASUS is not set
+# CONFIG_ACPI_TOSHIBA is not set
+# CONFIG_ACPI_DEBUG is not set
+# CONFIG_ACPI_RELAXED_AML is not set
+
+#
+# Memory Technology Devices (MTD)
+#
+# CONFIG_MTD is not set
+
+#
+# Parallel port support
+#
+CONFIG_PARPORT=m
+CONFIG_PARPORT_PC=m
+CONFIG_PARPORT_PC_CML1=m
+# CONFIG_PARPORT_SERIAL is not set
+# CONFIG_PARPORT_PC_FIFO is not set
+# CONFIG_PARPORT_PC_SUPERIO is not set
+CONFIG_PARPORT_PC_PCMCIA=m
+# CONFIG_PARPORT_AMIGA is not set
+# CONFIG_PARPORT_MFC3 is not set
+# CONFIG_PARPORT_ATARI is not set
+# CONFIG_PARPORT_GSC is not set
+# CONFIG_PARPORT_SUNBPP is not set
+# CONFIG_PARPORT_OTHER is not set
+# CONFIG_PARPORT_1284 is not set
+
+#
+# Plug and Play configuration
+#
+CONFIG_PNP=y
+CONFIG_ISAPNP=y
+
+#
+# Block devices
+#
+# CONFIG_BLK_DEV_FD is not set
+# CONFIG_BLK_DEV_XD is not set
+# CONFIG_PARIDE is not set
+# CONFIG_BLK_CPQ_DA is not set
+# CONFIG_BLK_CPQ_CISS_DA is not set
+# CONFIG_BLK_DEV_DAC960 is not set
+# CONFIG_BLK_DEV_UMEM is not set
+CONFIG_BLK_DEV_LOOP=m
+CONFIG_BLK_DEV_NBD=m
+# CONFIG_BLK_DEV_RAM is not set
+CONFIG_BLK_STATS=y
+
+#
+# Multi-device support (RAID and LVM)
+#
+# CONFIG_MD is not set
+
+#
+# Networking options
+#
+CONFIG_PACKET=y
+# CONFIG_PACKET_MMAP is not set
+# CONFIG_NETLINK_DEV is not set
+CONFIG_NETFILTER=y
+# CONFIG_NETFILTER_DEBUG is not set
+CONFIG_FILTER=y
+CONFIG_UNIX=y
+CONFIG_INET=y
+CONFIG_IP_MULTICAST=y
+# CONFIG_IP_ADVANCED_ROUTER is not set
+CONFIG_IP_PNP=y
+# CONFIG_IP_PNP_DHCP is not set
+# CONFIG_IP_PNP_BOOTP is not set
+# CONFIG_IP_PNP_RARP is not set
+# CONFIG_NET_IPIP is not set
+# CONFIG_NET_IPGRE is not set
+# CONFIG_IP_MROUTE is not set
+# CONFIG_ARPD is not set
+# CONFIG_INET_ECN is not set
+# CONFIG_SYN_COOKIES is not set
+
+#
+#   IP: Netfilter Configuration
+#
+CONFIG_IP_NF_CONNTRACK=m
+CONFIG_IP_NF_FTP=m
+# CONFIG_IP_NF_AMANDA is not set
+# CONFIG_IP_NF_TFTP is not set
+CONFIG_IP_NF_IRC=m
+# CONFIG_IP_NF_QUEUE is not set
+CONFIG_IP_NF_IPTABLES=m
+CONFIG_IP_NF_MATCH_LIMIT=m
+CONFIG_IP_NF_MATCH_MAC=m
+CONFIG_IP_NF_MATCH_PKTTYPE=m
+CONFIG_IP_NF_MATCH_MARK=m
+CONFIG_IP_NF_MATCH_MULTIPORT=m
+CONFIG_IP_NF_MATCH_TOS=m
+CONFIG_IP_NF_MATCH_RECENT=m
+CONFIG_IP_NF_MATCH_ECN=m
+CONFIG_IP_NF_MATCH_DSCP=m
+CONFIG_IP_NF_MATCH_AH_ESP=m
+CONFIG_IP_NF_MATCH_LENGTH=m
+CONFIG_IP_NF_MATCH_TTL=m
+CONFIG_IP_NF_MATCH_TCPMSS=m
+CONFIG_IP_NF_MATCH_HELPER=m
+CONFIG_IP_NF_MATCH_STATE=m
+CONFIG_IP_NF_MATCH_CONNTRACK=m
+CONFIG_IP_NF_MATCH_UNCLEAN=m
+CONFIG_IP_NF_MATCH_OWNER=m
+CONFIG_IP_NF_FILTER=m
+CONFIG_IP_NF_TARGET_REJECT=m
+CONFIG_IP_NF_TARGET_MIRROR=m
+CONFIG_IP_NF_NAT=m
+CONFIG_IP_NF_NAT_NEEDED=y
+CONFIG_IP_NF_TARGET_MASQUERADE=m
+CONFIG_IP_NF_TARGET_REDIRECT=m
+# CONFIG_IP_NF_NAT_LOCAL is not set
+CONFIG_IP_NF_NAT_SNMP_BASIC=m
+CONFIG_IP_NF_NAT_IRC=m
+CONFIG_IP_NF_NAT_FTP=m
+CONFIG_IP_NF_MANGLE=m
+CONFIG_IP_NF_TARGET_TOS=m
+CONFIG_IP_NF_TARGET_ECN=m
+CONFIG_IP_NF_TARGET_DSCP=m
+CONFIG_IP_NF_TARGET_MARK=m
+CONFIG_IP_NF_TARGET_LOG=m
+CONFIG_IP_NF_TARGET_ULOG=m
+CONFIG_IP_NF_TARGET_TCPMSS=m
+CONFIG_IP_NF_ARPTABLES=m
+CONFIG_IP_NF_ARPFILTER=m
+CONFIG_IP_NF_ARP_MANGLE=m
+CONFIG_IP_NF_COMPAT_IPCHAINS=m
+CONFIG_IP_NF_NAT_NEEDED=y
+CONFIG_IP_NF_COMPAT_IPFWADM=m
+CONFIG_IP_NF_NAT_NEEDED=y
+
+#
+#   IP: Virtual Server Configuration
+#
+# CONFIG_IP_VS is not set
+# CONFIG_IPV6 is not set
+# CONFIG_KHTTPD is not set
+
+#
+#    SCTP Configuration (EXPERIMENTAL)
+#
+CONFIG_IPV6_SCTP__=y
+# CONFIG_IP_SCTP is not set
+# CONFIG_ATM is not set
+# CONFIG_VLAN_8021Q is not set
+
+#
+#  
+#
+# CONFIG_IPX is not set
+# CONFIG_ATALK is not set
+
+#
+# Appletalk devices
+#
+# CONFIG_DECNET is not set
+# CONFIG_BRIDGE is not set
+# CONFIG_X25 is not set
+# CONFIG_LAPB is not set
+# CONFIG_LLC is not set
+# CONFIG_NET_DIVERT is not set
+# CONFIG_ECONET is not set
+# CONFIG_WAN_ROUTER is not set
+# CONFIG_NET_FASTROUTE is not set
+# CONFIG_NET_HW_FLOWCONTROL is not set
+
+#
+# QoS and/or fair queueing
+#
+# CONFIG_NET_SCHED is not set
+
+#
+# Network testing
+#
+# CONFIG_NET_PKTGEN is not set
+
+#
+# Telephony Support
+#
+# CONFIG_PHONE is not set
+
+#
+# ATA/IDE/MFM/RLL support
+#
+CONFIG_IDE=y
+
+#
+# IDE, ATA and ATAPI Block devices
+#
+CONFIG_BLK_DEV_IDE=y
+
+#
+# Please see Documentation/ide.txt for help/info on IDE drives
+#
+# CONFIG_BLK_DEV_HD_IDE is not set
+# CONFIG_BLK_DEV_HD is not set
+CONFIG_BLK_DEV_IDEDISK=y
+# CONFIG_IDEDISK_MULTI_MODE is not set
+# CONFIG_IDEDISK_STROKE is not set
+# CONFIG_BLK_DEV_IDECS is not set
+CONFIG_BLK_DEV_IDECD=y
+# CONFIG_BLK_DEV_IDETAPE is not set
+# CONFIG_BLK_DEV_IDEFLOPPY is not set
+CONFIG_BLK_DEV_IDESCSI=m
+# CONFIG_IDE_TASK_IOCTL is not set
+
+#
+# IDE chipset support/bugfixes
+#
+CONFIG_BLK_DEV_CMD640=y
+# CONFIG_BLK_DEV_CMD640_ENHANCED is not set
+# CONFIG_BLK_DEV_ISAPNP is not set
+CONFIG_BLK_DEV_IDEPCI=y
+# CONFIG_BLK_DEV_GENERIC is not set
+CONFIG_IDEPCI_SHARE_IRQ=y
+CONFIG_BLK_DEV_IDEDMA_PCI=y
+# CONFIG_BLK_DEV_OFFBOARD is not set
+# CONFIG_BLK_DEV_IDEDMA_FORCED is not set
+CONFIG_IDEDMA_PCI_AUTO=y
+# CONFIG_IDEDMA_ONLYDISK is not set
+CONFIG_BLK_DEV_IDEDMA=y
+# CONFIG_IDEDMA_PCI_WIP is not set
+# CONFIG_BLK_DEV_ADMA100 is not set
+# CONFIG_BLK_DEV_AEC62XX is not set
+# CONFIG_BLK_DEV_ALI15X3 is not set
+# CONFIG_BLK_DEV_AMD74XX is not set
+# CONFIG_BLK_DEV_CMD64X is not set
+# CONFIG_BLK_DEV_TRIFLEX is not set
+# CONFIG_BLK_DEV_CY82C693 is not set
+# CONFIG_BLK_DEV_CS5530 is not set
+# CONFIG_BLK_DEV_HPT34X is not set
+# CONFIG_BLK_DEV_HPT366 is not set
+CONFIG_BLK_DEV_PIIX=y
+# CONFIG_BLK_DEV_NS87415 is not set
+# CONFIG_BLK_DEV_OPTI621 is not set
+# CONFIG_BLK_DEV_PDC202XX_OLD is not set
+# CONFIG_BLK_DEV_PDC202XX_NEW is not set
+CONFIG_BLK_DEV_RZ1000=y
+# CONFIG_BLK_DEV_SC1200 is not set
+# CONFIG_BLK_DEV_SVWKS is not set
+# CONFIG_BLK_DEV_SIIMAGE is not set
+# CONFIG_BLK_DEV_SIS5513 is not set
+# CONFIG_BLK_DEV_SLC90E66 is not set
+# CONFIG_BLK_DEV_TRM290 is not set
+# CONFIG_BLK_DEV_VIA82CXXX is not set
+# CONFIG_IDE_CHIPSETS is not set
+CONFIG_IDEDMA_AUTO=y
+# CONFIG_IDEDMA_IVB is not set
+# CONFIG_DMA_NONPCI is not set
+CONFIG_BLK_DEV_IDE_MODES=y
+# CONFIG_BLK_DEV_ATARAID is not set
+
+#
+# SCSI support
+#
+CONFIG_SCSI=y
+
+#
+# SCSI support type (disk, tape, CD-ROM)
+#
+CONFIG_BLK_DEV_SD=m
+CONFIG_SD_EXTRA_DEVS=40
+# CONFIG_CHR_DEV_ST is not set
+# CONFIG_CHR_DEV_OSST is not set
+CONFIG_BLK_DEV_SR=m
+# CONFIG_BLK_DEV_SR_VENDOR is not set
+CONFIG_SR_EXTRA_DEVS=2
+CONFIG_CHR_DEV_SG=m
+
+#
+# Some SCSI devices (e.g. CD jukebox) support multiple LUNs
+#
+CONFIG_SCSI_DEBUG_QUEUES=y
+# CONFIG_SCSI_MULTI_LUN is not set
+CONFIG_SCSI_CONSTANTS=y
+# CONFIG_SCSI_LOGGING is not set
+
+#
+# SCSI low-level drivers
+#
+# CONFIG_BLK_DEV_3W_XXXX_RAID is not set
+# CONFIG_SCSI_7000FASST is not set
+# CONFIG_SCSI_ACARD is not set
+# CONFIG_SCSI_AHA152X is not set
+# CONFIG_SCSI_AHA1542 is not set
+# CONFIG_SCSI_AHA1740 is not set
+# CONFIG_SCSI_AACRAID is not set
+# CONFIG_SCSI_AIC7XXX is not set
+# CONFIG_SCSI_AIC79XX is not set
+# CONFIG_SCSI_AIC7XXX_OLD is not set
+# CONFIG_SCSI_DPT_I2O is not set
+# CONFIG_SCSI_ADVANSYS is not set
+# CONFIG_SCSI_IN2000 is not set
+# CONFIG_SCSI_AM53C974 is not set
+# CONFIG_SCSI_MEGARAID is not set
+# CONFIG_SCSI_MEGARAID2 is not set
+# CONFIG_SCSI_BUSLOGIC is not set
+# CONFIG_SCSI_CPQFCTS is not set
+# CONFIG_SCSI_DMX3191D is not set
+# CONFIG_SCSI_DTC3280 is not set
+# CONFIG_SCSI_EATA is not set
+# CONFIG_SCSI_EATA_DMA is not set
+# CONFIG_SCSI_EATA_PIO is not set
+# CONFIG_SCSI_FUTURE_DOMAIN is not set
+# CONFIG_SCSI_GDTH is not set
+# CONFIG_SCSI_GENERIC_NCR5380 is not set
+# CONFIG_SCSI_IPS is not set
+# CONFIG_SCSI_INITIO is not set
+# CONFIG_SCSI_INIA100 is not set
+# CONFIG_SCSI_PPA is not set
+# CONFIG_SCSI_IMM is not set
+# CONFIG_SCSI_NCR53C406A is not set
+# CONFIG_SCSI_NCR53C7xx is not set
+CONFIG_SCSI_SYM53C8XX_2=m
+CONFIG_SCSI_SYM53C8XX_DMA_ADDRESSING_MODE=1
+CONFIG_SCSI_SYM53C8XX_DEFAULT_TAGS=16
+CONFIG_SCSI_SYM53C8XX_MAX_TAGS=64
+# CONFIG_SCSI_SYM53C8XX_IOMAPPED is not set
+# CONFIG_SCSI_NCR53C8XX is not set
+# CONFIG_SCSI_SYM53C8XX is not set
+# CONFIG_SCSI_PAS16 is not set
+# CONFIG_SCSI_PCI2000 is not set
+# CONFIG_SCSI_PCI2220I is not set
+# CONFIG_SCSI_PSI240I is not set
+# CONFIG_SCSI_QLOGIC_FAS is not set
+# CONFIG_SCSI_QLOGIC_ISP is not set
+# CONFIG_SCSI_QLOGIC_FC is not set
+# CONFIG_SCSI_QLOGIC_1280 is not set
+# CONFIG_SCSI_SEAGATE is not set
+# CONFIG_SCSI_SIM710 is not set
+# CONFIG_SCSI_SYM53C416 is not set
+# CONFIG_SCSI_DC390T is not set
+# CONFIG_SCSI_T128 is not set
+# CONFIG_SCSI_U14_34F is not set
+# CONFIG_SCSI_ULTRASTOR is not set
+# CONFIG_SCSI_NSP32 is not set
+# CONFIG_SCSI_DEBUG is not set
+
+#
+# PCMCIA SCSI adapter support
+#
+# CONFIG_SCSI_PCMCIA is not set
+
+#
+# Fusion MPT device support
+#
+# CONFIG_FUSION is not set
+# CONFIG_FUSION_BOOT is not set
+# CONFIG_FUSION_ISENSE is not set
+# CONFIG_FUSION_CTL is not set
+# CONFIG_FUSION_LAN is not set
+
+#
+# IEEE 1394 (FireWire) support (EXPERIMENTAL)
+#
+# CONFIG_IEEE1394 is not set
+
+#
+# I2O device support
+#
+CONFIG_I2O=m
+CONFIG_I2O_PCI=m
+CONFIG_I2O_BLOCK=m
+CONFIG_I2O_LAN=m
+CONFIG_I2O_SCSI=m
+CONFIG_I2O_PROC=m
+
+#
+# Network device support
+#
+CONFIG_NETDEVICES=y
+
+#
+# ARCnet devices
+#
+# CONFIG_ARCNET is not set
+CONFIG_DUMMY=m
+# CONFIG_BONDING is not set
+# CONFIG_EQUALIZER is not set
+# CONFIG_TUN is not set
+# CONFIG_ETHERTAP is not set
+# CONFIG_NET_SB1000 is not set
+
+#
+# Ethernet (10 or 100Mbit)
+#
+CONFIG_NET_ETHERNET=y
+# CONFIG_HAPPYMEAL is not set
+# CONFIG_SUNGEM is not set
+# CONFIG_NET_VENDOR_3COM is not set
+# CONFIG_LANCE is not set
+# CONFIG_NET_VENDOR_SMC is not set
+# CONFIG_NET_VENDOR_RACAL is not set
+# CONFIG_AT1700 is not set
+# CONFIG_DEPCA is not set
+# CONFIG_HP100 is not set
+# CONFIG_NET_ISA is not set
+CONFIG_NET_PCI=y
+# CONFIG_PCNET32 is not set
+# CONFIG_AMD8111_ETH is not set
+# CONFIG_ADAPTEC_STARFIRE is not set
+# CONFIG_AC3200 is not set
+# CONFIG_APRICOT is not set
+# CONFIG_B44 is not set
+# CONFIG_CS89x0 is not set
+# CONFIG_TULIP is not set
+# CONFIG_DE4X5 is not set
+# CONFIG_DGRS is not set
+# CONFIG_DM9102 is not set
+CONFIG_EEPRO100=y
+# CONFIG_EEPRO100_PIO is not set
+# CONFIG_E100 is not set
+# CONFIG_FEALNX is not set
+# CONFIG_NATSEMI is not set
+# CONFIG_NE2K_PCI is not set
+# CONFIG_8139CP is not set
+# CONFIG_8139TOO is not set
+# CONFIG_SIS900 is not set
+# CONFIG_EPIC100 is not set
+# CONFIG_SUNDANCE is not set
+# CONFIG_TLAN is not set
+# CONFIG_VIA_RHINE is not set
+# CONFIG_WINBOND_840 is not set
+# CONFIG_NET_POCKET is not set
+
+#
+# Ethernet (1000 Mbit)
+#
+# CONFIG_ACENIC is not set
+# CONFIG_DL2K is not set
+# CONFIG_E1000 is not set
+# CONFIG_NS83820 is not set
+# CONFIG_HAMACHI is not set
+# CONFIG_YELLOWFIN is not set
+# CONFIG_R8169 is not set
+# CONFIG_SK98LIN is not set
+CONFIG_TIGON3=m
+# CONFIG_FDDI is not set
+# CONFIG_HIPPI is not set
+# CONFIG_PLIP is not set
+CONFIG_PPP=m
+# CONFIG_PPP_MULTILINK is not set
+CONFIG_PPP_FILTER=y
+CONFIG_PPP_ASYNC=m
+CONFIG_PPP_SYNC_TTY=m
+CONFIG_PPP_DEFLATE=m
+CONFIG_PPP_BSDCOMP=m
+# CONFIG_PPPOE is not set
+# CONFIG_SLIP is not set
+
+#
+# Wireless LAN (non-hamradio)
+#
+CONFIG_NET_RADIO=y
+# CONFIG_STRIP is not set
+# CONFIG_WAVELAN is not set
+# CONFIG_ARLAN is not set
+# CONFIG_AIRONET4500 is not set
+# CONFIG_AIRO is not set
+CONFIG_HERMES=m
+CONFIG_PLX_HERMES=m
+# CONFIG_TMD_HERMES is not set
+CONFIG_PCI_HERMES=m
+
+#
+# Wireless Pcmcia cards support
+#
+CONFIG_PCMCIA_HERMES=m
+# CONFIG_AIRO_CS is not set
+CONFIG_NET_WIRELESS=y
+
+#
+# Token Ring devices
+#
+# CONFIG_TR is not set
+# CONFIG_NET_FC is not set
+# CONFIG_RCPCI is not set
+# CONFIG_SHAPER is not set
+
+#
+# Wan interfaces
+#
+# CONFIG_WAN is not set
+
+#
+# PCMCIA network device support
+#
+CONFIG_NET_PCMCIA=y
+CONFIG_PCMCIA_3C589=m
+CONFIG_PCMCIA_3C574=m
+# CONFIG_PCMCIA_FMVJ18X is not set
+CONFIG_PCMCIA_PCNET=m
+CONFIG_PCMCIA_AXNET=m
+CONFIG_PCMCIA_NMCLAN=m
+CONFIG_PCMCIA_SMC91C92=m
+CONFIG_PCMCIA_XIRC2PS=m
+# CONFIG_PCMCIA_XIRCOM is not set
+# CONFIG_PCMCIA_XIRTULIP is not set
+CONFIG_NET_PCMCIA_RADIO=y
+# CONFIG_PCMCIA_RAYCS is not set
+CONFIG_PCMCIA_NETWAVE=m
+CONFIG_PCMCIA_WAVELAN=m
+
+#
+# Amateur Radio support
+#
+# CONFIG_HAMRADIO is not set
+
+#
+# IrDA (infrared) support
+#
+CONFIG_IRDA=m
+
+#
+# IrDA protocols
+#
+CONFIG_IRLAN=m
+# CONFIG_IRNET is not set
+CONFIG_IRCOMM=m
+# CONFIG_IRDA_ULTRA is not set
+
+#
+# IrDA options
+#
+# CONFIG_IRDA_CACHE_LAST_LSAP is not set
+# CONFIG_IRDA_FAST_RR is not set
+# CONFIG_IRDA_DEBUG is not set
+
+#
+# Infrared-port device drivers
+#
+
+#
+# SIR device drivers
+#
+CONFIG_IRTTY_SIR=m
+CONFIG_IRPORT_SIR=m
+
+#
+# Dongle support
+#
+# CONFIG_DONGLE is not set
+
+#
+# FIR device drivers
+#
+# CONFIG_USB_IRDA is not set
+# CONFIG_NSC_FIR is not set
+# CONFIG_WINBOND_FIR is not set
+# CONFIG_TOSHIBA_OLD is not set
+# CONFIG_TOSHIBA_FIR is not set
+# CONFIG_SMC_IRCC_FIR is not set
+# CONFIG_ALI_FIR is not set
+# CONFIG_VLSI_FIR is not set
+
+#
+# ISDN subsystem
+#
+# CONFIG_ISDN is not set
+
+#
+# Old CD-ROM drivers (not SCSI, not IDE)
+#
+# CONFIG_CD_NO_IDESCSI is not set
+
+#
+# Input core support
+#
+CONFIG_INPUT=m
+CONFIG_INPUT_KEYBDEV=m
+CONFIG_INPUT_MOUSEDEV=m
+CONFIG_INPUT_MOUSEDEV_SCREEN_X=1400
+CONFIG_INPUT_MOUSEDEV_SCREEN_Y=1050
+# CONFIG_INPUT_JOYDEV is not set
+# CONFIG_INPUT_EVDEV is not set
+
+#
+# Character devices
+#
+CONFIG_VT=y
+CONFIG_VT_CONSOLE=y
+CONFIG_SERIAL=y
+# CONFIG_SERIAL_CONSOLE is not set
+# CONFIG_SERIAL_EXTENDED is not set
+# CONFIG_SERIAL_NONSTANDARD is not set
+CONFIG_UNIX98_PTYS=y
+CONFIG_UNIX98_PTY_COUNT=256
+CONFIG_PRINTER=m
+# CONFIG_LP_CONSOLE is not set
+# CONFIG_PPDEV is not set
+# CONFIG_TIPAR is not set
+
+#
+# I2C support
+#
+# CONFIG_I2C is not set
+
+#
+# Mice
+#
+# CONFIG_BUSMOUSE is not set
+CONFIG_MOUSE=y
+CONFIG_PSMOUSE=y
+# CONFIG_82C710_MOUSE is not set
+# CONFIG_PC110_PAD is not set
+# CONFIG_MK712_MOUSE is not set
+
+#
+# Joysticks
+#
+# CONFIG_INPUT_GAMEPORT is not set
+# CONFIG_INPUT_SERIO is not set
+
+#
+# Joysticks
+#
+# CONFIG_INPUT_IFORCE_USB is not set
+# CONFIG_INPUT_DB9 is not set
+# CONFIG_INPUT_GAMECON is not set
+# CONFIG_INPUT_TURBOGRAFX is not set
+# CONFIG_QIC02_TAPE is not set
+# CONFIG_IPMI_HANDLER is not set
+
+#
+# Watchdog Cards
+#
+# CONFIG_WATCHDOG is not set
+# CONFIG_SCx200_GPIO is not set
+# CONFIG_AMD_RNG is not set
+CONFIG_INTEL_RNG=m
+# CONFIG_HW_RANDOM is not set
+# CONFIG_AMD_PM768 is not set
+# CONFIG_NVRAM is not set
+CONFIG_RTC=y
+# CONFIG_DTLK is not set
+# CONFIG_R3964 is not set
+# CONFIG_APPLICOM is not set
+# CONFIG_SONYPI is not set
+
+#
+# Ftape, the floppy tape device driver
+#
+# CONFIG_FTAPE is not set
+CONFIG_AGP=m
+CONFIG_AGP_INTEL=y
+CONFIG_AGP_I810=y
+# CONFIG_AGP_VIA is not set
+CONFIG_AGP_AMD=y
+# CONFIG_AGP_AMD_K8 is not set
+CONFIG_AGP_SIS=y
+CONFIG_AGP_ALI=y
+# CONFIG_AGP_SWORKS is not set
+# CONFIG_AGP_NVIDIA is not set
+CONFIG_AGP_ATI=y
+
+#
+# Direct Rendering Manager (XFree86 DRI support)
+#
+CONFIG_DRM=y
+# CONFIG_DRM_OLD is not set
+
+#
+# DRM 4.1 drivers
+#
+CONFIG_DRM_NEW=y
+# CONFIG_DRM_TDFX is not set
+# CONFIG_DRM_GAMMA is not set
+# CONFIG_DRM_R128 is not set
+CONFIG_DRM_RADEON=m
+CONFIG_DRM_I810=m
+CONFIG_DRM_I810_XFREE_41=y
+# CONFIG_DRM_I830 is not set
+# CONFIG_DRM_MGA is not set
+# CONFIG_DRM_SIS is not set
+
+#
+# PCMCIA character devices
+#
+CONFIG_PCMCIA_SERIAL_CS=m
+CONFIG_SYNCLINK_CS=m
+# CONFIG_MWAVE is not set
+
+#
+# Multimedia devices
+#
+CONFIG_VIDEO_DEV=m
+
+#
+# Video For Linux
+#
+CONFIG_VIDEO_PROC_FS=y
+
+#
+# Video Adapters
+#
+# CONFIG_VIDEO_PMS is not set
+# CONFIG_VIDEO_BWQCAM is not set
+# CONFIG_VIDEO_CQCAM is not set
+# CONFIG_VIDEO_CPIA is not set
+# CONFIG_VIDEO_STRADIS is not set
+
+#
+# Radio Adapters
+#
+# CONFIG_RADIO_CADET is not set
+# CONFIG_RADIO_RTRACK is not set
+# CONFIG_RADIO_RTRACK2 is not set
+# CONFIG_RADIO_AZTECH is not set
+# CONFIG_RADIO_GEMTEK is not set
+# CONFIG_RADIO_GEMTEK_PCI is not set
+# CONFIG_RADIO_MAXIRADIO is not set
+# CONFIG_RADIO_MAESTRO is not set
+# CONFIG_RADIO_SF16FMI is not set
+# CONFIG_RADIO_SF16FMR2 is not set
+# CONFIG_RADIO_TERRATEC is not set
+# CONFIG_RADIO_TRUST is not set
+# CONFIG_RADIO_TYPHOON is not set
+# CONFIG_RADIO_ZOLTRIX is not set
+
+#
+# File systems
+#
+CONFIG_QUOTA=y
+# CONFIG_QFMT_V2 is not set
+CONFIG_AUTOFS_FS=m
+CONFIG_AUTOFS4_FS=m
+CONFIG_REISERFS_FS=m
+# CONFIG_REISERFS_CHECK is not set
+# CONFIG_REISERFS_PROC_INFO is not set
+# CONFIG_ADFS_FS is not set
+# CONFIG_AFFS_FS is not set
+# CONFIG_HFS_FS is not set
+# CONFIG_HFSPLUS_FS is not set
+# CONFIG_BEFS_FS is not set
+# CONFIG_BFS_FS is not set
+CONFIG_EXT3_FS=y
+CONFIG_JBD=y
+CONFIG_JBD_DEBUG=y
+CONFIG_FAT_FS=m
+CONFIG_MSDOS_FS=m
+CONFIG_UMSDOS_FS=m
+CONFIG_VFAT_FS=m
+# CONFIG_EFS_FS is not set
+# CONFIG_CRAMFS is not set
+CONFIG_TMPFS=y
+CONFIG_RAMFS=y
+CONFIG_ISO9660_FS=y
+CONFIG_JOLIET=y
+CONFIG_ZISOFS=y
+# CONFIG_JFS_FS is not set
+# CONFIG_MINIX_FS is not set
+# CONFIG_VXFS_FS is not set
+CONFIG_NTFS_FS=y
+# CONFIG_NTFS_RW is not set
+# CONFIG_HPFS_FS is not set
+CONFIG_PROC_FS=y
+# CONFIG_DEVFS_FS is not set
+CONFIG_DEVPTS_FS=y
+# CONFIG_QNX4FS_FS is not set
+# CONFIG_ROMFS_FS is not set
+CONFIG_EXT2_FS=y
+# CONFIG_SYSV_FS is not set
+CONFIG_UDF_FS=m
+# CONFIG_UDF_RW is not set
+# CONFIG_UFS_FS is not set
+
+#
+# Network File Systems
+#
+# CONFIG_CODA_FS is not set
+# CONFIG_INTERMEZZO_FS is not set
+CONFIG_NFS_FS=m
+# CONFIG_NFS_V3 is not set
+# CONFIG_NFS_DIRECTIO is not set
+CONFIG_NFSD=m
+# CONFIG_NFSD_V3 is not set
+# CONFIG_NFSD_TCP is not set
+CONFIG_SUNRPC=m
+CONFIG_LOCKD=m
+CONFIG_SMB_FS=m
+# CONFIG_SMB_NLS_DEFAULT is not set
+# CONFIG_NCP_FS is not set
+CONFIG_ZISOFS_FS=y
+
+#
+# Partition Types
+#
+CONFIG_PARTITION_ADVANCED=y
+# CONFIG_ACORN_PARTITION is not set
+# CONFIG_OSF_PARTITION is not set
+# CONFIG_AMIGA_PARTITION is not set
+# CONFIG_ATARI_PARTITION is not set
+# CONFIG_MAC_PARTITION is not set
+CONFIG_MSDOS_PARTITION=y
+# CONFIG_BSD_DISKLABEL is not set
+# CONFIG_MINIX_SUBPARTITION is not set
+# CONFIG_SOLARIS_X86_PARTITION is not set
+# CONFIG_UNIXWARE_DISKLABEL is not set
+# CONFIG_LDM_PARTITION is not set
+# CONFIG_SGI_PARTITION is not set
+# CONFIG_ULTRIX_PARTITION is not set
+# CONFIG_SUN_PARTITION is not set
+# CONFIG_EFI_PARTITION is not set
+CONFIG_SMB_NLS=y
+CONFIG_NLS=y
+
+#
+# Native Language Support
+#
+CONFIG_NLS_DEFAULT="iso8859-1"
+CONFIG_NLS_CODEPAGE_437=m
+# CONFIG_NLS_CODEPAGE_737 is not set
+# CONFIG_NLS_CODEPAGE_775 is not set
+# CONFIG_NLS_CODEPAGE_850 is not set
+# CONFIG_NLS_CODEPAGE_852 is not set
+# CONFIG_NLS_CODEPAGE_855 is not set
+# CONFIG_NLS_CODEPAGE_857 is not set
+# CONFIG_NLS_CODEPAGE_860 is not set
+# CONFIG_NLS_CODEPAGE_861 is not set
+# CONFIG_NLS_CODEPAGE_862 is not set
+# CONFIG_NLS_CODEPAGE_863 is not set
+# CONFIG_NLS_CODEPAGE_864 is not set
+# CONFIG_NLS_CODEPAGE_865 is not set
+# CONFIG_NLS_CODEPAGE_866 is not set
+# CONFIG_NLS_CODEPAGE_869 is not set
+CONFIG_NLS_CODEPAGE_936=m
+CONFIG_NLS_CODEPAGE_950=m
+# CONFIG_NLS_CODEPAGE_932 is not set
+# CONFIG_NLS_CODEPAGE_949 is not set
+# CONFIG_NLS_CODEPAGE_874 is not set
+# CONFIG_NLS_ISO8859_8 is not set
+# CONFIG_NLS_CODEPAGE_1250 is not set
+# CONFIG_NLS_CODEPAGE_1251 is not set
+CONFIG_NLS_ISO8859_1=m
+# CONFIG_NLS_ISO8859_2 is not set
+# CONFIG_NLS_ISO8859_3 is not set
+# CONFIG_NLS_ISO8859_4 is not set
+# CONFIG_NLS_ISO8859_5 is not set
+# CONFIG_NLS_ISO8859_6 is not set
+# CONFIG_NLS_ISO8859_7 is not set
+# CONFIG_NLS_ISO8859_9 is not set
+# CONFIG_NLS_ISO8859_13 is not set
+# CONFIG_NLS_ISO8859_14 is not set
+# CONFIG_NLS_ISO8859_15 is not set
+# CONFIG_NLS_KOI8_R is not set
+# CONFIG_NLS_KOI8_U is not set
+CONFIG_NLS_UTF8=m
+
+#
+# Console drivers
+#
+CONFIG_VGA_CONSOLE=y
+CONFIG_VIDEO_SELECT=y
+# CONFIG_MDA_CONSOLE is not set
+
+#
+# Frame-buffer support
+#
+CONFIG_FB=y
+CONFIG_DUMMY_CONSOLE=y
+# CONFIG_FB_RIVA is not set
+# CONFIG_FB_CLGEN is not set
+# CONFIG_FB_PM2 is not set
+# CONFIG_FB_PM3 is not set
+# CONFIG_FB_CYBER2000 is not set
+CONFIG_FB_VESA=y
+# CONFIG_FB_VGA16 is not set
+# CONFIG_FB_HGA is not set
+CONFIG_VIDEO_SELECT=y
+# CONFIG_FB_MATROX is not set
+# CONFIG_FB_ATY is not set
+CONFIG_FB_RADEON=m
+# CONFIG_FB_ATY128 is not set
+# CONFIG_FB_INTEL is not set
+# CONFIG_FB_SIS is not set
+# CONFIG_FB_NEOMAGIC is not set
+# CONFIG_FB_3DFX is not set
+# CONFIG_FB_VOODOO1 is not set
+# CONFIG_FB_TRIDENT is not set
+# CONFIG_FB_VIRTUAL is not set
+# CONFIG_FBCON_ADVANCED is not set
+CONFIG_FBCON_CFB8=y
+CONFIG_FBCON_CFB16=y
+CONFIG_FBCON_CFB24=y
+CONFIG_FBCON_CFB32=y
+# CONFIG_FBCON_FONTWIDTH8_ONLY is not set
+# CONFIG_FBCON_FONTS is not set
+CONFIG_FONT_8x8=y
+CONFIG_FONT_8x16=y
+
+#
+# Sound
+#
+CONFIG_SOUND=y
+# CONFIG_SOUND_ALI5455 is not set
+# CONFIG_SOUND_BT878 is not set
+# CONFIG_SOUND_CMPCI is not set
+# CONFIG_SOUND_EMU10K1 is not set
+# CONFIG_SOUND_FUSION is not set
+# CONFIG_SOUND_CS4281 is not set
+# CONFIG_SOUND_ES1370 is not set
+# CONFIG_SOUND_ES1371 is not set
+# CONFIG_SOUND_ESSSOLO1 is not set
+# CONFIG_SOUND_MAESTRO is not set
+# CONFIG_SOUND_MAESTRO3 is not set
+# CONFIG_SOUND_FORTE is not set
+CONFIG_SOUND_ICH=m
+# CONFIG_SOUND_RME96XX is not set
+# CONFIG_SOUND_SONICVIBES is not set
+# CONFIG_SOUND_TRIDENT is not set
+# CONFIG_SOUND_MSNDCLAS is not set
+# CONFIG_SOUND_MSNDPIN is not set
+# CONFIG_SOUND_VIA82CXXX is not set
+# CONFIG_SOUND_OSS is not set
+# CONFIG_SOUND_AD1980 is not set
+# CONFIG_SOUND_WM97XX is not set
+
+#
+# USB support
+#
+CONFIG_USB=m
+# CONFIG_USB_DEBUG is not set
+
+#
+# Miscellaneous USB options
+#
+CONFIG_USB_DEVICEFS=y
+# CONFIG_USB_BANDWIDTH is not set
+
+#
+# USB Host Controller Drivers
+#
+CONFIG_USB_EHCI_HCD=m
+CONFIG_USB_UHCI=m
+CONFIG_USB_UHCI_ALT=m
+CONFIG_USB_OHCI=m
+# CONFIG_USB_SL811HS_ALT is not set
+# CONFIG_USB_SL811HS is not set
+
+#
+# USB Device Class drivers
+#
+# CONFIG_USB_AUDIO is not set
+CONFIG_USB_BLUETOOTH=m
+# CONFIG_USB_MIDI is not set
+CONFIG_USB_STORAGE=m
+# CONFIG_USB_STORAGE_DEBUG is not set
+# CONFIG_USB_STORAGE_DATAFAB is not set
+# CONFIG_USB_STORAGE_FREECOM is not set
+# CONFIG_USB_STORAGE_ISD200 is not set
+# CONFIG_USB_STORAGE_DPCM is not set
+# CONFIG_USB_STORAGE_HP8200e is not set
+# CONFIG_USB_STORAGE_SDDR09 is not set
+# CONFIG_USB_STORAGE_SDDR55 is not set
+# CONFIG_USB_STORAGE_JUMPSHOT is not set
+CONFIG_USB_ACM=m
+CONFIG_USB_PRINTER=m
+
+#
+# USB Human Interface Devices (HID)
+#
+CONFIG_USB_HID=m
+CONFIG_USB_HIDINPUT=y
+# CONFIG_USB_HIDDEV is not set
+# CONFIG_USB_KBD is not set
+# CONFIG_USB_MOUSE is not set
+# CONFIG_USB_AIPTEK is not set
+# CONFIG_USB_WACOM is not set
+# CONFIG_USB_KBTAB is not set
+# CONFIG_USB_POWERMATE is not set
+
+#
+# USB Imaging devices
+#
+# CONFIG_USB_DC2XX is not set
+# CONFIG_USB_MDC800 is not set
+# CONFIG_USB_SCANNER is not set
+# CONFIG_USB_MICROTEK is not set
+# CONFIG_USB_HPUSBSCSI is not set
+
+#
+# USB Multimedia devices
+#
+# CONFIG_USB_IBMCAM is not set
+# CONFIG_USB_KONICAWC is not set
+# CONFIG_USB_OV511 is not set
+# CONFIG_USB_PWC is not set
+# CONFIG_USB_SE401 is not set
+# CONFIG_USB_STV680 is not set
+# CONFIG_USB_VICAM is not set
+# CONFIG_USB_DSBR is not set
+# CONFIG_USB_DABUSB is not set
+
+#
+# USB Network adaptors
+#
+# CONFIG_USB_PEGASUS is not set
+# CONFIG_USB_RTL8150 is not set
+# CONFIG_USB_KAWETH is not set
+# CONFIG_USB_CATC is not set
+# CONFIG_USB_AX8817X is not set
+# CONFIG_USB_CDCETHER is not set
+# CONFIG_USB_USBNET is not set
+
+#
+# USB port drivers
+#
+# CONFIG_USB_USS720 is not set
+
+#
+# USB Serial Converter support
+#
+# CONFIG_USB_SERIAL is not set
+
+#
+# USB Miscellaneous drivers
+#
+# CONFIG_USB_RIO500 is not set
+# CONFIG_USB_AUERSWALD is not set
+# CONFIG_USB_TIGL is not set
+# CONFIG_USB_BRLVGER is not set
+# CONFIG_USB_LCD is not set
+
+#
+# Support for USB gadgets
+#
+# CONFIG_USB_GADGET is not set
+
+#
+# Bluetooth support
+#
+# CONFIG_BLUEZ is not set
+
+#
+# Kernel hacking
+#
+# CONFIG_DEBUG_KERNEL is not set
+CONFIG_LOG_BUF_SHIFT=0
+
+#
+# Cryptographic options
+#
+CONFIG_CRYPTO=y
+# CONFIG_CRYPTO_HMAC is not set
+# CONFIG_CRYPTO_NULL is not set
+CONFIG_CRYPTO_MD4=m
+CONFIG_CRYPTO_MD5=m
+# CONFIG_CRYPTO_SHA1 is not set
+# CONFIG_CRYPTO_SHA256 is not set
+# CONFIG_CRYPTO_SHA512 is not set
+# CONFIG_CRYPTO_DES is not set
+CONFIG_CRYPTO_BLOWFISH=m
+# CONFIG_CRYPTO_TWOFISH is not set
+# CONFIG_CRYPTO_SERPENT is not set
+# CONFIG_CRYPTO_AES is not set
+# CONFIG_CRYPTO_CAST5 is not set
+# CONFIG_CRYPTO_DEFLATE is not set
+# CONFIG_CRYPTO_TEST is not set
+
+#
+# Library routines
+#
+# CONFIG_CRC32 is not set
+CONFIG_ZLIB_INFLATE=y
+CONFIG_ZLIB_DEFLATE=m
+# CONFIG_FW_LOADER is not set
diff -uNr linux-2.4.23-vanilla/Documentation/00-INDEX
linux/Documentation/00-INDEX
--- linux-2.4.23-vanilla/Documentation/00-INDEX	2001-08-27
22:44:15.000000000 +0800
+++ linux/Documentation/00-INDEX	2003-12-01 20:38:29.000000000 +0800
@@ -52,6 +52,8 @@
 	- directory with information on the CD-ROM drivers that Linux has.
 computone.txt
 	- info on Computone Intelliport II/Plus Multiport Serial Driver
+cpufreq
+	- describes the CPU frequency and voltage scaling support 
 cpqarray.txt
 	- info on using Compaq's SMART2 Intelligent Disk Array Controllers.
 devices.txt
diff -uNr linux-2.4.23-vanilla/Documentation/Configure.help
linux/Documentation/Configure.help
--- linux-2.4.23-vanilla/Documentation/Configure.help	2003-11-29
02:26:19.000000000 +0800
+++ linux/Documentation/Configure.help	2003-12-01 20:38:29.000000000 +0800
@@ -27904,16 +27904,6 @@
   Say Y if you want support for the ARM926T processor.
   Otherwise, say N.
 
-Support CPU clock change (EXPERIMENTAL)
-CONFIG_CPU_FREQ
-  CPU clock scaling allows you to change the clock speed of the
-  running CPU on the fly. This is a nice method to save battery power,
-  because the lower the clock speed, the less power the CPU
-  consumes. Note that this driver doesn't automatically change the CPU
-  clock speed, you need some userland tools (which still have to be
-  written) to implement the policy. If you don't understand what this
-  is all about, it's safe to say 'N'.
-
 SiS
 CONFIG_DRM_SIS
   Choose this option if you have a SIS graphics card. AGP support is
@@ -28321,6 +28311,154 @@
 CONFIG_IPMI_WATCHDOG
   This enables the IPMI watchdog timer.
 
+CONFIG_CPU_FREQ
+  Clock scaling allows you to change the clock speed of CPUs on the
+  fly. This is a nice method to save battery power on notebooks,
+  because the lower the clock speed, the less power the CPU consumes.
+
+  For more information, take a look at linux/Documentation/cpu-freq.
+
+  If in doubt, say N.
+
+CONFIG_CPU_FREQ_TABLE
+  Many CPUFreq drivers use these helpers, so only say N here if
+  the CPUFreq driver of your choice doesn't need these helpers.
+
+  If in doubt, say Y.
+
+CONFIG_CPU_FREQ_24_API
+  This enables the /proc/sys/cpu/ sysctl interface for controlling
+  CPUFreq, as known from the 2.4.-kernel patches for CPUFreq. 2.5
+  uses /proc/cpufreq instead. Please note that some drivers do not 
+  work well with the 2.4. /proc/sys/cpu sysctl interface, so if in
+  doubt, say N here.
+
+  For details, take a look at linux/Documentation/cpu-freq. 
+
+  If in doubt, say N.
+
+CONFIG_X86_POWERNOW_K6
+  This adds the CPUFreq driver for mobile AMD K6-2+ and mobile
+  AMD K6-3+ processors.
+
+  For details, take a look at linux/Documentation/cpu-freq. 
+
+  If in doubt, say N.
+
+CONFIG_X86_POWERNOW_K7
+  This adds the CPUFreq driver for mobile AMD Athlon/Duron 
+  K7 processors.
+
+  For details, take a look at linux/Documentation/cpu-freq. 
+
+  If in doubt, say N.
+
+CONFIG_X86_POWERNOW_K8
+  This adds the CPUFreq driver for mobile AMD Opteron/Athlon64 processors.
+
+  For details, take a look at linux/Documentation/cpu-freq.
+
+  If in doubt, say N.
+
+CONFIG_X86_P4_CLOCKMOD
+  This adds the CPUFreq driver for Intel Pentium 4 / XEON
+  processors.
+
+  For details, take a look at linux/Documentation/cpu-freq. 
+
+  If in doubt, say N.
+
+CONFIG_ELAN_CPUFREQ
+  This adds the CPUFreq driver for AMD Elan SC400 and SC410
+  processors.
+
+  You need to specify the processor maximum speed as boot
+  parameter: elanfreq=maxspeed (in kHz) or as module
+  parameter "max_freq".
+
+  For details, take a look at linux/Documentation/cpu-freq. 
+
+  If in doubt, say N.
+
+CONFIG_X86_LONGHAUL
+  This adds the CPUFreq driver for VIA Samuel/CyrixIII, 
+  VIA Cyrix Samuel/C3, VIA Cyrix Ezra and VIA Cyrix Ezra-T 
+  processors.
+
+  If you do not want to scale the Front Side Bus or voltage,
+  pass the module parameter "dont_scale_fsb=1" or
+  "dont_scale_voltage=1". Additionally, it is advised that
+  you pass the current Front Side Bus speed (in MHz) to 
+  this module as module parameter "current_fsb", e.g. 
+  "current_fsb=133" for a Front Side Bus speed of 133 MHz.
+
+  For details, take a look at linux/Documentation/cpu-freq. 
+
+  If in doubt, say N.
+
+CONFIG_X86_SPEEDSTEP_PIIX4
+  This adds the CPUFreq driver for certain mobile Intel Pentium III
+  (Coppermine), all mobile Intel Pentium III-M (Tualatin) and all
+  mobile Intel Pentium 4 P4-Ms on chipsets with an Intel PIIX4
+  southbridge.
+
+  For details, take a look at linux/Documentation/cpu-freq. 
+
+  If in doubt, say N.
+
+CONFIG_X86_SPEEDSTEP_ICH
+  This adds the CPUFreq driver for certain mobile Intel Pentium III
+  (Coppermine), all mobile Intel Pentium III-M (Tualatin) and all
+  mobile Intel Pentium 4 P4-Ms on chipsets with an Intel ICH2, ICH3,
+  or ICH4 southbridge.
+
+  For details, take a look at linux/Documentation/cpu-freq. 
+
+  If in doubt, say N.
+
+CONFIG_X86_SPEEDSTEP_CENTRINO
+  This adds the CPUFreq driver for Enhanced SpeedStep enabled
+  mobile CPUs. This means Intel Pentium M (Centrino) CPUs.
+
+  For details, take a look at linux/Documentation/cpu-freq.
+
+  If in doubt, say N.
+
+CONFIG_X86_SPEEDSTEP_SMI
+  This adds the CPUFreq driver for certain mobile Intel Pentium III
+  (Coppermine), all mobile Intel Pentium III-M (Tualatin)  
+  on systems which have an Intel 440BX/ZX/MX southbridge.
+
+  For details, take a look at linux/Documentation/cpu-freq.
+
+  If in doubt, say N.
+
+CONFIG_X86_LONGRUN
+  This adds the CPUFreq driver for Transmeta Crusoe processors which
+  support LongRun.
+
+  For details, take a look at linux/Documentation/cpu-freq. 
+
+  If in doubt, say N.
+
+CONFIG_X86_GX_SUSPMOD
+  This adds the CPUFreq driver for NatSemi Geode processors which
+  support suspend modulation.
+
+  For details, take a look at linux/Documentation/cpu-freq.
+
+  If in doubt, say N.
+
+CONFIG_CPU_FREQ_GOV_USERSPACE
+  Enable this cpufreq governor when you either want to set the
+  CPU frequency manually or when an userspace programm shall
+  be able to set the CPU dynamically, like on LART 
+  ( http://www.lart.tudelft.nl/ )
+
+  For details, take a look at linux/Documentation/cpu-freq. 
+
+  If in doubt, say Y.
+
 CRC32 functions
 CONFIG_CRC32
   This option is provided for the case where no in-kernel-tree
diff -uNr linux-2.4.23-vanilla/Documentation/cpu-freq/core.txt
linux/Documentation/cpu-freq/core.txt
--- linux-2.4.23-vanilla/Documentation/cpu-freq/core.txt	1970-01-01
07:30:00.000000000 +0730
+++ linux/Documentation/cpu-freq/core.txt	2003-08-25
22:58:44.000000000 +0800
@@ -0,0 +1,94 @@
+     CPU frequency and voltage scaling code in the Linux(TM) kernel
+
+
+		         L i n u x    C P U F r e q
+
+			  C P U F r e q    C o r e
+
+
+		    Dominik Brodowski  <linux@xxxxxxxx>
+		     David Kimdon <dwhedon@xxxxxxxxxx>
+
+
+
+   Clock scaling allows you to change the clock speed of the CPUs on the
+    fly. This is a nice method to save battery power, because the lower
+            the clock speed, the less power the CPU consumes.
+
+
+Contents:
+---------
+1.  CPUFreq core and interfaces
+2.  CPUFreq notifiers
+
+1. General Information
+=======================
+
+The CPUFreq core code is located in linux/kernel/cpufreq.c. This
+cpufreq code offers a standardized interface for the CPUFreq
+architecture drivers (those pieces of code that do actual
+frequency transitions), as well as to "notifiers". These are device
+drivers or other part of the kernel that need to be informed of
+policy changes (ex. thermal modules like ACPI) or of all
+frequency changes (ex. timing code) or even need to force certain
+speed limits (like LCD drivers on ARM architecture). Additionally, the
+kernel "constant" loops_per_jiffy is updated on frequency changes
+here.
+
+Reference counting is done by cpufreq_get_cpu and cpufreq_put_cpu,
+which make sure that the cpufreq processor driver is correctly
+registered with the core, and will not be unloaded until
+cpufreq_put_cpu is called.
+
+2. CPUFreq notifiers
+====================
+
+CPUFreq notifiers conform to the standard kernel notifier interface.
+See linux/include/linux/notifier.h for details on notifiers.
+
+There are two different CPUFreq notifiers - policy notifiers and
+transition notifiers.
+
+
+2.1 CPUFreq policy notifiers
+----------------------------
+
+These are notified when a new policy is intended to be set. Each
+CPUFreq policy notifier is called three times for a policy transition:
+
+1.) During CPUFREQ_ADJUST all CPUFreq notifiers may change the limit if
+    they see a need for this - may it be thermal considerations or
+    hardware limitations.
+
+2.) During CPUFREQ_INCOMPATIBLE only changes may be done in order to avoid
+    hardware failure.
+
+3.) And during CPUFREQ_NOTIFY all notifiers are informed of the new policy
+   - if two hardware drivers failed to agree on a new policy before this
+   stage, the incompatible hardware shall be shut down, and the user
+   informed of this.
+
+The phase is specified in the second argument to the notifier.
+
+The third argument, a void *pointer, points to a struct cpufreq_policy
+consisting of five values: cpu, min, max, policy and max_cpu_freq. min 
+and max are the lower and upper frequencies (in kHz) of the new
+policy, policy the new policy, cpu the number of the affected CPU; and 
+max_cpu_freq the maximum supported CPU frequency. This value is given 
+for informational purposes only.
+
+
+2.2 CPUFreq transition notifiers
+--------------------------------
+
+These are notified twice when the CPUfreq driver switches the CPU core
+frequency and this change has any external implications.
+
+The second argument specifies the phase - CPUFREQ_PRECHANGE or
+CPUFREQ_POSTCHANGE.
+
+The third argument is a struct cpufreq_freqs with the following
+values:
+cpu	- number of the affected CPU
+old	- old frequency
+new	- new frequency
diff -uNr linux-2.4.23-vanilla/Documentation/cpu-freq/cpu-drivers.txt
linux/Documentation/cpu-freq/cpu-drivers.txt
--- linux-2.4.23-vanilla/Documentation/cpu-freq/cpu-drivers.txt	1970-01-01
07:30:00.000000000 +0730
+++ linux/Documentation/cpu-freq/cpu-drivers.txt	2003-08-25
22:58:46.000000000 +0800
@@ -0,0 +1,210 @@
+     CPU frequency and voltage scaling code in the Linux(TM) kernel
+
+
+		         L i n u x    C P U F r e q
+
+			   C P U   D r i v e r s 
+
+		       - information for developers -
+
+
+		    Dominik Brodowski  <linux@xxxxxxxx>
+
+
+
+   Clock scaling allows you to change the clock speed of the CPUs on the
+    fly. This is a nice method to save battery power, because the lower
+            the clock speed, the less power the CPU consumes.
+
+
+Contents:
+---------
+1.   What To Do?
+1.1  Initialization
+1.2  Per-CPU Initialization
+1.3  verify
+1.4  target or setpolicy?
+1.5  target
+1.6  setpolicy
+2.   Frequency Table Helpers
+
+
+
+1. What To Do?
+==============
+
+So, you just got a brand-new CPU / chipset with datasheets and want to
+add cpufreq support for this CPU / chipset? Great. Here are some hints
+on what is necessary:
+
+
+1.1 Initialization
+------------------
+
+First of all, in an __initcall level 7 (module_init()) or later
+function check whether this kernel runs on the right CPU and the right
+chipset. If so, register a struct cpufreq_driver with the CPUfreq core
+using cpufreq_register_driver()
+
+What shall this struct cpufreq_driver contain? 
+
+cpufreq_driver.name -		The name of this driver.
+
+cpufreq_driver.owner -		THIS_MODULE;
+
+cpufreq_driver.init -		A pointer to the per-CPU initialization 
+				function.
+
+cpufreq_driver.verify -		A pointer to a "verification"
function.
+
+cpufreq_driver.setpolicy _or_ 
+cpufreq_driver.target -		See below on the differences.
+
+And optionally
+
+cpufreq_driver.exit -		A pointer to a per-CPU cleanup function.
+
+cpufreq_driver.attr -		A pointer to a NULL-terminated list of
+				"struct freq_attr" which allow to
+				export values to sysfs.
+
+
+1.2 Per-CPU Initialization
+--------------------------
+
+Whenever a new CPU is registered with the device model, or after the
+cpufreq driver registers itself, the per-CPU initialization function 
+cpufreq_driver.init is called. It takes a struct cpufreq_policy
+*policy as argument. What to do now?
+
+If necessary, activate the CPUfreq support on your CPU.
+
+Then, the driver must fill in the following values:
+
+policy->cpuinfo.min_freq _and_
+policy->cpuinfo.max_freq -	the minimum and maximum frequency 
+				(in kHz) which is supported by 
+				this CPU
+policy->cpuinfo.transition_latency   the time it takes on this CPU to
+				switch between two frequencies (if
+				appropriate, else specify
+				CPUFREQ_ETERNAL)
+
+policy->cur			The current operating frequency of
+				this CPU (if appropriate)
+policy->min, 
+policy->max, 
+policy->policy and, if necessary,
+policy->governor		must contain the "default policy" for
+				this CPU. A few moments later,
+				cpufreq_driver.verify and either
+				cpufreq_driver.setpolicy or
+				cpufreq_driver.target is called with
+				these values.
+
+For setting some of these values, the frequency table helpers might be
+helpful. See the section 2 for more information on them.
+
+
+1.3 verify
+------------
+
+When the user decides a new policy (consisting of
+"policy,governor,min,max") shall be set, this policy must be validated
+so that incompatible values can be corrected. For verifying these
+values, a frequency table helper and/or the
+cpufreq_verify_within_limits(struct cpufreq_policy *policy, unsigned
+int min_freq, unsigned int max_freq) function might be helpful. See
+section 2 for details on frequency table helpers.
+
+You need to make sure that at least one valid frequency (or operating
+range) is within policy->min and policy->max. If necessary, increase
+policy->max fist, and only if this is no solution, decreas policy->min.
+
+
+1.4 target or setpolicy?
+----------------------------
+
+Most cpufreq drivers or even most cpu frequency scaling algorithms 
+only allow the CPU to be set to one frequency. For these, you use the
+->target call.
+
+Some cpufreq-capable processors switch the frequency between certain
+limits on their own. These shall use the ->setpolicy call
+
+
+1.4. target
+-------------
+
+The target call has three arguments: struct cpufreq_policy *policy,
+unsigned int target_frequency, unsigned int relation.
+
+The CPUfreq driver must set the new frequency when called here. The
+actual frequency must be determined using the following rules:
+
+- keep close to "target_freq"
+- policy->min <= new_freq <= policy->max (THIS MUST BE VALID!!!)
+- if relation==CPUFREQ_REL_L, try to select a new_freq higher than or equal
+  target_freq. ("L for lowest, but no lower than")
+- if relation==CPUFREQ_REL_H, try to select a new_freq lower than or equal
+  target_freq. ("H for highest, but no higher than")
+
+Here again the frequency table helper might assist you - see section 3
+for details.
+
+
+1.5 setpolicy
+---------------
+
+The setpolicy call only takes a struct cpufreq_policy *policy as
+argument. You need to set the lower limit of the in-processor or
+in-chipset dynamic frequency switching to policy->min, the upper limit
+to policy->max, and -if supported- select a performance-oriented
+setting when policy->policy is CPUFREQ_POLICY_PERFORMANCE, and a
+powersaving-oriented setting when CPUFREQ_POLICY_POWERSAVE. Also check
+the reference implementation in arch/i386/kernel/cpu/cpufreq/longrun.c
+
+
+
+2. Frequency Table Helpers
+==========================
+
+As most cpufreq processors only allow for being set to a few specific
+frequencies, a "frequency table" with some functions might assist in
+some work of the processor driver. Such a "frequency table" consists
+of an array of struct cpufreq_freq_table entries, with any value in
+"index" you want to use, and the corresponding frequency in
+"frequency". At the end of the table, you need to add a
+cpufreq_freq_table entry with frequency set to CPUFREQ_TABLE_END. And
+if you want to skip one entry in the table, set the frequency to 
+CPUFREQ_ENTRY_INVALID. The entries don't need to be in ascending
+order.
+
+By calling cpufreq_frequency_table_cpuinfo(struct cpufreq_policy *policy,
+					struct cpufreq_frequency_table
*table);
+the cpuinfo.min_freq and cpuinfo.max_freq values are detected, and
+policy->min and policy->max are set to the same values. This is
+helpful for the per-CPU initialization stage.
+
+int cpufreq_frequency_table_verify(struct cpufreq_policy *policy,
+                                   struct cpufreq_frequency_table *table);
+assures that at least one valid frequency is within policy->min and
+policy->max, and all other criteria are met. This is helpful for the
+->verify call.
+
+int cpufreq_frequency_table_target(struct cpufreq_policy *policy,
+                                   struct cpufreq_frequency_table *table,
+                                   unsigned int target_freq,
+                                   unsigned int relation,
+                                   unsigned int *index);
+
+is the corresponding frequency table helper for the ->target
+stage. Just pass the values to this function, and the unsigned int
+index returns the number of the frequency table entry which contains
+the frequency the CPU shall be set to. PLEASE NOTE: This is not the
+"index" which is in this cpufreq_table_entry.index, but instead
+cpufreq_table[index]. So, the new frequency is
+cpufreq_table[index].frequency, and the value you stored into the
+frequency table "index" field is
+cpufreq_table[index].index.
+
diff -uNr linux-2.4.23-vanilla/Documentation/cpu-freq/cpufreq/core.txt
linux/Documentation/cpu-freq/cpufreq/core.txt
--- linux-2.4.23-vanilla/Documentation/cpu-freq/cpufreq/core.txt
1970-01-01 07:30:00.000000000 +0730
+++ linux/Documentation/cpu-freq/cpufreq/core.txt	2003-08-25
22:58:44.000000000 +0800
@@ -0,0 +1,94 @@
+     CPU frequency and voltage scaling code in the Linux(TM) kernel
+
+
+		         L i n u x    C P U F r e q
+
+			  C P U F r e q    C o r e
+
+
+		    Dominik Brodowski  <linux@xxxxxxxx>
+		     David Kimdon <dwhedon@xxxxxxxxxx>
+
+
+
+   Clock scaling allows you to change the clock speed of the CPUs on the
+    fly. This is a nice method to save battery power, because the lower
+            the clock speed, the less power the CPU consumes.
+
+
+Contents:
+---------
+1.  CPUFreq core and interfaces
+2.  CPUFreq notifiers
+
+1. General Information
+=======================
+
+The CPUFreq core code is located in linux/kernel/cpufreq.c. This
+cpufreq code offers a standardized interface for the CPUFreq
+architecture drivers (those pieces of code that do actual
+frequency transitions), as well as to "notifiers". These are device
+drivers or other part of the kernel that need to be informed of
+policy changes (ex. thermal modules like ACPI) or of all
+frequency changes (ex. timing code) or even need to force certain
+speed limits (like LCD drivers on ARM architecture). Additionally, the
+kernel "constant" loops_per_jiffy is updated on frequency changes
+here.
+
+Reference counting is done by cpufreq_get_cpu and cpufreq_put_cpu,
+which make sure that the cpufreq processor driver is correctly
+registered with the core, and will not be unloaded until
+cpufreq_put_cpu is called.
+
+2. CPUFreq notifiers
+====================
+
+CPUFreq notifiers conform to the standard kernel notifier interface.
+See linux/include/linux/notifier.h for details on notifiers.
+
+There are two different CPUFreq notifiers - policy notifiers and
+transition notifiers.
+
+
+2.1 CPUFreq policy notifiers
+----------------------------
+
+These are notified when a new policy is intended to be set. Each
+CPUFreq policy notifier is called three times for a policy transition:
+
+1.) During CPUFREQ_ADJUST all CPUFreq notifiers may change the limit if
+    they see a need for this - may it be thermal considerations or
+    hardware limitations.
+
+2.) During CPUFREQ_INCOMPATIBLE only changes may be done in order to avoid
+    hardware failure.
+
+3.) And during CPUFREQ_NOTIFY all notifiers are informed of the new policy
+   - if two hardware drivers failed to agree on a new policy before this
+   stage, the incompatible hardware shall be shut down, and the user
+   informed of this.
+
+The phase is specified in the second argument to the notifier.
+
+The third argument, a void *pointer, points to a struct cpufreq_policy
+consisting of five values: cpu, min, max, policy and max_cpu_freq. min 
+and max are the lower and upper frequencies (in kHz) of the new
+policy, policy the new policy, cpu the number of the affected CPU; and 
+max_cpu_freq the maximum supported CPU frequency. This value is given 
+for informational purposes only.
+
+
+2.2 CPUFreq transition notifiers
+--------------------------------
+
+These are notified twice when the CPUfreq driver switches the CPU core
+frequency and this change has any external implications.
+
+The second argument specifies the phase - CPUFREQ_PRECHANGE or
+CPUFREQ_POSTCHANGE.
+
+The third argument is a struct cpufreq_freqs with the following
+values:
+cpu	- number of the affected CPU
+old	- old frequency
+new	- new frequency
diff -uNr
linux-2.4.23-vanilla/Documentation/cpu-freq/cpufreq/cpu-drivers.txt
linux/Documentation/cpu-freq/cpufreq/cpu-drivers.txt
--- linux-2.4.23-vanilla/Documentation/cpu-freq/cpufreq/cpu-drivers.txt
1970-01-01 07:30:00.000000000 +0730
+++ linux/Documentation/cpu-freq/cpufreq/cpu-drivers.txt	2003-08-25
22:58:46.000000000 +0800
@@ -0,0 +1,210 @@
+     CPU frequency and voltage scaling code in the Linux(TM) kernel
+
+
+		         L i n u x    C P U F r e q
+
+			   C P U   D r i v e r s 
+
+		       - information for developers -
+
+
+		    Dominik Brodowski  <linux@xxxxxxxx>
+
+
+
+   Clock scaling allows you to change the clock speed of the CPUs on the
+    fly. This is a nice method to save battery power, because the lower
+            the clock speed, the less power the CPU consumes.
+
+
+Contents:
+---------
+1.   What To Do?
+1.1  Initialization
+1.2  Per-CPU Initialization
+1.3  verify
+1.4  target or setpolicy?
+1.5  target
+1.6  setpolicy
+2.   Frequency Table Helpers
+
+
+
+1. What To Do?
+==============
+
+So, you just got a brand-new CPU / chipset with datasheets and want to
+add cpufreq support for this CPU / chipset? Great. Here are some hints
+on what is necessary:
+
+
+1.1 Initialization
+------------------
+
+First of all, in an __initcall level 7 (module_init()) or later
+function check whether this kernel runs on the right CPU and the right
+chipset. If so, register a struct cpufreq_driver with the CPUfreq core
+using cpufreq_register_driver()
+
+What shall this struct cpufreq_driver contain? 
+
+cpufreq_driver.name -		The name of this driver.
+
+cpufreq_driver.owner -		THIS_MODULE;
+
+cpufreq_driver.init -		A pointer to the per-CPU initialization 
+				function.
+
+cpufreq_driver.verify -		A pointer to a "verification"
function.
+
+cpufreq_driver.setpolicy _or_ 
+cpufreq_driver.target -		See below on the differences.
+
+And optionally
+
+cpufreq_driver.exit -		A pointer to a per-CPU cleanup function.
+
+cpufreq_driver.attr -		A pointer to a NULL-terminated list of
+				"struct freq_attr" which allow to
+				export values to sysfs.
+
+
+1.2 Per-CPU Initialization
+--------------------------
+
+Whenever a new CPU is registered with the device model, or after the
+cpufreq driver registers itself, the per-CPU initialization function 
+cpufreq_driver.init is called. It takes a struct cpufreq_policy
+*policy as argument. What to do now?
+
+If necessary, activate the CPUfreq support on your CPU.
+
+Then, the driver must fill in the following values:
+
+policy->cpuinfo.min_freq _and_
+policy->cpuinfo.max_freq -	the minimum and maximum frequency 
+				(in kHz) which is supported by 
+				this CPU
+policy->cpuinfo.transition_latency   the time it takes on this CPU to
+				switch between two frequencies (if
+				appropriate, else specify
+				CPUFREQ_ETERNAL)
+
+policy->cur			The current operating frequency of
+				this CPU (if appropriate)
+policy->min, 
+policy->max, 
+policy->policy and, if necessary,
+policy->governor		must contain the "default policy" for
+				this CPU. A few moments later,
+				cpufreq_driver.verify and either
+				cpufreq_driver.setpolicy or
+				cpufreq_driver.target is called with
+				these values.
+
+For setting some of these values, the frequency table helpers might be
+helpful. See the section 2 for more information on them.
+
+
+1.3 verify
+------------
+
+When the user decides a new policy (consisting of
+"policy,governor,min,max") shall be set, this policy must be validated
+so that incompatible values can be corrected. For verifying these
+values, a frequency table helper and/or the
+cpufreq_verify_within_limits(struct cpufreq_policy *policy, unsigned
+int min_freq, unsigned int max_freq) function might be helpful. See
+section 2 for details on frequency table helpers.
+
+You need to make sure that at least one valid frequency (or operating
+range) is within policy->min and policy->max. If necessary, increase
+policy->max fist, and only if this is no solution, decreas policy->min.
+
+
+1.4 target or setpolicy?
+----------------------------
+
+Most cpufreq drivers or even most cpu frequency scaling algorithms 
+only allow the CPU to be set to one frequency. For these, you use the
+->target call.
+
+Some cpufreq-capable processors switch the frequency between certain
+limits on their own. These shall use the ->setpolicy call
+
+
+1.4. target
+-------------
+
+The target call has three arguments: struct cpufreq_policy *policy,
+unsigned int target_frequency, unsigned int relation.
+
+The CPUfreq driver must set the new frequency when called here. The
+actual frequency must be determined using the following rules:
+
+- keep close to "target_freq"
+- policy->min <= new_freq <= policy->max (THIS MUST BE VALID!!!)
+- if relation==CPUFREQ_REL_L, try to select a new_freq higher than or equal
+  target_freq. ("L for lowest, but no lower than")
+- if relation==CPUFREQ_REL_H, try to select a new_freq lower than or equal
+  target_freq. ("H for highest, but no higher than")
+
+Here again the frequency table helper might assist you - see section 3
+for details.
+
+
+1.5 setpolicy
+---------------
+
+The setpolicy call only takes a struct cpufreq_policy *policy as
+argument. You need to set the lower limit of the in-processor or
+in-chipset dynamic frequency switching to policy->min, the upper limit
+to policy->max, and -if supported- select a performance-oriented
+setting when policy->policy is CPUFREQ_POLICY_PERFORMANCE, and a
+powersaving-oriented setting when CPUFREQ_POLICY_POWERSAVE. Also check
+the reference implementation in arch/i386/kernel/cpu/cpufreq/longrun.c
+
+
+
+2. Frequency Table Helpers
+==========================
+
+As most cpufreq processors only allow for being set to a few specific
+frequencies, a "frequency table" with some functions might assist in
+some work of the processor driver. Such a "frequency table" consists
+of an array of struct cpufreq_freq_table entries, with any value in
+"index" you want to use, and the corresponding frequency in
+"frequency". At the end of the table, you need to add a
+cpufreq_freq_table entry with frequency set to CPUFREQ_TABLE_END. And
+if you want to skip one entry in the table, set the frequency to 
+CPUFREQ_ENTRY_INVALID. The entries don't need to be in ascending
+order.
+
+By calling cpufreq_frequency_table_cpuinfo(struct cpufreq_policy *policy,
+					struct cpufreq_frequency_table
*table);
+the cpuinfo.min_freq and cpuinfo.max_freq values are detected, and
+policy->min and policy->max are set to the same values. This is
+helpful for the per-CPU initialization stage.
+
+int cpufreq_frequency_table_verify(struct cpufreq_policy *policy,
+                                   struct cpufreq_frequency_table *table);
+assures that at least one valid frequency is within policy->min and
+policy->max, and all other criteria are met. This is helpful for the
+->verify call.
+
+int cpufreq_frequency_table_target(struct cpufreq_policy *policy,
+                                   struct cpufreq_frequency_table *table,
+                                   unsigned int target_freq,
+                                   unsigned int relation,
+                                   unsigned int *index);
+
+is the corresponding frequency table helper for the ->target
+stage. Just pass the values to this function, and the unsigned int
+index returns the number of the frequency table entry which contains
+the frequency the CPU shall be set to. PLEASE NOTE: This is not the
+"index" which is in this cpufreq_table_entry.index, but instead
+cpufreq_table[index]. So, the new frequency is
+cpufreq_table[index].frequency, and the value you stored into the
+frequency table "index" field is
+cpufreq_table[index].index.
+
diff -uNr linux-2.4.23-vanilla/Documentation/cpu-freq/cpufreq/governors.txt
linux/Documentation/cpu-freq/cpufreq/governors.txt
--- linux-2.4.23-vanilla/Documentation/cpu-freq/cpufreq/governors.txt
1970-01-01 07:30:00.000000000 +0730
+++ linux/Documentation/cpu-freq/cpufreq/governors.txt	2003-08-25
22:58:46.000000000 +0800
@@ -0,0 +1,155 @@
+     CPU frequency and voltage scaling code in the Linux(TM) kernel
+
+
+		         L i n u x    C P U F r e q
+
+		      C P U F r e q   G o v e r n o r s
+
+		   - information for users and developers -
+
+
+		    Dominik Brodowski  <linux@xxxxxxxx>
+
+
+
+   Clock scaling allows you to change the clock speed of the CPUs on the
+    fly. This is a nice method to save battery power, because the lower
+            the clock speed, the less power the CPU consumes.
+
+
+Contents:
+---------
+1.   What is a CPUFreq Governor?
+
+2.   Governors In the Linux Kernel
+2.1  Performance
+2.2  Powersave
+2.3  Userspace
+
+3.   The Governor Interface in the CPUfreq Core
+
+
+
+1. What Is A CPUFreq Governor?
+==============================
+
+Most cpufreq drivers (in fact, all except one, longrun) or even most
+cpu frequency scaling algorithms only offer the CPU to be set to one
+frequency. In order to offer dynamic frequency scaling, the cpufreq
+core must be able to tell these drivers of a "target frequency". So
+these specific drivers will be transformed to offer a "->target"
+call instead of the existing "->setpolicy" call. For "longrun", all
+stays the same, though.
+
+How to decide what frequency within the CPUfreq policy should be used?
+That's done using "cpufreq governors". Two are already in this patch
+-- they're the already existing "powersave" and "performance" which
+set the frequency statically to the lowest or highest frequency,
+respectively. At least two more such governors will be ready for
+addition in the near future, but likely many more as there are various
+different theories and models about dynamic frequency scaling
+around. Using such a generic interface as cpufreq offers to scaling
+governors, these can be tested extensively, and the best one can be
+selected for each specific use.
+
+Basically, it's the following flow graph:
+
+CPU can be set to switch independetly	 |	   CPU can only be set
+      within specific "limits"		 |       to specific frequencies
+
+                                 "CPUfreq policy"
+		consists of frequency limits (policy->{min,max})
+  		     and CPUfreq governor to be used
+			 /		      \
+			/		       \
+		       /		       the cpufreq governor decides
+		      /			       (dynamically or statically)
+		     /			       what target_freq to set
within
+		    /			       the limits of
policy->{min,max}
+		   /			            \
+		  /				     \
+	Using the ->setpolicy call,		 Using the ->target call,
+	    the limits and the			  the frequency closest
+	     "policy" is set.			  to target_freq is set.
+						  It is assured that it
+						  is within
policy->{min,max}
+
+
+2. Governors In the Linux Kernel
+================================
+
+2.1 Performance
+---------------
+
+The CPUfreq governor "performance" sets the CPU statically to the
+highest frequency within the borders of scaling_min_freq and
+scaling_max_freq.
+
+
+2.1 Powersave
+-------------
+
+The CPUfreq governor "powersave" sets the CPU statically to the
+lowest frequency within the borders of scaling_min_freq and
+scaling_max_freq.
+
+
+2.2 Userspace
+-------------
+
+The CPUfreq governor "userspace" allows the user, or any userspace
+program running with UID "root", to set the CPU to a specific frequency
+by making a sysfs file "scaling_setspeed" available in the CPU-device
+directory.
+
+
+
+3. The Governor Interface in the CPUfreq Core
+=============================================
+
+A new governor must register itself with the CPUfreq core using
+"cpufreq_register_governor". The struct cpufreq_governor, which has to
+be passed to that function, must contain the following values:
+
+governor->name -	    A unique name for this governor
+governor->governor -	    The governor callback function
+governor->owner	-	    .THIS_MODULE for the governor module (if

+			    appropriate)
+
+The governor->governor callback is called with the current (or to-be-set)
+cpufreq_policy struct for that CPU, and an unsigned int event. The
+following events are currently defined:
+
+CPUFREQ_GOV_START:   This governor shall start its duty for the CPU
+		     policy->cpu
+CPUFREQ_GOV_STOP:    This governor shall end its duty for the CPU
+		     policy->cpu
+CPUFREQ_GOV_LIMITS:  The limits for CPU policy->cpu have changed to
+		     policy->min and policy->max.
+
+If you need other "events" externally of your driver, _only_ use the
+cpufreq_governor_l(unsigned int cpu, unsigned int event) call to the
+CPUfreq core to ensure proper locking.
+
+
+The CPUfreq governor may call the CPU processor driver using one of
+these two functions:
+
+int cpufreq_driver_target(struct cpufreq_policy *policy,
+                                 unsigned int target_freq,
+                                 unsigned int relation);
+
+int __cpufreq_driver_target(struct cpufreq_policy *policy,
+                                   unsigned int target_freq,
+                                   unsigned int relation);
+
+target_freq must be within policy->min and policy->max, of course.
+What's the difference between these two functions? When your governor
+still is in a direct code path of a call to governor->governor, the
+per-CPU cpufreq lock is still held in the cpufreq core, and there's
+no need to lock it again (in fact, this would cause a deadlock). So
+use __cpufreq_driver_target only in these cases. In all other cases 
+(for example, when there's a "daemonized" function that wakes up 
+every second), use cpufreq_driver_target to lock the cpufreq per-CPU
+lock before the command is passed to the cpufreq processor driver.
+
diff -uNr linux-2.4.23-vanilla/Documentation/cpu-freq/cpufreq/index.txt
linux/Documentation/cpu-freq/cpufreq/index.txt
--- linux-2.4.23-vanilla/Documentation/cpu-freq/cpufreq/index.txt
1970-01-01 07:30:00.000000000 +0730
+++ linux/Documentation/cpu-freq/cpufreq/index.txt	2003-08-25
22:58:46.000000000 +0800
@@ -0,0 +1,56 @@
+     CPU frequency and voltage scaling code in the Linux(TM) kernel
+
+
+		         L i n u x    C P U F r e q
+
+
+
+
+		    Dominik Brodowski  <linux@xxxxxxxx>
+
+
+
+   Clock scaling allows you to change the clock speed of the CPUs on the
+    fly. This is a nice method to save battery power, because the lower
+            the clock speed, the less power the CPU consumes.
+
+
+
+Documents in this directory:
+----------------------------
+core.txt	-	General description of the CPUFreq core and
+			of CPUFreq notifiers
+
+cpu-drivers.txt -	How to implement a new cpufreq processor driver
+
+governors.txt	-	What are cpufreq governors and how to
+			implement them?
+
+index.txt	-	File index, Mailing list and Links (this document)
+
+user-guide.txt	-	User Guide to CPUFreq
+
+
+Mailing List
+------------
+There is a CPU frequency changing CVS commit and general list where
+you can report bugs, problems or submit patches. To post a message,
+send an email to cpufreq@xxxxxxxxxxxxxxxx, to subscribe go to
+http://www.linux.org.uk/mailman/listinfo/cpufreq. Previous post to the
+mailing list are available to subscribers at
+http://www.linux.org.uk/mailman/private/cpufreq/.
+
+
+Links
+-----
+the FTP archives:
+* ftp://ftp.linux.org.uk/pub/linux/cpufreq/
+
+how to access the CVS repository:
+* http://cvs.arm.linux.org.uk/
+
+the CPUFreq Mailing list:
+* http://www.linux.org.uk/mailman/listinfo/cpufreq
+
+Clock and voltage scaling for the SA-1100:
+* http://www.lart.tudelft.nl/projects/scaling
diff -uNr linux-2.4.23-vanilla/Documentation/cpu-freq/cpufreq/user-guide.txt
linux/Documentation/cpu-freq/cpufreq/user-guide.txt
--- linux-2.4.23-vanilla/Documentation/cpu-freq/cpufreq/user-guide.txt
1970-01-01 07:30:00.000000000 +0730
+++ linux/Documentation/cpu-freq/cpufreq/user-guide.txt	2003-09-30
19:25:52.000000000 +0800
@@ -0,0 +1,184 @@
+     CPU frequency and voltage scaling code in the Linux(TM) kernel
+
+
+		         L i n u x    C P U F r e q
+
+			     U S E R   G U I D E
+
+
+		    Dominik Brodowski  <linux@xxxxxxxx>
+
+
+
+   Clock scaling allows you to change the clock speed of the CPUs on the
+    fly. This is a nice method to save battery power, because the lower
+            the clock speed, the less power the CPU consumes.
+
+
+Contents:
+---------
+1. Supported Architectures and Processors
+1.1 ARM
+1.2 x86
+1.3 sparc64
+1.4 ppc
+1.5 SuperH
+
+2. "Policy" / "Governor"?
+2.1 Policy
+2.2 Governor
+
+3. How to change the CPU cpufreq policy and/or speed
+3.1 Preferred interface: sysfs
+3.2 Deprecated interfaces
+
+
+
+1. Supported Architectures and Processors
+=========================================
+
+1.1 ARM
+-------
+
+The following ARM processors are supported by cpufreq:
+
+ARM Integrator
+ARM-SA1100
+ARM-SA1110
+
+
+1.2 x86
+-------
+
+The following processors for the x86 architecture are supported by cpufreq:
+
+AMD Elan - SC400, SC410
+AMD mobile K6-2+
+AMD mobile K6-3+
+AMD mobile Duron
+AMD mobile Athlon
+AMD Opteron
+AMD Athlon 64
+Cyrix Media GXm
+Intel mobile PIII and Intel mobile PIII-M on certain chipsets
+Intel Pentium 4, Intel Xeon
+Intel Pentium M (Centrino)
+National Semiconductors Geode GX
+Transmeta Crusoe
+VIA Cyrix 3 / C3
+various processors on some ACPI 2.0-compatible systems [*]
+
+[*] Only if "ACPI Processor Performance States" are available
+to the ACPI<->BIOS interface.
+
+
+1.3 sparc64
+-----------
+
+The following processors for the sparc64 architecture are supported by
+cpufreq:
+
+UltraSPARC-III
+
+
+1.4 ppc
+-------
+
+Several "PowerBook" and "iBook2" notebooks are supported.
+
+
+1.5 SuperH
+----------
+
+The following SuperH processors are supported by cpufreq:
+
+SH-3
+SH-4
+
+
+2. "Policy" / "Governor" ?
+==========================
+
+Some CPU frequency scaling-capable processor switch between various
+frequencies and operating voltages "on the fly" without any kernel or
+user involvement. This guarantees very fast switching to a frequency
+which is high enough to serve the user's needs, but low enough to save
+power.
+
+
+2.1 Policy
+----------
+
+On these systems, all you can do is select the lower and upper
+frequency limit as well as whether you want more aggressive
+power-saving or more instantly available processing power.
+
+
+2.2 Governor
+------------
+
+On all other cpufreq implementations, these boundaries still need to
+be set. Then, a "governor" must be selected. Such a "governor" decides
+what speed the processor shall run within the boundaries. One such
+"governor" is the "userspace" governor. This one allows the user - or
+a yet-to-implement userspace program - to decide what specific speed
+the processor shall run at.
+
+
+3. How to change the CPU cpufreq policy and/or speed
+====================================================
+
+3.1 Preferred Interface: sysfs
+------------------------------
+
+The preferred interface is located in the sysfs filesystem. If you
+mounted it at /sys, the cpufreq interface is located in a subdirectory
+"cpufreq" within the cpu-device directory
+(e.g. /sys/devices/system/cpu/cpu0/cpufreq/ for the first CPU).
+
+cpuinfo_min_freq :		this file shows the minimum operating
+				frequency the processor can run at(in kHz) 
+cpuinfo_max_freq :		this file shows the maximum operating
+				frequency the processor can run at(in kHz) 
+scaling_driver :		this file shows what cpufreq driver is
+				used to set the frequency on this CPU
+
+scaling_available_governors :	this file shows the CPUfreq governors
+				available in this kernel. You can see the
+				currently activated governor in
+
+scaling_governor,		and by "echoing" the name of another
+				governor you can change it. Please note
+				that some governors won't load - they only
+				work on some specific architectures or
+				processors.
+scaling_min_freq and 
+scaling_max_freq		show the current "policy limits" (in
+				kHz). By echoing new values into these
+				files, you can change these limits.
+
+
+If you have selected the "userspace" governor which allows you to
+set the CPU operating frequency to a specific value, you can read out
+the current frequency in
+
+scaling_setspeed.		By "echoing" a new frequency into this
+				you can change the speed of the CPU,
+				but only within the limits of
+				scaling_min_freq and scaling_max_freq.
+				
+
+3.2 Deprecated Interfaces
+-------------------------
+
+Depending on your kernel configuration, you might find the following 
+cpufreq-related files:
+/proc/cpufreq
+/proc/sys/cpu/*/speed
+/proc/sys/cpu/*/speed-min
+/proc/sys/cpu/*/speed-max
+
+These are files for deprecated interfaces to cpufreq, which offer far
+less functionality. Because of this, these interfaces aren't described
+here.
+
diff -uNr linux-2.4.23-vanilla/Documentation/cpu-freq/governors.txt
linux/Documentation/cpu-freq/governors.txt
--- linux-2.4.23-vanilla/Documentation/cpu-freq/governors.txt	1970-01-01
07:30:00.000000000 +0730
+++ linux/Documentation/cpu-freq/governors.txt	2003-08-25
22:58:46.000000000 +0800
@@ -0,0 +1,155 @@
+     CPU frequency and voltage scaling code in the Linux(TM) kernel
+
+
+		         L i n u x    C P U F r e q
+
+		      C P U F r e q   G o v e r n o r s
+
+		   - information for users and developers -
+
+
+		    Dominik Brodowski  <linux@xxxxxxxx>
+
+
+
+   Clock scaling allows you to change the clock speed of the CPUs on the
+    fly. This is a nice method to save battery power, because the lower
+            the clock speed, the less power the CPU consumes.
+
+
+Contents:
+---------
+1.   What is a CPUFreq Governor?
+
+2.   Governors In the Linux Kernel
+2.1  Performance
+2.2  Powersave
+2.3  Userspace
+
+3.   The Governor Interface in the CPUfreq Core
+
+
+
+1. What Is A CPUFreq Governor?
+==============================
+
+Most cpufreq drivers (in fact, all except one, longrun) or even most
+cpu frequency scaling algorithms only offer the CPU to be set to one
+frequency. In order to offer dynamic frequency scaling, the cpufreq
+core must be able to tell these drivers of a "target frequency". So
+these specific drivers will be transformed to offer a "->target"
+call instead of the existing "->setpolicy" call. For "longrun", all
+stays the same, though.
+
+How to decide what frequency within the CPUfreq policy should be used?
+That's done using "cpufreq governors". Two are already in this patch
+-- they're the already existing "powersave" and "performance" which
+set the frequency statically to the lowest or highest frequency,
+respectively. At least two more such governors will be ready for
+addition in the near future, but likely many more as there are various
+different theories and models about dynamic frequency scaling
+around. Using such a generic interface as cpufreq offers to scaling
+governors, these can be tested extensively, and the best one can be
+selected for each specific use.
+
+Basically, it's the following flow graph:
+
+CPU can be set to switch independetly	 |	   CPU can only be set
+      within specific "limits"		 |       to specific frequencies
+
+                                 "CPUfreq policy"
+		consists of frequency limits (policy->{min,max})
+  		     and CPUfreq governor to be used
+			 /		      \
+			/		       \
+		       /		       the cpufreq governor decides
+		      /			       (dynamically or statically)
+		     /			       what target_freq to set
within
+		    /			       the limits of
policy->{min,max}
+		   /			            \
+		  /				     \
+	Using the ->setpolicy call,		 Using the ->target call,
+	    the limits and the			  the frequency closest
+	     "policy" is set.			  to target_freq is set.
+						  It is assured that it
+						  is within
policy->{min,max}
+
+
+2. Governors In the Linux Kernel
+================================
+
+2.1 Performance
+---------------
+
+The CPUfreq governor "performance" sets the CPU statically to the
+highest frequency within the borders of scaling_min_freq and
+scaling_max_freq.
+
+
+2.1 Powersave
+-------------
+
+The CPUfreq governor "powersave" sets the CPU statically to the
+lowest frequency within the borders of scaling_min_freq and
+scaling_max_freq.
+
+
+2.2 Userspace
+-------------
+
+The CPUfreq governor "userspace" allows the user, or any userspace
+program running with UID "root", to set the CPU to a specific frequency
+by making a sysfs file "scaling_setspeed" available in the CPU-device
+directory.
+
+
+
+3. The Governor Interface in the CPUfreq Core
+=============================================
+
+A new governor must register itself with the CPUfreq core using
+"cpufreq_register_governor". The struct cpufreq_governor, which has to
+be passed to that function, must contain the following values:
+
+governor->name -	    A unique name for this governor
+governor->governor -	    The governor callback function
+governor->owner	-	    .THIS_MODULE for the governor module (if

+			    appropriate)
+
+The governor->governor callback is called with the current (or to-be-set)
+cpufreq_policy struct for that CPU, and an unsigned int event. The
+following events are currently defined:
+
+CPUFREQ_GOV_START:   This governor shall start its duty for the CPU
+		     policy->cpu
+CPUFREQ_GOV_STOP:    This governor shall end its duty for the CPU
+		     policy->cpu
+CPUFREQ_GOV_LIMITS:  The limits for CPU policy->cpu have changed to
+		     policy->min and policy->max.
+
+If you need other "events" externally of your driver, _only_ use the
+cpufreq_governor_l(unsigned int cpu, unsigned int event) call to the
+CPUfreq core to ensure proper locking.
+
+
+The CPUfreq governor may call the CPU processor driver using one of
+these two functions:
+
+int cpufreq_driver_target(struct cpufreq_policy *policy,
+                                 unsigned int target_freq,
+                                 unsigned int relation);
+
+int __cpufreq_driver_target(struct cpufreq_policy *policy,
+                                   unsigned int target_freq,
+                                   unsigned int relation);
+
+target_freq must be within policy->min and policy->max, of course.
+What's the difference between these two functions? When your governor
+still is in a direct code path of a call to governor->governor, the
+per-CPU cpufreq lock is still held in the cpufreq core, and there's
+no need to lock it again (in fact, this would cause a deadlock). So
+use __cpufreq_driver_target only in these cases. In all other cases 
+(for example, when there's a "daemonized" function that wakes up 
+every second), use cpufreq_driver_target to lock the cpufreq per-CPU
+lock before the command is passed to the cpufreq processor driver.
+
diff -uNr linux-2.4.23-vanilla/Documentation/cpu-freq/index.txt
linux/Documentation/cpu-freq/index.txt
--- linux-2.4.23-vanilla/Documentation/cpu-freq/index.txt	1970-01-01
07:30:00.000000000 +0730
+++ linux/Documentation/cpu-freq/index.txt	2003-08-25
22:58:46.000000000 +0800
@@ -0,0 +1,56 @@
+     CPU frequency and voltage scaling code in the Linux(TM) kernel
+
+
+		         L i n u x    C P U F r e q
+
+
+
+
+		    Dominik Brodowski  <linux@xxxxxxxx>
+
+
+
+   Clock scaling allows you to change the clock speed of the CPUs on the
+    fly. This is a nice method to save battery power, because the lower
+            the clock speed, the less power the CPU consumes.
+
+
+
+Documents in this directory:
+----------------------------
+core.txt	-	General description of the CPUFreq core and
+			of CPUFreq notifiers
+
+cpu-drivers.txt -	How to implement a new cpufreq processor driver
+
+governors.txt	-	What are cpufreq governors and how to
+			implement them?
+
+index.txt	-	File index, Mailing list and Links (this document)
+
+user-guide.txt	-	User Guide to CPUFreq
+
+
+Mailing List
+------------
+There is a CPU frequency changing CVS commit and general list where
+you can report bugs, problems or submit patches. To post a message,
+send an email to cpufreq@xxxxxxxxxxxxxxxx, to subscribe go to
+http://www.linux.org.uk/mailman/listinfo/cpufreq. Previous post to the
+mailing list are available to subscribers at
+http://www.linux.org.uk/mailman/private/cpufreq/.
+
+
+Links
+-----
+the FTP archives:
+* ftp://ftp.linux.org.uk/pub/linux/cpufreq/
+
+how to access the CVS repository:
+* http://cvs.arm.linux.org.uk/
+
+the CPUFreq Mailing list:
+* http://www.linux.org.uk/mailman/listinfo/cpufreq
+
+Clock and voltage scaling for the SA-1100:
+* http://www.lart.tudelft.nl/projects/scaling
diff -uNr linux-2.4.23-vanilla/Documentation/cpu-freq/user-guide.txt
linux/Documentation/cpu-freq/user-guide.txt
--- linux-2.4.23-vanilla/Documentation/cpu-freq/user-guide.txt	1970-01-01
07:30:00.000000000 +0730
+++ linux/Documentation/cpu-freq/user-guide.txt	2003-09-30
19:25:52.000000000 +0800
@@ -0,0 +1,184 @@
+     CPU frequency and voltage scaling code in the Linux(TM) kernel
+
+
+		         L i n u x    C P U F r e q
+
+			     U S E R   G U I D E
+
+
+		    Dominik Brodowski  <linux@xxxxxxxx>
+
+
+
+   Clock scaling allows you to change the clock speed of the CPUs on the
+    fly. This is a nice method to save battery power, because the lower
+            the clock speed, the less power the CPU consumes.
+
+
+Contents:
+---------
+1. Supported Architectures and Processors
+1.1 ARM
+1.2 x86
+1.3 sparc64
+1.4 ppc
+1.5 SuperH
+
+2. "Policy" / "Governor"?
+2.1 Policy
+2.2 Governor
+
+3. How to change the CPU cpufreq policy and/or speed
+3.1 Preferred interface: sysfs
+3.2 Deprecated interfaces
+
+
+
+1. Supported Architectures and Processors
+=========================================
+
+1.1 ARM
+-------
+
+The following ARM processors are supported by cpufreq:
+
+ARM Integrator
+ARM-SA1100
+ARM-SA1110
+
+
+1.2 x86
+-------
+
+The following processors for the x86 architecture are supported by cpufreq:
+
+AMD Elan - SC400, SC410
+AMD mobile K6-2+
+AMD mobile K6-3+
+AMD mobile Duron
+AMD mobile Athlon
+AMD Opteron
+AMD Athlon 64
+Cyrix Media GXm
+Intel mobile PIII and Intel mobile PIII-M on certain chipsets
+Intel Pentium 4, Intel Xeon
+Intel Pentium M (Centrino)
+National Semiconductors Geode GX
+Transmeta Crusoe
+VIA Cyrix 3 / C3
+various processors on some ACPI 2.0-compatible systems [*]
+
+[*] Only if "ACPI Processor Performance States" are available
+to the ACPI<->BIOS interface.
+
+
+1.3 sparc64
+-----------
+
+The following processors for the sparc64 architecture are supported by
+cpufreq:
+
+UltraSPARC-III
+
+
+1.4 ppc
+-------
+
+Several "PowerBook" and "iBook2" notebooks are supported.
+
+
+1.5 SuperH
+----------
+
+The following SuperH processors are supported by cpufreq:
+
+SH-3
+SH-4
+
+
+2. "Policy" / "Governor" ?
+==========================
+
+Some CPU frequency scaling-capable processor switch between various
+frequencies and operating voltages "on the fly" without any kernel or
+user involvement. This guarantees very fast switching to a frequency
+which is high enough to serve the user's needs, but low enough to save
+power.
+
+
+2.1 Policy
+----------
+
+On these systems, all you can do is select the lower and upper
+frequency limit as well as whether you want more aggressive
+power-saving or more instantly available processing power.
+
+
+2.2 Governor
+------------
+
+On all other cpufreq implementations, these boundaries still need to
+be set. Then, a "governor" must be selected. Such a "governor" decides
+what speed the processor shall run within the boundaries. One such
+"governor" is the "userspace" governor. This one allows the user - or
+a yet-to-implement userspace program - to decide what specific speed
+the processor shall run at.
+
+
+3. How to change the CPU cpufreq policy and/or speed
+====================================================
+
+3.1 Preferred Interface: sysfs
+------------------------------
+
+The preferred interface is located in the sysfs filesystem. If you
+mounted it at /sys, the cpufreq interface is located in a subdirectory
+"cpufreq" within the cpu-device directory
+(e.g. /sys/devices/system/cpu/cpu0/cpufreq/ for the first CPU).
+
+cpuinfo_min_freq :		this file shows the minimum operating
+				frequency the processor can run at(in kHz) 
+cpuinfo_max_freq :		this file shows the maximum operating
+				frequency the processor can run at(in kHz) 
+scaling_driver :		this file shows what cpufreq driver is
+				used to set the frequency on this CPU
+
+scaling_available_governors :	this file shows the CPUfreq governors
+				available in this kernel. You can see the
+				currently activated governor in
+
+scaling_governor,		and by "echoing" the name of another
+				governor you can change it. Please note
+				that some governors won't load - they only
+				work on some specific architectures or
+				processors.
+scaling_min_freq and 
+scaling_max_freq		show the current "policy limits" (in
+				kHz). By echoing new values into these
+				files, you can change these limits.
+
+
+If you have selected the "userspace" governor which allows you to
+set the CPU operating frequency to a specific value, you can read out
+the current frequency in
+
+scaling_setspeed.		By "echoing" a new frequency into this
+				you can change the speed of the CPU,
+				but only within the limits of
+				scaling_min_freq and scaling_max_freq.
+				
+
+3.2 Deprecated Interfaces
+-------------------------
+
+Depending on your kernel configuration, you might find the following 
+cpufreq-related files:
+/proc/cpufreq
+/proc/sys/cpu/*/speed
+/proc/sys/cpu/*/speed-min
+/proc/sys/cpu/*/speed-max
+
+These are files for deprecated interfaces to cpufreq, which offer far
+less functionality. Because of this, these interfaces aren't described
+here.
+
diff -uNr linux-2.4.23-vanilla/Documentation/cpufreq-old
linux/Documentation/cpufreq-old
--- linux-2.4.23-vanilla/Documentation/cpufreq-old	1970-01-01
07:30:00.000000000 +0730
+++ linux/Documentation/cpufreq-old	2003-08-25 22:58:44.000000000 +0800
@@ -0,0 +1,332 @@
+     CPU frequency and voltage scaling code in the Linux(TM) kernel
+
+
+		         L i n u x    C P U F r e q
+
+
+
+
+		     Dominik Brodowski <devel@xxxxxxxx>
+
+
+
+   Clock scaling allows you to change the clock speed of the CPUs on the
+    fly. This is a nice method to save battery power, because the lower
+            the clock speed, the less power the CPU consumes.
+
+
+
+Contents:
+---------
+1.  Supported architectures
+2.  User interface
+2.1   Sample script for command line interface
+3.  CPUFreq core and interfaces
+3.1   General information
+3.2   CPUFreq notifiers
+3.3   CPUFreq architecture drivers
+4.  Mailing list and Links
+
+
+
+1. Supported architectures
+==========================
+
+Some architectures detect the lowest and highest possible speed
+settings, while others rely on user information on this. For the
+latter, a boot parameter is required, for the former, you can specify
+one to set the limits between speed settings may occur. 
+The boot parameter has the following syntax:
+
+     cpufreq=minspeed-maxspeed
+
+with both minspeed and maxspeed being given in kHz. To set the lower
+limit to 59 MHz and the upper limit to 221 MHz, specify:
+
+      cpufreq=59000-221000
+
+Check the "Speed Limits Detection" information below on whether
+the driver detects the lowest and highest allowed speed setting
+automatically.
+
+
+ARM Integrator:
+    SA 1100, SA1110
+--------------------------------
+    Speed Limits Detection: On Integrators, the minimum speed is set
+    and the maximum speed has to be specified using the boot
+    parameter. On SA11x0s, the frequencies are fixed (59 - 287 MHz)
+
+
+AMD Elan:
+    SC400, SC410
+--------------------------------
+    Speed Limits Detection: Not implemented. You need to specify the
+    minimum and maximum frequency in the boot parameter (see above).
+
+
+VIA Cyrix Longhaul:
+    VIA Samuel/CyrixIII, VIA Cyrix Samuel/C3, 
+    VIA Cyrix Ezra, VIA Cyrix Ezra-T
+--------------------------------
+    Speed Limits Detection: working. No need for boot parameters.
+    NOTE: Support for certain processors is currently disabled,
+    waiting on updated docs from VIA.
+
+
+Intel SpeedStep:
+    certain mobile Intel Pentium III (Coppermine), and all mobile
+    Intel Pentium III-M (Tulatin) and mobile Intel Pentium 4 P4-Ms.
+--------------------------------
+    Speed Limits Detection: working. No need for boot parameters.
+    NOTE: 
+    1.) mobile Intel Pentium III (Coppermine):
+        The SpeedStep interface may only be used on SpeedStep
+        capable processors. Unforunately, due to lack of documentation,
+        such detection is not yet possible on mobile Intel PIII
+        (Coppermine) processors. In order to activate SpeedStep on such a
+        processor, you have to remove one line manually in
+        linux/drivers/arch/i386/speedstep.c
+
+
+P4 CPU Clock Modulation:
+    Intel Pentium 4 Xeon processors
+--------------------------------
+    Speed Limits Detection: Not implemented. You need to specify the
+    minimum and maximum frequency in the boot parameter (see above).
+
+
+
+2. User Interface
+=================
+
+CPUFreq uses a "sysctl" interface which is located in 
+	/proc/sys/cpu/0/	  on UP (uniprocessor) kernels, or 
+	/proc/sys/cpu/any/	  on SMP (symmetric multiprocessoring)
kernels.
+
+
+In this directory, you will find three files of importance for
+CPUFreq: speed-max, speed-min, and speed: 
+
+speed		    shows the current CPU frequency in kHz, 
+speed-min	    the minimal supported CPU frequency, and
+speed-max	    the maximal supported CPU frequency. 
+
+Please note that you might have to specify these limits as a boot
+parameter depending on the architecture (see above).
+
+
+To change the CPU frequency, "echo" the desired CPU frequency (in kHz)
+to speed. For example, to set the CPU speed to the lowest/highest
+allowed frequency do:
+
+root@notebook:# cat /proc/sys/cpu/0/speed-min > /proc/sys/cpu/0/speed
+root@notebook:# cat /proc/sys/cpu/0/speed-max > /proc/sys/cpu/0/speed
+
+
+2.1   Sample script for command line interface
+**********************************************
+
+
+Michael Ossmann <mike@xxxxxxxxxxx> has written a small command line
+interface for the infinitely lazy.
+
+#!/bin/bash
+#
+# /usr/local/bin/freq
+#   simple command line interface to cpufreq
+
+[ -n "$1" ] && case "$1" in
+  "min" )
+    # set frequency to minimum
+    cat /proc/sys/cpu/0/speed-min >/proc/sys/cpu/0/speed
+    ;;
+  "max" )
+    # set frequency to maximum
+    cat /proc/sys/cpu/0/speed-max >/proc/sys/cpu/0/speed
+    ;;
+  * )
+    echo "Usage: $0 [min|max]"
+    echo "  min: set frequency to minimum and display new frequency"
+    echo "  max: set frequency to maximum and display new frequency"
+    echo "  no options: display current frequency"
+    exit 1
+    ;;
+esac
+
+# display current frequency
+cat /proc/sys/cpu/0/speed
+exit 0
+
+
+
+3.  CPUFreq core and interfaces
+===============================
+
+3.1   General information
+*************************
+
+The CPUFreq core code is located in linux/kernel/cpufreq.c. This
+cpufreq code offers a standardized interface for the CPUFreq
+architecture drivers (those pieces of code that do the actual
+frequency transition), as well as to "notifiers". These are device
+drivers or other part of the kernel that need to be informed of
+frequency changes (like timing code) or even need to force certain
+speed limits (like LCD drivers on ARM architecture). Aditionally, the
+kernel "constant" loops_per_jiffy is updated on frequency changes
+here.
+
+
+3.2   CPUFreq notifiers
+***********************
+
+CPUFreq notifiers are kernel code that need to be called to either
+a) define certain minimum or maximum speed settings,
+b) be informed of frequency changes in advance of the transition, or
+c) be informed of frequency changes directly after the transition.
+
+A standard kernel notifier interface is offered for this. See
+linux/include/linux/notifier.h for details on notifiers.
+
+
+Data and value passed to CPUFreq notifiers
+------------------------------------------
+The second argument passed to any notifier is an unsigned int stating
+the phase of the transition: 
+CPUFREQ_MINMAX during the process of determing a valid new CPU
+	       frequency,
+CPUFREQ_PRECHANGE right before the transition, and 
+CPUFREQ_POSTCHANGE right after the transition.
+
+The third argument, a void *pointer, points to a struct
+cpufreq_freqs. This consists of four values: min, max, cur and new.
+
+min and max are the current speed limits. Please note: Never update
+these values directly, use cpufreq_updateminmax(struct cpufreq_freqs
+*freqs, unsigned int min, unsigned int max) instead. cur is the
+current/old speed, and new is the new speed, but might only be valid
+on CPUFREQ_PRECHANGE or CPUFREQ_POSTCHANGE.
+
+Each notifier gets called all three times on any transition:
+
+CPUFREQ_MINMAX
+Here the notifier is supposed to update the min and max values to the
+limits the protected device / kernel code needs. As stated above,
+always use cpufreq_updateminmax for this.
+
+CPUFREQ_PRECHANGE
+CPUFREQ_POSTCHANGE
+Here the notifier is supposed to update all internal (e.g. device
+driver) code which is dependend on the CPU frequency.
+
+
+3.3   CPUFreq architecture drivers
+**********************************
+
+CPUFreq architecture drivers are the pieces of kernel code that
+actually perform CPU frequency transitions. These need to be
+initialised seperately (seperate initcalls), and may be
+modularized. They interact with the CPUFreq core in the following way:
+
+
+cpufreq_register()
+------------------
+cpufreq_register registers an arch driver to the CPUFreq core. Please
+note that only one arch driver may be registered at any time, -EBUSY
+is returned when an arch driver is already registered. The argument to
+cpufreq_register, cpufreq_driver_t driver, is described later.
+
+
+cpufreq_unregister()
+--------------------
+cpufreq_unregister unregisters an arch driver, e.g. on module
+unloading. Please note that there is no check done that this is called
+from the driver which actually registered itself to the core, so
+please only call this function when you are sure the arch driver got
+registered correctly before.
+
+
+struct cpufreq_driver
+----------------
+On initialisation, the arch driver is supposed to pass the following
+entries in struct cpufreq_driver cpufreq_driver:
+
+cpufreq_verify_t validate: This is a pointer to a function with the
+following definition: 
+     unsigned int validating_function (unsigned int kHz). 
+It is called right before a transition occurs. The proposed new
+speed setting is passed as an argument in kHz; the validating code
+should verify this is a valid speed setting which is currently
+supported by the CPU. It shall return the closest valid CPU frequency
+in kHz.
+
+cpufreq_setspeed_t setspeed: This is a pointer to a function with the
+following definition: 
+     void setspeed_function (unsigned int kHz). 
+This function shall perform the transition to the new CPU frequency 
+given as argument in kHz. Note that this argument is exactly the same
+as the one returned by cpufreq_verify_t validate.
+
+
+unsigned int freq.cur: The current CPU core frequency. Note that this
+is a requirement while the next two entries are optional.
+
+
+unsigned int freq.min (optional): The minimal CPU core frequency this
+CPU supports. This value may be limited further by the
+cpufreq_verify_t validate function, and so this value should be the
+minimal core frequency allowed "theoretically" on this system in this
+configuration.
+
+
+unsigned int freq.max (optional): The maximum CPU core frequency this
+CPU supports. This value may be limited further by the
+cpufreq_verify_t validate function, and so this value should be the
+maximum core frequency allowed "theoretically" on this system in this
+configuration.
+
+
+Some Requirements to CPUFreq architecture drivers
+-------------------------------------------------
+* Only call cpufreq_register() when the ability to switch CPU
+  frequencies is _verified_ or can't be missing
+* cpufreq_unregister() may only be called if cpufreq_register() has
+  been successfully(!) called before
+* All CPUs have to be set to the same speed whenever setspeed() is
+  called
+* Be aware that there is currently no error management in the
+  setspeed() code in the CPUFreq core. So only call yourself a
+  cpufreq_driver if you are really a working cpufreq_driver!
+
+
+
+4.  Mailing list and Links
+**************************
+
+
+Mailing List
+------------
+There is a CPU frequency changing CVS commit and general list where
+you can report bugs, problems or submit patches. To post a message,
+send an email to cpufreq@xxxxxxxxxxxxxxxx, to subscribe go to
+http://www.linux.org.uk/mailman/listinfo/cpufreq. Previous post to the
+mailing list are available to subscribers at
+http://www.linux.org.uk/mailman/private/cpufreq/.
+
+
+Links
+-----
+the FTP archives:
+* ftp://ftp.linux.org.uk/pub/linux/cpufreq/
+
+how to access the CVS repository:
+* http://www.arm.linux.org.uk/cvs/
+
+the CPUFreq Mailing list:
+* http://www.linux.org.uk/mailman/listinfo/cpufreq
+
+Clock and voltage scaling for the SA-1100:
+* http://www.lart.tudelft.nl/projects/scaling
+
+CPUFreq project homepage
+* http://www.brodo.de/cpufreq/
diff -uNr linux-2.4.23-vanilla/drivers/cpufreq/cpufreq.c
linux/drivers/cpufreq/cpufreq.c
--- linux-2.4.23-vanilla/drivers/cpufreq/cpufreq.c	1970-01-01
07:30:00.000000000 +0730
+++ linux/drivers/cpufreq/cpufreq.c	2003-08-27 22:11:22.000000000 +0800
@@ -0,0 +1,720 @@
+/*
+ *  linux/kernel/cpufreq.c
+ *
+ *  Copyright (C) 2001 Russell King
+ *            (C) 2002 - 2003 Dominik Brodowski <linux@xxxxxxxx>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/notifier.h>
+#include <linux/cpufreq.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/spinlock.h>
+#include <linux/slab.h>
+
+#include <asm/semaphore.h>
+/**
+ * The "cpufreq driver" - the arch- or hardware-dependend low
+ * level driver of CPUFreq support, and its spinlock. This lock
+ * also protects the cpufreq_cpu_data array.
+ */
+static struct cpufreq_driver   	*cpufreq_driver;
+static struct cpufreq_policy	*cpufreq_cpu_data[NR_CPUS];
+static spinlock_t		cpufreq_driver_lock = SPIN_LOCK_UNLOCKED;
+
+/* internal prototype */
+static int __cpufreq_governor(struct cpufreq_policy *policy, unsigned int
event);
+
+
+/**
+ * Two notifier lists: the "policy" list is involved in the 
+ * validation process for a new CPU frequency policy; the 
+ * "transition" list for kernel code that needs to handle
+ * changes to devices when the CPU clock speed changes.
+ * The mutex locks both lists.
+ */
+static struct notifier_block    *cpufreq_policy_notifier_list;
+static struct notifier_block    *cpufreq_transition_notifier_list;
+static DECLARE_RWSEM		(cpufreq_notifier_rwsem);
+
+
+static LIST_HEAD(cpufreq_governor_list);
+static DECLARE_MUTEX		(cpufreq_governor_sem);
+
+/*
+ * backport info:
+ * we don't have a kobj we can use for ref-counting, so use a
+ * "unsigned int policy->use_count" and an "unload_sem" [idea from
+ * Pat Mochel's struct driver unload_sem] for proper reference counting.
+ */
+
+static struct cpufreq_policy * cpufreq_cpu_get(unsigned int cpu)
+{
+	struct cpufreq_policy *data;
+	unsigned long flags;
+
+	if (cpu >= NR_CPUS)
+		goto err_out;
+
+	/* get the cpufreq driver */
+	spin_lock_irqsave(&cpufreq_driver_lock, flags);
+
+	if (!cpufreq_driver)
+		goto err_out_unlock;
+
+	/* get the CPU */
+	data = cpufreq_cpu_data[cpu];
+
+	if (!data)
+		goto err_out_unlock;
+
+	if (!data->use_count)
+		goto err_out_unlock;
+
+	data->use_count += 1;
+
+	spin_unlock_irqrestore(&cpufreq_driver_lock, flags);
+
+	return data;
+
+ err_out_unlock:
+	spin_unlock_irqrestore(&cpufreq_driver_lock, flags);
+ err_out:
+	return NULL;
+}
+
+static void cpufreq_cpu_put(struct cpufreq_policy *data)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&cpufreq_driver_lock, flags);
+	data->use_count -= 1;
+	if (!data->use_count) {
+		spin_unlock_irqrestore(&cpufreq_driver_lock, flags);
+		up(&data->unload_sem);
+		return;
+	}
+	spin_unlock_irqrestore(&cpufreq_driver_lock, flags);
+}
+
+/*********************************************************************
+ *                          SYSFS INTERFACE                          *
+ *********************************************************************/
+
+/**
+ * cpufreq_parse_governor - parse a governor string
+ */
+int cpufreq_parse_governor (char *str_governor, unsigned int *policy,
+				struct cpufreq_governor **governor)
+{
+	if (!strnicmp(str_governor, "performance", CPUFREQ_NAME_LEN)) {
+		*policy = CPUFREQ_POLICY_PERFORMANCE;
+		return 0;
+	} else if (!strnicmp(str_governor, "powersave", CPUFREQ_NAME_LEN)) {
+		*policy = CPUFREQ_POLICY_POWERSAVE;
+		return 0;
+	} else 	{
+		struct cpufreq_governor *t;
+		down(&cpufreq_governor_sem);
+		if (!cpufreq_driver || !cpufreq_driver->target)
+			goto out;
+		list_for_each_entry(t, &cpufreq_governor_list,
governor_list) {
+			if
(!strnicmp(str_governor,t->name,CPUFREQ_NAME_LEN)) {
+				*governor = t;
+				*policy = CPUFREQ_POLICY_GOVERNOR;
+				up(&cpufreq_governor_sem);
+				return 0;
+			}
+		}
+	out:
+		up(&cpufreq_governor_sem);
+	}
+	return -EINVAL;
+}
+EXPORT_SYMBOL_GPL(cpufreq_parse_governor);
+
+
+/* backport info:
+ * all the sysfs stuff is missing -- of course
+ */
+
+/**
+ * cpufreq_add_dev - add a CPU device
+ *
+ * Adds the cpufreq interface for a CPU device. 
+ */
+static int cpufreq_add_dev (unsigned int cpu)
+{
+	int ret = 0;
+	struct cpufreq_policy new_policy;
+	struct cpufreq_policy *policy;
+	unsigned long flags;
+
+	policy = kmalloc(sizeof(struct cpufreq_policy), GFP_KERNEL);
+	if (!policy)
+		return -ENOMEM;
+	memset(policy, 0, sizeof(struct cpufreq_policy));
+
+	policy->cpu = cpu;
+	policy->use_count = 1;
+	init_MUTEX_LOCKED(&policy->lock);
+	init_MUTEX_LOCKED(&policy->unload_sem);
+
+	/* call driver. From then on the cpufreq must be able
+	 * to accept all calls to ->verify and ->setpolicy for this CPU
+	 */
+	ret = cpufreq_driver->init(policy);
+	if (ret)
+		goto err_out;
+
+	memcpy(&new_policy, policy, sizeof(struct cpufreq_policy));
+
+	spin_lock_irqsave(&cpufreq_driver_lock, flags);
+	cpufreq_cpu_data[cpu] = policy;
+	spin_unlock_irqrestore(&cpufreq_driver_lock, flags);
+
+	up(&policy->lock);
+	
+	/* set default policy */
+	ret = cpufreq_set_policy(&new_policy);
+	if (ret)
+		goto err_out_unregister;
+
+	return 0;
+
+
+ err_out_unregister:
+	spin_lock_irqsave(&cpufreq_driver_lock, flags);
+	cpufreq_cpu_data[cpu] = NULL;
+	spin_unlock_irqrestore(&cpufreq_driver_lock, flags);
+
+ err_out:
+	kfree(policy);
+	return ret;
+}
+
+
+/**
+ * cpufreq_remove_dev - remove a CPU device
+ *
+ * Removes the cpufreq interface for a CPU device.
+ */
+static int cpufreq_remove_dev (unsigned int cpu)
+{
+	unsigned long flags;
+	struct cpufreq_policy *data;
+
+	spin_lock_irqsave(&cpufreq_driver_lock, flags);
+	data = cpufreq_cpu_data[cpu];
+	if (!data) {
+		spin_unlock_irqrestore(&cpufreq_driver_lock, flags);
+		return -EINVAL;
+	}
+	cpufreq_cpu_data[cpu] = NULL;
+
+	data->use_count -= 1;
+	if (!data->use_count) {
+		spin_unlock_irqrestore(&cpufreq_driver_lock, flags);
+		up(&data->unload_sem);
+	} else {
+		spin_unlock_irqrestore(&cpufreq_driver_lock, flags);
+		/* this will sleep until data->use_count gets to zero */
+		down(&data->unload_sem);
+		up(&data->unload_sem);
+	}
+
+	if (cpufreq_driver->target)
+		__cpufreq_governor(data, CPUFREQ_GOV_STOP);
+
+	if (cpufreq_driver->exit)
+		cpufreq_driver->exit(data);
+
+	kfree(data);
+
+	return 0;
+}
+
+
+/*********************************************************************
+ *                     NOTIFIER LISTS INTERFACE                      *
+ *********************************************************************/
+
+/**
+ *	cpufreq_register_notifier - register a driver with cpufreq
+ *	@nb: notifier function to register
+ *      @list: CPUFREQ_TRANSITION_NOTIFIER or CPUFREQ_POLICY_NOTIFIER
+ *
+ *	Add a driver to one of two lists: either a list of drivers that 
+ *      are notified about clock rate changes (once before and once after
+ *      the transition), or a list of drivers that are notified about
+ *      changes in cpufreq policy.
+ *
+ *	This function may sleep, and has the same return conditions as
+ *	notifier_chain_register.
+ */
+int cpufreq_register_notifier(struct notifier_block *nb, unsigned int list)
+{
+	int ret;
+
+	down_write(&cpufreq_notifier_rwsem);
+	switch (list) {
+	case CPUFREQ_TRANSITION_NOTIFIER:
+		ret =
notifier_chain_register(&cpufreq_transition_notifier_list, nb);
+		break;
+	case CPUFREQ_POLICY_NOTIFIER:
+		ret = notifier_chain_register(&cpufreq_policy_notifier_list,
nb);
+		break;
+	default:
+		ret = -EINVAL;
+	}
+	up_write(&cpufreq_notifier_rwsem);
+
+	return ret;
+}
+EXPORT_SYMBOL(cpufreq_register_notifier);
+
+
+/**
+ *	cpufreq_unregister_notifier - unregister a driver with cpufreq
+ *	@nb: notifier block to be unregistered
+ *      @list: CPUFREQ_TRANSITION_NOTIFIER or CPUFREQ_POLICY_NOTIFIER
+ *
+ *	Remove a driver from the CPU frequency notifier list.
+ *
+ *	This function may sleep, and has the same return conditions as
+ *	notifier_chain_unregister.
+ */
+int cpufreq_unregister_notifier(struct notifier_block *nb, unsigned int
list)
+{
+	int ret;
+
+	down_write(&cpufreq_notifier_rwsem);
+	switch (list) {
+	case CPUFREQ_TRANSITION_NOTIFIER:
+		ret =
notifier_chain_unregister(&cpufreq_transition_notifier_list, nb);
+		break;
+	case CPUFREQ_POLICY_NOTIFIER:
+		ret =
notifier_chain_unregister(&cpufreq_policy_notifier_list, nb);
+		break;
+	default:
+		ret = -EINVAL;
+	}
+	up_write(&cpufreq_notifier_rwsem);
+
+	return ret;
+}
+EXPORT_SYMBOL(cpufreq_unregister_notifier);
+
+
+/*********************************************************************
+ *                              GOVERNORS                            *
+ *********************************************************************/
+
+
+int __cpufreq_driver_target(struct cpufreq_policy *policy,
+			    unsigned int target_freq,
+			    unsigned int relation)
+{
+	return cpufreq_driver->target(policy, target_freq, relation);
+}
+EXPORT_SYMBOL_GPL(__cpufreq_driver_target);
+
+
+int cpufreq_driver_target(struct cpufreq_policy *policy,
+			  unsigned int target_freq,
+			  unsigned int relation)
+{
+	unsigned int ret;
+
+	policy = cpufreq_cpu_get(policy->cpu);
+	if (!policy)
+		return -EINVAL;
+
+	down(&policy->lock);
+
+	ret = __cpufreq_driver_target(policy, target_freq, relation);
+
+	up(&policy->lock);
+
+	cpufreq_cpu_put(policy);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(cpufreq_driver_target);
+
+
+static int __cpufreq_governor(struct cpufreq_policy *policy, unsigned int
event)
+{
+	int ret = 0;
+
+	switch (policy->policy) {
+	case CPUFREQ_POLICY_POWERSAVE: 
+		if ((event == CPUFREQ_GOV_LIMITS) || (event ==
CPUFREQ_GOV_START)) {
+			ret = __cpufreq_driver_target(policy, policy->min,
CPUFREQ_RELATION_L);
+		}
+		break;
+	case CPUFREQ_POLICY_PERFORMANCE:
+		if ((event == CPUFREQ_GOV_LIMITS) || (event ==
CPUFREQ_GOV_START)) {
+			ret = __cpufreq_driver_target(policy, policy->max,
CPUFREQ_RELATION_H);
+		}
+		break;
+	case CPUFREQ_POLICY_GOVERNOR:
+		ret = policy->governor->governor(policy, event);
+		break;
+	default:
+		ret = -EINVAL;
+	}
+
+	return ret;
+}
+
+
+int cpufreq_governor(unsigned int cpu, unsigned int event)
+{
+	int ret = 0;
+	struct cpufreq_policy *policy = cpufreq_cpu_get(cpu);
+
+	if (!policy)
+		return -EINVAL;
+
+	down(&policy->lock);
+	ret = __cpufreq_governor(policy, event);
+	up(&policy->lock);
+
+	cpufreq_cpu_put(policy);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(cpufreq_governor);
+
+
+int cpufreq_register_governor(struct cpufreq_governor *governor)
+{
+	struct cpufreq_governor *t;
+
+	if (!governor)
+		return -EINVAL;
+
+	if (!strnicmp(governor->name,"powersave",CPUFREQ_NAME_LEN))
+		return -EBUSY;
+	if (!strnicmp(governor->name,"performance",CPUFREQ_NAME_LEN))
+		return -EBUSY;
+
+	down(&cpufreq_governor_sem);
+	
+	list_for_each_entry(t, &cpufreq_governor_list, governor_list) {
+		if (!strnicmp(governor->name,t->name,CPUFREQ_NAME_LEN)) {
+			up(&cpufreq_governor_sem);
+			return -EBUSY;
+		}
+	}
+	list_add(&governor->governor_list, &cpufreq_governor_list);
+
+ 	up(&cpufreq_governor_sem);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(cpufreq_register_governor);
+
+
+void cpufreq_unregister_governor(struct cpufreq_governor *governor)
+{
+	/* backport info: 
+	 * As the module usage count isn't assured in 2.4., check for
removal
+	 * of running cpufreq governor
+	 */
+	unsigned int i;
+
+	if (!governor)
+		return;
+
+	down(&cpufreq_governor_sem);
+
+	for (i=0; i<NR_CPUS; i++) {
+		struct cpufreq_policy *policy = cpufreq_cpu_get(i);
+		if (!policy)
+			goto done;
+		down(&policy->lock);
+
+		if (policy->policy != CPUFREQ_POLICY_GOVERNOR)
+			goto unlock_done;
+		if (policy->governor != governor)
+			goto unlock_done;
+
+		/* stop old one, start performance [always present] */
+		__cpufreq_governor(policy, CPUFREQ_GOV_STOP);
+		policy->policy = CPUFREQ_POLICY_PERFORMANCE;
+		__cpufreq_governor(policy, CPUFREQ_GOV_START);
+
+	unlock_done:
+		up(&policy->lock);
+	done:
+		cpufreq_cpu_put(policy);
+	}
+	list_del(&governor->governor_list);
+	up(&cpufreq_governor_sem);
+	return;
+}
+EXPORT_SYMBOL_GPL(cpufreq_unregister_governor);
+
+
+
+/*********************************************************************
+ *                          POLICY INTERFACE                         *
+ *********************************************************************/
+
+/**
+ * cpufreq_get_policy - get the current cpufreq_policy
+ * @policy: struct cpufreq_policy into which the current cpufreq_policy is
written
+ *
+ * Reads the current cpufreq policy.
+ */
+int cpufreq_get_policy(struct cpufreq_policy *policy, unsigned int cpu)
+{
+	struct cpufreq_policy *cpu_policy;
+	if (!policy)
+		return -EINVAL;
+
+	cpu_policy = cpufreq_cpu_get(cpu);
+	if (!cpu_policy)
+		return -EINVAL;
+
+	down(&cpu_policy->lock);
+	memcpy(policy, cpu_policy, sizeof(struct cpufreq_policy));
+	up(&cpu_policy->lock);
+
+	cpufreq_cpu_put(cpu_policy);
+
+	return 0;
+}
+EXPORT_SYMBOL(cpufreq_get_policy);
+
+
+/**
+ *	cpufreq_set_policy - set a new CPUFreq policy
+ *	@policy: policy to be set.
+ *
+ *	Sets a new CPU frequency and voltage scaling policy.
+ */
+int cpufreq_set_policy(struct cpufreq_policy *policy)
+{
+	int ret = 0;
+	struct cpufreq_policy *data;
+
+	if (!policy)
+		return -EINVAL;
+
+	data = cpufreq_cpu_get(policy->cpu);
+	if (!data)
+		return -EINVAL;
+
+	/* lock this CPU */
+	down(&data->lock);
+
+	memcpy(&policy->cpuinfo, 
+	       &data->cpuinfo, 
+	       sizeof(struct cpufreq_cpuinfo));
+
+	/* verify the cpu speed can be set within this limit */
+	ret = cpufreq_driver->verify(policy);
+	if (ret)
+		goto error_out;
+
+	down_read(&cpufreq_notifier_rwsem);
+
+	/* adjust if necessary - all reasons */
+	notifier_call_chain(&cpufreq_policy_notifier_list, CPUFREQ_ADJUST,
+			    policy);
+
+	/* adjust if necessary - hardware incompatibility*/
+	notifier_call_chain(&cpufreq_policy_notifier_list,
CPUFREQ_INCOMPATIBLE,
+			    policy);
+
+	/* verify the cpu speed can be set within this limit,
+	   which might be different to the first one */
+	ret = cpufreq_driver->verify(policy);
+	if (ret) {
+		up_read(&cpufreq_notifier_rwsem);
+		goto error_out;
+	}
+
+	/* notification of the new policy */
+	notifier_call_chain(&cpufreq_policy_notifier_list, CPUFREQ_NOTIFY,
+			    policy);
+
+	up_read(&cpufreq_notifier_rwsem);
+
+	data->min    = policy->min;
+	data->max    = policy->max;
+
+	if (cpufreq_driver->setpolicy) {
+		data->policy = policy->policy;
+		ret = cpufreq_driver->setpolicy(policy);
+	} else {
+		if ((policy->policy != data->policy) || 
+		    ((policy->policy == CPUFREQ_POLICY_GOVERNOR) &&
(policy->governor != data->governor))) {
+			/* save old, working values */
+			unsigned int old_pol = data->policy;
+			struct cpufreq_governor *old_gov = data->governor;
+
+			/* end old governor */
+			__cpufreq_governor(data, CPUFREQ_GOV_STOP);
+
+			/* start new governor */
+			data->policy = policy->policy;
+			data->governor = policy->governor;
+			if (__cpufreq_governor(data, CPUFREQ_GOV_START)) {
+				/* new governor failed, so re-start old one
*/
+				data->policy = old_pol;
+				data->governor = old_gov;
+				__cpufreq_governor(data, CPUFREQ_GOV_START);
+			}
+			/* might be a policy change, too, so fall through */
+		}
+		__cpufreq_governor(data, CPUFREQ_GOV_LIMITS);
+	}
+
+ error_out:
+	up(&data->lock);
+	cpufreq_cpu_put(data);
+
+	return ret;
+}
+EXPORT_SYMBOL(cpufreq_set_policy);
+
+
+
+/*********************************************************************
+ *            EXTERNALLY AFFECTING FREQUENCY CHANGES                 *
+ *********************************************************************/
+
+/**
+ * adjust_jiffies - adjust the system "loops_per_jiffy"
+ *
+ * This function alters the system "loops_per_jiffy" for the clock
+ * speed change. Note that loops_per_jiffy cannot be updated on SMP
+ * systems as each CPU might be scaled differently. So, use the arch 
+ * per-CPU loops_per_jiffy value wherever possible.
+ */
+#ifndef CONFIG_SMP
+static unsigned long l_p_j_ref = 0;
+static unsigned int  l_p_j_ref_freq = 0;
+
+static inline void adjust_jiffies(unsigned long val, struct cpufreq_freqs
*ci)
+{
+	if (!l_p_j_ref_freq) {
+		l_p_j_ref = loops_per_jiffy;
+		l_p_j_ref_freq = ci->old;
+	}
+	if ((val == CPUFREQ_PRECHANGE  && ci->old < ci->new) ||
+	    (val == CPUFREQ_POSTCHANGE && ci->old > ci->new))
+		loops_per_jiffy = cpufreq_scale(l_p_j_ref, l_p_j_ref_freq,
ci->new);
+}
+#else
+#define adjust_jiffies(x...) do {} while (0)
+#endif
+
+
+/**
+ * cpufreq_notify_transition - call notifier chain and adjust_jiffies on
frequency transition
+ *
+ * This function calls the transition notifiers and the "adjust_jiffies"
function. It is called
+ * twice on all CPU frequency changes that have external effects. 
+ */
+void cpufreq_notify_transition(struct cpufreq_freqs *freqs, unsigned int
state)
+{
+	down_read(&cpufreq_notifier_rwsem);
+	switch (state) {
+	case CPUFREQ_PRECHANGE:
+		notifier_call_chain(&cpufreq_transition_notifier_list,
CPUFREQ_PRECHANGE, freqs);
+		adjust_jiffies(CPUFREQ_PRECHANGE, freqs);
+		break;
+	case CPUFREQ_POSTCHANGE:
+		adjust_jiffies(CPUFREQ_POSTCHANGE, freqs);
+		notifier_call_chain(&cpufreq_transition_notifier_list,
CPUFREQ_POSTCHANGE, freqs);
+		cpufreq_cpu_data[freqs->cpu]->cur = freqs->new;
+		break;
+	}
+	up_read(&cpufreq_notifier_rwsem);
+}
+EXPORT_SYMBOL_GPL(cpufreq_notify_transition);
+
+
+
+/*********************************************************************
+ *               REGISTER / UNREGISTER CPUFREQ DRIVER                *
+ *********************************************************************/
+
+/**
+ * cpufreq_register_driver - register a CPU Frequency driver
+ * @driver_data: A struct cpufreq_driver containing the values#
+ * submitted by the CPU Frequency driver.
+ *
+ *   Registers a CPU Frequency driver to this core code. This code 
+ * returns zero on success, -EBUSY when another driver got here first
+ * (and isn't unregistered in the meantime). 
+ *
+ */
+int cpufreq_register_driver(struct cpufreq_driver *driver_data)
+{
+	unsigned long flags;
+	unsigned int i;
+
+	if (!driver_data || !driver_data->verify || !driver_data->init ||
+	    ((!driver_data->setpolicy) && (!driver_data->target)))
+		return -EINVAL;
+
+	spin_lock_irqsave(&cpufreq_driver_lock, flags);
+	if (cpufreq_driver) {
+		spin_unlock_irqrestore(&cpufreq_driver_lock, flags);
+		return -EBUSY;
+	}
+	cpufreq_driver = driver_data;
+	spin_unlock_irqrestore(&cpufreq_driver_lock, flags);
+
+	for (i=0; i<NR_CPUS; i++) {
+		if (cpu_online(i)) 
+			cpufreq_add_dev(i);
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(cpufreq_register_driver);
+
+
+/**
+ * cpufreq_unregister_driver - unregister the current CPUFreq driver
+ *
+ *    Unregister the current CPUFreq driver. Only call this if you have 
+ * the right to do so, i.e. if you have succeeded in initialising before!
+ * Returns zero if successful, and -EINVAL if the cpufreq_driver is
+ * currently not initialised.
+ */
+int cpufreq_unregister_driver(struct cpufreq_driver *driver)
+{
+	unsigned long flags;
+	unsigned int i;
+
+	if (!cpufreq_driver || (driver != cpufreq_driver))
+		return -EINVAL;
+
+	for (i=0; i<NR_CPUS; i++) {
+		if (cpu_online(i)) 
+			cpufreq_remove_dev(i);
+	}
+
+	spin_lock_irqsave(&cpufreq_driver_lock, flags);
+	cpufreq_driver = NULL;
+	spin_unlock_irqrestore(&cpufreq_driver_lock, flags);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(cpufreq_unregister_driver);
diff -uNr linux-2.4.23-vanilla/drivers/cpufreq/freq_table.c
linux/drivers/cpufreq/freq_table.c
--- linux-2.4.23-vanilla/drivers/cpufreq/freq_table.c	1970-01-01
07:30:00.000000000 +0730
+++ linux/drivers/cpufreq/freq_table.c	2003-08-25 22:58:48.000000000 +0800
@@ -0,0 +1,153 @@
+/*
+ * linux/drivers/cpufreq/freq_table.c
+ *
+ * Copyright (C) 2002 - 2003 Dominik Brodowski
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/cpufreq.h>
+#include <linux/smp.h>
+
+/*********************************************************************
+ *                     FREQUENCY TABLE HELPERS                       *
+ *********************************************************************/
+
+int cpufreq_frequency_table_cpuinfo(struct cpufreq_policy *policy,
+				    struct cpufreq_frequency_table *table)
+{
+	unsigned int min_freq = ~0;
+	unsigned int max_freq = 0;
+	unsigned int i = 0;
+
+	for (i=0; (table[i].frequency != CPUFREQ_TABLE_END); i++) {
+		unsigned int freq = table[i].frequency;
+		if (freq == CPUFREQ_ENTRY_INVALID)
+			continue;
+		if (freq < min_freq)
+			min_freq = freq;
+		if (freq > max_freq)
+			max_freq = freq;
+	}
+
+	policy->min = policy->cpuinfo.min_freq = min_freq;
+	policy->max = policy->cpuinfo.max_freq = max_freq;
+
+	if (policy->min == ~0)
+		return -EINVAL;
+	else
+		return 0;
+}
+EXPORT_SYMBOL_GPL(cpufreq_frequency_table_cpuinfo);
+
+
+int cpufreq_frequency_table_verify(struct cpufreq_policy *policy,
+				   struct cpufreq_frequency_table *table)
+{
+	unsigned int next_larger = ~0;
+	unsigned int i = 0;
+	unsigned int count = 0;
+
+	if (!cpu_online(policy->cpu))
+		return -EINVAL;
+
+	cpufreq_verify_within_limits(policy, 
+				     policy->cpuinfo.min_freq, 
+				     policy->cpuinfo.max_freq);
+
+	for (i=0; (table[i].frequency != CPUFREQ_TABLE_END); i++) {
+		unsigned int freq = table[i].frequency;
+		if (freq == CPUFREQ_ENTRY_INVALID)
+			continue;
+		if ((freq >= policy->min) && (freq <= policy->max))
+			count++;
+		else if ((next_larger > freq) && (freq > policy->max))
+			next_larger = freq;
+	}
+
+	if (!count)
+		policy->max = next_larger;
+
+	cpufreq_verify_within_limits(policy, 
+				     policy->cpuinfo.min_freq, 
+				     policy->cpuinfo.max_freq);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(cpufreq_frequency_table_verify);
+
+
+int cpufreq_frequency_table_target(struct cpufreq_policy *policy,
+				   struct cpufreq_frequency_table *table,
+				   unsigned int target_freq,
+				   unsigned int relation,
+				   unsigned int *index)
+{
+	struct cpufreq_frequency_table optimal = { .index = ~0, };
+	struct cpufreq_frequency_table suboptimal = { .index = ~0, };
+	unsigned int i;
+
+	switch (relation) {
+	case CPUFREQ_RELATION_H:
+		optimal.frequency = 0;
+		suboptimal.frequency = ~0;
+		break;
+	case CPUFREQ_RELATION_L:
+		optimal.frequency = ~0;
+		suboptimal.frequency = 0;
+		break;
+	}
+
+	if (!cpu_online(policy->cpu))
+		return -EINVAL;
+
+	for (i=0; (table[i].frequency != CPUFREQ_TABLE_END); i++) {
+		unsigned int freq = table[i].frequency;
+		if (freq == CPUFREQ_ENTRY_INVALID)
+			continue;
+		if ((freq < policy->min) || (freq > policy->max))
+			continue;
+		switch(relation) {
+		case CPUFREQ_RELATION_H:
+			if (freq <= target_freq) {
+				if (freq >= optimal.frequency) {
+					optimal.frequency = freq;
+					optimal.index = i;
+				}
+			} else {
+				if (freq <= suboptimal.frequency) {
+					suboptimal.frequency = freq;
+					suboptimal.index = i;
+				}
+			}
+			break;
+		case CPUFREQ_RELATION_L:
+			if (freq >= target_freq) {
+				if (freq <= optimal.frequency) {
+					optimal.frequency = freq;
+					optimal.index = i;
+				}
+			} else {
+				if (freq >= suboptimal.frequency) {
+					suboptimal.frequency = freq;
+					suboptimal.index = i;
+				}
+			}
+			break;
+		}
+	}
+	if (optimal.index > i) {
+		if (suboptimal.index > i)
+			return -EINVAL;
+		*index = suboptimal.index;
+	} else
+		*index = optimal.index;
+	
+	return 0;
+}
+EXPORT_SYMBOL_GPL(cpufreq_frequency_table_target);
+
+MODULE_AUTHOR ("Dominik Brodowski <linux@xxxxxxxx>");
+MODULE_DESCRIPTION ("CPUfreq frequency table helpers");
+MODULE_LICENSE ("GPL");
diff -uNr linux-2.4.23-vanilla/drivers/cpufreq/Kconfig
linux/drivers/cpufreq/Kconfig
--- linux-2.4.23-vanilla/drivers/cpufreq/Kconfig	1970-01-01
07:30:00.000000000 +0730
+++ linux/drivers/cpufreq/Kconfig	2003-02-22 18:23:48.000000000 +0800
@@ -0,0 +1,38 @@
+config CPU_FREQ_PROC_INTF
+	tristate "/proc/cpufreq interface (deprecated)"
+	depends on CPU_FREQ && PROC_FS
+	help
+	  This enables the /proc/cpufreq interface for controlling
+	  CPUFreq. Please note that it is recommended to use the sysfs
+	  interface instead (which is built automatically). 
+	  
+	  For details, take a look at linux/Documentation/cpufreq. 
+	  
+	  If in doubt, say N.
+
+config CPU_FREQ_GOV_USERSPACE
+       tristate "'userspace' governor for userspace frequency scaling"
+       depends on CPU_FREQ
+       help
+	  Enable this cpufreq governor when you either want to set the
+	  CPU frequency manually or when an userspace programm shall
+          be able to set the CPU dynamically, like on LART 
+	  ( http://www.lart.tudelft.nl/ )
+
+	  For details, take a look at linux/Documentation/cpufreq. 
+
+	  If in doubt, say Y.
+
+config CPU_FREQ_24_API
+	bool "/proc/sys/cpu/ interface (2.4. / OLD)"
+	depends on CPU_FREQ && SYSCTL && CPU_FREQ_GOV_USERSPACE
+	help
+	  This enables the /proc/sys/cpu/ sysctl interface for controlling
+	  the CPUFreq,"userspace" governor. This is the same interface
+	  as known from the.4.-kernel patches for CPUFreq, and offers
+	  the same functionality as long as "userspace" is the
+	  selected governor for the specified CPU.
+	
+	  For details, take a look at linux/Documentation/cpufreq. 
+
+	  If in doubt, say N.
diff -uNr linux-2.4.23-vanilla/drivers/cpufreq/Makefile
linux/drivers/cpufreq/Makefile
--- linux-2.4.23-vanilla/drivers/cpufreq/Makefile	1970-01-01
07:30:00.000000000 +0730
+++ linux/drivers/cpufreq/Makefile	2003-08-27 21:45:36.000000000 +0800
@@ -0,0 +1,15 @@
+O_TARGET        := built-in.o
+
+# CPUfreq core
+obj-$(CONFIG_CPU_FREQ)			+= cpufreq.o
+
+# CPUfreq governors 
+obj-$(CONFIG_CPU_FREQ_GOV_USERSPACE)	+= userspace.o
+
+# CPUfreq cross-arch helpers
+obj-$(CONFIG_CPU_FREQ_TABLE)		+= freq_table.o
+obj-$(CONFIG_CPU_FREQ_PROC_INTF)	+= proc_intf.o
+
+export-objs	:= userspace.o freq_table.o cpufreq.o
+
+include $(TOPDIR)/Rules.make
diff -uNr linux-2.4.23-vanilla/drivers/cpufreq/proc_intf.c
linux/drivers/cpufreq/proc_intf.c
--- linux-2.4.23-vanilla/drivers/cpufreq/proc_intf.c	1970-01-01
07:30:00.000000000 +0730
+++ linux/drivers/cpufreq/proc_intf.c	2003-08-25 22:58:48.000000000 +0800
@@ -0,0 +1,246 @@
+/*
+ * linux/drivers/cpufreq/proc_intf.c
+ *
+ * Copyright (C) 2002 - 2003 Dominik Brodowski
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/cpufreq.h>
+#include <linux/ctype.h>
+#include <linux/proc_fs.h>
+#include <asm/uaccess.h>
+
+
+#define CPUFREQ_ALL_CPUS		((NR_CPUS))
+
+/**
+ * cpufreq_parse_policy - parse a policy string
+ * @input_string: the string to parse.
+ * @policy: the policy written inside input_string
+ *
+ * This function parses a "policy string" - something the user echo'es into
+ * /proc/cpufreq or gives as boot parameter - into a struct cpufreq_policy.
+ * If there are invalid/missing entries, they are replaced with current
+ * cpufreq policy.
+ */
+static int cpufreq_parse_policy(char input_string[42], struct
cpufreq_policy *policy)
+{
+	unsigned int            min = 0;
+	unsigned int            max = 0;
+	unsigned int            cpu = 0;
+	char			str_governor[16];
+	struct cpufreq_policy   current_policy;
+	unsigned int            result = -EFAULT;
+
+	if (!policy)
+		return -EINVAL;
+
+	policy->min = 0;
+	policy->max = 0;
+	policy->policy = 0;
+	policy->cpu = CPUFREQ_ALL_CPUS;
+
+	if (sscanf(input_string, "%d:%d:%d:%15s", &cpu, &min, &max,
str_governor) == 4) 
+	{
+		policy->min = min;
+		policy->max = max;
+		policy->cpu = cpu;
+		result = 0;
+		goto scan_policy;
+	}
+	if (sscanf(input_string, "%d%%%d%%%d%%%15s", &cpu, &min, &max,
str_governor) == 4)
+	{
+		if (!cpufreq_get_policy(&current_policy, cpu)) {
+			policy->min = (min *
current_policy.cpuinfo.max_freq) / 100;
+			policy->max = (max *
current_policy.cpuinfo.max_freq) / 100;
+			policy->cpu = cpu;
+			result = 0;
+			goto scan_policy;
+		}
+	}
+
+	if (sscanf(input_string, "%d:%d:%15s", &min, &max, str_governor) ==
3) 
+	{
+		policy->min = min;
+		policy->max = max;
+		result = 0;
+		goto scan_policy;
+	}
+
+	if (sscanf(input_string, "%d%%%d%%%15s", &min, &max, str_governor)
== 3)
+	{
+		if (!cpufreq_get_policy(&current_policy, cpu)) {
+			policy->min = (min *
current_policy.cpuinfo.max_freq) / 100;
+			policy->max = (max *
current_policy.cpuinfo.max_freq) / 100;
+			result = 0;
+			goto scan_policy;
+		}
+	}
+
+	return -EINVAL;
+
+scan_policy:
+	result = cpufreq_parse_governor(str_governor, &policy->policy,
&policy->governor);
+
+	return result;
+}
+
+/**
+ * cpufreq_proc_read - read /proc/cpufreq
+ *
+ * This function prints out the current cpufreq policy.
+ */
+static int cpufreq_proc_read (
+	char			*page,
+	char			**start,
+	off_t			off,
+	int 			count,
+	int 			*eof,
+	void			*data)
+{
+	char			*p = page;
+	int			len = 0;
+	struct cpufreq_policy   policy;
+	unsigned int            min_pctg = 0;
+	unsigned int            max_pctg = 0;
+	unsigned int            i = 0;
+
+	if (off != 0)
+		goto end;
+
+	p += sprintf(p, "          minimum CPU frequency  -  maximum CPU
frequency  -  policy\n");
+	for (i=0;i<NR_CPUS;i++) {
+		if (!cpu_online(i))
+			continue;
+
+		if (cpufreq_get_policy(&policy, i))
+			continue;
+
+		if (!policy.cpuinfo.max_freq)
+			continue;
+
+		min_pctg = (policy.min * 100) / policy.cpuinfo.max_freq;
+		max_pctg = (policy.max * 100) / policy.cpuinfo.max_freq;
+
+		p += sprintf(p, "CPU%3d    %9d kHz (%3d %%)  -  %9d kHz (%3d
%%)  -  ",
+			     i , policy.min, min_pctg, policy.max,
max_pctg);
+		switch (policy.policy) {
+		case CPUFREQ_POLICY_POWERSAVE:
+			p += sprintf(p, "powersave\n");
+			break;
+		case CPUFREQ_POLICY_PERFORMANCE:
+			p += sprintf(p, "performance\n");
+			break;
+		case CPUFREQ_POLICY_GOVERNOR:
+			p += snprintf(p, CPUFREQ_NAME_LEN, "%s\n",
policy.governor->name);
+			break;
+		default:
+			p += sprintf(p, "INVALID\n");
+			break;
+		}
+	}
+end:
+	len = (p - page);
+	if (len <= off+count) 
+		*eof = 1;
+	*start = page + off;
+	len -= off;
+	if (len>count) 
+		len = count;
+	if (len<0) 
+		len = 0;
+
+	return len;
+}
+
+
+/**
+ * cpufreq_proc_write - handles writing into /proc/cpufreq
+ *
+ * This function calls the parsing script and then sets the policy
+ * accordingly.
+ */
+static int cpufreq_proc_write (
+        struct file		*file,
+        const char		*buffer,
+        unsigned long		count,
+        void			*data)
+{
+	int                     result = 0;
+	char			proc_string[42] = {'\0'};
+	struct cpufreq_policy   policy;
+	unsigned int            i = 0;
+
+
+	if ((count > sizeof(proc_string) - 1))
+		return -EINVAL;
+	
+	if (copy_from_user(proc_string, buffer, count))
+		return -EFAULT;
+	
+	proc_string[count] = '\0';
+
+	result = cpufreq_parse_policy(proc_string, &policy);
+	if (result)
+		return -EFAULT;
+
+	if (policy.cpu == CPUFREQ_ALL_CPUS)
+	{
+		for (i=0; i<NR_CPUS; i++) 
+		{
+			policy.cpu = i;
+			if (cpu_online(i))
+				cpufreq_set_policy(&policy);
+		}
+	} 
+	else
+		cpufreq_set_policy(&policy);
+
+	return count;
+}
+
+
+/**
+ * cpufreq_proc_init - add "cpufreq" to the /proc root directory
+ *
+ * This function adds "cpufreq" to the /proc root directory.
+ */
+static int __init cpufreq_proc_init (void)
+{
+	struct proc_dir_entry *entry = NULL;
+
+	/* are these acceptable values? */
+	entry = create_proc_entry("cpufreq", S_IFREG|S_IRUGO|S_IWUSR, 
+				  &proc_root);
+
+	if (!entry) {
+		printk(KERN_ERR "unable to create /proc/cpufreq entry\n");
+		return -EIO;
+	} else {
+		entry->read_proc = cpufreq_proc_read;
+		entry->write_proc = cpufreq_proc_write;
+	}
+
+	return 0;
+}
+
+
+/**
+ * cpufreq_proc_exit - removes "cpufreq" from the /proc root directory.
+ *
+ * This function removes "cpufreq" from the /proc root directory.
+ */
+static void __exit cpufreq_proc_exit (void)
+{
+	remove_proc_entry("cpufreq", &proc_root);
+	return;
+}
+
+MODULE_AUTHOR ("Dominik Brodowski <linux@xxxxxxxx>");
+MODULE_DESCRIPTION ("CPUfreq /proc/cpufreq interface");
+MODULE_LICENSE ("GPL");
+
+module_init(cpufreq_proc_init);
+module_exit(cpufreq_proc_exit);
diff -uNr linux-2.4.23-vanilla/drivers/cpufreq/userspace.c
linux/drivers/cpufreq/userspace.c
--- linux-2.4.23-vanilla/drivers/cpufreq/userspace.c	1970-01-01
07:30:00.000000000 +0730
+++ linux/drivers/cpufreq/userspace.c	2003-08-28 21:41:58.000000000 +0800
@@ -0,0 +1,559 @@
+/*
+ *  drivers/cpufreq/userspace.c
+ *
+ *  Copyright (C)  2001 Russell King
+ *            (C)  2002 - 2003 Dominik Brodowski <linux@xxxxxxxx>
+ *
+ * $Id: userspace.c,v 1.1.1.2 2003/08/28 13:41:57 ducrot Exp $
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/smp.h>
+#include <linux/init.h>
+#include <linux/spinlock.h>
+#include <linux/cpufreq.h>
+#include <linux/interrupt.h>
+#include <linux/ctype.h>
+#include <linux/sysctl.h>
+#include <linux/types.h>
+#include <linux/fs.h>
+
+#include <asm/uaccess.h>
+
+#define CTL_CPU_VARS_SPEED_MAX(cpunr) { \
+                .ctl_name	= CPU_NR_FREQ_MAX, \
+                .data		= &cpu_max_freq[cpunr], \
+                .procname	= "speed-max", \
+                .maxlen		= sizeof(cpu_max_freq[cpunr]),\
+                .mode		= 0444, \
+                .proc_handler	= proc_dointvec, }
+
+#define CTL_CPU_VARS_SPEED_MIN(cpunr) { \
+                .ctl_name	= CPU_NR_FREQ_MIN, \
+                .data		= &cpu_min_freq[cpunr], \
+                .procname	= "speed-min", \
+                .maxlen		= sizeof(cpu_min_freq[cpunr]),\
+                .mode		= 0444, \
+                .proc_handler	= proc_dointvec, }
+
+#define CTL_CPU_VARS_SPEED(cpunr) { \
+                .ctl_name	= CPU_NR_FREQ, \
+                .procname	= "speed", \
+                .mode		= 0644, \
+                .proc_handler	= cpufreq_procctl, \
+                .strategy	= cpufreq_sysctl, \
+                .extra1		= (void*) (cpunr), }
+
+#define CTL_TABLE_CPU_VARS(cpunr) static ctl_table ctl_cpu_vars_##cpunr[] =
{\
+                CTL_CPU_VARS_SPEED_MAX(cpunr), \
+                CTL_CPU_VARS_SPEED_MIN(cpunr), \
+                CTL_CPU_VARS_SPEED(cpunr),  \
+                { .ctl_name = 0, }, }
+
+/* the ctl_table entry for each CPU */
+#define CPU_ENUM(s) { \
+                .ctl_name	= (CPU_NR + s), \
+                .procname	= #s, \
+                .mode		= 0555, \
+                .child		= ctl_cpu_vars_##s }
+
+/**
+ * A few values needed by the userspace governor
+ */
+static unsigned int	cpu_max_freq[NR_CPUS];
+static unsigned int	cpu_min_freq[NR_CPUS];
+static unsigned int	cpu_cur_freq[NR_CPUS];
+static unsigned int	cpu_is_managed[NR_CPUS];
+static struct cpufreq_policy current_policy[NR_CPUS];
+
+static DECLARE_MUTEX	(userspace_sem); 
+
+
+/* keep track of frequency transitions */
+static int 
+userspace_cpufreq_notifier(struct notifier_block *nb, unsigned long val,
+                       void *data)
+{
+        struct cpufreq_freqs *freq = data;
+
+	cpu_cur_freq[freq->cpu] = freq->new;
+
+        return 0;
+}
+
+static struct notifier_block userspace_cpufreq_notifier_block = {
+        .notifier_call  = userspace_cpufreq_notifier
+};
+
+
+/** 
+ * cpufreq_set - set the CPU frequency
+ * @freq: target frequency in kHz
+ * @cpu: CPU for which the frequency is to be set
+ *
+ * Sets the CPU frequency to freq.
+ */
+int cpufreq_set(unsigned int freq, unsigned int cpu)
+{
+	int ret = -EINVAL;
+
+	down(&userspace_sem);
+	if (!cpu_is_managed[cpu])
+		goto err;
+
+	if (freq < cpu_min_freq[cpu])
+		freq = cpu_min_freq[cpu];
+	if (freq > cpu_max_freq[cpu])
+		freq = cpu_max_freq[cpu];
+
+	ret = cpufreq_driver_target(&current_policy[cpu], freq, 
+	      CPUFREQ_RELATION_L);
+
+ err:
+	up(&userspace_sem);
+	return ret;
+}
+EXPORT_SYMBOL_GPL(cpufreq_set);
+
+
+/** 
+ * cpufreq_setmax - set the CPU to the maximum frequency
+ * @cpu - affected cpu;
+ *
+ * Sets the CPU frequency to the maximum frequency supported by
+ * this CPU.
+ */
+int cpufreq_setmax(unsigned int cpu)
+{
+	if (!cpu_is_managed[cpu] || !cpu_online(cpu))
+		return -EINVAL;
+	return cpufreq_set(cpu_max_freq[cpu], cpu);
+}
+EXPORT_SYMBOL_GPL(cpufreq_setmax);
+
+
+/** 
+ * cpufreq_get - get the current CPU frequency (in kHz)
+ * @cpu: CPU number
+ *
+ * Get the CPU current (static) CPU frequency
+ */
+unsigned int cpufreq_get(unsigned int cpu)
+{
+	return cpu_cur_freq[cpu];
+}
+EXPORT_SYMBOL(cpufreq_get);
+
+
+#ifdef CONFIG_CPU_FREQ_24_API
+
+
+/*********************** cpufreq_sysctl interface ********************/
+static int
+cpufreq_procctl(ctl_table *ctl, int write, struct file *filp,
+		void *buffer, size_t *lenp)
+{
+	char buf[16], *p;
+	int cpu = (int) ctl->extra1;
+	int len, left = *lenp;
+
+	if (!left || (filp->f_pos && !write) || !cpu_online(cpu)) {
+		*lenp = 0;
+		return 0;
+	}
+
+	if (write) {
+		unsigned int freq;
+
+		len = left;
+		if (left > sizeof(buf))
+			left = sizeof(buf);
+		if (copy_from_user(buf, buffer, left))
+			return -EFAULT;
+		buf[sizeof(buf) - 1] = '\0';
+
+		freq = simple_strtoul(buf, &p, 0);
+		cpufreq_set(freq, cpu);
+	} else {
+		len = sprintf(buf, "%d\n", cpufreq_get(cpu));
+		if (len > left)
+			len = left;
+		if (copy_to_user(buffer, buf, len))
+			return -EFAULT;
+	}
+
+	*lenp = len;
+	filp->f_pos += len;
+	return 0;
+}
+
+static int
+cpufreq_sysctl(ctl_table *table, int *name, int nlen,
+	       void *oldval, size_t *oldlenp,
+	       void *newval, size_t newlen, void **context)
+{
+	int cpu = (int) table->extra1;
+
+	if (!cpu_online(cpu))
+		return -EINVAL;
+
+	if (oldval && oldlenp) {
+		size_t oldlen;
+
+		if (get_user(oldlen, oldlenp))
+			return -EFAULT;
+
+		if (oldlen != sizeof(unsigned int))
+			return -EINVAL;
+
+		if (put_user(cpufreq_get(cpu), (unsigned int *)oldval) ||
+		    put_user(sizeof(unsigned int), oldlenp))
+			return -EFAULT;
+	}
+	if (newval && newlen) {
+		unsigned int freq;
+
+		if (newlen != sizeof(unsigned int))
+			return -EINVAL;
+
+		if (get_user(freq, (unsigned int *)newval))
+			return -EFAULT;
+
+		cpufreq_set(freq, cpu);
+	}
+	return 1;
+}
+
+/* ctl_table ctl_cpu_vars_{0,1,...,(NR_CPUS-1)} */
+/* due to NR_CPUS tweaking, a lot of if/endifs are required, sorry */
+        CTL_TABLE_CPU_VARS(0);
+#if NR_CPUS > 1
+	CTL_TABLE_CPU_VARS(1);
+#endif
+#if NR_CPUS > 2
+	CTL_TABLE_CPU_VARS(2);
+#endif
+#if NR_CPUS > 3
+	CTL_TABLE_CPU_VARS(3);
+#endif
+#if NR_CPUS > 4
+	CTL_TABLE_CPU_VARS(4);
+#endif
+#if NR_CPUS > 5
+	CTL_TABLE_CPU_VARS(5);
+#endif
+#if NR_CPUS > 6
+	CTL_TABLE_CPU_VARS(6);
+#endif
+#if NR_CPUS > 7
+	CTL_TABLE_CPU_VARS(7);
+#endif
+#if NR_CPUS > 8
+	CTL_TABLE_CPU_VARS(8);
+#endif
+#if NR_CPUS > 9
+	CTL_TABLE_CPU_VARS(9);
+#endif
+#if NR_CPUS > 10
+	CTL_TABLE_CPU_VARS(10);
+#endif
+#if NR_CPUS > 11
+	CTL_TABLE_CPU_VARS(11);
+#endif
+#if NR_CPUS > 12
+	CTL_TABLE_CPU_VARS(12);
+#endif
+#if NR_CPUS > 13
+	CTL_TABLE_CPU_VARS(13);
+#endif
+#if NR_CPUS > 14
+	CTL_TABLE_CPU_VARS(14);
+#endif
+#if NR_CPUS > 15
+	CTL_TABLE_CPU_VARS(15);
+#endif
+#if NR_CPUS > 16
+	CTL_TABLE_CPU_VARS(16);
+#endif
+#if NR_CPUS > 17
+	CTL_TABLE_CPU_VARS(17);
+#endif
+#if NR_CPUS > 18
+	CTL_TABLE_CPU_VARS(18);
+#endif
+#if NR_CPUS > 19
+	CTL_TABLE_CPU_VARS(19);
+#endif
+#if NR_CPUS > 20
+	CTL_TABLE_CPU_VARS(20);
+#endif
+#if NR_CPUS > 21
+	CTL_TABLE_CPU_VARS(21);
+#endif
+#if NR_CPUS > 22
+	CTL_TABLE_CPU_VARS(22);
+#endif
+#if NR_CPUS > 23
+	CTL_TABLE_CPU_VARS(23);
+#endif
+#if NR_CPUS > 24
+	CTL_TABLE_CPU_VARS(24);
+#endif
+#if NR_CPUS > 25
+	CTL_TABLE_CPU_VARS(25);
+#endif
+#if NR_CPUS > 26
+	CTL_TABLE_CPU_VARS(26);
+#endif
+#if NR_CPUS > 27
+	CTL_TABLE_CPU_VARS(27);
+#endif
+#if NR_CPUS > 28
+	CTL_TABLE_CPU_VARS(28);
+#endif
+#if NR_CPUS > 29
+	CTL_TABLE_CPU_VARS(29);
+#endif
+#if NR_CPUS > 30
+	CTL_TABLE_CPU_VARS(30);
+#endif
+#if NR_CPUS > 31
+	CTL_TABLE_CPU_VARS(31);
+#endif
+#if NR_CPUS > 32
+#error please extend CPU enumeration
+#endif
+
+/* due to NR_CPUS tweaking, a lot of if/endifs are required, sorry */
+static ctl_table ctl_cpu_table[NR_CPUS + 1] = {
+	CPU_ENUM(0),
+#if NR_CPUS > 1
+	CPU_ENUM(1),
+#endif
+#if NR_CPUS > 2
+	CPU_ENUM(2),
+#endif
+#if NR_CPUS > 3
+	CPU_ENUM(3),
+#endif
+#if NR_CPUS > 4
+	CPU_ENUM(4),
+#endif
+#if NR_CPUS > 5
+	CPU_ENUM(5),
+#endif
+#if NR_CPUS > 6
+	CPU_ENUM(6),
+#endif
+#if NR_CPUS > 7
+	CPU_ENUM(7),
+#endif
+#if NR_CPUS > 8
+	CPU_ENUM(8),
+#endif
+#if NR_CPUS > 9
+	CPU_ENUM(9),
+#endif
+#if NR_CPUS > 10
+	CPU_ENUM(10),
+#endif
+#if NR_CPUS > 11
+	CPU_ENUM(11),
+#endif
+#if NR_CPUS > 12
+	CPU_ENUM(12),
+#endif
+#if NR_CPUS > 13
+	CPU_ENUM(13),
+#endif
+#if NR_CPUS > 14
+	CPU_ENUM(14),
+#endif
+#if NR_CPUS > 15
+	CPU_ENUM(15),
+#endif
+#if NR_CPUS > 16
+	CPU_ENUM(16),
+#endif
+#if NR_CPUS > 17
+	CPU_ENUM(17),
+#endif
+#if NR_CPUS > 18
+	CPU_ENUM(18),
+#endif
+#if NR_CPUS > 19
+	CPU_ENUM(19),
+#endif
+#if NR_CPUS > 20
+	CPU_ENUM(20),
+#endif
+#if NR_CPUS > 21
+	CPU_ENUM(21),
+#endif
+#if NR_CPUS > 22
+	CPU_ENUM(22),
+#endif
+#if NR_CPUS > 23
+	CPU_ENUM(23),
+#endif
+#if NR_CPUS > 24
+	CPU_ENUM(24),
+#endif
+#if NR_CPUS > 25
+	CPU_ENUM(25),
+#endif
+#if NR_CPUS > 26
+	CPU_ENUM(26),
+#endif
+#if NR_CPUS > 27
+	CPU_ENUM(27),
+#endif
+#if NR_CPUS > 28
+	CPU_ENUM(28),
+#endif
+#if NR_CPUS > 29
+	CPU_ENUM(29),
+#endif
+#if NR_CPUS > 30
+	CPU_ENUM(30),
+#endif
+#if NR_CPUS > 31
+	CPU_ENUM(31),
+#endif
+#if NR_CPUS > 32
+#error please extend CPU enumeration
+#endif
+	{
+		.ctl_name	= 0,
+	}
+};
+
+static ctl_table ctl_cpu[2] = {
+	{
+		.ctl_name	= CTL_CPU,
+		.procname	= "cpu",
+		.mode		= 0555,
+		.child		= ctl_cpu_table,
+	},
+	{
+		.ctl_name	= 0,
+	}
+};
+
+struct ctl_table_header *cpufreq_sysctl_table;
+
+static inline void cpufreq_sysctl_init(void)
+{
+	cpufreq_sysctl_table = register_sysctl_table(ctl_cpu, 0);
+}
+
+static inline void cpufreq_sysctl_exit(void)
+{
+	unregister_sysctl_table(cpufreq_sysctl_table);
+}
+
+#else
+#define cpufreq_sysctl_init() do {} while(0)
+#define cpufreq_sysctl_exit() do {} while(0)
+#endif /* CONFIG_CPU_FREQ_24API */
+
+
+static int cpufreq_governor_userspace(struct cpufreq_policy *policy,
+				   unsigned int event)
+{
+	unsigned int cpu = policy->cpu;
+	switch (event) {
+	case CPUFREQ_GOV_START:
+		if ((!cpu_online(cpu)) ||
+		    !policy->cur)
+			return -EINVAL;
+		down(&userspace_sem);
+		cpu_is_managed[cpu] = 1;		
+		cpu_min_freq[cpu] = policy->min;
+		cpu_max_freq[cpu] = policy->max;
+		cpu_cur_freq[cpu] = policy->cur;
+		memcpy (&current_policy[cpu], policy, sizeof(struct
cpufreq_policy));
+		up(&userspace_sem);
+		break;
+	case CPUFREQ_GOV_STOP:
+		down(&userspace_sem);
+		cpu_is_managed[cpu] = 0;
+		cpu_min_freq[cpu] = 0;
+		cpu_max_freq[cpu] = 0;
+		up(&userspace_sem);
+		break;
+	case CPUFREQ_GOV_LIMITS:
+		down(&userspace_sem);
+		cpu_min_freq[cpu] = policy->min;
+		cpu_max_freq[cpu] = policy->max;
+		if (policy->max < cpu_cur_freq[cpu])
+			__cpufreq_driver_target(&current_policy[cpu],
policy->max, 
+			      CPUFREQ_RELATION_H);
+		else if (policy->min > cpu_cur_freq[cpu])
+			__cpufreq_driver_target(&current_policy[cpu],
policy->min, 
+			      CPUFREQ_RELATION_L);
+		memcpy (&current_policy[cpu], policy, sizeof(struct
cpufreq_policy));
+		up(&userspace_sem);
+		break;
+	}
+	return 0;
+}
+
+/* on ARM SA1100 we need to rely on the values of cpufreq_get() - because 
+ * of this, cpu_cur_freq[] needs to be set early.
+ */
+#if defined(CONFIG_ARM) && defined(CONFIG_ARCH_SA1100)
+extern unsigned int sa11x0_getspeed(void);
+
+static void cpufreq_sa11x0_compat(void)
+{
+	cpu_cur_freq[0] = sa11x0_getspeed();
+}
+#else
+#define cpufreq_sa11x0_compat() do {} while(0)
+#endif
+
+
+static struct cpufreq_governor cpufreq_gov_userspace = {
+	.name		= "userspace",
+	.governor	= cpufreq_governor_userspace,
+};
+EXPORT_SYMBOL(cpufreq_gov_userspace);
+
+static int already_init = 0;
+
+int cpufreq_gov_userspace_init(void)
+{
+	if (!already_init) {
+		down(&userspace_sem);
+		cpufreq_sa11x0_compat();
+		cpufreq_sysctl_init();
+		cpufreq_register_notifier(&userspace_cpufreq_notifier_block,
CPUFREQ_TRANSITION_NOTIFIER);
+		already_init = 1;
+		up(&userspace_sem);
+	}
+	return cpufreq_register_governor(&cpufreq_gov_userspace);
+}
+EXPORT_SYMBOL(cpufreq_gov_userspace_init);
+
+
+static void __exit cpufreq_gov_userspace_exit(void)
+{
+	cpufreq_unregister_governor(&cpufreq_gov_userspace);
+        cpufreq_unregister_notifier(&userspace_cpufreq_notifier_block,
CPUFREQ_TRANSITION_NOTIFIER);
+	cpufreq_sysctl_exit();
+}
+
+
+MODULE_AUTHOR ("Dominik Brodowski <linux@xxxxxxxx>, Russell King
<rmk@xxxxxxxxxxxxxxxx>");
+MODULE_DESCRIPTION ("CPUfreq policy governor 'userspace'");
+MODULE_LICENSE ("GPL");
+
+module_init(cpufreq_gov_userspace_init);
+module_exit(cpufreq_gov_userspace_exit);
diff -uNr linux-2.4.23-vanilla/drivers/Makefile linux/drivers/Makefile
--- linux-2.4.23-vanilla/drivers/Makefile	2003-11-29
02:26:19.000000000 +0800
+++ linux/drivers/Makefile	2003-12-01 21:07:59.000000000 +0800
@@ -8,12 +8,13 @@
 
 mod-subdirs :=	dio hil mtd sbus video macintosh usb input telephony ide \
 		message/i2o message/fusion scsi md ieee1394 pnp isdn atm \
-		fc4 net/hamradio i2c acpi bluetooth usb/gadget
+		fc4 net/hamradio i2c acpi bluetooth usb/gadget cpufreq
 
 subdir-y :=	parport char block net sound misc media cdrom hotplug
 subdir-m :=	$(subdir-y)
 
 
+subdir-$(CONFIG_CPU_FREQ)  += cpufreq
 subdir-$(CONFIG_DIO)		+= dio
 subdir-$(CONFIG_PCI)		+= pci
 subdir-$(CONFIG_GSC)		+= gsc
diff -uNr linux-2.4.23-vanilla/include/asm-i386/ist.h
linux/include/asm-i386/ist.h
--- linux-2.4.23-vanilla/include/asm-i386/ist.h	1970-01-01
07:30:00.000000000 +0730
+++ linux/include/asm-i386/ist.h	2003-12-01 20:38:29.000000000 +0800
@@ -0,0 +1,32 @@
+#ifndef _ASM_IST_H
+#define _ASM_IST_H
+
+/*
+ * Include file for the interface to IST BIOS
+ * Copyright 2002 Andy Grover <andrew.grover@xxxxxxxxx>
+ *
+ * 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, or (at your option) any
+ * later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ */
+
+
+#ifdef __KERNEL__
+
+struct ist_info {
+	unsigned long	signature;
+	unsigned long	command;
+	unsigned long	event;
+	unsigned long	perf_level;
+};
+
+extern struct ist_info ist_info;
+
+#endif	/* __KERNEL__ */
+#endif	/* _ASM_IST_H */
diff -uNr linux-2.4.23-vanilla/include/asm-i386/msr.h
linux/include/asm-i386/msr.h
--- linux-2.4.23-vanilla/include/asm-i386/msr.h	2003-11-29
02:26:21.000000000 +0800
+++ linux/include/asm-i386/msr.h	2003-12-01 20:38:29.000000000 +0800
@@ -17,6 +17,21 @@
 			  : /* no outputs */ \
 			  : "c" (msr), "a" (val1), "d" (val2))
 
+#define rdmsrl(msr,val) do { \
+	unsigned long l__,h__; \
+	rdmsr (msr, l__, h__);  \
+	val = l__;  \
+	val |= ((u64)h__<<32);  \
+} while(0)
+
+static inline void wrmsrl (unsigned long msr, unsigned long long val)
+{
+	unsigned long lo, hi;
+	lo = (unsigned long) val;
+	hi = val >> 32;
+	wrmsr (msr, lo, hi);
+}
+
 #define rdtsc(low,high) \
      __asm__ __volatile__("rdtsc" : "=a" (low), "=d" (high))
 
@@ -92,7 +107,7 @@
 #define MSR_K7_HWCR			0xC0010015
 #define MSR_K7_CLK_CTL			0xC001001b
 #define MSR_K7_FID_VID_CTL		0xC0010041
-#define MSR_K7_VID_STATUS		0xC0010042
+#define MSR_K7_FID_VID_STATUS		0xC0010042
 
 /* Centaur-Hauls/IDT defined MSRs. */
 #define MSR_IDT_FCR1			0x107
diff -uNr linux-2.4.23-vanilla/include/asm-i386/smp.h
linux/include/asm-i386/smp.h
--- linux-2.4.23-vanilla/include/asm-i386/smp.h	2002-11-29
07:53:15.000000000 +0800
+++ linux/include/asm-i386/smp.h	2003-12-01 23:37:54.000000000 +0800
@@ -95,6 +95,8 @@
 	return GET_APIC_LOGICAL_ID(*(unsigned long *)(APIC_BASE+APIC_LDR));
 }
 
+#define cpu_online(cpu) (cpu_online_map & (1<<(cpu)))
+
 #endif /* !__ASSEMBLY__ */
 
 #define NO_PROC_ID		0xFF		/* No processor magic marker
*/
diff -uNr linux-2.4.23-vanilla/include/linux/cpufreq.h
linux/include/linux/cpufreq.h
--- linux-2.4.23-vanilla/include/linux/cpufreq.h	1970-01-01
07:30:00.000000000 +0730
+++ linux/include/linux/cpufreq.h	2003-12-01 23:42:35.000000000 +0800
@@ -0,0 +1,299 @@
+/*
+ *  linux/include/linux/cpufreq.h
+ *
+ *  Copyright (C) 2001 Russell King
+ *            (C) 2002 - 2003 Dominik Brodowski <linux@xxxxxxxx>
+ *            
+ *
+ * $Id: cpufreq.h,v 1.1.1.3 2003/08/28 13:41:58 ducrot Exp $
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#ifndef _LINUX_CPUFREQ_H
+#define _LINUX_CPUFREQ_H
+
+#include <linux/config.h>
+#include <linux/notifier.h>
+#include <linux/sched.h>
+#include <linux/threads.h>
+#include <asm/semaphore.h>
+
+#define CPUFREQ_NAME_LEN 16
+
+
+/*********************************************************************
+ *                     CPUFREQ NOTIFIER INTERFACE                    *
+ *********************************************************************/
+
+int cpufreq_register_notifier(struct notifier_block *nb, unsigned int
list);
+int cpufreq_unregister_notifier(struct notifier_block *nb, unsigned int
list);
+
+#define CPUFREQ_TRANSITION_NOTIFIER	(0)
+#define CPUFREQ_POLICY_NOTIFIER		(1)
+
+
+/********************** cpufreq policy notifiers *********************/
+
+#define CPUFREQ_POLICY_POWERSAVE	(1)
+#define CPUFREQ_POLICY_PERFORMANCE	(2)
+#define CPUFREQ_POLICY_GOVERNOR		(3)
+
+/* Frequency values here are CPU kHz so that hardware which doesn't run 
+ * with some frequencies can complain without having to guess what per 
+ * cent / per mille means. 
+ * Maximum transition latency is in microseconds - if it's unknown,
+ * CPUFREQ_ETERNAL shall be used.
+ */
+
+struct cpufreq_governor;
+
+#define CPUFREQ_ETERNAL			(-1)
+struct cpufreq_cpuinfo {
+	unsigned int		max_freq;
+	unsigned int		min_freq;
+	unsigned int		transition_latency; /* in 10^(-9) s */
+};
+
+struct cpufreq_policy {
+	unsigned int		cpu;    /* cpu nr */
+	struct cpufreq_cpuinfo	cpuinfo;/* see above */
+
+	unsigned int		min;    /* in kHz */
+	unsigned int		max;    /* in kHz */
+	unsigned int		cur;    /* in kHz, only needed if cpufreq
+					 * governors are used */
+        unsigned int		policy; /* see above */
+	struct cpufreq_governor	*governor; /* see below */
+
+ 	struct semaphore	lock;   /* CPU ->setpolicy or ->target may
+					   only be called once a time */
+
+	/* see backport info in kernel/cpufreq.c */
+	unsigned int		use_count;
+ 	struct semaphore	unload_sem;
+};
+
+#define CPUFREQ_ADJUST		(0)
+#define CPUFREQ_INCOMPATIBLE	(1)
+#define CPUFREQ_NOTIFY		(2)
+
+
+/******************** cpufreq transition notifiers *******************/
+
+#define CPUFREQ_PRECHANGE	(0)
+#define CPUFREQ_POSTCHANGE	(1)
+
+struct cpufreq_freqs {
+	unsigned int cpu;	/* cpu nr */
+	unsigned int old;
+	unsigned int new;
+};
+
+
+/**
+ * cpufreq_scale - "old * mult / div" calculation for large values
(32-bit-arch safe)
+ * @old:   old value
+ * @div:   divisor
+ * @mult:  multiplier
+ *
+ * Needed for loops_per_jiffy and similar calculations.  We do it 
+ * this way to avoid math overflow on 32-bit machines.  This will
+ * become architecture dependent once high-resolution-timer is
+ * merged (or any other thing that introduces sc_math.h).
+ *
+ *    new = old * mult / div
+ */
+static inline unsigned long cpufreq_scale(unsigned long old, u_int div,
u_int mult)
+{
+	unsigned long val, carry;
+
+	mult /= 100;
+	div  /= 100;
+        val   = (old / div) * mult;
+        carry = old % div;
+	carry = carry * mult / div;
+
+	return carry + val;
+};
+
+/*********************************************************************
+ *                          CPUFREQ GOVERNORS                        *
+ *********************************************************************/
+
+#define CPUFREQ_GOV_START  1
+#define CPUFREQ_GOV_STOP   2
+#define CPUFREQ_GOV_LIMITS 3
+
+struct cpufreq_governor {
+	char	name[CPUFREQ_NAME_LEN];
+	int 	(*governor)	(struct cpufreq_policy *policy,
+				 unsigned int event);
+	struct list_head	governor_list;
+};
+
+/* pass a target to the cpufreq driver 
+ */
+extern int cpufreq_driver_target(struct cpufreq_policy *policy,
+				 unsigned int target_freq,
+				 unsigned int relation);
+extern int __cpufreq_driver_target(struct cpufreq_policy *policy,
+				   unsigned int target_freq,
+				   unsigned int relation);
+
+
+/* pass an event to the cpufreq governor */
+int cpufreq_governor(unsigned int cpu, unsigned int event);
+
+int cpufreq_register_governor(struct cpufreq_governor *governor);
+void cpufreq_unregister_governor(struct cpufreq_governor *governor);
+
+/*********************************************************************
+ *                      CPUFREQ DRIVER INTERFACE                     *
+ *********************************************************************/
+
+#define CPUFREQ_RELATION_L 0  /* lowest frequency at or above target */
+#define CPUFREQ_RELATION_H 1  /* highest frequency below or at target */
+
+struct freq_attr;
+
+struct cpufreq_driver {
+	char			name[CPUFREQ_NAME_LEN];
+
+	/* needed by all drivers */
+	int	(*init)		(struct cpufreq_policy *policy);
+	int	(*verify)	(struct cpufreq_policy *policy);
+
+	/* define one out of two */
+	int	(*setpolicy)	(struct cpufreq_policy *policy);
+	int	(*target)	(struct cpufreq_policy *policy,
+				 unsigned int target_freq,
+				 unsigned int relation);
+
+	/* optional */
+	int	(*exit)		(struct cpufreq_policy *policy);
+};
+
+int cpufreq_register_driver(struct cpufreq_driver *driver_data);
+int cpufreq_unregister_driver(struct cpufreq_driver *driver_data);
+
+
+void cpufreq_notify_transition(struct cpufreq_freqs *freqs, unsigned int
state);
+
+
+static inline void cpufreq_verify_within_limits(struct cpufreq_policy
*policy, unsigned int min, unsigned int max) 
+{
+	if (policy->min < min)
+		policy->min = min;
+	if (policy->max < min)
+		policy->max = min;
+	if (policy->min > max)
+		policy->min = max;
+	if (policy->max > max)
+		policy->max = max;
+	if (policy->min > policy->max)
+		policy->min = policy->max;
+	return;
+}
+
+
+/*********************************************************************
+ *                        CPUFREQ 2.6. INTERFACE                     *
+ *********************************************************************/
+int cpufreq_set_policy(struct cpufreq_policy *policy);
+int cpufreq_get_policy(struct cpufreq_policy *policy, unsigned int cpu);
+
+/* the proc_intf.c needs this */
+int cpufreq_parse_governor (char *str_governor, unsigned int *policy,
struct cpufreq_governor **governor);
+
+#if defined(CONFIG_CPU_FREQ_GOV_USERSPACE) ||
defined(CONFIG_CPU_FREQ_GOV_USERSPACE_MODULE)
+/*********************************************************************
+ *                      CPUFREQ USERSPACE GOVERNOR                   *
+ *********************************************************************/
+extern struct cpufreq_governor cpufreq_gov_userspace;
+int cpufreq_gov_userspace_init(void);
+
+int cpufreq_setmax(unsigned int cpu);
+int cpufreq_set(unsigned int kHz, unsigned int cpu);
+unsigned int cpufreq_get(unsigned int cpu);
+
+#ifdef CONFIG_CPU_FREQ_24_API
+
+/* /proc/sys/cpu */
+enum {
+	CPU_NR   = 1,           /* compatibilty reasons */
+	CPU_NR_0 = 1,
+	CPU_NR_1 = 2,
+	CPU_NR_2 = 3,
+	CPU_NR_3 = 4,
+	CPU_NR_4 = 5,
+	CPU_NR_5 = 6,
+	CPU_NR_6 = 7,
+	CPU_NR_7 = 8,
+	CPU_NR_8 = 9,
+	CPU_NR_9 = 10,
+	CPU_NR_10 = 11,
+	CPU_NR_11 = 12,
+	CPU_NR_12 = 13,
+	CPU_NR_13 = 14,
+	CPU_NR_14 = 15,
+	CPU_NR_15 = 16,
+	CPU_NR_16 = 17,
+	CPU_NR_17 = 18,
+	CPU_NR_18 = 19,
+	CPU_NR_19 = 20,
+	CPU_NR_20 = 21,
+	CPU_NR_21 = 22,
+	CPU_NR_22 = 23,
+	CPU_NR_23 = 24,
+	CPU_NR_24 = 25,
+	CPU_NR_25 = 26,
+	CPU_NR_26 = 27,
+	CPU_NR_27 = 28,
+	CPU_NR_28 = 29,
+	CPU_NR_29 = 30,
+	CPU_NR_30 = 31,
+	CPU_NR_31 = 32,
+};
+
+/* /proc/sys/cpu/{0,1,...,(NR_CPUS-1)} */
+enum {
+	CPU_NR_FREQ_MAX = 1,
+	CPU_NR_FREQ_MIN = 2,
+	CPU_NR_FREQ = 3,
+};
+
+#endif /* CONFIG_CPU_FREQ_24_API */
+
+#endif /* CONFIG_CPU_FREQ_GOV_USERSPACE */
+
+
+/*********************************************************************
+ *                     FREQUENCY TABLE HELPERS                       *
+ *********************************************************************/
+
+#define CPUFREQ_ENTRY_INVALID ~0
+#define CPUFREQ_TABLE_END     ~1
+
+struct cpufreq_frequency_table {
+	unsigned int	index;     /* any */
+	unsigned int	frequency; /* kHz - doesn't need to be in ascending
+				    * order */
+};
+
+#if defined(CONFIG_CPU_FREQ_TABLE) || defined(CONFIG_CPU_FREQ_TABLE_MODULE)
+int cpufreq_frequency_table_cpuinfo(struct cpufreq_policy *policy,
+				    struct cpufreq_frequency_table *table);
+
+int cpufreq_frequency_table_verify(struct cpufreq_policy *policy,
+				   struct cpufreq_frequency_table *table);
+
+int cpufreq_frequency_table_target(struct cpufreq_policy *policy,
+				   struct cpufreq_frequency_table *table,
+				   unsigned int target_freq,
+				   unsigned int relation,
+				   unsigned int *index);
+
+#endif /* CONFIG_CPU_FREQ_TABLE */
+#endif /* _LINUX_CPUFREQ_H */
diff -uNr linux-2.4.23-vanilla/include/linux/smp.h linux/include/linux/smp.h
--- linux-2.4.23-vanilla/include/linux/smp.h	2001-11-23
03:46:19.000000000 +0800
+++ linux/include/linux/smp.h	2003-12-01 23:37:54.000000000 +0800
@@ -86,6 +86,7 @@
 #define cpu_number_map(cpu)			0
 #define smp_call_function(func,info,retry,wait)	({ 0; })
 #define cpu_online_map				1
+#define cpu_online(cpu)				({ BUG_ON((cpu) !=
0); 1; })
 
 #endif
 #endif
diff -uNr linux-2.4.23-vanilla/Makefile linux/Makefile
--- linux-2.4.23-vanilla/Makefile	2003-11-29 02:26:21.000000000 +0800
+++ linux/Makefile	2003-12-01 23:32:29.000000000 +0800
@@ -1,7 +1,7 @@
 VERSION = 2
 PATCHLEVEL = 4
 SUBLEVEL = 23
-EXTRAVERSION =
+EXTRAVERSION = -dec2
 
 KERNELRELEASE=$(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION)
 
@@ -195,6 +195,7 @@
 DRIVERS-$(CONFIG_HOTPLUG_PCI) += drivers/hotplug/vmlinux-obj.o
 DRIVERS-$(CONFIG_ISDN_BOOL) += drivers/isdn/vmlinux-obj.o
 DRIVERS-$(CONFIG_CRYPTO) += crypto/crypto.o
+DRIVERS-$(CONFIG_CPU_FREQ) += drivers/cpufreq/built-in.o
 
 DRIVERS := $(DRIVERS-y)
 

==================END patch-2.4.23-mh5==========================


-- 
Shrike-list mailing list
Shrike-list@xxxxxxxxxx
https://www.redhat.com/mailman/listinfo/shrike-list

[Index of Archives]     [Fedora Users]     [Centos Users]     [Kernel Development]     [Red Hat Install]     [Red Hat Watch]     [Red Hat Development]     [Red Hat Phoebe Beta]     [Yosemite Forum]     [Fedora Discussion]     [Gimp]     [Stuff]     [Yosemite News]

  Powered by Linux