Mailing List
Home
Linux - General Red Hat Linux discussion list
Enterprise Linux 3 - Discussion of Red Hat Enterprise Linux 3 (Taroon)
Red Hat Linux 9 - Discussion of Red Hat Linux 9 (Shrike)
Installation - Getting started with Red Hat Linux
Red Hat Linux 7.3 - Discussion of Red Hat Linux 7.3 (Valhalla)
Red Hat Linux 8.0 - Discussion of Red Hat Linux 8.0 (Psyche)
Red Hat Linux 7.2 - Discussion of Red Hat Linux 7.2 (Enigma)
Red Hat Linux 7.1 - Discussion of Red Hat Linux 7.1 (Seawolf)
Apache Web Server
Oracle database, Microsoft SQL server ...
Subjects
application/x mplayer2 plugin
RPM error: db4 error(16) from dbenv >remove: Device or resource
   busy
Command stream end of file while reading
X Windows problem (xauth)
Upgrading openoffice 1 1 rpm
FTP: connection refused
FTP: connection refused
mount: /dev/cdrom: is not a valid block device
Dell Precision 650, RedHat 9, no sound
how to trace the cause resulting in the crash of bind server
Virus on the list
UNINSTALL RPM MYSQL
usb pen drives: mounting as a user
broadcom network interface
make mrproper
sendmail configuration on redhat
Couldn 't open PID file /var/run/named/named pid Permission denied
Promise 378 controller
kernel 2 6 and /dev/sound/mixer not found
Problem using up2date
mrtg step by step howto/configuration for a newbie?
Compiling and Installing Kernel 2 6
Can 't locate module ppp0, can 't locate module ppp compress 21
HOW I CAN MAKE BOOTABLE FLOPPY DISKET
Lotus Notes under Wine
/etc/security/limits conf question
Intel E/1000 driver
Command stream end of file while reading
rpm database corrupt
qla2300 modules
 
Search:  
Power your search with and, or, +, -, or "some phrase" operators.
CPUFreq patch for kernel 2.4.23 [Was : Kernel 2.4.23]

CPUFreq patch for kernel 2.4.23 [Was : Kernel 2.4.23]

2003-12-01       - By Ow Mun Heng

 Back
Reply:     1     2     3     4     5     6     7  

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

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

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

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

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

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

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

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

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

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

diff -uNr linux-2.4.23-vanilla/arch/i386/boot/setup.S
linux/arch/i386/boot/setup.S
--- linux-2.4.23-vanilla/arch/i386/boot/setup.S   2003-11-29
02:26:19.000000000 +0800
+++ linux/arch/i386/boot/setup.S   2003-12-01 20:38:29.000000000 +0800
@@ -488,6 +488,18 @@
   movw   $0xAA, (0x1ff)         # device present
no_psmouse:

+#if defined(CONFIG_X86_SPEEDSTEP_SMI) ||
defined(CONFIG_X86_SPEEDSTEP_SMI_MODULE)
+   movl   $0x0000E980, %eax      # IST Support
+   movl   $0x47534943, %edx      # Request value
+   int   $0x15
+
+   movl   %eax, (96)
+   movl   %ebx, (100)
+   movl   %ecx, (104)
+   movl   %edx, (108)
+#endif
+
+
#if defined(CONFIG_APM) || defined(CONFIG_APM_MODULE)
# Then check for an APM BIOS...
                  # %ds points to the
bootsector
diff -uNr linux-2.4.23-vanilla/arch/i386/config.in linux/arch/i386/config.in
--- linux-2.4.23-vanilla/arch/i386/config.in   2003-11-29
02:26:19.000000000 +0800
+++ linux/arch/i386/config.in   2003-12-01 20:38:29.000000000 +0800
@@ -194,6 +194,33 @@

bool 'Machine Check Exception ' CONFIG_X86_MCE

+mainmenu_option next_comment
+comment 'CPU Frequency scaling '
+bool 'CPU Frequency scaling ' CONFIG_CPU_FREQ
+if [ "$CONFIG_CPU_FREQ " = "y " ]; then
+ bool ' CPU frequency table helpers ' CONFIG_CPU_FREQ_TABLE
+ define_bool CONFIG_CPU_FREQ_PROC_INTF y
+ comment 'CPUFreq governors '
+ bool ' Support for governing from userspace '
CONFIG_CPU_FREQ_GOV_USERSPACE
+ define_bool CONFIG_CPU_FREQ_24_API y
+ comment 'CPUFreq processor drivers '
+ dep_tristate ' AMD Mobile K6-2/K6-3 PowerNow! ' CONFIG_X86_POWERNOW_K6
$CONFIG_CPU_FREQ_TABLE
+ dep_tristate ' AMD Mobile Athlon/Duron K7 PowerNow! '
CONFIG_X86_POWERNOW_K7 $CONFIG_CPU_FREQ_TABLE
+ dep_tristate ' AMD Opteron/Athlon64 PowerNow! ' CONFIG_X86_POWERNOW_K8
$CONFIG_CPU_FREQ_TABLE
+ if [ "$CONFIG_MELAN " = "y " ]; then
+ dep_tristate ' AMD Elan ' CONFIG_ELAN_CPUFREQ $CONFIG_CPU_FREQ_TABLE
+ fi
+ dep_tristate ' VIA Cyrix III Longhaul ' CONFIG_X86_LONGHAUL
$CONFIG_CPU_FREQ_TABLE
+ dep_tristate ' Intel Speedstep (PIIX4) ' CONFIG_X86_SPEEDSTEP_PIIX4
$CONFIG_CPU_FREQ_TABLE
+ dep_tristate ' Intel SpeedStep on 440BX/ZX/MX chipsets (SMI interface) '
CONFIG_X86_SPEEDSTEP_SMI $CONFIG_CPU_FREQ_TABLE
+ dep_tristate ' Intel Speedstep (ICH) ' CONFIG_X86_SPEEDSTEP_ICH
$CONFIG_CPU_FREQ_TABLE
+ dep_tristate ' Intel Pentium-M Enhanced SpeedStep '
CONFIG_X86_SPEEDSTEP_CENTRINO $CONFIG_CPU_FREQ_TABLE
+ dep_tristate ' Intel Pentium 4 clock modulation ' CONFIG_X86_P4_CLOCKMOD
$CONFIG_CPU_FREQ_TABLE
+ tristate ' Transmeta LongRun ' CONFIG_X86_LONGRUN
+ tristate ' Cyrix MediaGX/NatSemi Geode Suspend Modulation '
CONFIG_X86_GX_SUSPMOD
+fi
+endmenu
+
tristate 'Toshiba Laptop support ' CONFIG_TOSHIBA
tristate 'Dell laptop support ' CONFIG_I8K

