diff -urN linux-2.4.23/Documentation/00-INDEX linux-2.4.23-cpufreq-20031214/Documentation/00-INDEX
--- linux-2.4.23/Documentation/00-INDEX	Sat Dec  1 18:27:13 2001
+++ linux-2.4.23-cpufreq-20031214/Documentation/00-INDEX	Sun Dec 14 12:27:56 2003
@@ -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 -urN linux-2.4.23/Documentation/Configure.help linux-2.4.23-cpufreq-20031214/Documentation/Configure.help
--- linux-2.4.23/Documentation/Configure.help	Sat Dec  6 08:14:41 2003
+++ linux-2.4.23-cpufreq-20031214/Documentation/Configure.help	Sun Dec 14 12:27:56 2003
@@ -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
@@ -28320,6 +28310,154 @@
 IPMI Watchdog Timer
 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
diff -urN linux-2.4.23/Documentation/cpu-freq/core.txt linux-2.4.23-cpufreq-20031214/Documentation/cpu-freq/core.txt
--- linux-2.4.23/Documentation/cpu-freq/core.txt	Thu Jan  1 01:00:00 1970
+++ linux-2.4.23-cpufreq-20031214/Documentation/cpu-freq/core.txt	Mon Aug 25 16:58:44 2003
@@ -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@brodo.de>
+		     David Kimdon <dwhedon@debian.org>
+
+
+
+   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 -urN linux-2.4.23/Documentation/cpu-freq/cpu-drivers.txt linux-2.4.23-cpufreq-20031214/Documentation/cpu-freq/cpu-drivers.txt
--- linux-2.4.23/Documentation/cpu-freq/cpu-drivers.txt	Thu Jan  1 01:00:00 1970
+++ linux-2.4.23-cpufreq-20031214/Documentation/cpu-freq/cpu-drivers.txt	Mon Aug 25 16:58:45 2003
@@ -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@brodo.de>
+
+
+
+   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 -urN linux-2.4.23/Documentation/cpu-freq/governors.txt linux-2.4.23-cpufreq-20031214/Documentation/cpu-freq/governors.txt
--- linux-2.4.23/Documentation/cpu-freq/governors.txt	Thu Jan  1 01:00:00 1970
+++ linux-2.4.23-cpufreq-20031214/Documentation/cpu-freq/governors.txt	Mon Aug 25 16:58:45 2003
@@ -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@brodo.de>
+
+
+
+   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 -urN linux-2.4.23/Documentation/cpu-freq/index.txt linux-2.4.23-cpufreq-20031214/Documentation/cpu-freq/index.txt
--- linux-2.4.23/Documentation/cpu-freq/index.txt	Thu Jan  1 01:00:00 1970
+++ linux-2.4.23-cpufreq-20031214/Documentation/cpu-freq/index.txt	Mon Aug 25 16:58:45 2003
@@ -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@brodo.de>
+
+
+
+   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@www.linux.org.uk, 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 -urN linux-2.4.23/Documentation/cpu-freq/user-guide.txt linux-2.4.23-cpufreq-20031214/Documentation/cpu-freq/user-guide.txt
--- linux-2.4.23/Documentation/cpu-freq/user-guide.txt	Thu Jan  1 01:00:00 1970
+++ linux-2.4.23-cpufreq-20031214/Documentation/cpu-freq/user-guide.txt	Tue Sep 30 13:25:51 2003
@@ -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@brodo.de>
+
+
+
+   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 -urN linux-2.4.23/Documentation/cpufreq-old linux-2.4.23-cpufreq-20031214/Documentation/cpufreq-old
--- linux-2.4.23/Documentation/cpufreq-old	Thu Jan  1 01:00:00 1970
+++ linux-2.4.23-cpufreq-20031214/Documentation/cpufreq-old	Mon Aug 25 16:58:44 2003
@@ -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@brodo.de>
+
+
+
+   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@ossmann.com> 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@www.linux.org.uk, 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 -urN linux-2.4.23/Makefile linux-2.4.23-cpufreq-20031214/Makefile
--- linux-2.4.23/Makefile	Sat Dec  6 08:14:41 2003
+++ linux-2.4.23-cpufreq-20031214/Makefile	Sun Dec 14 12:27:56 2003
@@ -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)
 
