diff -urNp --exclude CVS --exclude BitKeeper xx-ref/Documentation/00-INDEX xx/Documentation/00-INDEX
--- xx-ref/Documentation/00-INDEX 2003-03-15 03:24:52.000000000 +0100
+++ xx/Documentation/00-INDEX 2003-06-07 13:33:35.000000000 +0200
@@ -52,6 +52,8 @@ cdrom/
- 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 -urNp --exclude CVS --exclude BitKeeper xx-ref/Documentation/Configure.help xx/Documentation/Configure.help
--- xx-ref/Documentation/Configure.help 2003-06-04 14:21:10.000000000 +0200
+++ xx/Documentation/Configure.help 2003-06-07 13:33:35.000000000 +0200
@@ -26372,15 +26372,13 @@ CONFIG_CPU_ARM926T
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'.
+CONFIG_X86_GX_SUSPMOD
+ This add the CPUFreq driver for NatSemi Geode processors which
+ support suspend modulation.
+
+ For details, take a look at linux/Documentation/cpufreq.
+
+ If in doubt, say N.
SiS
CONFIG_DRM_SIS
@@ -26649,6 +26647,120 @@ 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/cpufreq or
+ at
+
+ 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/cpufreq.
+
+ 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/cpufreq.
+
+ 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/cpufreq.
+
+ 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/cpufreq.
+
+ 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/cpufreq.
+
+ 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/cpufreq.
+
+ If in doubt, say N.
+
+CONFIG_X86_SPEEDSTEP
+ This adds the CPUFreq driver for certain mobile Intel Pentium III
+ (Coppermine), all mobile Intel Pentium III-M (Tulatin) and all
+ mobile Intel Pentium 4 P4-Ms.
+
+ For details, take a look at linux/Documentation/cpufreq.
+
+ 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/cpufreq.
+
+ 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/cpufreq.
+
+ 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/cpufreq.
+
+ If in doubt, say Y.
+
#
# A couple of things I keep forgetting:
# capitalize: AppleTalk, Ethernet, DOS, DMA, FAT, FTP, Internet,
diff -urNp --exclude CVS --exclude BitKeeper xx-ref/Documentation/cpufreq/core.txt xx/Documentation/cpufreq/core.txt
--- xx-ref/Documentation/cpufreq/core.txt 1970-01-01 01:00:00.000000000 +0100
+++ xx/Documentation/cpufreq/core.txt 2003-06-07 13:33:35.000000000 +0200
@@ -0,0 +1,90 @@
+ 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
+ David Kimdon
+
+
+
+ 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 (like thermal modules like ACPI) or of all
+frequency changes (like 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.
+
+
+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 or
+CPUFREQ_ALL_CPUS for all CPUs; 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 or CPUFREQ_ALL_CPUS
+old - old frequency
+new - new frequency
diff -urNp --exclude CVS --exclude BitKeeper xx-ref/Documentation/cpufreq/cpu-drivers.txt xx/Documentation/cpufreq/cpu-drivers.txt
--- xx-ref/Documentation/cpufreq/cpu-drivers.txt 1970-01-01 01:00:00.000000000 +0100
+++ xx/Documentation/cpufreq/cpu-drivers.txt 2003-06-07 13:33:35.000000000 +0200
@@ -0,0 +1,207 @@
+ 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
+
+
+
+ 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 neccessary:
+
+
+1.1 Initialization
+------------------
+
+First of all, in an __initcall level 7 or later (preferrably
+module_init() so that your driver is modularized) 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.init - A pointer to the per-CPU initialization
+ function.
+
+cpufreq_driver.verify - A pointer to a "verfication" funciton.
+
+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.
+
+
+
+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 fucntion
+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 (unlock that
+register etc.).
+
+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 neccessary,
+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 -urNp --exclude CVS --exclude BitKeeper xx-ref/Documentation/cpufreq/governors.txt xx/Documentation/cpufreq/governors.txt
--- xx-ref/Documentation/cpufreq/governors.txt 1970-01-01 01:00:00.000000000 +0100
+++ xx/Documentation/cpufreq/governors.txt 2003-06-07 13:33:35.000000000 +0200
@@ -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
+
+
+
+ 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 specifc 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:
+
+inline int cpufreq_driver_target(struct cpufreq_policy *policy,
+ unsigned int target_freq,
+ unsigned int relation);
+
+inline int cpufreq_driver_target_l(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
+cpufreq_driver_sem 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_l to lock the cpufreq_driver_sem
+before the command is passed to the cpufreq processor driver.
+
diff -urNp --exclude CVS --exclude BitKeeper xx-ref/Documentation/cpufreq/index.txt xx/Documentation/cpufreq/index.txt
--- xx-ref/Documentation/cpufreq/index.txt 1970-01-01 01:00:00.000000000 +0100
+++ xx/Documentation/cpufreq/index.txt 2003-06-07 13:33:35.000000000 +0200
@@ -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
+
+
+
+ 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 -urNp --exclude CVS --exclude BitKeeper xx-ref/Documentation/cpufreq/user-guide.txt xx/Documentation/cpufreq/user-guide.txt
--- xx-ref/Documentation/cpufreq/user-guide.txt 1970-01-01 01:00:00.000000000 +0100
+++ xx/Documentation/cpufreq/user-guide.txt 2003-06-07 13:33:35.000000000 +0200
@@ -0,0 +1,136 @@
+ 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
+
+
+
+ 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 Processors
+
+
+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 Processors
+=========================================
+
+The following processors are supported by cpufreq in this kernel:
+
+AMD Elan - SC400, SC410
+AMD mobile K6-2+
+AMD mobile K6-3+
+AMD mobile Duron/Athlon K7
+Cyrix Media GXm
+Intel mobile PIII [*] and Intel mobile PIII-M on certain chipsets
+Intel Pentium 4, Intel Xeon
+National Semiconductors Geode GX
+Transmeta Crusoe
+
+[*] only certain Intel mobile PIII processors are supported. If you
+know that you own a speedstep-capable processor, pass the option
+"speedstep_coppermine=1" to the module speedstep.o
+
+
+
+2. "Policy" / "Governor" ?
+==========================
+
+Some CPU frequency scaling-capable processor switch between varios
+frequencies and operating voltages "on the fly" without any kernel or
+user involvement. This guarantuees 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 avaialble 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
+====================================================
+
+Two files are of special importance for cpufreq in this backport of
+cpufreq to the 2.4. kernel:
+
+/proc/cpufreq
+and
+/proc/sys/cpu/0/speed
+
+When you "cat" the file /proc/cpufreq, you'll find something like:
+
+ minimum CPU frequency - maximum CPU frequency - policy
+CPU 0 1200000 ( 75%) - 1600000 (100%) - performance
+
+This means the current policy allows this CPU to be run anywhere
+between 1.2 GHz (the value is in kHz) and 1.6 GHz with an eye towards
+performance.
+
+To change the policy, "echo" the desired new policy or governor into
+/proc/cpufreq. Use one of the following formats:
+
+cpu_nr:min_freq:max_freq:policy
+cpu_nr%min_freq%max_freq%policy
+min_freq:max_freq:policy
+
+with cpu_nr being the CPU which shall be affected, min_freq and max_freq the lower and upper limit of the CPU core frequency in kHz, and policy either "performance" or "powersave".
+A few examples:
+
+root@notebook:#echo -n "0:0:0:powersave" > /proc/cpufreq
+
+sets the CPU #0 to the lowest supported frequency.
+
+root@notebook:#echo -n "1%100%100%performance" > /proc/cpufreq
+
+sets the CPU #1 to the highest supported frequency.
+
+root@notebook:#echo -n "1000000:2000000:performance" > /proc/cpufreq
+
+to set the frequency of all CPUs between 1 GHz and 2 GHz and to the
+policy "performance".
+
+Please note that the values you "echo" into /proc/cpufreq are
+validated first, and may be limited by hardware or thermal
+considerations. Because of this, a read from /proc/cpufreq might
+differ from what was written into it.
+
+
+
+To select a specific speed within these general limits, you can "echo"
+a specific speed into /proc/sys/cpu/0/speed (in kHz) again. For example, to set the
+CPU speed to 2GHz,
+
+root@notebook:#echo -n 2000000 > /proc/sys/cpu/0/speed
diff -urNp --exclude CVS --exclude BitKeeper xx-ref/Makefile xx/Makefile
--- xx-ref/Makefile 2003-06-04 14:21:10.000000000 +0200
+++ xx/Makefile 2003-06-07 13:33:35.000000000 +0200
@@ -133,6 +133,7 @@ DRIVERS-m :=
DRIVERS- :=
DRIVERS-$(CONFIG_ACPI) += drivers/acpi/acpi.o
+DRIVERS-$(CONFIG_CPU_FREQ) += drivers/cpufreq/cpufreq.o
DRIVERS-$(CONFIG_PARPORT) += drivers/parport/driver.o
DRIVERS-y += drivers/char/char.o \
drivers/block/block.o \
diff -urNp --exclude CVS --exclude BitKeeper xx-ref/arch/i386/config.in xx/arch/i386/config.in
--- xx-ref/arch/i386/config.in 2003-06-07 13:33:28.000000000 +0200
+++ xx/arch/i386/config.in 2003-06-07 13:33:35.000000000 +0200
@@ -194,6 +194,29 @@ fi
bool 'Machine Check Exception' CONFIG_X86_MCE
+if [ "$CONFIG_SMP" != "y" ]; then
+ mainmenu_option next_comment
+ comment 'CPU Frequency scaling'
+ bool 'CPU Frequency scaling' CONFIG_CPU_FREQ
+ if [ "$CONFIG_CPU_FREQ" = "y" ]; then
+ tristate ' CPU frequency table helpers' CONFIG_CPU_FREQ_TABLE
+ define_bool CONFIG_CPU_FREQ_PROC_INTF y
+ tristate '"userspace" for userspace frequency scaling' CONFIG_CPU_FREQ_GOV_USERSPACE
+ define_bool CONFIG_CPU_FREQ_24_API y
+ 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
+ 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' CONFIG_X86_SPEEDSTEP $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
+fi
+
tristate 'Toshiba Laptop support' CONFIG_TOSHIBA
tristate 'Dell laptop support' CONFIG_I8K
diff -urNp --exclude CVS --exclude BitKeeper xx-ref/arch/i386/kernel/Makefile xx/arch/i386/kernel/Makefile
--- xx-ref/arch/i386/kernel/Makefile 2002-11-29 02:22:55.000000000 +0100
+++ xx/arch/i386/kernel/Makefile 2003-06-07 13:33:35.000000000 +0200
@@ -40,5 +40,13 @@ obj-$(CONFIG_SMP) += smp.o smpboot.o tr
obj-$(CONFIG_X86_LOCAL_APIC) += mpparse.o apic.o nmi.o
obj-$(CONFIG_X86_IO_APIC) += io_apic.o acpitable.o
obj-$(CONFIG_X86_VISWS_APIC) += visws_apic.o
+obj-$(CONFIG_X86_POWERNOW_K6) += powernow-k6.o
+obj-$(CONFIG_X86_POWERNOW_K7) += powernow-k7.o
+obj-$(CONFIG_X86_LONGHAUL) += longhaul.o
+obj-$(CONFIG_X86_SPEEDSTEP) += speedstep.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 -urNp --exclude CVS --exclude BitKeeper xx-ref/arch/i386/kernel/elanfreq.c xx/arch/i386/kernel/elanfreq.c
--- xx-ref/arch/i386/kernel/elanfreq.c 1970-01-01 01:00:00.000000000 +0100
+++ xx/arch/i386/kernel/elanfreq.c 2003-06-07 13:33:35.000000000 +0200
@@ -0,0 +1,289 @@
+/*
+ * elanfreq: cpufreq driver for the AMD ELAN family
+ *
+ * (c) Copyright 2002 Robert Schwebel
+ *
+ * Parts of this code are (c) Sven Geggus
+ *
+ * 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
+#include
+#include
+
+#include
+#include
+#include
+
+#include
+#include
+#include
+
+#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,
+ .exit = NULL,
+ .policy = NULL,
+ .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 , Sven Geggus ");
+MODULE_DESCRIPTION("cpufreq driver for AMD's Elan CPUs");
+
+module_init(elanfreq_init);
+module_exit(elanfreq_exit);
+
diff -urNp --exclude CVS --exclude BitKeeper xx-ref/arch/i386/kernel/gx-suspmod.c xx/arch/i386/kernel/gx-suspmod.c
--- xx-ref/arch/i386/kernel/gx-suspmod.c 1970-01-01 01:00:00.000000000 +0100
+++ xx/arch/i386/kernel/gx-suspmod.c 2003-06-07 13:33:35.000000000 +0200
@@ -0,0 +1,510 @@
+/*
+ * Cyrix MediaGX and NatSemi Geode Suspend Modulation
+ * (C) 2002 Zwane Mwaikambo
+ * (C) 2002 Hiroshi Miura
+ * 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
+ * - rewrite for Cyrix MediaGX Cx5510/5520 and
+ * NatSemi Geode Cs5530(A).
+ *
+ * Jul. ??, 2002 Zwane Mwaikambo
+ * - cs5530_mod patch for 2.4.19-rc1.
+ *
+ *---------------------------------------------------------------------------
+ *
+ * Todo
+ * Test on machines with 5510, 5530, 5530A
+ */
+
+/************************************************************************
+ * Suspend Modulation - Definitions *
+ ************************************************************************/
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+/* 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;
+
+ /* 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 */
+ pci_for_each_dev(gx_pci) {
+ 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 initilize 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 ");
+MODULE_DESCRIPTION ("Cpufreq driver for Cyrix MediaGX and NatSemi Geode");
+MODULE_LICENSE ("GPL");
+
+module_init(cpufreq_gx_init);
+module_exit(cpufreq_gx_exit);
+
diff -urNp --exclude CVS --exclude BitKeeper xx-ref/arch/i386/kernel/i386_ksyms.c xx/arch/i386/kernel/i386_ksyms.c
--- xx-ref/arch/i386/kernel/i386_ksyms.c 2003-06-04 14:21:11.000000000 +0200
+++ xx/arch/i386/kernel/i386_ksyms.c 2003-06-07 13:33:35.000000000 +0200
@@ -49,6 +49,7 @@ extern struct drive_info_struct drive_in
EXPORT_SYMBOL(drive_info);
#endif
+extern unsigned long cpu_khz;
extern unsigned long get_cmos_time(void);
/* platform dependent support */
@@ -71,6 +72,7 @@ EXPORT_SYMBOL(kernel_thread);
EXPORT_SYMBOL(pm_idle);
EXPORT_SYMBOL(pm_power_off);
EXPORT_SYMBOL(get_cmos_time);
+EXPORT_SYMBOL(cpu_khz);
EXPORT_SYMBOL(apm_info);
EXPORT_SYMBOL(gdt);
EXPORT_SYMBOL(empty_zero_page);
@@ -130,7 +132,9 @@ EXPORT_SYMBOL(mmx_copy_page);
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 -urNp --exclude CVS --exclude BitKeeper xx-ref/arch/i386/kernel/longhaul.c xx/arch/i386/kernel/longhaul.c
--- xx-ref/arch/i386/kernel/longhaul.c 1970-01-01 01:00:00.000000000 +0100
+++ xx/arch/i386/kernel/longhaul.c 2003-06-07 13:33:35.000000000 +0200
@@ -0,0 +1,690 @@
+/*
+ * $Id: longhaul.c,v 1.84 2003/01/13 23:41:44 db Exp $
+ *
+ * (C) 2001 Dave Jones.
+ * (C) 2002 Padraig Brady.
+ *
+ * Licensed under the terms of the GNU GPL License version 2.
+ * Based upon datasheets & sample CPUs kindly provided by VIA.
+ *
+ * VIA have currently 3 different versions of Longhaul.
+ *
+ * +---------------------+----------+---------------------------------+
+ * | Marketing name | Codename | longhaul version / features. |
+ * +---------------------+----------+---------------------------------+
+ * | Samuel/CyrixIII | C5A | v1 : multipliers only |
+ * | Samuel2/C3 | C3E/C5B | v1 : multiplier only |
+ * | Ezra | C5C | v2 : multipliers & voltage |
+ * | Ezra-T | C5M/C5N | v3 : multipliers, voltage & FSB |
+ * +---------------------+----------+---------------------------------+
+ *
+ * BIG FAT DISCLAIMER: Work in progress code. Possibly *dangerous*
+ */
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+#include
+
+#define DEBUG
+
+#ifdef DEBUG
+#define dprintk(msg...) printk(msg)
+#else
+#define dprintk(msg...) do { } while(0);
+#endif
+
+static int numscales=16, numvscales;
+static int minvid, maxvid;
+static int can_scale_voltage;
+static int can_scale_fsb;
+static int vrmrev;
+
+
+/* Module parameters */
+static int prefer_slow_fsb;
+static int dont_scale_voltage;
+static int dont_scale_fsb;
+static int current_fsb;
+
+#define __hlt() __asm__ __volatile__("hlt": : :"memory")
+
+/*
+ * Clock ratio tables.
+ * The eblcr ones specify the ratio read from the CPU.
+ * The clock_ratio ones specify what to write to the CPU.
+ */
+
+/* VIA C3 Samuel 1 & Samuel 2 (stepping 0)*/
+static int __initdata longhaul1_clock_ratio[16] = {
+ -1, /* 0000 -> RESERVED */
+ 30, /* 0001 -> 3.0x */
+ 40, /* 0010 -> 4.0x */
+ -1, /* 0011 -> RESERVED */
+ -1, /* 0100 -> RESERVED */
+ 35, /* 0101 -> 3.5x */
+ 45, /* 0110 -> 4.5x */
+ 55, /* 0111 -> 5.5x */
+ 60, /* 1000 -> 6.0x */
+ 70, /* 1001 -> 7.0x */
+ 80, /* 1010 -> 8.0x */
+ 50, /* 1011 -> 5.0x */
+ 65, /* 1100 -> 6.5x */
+ 75, /* 1101 -> 7.5x */
+ -1, /* 1110 -> RESERVED */
+ -1, /* 1111 -> RESERVED */
+};
+
+static int __initdata samuel1_eblcr[16] = {
+ 50, /* 0000 -> RESERVED */
+ 30, /* 0001 -> 3.0x */
+ 40, /* 0010 -> 4.0x */
+ -1, /* 0011 -> RESERVED */
+ 55, /* 0100 -> 5.5x */
+ 35, /* 0101 -> 3.5x */
+ 45, /* 0110 -> 4.5x */
+ -1, /* 0111 -> RESERVED */
+ -1, /* 1000 -> RESERVED */
+ 70, /* 1001 -> 7.0x */
+ 80, /* 1010 -> 8.0x */
+ 60, /* 1011 -> 6.0x */
+ -1, /* 1100 -> RESERVED */
+ 75, /* 1101 -> 7.5x */
+ -1, /* 1110 -> RESERVED */
+ 65, /* 1111 -> 6.5x */
+};
+
+/* VIA C3 Samuel2 Stepping 1->15 & VIA C3 Ezra */
+static int __initdata longhaul2_clock_ratio[16] = {
+ 100, /* 0000 -> 10.0x */
+ 30, /* 0001 -> 3.0x */
+ 40, /* 0010 -> 4.0x */
+ 90, /* 0011 -> 9.0x */
+ 95, /* 0100 -> 9.5x */
+ 35, /* 0101 -> 3.5x */
+ 45, /* 0110 -> 4.5x */
+ 55, /* 0111 -> 5.5x */
+ 60, /* 1000 -> 6.0x */
+ 70, /* 1001 -> 7.0x */
+ 80, /* 1010 -> 8.0x */
+ 50, /* 1011 -> 5.0x */
+ 65, /* 1100 -> 6.5x */
+ 75, /* 1101 -> 7.5x */
+ 85, /* 1110 -> 8.5x */
+ 120, /* 1111 -> 12.0x */
+};
+
+static int __initdata samuel2_eblcr[16] = {
+ 50, /* 0000 -> 5.0x */
+ 30, /* 0001 -> 3.0x */
+ 40, /* 0010 -> 4.0x */
+ 100, /* 0011 -> 10.0x */
+ 55, /* 0100 -> 5.5x */
+ 35, /* 0101 -> 3.5x */
+ 45, /* 0110 -> 4.5x */
+ 110, /* 0111 -> 11.0x */
+ 90, /* 1000 -> 9.0x */
+ 70, /* 1001 -> 7.0x */
+ 80, /* 1010 -> 8.0x */
+ 60, /* 1011 -> 6.0x */
+ 120, /* 1100 -> 12.0x */
+ 75, /* 1101 -> 7.5x */
+ 130, /* 1110 -> 13.0x */
+ 65, /* 1111 -> 6.5x */
+};
+
+static int __initdata ezra_eblcr[16] = {
+ 50, /* 0000 -> 5.0x */
+ 30, /* 0001 -> 3.0x */
+ 40, /* 0010 -> 4.0x */
+ 100, /* 0011 -> 10.0x */
+ 55, /* 0100 -> 5.5x */
+ 35, /* 0101 -> 3.5x */
+ 45, /* 0110 -> 4.5x */
+ 95, /* 0111 -> 9.5x */
+ 90, /* 1000 -> 9.0x */
+ 70, /* 1001 -> 7.0x */
+ 80, /* 1010 -> 8.0x */
+ 60, /* 1011 -> 6.0x */
+ 120, /* 1100 -> 12.0x */
+ 75, /* 1101 -> 7.5x */
+ 85, /* 1110 -> 8.5x */
+ 65, /* 1111 -> 6.5x */
+};
+
+/* VIA C5M. */
+static int __initdata longhaul3_clock_ratio[32] = {
+ 100, /* 0000 -> 10.0x */
+ 30, /* 0001 -> 3.0x */
+ 40, /* 0010 -> 4.0x */
+ 90, /* 0011 -> 9.0x */
+ 95, /* 0100 -> 9.5x */
+ 35, /* 0101 -> 3.5x */
+ 45, /* 0110 -> 4.5x */
+ 55, /* 0111 -> 5.5x */
+ 60, /* 1000 -> 6.0x */
+ 70, /* 1001 -> 7.0x */
+ 80, /* 1010 -> 8.0x */
+ 50, /* 1011 -> 5.0x */
+ 65, /* 1100 -> 6.5x */
+ 75, /* 1101 -> 7.5x */
+ 85, /* 1110 -> 8.5x */
+ 120, /* 1111 -> 12.0x */
+
+ -1, /* 0000 -> RESERVED (10.0x) */
+ 110, /* 0001 -> 11.0x */
+ 120, /* 0010 -> 12.0x */
+ -1, /* 0011 -> RESERVED (9.0x)*/
+ 105, /* 0100 -> 10.5x */
+ 115, /* 0101 -> 11.5x */
+ 125, /* 0110 -> 12.5x */
+ 135, /* 0111 -> 13.5x */
+ 140, /* 1000 -> 14.0x */
+ 150, /* 1001 -> 15.0x */
+ 160, /* 1010 -> 16.0x */
+ 130, /* 1011 -> 13.0x */
+ 145, /* 1100 -> 14.5x */
+ 155, /* 1101 -> 15.5x */
+ -1, /* 1110 -> RESERVED (13.0x) */
+ -1, /* 1111 -> RESERVED (12.0x) */
+};
+
+static int __initdata c5m_eblcr[32] = {
+ 50, /* 0000 -> 5.0x */
+ 30, /* 0001 -> 3.0x */
+ 40, /* 0010 -> 4.0x */
+ 100, /* 0011 -> 10.0x */
+ 55, /* 0100 -> 5.5x */
+ 35, /* 0101 -> 3.5x */
+ 45, /* 0110 -> 4.5x */
+ 95, /* 0111 -> 9.5x */
+ 90, /* 1000 -> 9.0x */
+ 70, /* 1001 -> 7.0x */
+ 80, /* 1010 -> 8.0x */
+ 60, /* 1011 -> 6.0x */
+ 120, /* 1100 -> 12.0x */
+ 75, /* 1101 -> 7.5x */
+ 85, /* 1110 -> 8.5x */
+ 65, /* 1111 -> 6.5x */
+
+ -1, /* 0000 -> RESERVED (9.0x) */
+ 110, /* 0001 -> 11.0x */
+ 120, /* 0010 -> 12.0x */
+ -1, /* 0011 -> RESERVED (10.0x)*/
+ 135, /* 0100 -> 13.5x */
+ 115, /* 0101 -> 11.5x */
+ 125, /* 0110 -> 12.5x */
+ 105, /* 0111 -> 10.5x */
+ 130, /* 1000 -> 13.0x */
+ 150, /* 1001 -> 15.0x */
+ 160, /* 1010 -> 16.0x */
+ 140, /* 1011 -> 14.0x */
+ -1, /* 1100 -> RESERVED (12.0x) */
+ 155, /* 1101 -> 15.5x */
+ -1, /* 1110 -> RESERVED (13.0x) */
+ 145, /* 1111 -> 14.5x */
+};
+
+/* fsb values as defined in CPU */
+static unsigned int eblcr_fsb_table[] = { 66, 133, 100, -1 };
+/* fsb values to favour low fsb speed (lower power) */
+static unsigned int power_fsb_table[] = { 66, 100, 133, -1 };
+/* fsb values to favour high fsb speed (for e.g. if lowering CPU
+ freq because of heat, but want to maintain highest performance possible) */
+static unsigned int perf_fsb_table[] = { 133, 100, 66, -1 };
+
+/* Voltage scales. Div by 1000 to get actual voltage. */
+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,
+};
+
+/* Clock ratios multiplied by 10 */
+static int clock_ratio[32];
+static int eblcr_table[32];
+static int voltage_table[32];
+static int highest_speed, lowest_speed; /* kHz */
+static int longhaul; /* version. */
+static struct cpufreq_frequency_table *longhaul_table;
+
+
+static int longhaul_get_cpu_fsb (void)
+{
+ unsigned long invalue=0,lo, hi;
+
+ if (current_fsb == 0) {
+ rdmsr (MSR_IA32_EBL_CR_POWERON, lo, hi);
+ invalue = (lo & (1<<18|1<<19)) >>18;
+ return eblcr_fsb_table[invalue];
+ } else {
+ return current_fsb;
+ }
+}
+
+
+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==3) {
+ if (lo & (1<<27))
+ invalue+=16;
+ }
+ return eblcr_table[invalue];
+}
+
+
+/**
+ * longhaul_set_cpu_frequency()
+ * @clock_ratio_index : index of clock_ratio[] for new frequency
+ * @newfsb: the new FSB
+ *
+ * Sets a new clock ratio, and -if applicable- a new Front Side Bus
+ */
+
+static void longhaul_setstate (unsigned int clock_ratio_index, unsigned int newfsb)
+{
+ unsigned long lo, hi;
+ unsigned int bits;
+ int revkey;
+ int vidindex, i;
+ struct cpufreq_freqs freqs;
+
+ if (!newfsb || (clock_ratio[clock_ratio_index] == -1))
+ return;
+
+ if ((!can_scale_fsb) && (newfsb != current_fsb))
+ return;
+
+ if (((clock_ratio[clock_ratio_index] * newfsb * 100) > highest_speed) ||
+ ((clock_ratio[clock_ratio_index] * newfsb * 100) < lowest_speed))
+ return;
+
+ freqs.old = longhaul_get_cpu_mult() * longhaul_get_cpu_fsb() * 100;
+ freqs.new = clock_ratio[clock_ratio_index] * newfsb * 100;
+ freqs.cpu = 0; /* longhaul.c is UP only driver */
+
+ cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
+
+ dprintk (KERN_INFO "longhaul: New FSB:%d Mult(x10):%d\n",
+ newfsb, clock_ratio[clock_ratio_index]);
+
+ bits = clock_ratio_index;
+ /* "bits" contains the bitpattern of the new multiplier.
+ we now need to transform it to the desired format. */
+
+ switch (longhaul) {
+ case 1:
+ rdmsr (MSR_VIA_BCR2, lo, hi);
+ revkey = (lo & 0xf)<<4; /* Rev key. */
+ lo &= ~(1<<23|1<<24|1<<25|1<<26);
+ lo |= (1<<19); /* Enable software clock multiplier */
+ lo |= (bits<<23); /* desired multiplier */
+ lo |= revkey;
+ wrmsr (MSR_VIA_BCR2, lo, hi);
+
+ __hlt();
+
+ /* Disable software clock multiplier */
+ rdmsr (MSR_VIA_BCR2, lo, hi);
+ lo &= ~(1<<19);
+ lo |= revkey;
+ wrmsr (MSR_VIA_BCR2, lo, hi);
+ break;
+
+ case 2:
+ rdmsr (MSR_VIA_LONGHAUL, lo, hi);
+ revkey = (lo & 0xf)<<4; /* Rev key. */
+ lo &= 0xfff0bf0f; /* reset [19:16,14](bus ratio) and [7:4](rev key) to 0 */
+ lo |= (bits<<16);
+ lo |= (1<<8); /* EnableSoftBusRatio */
+ lo |= revkey;
+
+ if (can_scale_voltage) {
+ if (can_scale_fsb==1) {
+ dprintk (KERN_INFO "longhaul: Voltage scaling + FSB scaling not done yet.\n");
+ goto bad_voltage;
+ } else {
+ /* PB: TODO fix this up */
+ vidindex = (((highest_speed-lowest_speed) / (newfsb/2)) -
+ ((highest_speed-((clock_ratio[clock_ratio_index] * newfsb * 100)/1000)) / (newfsb/2)));
+ }
+ for (i=0;i<32;i++) {
+ dprintk (KERN_INFO "VID hunting. Looking for %d, found %d\n",
+ minvid+(vidindex*25), voltage_table[i]);
+ if (voltage_table[i]==(minvid + (vidindex * 25)))
+ break;
+ }
+ if (i==32)
+ goto bad_voltage;
+
+ dprintk (KERN_INFO "longhaul: Desired vid index=%d\n", i);
+#if 0
+ lo &= 0xfe0fffff;/* reset [24:20](voltage) to 0 */
+ lo |= (i<<20); /* set voltage */
+ lo |= (1<<9); /* EnableSoftVID */
+#endif
+ }
+
+bad_voltage:
+ wrmsr (MSR_VIA_LONGHAUL, lo, hi);
+ __hlt();
+
+ rdmsr (MSR_VIA_LONGHAUL, lo, hi);
+ lo &= ~(1<<8);
+ if (can_scale_voltage)
+ lo &= ~(1<<9);
+ lo |= revkey;
+ wrmsr (MSR_VIA_LONGHAUL, lo, hi);
+ break;
+
+ case 3:
+ rdmsr (MSR_VIA_LONGHAUL, lo, hi);
+ revkey = (lo & 0xf)<<4; /* Rev key. */
+ lo &= 0xfff0bf0f; /* reset longhaul[19:16,14] to 0 */
+ lo |= (bits<<16);
+ lo |= (1<<8); /* EnableSoftBusRatio */
+ lo |= revkey;
+
+ /* Set FSB */
+ if (can_scale_fsb==1) {
+ lo &= ~(1<<28|1<<29);
+ switch (newfsb) {
+ case 66: lo |= (1<<28|1<<29); /* 11 */
+ break;
+ case 100: lo |= 1<<28; /* 01 */
+ break;
+ case 133: break; /* 00*/
+ }
+ }
+ wrmsr (MSR_VIA_LONGHAUL, lo, hi);
+ __hlt();
+
+ rdmsr (MSR_VIA_LONGHAUL, lo, hi);
+ lo &= ~(1<<8);
+ lo |= revkey;
+ wrmsr (MSR_VIA_LONGHAUL, lo, hi);
+ break;
+ }
+
+ cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
+}
+
+
+static int __init longhaul_get_ranges (void)
+{
+ unsigned long lo, hi, invalue;
+ unsigned int minmult=0, maxmult=0, minfsb=0, maxfsb=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 fsb_table[4] = { 133, 100, -1, 66 };
+ unsigned int fsbcount = 1;
+ unsigned int i, j, k = 0;
+ static unsigned int *fsb_search_table;
+
+ switch (longhaul) {
+ 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();
+ minfsb = maxfsb = current_fsb;
+ break;
+
+ case 2 ... 3:
+ rdmsr (MSR_VIA_LONGHAUL, lo, hi);
+
+ invalue = (hi & (1<<0|1<<1|1<<2|1<<3));
+ if (hi & (1<<11))
+ invalue += 16;
+ maxmult=multipliers[invalue];
+
+#if 0 /* This is MaxMhz @ Min Voltage. Ignore for now */
+ invalue = (hi & (1<<16|1<<17|1<<18|1<<19)) >> 16;
+ if (hi & (1<<27))
+ invalue += 16;
+ minmult = multipliers[invalue];
+#else
+ minmult = 30; /* as per spec */
+#endif
+
+ if (can_scale_fsb==1) {
+ invalue = (hi & (1<<9|1<<10)) >> 9;
+ maxfsb = fsb_table[invalue];
+
+ invalue = (hi & (1<<25|1<<26)) >> 25;
+ minfsb = fsb_table[invalue];
+
+ dprintk (KERN_INFO "longhaul: Min FSB=%d Max FSB=%d\n",
+ minfsb, maxfsb);
+ fsbcount = 0;
+ for (i=0;i<4;i++) {
+ if((fsb_table[i] >= minfsb) && (fsb_table[i] <= maxfsb))
+ fsbcount++;
+ }
+ } else {
+ minfsb = maxfsb = current_fsb;
+ }
+ break;
+ }
+
+ highest_speed = maxmult * maxfsb * 100;
+ lowest_speed = minmult * minfsb * 100;
+ dprintk (KERN_INFO "longhaul: MinMult(x10)=%d MaxMult(x10)=%d\n",
+ minmult, maxmult);
+ dprintk (KERN_INFO "longhaul: Lowestspeed=%d Highestspeed=%d\n",
+ lowest_speed, highest_speed);
+
+ longhaul_table = kmalloc((numscales * fsbcount + 1) * sizeof(struct cpufreq_frequency_table), GFP_KERNEL);
+ if(!longhaul_table)
+ return -ENOMEM;
+
+ if (prefer_slow_fsb)
+ fsb_search_table = perf_fsb_table; // yep, this is right: the last entry is preferred by cpufreq_frequency_table_* ...
+ else
+ fsb_search_table = power_fsb_table;
+
+ for (i=0; (i<4); i++) {
+ if ((fsb_search_table[i] > maxfsb) || (fsb_search_table[i] < minfsb) || (fsb_search_table[i] == -1))
+ continue;
+ for (j=0; (j maxmult) || (clock_ratio[j] < minmult) || (clock_ratio[j] == -1))
+ continue;
+ longhaul_table[k].frequency= clock_ratio[j] * fsb_search_table[i] * 100;
+ longhaul_table[k].index = (j << 8) | (i);
+ k++;
+ }
+ }
+
+ longhaul_table[k].frequency = CPUFREQ_TABLE_END;
+ if (!k)
+ return -EINVAL;
+
+ return 0;
+}
+
+
+static void __init longhaul_setup_voltagescaling (unsigned long lo, unsigned long hi)
+{
+ int revkey;
+
+ can_scale_voltage = 1;
+
+ minvid = (hi & (1<<20|1<<21|1<<22|1<<23|1<<24)) >> 20; /* 56:52 */
+ maxvid = (hi & (1<<4|1<<5|1<<6|1<<7|1<<8)) >> 4; /* 40:36 */
+ vrmrev = (lo & (1<<15))>>15;
+
+ if (vrmrev==0) {
+ dprintk (KERN_INFO "longhaul: VRM 8.5 : ");
+ memcpy (voltage_table, vrm85scales, sizeof(voltage_table));
+ numvscales = (voltage_table[maxvid]-voltage_table[minvid])/25;
+ } else {
+ dprintk (KERN_INFO "longhaul: 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 */
+ revkey = (lo & 0xf)<<4; /* Rev key. */
+ lo &= 0xfe0fff0f; /* Mask unneeded bits */
+ lo |= (1<<9); /* EnableSoftVID */
+ lo |= revkey; /* Reinsert key */
+ lo |= maxvid << 20;
+ wrmsr (MSR_VIA_LONGHAUL, lo, hi);
+ 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);
+}
+
+
+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_fsb = 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;
+ new_fsb = power_fsb_table[(longhaul_table[table_index].index & 0xFF00) >> 8];
+
+ longhaul_setstate(new_clock_ratio, new_fsb);
+
+ return 0;
+}
+
+
+static int longhaul_cpu_init (struct cpufreq_policy *policy)
+{
+ struct cpuinfo_x86 *c = cpu_data;
+
+ if ((c->x86_vendor != X86_VENDOR_CENTAUR) || (c->x86 !=6) )
+ return -ENODEV;
+
+ switch (c->x86_model) {
+ case 6: /* VIA C3 Samuel C5A */
+ longhaul=1;
+ memcpy (clock_ratio, longhaul1_clock_ratio, sizeof(longhaul1_clock_ratio));
+ memcpy (eblcr_table, samuel1_eblcr, sizeof(samuel1_eblcr));
+ break;
+
+ case 7: /* C5B / C5C */
+ switch (c->x86_mask) {
+ case 0:
+ longhaul=1;
+ memcpy (clock_ratio, longhaul1_clock_ratio, sizeof(longhaul1_clock_ratio));
+ memcpy (eblcr_table, samuel2_eblcr, sizeof(samuel2_eblcr));
+ break;
+ case 1 ... 15:
+ longhaul=2;
+ memcpy (clock_ratio, longhaul2_clock_ratio, sizeof(longhaul2_clock_ratio));
+ memcpy (eblcr_table, ezra_eblcr, sizeof(ezra_eblcr));
+ break;
+ }
+ break;
+
+ case 8: /* C5M/C5N */
+ return -ENODEV; // Waiting on updated docs from VIA before this is usable
+ longhaul=3;
+ numscales=32;
+ memcpy (clock_ratio, longhaul3_clock_ratio, sizeof(longhaul3_clock_ratio));
+ memcpy (eblcr_table, c5m_eblcr, sizeof(c5m_eblcr));
+ break;
+
+ }
+
+ printk (KERN_INFO "longhaul: VIA CPU detected. Longhaul version %d supported\n", longhaul);
+
+ if (longhaul==2 || longhaul==3) {
+ unsigned long lo, hi;
+ rdmsr (MSR_VIA_LONGHAUL, lo, hi);
+ if ((lo & (1<<0)) && (dont_scale_voltage==0))
+ longhaul_setup_voltagescaling (lo, hi);
+
+ if ((lo & (1<<1)) && (dont_scale_fsb==0) && (current_fsb==0))
+ can_scale_fsb = 1;
+ }
+
+ if (longhaul_get_ranges())
+ return -ENOMEM;
+
+ policy->policy = CPUFREQ_POLICY_PERFORMANCE;
+ policy->cpuinfo.transition_latency = CPUFREQ_ETERNAL;
+ policy->cur = (unsigned int) (longhaul_get_cpu_fsb() * longhaul_get_cpu_mult() * 100);
+
+ return cpufreq_frequency_table_cpuinfo(policy, longhaul_table);
+}
+
+static struct cpufreq_driver longhaul_driver = {
+ .verify = longhaul_verify,
+ .target = longhaul_target,
+ .init = longhaul_cpu_init,
+ .name = "longhaul",
+};
+
+static int __init longhaul_init (void)
+{
+ struct cpuinfo_x86 *c = cpu_data;
+
+ if ((c->x86_vendor != X86_VENDOR_CENTAUR) || (c->x86 !=6) )
+ return -ENODEV;
+
+ switch (c->x86_model) {
+ case 6 ... 7:
+ return cpufreq_register_driver(&longhaul_driver);
+ case 8:
+ return -ENODEV;
+ default:
+ printk (KERN_INFO "longhaul: Unknown VIA CPU. Contact davej@suse.de\n");
+ }
+
+ return -ENODEV;
+}
+
+static void __exit longhaul_exit (void)
+{
+ cpufreq_unregister_driver(&longhaul_driver);
+ kfree(longhaul_table);
+}
+
+MODULE_PARM (dont_scale_fsb, "i");
+MODULE_PARM (dont_scale_voltage, "i");
+MODULE_PARM (current_fsb, "i");
+MODULE_PARM (prefer_slow_fsb, "i");
+
+MODULE_AUTHOR ("Dave Jones ");
+MODULE_DESCRIPTION ("Longhaul driver for VIA Cyrix processors.");
+MODULE_LICENSE ("GPL");
+
+module_init(longhaul_init);
+module_exit(longhaul_exit);
+
diff -urNp --exclude CVS --exclude BitKeeper xx-ref/arch/i386/kernel/longrun.c xx/arch/i386/kernel/longrun.c
--- xx-ref/arch/i386/kernel/longrun.c 1970-01-01 01:00:00.000000000 +0100
+++ xx/arch/i386/kernel/longrun.c 2003-06-07 13:33:35.000000000 +0200
@@ -0,0 +1,292 @@
+/*
+ * $Id: longrun.c,v 1.21 2003/01/20 17:31:47 db Exp $
+ *
+ * (C) 2002 - 2003 Dominik Brodowski
+ *
+ * Licensed under the terms of the GNU GPL License version 2.
+ *
+ * BIG FAT DISCLAIMER: Work in progress code. Possibly *dangerous*
+ */
+
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+#include
+
+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)
+ policy->policy = longrun_driver.policy[0].policy;
+
+ 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 neccessary 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;
+ struct cpuinfo_x86 *c = cpu_data;
+
+ /* capability check */
+ if (policy->cpu != 0)
+ return -ENODEV;
+ if (c->x86_vendor != X86_VENDOR_TRANSMETA ||
+ !cpu_has(c, X86_FEATURE_LONGRUN))
+ 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,
+ .exit = NULL,
+ .policy = NULL,
+ .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 ");
+MODULE_DESCRIPTION ("LongRun driver for Transmeta Crusoe processors.");
+MODULE_LICENSE ("GPL");
+
+module_init(longrun_init);
+module_exit(longrun_exit);
diff -urNp --exclude CVS --exclude BitKeeper xx-ref/arch/i386/kernel/p4-clockmod.c xx/arch/i386/kernel/p4-clockmod.c
--- xx-ref/arch/i386/kernel/p4-clockmod.c 1970-01-01 01:00:00.000000000 +0100
+++ xx/arch/i386/kernel/p4-clockmod.c 2003-06-07 13:33:35.000000000 +0200
@@ -0,0 +1,243 @@
+/*
+ * Pentium 4/Xeon CPU on demand clock modulation/speed scaling
+ * (C) 2002 - 2003 Dominik Brodowski
+ * (C) 2002 Zwane Mwaikambo
+ * (C) 2002 Arjan van de Ven
+ * (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
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+#include
+
+#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;
+ struct cpufreq_freqs freqs;
+
+ if (!cpu_online(cpu) || (newstate > DC_DISABLE) ||
+ (newstate == DC_RESV))
+ return -EINVAL;
+
+ /* get current state */
+ rdmsr(MSR_IA32_THERM_CONTROL, l, h);
+ if (l & 0x10) {
+ l = l >> 1;
+ l &= 0x7;
+ } else
+ l = DC_DISABLE;
+
+ /* notifiers */
+ freqs.old = stock_freq * l / 8;
+ freqs.new = stock_freq * newstate / 8;
+ freqs.cpu = cpu;
+ cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
+
+ rdmsr(MSR_IA32_THERM_STATUS, l, h);
+ if (l & 0x01)
+// printk(KERN_DEBUG PFX "CPU#%d currently thermal throttled\n", cpu);
+
+ 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);
+ }
+
+ /* notifiers */
+ 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;
+
+ /* capability check */
+ 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;
+
+ /* 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,
+ .setpolicy = NULL,
+ .target = cpufreq_p4_target,
+ .init = cpufreq_p4_cpu_init,
+ .exit = cpufreq_p4_cpu_exit,
+ .policy = NULL,
+ .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 ");
+MODULE_DESCRIPTION ("cpufreq driver for Pentium(TM) 4/Xeon(TM)");
+MODULE_LICENSE ("GPL");
+
+module_init(cpufreq_p4_init);
+module_exit(cpufreq_p4_exit);
+
diff -urNp --exclude CVS --exclude BitKeeper xx-ref/arch/i386/kernel/powernow-k6.c xx/arch/i386/kernel/powernow-k6.c
--- xx-ref/arch/i386/kernel/powernow-k6.c 1970-01-01 01:00:00.000000000 +0100
+++ xx/arch/i386/kernel/powernow-k6.c 2003-06-07 13:33:35.000000000 +0200
@@ -0,0 +1,239 @@
+/*
+ * $Id: powernow-k6.c,v 1.46 2003/01/20 17:31:47 db Exp $
+ * This file was part of Powertweak Linux (http://powertweak.sf.net)
+ * and is shared with the Linux Kernel module.
+ *
+ * (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
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+#include
+
+
+#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)
+{
+ struct cpuinfo_x86 *c = cpu_data;
+ unsigned int i;
+
+ /* capability check */
+ if ((c->x86_vendor != X86_VENDOR_AMD) || (c->x86 != 5) ||
+ ((c->x86_model != 12) && (c->x86_model != 13)))
+ return -ENODEV;
+ 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,
+ .policy = NULL,
+ .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 , Dave Jones , Dominik Brodowski ");
+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 -urNp --exclude CVS --exclude BitKeeper xx-ref/arch/i386/kernel/powernow-k7.c xx/arch/i386/kernel/powernow-k7.c
--- xx-ref/arch/i386/kernel/powernow-k7.c 1970-01-01 01:00:00.000000000 +0100
+++ xx/arch/i386/kernel/powernow-k7.c 2003-06-07 13:33:35.000000000 +0200
@@ -0,0 +1,403 @@
+/*
+ * $Id: powernow-k7.c,v 1.31 2003/02/12 21:16:35 davej Exp $
+ *
+ * (C) 2003 Dave Jones
+ *
+ * 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
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+#include
+
+#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;
+
+
+#ifndef rdmsrl
+#define rdmsrl(msr,val) do {unsigned long l__,h__; \
+ rdmsr (msr, l__, h__); \
+ val = l__; \
+ val |= ((u64)h__<<32); \
+} while(0);
+#endif
+
+#ifndef wrmsrl
+static void wrmsrl (u32 msr, u64 val)
+{
+ u32 lo, hi;
+
+ lo = (u32) val;
+ hi = val >> 32;
+ wrmsr (msr, lo, hi);
+}
+#endif
+
+
+
+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)
+{
+ int j;
+ u8 fid, vid;
+ unsigned int speed;
+
+ 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_speed (unsigned int index)
+{
+ u8 fid, vid;
+ struct cpufreq_freqs freqs;
+ union msr_fidvidstatus fidvidstatus;
+ union msr_fidvidctl fidvidctl;
+
+
+ /* 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);
+ freqs.old = fsb * fid_codes[fidvidstatus.bits.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 */
+ __asm__("\tcli\n");
+ rdmsrl (MSR_K7_FID_VID_CTL, fidvidctl.val);
+ fidvidctl.bits.SGTC = latency; /* Stop grant timeout counter */
+ fidvidctl.bits.FID = fid;
+ fidvidctl.bits.VID = vid;
+ /* Note, we could set these lazily. Ie, only do voltage transition
+ if its changed since last time (Some speeds have the same voltage) */
+ fidvidctl.bits.FIDC = 1;
+ fidvidctl.bits.VIDC = 1;
+ wrmsrl (MSR_K7_FID_VID_CTL, fidvidctl.val);
+ if (have_a0 == 1)
+ __asm__("\tsti\n");
+
+ cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
+}
+
+
+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;
+ 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);
+
+ p += sizeof (struct psb_s);
+
+ pst = (struct pst_s *) p;
+
+ for (i = 0 ; i 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;
+ }
+ }
+ 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 ");
+MODULE_DESCRIPTION ("Powernow driver for AMD K7 processors.");
+MODULE_LICENSE ("GPL");
+
+module_init(powernow_init);
+module_exit(powernow_exit);
+
diff -urNp --exclude CVS --exclude BitKeeper xx-ref/arch/i386/kernel/powernow-k7.h xx/arch/i386/kernel/powernow-k7.h
--- xx-ref/arch/i386/kernel/powernow-k7.h 1970-01-01 01:00:00.000000000 +0100
+++ xx/arch/i386/kernel/powernow-k7.h 2003-06-07 13:33:35.000000000 +0200
@@ -0,0 +1,52 @@
+/*
+ * $Id: powernow-k7.h,v 1.2 2003/02/10 18:26:01 davej Exp $
+ * (C) 2003 Dave Jones.
+ *
+ * Licensed under the terms of the GNU GPL License version 2.
+ *
+ * AMD-specific information
+ *
+ */
+
+#ifndef MSR_K7_FID_VID_CTL
+#define MSR_K7_FID_VID_CTL 0xc0010041
+#endif
+#ifndef MSR_K7_FID_VID_STATUS
+#define MSR_K7_FID_VID_STATUS 0xc0010042
+#endif
+
+
+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 -urNp --exclude CVS --exclude BitKeeper xx-ref/arch/i386/kernel/speedstep.c xx/arch/i386/kernel/speedstep.c
--- xx-ref/arch/i386/kernel/speedstep.c 1970-01-01 01:00:00.000000000 +0100
+++ xx/arch/i386/kernel/speedstep.c 2003-06-07 13:33:35.000000000 +0200
@@ -0,0 +1,713 @@
+/*
+ * $Id: speedstep.c,v 1.70 2003/02/22 10:23:46 db Exp $
+ *
+ * (C) 2001 Dave Jones, Arjan van de ven.
+ * (C) 2002 - 2003 Dominik Brodowski
+ *
+ * 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
+#include
+#include
+#include
+#include
+#include
+
+#include
+
+
+/* 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 unsigned int speedstep_chipset;
+static struct pci_dev *speedstep_chipset_dev;
+
+#define SPEEDSTEP_CHIPSET_ICH2M 0x00000002
+#define SPEEDSTEP_CHIPSET_ICH3M 0x00000003
+
+
+/* speedstep_processor
+ */
+static unsigned int speedstep_processor = 0;
+static int speedstep_coppermine = 0;
+
+#define SPEEDSTEP_PROCESSOR_PIII_C 0x00000001 /* Coppermine core */
+#define SPEEDSTEP_PROCESSOR_PIII_T 0x00000002 /* Tualatin core */
+#define SPEEDSTEP_PROCESSOR_P4M 0x00000003 /* P4-M with 100 MHz FSB */
+
+
+/* speedstep_[low,high]_freq
+ * There are only two frequency states for each processor. Values
+ * are in kHz for the time being.
+ */
+#define SPEEDSTEP_HIGH 0x00000000
+#define SPEEDSTEP_LOW 0x00000001
+
+static struct cpufreq_frequency_table speedstep_freqs[] = {
+ {SPEEDSTEP_HIGH, 0},
+ {SPEEDSTEP_LOW, 0},
+ {0, CPUFREQ_TABLE_END},
+};
+
+#define speedstep_low_freq speedstep_freqs[SPEEDSTEP_LOW].frequency
+#define speedstep_high_freq speedstep_freqs[SPEEDSTEP_HIGH].frequency
+
+
+/* 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
+
+
+
+/*********************************************************************
+ * LOW LEVEL CHIPSET INTERFACE *
+ *********************************************************************/
+
+/**
+ * speedstep_get_state - read the current SpeedStep state
+ * @state: Speedstep state (SPEEDSTEP_LOW or SPEEDSTEP_HIGH)
+ *
+ * Tries to read the SpeedStep state. Returns -EIO when there has been
+ * trouble to read the status or write to the control register, -EINVAL
+ * on an unsupported chipset, and zero on success.
+ */
+static int speedstep_get_state (unsigned int *state)
+{
+ unsigned long flags;
+ u32 pmbase;
+ u8 value;
+
+ if (!speedstep_chipset_dev || !state)
+ return -EINVAL;
+
+ switch (speedstep_chipset) {
+ case SPEEDSTEP_CHIPSET_ICH2M:
+ case SPEEDSTEP_CHIPSET_ICH3M:
+ /* get PMBASE */
+ pci_read_config_dword(speedstep_chipset_dev, 0x40, &pmbase);
+ if (!(pmbase & 0x01))
+ return -EIO;
+
+ pmbase &= 0xFFFFFFFE;
+ if (!pmbase)
+ return -EIO;
+
+ /* read state */
+ local_irq_save(flags);
+ value = inb(pmbase + 0x50);
+ local_irq_restore(flags);
+
+ dprintk(KERN_DEBUG "cpufreq: read at pmbase 0x%x + 0x50 returned 0x%x\n", pmbase, value);
+
+ *state = value & 0x01;
+ return 0;
+
+ }
+
+ printk (KERN_ERR "cpufreq: setting CPU frequency on this chipset unsupported.\n");
+ return -EINVAL;
+}
+
+
+/**
+ * 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, int notify)
+{
+ u32 pmbase;
+ u8 pm2_blk;
+ u8 value;
+ unsigned long flags;
+ unsigned int oldstate;
+ struct cpufreq_freqs freqs;
+
+ if (!speedstep_chipset_dev || (state > 0x1))
+ return;
+
+ if (speedstep_get_state(&oldstate))
+ return;
+
+ if (oldstate == state)
+ return;
+
+ freqs.old = (oldstate == SPEEDSTEP_HIGH) ? speedstep_high_freq : speedstep_low_freq;
+ freqs.new = (state == SPEEDSTEP_HIGH) ? speedstep_high_freq : speedstep_low_freq;
+ freqs.cpu = 0; /* speedstep.c is UP only driver */
+
+ if (notify)
+ cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
+
+ switch (speedstep_chipset) {
+ case SPEEDSTEP_CHIPSET_ICH2M:
+ case SPEEDSTEP_CHIPSET_ICH3M:
+ /* 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", (freqs.new / 1000));
+ } else {
+ printk (KERN_ERR "cpufreq: change failed - I/O error\n");
+ }
+ break;
+ default:
+ printk (KERN_ERR "cpufreq: setting CPU frequency on this chipset unsupported.\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)
+{
+ if (!speedstep_chipset_dev)
+ return -EINVAL;
+
+ switch (speedstep_chipset) {
+ case SPEEDSTEP_CHIPSET_ICH2M:
+ case SPEEDSTEP_CHIPSET_ICH3M:
+ {
+ u16 value = 0;
+
+ 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;
+ }
+ }
+
+ printk (KERN_ERR "cpufreq: SpeedStep (TM) on this chipset unsupported.\n");
+ return -EINVAL;
+}
+
+
+/**
+ * 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_82801CA_12,
+ PCI_ANY_ID,
+ PCI_ANY_ID,
+ NULL);
+ if (speedstep_chipset_dev)
+ return SPEEDSTEP_CHIPSET_ICH3M;
+
+
+ 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 SPEEDSTEP_CHIPSET_ICH2M;
+
+ 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 SPEEDSTEP_CHIPSET_ICH2M;
+ }
+
+ return 0;
+}
+
+
+
+/*********************************************************************
+ * LOW LEVEL PROCESSOR INTERFACE *
+ *********************************************************************/
+
+
+/**
+ * pentium3_get_frequency - get the core frequencies for PIIIs
+ *
+ * Returns the core frequency of a Pentium III processor (in kHz)
+ */
+static unsigned int pentium3_get_frequency (void)
+{
+ /* 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;
+ struct cpuinfo_x86 *c = cpu_data;
+
+ /* read MSR 0x2a - we only need the low 32 bits */
+ rdmsr(MSR_IA32_EBL_CR_POWERON, msr_lo, msr_tmp);
+ dprintk(KERN_DEBUG "cpufreq: 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 -EINVAL;
+ i++;
+ }
+
+ /* decode the multiplier */
+ if ((c->x86_model == 0x08) && (c->x86_mask == 0x01))
+ /* different on early Coppermine PIII */
+ 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 -EINVAL;
+ j++;
+ }
+
+ return (msr_decode_mult[j].ratio * msr_decode_fsb[i].value * 100);
+}
+
+
+/**
+ * pentium4_get_frequency - get the core frequency for P4-Ms
+ *
+ * Should return the core frequency (in kHz) for P4-Ms.
+ */
+static unsigned int pentium4_get_frequency(void)
+{
+ u32 msr_lo, msr_hi;
+
+ rdmsr(0x2c, msr_lo, msr_hi);
+
+ dprintk(KERN_DEBUG "cpufreq: P4 - MSR_EBC_FREQUENCY_ID: 0x%x 0x%x\n", msr_lo, msr_hi);
+
+ /* First 12 bits seem to change a lot (0x511, 0x410 and 0x30f seen
+ * yet). Next 12 bits always seem to be 0x300. If this is not true
+ * on this CPU, complain. Last 8 bits are frequency (in 100MHz).
+ */
+ if (msr_hi || ((msr_lo & 0x00FFF000) != 0x300000)) {
+ printk(KERN_DEBUG "cpufreq: P4 - MSR_EBC_FREQUENCY_ID: 0x%x 0x%x\n", msr_lo, msr_hi);
+ printk(KERN_INFO "cpufreq: problem in initialization. Please contact Dominik Brodowski\n");
+ printk(KERN_INFO "cpufreq: and attach this dmesg. Thanks in advance\n");
+ return 0;
+ }
+
+ msr_lo >>= 24;
+ return (msr_lo * 100000);
+}
+
+
+/**
+ * speedstep_detect_processor - detect Intel SpeedStep-capable processors.
+ *
+ * Returns the SPEEDSTEP_PROCESSOR_-number for the detected processor,
+ * or zero on failure.
+ */
+static unsigned int speedstep_detect_processor (void)
+{
+ struct cpuinfo_x86 *c = cpu_data;
+ u32 ebx;
+
+ if ((c->x86_vendor != X86_VENDOR_INTEL) ||
+ ((c->x86 != 6) && (c->x86 != 0xF)))
+ return 0;
+
+ if (c->x86 == 0xF) {
+ /* Intel Pentium 4 Mobile P4-M */
+ if (c->x86_model != 2)
+ return 0;
+
+ if ((c->x86_mask != 4) && (c->x86_mask != 7))
+ 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 24540633.pdf of August 2002
+ */
+
+ return SPEEDSTEP_PROCESSOR_PIII_T;
+
+ case 0x08: /* Intel PIII [Coppermine] */
+ {
+ u32 msr_lo, msr_hi;
+
+ /* 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 (speedstep_coppermine)
+ return SPEEDSTEP_PROCESSOR_PIII_C;
+
+ printk(KERN_INFO "cpufreq: in case this is a SpeedStep-capable Intel Pentium III Coppermine\n");
+ printk(KERN_INFO "cpufreq: processor, please pass the boot option or module parameter\n");
+ printk(KERN_INFO "cpufreq: `speedstep_coppermine=1` to the kernel. Thanks!\n");
+ return 0;
+ }
+
+ default:
+ return 0;
+ }
+}
+
+
+
+/*********************************************************************
+ * HIGH LEVEL FUNCTIONS *
+ *********************************************************************/
+
+/**
+ * speedstep_detect_speeds - detects low and high CPU frequencies.
+ *
+ * Detects the low and high CPU frequencies in kHz. Returns 0 on
+ * success or -EINVAL / -EIO on problems.
+ */
+static int speedstep_detect_speeds (void)
+{
+ unsigned long flags;
+ unsigned int state;
+ int i, result;
+
+ /* Disable irqs for entire detection process */
+ local_irq_save(flags);
+
+ for (i=0; i<2; i++) {
+ /* read the current state */
+ result = speedstep_get_state(&state);
+ if (result)
+ return result;
+
+ /* save the correct value, and switch to other */
+ if (state == SPEEDSTEP_LOW) {
+ switch (speedstep_processor) {
+ case SPEEDSTEP_PROCESSOR_PIII_C:
+ case SPEEDSTEP_PROCESSOR_PIII_T:
+ speedstep_low_freq = pentium3_get_frequency();
+ break;
+ case SPEEDSTEP_PROCESSOR_P4M:
+ speedstep_low_freq = pentium4_get_frequency();
+ }
+ speedstep_set_state(SPEEDSTEP_HIGH, 0);
+ } else {
+ switch (speedstep_processor) {
+ case SPEEDSTEP_PROCESSOR_PIII_C:
+ case SPEEDSTEP_PROCESSOR_PIII_T:
+ speedstep_high_freq = pentium3_get_frequency();
+ break;
+ case SPEEDSTEP_PROCESSOR_P4M:
+ speedstep_high_freq = pentium4_get_frequency();
+ }
+ speedstep_set_state(SPEEDSTEP_LOW, 0);
+ }
+ }
+
+ local_irq_restore(flags);
+
+ if (!speedstep_low_freq || !speedstep_high_freq ||
+ (speedstep_low_freq == speedstep_high_freq))
+ return -EIO;
+
+ 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_detect_speeds();
+ if (result)
+ return result;
+
+ /* get current speed setting */
+ result = speedstep_get_state(&speed);
+ if (result)
+ return result;
+
+ speed = (speed == SPEEDSTEP_LOW) ? speedstep_low_freq : speedstep_high_freq;
+ dprintk(KERN_INFO "cpufreq: currently at %s speed setting - %i MHz\n",
+ (speed == speedstep_low_freq) ? "low" : "high",
+ (speed / 1000));
+
+ /* cpuinfo and default policy values */
+ policy->policy = (speed == speedstep_low_freq) ?
+ CPUFREQ_POLICY_POWERSAVE : CPUFREQ_POLICY_PERFORMANCE;
+ policy->cpuinfo.transition_latency = CPUFREQ_ETERNAL;
+ policy->cur = speed;
+
+ return cpufreq_frequency_table_cpuinfo(policy, &speedstep_freqs[0]);
+}
+
+
+#ifndef MODULE
+/**
+ * speedstep_setup speedstep command line parameter parsing
+ *
+ * speedstep command line parameter. Use:
+ * speedstep_coppermine=1
+ * if the CPU in your notebook is a SpeedStep-capable Intel
+ * Pentium III Coppermine. These processors cannot be detected
+ * automatically, as Intel continues to consider the detection
+ * algorithm as proprietary material.
+ */
+static int __init speedstep_setup(char *str)
+{
+ speedstep_coppermine = simple_strtoul(str, &str, 0);
+ return 1;
+}
+__setup("speedstep_coppermine=", speedstep_setup);
+#endif
+
+
+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 chipset */
+ speedstep_chipset = speedstep_detect_chipset();
+
+ /* detect chipset */
+ if (speedstep_chipset)
+ speedstep_processor = speedstep_detect_processor();
+
+ if ((!speedstep_chipset) || (!speedstep_processor)) {
+ printk(KERN_INFO "cpufreq: Intel(R) SpeedStep(TM) for this %s not (yet) available.\n", speedstep_chipset ? "processor" : "chipset");
+ return -ENODEV;
+ }
+
+ dprintk(KERN_INFO "cpufreq: Intel(R) SpeedStep(TM) support $Revision: 1.70 $\n");
+
+ /* 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_PARM (speedstep_coppermine, "i");
+
+MODULE_AUTHOR ("Dave Jones , Dominik Brodowski ");
+MODULE_DESCRIPTION ("Speedstep driver for Intel mobile processors.");
+MODULE_LICENSE ("GPL");
+
+module_init(speedstep_init);
+module_exit(speedstep_exit);
diff -urNp --exclude CVS --exclude BitKeeper xx-ref/arch/i386/kernel/time.c xx/arch/i386/kernel/time.c
--- xx-ref/arch/i386/kernel/time.c 2003-06-04 14:21:11.000000000 +0200
+++ xx/arch/i386/kernel/time.c 2003-06-07 13:33:35.000000000 +0200
@@ -55,6 +55,7 @@
#include
#include
#include
+#include
#include
#include
@@ -833,6 +834,49 @@ bad_ctc:
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;
@@ -901,6 +945,9 @@ void __init time_init(void)
"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 -urNp --exclude CVS --exclude BitKeeper xx-ref/drivers/Makefile xx/drivers/Makefile
--- xx-ref/drivers/Makefile 2003-03-15 03:25:07.000000000 +0100
+++ xx/drivers/Makefile 2003-06-07 13:33:35.000000000 +0200
@@ -8,12 +8,13 @@
mod-subdirs := dio hil mtd sbus video macintosh usb input telephony sgi ide \
message/i2o message/fusion scsi md ieee1394 pnp isdn atm \
- fc4 net/hamradio i2c acpi bluetooth
+ fc4 net/hamradio i2c acpi bluetooth cpufreq
subdir-y := parport char block net sound misc media cdrom hotplug
subdir-m := $(subdir-y)
+subdir-$(CONFIG_CPU_FREQ) += cpufreq
subdir-$(CONFIG_DIO) += dio
subdir-$(CONFIG_PCI) += pci
subdir-$(CONFIG_GSC) += gsc
diff -urNp --exclude CVS --exclude BitKeeper xx-ref/drivers/cpufreq/Makefile xx/drivers/cpufreq/Makefile
--- xx-ref/drivers/cpufreq/Makefile 1970-01-01 01:00:00.000000000 +0100
+++ xx/drivers/cpufreq/Makefile 2003-06-07 13:33:35.000000000 +0200
@@ -0,0 +1,12 @@
+O_TARGET := 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
+
+include $(TOPDIR)/Rules.make
diff -urNp --exclude CVS --exclude BitKeeper xx-ref/drivers/cpufreq/freq_table.c xx/drivers/cpufreq/freq_table.c
--- xx-ref/drivers/cpufreq/freq_table.c 1970-01-01 01:00:00.000000000 +0100
+++ xx/drivers/cpufreq/freq_table.c 2003-06-07 13:33:35.000000000 +0200
@@ -0,0 +1,202 @@
+/*
+ * linux/drivers/cpufreq/freq_table.c
+ *
+ * Copyright (C) 2002 - 2003 Dominik Brodowski
+ */
+
+#include
+#include
+#include
+#include
+#include
+
+/*********************************************************************
+ * 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_setpolicy(struct cpufreq_policy *policy,
+ struct cpufreq_frequency_table *table,
+ unsigned int *index)
+{
+ struct cpufreq_frequency_table optimal = { .index = ~0, };
+ unsigned int i;
+
+ switch (policy->policy) {
+ case CPUFREQ_POLICY_PERFORMANCE:
+ optimal.frequency = 0;
+ break;
+ case CPUFREQ_POLICY_POWERSAVE:
+ optimal.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(policy->policy) {
+ case CPUFREQ_POLICY_PERFORMANCE:
+ if (optimal.frequency <= freq) {
+ optimal.frequency = freq;
+ optimal.index = i;
+ }
+ break;
+ case CPUFREQ_POLICY_POWERSAVE:
+ if (optimal.frequency >= freq) {
+ optimal.frequency = freq;
+ optimal.index = i;
+ }
+ break;
+ }
+ }
+ if (optimal.index > i)
+ return -EINVAL;
+
+ *index = optimal.index;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(cpufreq_frequency_table_setpolicy);
+
+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 ");
+MODULE_DESCRIPTION ("CPUfreq frequency table helpers");
+MODULE_LICENSE ("GPL");
diff -urNp --exclude CVS --exclude BitKeeper xx-ref/drivers/cpufreq/proc_intf.c xx/drivers/cpufreq/proc_intf.c
--- xx-ref/drivers/cpufreq/proc_intf.c 1970-01-01 01:00:00.000000000 +0100
+++ xx/drivers/cpufreq/proc_intf.c 2003-06-07 13:33:35.000000000 +0200
@@ -0,0 +1,244 @@
+/*
+ * linux/drivers/cpufreq/proc_intf.c
+ *
+ * Copyright (C) 2002 - 2003 Dominik Brodowski
+ */
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+
+/**
+ * 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(¤t_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(¤t_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;iname);
+ 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; iread_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 ");
+MODULE_DESCRIPTION ("CPUfreq /proc/cpufreq interface");
+MODULE_LICENSE ("GPL");
+
+module_init(cpufreq_proc_init);
+module_exit(cpufreq_proc_exit);
diff -urNp --exclude CVS --exclude BitKeeper xx-ref/drivers/cpufreq/userspace.c xx/drivers/cpufreq/userspace.c
--- xx-ref/drivers/cpufreq/userspace.c 1970-01-01 01:00:00.000000000 +0100
+++ xx/drivers/cpufreq/userspace.c 2003-06-07 13:33:35.000000000 +0200
@@ -0,0 +1,342 @@
+/*
+ * drivers/cpufreq/userspace.c
+ *
+ * Copyright (C) 2001 Russell King
+ * (C) 2002 - 2003 Dominik Brodowski
+ *
+ * $Id:$
+ *
+ * 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
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+
+#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_l(¤t_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_CPU_VARS(0);
+
+static ctl_table ctl_cpu_table[NR_CPUS + 1] = {
+ CPU_ENUM(0),
+ {
+ .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);
+ MOD_INC_USE_COUNT;
+ cpu_is_managed[cpu] = 1;
+ cpu_min_freq[cpu] = policy->min;
+ cpu_max_freq[cpu] = policy->max;
+ cpu_cur_freq[cpu] = policy->cur;
+ memcpy (¤t_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;
+ MOD_DEC_USE_COUNT;
+ 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(¤t_policy[cpu], policy->max,
+ CPUFREQ_RELATION_H);
+ else if (policy->min > cpu_cur_freq[cpu])
+ cpufreq_driver_target(¤t_policy[cpu], policy->min,
+ CPUFREQ_RELATION_L);
+ memcpy (¤t_policy[cpu], policy, sizeof(struct cpufreq_policy));
+ up(&userspace_sem);
+ break;
+ }
+ return 0;
+}
+
+
+static struct cpufreq_governor cpufreq_gov_userspace = {
+ .name = "userspace",
+ .governor = cpufreq_governor_userspace,
+ .owner = THIS_MODULE,
+};
+EXPORT_SYMBOL(cpufreq_gov_userspace);
+
+static int cpufreq_gov_userspace_init(void)
+{
+ cpufreq_sysctl_init();
+ cpufreq_register_notifier(&userspace_cpufreq_notifier_block, CPUFREQ_TRANSITION_NOTIFIER);
+ return cpufreq_register_governor(&cpufreq_gov_userspace);
+}
+
+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 , Russell King ");
+MODULE_DESCRIPTION ("CPUfreq policy governor 'userspace'");
+MODULE_LICENSE ("GPL");
+
+module_init(cpufreq_gov_userspace_init);
+module_exit(cpufreq_gov_userspace_exit);
diff -urNp --exclude CVS --exclude BitKeeper xx-ref/include/asm-i386/smp.h xx/include/asm-i386/smp.h
--- xx-ref/include/asm-i386/smp.h 2003-02-14 07:01:58.000000000 +0100
+++ xx/include/asm-i386/smp.h 2003-06-07 13:33:35.000000000 +0200
@@ -95,6 +95,8 @@ static __inline int logical_smp_processo
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 -urNp --exclude CVS --exclude BitKeeper xx-ref/include/linux/cpufreq.h xx/include/linux/cpufreq.h
--- xx-ref/include/linux/cpufreq.h 1970-01-01 01:00:00.000000000 +0100
+++ xx/include/linux/cpufreq.h 2003-06-07 13:33:35.000000000 +0200
@@ -0,0 +1,269 @@
+/*
+ * linux/include/linux/cpufreq.h
+ *
+ * Copyright (C) 2001 Russell King
+ * (C) 2002 - 2003 Dominik Brodowski
+ *
+ *
+ * $Id: cpufreq.h,v 1.36 2003/01/20 17:31:48 db 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
+#include
+#include
+
+
+#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)
+
+#define CPUFREQ_ALL_CPUS ((NR_CPUS))
+
+
+/********************** 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;
+};
+
+struct cpufreq_policy {
+ unsigned int cpu; /* cpu nr or CPUFREQ_ALL_CPUS */
+ 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 cpufreq_cpuinfo cpuinfo; /* see above */
+};
+
+#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 or CPUFREQ_ALL_CPUS */
+ 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;
+ struct module *owner;
+};
+
+/* pass a target to the cpufreq driver
+ * _l : (cpufreq_driver_sem is not held)
+ */
+inline int cpufreq_driver_target(struct cpufreq_policy *policy,
+ unsigned int target_freq,
+ unsigned int relation);
+
+inline int cpufreq_driver_target_l(struct cpufreq_policy *policy,
+ unsigned int target_freq,
+ unsigned int relation);
+
+/* pass an event to the cpufreq governor */
+int cpufreq_governor_l(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 cpufreq_driver {
+ /* needed by all drivers */
+ int (*verify) (struct cpufreq_policy *policy);
+ struct cpufreq_policy *policy;
+ char name[CPUFREQ_NAME_LEN];
+ /* 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, for the moment */
+ int (*init) (struct cpufreq_policy *policy);
+ int (*exit) (struct cpufreq_policy *policy);
+};
+
+int cpufreq_register_driver(struct cpufreq_driver *driver_data);
+int cpufreq_unregister_driver(struct cpufreq_driver *driver_data);
+/* deprecated */
+#define cpufreq_register(x) cpufreq_register_driver(x)
+#define cpufreq_unregister() cpufreq_unregister_driver(NULL)
+
+
+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);
+
+#ifdef CONFIG_PM
+int cpufreq_restore(void);
+#endif
+
+/* 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 *
+ *********************************************************************/
+
+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,
+};
+
+/* /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 */
+
+
+
+#if defined(CONFIG_CPU_FREQ_TABLE) || defined(CONFIG_CPU_FREQ_TABLE_MODULE)
+/*********************************************************************
+ * 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 */
+};
+
+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_setpolicy(struct cpufreq_policy *policy,
+ struct cpufreq_frequency_table *table,
+ unsigned int *index);
+
+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 -urNp --exclude CVS --exclude BitKeeper xx-ref/include/linux/kernel.h xx/include/linux/kernel.h
--- xx-ref/include/linux/kernel.h 2003-02-14 07:01:58.000000000 +0100
+++ xx/include/linux/kernel.h 2003-06-07 13:33:45.000000000 +0200
@@ -13,6 +13,7 @@
#include
#include
#include
+#include
/* Optimization barrier */
/* The "volatile" is due to gcc bugs */
diff -urNp --exclude CVS --exclude BitKeeper xx-ref/include/linux/smp.h xx/include/linux/smp.h
--- xx-ref/include/linux/smp.h 2003-05-29 12:10:28.000000000 +0200
+++ xx/include/linux/smp.h 2003-06-07 13:33:35.000000000 +0200
@@ -86,6 +86,7 @@ extern volatile int smp_msg_id;
#define cpu_number_map(cpu) 0
#define smp_call_function(func,info,retry,wait) ({ 0; })
#define cpu_online_map 1
+#define cpu_online(cpu) ({ BUG_ON((cpu) != 0); 1; })
#endif
#endif
diff -urNp --exclude CVS --exclude BitKeeper xx-ref/kernel/Makefile xx/kernel/Makefile
--- xx-ref/kernel/Makefile 2002-01-22 18:53:55.000000000 +0100
+++ xx/kernel/Makefile 2003-06-07 13:33:35.000000000 +0200
@@ -9,7 +9,7 @@
O_TARGET := kernel.o
-export-objs = signal.o sys.o kmod.o context.o ksyms.o pm.o exec_domain.o printk.o
+export-objs = signal.o sys.o kmod.o context.o ksyms.o pm.o exec_domain.o printk.o cpufreq.o
obj-y = sched.o dma.o fork.o exec_domain.o panic.o printk.o \
module.o exit.o itimer.o info.o time.o softirq.o resource.o \
@@ -19,6 +19,7 @@ obj-y = sched.o dma.o fork.o exec_do
obj-$(CONFIG_UID16) += uid16.o
obj-$(CONFIG_MODULES) += ksyms.o
obj-$(CONFIG_PM) += pm.o
+obj-$(CONFIG_CPU_FREQ) += cpufreq.o
ifneq ($(CONFIG_IA64),y)
# According to Alan Modra , the -fno-omit-frame-pointer is
diff -urNp --exclude CVS --exclude BitKeeper xx-ref/kernel/cpufreq.c xx/kernel/cpufreq.c
--- xx-ref/kernel/cpufreq.c 1970-01-01 01:00:00.000000000 +0100
+++ xx/kernel/cpufreq.c 2003-06-07 13:33:35.000000000 +0200
@@ -0,0 +1,651 @@
+/*
+ * linux/kernel/cpufreq.c
+ *
+ * Copyright (C) 2001 Russell King
+ * (C) 2002 - 2003 Dominik Brodowski
+ *
+ * $Id: cpufreq.c,v 1.59 2003/01/20 17:31:48 db 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
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+/**
+ * The "cpufreq driver" - the arch- or hardware-dependend low
+ * level driver of CPUFreq support, and its locking mutex.
+ * cpu_max_freq is in kHz.
+ */
+static struct cpufreq_driver *cpufreq_driver;
+static DECLARE_MUTEX (cpufreq_driver_sem);
+
+
+/**
+ * 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. If both cpufreq_driver_sem
+ * and cpufreq_notifier_sem need to be hold, get cpufreq_driver_sem
+ * first.
+ */
+static struct notifier_block *cpufreq_policy_notifier_list;
+static struct notifier_block *cpufreq_transition_notifier_list;
+static DECLARE_MUTEX (cpufreq_notifier_sem);
+
+
+LIST_HEAD(cpufreq_governor_list);
+
+static int cpufreq_governor(unsigned int cpu, unsigned int event);
+
+/*********************************************************************
+ * 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_driver_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_driver_sem);
+ return 0;
+ }
+ }
+ out:
+ up(&cpufreq_driver_sem);
+ }
+ return -EINVAL;
+}
+EXPORT_SYMBOL_GPL(cpufreq_parse_governor);
+
+
+/**
+ * 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 policy;
+
+ down(&cpufreq_driver_sem);
+ if (!cpufreq_driver) {
+ up(&cpufreq_driver_sem);
+ return -EINVAL;
+ }
+
+ /* call driver. From then on the cpufreq must be able
+ * to accept all calls to ->verify and ->setpolicy for this CPU
+ */
+ cpufreq_driver->policy[cpu].cpu = cpu;
+ if (cpufreq_driver->init) {
+ ret = cpufreq_driver->init(&cpufreq_driver->policy[cpu]);
+ if (ret) {
+ up(&cpufreq_driver_sem);
+ return -ENODEV;
+ }
+ }
+
+ /* set default policy on this CPU */
+ memcpy(&policy,
+ &cpufreq_driver->policy[cpu],
+ sizeof(struct cpufreq_policy));
+
+ if (cpufreq_driver->target)
+ cpufreq_governor(cpu, CPUFREQ_GOV_START);
+
+ up(&cpufreq_driver_sem);
+ ret = cpufreq_set_policy(&policy);
+ if (ret)
+ return -EINVAL;
+
+ return ret;
+}
+
+
+/**
+ * cpufreq_remove_dev - remove a CPU device
+ *
+ * Removes the cpufreq interface for a CPU device. Is called with
+ * cpufreq_driver_sem locked.
+ */
+static int cpufreq_remove_dev (unsigned int cpu)
+{
+ if (cpufreq_driver->target)
+ cpufreq_governor(cpu, CPUFREQ_GOV_STOP);
+
+ if (cpufreq_driver->exit)
+ cpufreq_driver->exit(&cpufreq_driver->policy[cpu]);
+
+ 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(&cpufreq_notifier_sem);
+ 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(&cpufreq_notifier_sem);
+
+ 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(&cpufreq_notifier_sem);
+ 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(&cpufreq_notifier_sem);
+
+ return ret;
+}
+EXPORT_SYMBOL(cpufreq_unregister_notifier);
+
+
+/*********************************************************************
+ * GOVERNORS *
+ *********************************************************************/
+
+inline int cpufreq_driver_target_l(struct cpufreq_policy *policy,
+ unsigned int target_freq,
+ unsigned int relation)
+{
+ unsigned int ret;
+ down(&cpufreq_driver_sem);
+ if (!cpufreq_driver)
+ ret = -EINVAL;
+ else
+ ret = cpufreq_driver->target(policy, target_freq, relation);
+ up(&cpufreq_driver_sem);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(cpufreq_driver_target_l);
+
+
+inline 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);
+
+
+static int cpufreq_governor(unsigned int cpu, unsigned int event)
+{
+ int ret = 0;
+ struct cpufreq_policy *policy = &cpufreq_driver->policy[cpu];
+
+ 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 = -EINVAL;
+ ret = cpufreq_driver->policy[cpu].governor->governor(policy, event);
+ break;
+ default:
+ ret = -EINVAL;
+ }
+ return ret;
+}
+
+
+int cpufreq_governor_l(unsigned int cpu, unsigned int event)
+{
+ int ret = 0;
+ down(&cpufreq_driver_sem);
+ ret = cpufreq_governor(cpu, event);
+ up(&cpufreq_driver_sem);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(cpufreq_governor_l);
+
+
+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_driver_sem);
+
+ list_for_each_entry(t, &cpufreq_governor_list, governor_list) {
+ if (!strnicmp(governor->name,t->name,CPUFREQ_NAME_LEN)) {
+ up(&cpufreq_driver_sem);
+ return -EBUSY;
+ }
+ }
+ list_add(&governor->governor_list, &cpufreq_governor_list);
+ up(&cpufreq_driver_sem);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(cpufreq_register_governor);
+
+
+void cpufreq_unregister_governor(struct cpufreq_governor *governor)
+{
+ unsigned int i;
+
+ if (!governor)
+ return;
+
+ down(&cpufreq_driver_sem);
+ /*
+ * Unless the user uses rmmod -f, we can be safe. But we never
+ * know, so check whether if it's currently used. If so,
+ * stop it and replace it with the default governor.
+ */
+ for (i=0; ipolicy[i].policy == CPUFREQ_POLICY_GOVERNOR) &&
+ (cpufreq_driver->policy[i].governor == governor)) {
+ cpufreq_governor(i, CPUFREQ_GOV_STOP);
+ cpufreq_driver->policy[i].policy = CPUFREQ_POLICY_PERFORMANCE;
+ cpufreq_governor(i, CPUFREQ_GOV_START);
+ }
+ }
+ /* now we can safely remove it from the list */
+ list_del(&governor->governor_list);
+ up(&cpufreq_driver_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)
+{
+ down(&cpufreq_driver_sem);
+ if (!cpufreq_driver || !policy ||
+ (cpu >= NR_CPUS) || (!cpu_online(cpu))) {
+ up(&cpufreq_driver_sem);
+ return -EINVAL;
+ }
+
+ memcpy(policy,
+ &cpufreq_driver->policy[cpu],
+ sizeof(struct cpufreq_policy));
+
+ up(&cpufreq_driver_sem);
+
+ 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;
+
+ down(&cpufreq_driver_sem);
+ if (!cpufreq_driver || !policy ||
+ (policy->cpu >= NR_CPUS) || (!cpu_online(policy->cpu))) {
+ up(&cpufreq_driver_sem);
+ return -EINVAL;
+ }
+
+ memcpy(&policy->cpuinfo,
+ &cpufreq_driver->policy[policy->cpu].cpuinfo,
+ sizeof(struct cpufreq_cpuinfo));
+
+ /* verify the cpu speed can be set within this limit */
+ ret = cpufreq_driver->verify(policy);
+ if (ret) {
+ up(&cpufreq_driver_sem);
+ return ret;
+ }
+
+ down(&cpufreq_notifier_sem);
+
+ /* adjust if neccessary - all reasons */
+ notifier_call_chain(&cpufreq_policy_notifier_list, CPUFREQ_ADJUST,
+ policy);
+
+ /* adjust if neccessary - 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(&cpufreq_notifier_sem);
+ up(&cpufreq_driver_sem);
+ return ret;
+ }
+
+ /* notification of the new policy */
+ notifier_call_chain(&cpufreq_policy_notifier_list, CPUFREQ_NOTIFY,
+ policy);
+
+ up(&cpufreq_notifier_sem);
+
+ cpufreq_driver->policy[policy->cpu].min = policy->min;
+ cpufreq_driver->policy[policy->cpu].max = policy->max;
+
+ if (cpufreq_driver->setpolicy) {
+ cpufreq_driver->policy[policy->cpu].policy = policy->policy;
+ ret = cpufreq_driver->setpolicy(policy);
+ } else {
+ if ((policy->policy != cpufreq_driver->policy[policy->cpu].policy) ||
+ ((policy->policy == CPUFREQ_POLICY_GOVERNOR) && (policy->governor != cpufreq_driver->policy[policy->cpu].governor))) {
+ unsigned int old_pol = cpufreq_driver->policy[policy->cpu].policy;
+ struct cpufreq_governor *old_gov = cpufreq_driver->policy[policy->cpu].governor;
+ /* end old governor */
+ cpufreq_governor(policy->cpu, CPUFREQ_GOV_STOP);
+ cpufreq_driver->policy[policy->cpu].policy = policy->policy;
+ cpufreq_driver->policy[policy->cpu].governor = policy->governor;
+ /* start new governor */
+ if (cpufreq_governor(policy->cpu, CPUFREQ_GOV_START)) {
+ cpufreq_driver->policy[policy->cpu].policy = old_pol;
+ cpufreq_driver->policy[policy->cpu].governor = old_gov;
+ cpufreq_governor(policy->cpu, CPUFREQ_GOV_START);
+ }
+ /* might be a policy change, too */
+ cpufreq_governor(policy->cpu, CPUFREQ_GOV_LIMITS);
+ } else {
+ cpufreq_governor(policy->cpu, CPUFREQ_GOV_LIMITS);
+ }
+ }
+
+ up(&cpufreq_driver_sem);
+
+ 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(&cpufreq_notifier_sem);
+ 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_driver->policy[freqs->cpu].cur = freqs->new;
+ break;
+ }
+ up(&cpufreq_notifier_sem);
+}
+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 int i = 0;
+
+ if (cpufreq_driver)
+ return -EBUSY;
+
+ if (!driver_data || !driver_data->verify ||
+ ((!driver_data->setpolicy) && (!driver_data->target)))
+ return -EINVAL;
+
+ down(&cpufreq_driver_sem);
+
+ cpufreq_driver = driver_data;
+
+ if (!cpufreq_driver->policy) {
+ /* then we need per-CPU init */
+ if (!cpufreq_driver->init) {
+ up(&cpufreq_driver_sem);
+ return -EINVAL;
+ }
+ cpufreq_driver->policy = kmalloc(NR_CPUS * sizeof(struct cpufreq_policy), GFP_KERNEL);
+ if (!cpufreq_driver->policy) {
+ up(&cpufreq_driver_sem);
+ return -ENOMEM;
+ }
+ memset(cpufreq_driver->policy, 0, NR_CPUS * sizeof(struct cpufreq_policy));
+ }
+
+ up(&cpufreq_driver_sem);
+
+ for (i=0; ipolicy);
+ cpufreq_driver = NULL;
+
+ up(&cpufreq_driver_sem);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(cpufreq_unregister_driver);
+
+
+#ifdef CONFIG_PM
+/**
+ * cpufreq_restore - restore the CPU clock frequency after resume
+ *
+ * Restore the CPU clock frequency so that our idea of the current
+ * frequency reflects the actual hardware.
+ */
+int cpufreq_restore(void)
+{
+ struct cpufreq_policy policy;
+ unsigned int i;
+ unsigned int ret = 0;
+
+ if (in_interrupt())
+ panic("cpufreq_restore() called from interrupt context!");
+
+ for (i=0;ipolicy[i], sizeof(struct cpufreq_policy));
+ up(&cpufreq_driver_sem);
+
+ ret += cpufreq_set_policy(&policy);
+ }
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(cpufreq_restore);
+#else
+#define cpufreq_restore() do {} while (0)
+#endif /* CONFIG_PM */