diff -uNr linux-2.4.23-vanilla/arch/i386/kernel/elanfreq.c
linux/arch/i386/kernel/elanfreq.c
--- linux-2.4.23-vanilla/arch/i386/kernel/elanfreq.c   1970-01-01
07:30:00.000000000 +0730
+++ linux/arch/i386/kernel/elanfreq.c   2003-08-25 22:58:46.000000000 +0800
@@ -0,0 +1,286 @@
+/*
+ *    elanfreq:    cpufreq driver for the AMD ELAN family
+ *
+ *   (c) Copyright 2002 Robert Schwebel <r.schwebel@(protected) >
+ *
+ *   Parts of this code are (c) Sven Geggus <sven@(protected) >
+ *
+ * All Rights Reserved.
+ *
+ *   This program is free software; you can redistribute it and/or
+ *   modify it under the terms of the GNU General Public License
+ *   as published by the Free Software Foundation; either version
+ *   2 of the License, or (at your option) any later version.
+ *
+ *   2002-02-13: - initial revision for 2.4.18-pre9 by Robert Schwebel
+ *
+ */
+
+#include <linux/kernel.h >
+#include <linux/module.h >
+#include <linux/init.h >
+
+#include <linux/slab.h >
+#include <linux/delay.h >
+#include <linux/cpufreq.h >
+
+#include <asm/msr.h >
+#include <asm/timex.h >
+#include <asm/io.h >
+
+#define REG_CSCIR 0x22       /* Chip Setup and Control Index
Register */
+#define REG_CSCDR 0x23      /* Chip Setup and Control Data Register
*/
+
+/* Module parameter */
+static int max_freq;
+
+struct s_elan_multiplier {
+   int clock;      /* frequency in kHz
*/
+   int val40h;      /* PMU Force Mode register
*/
+   int val80h;      /* CPU Clock Speed Register
*/
+};
+
+/*
+ * It is important that the frequencies
+ * are listed in ascending order here!
+ */
+struct s_elan_multiplier elan_multiplier[] = {
+   {1000,   0x02,   0x18},
+   {2000,   0x02,   0x10},
+   {4000,   0x02,   0x08},
+   {8000,   0x00,   0x00},
+   {16000,   0x00,   0x02},
+   {33000,   0x00,   0x04},
+   {66000,   0x01,   0x04},
+   {99000,   0x01,   0x05}
+};
+
+static struct cpufreq_frequency_table elanfreq_table[] = {
+   {0,   1000},
+   {1,   2000},
+   {2,   4000},
+   {3,   8000},
+   {4,   16000},
+   {5,   33000},
+   {6,   66000},
+   {7,   99000},
+   {0,   CPUFREQ_TABLE_END},
+};
+
+
+/**
+ *   elanfreq_get_cpu_frequency: determine current cpu speed
+ *
+ *   Finds out at which frequency the CPU of the Elan SOC runs
+ *   at the moment. Frequencies from 1 to 33 MHz are generated
+ *   the normal way, 66 and 99 MHz are called "Hyperspeed Mode "
+ *   and have the rest of the chip running with 33 MHz.
+ */
+
+static unsigned int elanfreq_get_cpu_frequency(void)
+{
+ u8 clockspeed_reg; /* Clock Speed Register */
+   
+   local_irq_disable();
+ outb_p(0x80,REG_CSCIR);
+ clockspeed_reg = inb_p(REG_CSCDR);
+   local_irq_enable();
+
+ if ((clockspeed_reg & 0xE0) == 0xE0) { return 0; }
+
+ /* Are we in CPU clock multiplied mode (66/99 MHz)? */
+ if ((clockspeed_reg & 0xE0) == 0xC0) {
+ if ((clockspeed_reg & 0x01) == 0) {
+         return 66000;
+      } else {
+         return 99000;
+      }
+ }
+
+   /* 33 MHz is not 32 MHz... */
+   if ((clockspeed_reg & 0xE0)==0xA0)
+      return 33000;
+
+ return ((1 < <((clockspeed_reg & 0xE0) > > 5)) * 1000);
+}
+
+
+/**
+ * elanfreq_set_cpu_frequency: Change the CPU core frequency
+ *    @(protected): cpu number
+ *   @(protected): frequency in kHz
+ *
+ * This function takes a frequency value and changes the CPU frequency

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

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

+    * specific processing capacity.
+    */
+   tmp_freq = gx_validate_speed(policy- >min, &tmp1, &tmp2);
+   if (tmp_freq < policy- >min)
+      tmp_freq += stock_freq / max_duration;
+   policy- >min = tmp_freq;
+   if (policy- >min > policy- >max)
+      policy- >max = tmp_freq;
+   tmp_freq = gx_validate_speed(policy- >max, &tmp1, &tmp2);
+   if (tmp_freq > policy- >max)
+      tmp_freq -= stock_freq / max_duration;
+   policy- >max = tmp_freq;
+   if (policy- >max < policy- >min)
+      policy- >max = policy- >min;
+   cpufreq_verify_within_limits(policy, (stock_freq / max_duration),
stock_freq);
+   
+   return 0;
+}
+
+/*
+ * cpufreq_gx_target:
+ *
+ */
+static int cpufreq_gx_target(struct cpufreq_policy *policy,
+          unsigned int target_freq,
+          unsigned int relation)
+{
+   u8 tmp1, tmp2;
+   unsigned int tmp_freq;
+
+ if (!stock_freq || !policy)
+ return -EINVAL;
+
+   policy- >cpu = 0;
+
+   tmp_freq = gx_validate_speed(target_freq, &tmp1, &tmp2);
+   while (tmp_freq < policy- >min) {
+      tmp_freq += stock_freq / max_duration;
+      tmp_freq = gx_validate_speed(tmp_freq, &tmp1, &tmp2);
+   }
+   while (tmp_freq > policy- >max) {
+      tmp_freq -= stock_freq / max_duration;
+      tmp_freq = gx_validate_speed(tmp_freq, &tmp1, &tmp2);
+   }
+
+   gx_set_cpuspeed(tmp_freq);
+
+   return 0;
+}
+
+static int cpufreq_gx_cpu_init(struct cpufreq_policy *policy)
+{
+   int maxfreq, curfreq;
+
+   if (!policy || policy- >cpu != 0)
+      return -ENODEV;
+
+   /* determine maximum frequency */
+   if (pci_busclk) {
+      maxfreq = pci_busclk * gx_freq_mult[getCx86(CX86_DIR1) &
0x0f];
+   } else if (cpu_khz) {
+      maxfreq = cpu_khz;
+   } else {
+      maxfreq = 30000 * gx_freq_mult[getCx86(CX86_DIR1) & 0x0f];
+   }
+   stock_freq = maxfreq;
+   curfreq = gx_get_cpuspeed();
+
+   dprintk( "cpu max frequency is %d.\n ", maxfreq);
+   dprintk( "cpu current frequency is %dkHz.\n ",curfreq);
+
+   /* setup basic struct for cpufreq API */
+   policy- >cpu = 0;
+
+   if (max_duration < POLICY_MIN_DIV)
+      policy- >min = maxfreq / max_duration;
+   else
+      policy- >min = maxfreq / POLICY_MIN_DIV;
+   policy- >max = maxfreq;
+   policy- >cur = curfreq;
+   policy- >policy = CPUFREQ_POLICY_PERFORMANCE;
+   policy- >cpuinfo.min_freq = maxfreq / max_duration;
+   policy- >cpuinfo.max_freq = maxfreq;
+   policy- >cpuinfo.transition_latency = CPUFREQ_ETERNAL;
+
+   return 0;
+}
+
+/*
+ * cpufreq_gx_init:
+ * MediaGX/Geode GX initialize cpufreq driver
+ */
+static struct cpufreq_driver gx_suspmod_driver = {
+   .verify      = cpufreq_gx_verify,
+   .target      = cpufreq_gx_target,
+   .init      = cpufreq_gx_cpu_init,
+   .name      = "gx-suspmod ",
+};
+
+static int __init cpufreq_gx_init(void)
+{
+   int ret;
+   struct gxfreq_params *params;
+   struct pci_dev *gx_pci;
+   u32 class_rev;
+
+   /* Test if we have the right hardware */
+   if ((gx_pci = gx_detect_chipset()) == NULL)
+      return -ENODEV;
+
+   /* check whether module parameters are sane */
+   if (max_duration > 0xff)
+      max_duration = 0xff;
+
+   dprintk( "geode suspend modulation available.\n ");
+
+   params = kmalloc(sizeof(struct gxfreq_params), GFP_KERNEL);
+   if (params == NULL)
+      return -ENOMEM;
+   memset(params, 0, sizeof(struct gxfreq_params));
+
+   params- >cs55x0 = gx_pci;
+   gx_params = params;
+
+   /* keep cs55x0 configurations */
+   pci_read_config_byte(params- >cs55x0, PCI_SUSCFG,
&(params- >pci_suscfg));
+   pci_read_config_byte(params- >cs55x0, PCI_PMER1,
&(params- >pci_pmer1));
+   pci_read_config_byte(params- >cs55x0, PCI_PMER2,
&(params- >pci_pmer2));
+   pci_read_config_byte(params- >cs55x0, PCI_MODON,
&(params- >on_duration));
+   pci_read_config_byte(params- >cs55x0, PCI_MODOFF,
&(params- >off_duration));
+ pci_read_config_dword(params- >cs55x0, PCI_CLASS_REVISION,
&class_rev);
+   params- >pci_rev = class_rev && 0xff;
+
+   if ((ret = cpufreq_register_driver(&gx_suspmod_driver))) {
+      kfree(params);
+      return ret; /* register error! */
+   }
+
+   return 0;
+}
+
+static void __exit cpufreq_gx_exit(void)
+{
+   cpufreq_unregister_driver(&gx_suspmod_driver);
+   kfree(gx_params);
+}
+
+MODULE_AUTHOR ( "Hiroshi Miura <miura@(protected) > ");
+MODULE_DESCRIPTION ( "Cpufreq driver for Cyrix MediaGX and NatSemi Geode ");
+MODULE_LICENSE ( "GPL ");
+
+module_init(cpufreq_gx_init);
+module_exit(cpufreq_gx_exit);
+
diff -uNr linux-2.4.23-vanilla/arch/i386/kernel/i386_ksyms.c
linux/arch/i386/kernel/i386_ksyms.c
--- linux-2.4.23-vanilla/arch/i386/kernel/i386_ksyms.c   2003-11-29
02:26:19.000000000 +0800
+++ linux/arch/i386/kernel/i386_ksyms.c   2003-12-01 21:04:21.000000000 +0800
@@ -29,6 +29,7 @@
#include <asm/pgtable.h >
#include <asm/pgalloc.h >
#include <asm/edd.h >
+#include <asm/ist.h >

extern void dump_thread(struct pt_regs *, struct user *);
extern spinlock_t rtc_lock;
@@ -50,6 +51,7 @@
EXPORT_SYMBOL(drive_info);
#endif

+extern unsigned long cpu_khz;
extern unsigned long get_cmos_time(void);

/* platform dependent support */
@@ -72,7 +74,9 @@
EXPORT_SYMBOL(pm_idle);
EXPORT_SYMBOL(pm_power_off);
EXPORT_SYMBOL(get_cmos_time);
+EXPORT_SYMBOL(cpu_khz);
EXPORT_SYMBOL(apm_info);
+EXPORT_SYMBOL(ist_info);
EXPORT_SYMBOL(gdt);
EXPORT_SYMBOL(empty_zero_page);

@@ -131,7 +135,9 @@
EXPORT_SYMBOL(cpu_data);
EXPORT_SYMBOL(kernel_flag_cacheline);
EXPORT_SYMBOL(smp_num_cpus);
+EXPORT_SYMBOL(smp_num_siblings);
EXPORT_SYMBOL(cpu_online_map);
+EXPORT_SYMBOL(cpu_sibling_map);
EXPORT_SYMBOL_NOVERS(__write_lock_failed);
EXPORT_SYMBOL_NOVERS(__read_lock_failed);