diff -urN linux-2.4.23/arch/i386/boot/setup.S linux-2.4.23-cpufreq-20031214/arch/i386/boot/setup.S
--- linux-2.4.23/arch/i386/boot/setup.S	Sat Dec  6 08:14:41 2003
+++ linux-2.4.23-cpufreq-20031214/arch/i386/boot/setup.S	Sun Dec 14 12:27:56 2003
@@ -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 -urN linux-2.4.23/arch/i386/config.in linux-2.4.23-cpufreq-20031214/arch/i386/config.in
--- linux-2.4.23/arch/i386/config.in	Sat Dec  6 08:14:41 2003
+++ linux-2.4.23-cpufreq-20031214/arch/i386/config.in	Sun Dec 14 12:27:56 2003
@@ -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 -urN linux-2.4.23/arch/i386/kernel/Makefile linux-2.4.23-cpufreq-20031214/arch/i386/kernel/Makefile
--- linux-2.4.23/arch/i386/kernel/Makefile	Sat Dec  6 08:14:41 2003
+++ linux-2.4.23-cpufreq-20031214/arch/i386/kernel/Makefile	Sun Dec 14 12:27:56 2003
@@ -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 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 -urN linux-2.4.23/arch/i386/kernel/elanfreq.c linux-2.4.23-cpufreq-20031214/arch/i386/kernel/elanfreq.c
--- linux-2.4.23/arch/i386/kernel/elanfreq.c	Thu Jan  1 01:00:00 1970
+++ linux-2.4.23-cpufreq-20031214/arch/i386/kernel/elanfreq.c	Mon Aug 25 16:58:45 2003
@@ -0,0 +1,286 @@
+/*
+ * 	elanfreq: 	cpufreq driver for the AMD ELAN family
+ *
+ *	(c) Copyright 2002 Robert Schwebel <r.schwebel@pengutronix.de>
+ *
+ *	Parts of this code are (c) Sven Geggus <sven@geggus.net> 
+ *
+ *      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@pengutronix.de>, Sven Geggus <sven@geggus.net>");
+MODULE_DESCRIPTION("cpufreq driver for AMD's Elan CPUs");
+
+module_init(elanfreq_init);
+module_exit(elanfreq_exit);
+
diff -urN linux-2.4.23/arch/i386/kernel/gx-suspmod.c linux-2.4.23-cpufreq-20031214/arch/i386/kernel/gx-suspmod.c
--- linux-2.4.23/arch/i386/kernel/gx-suspmod.c	Thu Jan  1 01:00:00 1970
+++ linux-2.4.23-cpufreq-20031214/arch/i386/kernel/gx-suspmod.c	Mon Aug 25 16:58:46 2003
@@ -0,0 +1,510 @@
+/*
+ *	Cyrix MediaGX and NatSemi Geode Suspend Modulation
+ *	(C) 2002 Zwane Mwaikambo <zwane@commfireservices.com>
+ *	(C) 2002 Hiroshi Miura   <miura@da-cha.org>
+ *	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@da-cha.org>
+ *		- rewrite for Cyrix MediaGX Cx5510/5520 and 
+ *		  NatSemi Geode Cs5530(A).
+ *
+ *	Jul. ??, 2002  Zwane Mwaikambo <zwane@commfireservices.com>
+ *		- 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@da-cha.org>");
+MODULE_DESCRIPTION ("Cpufreq driver for Cyrix MediaGX and NatSemi Geode");
+MODULE_LICENSE ("GPL");
+
+module_init(cpufreq_gx_init);
+module_exit(cpufreq_gx_exit);
+
diff -urN linux-2.4.23/arch/i386/kernel/i386_ksyms.c linux-2.4.23-cpufreq-20031214/arch/i386/kernel/i386_ksyms.c
--- linux-2.4.23/arch/i386/kernel/i386_ksyms.c	Sat Dec  6 08:14:41 2003
+++ linux-2.4.23-cpufreq-20031214/arch/i386/kernel/i386_ksyms.c	Sun Dec 14 12:27:56 2003
@@ -28,6 +28,7 @@
 #include <asm/desc.h>
 #include <asm/pgtable.h>
 #include <asm/pgalloc.h>
+#include <asm/ist.h>
 #include <asm/edd.h>
 
 extern void dump_thread(struct pt_regs *, struct user *);
@@ -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 -urN linux-2.4.23/arch/i386/kernel/longhaul.c linux-2.4.23-cpufreq-20031214/arch/i386/kernel/longhaul.c
--- linux-2.4.23/arch/i386/kernel/longhaul.c	Thu Jan  1 01:00:00 1970
+++ linux-2.4.23-cpufreq-20031214/arch/i386/kernel/longhaul.c	Tue Dec  9 10:07:30 2003
@@ -0,0 +1,475 @@
+/*
+ *  (C) 2001-2003  Dave Jones. <davej@codemonkey.org.uk>
+ *  (C) 2002  Padraig Brady. <padraig@antefacto.com>
+ *
+ *  Licensed under the terms of the GNU GPL License version 2.
+ *  Based upon datasheets & sample CPUs kindly provided by VIA.
+ *
+ *  VIA have currently 2 different versions of Longhaul.
+ *  Version 1 (Longhaul) uses the BCR2 MSR at 0x1147.
+ *   It is present only in Samuel 1, Samuel 2 and Ezra.
+ *  Version 2 (Powersaver) uses the POWERSAVER MSR at 0x110a.
+ *   It is present in Ezra-T, Nehemiah and above.
+ *   In addition to scaling multiplier, it can also scale voltage.
+ *   There is provision for scaling FSB too, but this doesn't work
+ *   too well in practice.
+ *
+ *  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)
+{
+	int mhz;
+	mhz = (mult/10)*fsb;
+	if (mult%10)
+		mhz += fsb/2;
+	return mhz;
+}
+
+
+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==2) {
+		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;
+
+	/*
+	 * Powersaver. (Ezra-T [C5M], Nehemiah [C5N])
+	 * 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)
+	 * Ezra-T was alleged to do FSB scaling too, but it never worked in practice.
+	 */
+	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 = 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.
+ */
+
+#define ROUNDING	0xf
+
+static int _guess (int guess, int maxmult)
+{
+	int target;
+
+	target = ((maxmult/10)*guess);
+	if (maxmult%10 != 0)
+		target += (guess/2);
+	target += ROUNDING/2;
+	target &= ~ROUNDING;
+	return target;
+}
+
+static int guess_fsb(int maxmult)
+{
+	int speed = (cpu_khz/1000);
+	int i;
+	int speeds[3] = { 66, 100, 133 };
+
+	speed += ROUNDING/2;
+	speed &= ~ROUNDING;
+
+	for (i=0; i<3; i++) {
+		if (_guess(speeds[i],maxmult) == speed)
+			return speeds[i];
+	}
+	return 0;
+}
+
+
+
+static int __init longhaul_get_ranges (void)
+{
+	struct cpuinfo_x86 *c = cpu_data;
+	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;
+		if (c->x86_model==6)
+			fsb = eblcr_fsb_table[invalue];
+		else
+			fsb = guess_fsb(maxmult);
+		break;
+
+	case 2:
+		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, samuel1_clock_ratio, sizeof(samuel1_clock_ratio));
+		memcpy (eblcr_table, samuel1_eblcr, sizeof(samuel1_eblcr));
+		break;
+
+	case 7:		/* C5B / C5C */
+		longhaul_version=1;
+		switch (c->x86_mask) {
+		case 0:
+			cpuname = "C3 'Samuel 2' [C5B]";
+			/* Note, this is not a typo, early Samuel2's had Samuel1 ratios. */
+			memcpy (clock_ratio, samuel1_clock_ratio, sizeof(samuel1_clock_ratio));
+			memcpy (eblcr_table, samuel2_eblcr, sizeof(samuel2_eblcr));
+			break;
+		case 1 ... 15:
+			if (c->x86_mask < 8)
+				cpuname = "C3 'Samuel 2' [C5B]";
+			else
+				cpuname = "C3 'Ezra' [C5C]";
+			memcpy (clock_ratio, ezra_clock_ratio, sizeof(ezra_clock_ratio));
+			memcpy (eblcr_table, ezra_eblcr, sizeof(ezra_eblcr));
+			break;
+		}
+		break;
+
+	case 8:
+		cpuname = "C3 'Ezra-T' [C5M]";
+		longhaul_version=2;
+		numscales=32;
+		memcpy (clock_ratio, ezrat_clock_ratio, sizeof(ezrat_clock_ratio));
+		memcpy (eblcr_table, ezrat_eblcr, sizeof(ezrat_eblcr));
+		break;
+
+	case 9:
+		cpuname = "C3 'Nehemiah' [C5N]";
+		longhaul_version=2;
+		numscales=32;
+		memcpy (clock_ratio, nehemiah_clock_ratio, sizeof(nehemiah_clock_ratio));
+		memcpy (eblcr_table, nehemiah_eblcr, sizeof(nehemiah_eblcr));
+		break;
+
+	default:
+		cpuname = "Unknown";
+		break;
+	}
+
+	printk (KERN_INFO PFX "VIA %s CPU detected. Longhaul v%d supported.\n",
+					cpuname, longhaul_version);
+
+	if ((longhaul_version==2) && (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 ... 8:
+		return cpufreq_register_driver(&longhaul_driver);
+	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@codemonkey.org.uk\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@codemonkey.org.uk>");
+MODULE_DESCRIPTION ("Longhaul driver for VIA Cyrix processors.");
+MODULE_LICENSE ("GPL");
+
+module_init(longhaul_init);
+module_exit(longhaul_exit);
+
diff -urN linux-2.4.23/arch/i386/kernel/longhaul.h linux-2.4.23-cpufreq-20031214/arch/i386/kernel/longhaul.h
--- linux-2.4.23/arch/i386/kernel/longhaul.h	Thu Jan  1 01:00:00 1970
+++ linux-2.4.23-cpufreq-20031214/arch/i386/kernel/longhaul.h	Tue Dec  9 09:58:33 2003
@@ -0,0 +1,325 @@
+/*
+ *  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 samuel1_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
+ */
+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 */
+};
+
+/*
+ * VIA C3 Ezra
+ */
+static int __initdata ezra_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 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 ezrat_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 ezrat_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 */
+};
+
+/*
+ * VIA C3 Nehemiah */
+static int __initdata nehemiah_clock_ratio[32] = {
+	100, /* 0000 -> 10.0x */
+	160, /* 0001 -> 16.0x */
+	-1,  /* 0010 -> RESERVED */
+	90,  /* 0011 ->  9.0x */
+	95,  /* 0100 ->  9.5x */
+	-1,  /* 0101 -> RESERVED */
+	-1,  /* 0110 -> RESERVED */
+	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 */
+
+	100, /* 0000 -> 10.0x */
+	110, /* 0001 -> 11.0x */
+	120, /* 0010 -> 12.0x */
+	90,  /* 0011 ->  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 */
+	120, /* 1111 -> 12.0x */
+};
+
+static int __initdata nehemiah_eblcr[32] = {
+	50,  /* 0000 ->  5.0x */
+	160, /* 0001 -> 16.0x */
+	-1,  /* 0010 -> RESERVED */
+	100, /* 0011 -> 10.0x */
+	55,  /* 0100 ->  5.5x */
+	-1,  /* 0101 -> RESERVED */
+	-1,  /* 0110 -> RESERVED */
+	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 */
+
+	90,  /* 0000 ->  9.0x */
+	110, /* 0001 -> 11.0x */
+	120, /* 0010 -> 12.0x */
+	100, /* 0011 -> 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 */
+	120, /* 1100 -> 12.0x */
+	155, /* 1101 -> 15.5x */
+	-1,  /* 1110 -> RESERVED */
+	-1,  /* 1111 -> RESERVED */
+};
+/* 
+ * 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 -urN linux-2.4.23/arch/i386/kernel/longrun.c linux-2.4.23-cpufreq-20031214/arch/i386/kernel/longrun.c
--- linux-2.4.23/arch/i386/kernel/longrun.c	Thu Jan  1 01:00:00 1970
+++ linux-2.4.23-cpufreq-20031214/arch/i386/kernel/longrun.c	Mon Aug 25 16:58:47 2003
@@ -0,0 +1,284 @@
+/*
+ * (C) 2002 - 2003  Dominik Brodowski <linux@brodo.de>
+ *
+ *  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@brodo.de>");
+MODULE_DESCRIPTION ("LongRun driver for Transmeta Crusoe processors.");
+MODULE_LICENSE ("GPL");
+
+module_init(longrun_init);
+module_exit(longrun_exit);
diff -urN linux-2.4.23/arch/i386/kernel/p4-clockmod.c linux-2.4.23-cpufreq-20031214/arch/i386/kernel/p4-clockmod.c
--- linux-2.4.23/arch/i386/kernel/p4-clockmod.c	Thu Jan  1 01:00:00 1970
+++ linux-2.4.23-cpufreq-20031214/arch/i386/kernel/p4-clockmod.c	Mon Aug 25 16:58:47 2003
@@ -0,0 +1,271 @@
+/*
+ *	Pentium 4/Xeon CPU on demand clock modulation/speed scaling
+ *	(C) 2002 - 2003 Dominik Brodowski <linux@brodo.de>
+ *	(C) 2002 Zwane Mwaikambo <zwane@commfireservices.com>
+ *	(C) 2002 Arjan van de Ven <arjanv@redhat.com>
+ *	(C) 2002 Tora T. Engstad
+ *	All Rights Reserved
+ *
+ *	This program is free software; you can redistribute it and/or
+ *      modify it under the terms of the GNU General Public License
+ *      as published by the Free Software Foundation; either version
+ *      2 of the License, or (at your option) any later version.
+ *
+ *      The author(s) of this software shall not be held liable for damages
+ *      of any nature resulting due to the use of this software. This
+ *      software is provided AS-IS with no warranties.
+ *	
+ *	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@commfireservices.com>");
+MODULE_DESCRIPTION ("cpufreq driver for Pentium(TM) 4/Xeon(TM)");
+MODULE_LICENSE ("GPL");
+
+module_init(cpufreq_p4_init);
+module_exit(cpufreq_p4_exit);
+
diff -urN linux-2.4.23/arch/i386/kernel/powernow-k6.c linux-2.4.23-cpufreq-20031214/arch/i386/kernel/powernow-k6.c
--- linux-2.4.23/arch/i386/kernel/powernow-k6.c	Thu Jan  1 01:00:00 1970
+++ linux-2.4.23-cpufreq-20031214/arch/i386/kernel/powernow-k6.c	Mon Aug 25 16:58:47 2003
@@ -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 Pänkälä, 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@redhat.com>, Dave Jones <davej@codemonkey.org.uk>, Dominik Brodowski <linux@brodo.de>");
+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 -urN linux-2.4.23/arch/i386/kernel/powernow-k7.c linux-2.4.23-cpufreq-20031214/arch/i386/kernel/powernow-k7.c
--- linux-2.4.23/arch/i386/kernel/powernow-k7.c	Thu Jan  1 01:00:00 1970
+++ linux-2.4.23-cpufreq-20031214/arch/i386/kernel/powernow-k7.c	Fri Sep 19 18:47:43 2003
@@ -0,0 +1,422 @@
+/*
+ *  AMD K7 Powernow driver.
+ *  (C) 2003 Dave Jones <davej@codemonkey.org.uk> on behalf of SuSE Labs.
+ *  (C) 2003 Dave Jones <davej@redhat.com>
+ *
+ *  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@redhat.com\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@codemonkey.org.uk>");
+MODULE_DESCRIPTION ("Powernow driver for AMD K7 processors.");
+MODULE_LICENSE ("GPL");
+
+module_init(powernow_init);
+module_exit(powernow_exit);
+
diff -urN linux-2.4.23/arch/i386/kernel/powernow-k7.h linux-2.4.23-cpufreq-20031214/arch/i386/kernel/powernow-k7.h
--- linux-2.4.23/arch/i386/kernel/powernow-k7.h	Thu Jan  1 01:00:00 1970
+++ linux-2.4.23-cpufreq-20031214/arch/i386/kernel/powernow-k7.h	Mon Aug 25 16:58:47 2003
@@ -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 -urN linux-2.4.23/arch/i386/kernel/powernow-k8.c linux-2.4.23-cpufreq-20031214/arch/i386/kernel/powernow-k8.c
--- linux-2.4.23/arch/i386/kernel/powernow-k8.c	Thu Jan  1 01:00:00 1970
+++ linux-2.4.23-cpufreq-20031214/arch/i386/kernel/powernow-k8.c	Tue Sep 30 13:25:51 2003
@@ -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@amd.com
+ *
+ *  Based on the powernow-k7.c module written by Dave Jones.
+ *  (C) 2003 Dave Jones <davej@codemonkey.ork.uk> 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@amd.com>");
+MODULE_DESCRIPTION("AMD Athlon 64 and Opteron processor frequency driver.");
+MODULE_LICENSE("GPL");
+
+module_init(drv_init);
+module_exit(drv_exit);
diff -urN linux-2.4.23/arch/i386/kernel/powernow-k8.h linux-2.4.23-cpufreq-20031214/arch/i386/kernel/powernow-k8.h
--- linux-2.4.23/arch/i386/kernel/powernow-k8.h	Thu Jan  1 01:00:00 1970
+++ linux-2.4.23-cpufreq-20031214/arch/i386/kernel/powernow-k8.h	Tue Sep 30 11:26:27 2003
@@ -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 -urN linux-2.4.23/arch/i386/kernel/setup.c linux-2.4.23-cpufreq-20031214/arch/i386/kernel/setup.c
--- linux-2.4.23/arch/i386/kernel/setup.c	Sat Dec  6 08:14:41 2003
+++ linux-2.4.23-cpufreq-20031214/arch/i386/kernel/setup.c	Sun Dec 14 12:27:56 2003
@@ -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 -urN linux-2.4.23/arch/i386/kernel/speedstep-centrino.c linux-2.4.23-cpufreq-20031214/arch/i386/kernel/speedstep-centrino.c
--- linux-2.4.23/arch/i386/kernel/speedstep-centrino.c	Thu Jan  1 01:00:00 1970
+++ linux-2.4.23-cpufreq-20031214/arch/i386/kernel/speedstep-centrino.c	Tue Dec  9 09:57:06 2003
@@ -0,0 +1,393 @@
+/*
+ * cpufreq driver for Enhanced SpeedStep, as found in Intel's Pentium
+ * M (part of the Centrino chipset).
+ *
+ * Despite the "SpeedStep" in the name, this is almost entirely unlike
+ * traditional SpeedStep.
+ *
+ * Modelled on speedstep.c
+ *
+ * Copyright (C) 2003 Jeremy Fitzhardinge <jeremy@goop.org>
+ *
+ * WARNING WARNING WARNING
+ * 
+ * This driver manipulates the PERF_CTL MSR, which is only somewhat
+ * documented.  While it seems to work on my laptop, it has not been
+ * tested anywhere else, and it may not work for you, do strange
+ * things or simply crash.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/cpufreq.h>
+#include <linux/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@goop.org>"
+
+/*#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 }
+};
+
+/* Ultra Low Voltage Intel Pentium M processor 1000MHz */
+static struct cpufreq_frequency_table op_1000[] =
+{
+	OP(600,  844),
+	OP(800,  972),
+	OP(900,  988),
+	OP(1000, 1004),
+	{ .frequency = CPUFREQ_TABLE_END }
+};
+
+/* Low Voltage Intel Pentium M processor 1.10GHz */
+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(1000),
+	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@goop.org>");
+MODULE_DESCRIPTION ("Enhanced SpeedStep driver for Intel Pentium M processors.");
+MODULE_LICENSE ("GPL");
+
+module_init(centrino_init);
+module_exit(centrino_exit);
diff -urN linux-2.4.23/arch/i386/kernel/speedstep-ich.c linux-2.4.23-cpufreq-20031214/arch/i386/kernel/speedstep-ich.c
--- linux-2.4.23/arch/i386/kernel/speedstep-ich.c	Thu Jan  1 01:00:00 1970
+++ linux-2.4.23-cpufreq-20031214/arch/i386/kernel/speedstep-ich.c	Mon Aug 25 16:58:47 2003
@@ -0,0 +1,363 @@
+/*
+ * (C) 2001  Dave Jones, Arjan van de ven.
+ * (C) 2002 - 2003  Dominik Brodowski <linux@brodo.de>
+ *
+ *  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@codemonkey.org.uk>, Dominik Brodowski <linux@brodo.de>");
+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 -urN linux-2.4.23/arch/i386/kernel/speedstep-lib.c linux-2.4.23-cpufreq-20031214/arch/i386/kernel/speedstep-lib.c
--- linux-2.4.23/arch/i386/kernel/speedstep-lib.c	Thu Jan  1 01:00:00 1970
+++ linux-2.4.23-cpufreq-20031214/arch/i386/kernel/speedstep-lib.c	Mon Aug 25 16:58:47 2003
@@ -0,0 +1,275 @@
+/*
+ * (C) 2002 - 2003 Dominik Brodowski <linux@brodo.de>
+ *
+ *  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@brodo.de>");
+MODULE_DESCRIPTION ("Library for Intel SpeedStep 1 or 2 cpufreq drivers.");
+MODULE_LICENSE ("GPL");
diff -urN linux-2.4.23/arch/i386/kernel/speedstep-lib.h linux-2.4.23-cpufreq-20031214/arch/i386/kernel/speedstep-lib.h
--- linux-2.4.23/arch/i386/kernel/speedstep-lib.h	Thu Jan  1 01:00:00 1970
+++ linux-2.4.23-cpufreq-20031214/arch/i386/kernel/speedstep-lib.h	Mon Aug 25 16:58:47 2003
@@ -0,0 +1,41 @@
+/*
+ * (C) 2002 - 2003 Dominik Brodowski <linux@brodo.de>
+ *
+ *  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 -urN linux-2.4.23/arch/i386/kernel/speedstep-piix4.c linux-2.4.23-cpufreq-20031214/arch/i386/kernel/speedstep-piix4.c
--- linux-2.4.23/arch/i386/kernel/speedstep-piix4.c	Thu Jan  1 01:00:00 1970
+++ linux-2.4.23-cpufreq-20031214/arch/i386/kernel/speedstep-piix4.c	Thu Aug 28 15:32:49 2003
@@ -0,0 +1,342 @@
+/*
+ * (C) 2001  Dave Jones, Arjan van de ven.
+ * (C) 2002 - 2003  Dominik Brodowski <linux@brodo.de>
+ * (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 -urN linux-2.4.23/arch/i386/kernel/speedstep-smi.c linux-2.4.23-cpufreq-20031214/arch/i386/kernel/speedstep-smi.c
--- linux-2.4.23/arch/i386/kernel/speedstep-smi.c	Thu Jan  1 01:00:00 1970
+++ linux-2.4.23-cpufreq-20031214/arch/i386/kernel/speedstep-smi.c	Mon Nov 10 12:22:44 2003
@@ -0,0 +1,365 @@
+/*
+ * Intel SpeedStep SMI driver.
+ *
+ * (C) 2003  Hiroshi Miura <miura@da-cha.org>
+ *
+ *  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 -urN linux-2.4.23/arch/i386/kernel/time.c linux-2.4.23-cpufreq-20031214/arch/i386/kernel/time.c
--- linux-2.4.23/arch/i386/kernel/time.c	Sat Dec  6 08:14:41 2003
+++ linux-2.4.23-cpufreq-20031214/arch/i386/kernel/time.c	Sun Dec 14 12:27:56 2003
@@ -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 -urN linux-2.4.23/drivers/Makefile linux-2.4.23-cpufreq-20031214/drivers/Makefile
--- linux-2.4.23/drivers/Makefile	Sat Dec  6 08:14:44 2003
+++ linux-2.4.23-cpufreq-20031214/drivers/Makefile	Sun Dec 14 12:27:56 2003
@@ -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 cpufreq usb/gadget
 
 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 -urN linux-2.4.23/drivers/cpufreq/Kconfig linux-2.4.23-cpufreq-20031214/drivers/cpufreq/Kconfig
--- linux-2.4.23/drivers/cpufreq/Kconfig	Thu Jan  1 01:00:00 1970
+++ linux-2.4.23-cpufreq-20031214/drivers/cpufreq/Kconfig	Sat Feb 22 11:23:47 2003
@@ -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 -urN linux-2.4.23/drivers/cpufreq/Makefile linux-2.4.23-cpufreq-20031214/drivers/cpufreq/Makefile
--- linux-2.4.23/drivers/cpufreq/Makefile	Thu Jan  1 01:00:00 1970
+++ linux-2.4.23-cpufreq-20031214/drivers/cpufreq/Makefile	Wed Aug 27 15:45:36 2003
@@ -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 -urN linux-2.4.23/drivers/cpufreq/cpufreq.c linux-2.4.23-cpufreq-20031214/drivers/cpufreq/cpufreq.c
--- linux-2.4.23/drivers/cpufreq/cpufreq.c	Thu Jan  1 01:00:00 1970
+++ linux-2.4.23-cpufreq-20031214/drivers/cpufreq/cpufreq.c	Wed Aug 27 16:11:21 2003
@@ -0,0 +1,720 @@
+/*
+ *  linux/kernel/cpufreq.c
+ *
+ *  Copyright (C) 2001 Russell King
+ *            (C) 2002 - 2003 Dominik Brodowski <linux@brodo.de>
+ *
+ * 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 -urN linux-2.4.23/drivers/cpufreq/freq_table.c linux-2.4.23-cpufreq-20031214/drivers/cpufreq/freq_table.c
--- linux-2.4.23/drivers/cpufreq/freq_table.c	Thu Jan  1 01:00:00 1970
+++ linux-2.4.23-cpufreq-20031214/drivers/cpufreq/freq_table.c	Mon Aug 25 16:58:47 2003
@@ -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@brodo.de>");
+MODULE_DESCRIPTION ("CPUfreq frequency table helpers");
+MODULE_LICENSE ("GPL");
diff -urN linux-2.4.23/drivers/cpufreq/proc_intf.c linux-2.4.23-cpufreq-20031214/drivers/cpufreq/proc_intf.c
--- linux-2.4.23/drivers/cpufreq/proc_intf.c	Thu Jan  1 01:00:00 1970
+++ linux-2.4.23-cpufreq-20031214/drivers/cpufreq/proc_intf.c	Mon Aug 25 16:58:47 2003
@@ -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@brodo.de>");
+MODULE_DESCRIPTION ("CPUfreq /proc/cpufreq interface");
+MODULE_LICENSE ("GPL");
+
+module_init(cpufreq_proc_init);
+module_exit(cpufreq_proc_exit);
diff -urN linux-2.4.23/drivers/cpufreq/userspace.c linux-2.4.23-cpufreq-20031214/drivers/cpufreq/userspace.c
--- linux-2.4.23/drivers/cpufreq/userspace.c	Thu Jan  1 01:00:00 1970
+++ linux-2.4.23-cpufreq-20031214/drivers/cpufreq/userspace.c	Thu Aug 28 15:41:57 2003
@@ -0,0 +1,559 @@
+/*
+ *  drivers/cpufreq/userspace.c
+ *
+ *  Copyright (C)  2001 Russell King
+ *            (C)  2002 - 2003 Dominik Brodowski <linux@brodo.de>
+ *
+ * $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@brodo.de>, Russell King <rmk@arm.linux.org.uk>");
+MODULE_DESCRIPTION ("CPUfreq policy governor 'userspace'");
+MODULE_LICENSE ("GPL");
+
+module_init(cpufreq_gov_userspace_init);
+module_exit(cpufreq_gov_userspace_exit);
diff -urN linux-2.4.23/include/asm-i386/ist.h linux-2.4.23-cpufreq-20031214/include/asm-i386/ist.h
--- linux-2.4.23/include/asm-i386/ist.h	Thu Jan  1 01:00:00 1970
+++ linux-2.4.23-cpufreq-20031214/include/asm-i386/ist.h	Sun Dec 14 12:27:56 2003
@@ -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@intel.com>
+ *
+ * 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 -urN linux-2.4.23/include/asm-i386/msr.h linux-2.4.23-cpufreq-20031214/include/asm-i386/msr.h
--- linux-2.4.23/include/asm-i386/msr.h	Sat Dec  6 08:14:50 2003
+++ linux-2.4.23-cpufreq-20031214/include/asm-i386/msr.h	Sun Dec 14 12:27:56 2003
@@ -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 -urN linux-2.4.23/include/asm-i386/smp.h linux-2.4.23-cpufreq-20031214/include/asm-i386/smp.h
--- linux-2.4.23/include/asm-i386/smp.h	Sat Dec 13 15:43:12 2003
+++ linux-2.4.23-cpufreq-20031214/include/asm-i386/smp.h	Sun Dec 14 12:27:56 2003
@@ -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 -urN linux-2.4.23/include/linux/cpufreq.h linux-2.4.23-cpufreq-20031214/include/linux/cpufreq.h
--- linux-2.4.23/include/linux/cpufreq.h	Thu Jan  1 01:00:00 1970
+++ linux-2.4.23-cpufreq-20031214/include/linux/cpufreq.h	Thu Aug 28 15:41:58 2003
@@ -0,0 +1,299 @@
+/*
+ *  linux/include/linux/cpufreq.h
+ *
+ *  Copyright (C) 2001 Russell King
+ *            (C) 2002 - 2003 Dominik Brodowski <linux@brodo.de>
+ *            
+ *
+ * $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 -urN linux-2.4.23/include/linux/smp.h linux-2.4.23-cpufreq-20031214/include/linux/smp.h
--- linux-2.4.23/include/linux/smp.h	Sat Dec 13 15:43:12 2003
+++ linux-2.4.23-cpufreq-20031214/include/linux/smp.h	Sun Dec 14 12:27:56 2003
@@ -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