diff -uNr linux-2.4.23-vanilla/arch/i386/kernel/longhaul.c
linux/arch/i386/kernel/longhaul.c
--- linux-2.4.23-vanilla/arch/i386/kernel/longhaul.c   1970-01-01
07:30:00.000000000 +0730
+++ linux/arch/i386/kernel/longhaul.c   2003-09-30 20:30:04.000000000 +0800
@@ -0,0 +1,478 @@
+/*
+ * (C) 2001-2003 Dave Jones. <davej@(protected) >
+ * (C) 2002 Padraig Brady. <padraig@(protected) >
+ *
+ * Licensed under the terms of the GNU GPL License version 2.
+ * Based upon datasheets & sample CPUs kindly provided by VIA.
+ *
+ * VIA have currently 3 different versions of Longhaul.
+ *
+ * +-- ---- ---- ---- ---+-- ---- --+-- ---- ---- ---- ---- ---- -----+
+ * | Marketing name | Codename | longhaul version / features. |
+ * +-- ---- ---- ---- ---+-- ---- --+-- ---- ---- ---- ---- ---- -----+
+ * | Samuel/CyrixIII | C5A | v1 : multipliers only |
+ * | Samuel2/C3 | C3E/C5B | v1 : multiplier only |
+ * | Ezra | C5C | v2 : multipliers & voltage |
+ * | Ezra-T | C5M | v3 : multipliers, voltage & FSB |
+ * | Nehemiah | C5N | v3 : multipliers, voltage & FSB |
+ * +-- ---- ---- ---- ---+-- ---- --+-- ---- ---- ---- ---- ---- -----+
+ *
+ * BIG FAT DISCLAIMER: Work in progress code. Possibly *dangerous*
+ */
+
+#include <linux/kernel.h >
+#include <linux/module.h >
+#include <linux/init.h >
+#include <linux/cpufreq.h >
+#include <linux/slab.h >
+#include <linux/string.h >
+
+#include <asm/msr.h >
+#include <asm/timex.h >
+#include <asm/io.h >
+
+#include "longhaul.h "
+
+#define DEBUG
+
+#ifdef DEBUG
+#define dprintk(msg...) printk(msg)
+#else
+#define dprintk(msg...) do { } while(0)
+#endif
+
+#define PFX "longhaul: "
+
+static unsigned int numscales=16, numvscales;
+static int minvid, maxvid;
+static int can_scale_voltage;
+static int vrmrev;
+
+
+/* Module parameters */
+static int dont_scale_voltage;
+static unsigned int fsb;
+
+#define __hlt() __asm__ __volatile__( "hlt ": : : "memory ")
+
+/* Clock ratios multiplied by 10 */
+static int clock_ratio[32];
+static int eblcr_table[32];
+static int voltage_table[32];
+static unsigned int highest_speed, lowest_speed; /* kHz */
+static int longhaul_version;
+static struct cpufreq_frequency_table *longhaul_table;
+
+
+static unsigned int calc_speed (int mult, int fsb)
+{
+   return ((mult/10)*fsb) + ((mult%10)*(fsb/2));
+}
+
+
+static int longhaul_get_cpu_mult (void)
+{
+   unsigned long invalue=0,lo, hi;
+
+   rdmsr (MSR_IA32_EBL_CR_POWERON, lo, hi);
+   invalue = (lo & (1 < <22|1 < <23|1 < <24|1 < <25)) > >22;
+   if (longhaul_version==3) {
+      if (lo & (1 < <27))
+         invalue+=16;
+   }
+   return eblcr_table[invalue];
+}
+
+
+/**
+ * longhaul_set_cpu_frequency()
+ * @(protected) : bitpattern of the new multiplier.
+ *
+ * Sets a new clock ratio, and -if applicable- a new Front Side Bus
+ */
+
+static void longhaul_setstate (unsigned int clock_ratio_index)
+{
+   int speed, mult;
+   struct cpufreq_freqs freqs;
+   union msr_longhaul longhaul;
+   union msr_bcr2 bcr2;
+
+   mult = clock_ratio[clock_ratio_index];
+   if (mult == -1)
+      return;
+
+   speed = calc_speed (mult, fsb);
+   if ((speed > highest_speed) || (speed < lowest_speed))
+      return;
+
+   freqs.old = calc_speed (longhaul_get_cpu_mult(), fsb);
+   freqs.new = speed;
+   freqs.cpu = 0; /* longhaul.c is UP only driver */
+
+   cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
+
+   dprintk (KERN_INFO PFX "FSB:%d Mult:%d.%dx\n ", fsb,
+            mult/10, mult%10);
+
+   switch (longhaul_version) {
+   case 1:
+      rdmsrl (MSR_VIA_BCR2, bcr2.val);
+      /* Enable software clock multiplier */
+      bcr2.bits.ESOFTBF = 1;
+      bcr2.bits.CLOCKMUL = clock_ratio_index;
+      wrmsrl (MSR_VIA_BCR2, bcr2.val);
+
+      __hlt();
+
+      /* Disable software clock multiplier */
+      rdmsrl (MSR_VIA_BCR2, bcr2.val);
+      bcr2.bits.ESOFTBF = 0;
+      wrmsrl (MSR_VIA_BCR2, bcr2.val);
+      break;
+
+   /*
+    * Longhaul v2. (Ezra [C5C])
+    * We can scale voltage with this too, but that 's currently
+    * disabled until we come up with a decent 'match freq to voltage '
+    * algorithm.
+    * We also need to do the voltage/freq setting in order depending
+    * on the direction of scaling (like we do in powernow-k7.c)
+    */
+   case 2:
+      rdmsrl (MSR_VIA_LONGHAUL, longhaul.val);
+      longhaul.bits.SoftBusRatio = clock_ratio_index & 0xf;
+      longhaul.bits.SoftBusRatio4 = (clock_ratio_index & 0x10) > >
4;
+      longhaul.bits.EnableSoftBusRatio = 1;
+      /* We must program the revision key only with values we
+       * know about, not blindly copy it from 0:3 */
+      longhaul.bits.RevisionKey = 1;
+      wrmsrl (MSR_VIA_LONGHAUL, longhaul.val);
+      __hlt();
+
+      break;
+
+   /*
+    * Longhaul v3. (Ezra-T [C5M], Nehemiah [C5N])
+    * This can also do voltage scaling, but see above.
+    * Ezra-T was alleged to do FSB scaling too, but it never worked in
practice.
+    */
+   case 3:
+      rdmsrl (MSR_VIA_LONGHAUL, longhaul.val);
+      longhaul.bits.SoftBusRatio = clock_ratio_index & 0xf;
+      longhaul.bits.SoftBusRatio4 = (clock_ratio_index & 0x10) > >
4;
+      longhaul.bits.EnableSoftBusRatio = 1;
+      /* We must program the revision key only with values we
+       * know about, not blindly copy it from 0:3 */
+      longhaul.bits.RevisionKey = 3;   /* SoftVID & SoftBSEL */
+      wrmsrl(MSR_VIA_LONGHAUL, longhaul.val);
+      __hlt();
+
+      rdmsrl (MSR_VIA_LONGHAUL, longhaul.val);
+      longhaul.bits.EnableSoftBusRatio = 0;
+      longhaul.bits.RevisionKey = 3;
+      wrmsrl (MSR_VIA_LONGHAUL, longhaul.val);
+      break;
+   }
+
+   cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
+}
+
+/*
+ * Centaur decided to make life a little more tricky.
+ * Only longhaul v1 is allowed to read EBLCR BSEL[0:1].
+ * Samuel2 and above have to try and guess what the FSB is.
+ * We do this by assuming we booted at maximum multiplier, and interpolate
+ * between that value multiplied by possible FSBs and cpu_mhz which
+ * was calculated at boot time. Really ugly, but no other way to do this.
+ */
+static int _guess (int guess, int maxmult)
+{
+   int target;
+
+   target = ((maxmult/10)*guess);
+   if (maxmult%10 != 0)
+      target += (guess/2);
+   target &= ~0xf;
+   return target;
+}
+
+static int guess_fsb(int maxmult)
+{
+   int speed = (cpu_khz/1000) & ~0xf;
+   int i;
+   int speeds[3] = { 66, 100, 133 };
+
+   for (i=0; i <3; i++) {
+      if (_guess(speeds[i],maxmult) == speed)
+         return speeds[i];
+   }
+   return 0;
+}
+
+
+
+static int __init longhaul_get_ranges (void)
+{
+   unsigned long invalue;
+   unsigned int minmult=0, maxmult=0;
+   unsigned int multipliers[32]= {
+      50,30,40,100,55,35,45,95,90,70,80,60,120,75,85,65,
+      -1,110,120,-1,135,115,125,105,130,150,160,140,-1,155,-1,145
};
+   unsigned int j, k = 0;
+   union msr_longhaul longhaul;
+   unsigned long lo, hi;
+   unsigned int eblcr_fsb_table[] = { 66, 133, 100, -1 };
+
+   switch (longhaul_version) {
+   case 1:
+      /* Ugh, Longhaul v1 didn 't have the min/max MSRs.
+       Assume min=3.0x & max = whatever we booted at. */
+      minmult = 30;
+      maxmult = longhaul_get_cpu_mult();
+      rdmsr (MSR_IA32_EBL_CR_POWERON, lo, hi);
+      invalue = (lo & (1 < <18|1 < <19)) > >18;
+      fsb = eblcr_fsb_table[invalue];
+      break;
+
+   case 2 ... 3:
+      rdmsrl (MSR_VIA_LONGHAUL, longhaul.val);
+
+      invalue = longhaul.bits.MaxMHzBR;
+      if (longhaul.bits.MaxMHzBR4)
+         invalue += 16;
+      maxmult=multipliers[invalue];
+
+      invalue = longhaul.bits.MinMHzBR;
+      if (longhaul.bits.MinMHzBR4 == 1)
+         minmult = 30;
+      else
+         minmult = multipliers[invalue];
+
+      fsb = guess_fsb(maxmult);
+      break;
+   }
+
+   dprintk (KERN_INFO PFX "MinMult=%d.%dx MaxMult=%d.%dx\n ",
+       minmult/10, minmult%10, maxmult/10, maxmult%10);
+   highest_speed = calc_speed (maxmult, fsb);
+   lowest_speed = calc_speed (minmult,fsb);
+   dprintk (KERN_INFO PFX "FSB: %dMHz Lowestspeed=%dMHz
Highestspeed=%dMHz\n ",
+       fsb, lowest_speed, highest_speed);
+
+   longhaul_table = kmalloc((numscales + 1) * sizeof(struct
cpufreq_frequency_table), GFP_KERNEL);
+   if(!longhaul_table)
+      return -ENOMEM;
+
+   for (j=0; j < numscales; j++) {
+      unsigned int ratio;
+      ratio = clock_ratio[j];
+      if (ratio == -1)
+         continue;
+      if (ratio > maxmult || ratio < minmult)
+         continue;
+      longhaul_table[k].frequency = calc_speed (ratio, fsb);
+      longhaul_table[k].index   = (j < < 8);
+      k++;
+   }
+
+   longhaul_table[k].frequency = CPUFREQ_TABLE_END;
+   if (!k) {
+      kfree (longhaul_table);
+      return -EINVAL;
+   }
+
+   return 0;
+}
+
+
+static void __init longhaul_setup_voltagescaling(void)
+{
+   union msr_longhaul longhaul;
+
+   rdmsrl (MSR_VIA_LONGHAUL, longhaul.val);
+
+   if (!(longhaul.bits.RevisionID & 1))
+      return;
+
+   minvid = longhaul.bits.MinimumVID;
+   maxvid = longhaul.bits.MaximumVID;
+   vrmrev = longhaul.bits.VRMRev;
+
+   if (minvid == 0 || maxvid == 0) {
+      printk (KERN_INFO PFX "Bogus values Min:%d.%03d Max:%d.%03d.
"
+                "Voltage scaling disabled.\n ",
+               minvid/1000, minvid%1000,
maxvid/1000, maxvid%1000);
+      return;
+   }
+
+   if (minvid == maxvid) {
+      printk (KERN_INFO PFX "Claims to support voltage scaling but
min & max are "
+             "both %d.%03d. Voltage scaling disabled\n ",
+            maxvid/1000, maxvid%1000);
+      return;
+   }
+
+   if (vrmrev==0) {
+      dprintk (KERN_INFO PFX "VRM 8.5 : ");
+      memcpy (voltage_table, vrm85scales, sizeof(voltage_table));
+      numvscales =
(voltage_table[maxvid]-voltage_table[minvid])/25;
+   } else {
+      dprintk (KERN_INFO PFX "Mobile VRM : ");
+      memcpy (voltage_table, mobilevrmscales,
sizeof(voltage_table));
+      numvscales =
(voltage_table[maxvid]-voltage_table[minvid])/5;
+   }
+
+   /* Current voltage isn 't readable at first, so we need to
+    set it to a known value. The spec says to use maxvid */
+   longhaul.bits.RevisionKey = longhaul.bits.RevisionID;   /* FIXME:
This is bad. */
+   longhaul.bits.EnableSoftVID = 1;
+   longhaul.bits.SoftVID = maxvid;
+   wrmsrl (MSR_VIA_LONGHAUL, longhaul.val);
+
+   minvid = voltage_table[minvid];
+   maxvid = voltage_table[maxvid];
+
+   dprintk ( "Min VID=%d.%03d Max VID=%d.%03d, %d possible voltage
scales\n ",
+      maxvid/1000, maxvid%1000, minvid/1000, minvid%1000,
numvscales);
+
+   can_scale_voltage = 1;
+}
+
+
+static int longhaul_verify(struct cpufreq_policy *policy)
+{
+   return cpufreq_frequency_table_verify(policy, longhaul_table);
+}
+
+
+static int longhaul_target (struct cpufreq_policy *policy,
+          unsigned int target_freq,
+          unsigned int relation)
+{
+   unsigned int table_index = 0;
+    unsigned int new_clock_ratio = 0;
+
+   if (cpufreq_frequency_table_target(policy, longhaul_table,
target_freq, relation, &table_index))
+      return -EINVAL;
+
+   new_clock_ratio = longhaul_table[table_index].index & 0xFF;
+
+   longhaul_setstate(new_clock_ratio);
+
+   return 0;
+}
+
+static int longhaul_cpu_init (struct cpufreq_policy *policy)
+{
+   struct cpuinfo_x86 *c = cpu_data;
+   char *cpuname=NULL;
+   int ret;
+
+   switch (c- >x86_model) {
+   case 6:
+      cpuname = "C3 'Samuel ' [C5A] ";
+      longhaul_version=1;
+      memcpy (clock_ratio, longhaul1_clock_ratio,
sizeof(longhaul1_clock_ratio));
+      memcpy (eblcr_table, samuel1_eblcr, sizeof(samuel1_eblcr));
+      break;
+
+   case 7:      /* C5B / C5C */
+      switch (c- >x86_mask) {
+      case 0:
+         cpuname = "C3 'Samuel 2 ' [C5B] ";
+         longhaul_version=1;
+         memcpy (clock_ratio, longhaul1_clock_ratio,
sizeof(longhaul1_clock_ratio));
+         memcpy (eblcr_table, samuel2_eblcr,
sizeof(samuel2_eblcr));
+         break;
+      case 1 ... 15:
+         cpuname = "C3 'Ezra ' [C5C] ";
+         longhaul_version=2;
+         memcpy (clock_ratio, longhaul2_clock_ratio,
sizeof(longhaul2_clock_ratio));
+         memcpy (eblcr_table, ezra_eblcr,
sizeof(ezra_eblcr));
+         break;
+      }
+      break;
+
+   case 8:
+      cpuname = "C3 'Ezra-T [C5M] ";
+      longhaul_version=3;
+      numscales=32;
+      memcpy (clock_ratio, longhaul3_clock_ratio,
sizeof(longhaul3_clock_ratio));
+      memcpy (eblcr_table, c5m_eblcr, sizeof(c5m_eblcr));
+      break;
+   /*
+   case 9:
+      cpuname = "C3 'Nehemiah ' [C5N] ";
+      longhaul_version=3;
+      numscales=32;
+   */
+   default:
+      cpuname = "Unknown ";
+      break;
+   }
+
+   printk (KERN_INFO PFX "VIA %s CPU detected. Longhaul v%d
supported.\n ",
+               cpuname, longhaul_version);
+
+   if ((longhaul_version==2 || longhaul_version==3) &&
(dont_scale_voltage==0))
+      longhaul_setup_voltagescaling();
+
+   ret = longhaul_get_ranges();
+   if (ret != 0)
+      return ret;
+
+    policy- >policy = CPUFREQ_POLICY_PERFORMANCE;
+    policy- >cpuinfo.transition_latency = CPUFREQ_ETERNAL;
+   policy- >cur = calc_speed (longhaul_get_cpu_mult(), fsb);
+
+   return cpufreq_frequency_table_cpuinfo(policy, longhaul_table);
+}
+
+static struct cpufreq_driver longhaul_driver = {
+   .verify    = longhaul_verify,
+   .target    = longhaul_target,
+   .init      = longhaul_cpu_init,
+   .name      = "longhaul ",
+};
+
+static int __init longhaul_init (void)
+{
+   struct cpuinfo_x86 *c = cpu_data;
+
+   if (c- >x86_vendor != X86_VENDOR_CENTAUR || c- >x86 != 6)
+      return -ENODEV;
+
+   switch (c- >x86_model) {
+   case 6 ... 7:
+      return cpufreq_register_driver(&longhaul_driver);
+   case 8:
+      printk (KERN_INFO PFX "Ezra-T unsupported: Waiting on
updated docs "
+                   "from VIA before this is
usable.\n ");
+      break;
+   case 9:
+      printk (KERN_INFO PFX "Nehemiah unsupported: Waiting on
working silicon "
+                   "from VIA before this is
usable.\n ");
+      break;
+   default:
+      printk (KERN_INFO PFX "Unknown VIA CPU. Contact
davej@(protected)\n ");
+   }
+
+   return -ENODEV;
+}
+
+static void __exit longhaul_exit (void)
+{
+   cpufreq_unregister_driver(&longhaul_driver);
+   kfree(longhaul_table);
+}
+
+MODULE_PARM (dont_scale_voltage, "i ");
+
+MODULE_AUTHOR ( "Dave Jones <davej@(protected) > ");
+MODULE_DESCRIPTION ( "Longhaul driver for VIA Cyrix processors. ");
+MODULE_LICENSE ( "GPL ");
+
+module_init(longhaul_init);
+module_exit(longhaul_exit);
+
diff -uNr linux-2.4.23-vanilla/arch/i386/kernel/longhaul.h
linux/arch/i386/kernel/longhaul.h
--- linux-2.4.23-vanilla/arch/i386/kernel/longhaul.h   1970-01-01
07:30:00.000000000 +0730
+++ linux/arch/i386/kernel/longhaul.h   2003-09-30 19:52:08.000000000 +0800
@@ -0,0 +1,250 @@
+/*
+ * longhaul.h
+ * (C) 2003 Dave Jones.
+ *
+ * Licensed under the terms of the GNU GPL License version 2.
+ *
+ * VIA-specific information
+ */
+
+union msr_bcr2 {
+   struct {
+      unsigned Reseved:19,   // 18:0
+      ESOFTBF:1,      // 19
+      Reserved2:3,      // 22:20
+      CLOCKMUL:4,      // 26:23
+      Reserved3:5;      // 31:27
+   } bits;
+   unsigned long val;
+};
+
+union msr_longhaul {
+   struct {
+      unsigned RevisionID:4,   // 3:0
+      RevisionKey:4,      // 7:4
+      EnableSoftBusRatio:1,   // 8
+      EnableSoftVID:1,   // 9
+      EnableSoftBSEL:1,   // 10
+      Reserved:3,      // 11:13
+      SoftBusRatio4:1,   // 14
+      VRMRev:1,      // 15
+      SoftBusRatio:4,      // 19:16
+      SoftVID:5,      // 24:20
+      Reserved2:3,      // 27:25
+      SoftBSEL:2,      // 29:28
+      Reserved3:2,      // 31:30
+      MaxMHzBR:4,      // 35:32
+      MaximumVID:5,      // 40:36
+      MaxMHzFSB:2,      // 42:41
+      MaxMHzBR4:1,      // 43
+      Reserved4:4,      // 47:44
+      MinMHzBR:4,      // 51:48
+      MinimumVID:5,      // 56:52
+      MinMHzFSB:2,      // 58:57
+      MinMHzBR4:1,      // 59
+      Reserved5:4;      // 63:60
+   } bits;
+   unsigned long long val;
+};
+
+/*
+ * Clock ratio tables. Div/Mod by 10 to get ratio.
+ * The eblcr ones specify the ratio read from the CPU.
+ * The clock_ratio ones specify what to write to the CPU.
+ */
+
+/*
+ * VIA C3 Samuel 1 & Samuel 2 (stepping 0)
+ */
+static int __initdata longhaul1_clock_ratio[16] = {
+   -1, /* 0000 - > RESERVED */
+   30, /* 0001 - > 3.0x */
+   40, /* 0010 - > 4.0x */
+   -1, /* 0011 - > RESERVED */
+   -1, /* 0100 - > RESERVED */
+   35, /* 0101 - > 3.5x */
+   45, /* 0110 - > 4.5x */
+   55, /* 0111 - > 5.5x */
+   60, /* 1000 - > 6.0x */
+   70, /* 1001 - > 7.0x */
+   80, /* 1010 - > 8.0x */
+   50, /* 1011 - > 5.0x */
+   65, /* 1100 - > 6.5x */
+   75, /* 1101 - > 7.5x */
+   -1, /* 1110 - > RESERVED */
+   -1, /* 1111 - > RESERVED */
+};
+
+static int __initdata samuel1_eblcr[16] = {
+   50, /* 0000 - > RESERVED */
+   30, /* 0001 - > 3.0x */
+   40, /* 0010 - > 4.0x */
+   -1, /* 0011 - > RESERVED */
+   55, /* 0100 - > 5.5x */
+   35, /* 0101 - > 3.5x */
+   45, /* 0110 - > 4.5x */
+   -1, /* 0111 - > RESERVED */
+   -1, /* 1000 - > RESERVED */
+   70, /* 1001 - > 7.0x */
+   80, /* 1010 - > 8.0x */
+   60, /* 1011 - > 6.0x */
+   -1, /* 1100 - > RESERVED */
+   75, /* 1101 - > 7.5x */
+   -1, /* 1110 - > RESERVED */
+   65, /* 1111 - > 6.5x */
+};
+
+/*
+ * VIA C3 Samuel2 Stepping 1- >15 & VIA C3 Ezra
+ */
+static int __initdata longhaul2_clock_ratio[16] = {
+   100, /* 0000 - > 10.0x */
+   30, /* 0001 - > 3.0x */
+   40, /* 0010 - > 4.0x */
+   90, /* 0011 - > 9.0x */
+   95, /* 0100 - > 9.5x */
+   35, /* 0101 - > 3.5x */
+   45, /* 0110 - > 4.5x */
+   55, /* 0111 - > 5.5x */
+   60, /* 1000 - > 6.0x */
+   70, /* 1001 - > 7.0x */
+   80, /* 1010 - > 8.0x */
+   50, /* 1011 - > 5.0x */
+   65, /* 1100 - > 6.5x */
+   75, /* 1101 - > 7.5x */
+   85, /* 1110 - > 8.5x */
+   120, /* 1111 - > 12.0x */
+};
+
+static int __initdata samuel2_eblcr[16] = {
+   50, /* 0000 - > 5.0x */
+   30, /* 0001 - > 3.0x */
+   40, /* 0010 - > 4.0x */
+   100, /* 0011 - > 10.0x */
+   55, /* 0100 - > 5.5x */
+   35, /* 0101 - > 3.5x */
+   45, /* 0110 - > 4.5x */
+   110, /* 0111 - > 11.0x */
+   90, /* 1000 - > 9.0x */
+   70, /* 1001 - > 7.0x */
+   80, /* 1010 - > 8.0x */
+   60, /* 1011 - > 6.0x */
+   120, /* 1100 - > 12.0x */
+   75, /* 1101 - > 7.5x */
+   130, /* 1110 - > 13.0x */
+   65, /* 1111 - > 6.5x */
+};
+
+static int __initdata ezra_eblcr[16] = {
+   50, /* 0000 - > 5.0x */
+   30, /* 0001 - > 3.0x */
+   40, /* 0010 - > 4.0x */
+   100, /* 0011 - > 10.0x */
+   55, /* 0100 - > 5.5x */
+   35, /* 0101 - > 3.5x */
+   45, /* 0110 - > 4.5x */
+   95, /* 0111 - > 9.5x */
+   90, /* 1000 - > 9.0x */
+   70, /* 1001 - > 7.0x */
+   80, /* 1010 - > 8.0x */
+   60, /* 1011 - > 6.0x */
+   120, /* 1100 - > 12.0x */
+   75, /* 1101 - > 7.5x */
+   85, /* 1110 - > 8.5x */
+   65, /* 1111 - > 6.5x */
+};
+
+/*
+ * VIA C3 (Ezra-T) [C5M].
+ */
+static int __initdata longhaul3_clock_ratio[32] = {
+   100, /* 0000 - > 10.0x */
+   30, /* 0001 - > 3.0x */
+   40, /* 0010 - > 4.0x */
+   90, /* 0011 - > 9.0x */
+   95, /* 0100 - > 9.5x */
+   35, /* 0101 - > 3.5x */
+   45, /* 0110 - > 4.5x */
+   55, /* 0111 - > 5.5x */
+   60, /* 1000 - > 6.0x */
+   70, /* 1001 - > 7.0x */
+   80, /* 1010 - > 8.0x */
+   50, /* 1011 - > 5.0x */
+   65, /* 1100 - > 6.5x */
+   75, /* 1101 - > 7.5x */
+   85, /* 1110 - > 8.5x */
+   120, /* 1111 - > 12.0x */
+
+   -1, /* 0000 - > RESERVED (10.0x) */
+   110, /* 0001 - > 11.0x */
+   120, /* 0010 - > 12.0x */
+   -1, /* 0011 - > RESERVED (9.0x)*/
+   105, /* 0100 - > 10.5x */
+   115, /* 0101 - > 11.5x */
+   125, /* 0110 - > 12.5x */
+   135, /* 0111 - > 13.5x */
+   140, /* 1000 - > 14.0x */
+   150, /* 1001 - > 15.0x */
+   160, /* 1010 - > 16.0x */
+   130, /* 1011 - > 13.0x */
+   145, /* 1100 - > 14.5x */
+   155, /* 1101 - > 15.5x */
+   -1, /* 1110 - > RESERVED (13.0x) */
+   -1, /* 1111 - > RESERVED (12.0x) */
+};
+
+static int __initdata c5m_eblcr[32] = {
+   50, /* 0000 - > 5.0x */
+   30, /* 0001 - > 3.0x */
+   40, /* 0010 - > 4.0x */
+   100, /* 0011 - > 10.0x */
+   55, /* 0100 - > 5.5x */
+   35, /* 0101 - > 3.5x */
+   45, /* 0110 - > 4.5x */
+   95, /* 0111 - > 9.5x */
+   90, /* 1000 - > 9.0x */
+   70, /* 1001 - > 7.0x */
+   80, /* 1010 - > 8.0x */
+   60, /* 1011 - > 6.0x */
+   120, /* 1100 - > 12.0x */
+   75, /* 1101 - > 7.5x */
+   85, /* 1110 - > 8.5x */
+   65, /* 1111 - > 6.5x */
+
+   -1, /* 0000 - > RESERVED (9.0x) */
+   110, /* 0001 - > 11.0x */
+   120, /* 0010 - > 12.0x */
+   -1, /* 0011 - > RESERVED (10.0x)*/
+   135, /* 0100 - > 13.5x */
+   115, /* 0101 - > 11.5x */
+   125, /* 0110 - > 12.5x */
+   105, /* 0111 - > 10.5x */
+   130, /* 1000 - > 13.0x */
+   150, /* 1001 - > 15.0x */
+   160, /* 1010 - > 16.0x */
+   140, /* 1011 - > 14.0x */
+   -1, /* 1100 - > RESERVED (12.0x) */
+   155, /* 1101 - > 15.5x */
+   -1, /* 1110 - > RESERVED (13.0x) */
+   145, /* 1111 - > 14.5x */
+};
+
+
+/*
+ * Voltage scales. Div/Mod by 1000 to get actual voltage.
+ * Which scale to use depends on the VRM type in use.
+ */
+static int __initdata vrm85scales[32] = {
+   1250, 1200, 1150, 1100, 1050, 1800, 1750, 1700,
+   1650, 1600, 1550, 1500, 1450, 1400, 1350, 1300,
+   1275, 1225, 1175, 1125, 1075, 1825, 1775, 1725,
+   1675, 1625, 1575, 1525, 1475, 1425, 1375, 1325,
+};
+
+static int __initdata mobilevrmscales[32] = {
+   2000, 1950, 1900, 1850, 1800, 1750, 1700, 1650,
+   1600, 1550, 1500, 1450, 1500, 1350, 1300, -1,
+   1275, 1250, 1225, 1200, 1175, 1150, 1125, 1100,
+   1075, 1050, 1025, 1000, 975, 950, 925, -1,
+};
+
diff -uNr linux-2.4.23-vanilla/arch/i386/kernel/longrun.c
linux/arch/i386/kernel/longrun.c
--- linux-2.4.23-vanilla/arch/i386/kernel/longrun.c   1970-01-01
07:30:00.000000000 +0730
+++ linux/arch/i386/kernel/longrun.c   2003-08-25 22:58:48.000000000 +0800
@@ -0,0 +1,284 @@
+/*
+ * (C) 2002 - 2003 Dominik Brodowski <linux@(protected) >
+ *
+ * Licensed under the terms of the GNU GPL License version 2.
+ *
+ * BIG FAT DISCLAIMER: Work in progress code. Possibly *dangerous*
+ */
+
+#include <linux/kernel.h >
+#include <linux/module.h >
+#include <linux/init.h >
+#include <linux/slab.h >
+#include <linux/cpufreq.h >
+
+#include <asm/msr.h >
+#include <asm/processor.h >
+#include <asm/timex.h >
+
+static struct cpufreq_driver   longrun_driver;
+
+/**
+ * longrun_{low,high}_freq is needed for the conversion of cpufreq kHz
+ * values into per cent values. In TMTA microcode, the following is valid:
+ * performance_pctg = (current_freq - low_freq)/(high_freq - low_freq)
+ */
+static unsigned int longrun_low_freq, longrun_high_freq;
+
+
+/**
+ * longrun_get_policy - get the current LongRun policy
+ * @(protected): 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
+ * @(protected) - new policy
+ *
+ * Sets a new CPUFreq policy on LongRun-capable processors. This function
+ * has to be called with cpufreq_driver locked.
+ */
+static int longrun_set_policy(struct cpufreq_policy *policy)
+{
+   u32 msr_lo, msr_hi;
+   u32 pctg_lo, pctg_hi;
+
+   if (!policy)
+      return -EINVAL;
+
+   pctg_lo = (policy- >min - longrun_low_freq) /
+      ((longrun_high_freq - longrun_low_freq) / 100);
+   pctg_hi = (policy- >max - longrun_low_freq) /
+      ((longrun_high_freq - longrun_low_freq) / 100);
+
+   if (pctg_hi > 100)
+      pctg_hi = 100;
+   if (pctg_lo > pctg_hi)
+      pctg_lo = pctg_hi;
+
+   /* performance or economy mode */
+   rdmsr(MSR_TMTA_LONGRUN_FLAGS, msr_lo, msr_hi);
+   msr_lo &= 0xFFFFFFFE;
+   switch (policy- >policy) {
+   case CPUFREQ_POLICY_PERFORMANCE:
+      msr_lo |= 0x00000001;
+      break;
+   case CPUFREQ_POLICY_POWERSAVE:
+      break;
+   }
+   wrmsr(MSR_TMTA_LONGRUN_FLAGS, msr_lo, msr_hi);
+
+   /* lower and upper boundary */
+   rdmsr(MSR_TMTA_LONGRUN_CTRL, msr_lo, msr_hi);
+   msr_lo &= 0xFFFFFF80;
+   msr_hi &= 0xFFFFFF80;
+   msr_lo |= pctg_lo;
+   msr_hi |= pctg_hi;
+   wrmsr(MSR_TMTA_LONGRUN_CTRL, msr_lo, msr_hi);
+
+   return 0;
+}
+
+
+/**
+ * longrun_verify_poliy - verifies a new CPUFreq policy
+ *
+ * Validates a new CPUFreq policy. This function has to be called with
+ * cpufreq_driver locked.
+ */
+static int longrun_verify_policy(struct cpufreq_policy *policy)
+{
+   if (!policy)
+      return -EINVAL;
+
+   policy- >cpu = 0;
+   cpufreq_verify_within_limits(policy,
+      policy- >cpuinfo.min_freq,
+      policy- >cpuinfo.max_freq);
+
+   if (policy- >policy == CPUFREQ_POLICY_GOVERNOR)
+      return -EINVAL;
+
+   return 0;
+}
+
+
+/**
+ * longrun_determine_freqs - determines the lowest and highest possible
core frequency
+ *
+ * Determines the lowest and highest possible core frequencies on this CPU.
+ * This is necessary to calculate the performance percentage according to
+ * TMTA rules:
+ * performance_pctg = (target_freq - low_freq)/(high_freq - low_freq)
+ */
+static unsigned int __init longrun_determine_freqs(unsigned int *low_freq,
+                   unsigned int *high_freq)
+{
+   u32 msr_lo, msr_hi;
+   u32 save_lo, save_hi;
+   u32 eax, ebx, ecx, edx;
+   struct cpuinfo_x86 *c = cpu_data;
+
+   if (!low_freq || !high_freq)
+      return -EINVAL;
+
+   if (cpu_has(c, X86_FEATURE_LRTI)) {
+      /* if the LongRun Table Interface is present, the
+       * detection is a bit easier:
+       * For minimum frequency, read out the maximum
+       * level (msr_hi), write that into "currently
+       * selected level ", and read out the frequency.
+       * For maximum frequency, read out level zero.
+       */
+      /* minimum */
+      rdmsr(MSR_TMTA_LRTI_READOUT, msr_lo, msr_hi);
+      wrmsr(MSR_TMTA_LRTI_READOUT, msr_hi, msr_hi);
+      rdmsr(MSR_TMTA_LRTI_VOLT_MHZ, msr_lo, msr_hi);
+      *low_freq = msr_lo * 1000; /* to kHz */
+
+      /* maximum */
+      wrmsr(MSR_TMTA_LRTI_READOUT, 0, msr_hi);
+      rdmsr(MSR_TMTA_LRTI_VOLT_MHZ, msr_lo, msr_hi);
+      *high_freq = msr_lo * 1000; /* to kHz */
+
+      if (*low_freq > *high_freq)
+         *low_freq = *high_freq;
+      return 0;
+   }
+
+   /* set the upper border to the value determined during TSC init */
+   *high_freq = (cpu_khz / 1000);
+   *high_freq = *high_freq * 1000;
+
+   /* get current borders */
+   rdmsr(MSR_TMTA_LONGRUN_CTRL, msr_lo, msr_hi);
+   save_lo = msr_lo & 0x0000007F;
+   save_hi = msr_hi & 0x0000007F;
+
+   /* if current perf_pctg is larger than 90%, we need to decrease the
+    * upper limit to make the calculation more accurate.
+    */
+   cpuid(0x80860007, &eax, &ebx, &ecx, &edx);
+   if (ecx > 90) {
+      /* set to 0 to 80 perf_pctg */
+      msr_lo &= 0xFFFFFF80;
+      msr_hi &= 0xFFFFFF80;
+      msr_lo |= 0;
+      msr_hi |= 80;
+      wrmsr(MSR_TMTA_LONGRUN_CTRL, msr_lo, msr_hi);
+
+      /* read out current core MHz and current perf_pctg */
+      cpuid(0x80860007, &eax, &ebx, &ecx, &edx);
+
+      /* restore values */
+      wrmsr(MSR_TMTA_LONGRUN_CTRL, save_lo, save_hi);   
+   }
+
+   /* performance_pctg = (current_freq - low_freq)/(high_freq -
low_freq)
+    * eqals
+    * low_freq * ( 1 - perf_pctg) = (cur_freq - high_freq * perf_pctg)
+    *
+    * high_freq * perf_pctg is stored tempoarily into "ebx ".
+    */
+   ebx = (((cpu_khz / 1000) * ecx) / 100); /* to MHz */
+
+   if ((ecx > 95) || (ecx == 0) || (eax < ebx))
+      return -EIO;
+
+   edx = (eax - ebx) / (100 - ecx);
+   *low_freq = edx * 1000; /* back to kHz */
+
+   if (*low_freq > *high_freq)
+      *low_freq = *high_freq;
+
+   return 0;
+}
+
+
+static int longrun_cpu_init(struct cpufreq_policy *policy)
+{
+   int result = 0;
+
+   /* capability check */
+   if (policy- >cpu != 0)
+      return -ENODEV;
+
+   /* detect low and high frequency */
+   result = longrun_determine_freqs(&longrun_low_freq,
&longrun_high_freq);
+   if (result)
+      return result;
+
+   /* cpuinfo and default policy values */
+   policy- >cpuinfo.min_freq = longrun_low_freq;
+   policy- >cpuinfo.max_freq = longrun_high_freq;
+   policy- >cpuinfo.transition_latency = CPUFREQ_ETERNAL;
+   longrun_get_policy(policy);
+   
+   return 0;
+}
+
+
+static struct cpufreq_driver longrun_driver = {
+   .verify    = longrun_verify_policy,
+   .setpolicy    = longrun_set_policy,
+   .init      = longrun_cpu_init,
+   .name      = "longrun ",
+};
+
+
+/**
+ * longrun_init - initializes the Transmeta Crusoe LongRun CPUFreq driver
+ *
+ * Initializes the LongRun support.
+ */
+static int __init longrun_init(void)
+{
+   struct cpuinfo_x86 *c = cpu_data;
+
+   if (c- >x86_vendor != X86_VENDOR_TRANSMETA ||
+    !cpu_has(c, X86_FEATURE_LONGRUN))
+      return -ENODEV;
+
+   return cpufreq_register_driver(&longrun_driver);
+}
+
+
+/**
+ * longrun_exit - unregisters LongRun support
+ */
+static void __exit longrun_exit(void)
+{
+   cpufreq_unregister_driver(&longrun_driver);
+}
+
+
+MODULE_AUTHOR ( "Dominik Brodowski <linux@(protected) > ");
+MODULE_DESCRIPTION ( "LongRun driver for Transmeta Crusoe processors. ");
+MODULE_LICENSE ( "GPL ");
+
+module_init(longrun_init);
+module_exit(longrun_exit);
diff -uNr linux-2.4.23-vanilla/arch/i386/kernel/Makefile
linux/arch/i386/kernel/Makefile
--- linux-2.4.23-vanilla/arch/i386/kernel/Makefile   2003-11-29
02:26:19.000000000 +0800
+++ linux/arch/i386/kernel/Makefile   2003-12-01 23:37:33.000000000 +0800
@@ -14,7 +14,8 @@

O_TARGET := kernel.o

-export-objs := mca.o mtrr.o msr.o cpuid.o microcode.o i386_ksyms.o
time.o setup.o
+export-objs := mca.o mtrr.o msr.o cpuid.o microcode.o i386_ksyms.o
time.o setup.o \
+ setup.o speedstep-lib.o

obj-y   := process.o semaphore.o signal.o entry.o traps.o irq.o vm86.o \
      ptrace.o i8259.o ioport.o ldt.o setup.o time.o sys_i386.o \
@@ -43,5 +44,17 @@
obj-$(CONFIG_X86_IO_APIC)   += io_apic.o
obj-$(CONFIG_X86_VISWS_APIC)   += visws_apic.o
obj-$(CONFIG_EDD)    += edd.o
+obj-$(CONFIG_X86_POWERNOW_K6) += powernow-k6.o
+obj-$(CONFIG_X86_POWERNOW_K7) += powernow-k7.o
+obj-$(CONFIG_X86_POWERNOW_K8) += powernow-k8.o
+obj-$(CONFIG_X86_LONGHAUL) += longhaul.o
+obj-$(CONFIG_X86_SPEEDSTEP_CENTRINO) += speedstep-centrino.o
+obj-$(CONFIG_X86_SPEEDSTEP_PIIX4) += speedstep-piix4.o speedstep-lib.o
+obj-$(CONFIG_X86_SPEEDSTEP_SMI) += speedstep-smi.o speedstep-lib.o
+obj-$(CONFIG_X86_SPEEDSTEP_ICH) += speedstep-ich.o speedstep-lib.o
+obj-$(CONFIG_X86_P4_CLOCKMOD) += p4-clockmod.o
+obj-$(CONFIG_ELAN_CPUFREQ) += elanfreq.o
+obj-$(CONFIG_X86_LONGRUN) += longrun.o
+obj-$(CONFIG_X86_GX_SUSPMOD) += gx-suspmod.o

include $(TOPDIR)/Rules.make
diff -uNr linux-2.4.23-vanilla/arch/i386/kernel/p4-clockmod.c
linux/arch/i386/kernel/p4-clockmod.c
--- linux-2.4.23-vanilla/arch/i386/kernel/p4-clockmod.c   1970-01-01
07:30:00.000000000 +0730
+++ linux/arch/i386/kernel/p4-clockmod.c   2003-08-25
22:58:48.000000000 +0800
@@ -0,0 +1,271 @@
+/*
+ *   Pentium 4/Xeon CPU on demand clock modulation/speed scaling
+ *   (C) 2002 - 2003 Dominik Brodowski <linux@(protected) >
+ *   (C) 2002 Zwane Mwaikambo <zwane@(protected) >
+ *   (C) 2002 Arjan van de Ven <arjanv@(protected) >
+ *   (C) 2002 Tora T. Engstad
+ *   All Rights Reserved
+ *
+ *   This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * The author(s) of this software shall not be held liable for damages
+ * of any nature resulting due to the use of this software. This
+ * software is provided AS-IS with no warranties.
+ *   
+ *   Date      Errata         Description
+ *   20020525   N44, O17   12.5% or 25% DC causes lockup
+ *
+ */
+
+#include <linux/kernel.h >
+#include <linux/module.h >
+#include <linux/init.h >
+#include <linux/smp.h >
+#include <linux/cpufreq.h >
+#include <linux/slab.h >
+#include <linux/sched.h >
+
+#include <asm/processor.h >
+#include <asm/msr.h >
+#include <asm/timex.h >
+
+#define PFX    "cpufreq: "
+
+/*
+ * Duty Cycle (3bits), note DC_DISABLE is not specified in
+ * intel docs i just use it to mean disable
+ */
+enum {
+   DC_RESV, DC_DFLT, DC_25PT, DC_38PT, DC_50PT,
+   DC_64PT, DC_75PT, DC_88PT, DC_DISABLE
+};
+
+#define DC_ENTRIES   8
+
+
+static int has_N44_O17_errata[NR_CPUS];
+static int stock_freq;
+
+
+static int cpufreq_p4_setdc(unsigned int cpu, unsigned int newstate)
+{
+   u32 l, h;
+   unsigned long cpus_allowed;
+   struct cpufreq_freqs freqs;
+   int hyperthreading = 0;
+   int affected_cpu_map = 0;
+   int sibling = 0;
+
+   if (!cpu_online(cpu) || (newstate > DC_DISABLE) ||
+      (newstate == DC_RESV))
+      return -EINVAL;
+
+   /* switch to physical CPU where state is to be changed*/
+   cpus_allowed = current- >cpus_allowed;
+
+   /* only run on CPU to be set, or on its sibling */
+   affected_cpu_map = 1 < < cpu;
+#ifdef CONFIG_X86_HT
+   hyperthreading = ((cpu_has_ht) && (smp_num_siblings == 2));
+   if (hyperthreading) {
+      sibling = cpu_sibling_map[cpu];
+      affected_cpu_map |= (1 < < sibling);
+   }
+#endif
+   set_cpus_allowed(current, affected_cpu_map);
+   BUG_ON(!(affected_cpu_map & (1 < < smp_processor_id())));
+
+   /* get current state */
+   rdmsr(MSR_IA32_THERM_CONTROL, l, h);
+   if (l & 0x10) {
+      l = l > > 1;
+      l &= 0x7;
+   } else
+      l = DC_DISABLE;
+   
+   if (l == newstate) {
+      set_cpus_allowed(current, cpus_allowed);
+      return 0;
+   } else if (l == DC_RESV) {
+      printk(KERN_ERR PFX "BIG FAT WARNING: currently in invalid
setting\n ");
+   }
+
+   /* notifiers */
+   freqs.old = stock_freq * l / 8;
+   freqs.new = stock_freq * newstate / 8;
+   freqs.cpu = cpu;
+   cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
+   if (hyperthreading) {
+      freqs.cpu = sibling;
+      cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
+   }
+
+   rdmsr(MSR_IA32_THERM_STATUS, l, h);
+#if 0
+   if (l & 0x01)
+      printk(KERN_DEBUG PFX "CPU#%d currently thermal
throttled\n ", cpu);
+#endif
+   if (has_N44_O17_errata[cpu] && (newstate == DC_25PT || newstate ==
DC_DFLT))
+      newstate = DC_38PT;
+
+   rdmsr(MSR_IA32_THERM_CONTROL, l, h);
+   if (newstate == DC_DISABLE) {
+      /* printk(KERN_INFO PFX "CPU#%d disabling modulation\n ",
cpu); */
+      wrmsr(MSR_IA32_THERM_CONTROL, l & ~(1 < <4), h);
+   } else {
+      /* printk(KERN_INFO PFX "CPU#%d setting duty cycle to
%d%%\n ",
+         cpu, ((125 * newstate) / 10)); */
+      /* bits 63 - 5   : reserved
+       * bit 4   : enable/disable
+       * bits 3-1   : duty cycle
+       * bit 0   : reserved
+       */
+      l = (l & ~14);
+      l = l | (1 < <4) | ((newstate & 0x7) < <1);
+      wrmsr(MSR_IA32_THERM_CONTROL, l, h);
+   }
+
+   set_cpus_allowed(current, cpus_allowed);
+
+   /* notifiers */
+   cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
+   if (hyperthreading) {
+      freqs.cpu = cpu;
+      cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
+   }
+
+   return 0;
+}
+
+
+static struct cpufreq_frequency_table p4clockmod_table[] = {
+   {DC_RESV, CPUFREQ_ENTRY_INVALID},
+   {DC_DFLT, 0},
+   {DC_25PT, 0},
+   {DC_38PT, 0},
+   {DC_50PT, 0},
+   {DC_64PT, 0},
+   {DC_75PT, 0},
+   {DC_88PT, 0},
+   {DC_DISABLE, 0},
+   {DC_RESV, CPUFREQ_TABLE_END},
+};
+
+
+static int cpufreq_p4_target(struct cpufreq_policy *policy,
+          unsigned int target_freq,
+          unsigned int relation)
+{
+   unsigned int newstate = DC_RESV;
+
+   if (cpufreq_frequency_table_target(policy, &p4clockmod_table[0],
target_freq, relation, &newstate))
+      return -EINVAL;
+
+   cpufreq_p4_setdc(policy- >cpu, p4clockmod_table[newstate].index);
+
+   return 0;
+}
+
+
+static int cpufreq_p4_verify(struct cpufreq_policy *policy)
+{
+   return cpufreq_frequency_table_verify(policy, &p4clockmod_table[0]);
+}
+
+
+static int cpufreq_p4_cpu_init(struct cpufreq_policy *policy)
+{
+   struct cpuinfo_x86 *c = &cpu_data[policy- >cpu];
+   int cpuid = 0;
+   unsigned int i;
+
+   /* Errata workaround */
+   cpuid = (c- >x86 < < 8) | (c- >x86_model < < 4) | c- >x86_mask;
+   switch (cpuid) {
+   case 0x0f07:
+   case 0x0f0a:
+   case 0x0f11:
+   case 0x0f12:
+      has_N44_O17_errata[policy- >cpu] = 1;
+   }
+   
+   /* get frequency */
+   if (!stock_freq) {
+      if (cpu_khz)
+         stock_freq = cpu_khz;
+      else {
+         printk(KERN_INFO PFX "unknown core frequency -
please use module parameter 'stock_freq '\n ");
+         return -EINVAL;
+      }
+   }
+
+   /* table init */
+   for (i=1; (p4clockmod_table[i].frequency != CPUFREQ_TABLE_END); i++)
{
+      if ((i <2) && (has_N44_O17_errata[policy- >cpu]))
+         p4clockmod_table[i].frequency =
CPUFREQ_ENTRY_INVALID;
+      else
+         p4clockmod_table[i].frequency = (stock_freq * i)/8;
+   }
+   
+   /* cpuinfo and default policy values */
+   policy- >policy = CPUFREQ_POLICY_PERFORMANCE;
+   policy- >cpuinfo.transition_latency = 1000;
+   policy- >cur = stock_freq;
+
+   return cpufreq_frequency_table_cpuinfo(policy,
&p4clockmod_table[0]);
+}
+
+
+static int cpufreq_p4_cpu_exit(struct cpufreq_policy *policy)
+{
+   return cpufreq_p4_setdc(policy- >cpu, DC_DISABLE);
+}
+
+static struct cpufreq_driver p4clockmod_driver = {
+   .verify    = cpufreq_p4_verify,
+   .target      = cpufreq_p4_target,
+   .init      = cpufreq_p4_cpu_init,
+   .exit      = cpufreq_p4_cpu_exit,
+   .name      = "p4-clockmod ",
+};
+
+
+static int __init cpufreq_p4_init(void)
+{   
+   struct cpuinfo_x86 *c = cpu_data;
+
+   /*
+    * THERM_CONTROL is architectural for IA32 now, so
+    * we can rely on the capability checks
+    */
+   if (c- >x86_vendor != X86_VENDOR_INTEL)
+      return -ENODEV;
+
+   if (!test_bit(X86_FEATURE_ACPI, c- >x86_capability) ||
+      !test_bit(X86_FEATURE_ACC, c- >x86_capability))
+      return -ENODEV;
+
+   printk(KERN_INFO PFX "P4/Xeon(TM) CPU On-Demand Clock Modulation
available\n ");
+
+   return cpufreq_register_driver(&p4clockmod_driver);
+}
+
+
+static void __exit cpufreq_p4_exit(void)
+{
+   cpufreq_unregister_driver(&p4clockmod_driver);
+}
+
+
+MODULE_PARM(stock_freq, "i ");
+
+MODULE_AUTHOR ( "Zwane Mwaikambo <zwane@(protected) > ");
+MODULE_DESCRIPTION ( "cpufreq driver for Pentium(TM) 4/Xeon(TM) ");
+MODULE_LICENSE ( "GPL ");
+
+module_init(cpufreq_p4_init);
+module_exit(cpufreq_p4_exit);
+
diff -uNr linux-2.4.23-vanilla/arch/i386/kernel/powernow-k6.c
linux/arch/i386/kernel/powernow-k6.c
--- linux-2.4.23-vanilla/arch/i386/kernel/powernow-k6.c   1970-01-01
07:30:00.000000000 +0730
+++ linux/arch/i386/kernel/powernow-k6.c   2003-08-25
22:58:48.000000000 +0800
@@ -0,0 +1,234 @@
+/*
+ * This file was based upon code in Powertweak Linux
( http://powertweak.sf.net )
+ * (C) 2000-2003 Dave Jones, Arjan van de Ven, Janne Pankala, Dominik
Brodowski.
+ *
+ * Licensed under the terms of the GNU GPL License version 2.
+ *
+ * BIG FAT DISCLAIMER: Work in progress code. Possibly *dangerous*
+ */
+
+#include <linux/kernel.h >
+#include <linux/module.h >
+#include <linux/init.h >
+#include <linux/cpufreq.h >
+#include <linux/ioport.h >
+#include <linux/slab.h >
+
+#include <asm/msr.h >
+#include <asm/timex.h >
+#include <asm/io.h >
+
+
+#define POWERNOW_IOPORT 0xfff0 /* it doesn 't matter where, as long
+                as it is unused */
+
+static unsigned int busfreq; /* FSB, in 10 kHz */
+static unsigned int max_multiplier;
+
+
+/* Clock ratio multiplied by 10 - see table 27 in AMD#23446 */
+static struct cpufreq_frequency_table clock_ratio[] = {
+   {45, /* 000 - > 4.5x */ 0},
+   {50, /* 001 - > 5.0x */ 0},
+   {40, /* 010 - > 4.0x */ 0},
+   {55, /* 011 - > 5.5x */ 0},
+   {20, /* 100 - > 2.0x */ 0},
+   {30, /* 101 - > 3.0x */ 0},
+   {60, /* 110 - > 6.0x */ 0},
+   {35, /* 111 - > 3.5x */ 0},
+   {0, CPUFREQ_TABLE_END}
+};
+
+
+/**
+ * powernow_k6_get_cpu_multiplier - returns the current FSB multiplier
+ *
+ * Returns the current setting of the frequency multiplier. Core clock
+ * speed is frequency of the Front-Side Bus multiplied with this value.
+ */
+static int powernow_k6_get_cpu_multiplier(void)
+{
+   u64 invalue = 0;
+   u32 msrval;
+   
+   msrval = POWERNOW_IOPORT + 0x1;
+   wrmsr(MSR_K6_EPMR, msrval, 0); /* enable the PowerNow port */
+   invalue=inl(POWERNOW_IOPORT + 0x8);
+   msrval = POWERNOW_IOPORT + 0x0;
+   wrmsr(MSR_K6_EPMR, msrval, 0); /* disable it again */
+
+   return clock_ratio[(invalue > > 5)&7].index;
+}
+
+
+/**
+ * powernow_k6_set_state - set the PowerNow! multiplier
+ * @(protected): 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
+ * @(protected): 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
+ * @(protected) - new policy
+ *
+ * sets a new CPUFreq policy
+ */
+static int powernow_k6_target (struct cpufreq_policy *policy,
+          unsigned int target_freq,
+          unsigned int relation)
+{
+   unsigned int newstate = 0;
+
+   if (cpufreq_frequency_table_target(policy, &clock_ratio[0],
target_freq, relation, &newstate))
+      return -EINVAL;
+
+   powernow_k6_set_state(newstate);
+
+   return 0;
+}
+
+
+static int powernow_k6_cpu_init(struct cpufreq_policy *policy)
+{
+   unsigned int i;
+
+   if (policy- >cpu != 0)
+      return -ENODEV;
+
+   /* get frequencies */
+   max_multiplier = powernow_k6_get_cpu_multiplier();
+   busfreq = cpu_khz / max_multiplier;
+
+   /* table init */
+    for (i=0; (clock_ratio[i].frequency != CPUFREQ_TABLE_END); i++) {
+      if (clock_ratio[i].index > max_multiplier)
+         clock_ratio[i].frequency = CPUFREQ_ENTRY_INVALID;
+      else
+         clock_ratio[i].frequency = busfreq *
clock_ratio[i].index;
+   }
+
+   /* cpuinfo and default policy values */
+   policy- >policy = CPUFREQ_POLICY_PERFORMANCE;
+   policy- >cpuinfo.transition_latency = CPUFREQ_ETERNAL;
+   policy- >cur = busfreq * max_multiplier;
+
+   return cpufreq_frequency_table_cpuinfo(policy, &clock_ratio[0]);
+}
+
+
+static int powernow_k6_cpu_exit(struct cpufreq_policy *policy)
+{
+   unsigned int i;
+   for (i=0; i <8; i++) {
+      if (i==max_multiplier)
+         powernow_k6_set_state(i);
+   }
+   return 0;
+}
+
+
+static struct cpufreq_driver powernow_k6_driver = {
+   .verify    = powernow_k6_verify,
+   .target    = powernow_k6_target,
+   .init      = powernow_k6_cpu_init,
+   .exit      = powernow_k6_cpu_exit,
+   .name      = "powernow-k6 ",
+};
+
+
+/**
+ * powernow_k6_init - initializes the k6 PowerNow! CPUFreq driver
+ *
+ * Initializes the K6 PowerNow! support. Returns -ENODEV on unsupported
+ * devices, -EINVAL or -ENOMEM on problems during initiatization, and zero
+ * on success.
+ */
+static int __init powernow_k6_init(void)
+{   
+   struct cpuinfo_x86 *c = cpu_data;
+
+   if ((c- >x86_vendor != X86_VENDOR_AMD) || (c- >x86 != 5) ||
+      ((c- >x86_model != 12) && (c- >x86_model != 13)))
+      return -ENODEV;
+
+   if (!request_region(POWERNOW_IOPORT, 16, "PowerNow! ")) {
+      printk( "cpufreq: PowerNow IOPORT region already used.\n ");
+      return -EIO;
+   }
+
+   if (cpufreq_register_driver(&powernow_k6_driver)) {
+      release_region (POWERNOW_IOPORT, 16);
+      return -EINVAL;
+   }
+
+   return 0;
+}
+
+
+/**
+ * powernow_k6_exit - unregisters AMD K6-2+/3+ PowerNow! support
+ *
+ * Unregisters AMD K6-2+ / K6-3+ PowerNow! support.
+ */
+static void __exit powernow_k6_exit(void)
+{
+   cpufreq_unregister_driver(&powernow_k6_driver);
+   release_region (POWERNOW_IOPORT, 16);
+}
+
+
+MODULE_AUTHOR ( "Arjan van de Ven <arjanv@(protected) >, Dave Jones
<davej@(protected) >, Dominik Brodowski <linux@(protected) > ");
+MODULE_DESCRIPTION ( "PowerNow! driver for AMD K6-2+ / K6-3+ processors. ");
+MODULE_LICENSE ( "GPL ");
+
+module_init(powernow_k6_init);
+module_exit(powernow_k6_exit);
diff -uNr linux-2.4.23-vanilla/arch/i386/kernel/powernow-k7.c
linux/arch/i386/kernel/powernow-k7.c
--- linux-2.4.23-vanilla/arch/i386/kernel/powernow-k7.c   1970-01-01
07:30:00.000000000 +0730
+++ linux/arch/i386/kernel/powernow-k7.c   2003-09-20
00:47:44.000000000 +0800
@@ -0,0 +1,422 @@
+/*
+ * AMD K7 Powernow driver.
+ * (C) 2003 Dave Jones <davej@(protected) > on behalf of SuSE Labs.
+ * (C) 2003 Dave Jones <davej@(protected) >
+ *
+ * Licensed under the terms of the GNU GPL License version 2.
+ * Based upon datasheets & sample CPUs kindly provided by AMD.
+ *
+ * BIG FAT DISCLAIMER: Work in progress code. Possibly *dangerous*
+ *
+ * Errata 5: Processor may fail to execute a FID/VID change in presence of
interrupt.
+ * - We cli/sti on stepping A0 CPUs around the FID/VID transition.
+ * Errata 15: Processors with half frequency multipliers may hang upon
wakeup from disconnect.
+ * - We disable half multipliers if ACPI is used on A0 stepping CPUs.
+ */
+
+#include <linux/kernel.h >
+#include <linux/module.h >
+#include <linux/init.h >
+#include <linux/cpufreq.h >
+#include <linux/slab.h >
+#include <linux/string.h >
+
+#include <asm/msr.h >
+#include <asm/timex.h >
+#include <asm/io.h >
+#include <asm/system.h >
+
+#include "powernow-k7.h "
+
+#define DEBUG
+
+#ifdef DEBUG
+#define dprintk(msg...) printk(msg)
+#else
+#define dprintk(msg...) do { } while(0)
+#endif
+
+#define PFX "powernow: "
+
+
+struct psb_s {
+   u8 signature[10];
+   u8 tableversion;
+   u8 flags;
+   u16 settlingtime;
+   u8 reserved1;
+   u8 numpst;
+};
+
+struct pst_s {
+   u32 cpuid;
+   u8 fsbspeed;
+   u8 maxfid;
+   u8 startvid;
+   u8 numpstates;
+};
+
+
+/* divide by 1000 to get VID. */
+static int mobile_vid_table[32] = {
+ 2000, 1950, 1900, 1850, 1800, 1750, 1700, 1650,
+ 1600, 1550, 1500, 1450, 1400, 1350, 1300, 0,
+ 1275, 1250, 1225, 1200, 1175, 1150, 1125, 1100,
+ 1075, 1050, 1024, 1000, 975, 950, 925, 0,
+};
+
+/* divide by 10 to get FID. */
+static int fid_codes[32] = {
+ 110, 115, 120, 125, 50, 55, 60, 65,
+ 70, 75, 80, 85, 90, 95, 100, 105,
+ 30, 190, 40, 200, 130, 135, 140, 210,
+ 150, 225, 160, 165, 170, 180, -1, -1,
+};
+
+static struct cpufreq_frequency_table *powernow_table;
+
+static unsigned int can_scale_bus;
+static unsigned int can_scale_vid;
+static unsigned int minimum_speed=-1;
+static unsigned int maximum_speed;
+static unsigned int number_scales;
+static unsigned int fsb;
+static unsigned int latency;
+static char have_a0;
+
+
+static int check_powernow(void)
+{
+   struct cpuinfo_x86 *c = cpu_data;
+   unsigned int maxei, eax, ebx, ecx, edx;
+
+   if (c- >x86_vendor != X86_VENDOR_AMD) {
+      printk (KERN_INFO PFX "AMD processor not detected.\n ");
+      return 0;
+   }
+
+   if (c- >x86 !=6) {
+      printk (KERN_INFO PFX "This module only works with AMD K7
CPUs\n ");
+      return 0;
+   }
+
+   printk (KERN_INFO PFX "AMD K7 CPU detected.\n ");
+
+   if ((c- >x86_model == 6) && (c- >x86_mask == 0)) {
+      printk (KERN_INFO PFX "K7 660[A0] core detected, enabling
errata workarounds\n ");
+      have_a0 = 1;
+   }
+
+   /* Get maximum capabilities */
+   maxei = cpuid_eax (0x80000000);
+   if (maxei < 0x80000007) {   /* Any powernow info ? */
+      printk (KERN_INFO PFX "No powernow capabilities
detected\n ");
+      return 0;
+   }
+
+   cpuid(0x80000007, &eax, &ebx, &ecx, &edx);
+   printk (KERN_INFO PFX "PowerNOW! Technology present. Can scale: ");
+
+   if (edx & 1 < < 1) {
+      printk ( "frequency ");
+      can_scale_bus=1;
+   }
+
+   if ((edx & (1 < < 1 | 1 < < 2)) == 0x6)
+      printk ( " and ");
+
+   if (edx & 1 < < 2) {<