diff -urN ../kernel-2.4.20/linux-2.4.20/Documentation/Configure.help linux-2.4.20/Documentation/Configure.help --- ../kernel-2.4.20/linux-2.4.20/Documentation/Configure.help 2003-09-10 00:38:33.000000000 -0400 +++ linux-2.4.20/Documentation/Configure.help 2003-09-09 19:54:16.000000000 -0400 @@ -26288,16 +26288,6 @@ Say Y if you want support for the ARM926T processor. Otherwise, say N. -Support CPU clock change (EXPERIMENTAL) -CONFIG_CPU_FREQ - CPU clock scaling allows you to change the clock speed of the - running CPU on the fly. This is a nice method to save battery power, - because the lower the clock speed, the less power the CPU - consumes. Note that this driver doesn't automatically change the CPU - clock speed, you need some userland tools (which still have to be - written) to implement the policy. If you don't understand what this - is all about, it's safe to say 'N'. - SiS CONFIG_DRM_SIS Choose this option if you have a SIS graphics card. AGP support is @@ -26506,6 +26496,147 @@ CONFIG_IPMI_WATCHDOG This enables the IPMI watchdog timer. +CONFIG_CPU_FREQ + Clock scaling allows you to change the clock speed of CPUs on the + fly. This is a nice method to save battery power on notebooks, + because the lower the clock speed, the less power the CPU consumes. + + For more information, take a look at linux/Documentation/cpu-freq. + + If in doubt, say N. + +CONFIG_CPU_FREQ_TABLE + Many CPUFreq drivers use these helpers, so only say N here if + the CPUFreq driver of your choice doesn't need these helpers. + + If in doubt, say Y. + +CONFIG_CPU_FREQ_24_API + This enables the /proc/sys/cpu/ sysctl interface for controlling + CPUFreq, as known from the 2.4.-kernel patches for CPUFreq. 2.5 + uses /proc/cpufreq instead. Please note that some drivers do not + work well with the 2.4. /proc/sys/cpu sysctl interface, so if in + doubt, say N here. + + For details, take a look at linux/Documentation/cpu-freq. + + If in doubt, say N. + +CONFIG_X86_POWERNOW_K6 + This adds the CPUFreq driver for mobile AMD K6-2+ and mobile + AMD K6-3+ processors. + + For details, take a look at linux/Documentation/cpu-freq. + + If in doubt, say N. + +CONFIG_X86_POWERNOW_K7 + This adds the CPUFreq driver for mobile AMD Athlon/Duron + K7 processors. + + For details, take a look at linux/Documentation/cpu-freq. + + If in doubt, say N. + +CONFIG_X86_P4_CLOCKMOD + This adds the CPUFreq driver for Intel Pentium 4 / XEON + processors. + + For details, take a look at linux/Documentation/cpu-freq. + + If in doubt, say N. + +CONFIG_ELAN_CPUFREQ + This adds the CPUFreq driver for AMD Elan SC400 and SC410 + processors. + + You need to specify the processor maximum speed as boot + parameter: elanfreq=maxspeed (in kHz) or as module + parameter "max_freq". + + For details, take a look at linux/Documentation/cpu-freq. + + If in doubt, say N. + +CONFIG_X86_LONGHAUL + This adds the CPUFreq driver for VIA Samuel/CyrixIII, + VIA Cyrix Samuel/C3, VIA Cyrix Ezra and VIA Cyrix Ezra-T + processors. + + If you do not want to scale the Front Side Bus or voltage, + pass the module parameter "dont_scale_fsb=1" or + "dont_scale_voltage=1". Additionally, it is advised that + you pass the current Front Side Bus speed (in MHz) to + this module as module parameter "current_fsb", e.g. + "current_fsb=133" for a Front Side Bus speed of 133 MHz. + + For details, take a look at linux/Documentation/cpu-freq. + + If in doubt, say N. + +CONFIG_X86_SPEEDSTEP_PIIX4 + This adds the CPUFreq driver for certain mobile Intel Pentium III + (Coppermine), all mobile Intel Pentium III-M (Tualatin) and all + mobile Intel Pentium 4 P4-Ms on chipsets with an Intel PIIX4 + southbridge. + + For details, take a look at linux/Documentation/cpu-freq. + + If in doubt, say N. + +CONFIG_X86_SPEEDSTEP_ICH + This adds the CPUFreq driver for certain mobile Intel Pentium III + (Coppermine), all mobile Intel Pentium III-M (Tualatin) and all + mobile Intel Pentium 4 P4-Ms on chipsets with an Intel ICH2, ICH3, + or ICH4 southbridge. + + For details, take a look at linux/Documentation/cpu-freq. + + If in doubt, say N. + +CONFIG_X86_SPEEDSTEP_CENTRINO + This adds the CPUFreq driver for Enhanced SpeedStep enabled + mobile CPUs. This means Intel Pentium M (Centrino) CPUs. + + For details, take a look at linux/Documentation/cpu-freq. + + If in doubt, say N. + +CONFIG_X86_SPEEDSTEP_SMI + This adds the CPUFreq driver for certain mobile Intel Pentium III + (Coppermine), all mobile Intel Pentium III-M (Tualatin) + on systems which have an Intel 440BX/ZX/MX southbridge. + + For details, take a look at linux/Documentation/cpu-freq. + + If in doubt, say N. + +CONFIG_X86_LONGRUN + This adds the CPUFreq driver for Transmeta Crusoe processors which + support LongRun. + + For details, take a look at linux/Documentation/cpu-freq. + + If in doubt, say N. + +CONFIG_X86_GX_SUSPMOD + This adds the CPUFreq driver for NatSemi Geode processors which + support suspend modulation. + + For details, take a look at linux/Documentation/cpu-freq. + + If in doubt, say N. + +CONFIG_CPU_FREQ_GOV_USERSPACE + Enable this cpufreq governor when you either want to set the + CPU frequency manually or when an userspace programm shall + be able to set the CPU dynamically, like on LART + ( http://www.lart.tudelft.nl/ ) + + For details, take a look at linux/Documentation/cpu-freq. + + If in doubt, say Y. + # # A couple of things I keep forgetting: # capitalize: AppleTalk, Ethernet, DOS, DMA, FAT, FTP, Internet, File ../kernel-2.4.20/linux-2.4.20/Documentation/cpufreq is a regular file while file linux-2.4.20/Documentation/cpufreq is a directory diff -urN ../kernel-2.4.20/linux-2.4.20/Documentation/cpufreq-old linux-2.4.20/Documentation/cpufreq-old --- ../kernel-2.4.20/linux-2.4.20/Documentation/cpufreq-old 1969-12-31 19:00:00.000000000 -0500 +++ linux-2.4.20/Documentation/cpufreq-old 2003-08-25 10:58:44.000000000 -0400 @@ -0,0 +1,332 @@ + CPU frequency and voltage scaling code in the Linux(TM) kernel + + + L i n u x C P U F r e q + + + + + Dominik Brodowski + + + + Clock scaling allows you to change the clock speed of the CPUs on the + fly. This is a nice method to save battery power, because the lower + the clock speed, the less power the CPU consumes. + + + +Contents: +--------- +1. Supported architectures +2. User interface +2.1 Sample script for command line interface +3. CPUFreq core and interfaces +3.1 General information +3.2 CPUFreq notifiers +3.3 CPUFreq architecture drivers +4. Mailing list and Links + + + +1. Supported architectures +========================== + +Some architectures detect the lowest and highest possible speed +settings, while others rely on user information on this. For the +latter, a boot parameter is required, for the former, you can specify +one to set the limits between speed settings may occur. +The boot parameter has the following syntax: + + cpufreq=minspeed-maxspeed + +with both minspeed and maxspeed being given in kHz. To set the lower +limit to 59 MHz and the upper limit to 221 MHz, specify: + + cpufreq=59000-221000 + +Check the "Speed Limits Detection" information below on whether +the driver detects the lowest and highest allowed speed setting +automatically. + + +ARM Integrator: + SA 1100, SA1110 +-------------------------------- + Speed Limits Detection: On Integrators, the minimum speed is set + and the maximum speed has to be specified using the boot + parameter. On SA11x0s, the frequencies are fixed (59 - 287 MHz) + + +AMD Elan: + SC400, SC410 +-------------------------------- + Speed Limits Detection: Not implemented. You need to specify the + minimum and maximum frequency in the boot parameter (see above). + + +VIA Cyrix Longhaul: + VIA Samuel/CyrixIII, VIA Cyrix Samuel/C3, + VIA Cyrix Ezra, VIA Cyrix Ezra-T +-------------------------------- + Speed Limits Detection: working. No need for boot parameters. + NOTE: Support for certain processors is currently disabled, + waiting on updated docs from VIA. + + +Intel SpeedStep: + certain mobile Intel Pentium III (Coppermine), and all mobile + Intel Pentium III-M (Tulatin) and mobile Intel Pentium 4 P4-Ms. +-------------------------------- + Speed Limits Detection: working. No need for boot parameters. + NOTE: + 1.) mobile Intel Pentium III (Coppermine): + The SpeedStep interface may only be used on SpeedStep + capable processors. Unforunately, due to lack of documentation, + such detection is not yet possible on mobile Intel PIII + (Coppermine) processors. In order to activate SpeedStep on such a + processor, you have to remove one line manually in + linux/drivers/arch/i386/speedstep.c + + +P4 CPU Clock Modulation: + Intel Pentium 4 Xeon processors +-------------------------------- + Speed Limits Detection: Not implemented. You need to specify the + minimum and maximum frequency in the boot parameter (see above). + + + +2. User Interface +================= + +CPUFreq uses a "sysctl" interface which is located in + /proc/sys/cpu/0/ on UP (uniprocessor) kernels, or + /proc/sys/cpu/any/ on SMP (symmetric multiprocessoring) kernels. + + +In this directory, you will find three files of importance for +CPUFreq: speed-max, speed-min, and speed: + +speed shows the current CPU frequency in kHz, +speed-min the minimal supported CPU frequency, and +speed-max the maximal supported CPU frequency. + +Please note that you might have to specify these limits as a boot +parameter depending on the architecture (see above). + + +To change the CPU frequency, "echo" the desired CPU frequency (in kHz) +to speed. For example, to set the CPU speed to the lowest/highest +allowed frequency do: + +root@notebook:# cat /proc/sys/cpu/0/speed-min > /proc/sys/cpu/0/speed +root@notebook:# cat /proc/sys/cpu/0/speed-max > /proc/sys/cpu/0/speed + + +2.1 Sample script for command line interface +********************************************** + + +Michael Ossmann has written a small command line +interface for the infinitely lazy. + +#!/bin/bash +# +# /usr/local/bin/freq +# simple command line interface to cpufreq + +[ -n "$1" ] && case "$1" in + "min" ) + # set frequency to minimum + cat /proc/sys/cpu/0/speed-min >/proc/sys/cpu/0/speed + ;; + "max" ) + # set frequency to maximum + cat /proc/sys/cpu/0/speed-max >/proc/sys/cpu/0/speed + ;; + * ) + echo "Usage: $0 [min|max]" + echo " min: set frequency to minimum and display new frequency" + echo " max: set frequency to maximum and display new frequency" + echo " no options: display current frequency" + exit 1 + ;; +esac + +# display current frequency +cat /proc/sys/cpu/0/speed +exit 0 + + + +3. CPUFreq core and interfaces +=============================== + +3.1 General information +************************* + +The CPUFreq core code is located in linux/kernel/cpufreq.c. This +cpufreq code offers a standardized interface for the CPUFreq +architecture drivers (those pieces of code that do the actual +frequency transition), as well as to "notifiers". These are device +drivers or other part of the kernel that need to be informed of +frequency changes (like timing code) or even need to force certain +speed limits (like LCD drivers on ARM architecture). Aditionally, the +kernel "constant" loops_per_jiffy is updated on frequency changes +here. + + +3.2 CPUFreq notifiers +*********************** + +CPUFreq notifiers are kernel code that need to be called to either +a) define certain minimum or maximum speed settings, +b) be informed of frequency changes in advance of the transition, or +c) be informed of frequency changes directly after the transition. + +A standard kernel notifier interface is offered for this. See +linux/include/linux/notifier.h for details on notifiers. + + +Data and value passed to CPUFreq notifiers +------------------------------------------ +The second argument passed to any notifier is an unsigned int stating +the phase of the transition: +CPUFREQ_MINMAX during the process of determing a valid new CPU + frequency, +CPUFREQ_PRECHANGE right before the transition, and +CPUFREQ_POSTCHANGE right after the transition. + +The third argument, a void *pointer, points to a struct +cpufreq_freqs. This consists of four values: min, max, cur and new. + +min and max are the current speed limits. Please note: Never update +these values directly, use cpufreq_updateminmax(struct cpufreq_freqs +*freqs, unsigned int min, unsigned int max) instead. cur is the +current/old speed, and new is the new speed, but might only be valid +on CPUFREQ_PRECHANGE or CPUFREQ_POSTCHANGE. + +Each notifier gets called all three times on any transition: + +CPUFREQ_MINMAX +Here the notifier is supposed to update the min and max values to the +limits the protected device / kernel code needs. As stated above, +always use cpufreq_updateminmax for this. + +CPUFREQ_PRECHANGE +CPUFREQ_POSTCHANGE +Here the notifier is supposed to update all internal (e.g. device +driver) code which is dependend on the CPU frequency. + + +3.3 CPUFreq architecture drivers +********************************** + +CPUFreq architecture drivers are the pieces of kernel code that +actually perform CPU frequency transitions. These need to be +initialised seperately (seperate initcalls), and may be +modularized. They interact with the CPUFreq core in the following way: + + +cpufreq_register() +------------------ +cpufreq_register registers an arch driver to the CPUFreq core. Please +note that only one arch driver may be registered at any time, -EBUSY +is returned when an arch driver is already registered. The argument to +cpufreq_register, cpufreq_driver_t driver, is described later. + + +cpufreq_unregister() +-------------------- +cpufreq_unregister unregisters an arch driver, e.g. on module +unloading. Please note that there is no check done that this is called +from the driver which actually registered itself to the core, so +please only call this function when you are sure the arch driver got +registered correctly before. + + +struct cpufreq_driver +---------------- +On initialisation, the arch driver is supposed to pass the following +entries in struct cpufreq_driver cpufreq_driver: + +cpufreq_verify_t validate: This is a pointer to a function with the +following definition: + unsigned int validating_function (unsigned int kHz). +It is called right before a transition occurs. The proposed new +speed setting is passed as an argument in kHz; the validating code +should verify this is a valid speed setting which is currently +supported by the CPU. It shall return the closest valid CPU frequency +in kHz. + +cpufreq_setspeed_t setspeed: This is a pointer to a function with the +following definition: + void setspeed_function (unsigned int kHz). +This function shall perform the transition to the new CPU frequency +given as argument in kHz. Note that this argument is exactly the same +as the one returned by cpufreq_verify_t validate. + + +unsigned int freq.cur: The current CPU core frequency. Note that this +is a requirement while the next two entries are optional. + + +unsigned int freq.min (optional): The minimal CPU core frequency this +CPU supports. This value may be limited further by the +cpufreq_verify_t validate function, and so this value should be the +minimal core frequency allowed "theoretically" on this system in this +configuration. + + +unsigned int freq.max (optional): The maximum CPU core frequency this +CPU supports. This value may be limited further by the +cpufreq_verify_t validate function, and so this value should be the +maximum core frequency allowed "theoretically" on this system in this +configuration. + + +Some Requirements to CPUFreq architecture drivers +------------------------------------------------- +* Only call cpufreq_register() when the ability to switch CPU + frequencies is _verified_ or can't be missing +* cpufreq_unregister() may only be called if cpufreq_register() has + been successfully(!) called before +* All CPUs have to be set to the same speed whenever setspeed() is + called +* Be aware that there is currently no error management in the + setspeed() code in the CPUFreq core. So only call yourself a + cpufreq_driver if you are really a working cpufreq_driver! + + + +4. Mailing list and Links +************************** + + +Mailing List +------------ +There is a CPU frequency changing CVS commit and general list where +you can report bugs, problems or submit patches. To post a message, +send an email to cpufreq@www.linux.org.uk, to subscribe go to +http://www.linux.org.uk/mailman/listinfo/cpufreq. Previous post to the +mailing list are available to subscribers at +http://www.linux.org.uk/mailman/private/cpufreq/. + + +Links +----- +the FTP archives: +* ftp://ftp.linux.org.uk/pub/linux/cpufreq/ + +how to access the CVS repository: +* http://www.arm.linux.org.uk/cvs/ + +the CPUFreq Mailing list: +* http://www.linux.org.uk/mailman/listinfo/cpufreq + +Clock and voltage scaling for the SA-1100: +* http://www.lart.tudelft.nl/projects/scaling + +CPUFreq project homepage +* http://www.brodo.de/cpufreq/ diff -urN ../kernel-2.4.20/linux-2.4.20/arch/arm/mach-integrator/cpu.c linux-2.4.20/arch/arm/mach-integrator/cpu.c --- ../kernel-2.4.20/linux-2.4.20/arch/arm/mach-integrator/cpu.c 2002-11-28 18:53:09.000000000 -0500 +++ linux-2.4.20/arch/arm/mach-integrator/cpu.c 2003-08-25 10:58:45.000000000 -0400 @@ -1,9 +1,9 @@ /* * linux/arch/arm/mach-integrator/cpu.c * - * Copyright (C) 2001 Deep Blue Solutions Ltd. + * Copyright (C) 2001-2002 Deep Blue Solutions Ltd. * - * $Id: cpu.c,v 1.2 2001/09/22 12:11:17 rmk Exp $ + * $Id: cpu.c,v 1.1.1.1 2003/08/25 14:58:45 ducrot Exp $ * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -12,123 +12,195 @@ * CPU support functions */ #include +#include #include #include #include +#include +#include +#include #include #include #include +#include + +static struct cpufreq_driver integrator_driver; #define CM_ID (IO_ADDRESS(INTEGRATOR_HDR_BASE)+INTEGRATOR_HDR_ID_OFFSET) #define CM_OSC (IO_ADDRESS(INTEGRATOR_HDR_BASE)+INTEGRATOR_HDR_OSC_OFFSET) #define CM_STAT (IO_ADDRESS(INTEGRATOR_HDR_BASE)+INTEGRATOR_HDR_STAT_OFFSET) #define CM_LOCK (IO_ADDRESS(INTEGRATOR_HDR_BASE)+INTEGRATOR_HDR_LOCK_OFFSET) -struct vco { - unsigned char vdw; - unsigned char od; +static const struct icst525_params lclk_params = { + .ref = 24000, + .vco_max = 320000, + .vd_min = 8, + .vd_max = 132, + .rd_min = 24, + .rd_max = 24, }; -/* - * Divisors for each OD setting. - */ -static unsigned char cc_divisor[8] = { 10, 2, 8, 4, 5, 7, 9, 6 }; - -static unsigned int vco_to_freq(struct vco vco, int factor) -{ - return 2000 * (vco.vdw + 8) / cc_divisor[vco.od] / factor; -} +static const struct icst525_params cclk_params = { + .ref = 24000, + .vco_max = 320000, + .vd_min = 12, + .vd_max = 160, + .rd_min = 24, + .rd_max = 24, +}; -#ifdef CONFIG_CPU_FREQ /* - * Divisor indexes for in ascending divisor order + * Validate the speed policy. */ -static unsigned char s2od[] = { 1, 3, 4, 7, 5, 2, 6, 0 }; - -static struct vco freq_to_vco(unsigned int freq_khz, int factor) +static int integrator_verify_policy(struct cpufreq_policy *policy) { - struct vco vco = {0, 0}; - unsigned int i, f; + struct icst525_vco vco; - freq_khz *= factor; + cpufreq_verify_within_limits(policy, + policy->cpuinfo.min_freq, + policy->cpuinfo.max_freq); - for (i = 0; i < 8; i++) { - f = freq_khz * cc_divisor[s2od[i]]; - /* f must be between 10MHz and 320MHz */ - if (f > 10000 && f <= 320000) - break; - } + vco = icst525_khz_to_vco(&cclk_params, policy->max); + policy->max = icst525_khz(&cclk_params, vco); - vco.od = s2od[i]; - vco.vdw = f / 2000 - 8; + vco = icst525_khz_to_vco(&cclk_params, policy->min); + policy->min = icst525_khz(&cclk_params, vco); - return vco; -} + cpufreq_verify_within_limits(policy, + policy->cpuinfo.min_freq, + policy->cpuinfo.max_freq); -/* - * Validate the speed in khz. If it is outside our - * range, then return the lowest. - */ -unsigned int integrator_validatespeed(unsigned int freq_khz) -{ - struct vco vco; + return 0; +} - if (freq_khz < 12000) - freq_khz = 12000; - if (freq_khz > 160000) - freq_khz = 160000; - vco = freq_to_vco(freq_khz, 1); +static int integrator_set_target(struct cpufreq_policy *policy, + unsigned int target_freq, + unsigned int relation) +{ + unsigned long cpus_allowed; + int cpu = policy->cpu; + struct icst525_vco vco; + struct cpufreq_freqs freqs; + u_int cm_osc; - if (vco.vdw < 4 || vco.vdw > 152) - return -EINVAL; + /* + * Save this threads cpus_allowed mask. + */ + cpus_allowed = current->cpus_allowed; + + /* + * Bind to the specified CPU. When this call returns, + * we should be running on the right CPU. + */ + set_cpus_allowed(current, 1 << cpu); + BUG_ON(cpu != smp_processor_id()); - return vco_to_freq(vco, 1); -} + /* get current setting */ + cm_osc = __raw_readl(CM_OSC); + vco.s = (cm_osc >> 8) & 7; + vco.v = cm_osc & 255; + vco.r = 22; + freqs.old = icst525_khz(&cclk_params, vco); + + /* icst525_khz_to_vco rounds down -- so we need the next + * larger freq in case of CPUFREQ_RELATION_L. + */ + if (relation == CPUFREQ_RELATION_L) + target_freq += 1999; + if (target_freq > policy->max) + target_freq = policy->max; + vco = icst525_khz_to_vco(&cclk_params, target_freq); + freqs.new = icst525_khz(&cclk_params, vco); + + freqs.cpu = policy->cpu; + + if (freqs.old == freqs.new) { + set_cpus_allowed(current, cpus_allowed); + return 0; + } -void integrator_setspeed(unsigned int freq_khz) -{ - struct vco vco = freq_to_vco(freq_khz, 1); - u_int cm_osc; + cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE); cm_osc = __raw_readl(CM_OSC); cm_osc &= 0xfffff800; - cm_osc |= vco.vdw | vco.od << 8; + cm_osc |= vco.v | vco.s << 8; __raw_writel(0xa05f, CM_LOCK); __raw_writel(cm_osc, CM_OSC); __raw_writel(0, CM_LOCK); + + /* + * Restore the CPUs allowed mask. + */ + set_cpus_allowed(current, cpus_allowed); + + cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE); + + return 0; } -#endif -static int __init cpu_init(void) +static int integrator_cpufreq_init(struct cpufreq_policy *policy) { - u_int cm_osc, cm_stat, cpu_freq_khz, mem_freq_khz; - struct vco vco; + unsigned long cpus_allowed; + unsigned int cpu = policy->cpu; + u_int cm_osc, cm_stat, mem_freq_khz; + struct icst525_vco vco; - cm_osc = __raw_readl(CM_OSC); + cpus_allowed = current->cpus_allowed; - vco.od = (cm_osc >> 20) & 7; - vco.vdw = (cm_osc >> 12) & 255; - mem_freq_khz = vco_to_freq(vco, 2); - - printk(KERN_INFO "Memory clock = %d.%03d MHz\n", - mem_freq_khz / 1000, mem_freq_khz % 1000); - - vco.od = (cm_osc >> 8) & 7; - vco.vdw = cm_osc & 255; - cpu_freq_khz = vco_to_freq(vco, 1); - -#ifdef CONFIG_CPU_FREQ - cpufreq_init(cpu_freq_khz, 1000, 0); - cpufreq_setfunctions(integrator_validatespeed, integrator_setspeed); -#endif + set_cpus_allowed(current, 1 << cpu); + BUG_ON(cpu != smp_processor_id()); + /* detect memory etc. */ cm_stat = __raw_readl(CM_STAT); - printk("Module id: %d\n", cm_stat & 255); + cm_osc = __raw_readl(CM_OSC); + vco.s = (cm_osc >> 20) & 7; + vco.v = (cm_osc >> 12) & 255; + vco.r = 22; + mem_freq_khz = icst525_khz(&lclk_params, vco) / 2; + + printk(KERN_INFO "CPU%d: Module id: %d\n", cpu, cm_stat & 255); + printk(KERN_INFO "CPU%d: Memory clock = %d.%03d MHz\n", + cpu, mem_freq_khz / 1000, mem_freq_khz % 1000); + + vco.s = (cm_osc >> 8) & 7; + vco.v = cm_osc & 255; + vco.r = 22; + + /* set default policy and cpuinfo */ + policy->policy = CPUFREQ_POLICY_PERFORMANCE; + policy->cpuinfo.max_freq = 160000; + policy->cpuinfo.min_freq = 12000; + policy->cpuinfo.transition_latency = 1000; /* 1 ms, assumed */ + policy->cur = policy->min = policy->max = + icst525_khz(&cclk_params, vco); /* current freq */ + + set_cpus_allowed(current, cpus_allowed); return 0; } -__initcall(cpu_init); +static struct cpufreq_driver integrator_driver = { + .verify = integrator_verify_policy, + .target = integrator_set_target, + .init = integrator_cpufreq_init, +}; + +static int __init integrator_cpu_init(void) +{ + return cpufreq_register_driver(&integrator_driver); +} + +static void __exit integrator_cpu_exit(void) +{ + cpufreq_unregister_driver(&integrator_driver); +} + +MODULE_AUTHOR ("Russell M. King"); +MODULE_DESCRIPTION ("cpufreq driver for ARM Integrator CPUs"); +MODULE_LICENSE ("GPL"); + +module_init(integrator_cpu_init); +module_exit(integrator_cpu_exit); diff -urN ../kernel-2.4.20/linux-2.4.20/arch/arm/mach-sa1100/cpu-sa1100.c linux-2.4.20/arch/arm/mach-sa1100/cpu-sa1100.c --- ../kernel-2.4.20/linux-2.4.20/arch/arm/mach-sa1100/cpu-sa1100.c 2002-08-02 20:39:42.000000000 -0400 +++ linux-2.4.20/arch/arm/mach-sa1100/cpu-sa1100.c 2003-08-25 10:58:45.000000000 -0400 @@ -90,9 +90,7 @@ #include -extern unsigned int sa11x0_freq_to_ppcr(unsigned int khz); -extern unsigned int sa11x0_validatespeed(unsigned int khz); - +#include "generic.h" typedef struct { int speed; @@ -103,11 +101,11 @@ } sa1100_dram_regs_t; - +static struct cpufreq_driver sa1100_driver; static sa1100_dram_regs_t sa1100_dram_settings[] = { - /* { mdcnfg, mdcas0, mdcas1, mdcas2 } */ /* clock frequency */ + /* speed, mdcnfg, mdcas0, mdcas1, mdcas2 clock frequency */ { 59000, 0x00dc88a3, 0xcccccccf, 0xfffffffc, 0xffffffff }, /* 59.0 MHz */ { 73700, 0x011490a3, 0xcccccccf, 0xfffffffc, 0xffffffff }, /* 73.7 MHz */ { 88500, 0x014e90a3, 0xcccccccf, 0xfffffffc, 0xffffffff }, /* 88.5 MHz */ @@ -127,28 +125,25 @@ { 0, 0, 0, 0, 0 } /* last entry */ }; - - - static void sa1100_update_dram_timings(int current_speed, int new_speed) { sa1100_dram_regs_t *settings = sa1100_dram_settings; /* find speed */ - while(settings->speed != 0) { + while (settings->speed != 0) { if(new_speed == settings->speed) break; settings++; } - if(settings->speed == 0) { + if (settings->speed == 0) { panic("%s: couldn't find dram setting for speed %d\n", __FUNCTION__, new_speed); } /* No risk, no fun: run with interrupts on! */ - if(new_speed > current_speed) { + if (new_speed > current_speed) { /* We're going FASTER, so first relax the memory * timings before changing the core frequency */ @@ -181,61 +176,72 @@ } } - - - -static int sa1100_dram_notifier(struct notifier_block *nb, - unsigned long val, void *data) +static int sa1100_target(struct cpufreq_policy *policy, + unsigned int target_freq, + unsigned int relation) { - struct cpufreq_info *ci = data; - - switch(val) { - case CPUFREQ_MINMAX: - cpufreq_updateminmax(data, sa1100_dram_settings->speed, -1); - break; + unsigned int cur = sa11x0_getspeed(); + unsigned int new_ppcr; - case CPUFREQ_PRECHANGE: - if(ci->new_freq > ci->old_freq) - sa1100_update_dram_timings(ci->old_freq, ci->new_freq); + struct cpufreq_freqs freqs; + switch(relation){ + case CPUFREQ_RELATION_L: + new_ppcr = sa11x0_freq_to_ppcr(target_freq); + if (sa11x0_ppcr_to_freq(new_ppcr) > policy->max) + new_ppcr--; break; - - case CPUFREQ_POSTCHANGE: - if(ci->new_freq < ci->old_freq) - sa1100_update_dram_timings(ci->old_freq, ci->new_freq); + case CPUFREQ_RELATION_H: + new_ppcr = sa11x0_freq_to_ppcr(target_freq); + if ((sa11x0_ppcr_to_freq(new_ppcr) > target_freq) && + (sa11x0_ppcr_to_freq(new_ppcr - 1) >= policy->min)) + mew_ppcr--; break; - - default: - printk(KERN_INFO "%s: ignoring unknown notifier type (%ld)\n", - __FUNCTION__, val); } - return 0; -} + freqs.old = cur; + freqs.new = sa11x0_ppcr_to_freq(new_ppcr); + freqs.cpu = 0; + cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE); + if (freqs.new > cur) + sa1100_update_dram_timings(cur, freqs.new); + PPCR = new_ppcr; -static struct notifier_block sa1100_dram_block = { - notifier_call: sa1100_dram_notifier, -}; + if (freqs.new < cur) + sa1100_update_dram_timings(cur, freqs.new); + cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE); -static void sa1100_setspeed(unsigned int khz) -{ - PPCR = sa11x0_freq_to_ppcr(khz); + return 0; } -static int __init sa1100_dram_init(void) +static int __init sa1100_cpu_init(struct cpufreq_policy *policy) { - int ret = -ENODEV; - - if ((processor_id & CPU_SA1100_MASK) == CPU_SA1100_ID) { - ret = cpufreq_register_notifier(&sa1100_dram_block); + if (policy->cpu != 0) + return -EINVAL; + policy->cur = policy->min = policy->max = sa11x0_getspeed(); + policy->policy = CPUFREQ_POLICY_POWERSAVE; + policy->cpuinfo.min_freq = 59000; + policy->cpuinfo.max_freq = 287000; + policy->cpuinfo.transition_latency = CPUFREQ_ETERNAL; + return 0; +} - cpufreq_setfunctions(sa11x0_validatespeed, sa1100_setspeed); - } +static struct cpufreq_driver sa1100_driver = { + .verify = sa11x0_verify_speed, + .target = sa1100_target, + .init = sa1100_cpu_init, +}; - return ret; +static int __init sa1100_dram_init(void) +{ + cpufreq_gov_userspace_init(); + if ((processor_id & CPU_SA1100_MASK) == CPU_SA1100_ID) + return cpufreq_register_driver(&sa1100_driver); + else + return -ENODEV; } -__initcall(sa1100_dram_init); +arch_initcall(sa1100_dram_init); diff -urN ../kernel-2.4.20/linux-2.4.20/arch/arm/mach-sa1100/cpu-sa1110.c linux-2.4.20/arch/arm/mach-sa1100/cpu-sa1110.c --- ../kernel-2.4.20/linux-2.4.20/arch/arm/mach-sa1100/cpu-sa1110.c 2002-08-02 20:39:42.000000000 -0400 +++ linux-2.4.20/arch/arm/mach-sa1100/cpu-sa1110.c 2003-08-25 10:58:45.000000000 -0400 @@ -3,7 +3,7 @@ * * Copyright (C) 2001 Russell King * - * $Id: cpu-sa1110.c,v 1.6 2001/10/22 11:53:47 rmk Exp $ + * $Id: cpu-sa1110.c,v 1.1.1.1 2003/08/25 14:58:45 ducrot Exp $ * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -24,13 +24,15 @@ #include #include +#include #include #include +#include "generic.h" + #undef DEBUG -extern unsigned int sa11x0_freq_to_ppcr(unsigned int khz); -extern unsigned int sa11x0_validatespeed(unsigned int khz); +static struct cpufreq_driver sa1110_driver; struct sdram_params { u_char rows; /* bits */ @@ -49,33 +51,53 @@ }; static struct sdram_params tc59sm716_cl2_params __initdata = { - rows: 12, - tck: 10, - trcd: 20, - trp: 20, - twr: 10, - refresh: 64000, - cas_latency: 2, + .rows = 12, + .tck = 10, + .trcd = 20, + .trp = 20, + .twr = 10, + .refresh = 64000, + .cas_latency = 2, }; static struct sdram_params tc59sm716_cl3_params __initdata = { - rows: 12, - tck: 8, - trcd: 20, - trp: 20, - twr: 8, - refresh: 64000, - cas_latency: 3, + .rows = 12, + .tck = 8, + .trcd = 20, + .trp = 20, + .twr = 8, + .refresh = 64000, + .cas_latency = 3, }; static struct sdram_params samsung_k4s641632d_tc75 __initdata = { - rows: 14, - tck: 9, - trcd: 27, - trp: 20, - twr: 9, - refresh: 64000, - cas_latency: 3, + .rows = 14, + .tck = 9, + .trcd = 27, + .trp = 20, + .twr = 9, + .refresh = 64000, + .cas_latency = 3, +}; + +static struct sdram_params samsung_km416s4030ct __initdata = { + .rows = 13, + .tck = 8, + .trcd = 24, /* 3 CLKs */ + .trp = 24, /* 3 CLKs */ + .twr = 16, /* Trdl: 2 CLKs */ + .refresh = 64000, + .cas_latency = 3, +}; + +static struct sdram_params wbond_w982516ah75l_cl3_params __initdata = { + .rows = 16, + .tck = 8, + .trcd = 20, + .trp = 20, + .twr = 8, + .refresh = 64000, + .cas_latency = 3, }; static struct sdram_params sdram_params; @@ -188,19 +210,39 @@ } /* - * Ok, set the CPU frequency. Since we've done the validation - * above, we can match for an exact frequency. If we don't find - * an exact match, we will to set the lowest frequency to be safe. + * Ok, set the CPU frequency. */ -static void sa1110_setspeed(unsigned int khz) +static int sa1110_target(struct cpufreq_policy *policy, + unsigned int target_freq, + unsigned int relation) { struct sdram_params *sdram = &sdram_params; + struct cpufreq_freqs freqs; struct sdram_info sd; unsigned long flags; unsigned int ppcr, unused; - ppcr = sa11x0_freq_to_ppcr(khz); - sdram_calculate_timing(&sd, khz, sdram); + switch(relation){ + case CPUFREQ_RELATION_L: + ppcr = sa11x0_freq_to_ppcr(target_freq); + if (sa11x0_ppcr_to_freq(ppcr) > policy->max) + ppcr--; + break; + case CPUFREQ_RELATION_H: + ppcr = sa11x0_freq_to_ppcr(target_freq); + if (ppcr && (sa11x0_ppcr_to_freq(ppcr) > target_freq) && + (sa11x0_ppcr_to_freq(ppcr-1) >= policy->min)) + ppcr--; + break; + default: + return -EINVAL; + } + + freqs.old = sa11x0_getspeed(); + freqs.new = sa11x0_ppcr_to_freq(ppcr); + freqs.cpu = 0; + + sdram_calculate_timing(&sd, freqs.new, sdram); #if 0 /* @@ -208,7 +250,7 @@ * and errata, but they seem to work. Need to get a storage * scope on to the SDRAM signals to work out why. */ - if (khz < 147500) { + if (policy->max < 147500) { sd.mdrefr |= MDREFR_K1DB2; sd.mdcas[0] = 0xaaaaaa7f; } else { @@ -218,6 +260,9 @@ sd.mdcas[1] = 0xaaaaaaaa; sd.mdcas[2] = 0xaaaaaaaa; #endif + + cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE); + /* * The clock could be going away for some time. Set the SDRAMs * to refresh rapidly (every 64 memory clock cycles). To get @@ -235,7 +280,7 @@ * the programming. */ local_irq_save(flags); - asm("mcr p15, 0, %0, c10, c4" : : "r" (0)); + asm("mcr p15, 0, %0, c7, c10, 4" : : "r" (0)); udelay(10); __asm__ __volatile__(" b 2f @@ -260,9 +305,31 @@ /* * Now, return the SDRAM refresh back to normal. */ - sdram_update_refresh(khz, sdram); + sdram_update_refresh(freqs.new, sdram); + + cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE); + + return 0; +} + +static int __init sa1110_cpu_init(struct cpufreq_policy *policy) +{ + if (policy->cpu != 0) + return -EINVAL; + policy->cur = policy->min = policy->max = sa11x0_getspeed(); + policy->policy = CPUFREQ_POLICY_POWERSAVE; + policy->cpuinfo.min_freq = 59000; + policy->cpuinfo.max_freq = 287000; + policy->cpuinfo.transition_latency = CPUFREQ_ETERNAL; + return 0; } +static struct cpufreq_driver sa1110_driver = { + .verify = sa11x0_verify_speed, + .target = sa1110_target, + .init = sa1110_cpu_init, +}; + static int __init sa1110_clk_init(void) { struct sdram_params *sdram = NULL; @@ -273,19 +340,23 @@ if (machine_is_pt_system3()) sdram = &samsung_k4s641632d_tc75; + if (machine_is_h3100()) + sdram = &samsung_km416s4030ct; + if (sdram) { printk(KERN_DEBUG "SDRAM: tck: %d trcd: %d trp: %d" " twr: %d refresh: %d cas_latency: %d\n", sdram->tck, sdram->trcd, sdram->trp, sdram->twr, sdram->refresh, sdram->cas_latency); + cpufreq_gov_userspace_init(); + memcpy(&sdram_params, sdram, sizeof(sdram_params)); - sa1110_setspeed(cpufreq_get(0)); - cpufreq_setfunctions(sa11x0_validatespeed, sa1110_setspeed); + return cpufreq_register_driver(&sa1110_driver); } return 0; } -__initcall(sa1110_clk_init); +arch_initcall(sa1110_clk_init); diff -urN ../kernel-2.4.20/linux-2.4.20/arch/i386/boot/setup.S linux-2.4.20/arch/i386/boot/setup.S --- ../kernel-2.4.20/linux-2.4.20/arch/i386/boot/setup.S 2003-09-10 00:38:33.000000000 -0400 +++ linux-2.4.20/arch/i386/boot/setup.S 2003-09-09 19:54:16.000000000 -0400 @@ -488,6 +488,18 @@ movw $0xAA, (0x1ff) # device present no_psmouse: +#if defined(CONFIG_X86_SPEEDSTEP_SMI) || defined(CONFIG_X86_SPEEDSTEP_SMI_MODULE) + movl $0x0000E980, %eax # IST Support + movl $0x47534943, %edx # Request value + int $0x15 + + movl %eax, (96) + movl %ebx, (100) + movl %ecx, (104) + movl %edx, (108) +#endif + + #if defined(CONFIG_APM) || defined(CONFIG_APM_MODULE) # Then check for an APM BIOS... # %ds points to the bootsector diff -urN ../kernel-2.4.20/linux-2.4.20/arch/i386/config.in linux-2.4.20/arch/i386/config.in --- ../kernel-2.4.20/linux-2.4.20/arch/i386/config.in 2003-09-10 00:38:32.000000000 -0400 +++ linux-2.4.20/arch/i386/config.in 2003-09-09 20:00:25.000000000 -0400 @@ -183,21 +183,30 @@ mainmenu_option next_comment comment 'CPU Frequency scaling' -dep_bool 'CPU Frequency scaling (EXPERIMENTAL)' CONFIG_CPU_FREQ $CONFIG_EXPERIMENTAL +bool 'CPU Frequency scaling' CONFIG_CPU_FREQ if [ "$CONFIG_CPU_FREQ" = "y" ]; then - bool ' /proc/sys/cpu/ interface (2.4. / OLD)' CONFIG_CPU_FREQ_24_API - tristate ' AMD Mobile K6-2/K6-3 PowerNow!' CONFIG_X86_POWERNOW_K6 + 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 if [ "$CONFIG_MELAN" = "y" ]; then - tristate ' AMD Elan' CONFIG_ELAN_CPUFREQ + dep_tristate ' AMD Elan' CONFIG_ELAN_CPUFREQ $CONFIG_CPU_FREQ_TABLE fi - tristate ' VIA Cyrix III Longhaul' CONFIG_X86_LONGHAUL - tristate ' Intel Speedstep' CONFIG_X86_SPEEDSTEP - tristate ' Intel Pentium 4 clock modulation' CONFIG_X86_P4_CLOCKMOD + dep_tristate ' VIA Cyrix III Longhaul' CONFIG_X86_LONGHAUL $CONFIG_CPU_FREQ_TABLE + dep_tristate ' Intel Speedstep (PIIX4)' CONFIG_X86_SPEEDSTEP_PIIX4 $CONFIG_CPU_FREQ_TABLE + dep_tristate ' Intel SpeedStep on 440BX/ZX/MX chipsets (SMI interface)' CONFIG_X86_SPEEDSTEP_SMI $CONFIG_CPU_FREQ_TABLE + dep_tristate ' Intel Speedstep (ICH)' CONFIG_X86_SPEEDSTEP_ICH $CONFIG_CPU_FREQ_TABLE + dep_tristate ' Intel Pentium-M Enhanced SpeedStep' CONFIG_X86_SPEEDSTEP_CENTRINO $CONFIG_CPU_FREQ_TABLE + dep_tristate ' Intel Pentium 4 clock modulation' CONFIG_X86_P4_CLOCKMOD $CONFIG_CPU_FREQ_TABLE tristate ' Transmeta LongRun' CONFIG_X86_LONGRUN - + tristate ' Cyrix MediaGX/NatSemi Geode Suspend Modulation' CONFIG_X86_GX_SUSPMOD fi endmenu - + tristate 'Toshiba Laptop support' CONFIG_TOSHIBA tristate 'Dell laptop support' CONFIG_I8K diff -urN ../kernel-2.4.20/linux-2.4.20/arch/i386/kernel/Makefile linux-2.4.20/arch/i386/kernel/Makefile --- ../kernel-2.4.20/linux-2.4.20/arch/i386/kernel/Makefile 2003-09-10 00:38:33.000000000 -0400 +++ linux-2.4.20/arch/i386/kernel/Makefile 2003-09-09 19:57:34.000000000 -0400 @@ -14,7 +14,7 @@ O_TARGET := kernel.o -export-objs := mca.o mtrr.o msr.o cpuid.o microcode.o i386_ksyms.o traps.o time.o apm.o +export-objs := mca.o mtrr.o msr.o cpuid.o microcode.o i386_ksyms.o traps.o time.o apm.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 \ @@ -42,13 +42,18 @@ obj-$(CONFIG_X86_IO_APIC) += io_apic.o acpitable.o obj-$(CONFIG_X86_VISWS_APIC) += visws_apic.o obj-$(CONFIG_EDD) += edd.o +obj-$(CONFIG_PROFILING) += profile.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_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_X86_LONGRUN) += longrun.o obj-$(CONFIG_ELAN_CPUFREQ) += elanfreq.o -obj-$(CONFIG_PROFILING) += profile.o +obj-$(CONFIG_X86_LONGRUN) += longrun.o +obj-$(CONFIG_X86_GX_SUSPMOD) += gx-suspmod.o include $(TOPDIR)/Rules.make diff -urN ../kernel-2.4.20/linux-2.4.20/arch/i386/kernel/elanfreq.c linux-2.4.20/arch/i386/kernel/elanfreq.c --- ../kernel-2.4.20/linux-2.4.20/arch/i386/kernel/elanfreq.c 2003-09-10 00:38:24.000000000 -0400 +++ linux-2.4.20/arch/i386/kernel/elanfreq.c 2003-08-25 10:58:45.000000000 -0400 @@ -31,15 +31,9 @@ #define REG_CSCIR 0x22 /* Chip Setup and Control Index Register */ #define REG_CSCDR 0x23 /* Chip Setup and Control Data Register */ -static struct cpufreq_driver *elanfreq_driver; - /* Module parameter */ static int max_freq; -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Robert Schwebel , Sven Geggus "); -MODULE_DESCRIPTION("cpufreq driver for AMD's Elan CPUs"); - struct s_elan_multiplier { int clock; /* frequency in kHz */ int val40h; /* PMU Force Mode register */ @@ -127,11 +121,6 @@ struct cpufreq_freqs freqs; - if (!elanfreq_driver) { - printk(KERN_ERR "cpufreq: initialization problem or invalid target frequency\n"); - return; - } - freqs.old = elanfreq_get_cpu_frequency(); freqs.new = elan_multiplier[state].clock; freqs.cpu = 0; /* elanfreq.c is UP only driver */ @@ -187,11 +176,13 @@ return cpufreq_frequency_table_verify(policy, &elanfreq_table[0]); } -static int elanfreq_setpolicy (struct cpufreq_policy *policy) +static int elanfreq_target (struct cpufreq_policy *policy, + unsigned int target_freq, + unsigned int relation) { unsigned int newstate = 0; - if (cpufreq_frequency_table_setpolicy(policy, &elanfreq_table[0], &newstate)) + if (cpufreq_frequency_table_target(policy, &elanfreq_table[0], target_freq, relation, &newstate)) return -EINVAL; elanfreq_set_cpu_state(newstate); @@ -204,6 +195,35 @@ * 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 @@ -224,11 +244,18 @@ __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; - struct cpufreq_driver *driver; - int ret, i; /* Test if we have the right hardware */ if ((c->x86_vendor != X86_VENDOR_AMD) || @@ -238,60 +265,22 @@ return -ENODEV; } - driver = kmalloc(sizeof(struct cpufreq_driver) + - NR_CPUS * sizeof(struct cpufreq_policy), GFP_KERNEL); - if (!driver) - return -ENOMEM; - - driver->policy = (struct cpufreq_policy *) (driver + 1); - - 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; - } - -#ifdef CONFIG_CPU_FREQ_24_API - driver->cpu_cur_freq[0] = elanfreq_get_cpu_frequency(); -#endif - - driver->verify = &elanfreq_verify; - driver->setpolicy = &elanfreq_setpolicy; - - driver->policy[0].cpu = 0; - ret = cpufreq_frequency_table_cpuinfo(&driver->policy[0], &elanfreq_table[0]); - if (ret) { - kfree(driver); - return ret; - } - driver->policy[0].policy = CPUFREQ_POLICY_PERFORMANCE; - driver->policy[0].cpuinfo.transition_latency = CPUFREQ_ETERNAL; - - elanfreq_driver = driver; - - ret = cpufreq_register(driver); - if (ret) { - elanfreq_driver = NULL; - kfree(driver); - } - - return ret; + return cpufreq_register_driver(&elanfreq_driver); } static void __exit elanfreq_exit(void) { - if (elanfreq_driver) { - cpufreq_unregister(); - kfree(elanfreq_driver); - } + cpufreq_unregister_driver(&elanfreq_driver); } -module_init(elanfreq_init); -module_exit(elanfreq_exit); 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 -urN ../kernel-2.4.20/linux-2.4.20/arch/i386/kernel/gx-suspmod.c linux-2.4.20/arch/i386/kernel/gx-suspmod.c --- ../kernel-2.4.20/linux-2.4.20/arch/i386/kernel/gx-suspmod.c 1969-12-31 19:00:00.000000000 -0500 +++ linux-2.4.20/arch/i386/kernel/gx-suspmod.c 2003-08-25 10:58:46.000000000 -0400 @@ -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 = 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 "); +MODULE_DESCRIPTION ("Cpufreq driver for Cyrix MediaGX and NatSemi Geode"); +MODULE_LICENSE ("GPL"); + +module_init(cpufreq_gx_init); +module_exit(cpufreq_gx_exit); + diff -urN ../kernel-2.4.20/linux-2.4.20/arch/i386/kernel/i386_ksyms.c linux-2.4.20/arch/i386/kernel/i386_ksyms.c --- ../kernel-2.4.20/linux-2.4.20/arch/i386/kernel/i386_ksyms.c 2003-09-10 00:38:33.000000000 -0400 +++ linux-2.4.20/arch/i386/kernel/i386_ksyms.c 2003-09-09 19:58:46.000000000 -0400 @@ -30,6 +30,7 @@ #include #include #include +#include extern void dump_thread(struct pt_regs *, struct user *); extern spinlock_t rtc_lock; @@ -76,6 +77,7 @@ EXPORT_SYMBOL(get_cmos_time); EXPORT_SYMBOL_GPL(cpu_khz); EXPORT_SYMBOL(apm_info); +EXPORT_SYMBOL(ist_info); EXPORT_SYMBOL(empty_zero_page); #ifdef CONFIG_DEBUG_IOVIRT diff -urN ../kernel-2.4.20/linux-2.4.20/arch/i386/kernel/longhaul.c linux-2.4.20/arch/i386/kernel/longhaul.c --- ../kernel-2.4.20/linux-2.4.20/arch/i386/kernel/longhaul.c 2003-09-10 00:38:24.000000000 -0400 +++ linux-2.4.20/arch/i386/kernel/longhaul.c 2003-08-25 10:58:46.000000000 -0400 @@ -1,7 +1,5 @@ /* - * $Id: longhaul.c,v 1.83 2003/01/02 22:16:26 db Exp $ - * - * (C) 2001 Dave Jones. + * (C) 2001-2003 Dave Jones. * (C) 2002 Padraig Brady. * * Licensed under the terms of the GNU GPL License version 2. @@ -32,25 +30,27 @@ #include #include +#include "longhaul.h" + #define DEBUG #ifdef DEBUG #define dprintk(msg...) printk(msg) #else -#define dprintk(msg...) do { } while(0); +#define dprintk(msg...) do { } while(0) #endif -static int numscales=16, numvscales; +#define PFX "longhaul: " + +static unsigned 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 dont_scale_voltage; -static int dont_scale_fsb; -static int current_fsb; +static unsigned int fsb; #define __hlt() __asm__ __volatile__("hlt": : :"memory") @@ -230,15 +230,6 @@ 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 }; -static unsigned int *fsb_search_table; - /* Voltage scales. Div by 1000 to get actual voltage. */ static int __initdata vrm85scales[32] = { 1250, 1200, 1150, 1100, 1050, 1800, 1750, 1700, @@ -258,21 +249,22 @@ 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_driver *longhaul_driver; +static unsigned 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 int eblcr_fsb_table[] = { 66, 133, 100, -1 }; unsigned long invalue=0,lo, hi; - if (current_fsb == 0) { + if (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; + return fsb; } } @@ -283,7 +275,7 @@ rdmsr (MSR_IA32_EBL_CR_POWERON, lo, hi); invalue = (lo & (1<<22|1<<23|1<<24|1<<25)) >>22; - if (longhaul==3) { + if (longhaul_version==3) { if (lo & (1<<27)) invalue+=16; } @@ -293,79 +285,63 @@ /** * longhaul_set_cpu_frequency() - * @clock_ratio_index : index of clock_ratio[] for new frequency - * @newfsb: the new FSB + * @clock_ratio_index : bitpattern of the new multiplier. * * Sets a new clock ratio, and -if applicable- a new Front Side Bus */ -static void longhaul_setstate (unsigned int clock_ratio_index, unsigned int newfsb) +static void longhaul_setstate (unsigned int clock_ratio_index) { - 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; + union msr_longhaul longhaul; + union msr_bcr2 bcr2; - if ((!can_scale_fsb) && (newfsb != current_fsb)) + if (clock_ratio[clock_ratio_index] == -1) return; - if (((clock_ratio[clock_ratio_index] * newfsb * 100) > highest_speed) || - ((clock_ratio[clock_ratio_index] * newfsb * 100) < lowest_speed)) + if (((clock_ratio[clock_ratio_index] * fsb * 100) > highest_speed) || + ((clock_ratio[clock_ratio_index] * fsb * 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.new = clock_ratio[clock_ratio_index] * fsb * 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. */ + dprintk (KERN_INFO PFX "FSB:%d Mult(x10):%d\n", + fsb * 100, clock_ratio[clock_ratio_index]); - switch (longhaul) { + switch (longhaul_version) { 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); + 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 */ - rdmsr (MSR_VIA_BCR2, lo, hi); - lo &= ~(1<<19); - lo |= revkey; - wrmsr (MSR_VIA_BCR2, lo, hi); + rdmsrl (MSR_VIA_BCR2, bcr2.val); + bcr2.bits.ESOFTBF = 0; + wrmsrl (MSR_VIA_BCR2, bcr2.val); 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; + 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; 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))); - } + /* PB: TODO fix this up */ + vidindex = (((highest_speed-lowest_speed) / (fsb/2)) - + ((highest_speed-((clock_ratio[clock_ratio_index] * fsb * 100)/1000)) / (fsb/2))); for (i=0;i<32;i++) { dprintk (KERN_INFO "VID hunting. Looking for %d, found %d\n", minvid+(vidindex*25), voltage_table[i]); @@ -375,52 +351,41 @@ if (i==32) goto bad_voltage; - dprintk (KERN_INFO "longhaul: Desired vid index=%d\n", i); + dprintk (KERN_INFO PFX "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 */ + longhaul.bits.SoftVID = i; + longhaul.bits.EnableSoftVID = 1; #endif } - +/* FIXME: Do voltage and freq seperatly like we do in powernow-k7 */ bad_voltage: - wrmsr (MSR_VIA_LONGHAUL, lo, hi); + wrmsrl (MSR_VIA_LONGHAUL, longhaul.val); __hlt(); - rdmsr (MSR_VIA_LONGHAUL, lo, hi); - lo &= ~(1<<8); + rdmsrl (MSR_VIA_LONGHAUL, longhaul.val); + longhaul.bits.EnableSoftBusRatio = 0; if (can_scale_voltage) - lo &= ~(1<<9); - lo |= revkey; - wrmsr (MSR_VIA_LONGHAUL, lo, hi); + longhaul.bits.EnableSoftVID = 0; + longhaul.bits.RevisionKey = 1; + wrmsrl (MSR_VIA_LONGHAUL, longhaul.val); 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); + 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(); - rdmsr (MSR_VIA_LONGHAUL, lo, hi); - lo &= ~(1<<8); - lo |= revkey; - wrmsr (MSR_VIA_LONGHAUL, lo, hi); + rdmsrl (MSR_VIA_LONGHAUL, longhaul.val); + longhaul.bits.EnableSoftBusRatio = 0; + longhaul.bits.RevisionKey = 3; + wrmsrl (MSR_VIA_LONGHAUL, longhaul.val); break; } @@ -428,284 +393,159 @@ } -static void __init longhaul_get_ranges (void) +static int __init longhaul_get_ranges (void) { - unsigned long lo, hi, invalue; - unsigned int minmult=0, maxmult=0, minfsb=0, maxfsb=0; + 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 fsb_table[4] = { 133, 100, -1, 66 }; + unsigned int j, k = 0; + union msr_longhaul longhaul; - switch (longhaul) { + 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(); - minfsb = maxfsb = current_fsb; break; case 2 ... 3: - rdmsr (MSR_VIA_LONGHAUL, lo, hi); + rdmsrl (MSR_VIA_LONGHAUL, longhaul.val); - invalue = (hi & (1<<0|1<<1|1<<2|1<<3)); - if (hi & (1<<11)) + invalue = longhaul.bits.MaxMHzBR; + if (longhaul.bits.MaxMHzBR4) 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; +#if 0 + invalue = longhaul.bits.MinMHzBR; + if (longhaul.bits.MinMHzBR4); + 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); - } else { - minfsb = maxfsb = current_fsb; - } break; } - highest_speed = maxmult * maxfsb * 100; - lowest_speed = minmult * minfsb * 100; + highest_speed = maxmult * fsb * 100; + lowest_speed = minmult * fsb * 100; + dprintk (KERN_INFO PFX "MinMult(x10)=%d MaxMult(x10)=%d\n", + minmult, maxmult); + dprintk (KERN_INFO PFX "Lowestspeed=%d Highestspeed=%d\n", + 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 maxmult) || ((unsigned int)clock_ratio[j] < minmult)) + continue; + longhaul_table[k].frequency= clock_ratio[j] * fsb * 100; + longhaul_table[k].index = (j << 8); + k++; + } + + longhaul_table[k].frequency = CPUFREQ_TABLE_END; + if (!k) { + kfree (longhaul_table); + return -EINVAL; + } - 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); + return 0; } -static void __init longhaul_setup_voltagescaling (unsigned long lo, unsigned long hi) +static void __init longhaul_setup_voltagescaling(void) { - int revkey; + union msr_longhaul longhaul; - can_scale_voltage = 1; + rdmsrl (MSR_VIA_LONGHAUL, longhaul.val); - 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 (!(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 "longhaul: VRM 8.5 : "); + 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 "longhaul: Mobile VRM : "); + 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 */ - 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); + 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); -} - -static inline unsigned int longhaul_statecount_fsb(struct cpufreq_policy *policy, unsigned int fsb) { - unsigned int i, count = 0; - - for(i=0; imax) && - ((clock_ratio[i] * fsb * 100) >= policy->min)) - count++; - } - - return count; + can_scale_voltage = 1; } static int longhaul_verify(struct cpufreq_policy *policy) { - unsigned int number_states = 0; - unsigned int i; - unsigned int fsb_index = 0; - unsigned int tmpfreq = 0; - unsigned int newmax = -1; - - if (!policy || !longhaul_driver) - return -EINVAL; - - policy->cpu = 0; - cpufreq_verify_within_limits(policy, lowest_speed, highest_speed); - - if (can_scale_fsb==1) { - for (fsb_index=0; fsb_search_table[fsb_index]!=-1; fsb_index++) - number_states += longhaul_statecount_fsb(policy, fsb_search_table[fsb_index]); - } else - number_states = longhaul_statecount_fsb(policy, current_fsb); - - if (number_states) - return 0; - - /* get frequency closest above current policy->max */ - if (can_scale_fsb==1) { - for (fsb_index=0; fsb_search_table[fsb_index] != -1; fsb_index++) - for(i=0; i policy->max) && - (tmpfreq < newmax)) - newmax = tmpfreq; - } - } else { - for(i=0; i policy->max) && - (tmpfreq < newmax)) - newmax = tmpfreq; - } - } - - policy->max = newmax; - - cpufreq_verify_within_limits(policy, lowest_speed, highest_speed); - - return 0; + return cpufreq_frequency_table_verify(policy, longhaul_table); } -static int longhaul_get_best_freq_for_fsb(struct cpufreq_policy *policy, - unsigned int min_mult, - unsigned int max_mult, - unsigned int fsb, - unsigned int *new_mult) +static int longhaul_target (struct cpufreq_policy *policy, + unsigned int target_freq, + unsigned int relation) { - unsigned int optimal = 0; - unsigned int found_optimal = 0; - unsigned int i; - - switch(policy->policy) { - case CPUFREQ_POLICY_POWERSAVE: - optimal = max_mult; - break; - case CPUFREQ_POLICY_PERFORMANCE: - optimal = min_mult; - } + unsigned int table_index = 0; + unsigned int new_clock_ratio = 0; - for(i=0; i policy->max) || - (freq < policy->min)) - continue; - switch(policy->policy) { - case CPUFREQ_POLICY_POWERSAVE: - if (clock_ratio[i] < clock_ratio[optimal]) { - found_optimal = 1; - optimal = i; - } - break; - case CPUFREQ_POLICY_PERFORMANCE: - if (clock_ratio[i] > clock_ratio[optimal]) { - found_optimal = 1; - optimal = i; - } - break; - } - } - - if (found_optimal) { - *new_mult = optimal; - return 1; - } - return 0; -} - - -static int longhaul_setpolicy (struct cpufreq_policy *policy) -{ - unsigned int i; - unsigned int fsb_index = 0; - unsigned int new_fsb = 0; - unsigned int new_clock_ratio = 0; - unsigned int min_mult = 0; - unsigned int max_mult = 0; - - - if (!longhaul_driver) + if (cpufreq_frequency_table_target(policy, longhaul_table, target_freq, relation, &table_index)) return -EINVAL; - if (policy->policy==CPUFREQ_POLICY_PERFORMANCE) - fsb_search_table = perf_fsb_table; - else - fsb_search_table = power_fsb_table; - - for(i=0;i clock_ratio[i]) - min_mult = i; - } - - if (can_scale_fsb==1) { - unsigned int found = 0; - for (fsb_index=0; fsb_search_table[fsb_index]!=-1; fsb_index++) - { - if (longhaul_get_best_freq_for_fsb(policy, - min_mult, max_mult, - fsb_search_table[fsb_index], - &new_clock_ratio)) { - new_fsb = fsb_search_table[fsb_index]; - break; - } - } - if (!found) - return -EINVAL; - } else { - new_fsb = current_fsb; - if (!longhaul_get_best_freq_for_fsb(policy, min_mult, - max_mult, new_fsb, &new_clock_ratio)) - return -EINVAL; - } - - longhaul_setstate(new_clock_ratio, new_fsb); + new_clock_ratio = longhaul_table[table_index].index & 0xFF; + + longhaul_setstate(new_clock_ratio); return 0; } - -static int __init longhaul_init (void) +static int longhaul_cpu_init (struct cpufreq_policy *policy) { struct cpuinfo_x86 *c = cpu_data; - unsigned int currentspeed; - static int currentmult; - unsigned long lo, hi; int ret; - struct cpufreq_driver *driver; - - if ((c->x86_vendor != X86_VENDOR_CENTAUR) || (c->x86 !=6) ) - return -ENODEV; switch (c->x86_model) { case 6: /* VIA C3 Samuel C5A */ - longhaul=1; + longhaul_version=1; memcpy (clock_ratio, longhaul1_clock_ratio, sizeof(longhaul1_clock_ratio)); memcpy (eblcr_table, samuel1_eblcr, sizeof(samuel1_eblcr)); break; @@ -713,12 +553,12 @@ case 7: /* C5B / C5C */ switch (c->x86_mask) { case 0: - longhaul=1; + 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: - longhaul=2; + longhaul_version=2; memcpy (clock_ratio, longhaul2_clock_ratio, sizeof(longhaul2_clock_ratio)); memcpy (eblcr_table, ezra_eblcr, sizeof(ezra_eblcr)); break; @@ -727,84 +567,66 @@ case 8: /* C5M/C5N */ return -ENODEV; // Waiting on updated docs from VIA before this is usable - longhaul=3; + longhaul_version=3; numscales=32; memcpy (clock_ratio, longhaul3_clock_ratio, sizeof(longhaul3_clock_ratio)); memcpy (eblcr_table, c5m_eblcr, sizeof(c5m_eblcr)); break; - - default: - printk (KERN_INFO "longhaul: Unknown VIA CPU. Contact davej@suse.de\n"); - return -ENODEV; } - printk (KERN_INFO "longhaul: VIA CPU detected. Longhaul version %d supported\n", longhaul); - - current_fsb = longhaul_get_cpu_fsb(); - currentmult = longhaul_get_cpu_mult(); - currentspeed = currentmult * current_fsb * 100; + printk (KERN_INFO PFX "VIA CPU detected. Longhaul version %d supported\n", + longhaul_version); - dprintk (KERN_INFO "longhaul: CPU currently at %dMHz (%d x %d.%d)\n", - (currentspeed/1000), current_fsb, currentmult/10, currentmult%10); + if ((longhaul_version==2 || longhaul_version==3) && (dont_scale_voltage==0)) + longhaul_setup_voltagescaling(); - if (longhaul==2 || longhaul==3) { - rdmsr (MSR_VIA_LONGHAUL, lo, hi); - if ((lo & (1<<0)) && (dont_scale_voltage==0)) - longhaul_setup_voltagescaling (lo, hi); + ret = longhaul_get_ranges(); + if (ret != 0) + return ret; - if ((lo & (1<<1)) && (dont_scale_fsb==0) && (current_fsb==0)) - can_scale_fsb = 1; - } + policy->policy = CPUFREQ_POLICY_PERFORMANCE; + policy->cpuinfo.transition_latency = CPUFREQ_ETERNAL; - longhaul_get_ranges(); + policy->cur = (unsigned int) (longhaul_get_cpu_fsb() * longhaul_get_cpu_mult() * 100); - driver = kmalloc(sizeof(struct cpufreq_driver) + - NR_CPUS * sizeof(struct cpufreq_policy), GFP_KERNEL); - if (!driver) - return -ENOMEM; + return cpufreq_frequency_table_cpuinfo(policy, longhaul_table); +} - driver->policy = (struct cpufreq_policy *) (driver + 1); +static struct cpufreq_driver longhaul_driver = { + .verify = longhaul_verify, + .target = longhaul_target, + .init = longhaul_cpu_init, + .name = "longhaul", +}; -#ifdef CONFIG_CPU_FREQ_24_API - driver->cpu_cur_freq[0] = currentspeed; -#endif +static int __init longhaul_init (void) +{ + struct cpuinfo_x86 *c = cpu_data; - driver->verify = &longhaul_verify; - driver->setpolicy = &longhaul_setpolicy; + if ((c->x86_vendor != X86_VENDOR_CENTAUR) || (c->x86 !=6) ) + return -ENODEV; - driver->policy[0].cpu = 0; - driver->policy[0].min = (unsigned int) lowest_speed; - driver->policy[0].max = (unsigned int) highest_speed; - driver->policy[0].policy = CPUFREQ_POLICY_PERFORMANCE; - driver->policy[0].cpuinfo.min_freq = (unsigned int) lowest_speed; - driver->policy[0].cpuinfo.max_freq = (unsigned int) highest_speed; - driver->policy[0].cpuinfo.transition_latency = CPUFREQ_ETERNAL; - - longhaul_driver = driver; - - ret = cpufreq_register(driver); - if (ret) { - longhaul_driver = NULL; - kfree(driver); + switch (c->x86_model) { + case 6 ... 7: + return cpufreq_register_driver(&longhaul_driver); + case 8: + return -ENODEV; + default: + printk (KERN_INFO PFX "Unknown VIA CPU. Contact davej@codemonkey.org.uk\n"); } - return ret; + return -ENODEV; } - static void __exit longhaul_exit (void) { - if (longhaul_driver) { - cpufreq_unregister(); - kfree(longhaul_driver); - } + 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_AUTHOR ("Dave Jones "); +MODULE_AUTHOR ("Dave Jones "); MODULE_DESCRIPTION ("Longhaul driver for VIA Cyrix processors."); MODULE_LICENSE ("GPL"); diff -urN ../kernel-2.4.20/linux-2.4.20/arch/i386/kernel/longhaul.h linux-2.4.20/arch/i386/kernel/longhaul.h --- ../kernel-2.4.20/linux-2.4.20/arch/i386/kernel/longhaul.h 1969-12-31 19:00:00.000000000 -0500 +++ linux-2.4.20/arch/i386/kernel/longhaul.h 2003-08-19 09:38:32.000000000 -0400 @@ -0,0 +1,49 @@ +/* + * 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; +}; + diff -urN ../kernel-2.4.20/linux-2.4.20/arch/i386/kernel/longrun.c linux-2.4.20/arch/i386/kernel/longrun.c --- ../kernel-2.4.20/linux-2.4.20/arch/i386/kernel/longrun.c 2003-09-10 00:38:24.000000000 -0400 +++ linux-2.4.20/arch/i386/kernel/longrun.c 2003-08-25 10:58:47.000000000 -0400 @@ -1,7 +1,5 @@ /* - * $Id: longrun.c,v 1.18 2003/01/02 22:16:26 db Exp $ - * - * (C) 2002 Dominik Brodowski + * (C) 2002 - 2003 Dominik Brodowski * * Licensed under the terms of the GNU GPL License version 2. * @@ -18,7 +16,7 @@ #include #include -static struct cpufreq_driver *longrun_driver; +static struct cpufreq_driver longrun_driver; /** * longrun_{low,high}_freq is needed for the conversion of cpufreq kHz @@ -39,9 +37,6 @@ { u32 msr_lo, msr_hi; - if (!longrun_driver) - return; - rdmsr(MSR_TMTA_LONGRUN_FLAGS, msr_lo, msr_hi); if (msr_lo & 0x01) policy->policy = CPUFREQ_POLICY_PERFORMANCE; @@ -54,7 +49,7 @@ policy->min = longrun_low_freq + msr_lo * ((longrun_high_freq - longrun_low_freq) / 100); - policy->min = longrun_low_freq + msr_hi * + policy->max = longrun_low_freq + msr_hi * ((longrun_high_freq - longrun_low_freq) / 100); policy->cpu = 0; } @@ -72,7 +67,7 @@ u32 msr_lo, msr_hi; u32 pctg_lo, pctg_hi; - if (!longrun_driver || !policy) + if (!policy) return -EINVAL; pctg_lo = (policy->min - longrun_low_freq) / @@ -117,13 +112,16 @@ */ static int longrun_verify_policy(struct cpufreq_policy *policy) { - if (!policy || !longrun_driver) + if (!policy) return -EINVAL; policy->cpu = 0; cpufreq_verify_within_limits(policy, - longrun_driver->policy[0].cpuinfo.min_freq, - longrun_driver->policy[0].cpuinfo.max_freq); + policy->cpuinfo.min_freq, + policy->cpuinfo.max_freq); + + if (policy->policy == CPUFREQ_POLICY_GOVERNOR) + return -EINVAL; return 0; } @@ -133,7 +131,7 @@ * 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 + * This is necessary to calculate the performance percentage according to * TMTA rules: * performance_pctg = (target_freq - low_freq)/(high_freq - low_freq) */ @@ -221,6 +219,37 @@ } +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 * @@ -228,48 +257,13 @@ */ static int __init longrun_init(void) { - int result; - struct cpufreq_driver *driver; struct cpuinfo_x86 *c = cpu_data; if (c->x86_vendor != X86_VENDOR_TRANSMETA || !cpu_has(c, X86_FEATURE_LONGRUN)) - return 0; + return -ENODEV; - /* initialization of main "cpufreq" code*/ - driver = kmalloc(sizeof(struct cpufreq_driver) + - NR_CPUS * sizeof(struct cpufreq_policy), GFP_KERNEL); - if (!driver) - return -ENOMEM; - - driver->policy = (struct cpufreq_policy *) (driver + 1); - - if (longrun_determine_freqs(&longrun_low_freq, &longrun_high_freq)) { - kfree(driver); - return -EIO; - } - driver->policy[0].cpuinfo.min_freq = longrun_low_freq; - driver->policy[0].cpuinfo.max_freq = longrun_high_freq; - driver->policy[0].cpuinfo.transition_latency = CPUFREQ_ETERNAL; - - longrun_get_policy(&driver->policy[0]); - -#ifdef CONFIG_CPU_FREQ_24_API - driver->cpu_cur_freq[0] = longrun_high_freq; /* dummy value */ -#endif - - driver->verify = &longrun_verify_policy; - driver->setpolicy = &longrun_set_policy; - - longrun_driver = driver; - - result = cpufreq_register(driver); - if (result) { - longrun_driver = NULL; - kfree(driver); - } - - return result; + return cpufreq_register_driver(&longrun_driver); } @@ -278,15 +272,13 @@ */ static void __exit longrun_exit(void) { - if (longrun_driver) { - cpufreq_unregister(); - kfree(longrun_driver); - } + 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 -urN ../kernel-2.4.20/linux-2.4.20/arch/i386/kernel/p4-clockmod.c linux-2.4.20/arch/i386/kernel/p4-clockmod.c --- ../kernel-2.4.20/linux-2.4.20/arch/i386/kernel/p4-clockmod.c 2003-09-10 00:38:24.000000000 -0400 +++ linux-2.4.20/arch/i386/kernel/p4-clockmod.c 2003-08-25 10:58:47.000000000 -0400 @@ -1,5 +1,6 @@ /* * 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 @@ -45,11 +46,8 @@ #define DC_ENTRIES 8 -static int has_N44_O17_errata; +static int has_N44_O17_errata[NR_CPUS]; static int stock_freq; -MODULE_PARM(stock_freq, "i"); - -static struct cpufreq_driver *cpufreq_p4_driver; static int cpufreq_p4_setdc(unsigned int cpu, unsigned int newstate) @@ -106,18 +104,20 @@ } rdmsr(MSR_IA32_THERM_STATUS, l, h); +#if 0 if (l & 0x01) printk(KERN_DEBUG PFX "CPU#%d currently thermal throttled\n", cpu); - - if (has_N44_O17_errata && (newstate == DC_25PT || newstate == DC_DFLT)) +#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); + /* 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)); + /* 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 @@ -155,14 +155,16 @@ }; -static int cpufreq_p4_setpolicy(struct cpufreq_policy *policy) +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_setpolicy(policy, &p4clockmod_table[0], &newstate)) + if (cpufreq_frequency_table_target(policy, &p4clockmod_table[0], target_freq, relation, &newstate)) return -EINVAL; - cpufreq_p4_setdc(policy->cpu, newstate); + cpufreq_p4_setdc(policy->cpu, p4clockmod_table[newstate].index); return 0; } @@ -174,39 +176,23 @@ } -static int __init cpufreq_p4_init(void) -{ - struct cpuinfo_x86 *c = cpu_data; - int cpuid; - int ret; - struct cpufreq_driver *driver; +static int cpufreq_p4_cpu_init(struct cpufreq_policy *policy) +{ + struct cpuinfo_x86 *c = &cpu_data[policy->cpu]; + int cpuid = 0; unsigned int i; - /* - * 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; - - /* Errata workarounds */ + /* 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 = 1; - default: - break; + case 0x0f07: + case 0x0f0a: + case 0x0f11: + case 0x0f12: + has_N44_O17_errata[policy->cpu] = 1; } - - printk(KERN_INFO PFX "P4/Xeon(TM) CPU On-Demand Clock Modulation available\n"); - + + /* get frequency */ if (!stock_freq) { if (cpu_khz) stock_freq = cpu_khz; @@ -216,68 +202,66 @@ } } - driver = kmalloc(sizeof(struct cpufreq_driver) + - NR_CPUS * sizeof(struct cpufreq_policy), GFP_KERNEL); - if (!driver) - return -ENOMEM; - - driver->policy = (struct cpufreq_policy *) (driver + 1); - /* table init */ for (i=1; (p4clockmod_table[i].frequency != CPUFREQ_TABLE_END); i++) { - if ((i<2) && (has_N44_O17_errata)) + 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; -#ifdef CONFIG_CPU_FREQ_24_API - for (i=0;icpu_cur_freq[i] = stock_freq; - } -#endif + return cpufreq_frequency_table_cpuinfo(policy, &p4clockmod_table[0]); +} - driver->verify = &cpufreq_p4_verify; - driver->setpolicy = &cpufreq_p4_setpolicy; - for (i=0;ipolicy[i].cpu = i; - ret = cpufreq_frequency_table_cpuinfo(&driver->policy[i], &p4clockmod_table[0]); - if (ret) { - kfree(driver); - return ret; - } - driver->policy[i].policy = CPUFREQ_POLICY_PERFORMANCE; - driver->policy[i].cpuinfo.transition_latency = CPUFREQ_ETERNAL; - } +static int cpufreq_p4_cpu_exit(struct cpufreq_policy *policy) +{ + return cpufreq_p4_setdc(policy->cpu, DC_DISABLE); +} - cpufreq_p4_driver = driver; - - ret = cpufreq_register(driver); - if (ret) { - cpufreq_p4_driver = NULL; - kfree(driver); - } +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 ret; + return cpufreq_register_driver(&p4clockmod_driver); } static void __exit cpufreq_p4_exit(void) { - unsigned int i; - - if (cpufreq_p4_driver) { - for (i=0; i"); MODULE_DESCRIPTION ("cpufreq driver for Pentium(TM) 4/Xeon(TM)"); MODULE_LICENSE ("GPL"); diff -urN ../kernel-2.4.20/linux-2.4.20/arch/i386/kernel/powernow-k6.c linux-2.4.20/arch/i386/kernel/powernow-k6.c --- ../kernel-2.4.20/linux-2.4.20/arch/i386/kernel/powernow-k6.c 2003-09-10 00:38:24.000000000 -0400 +++ linux-2.4.20/arch/i386/kernel/powernow-k6.c 2003-08-25 10:58:47.000000000 -0400 @@ -1,9 +1,6 @@ /* - * $Id: powernow-k6.c,v 1.42 2003/01/02 22:41:08 db Exp $ - * This file was part of Powertweak Linux (http://powertweak.sf.net) - * and is shared with the Linux Kernel module. - * - * (C) 2000-2002 Dave Jones, Arjan van de Ven, Janne Pänkälä, Dominik Brodowski. + * This file was based upon code in Powertweak Linux (http://powertweak.sf.net) + * (C) 2000-2003 Dave Jones, Arjan van de Ven, Janne Pänkälä, Dominik Brodowski. * * Licensed under the terms of the GNU GPL License version 2. * @@ -25,7 +22,6 @@ #define POWERNOW_IOPORT 0xfff0 /* it doesn't matter where, as long as it is unused */ -static struct cpufreq_driver *powernow_driver; static unsigned int busfreq; /* FSB, in 10 kHz */ static unsigned int max_multiplier; @@ -77,8 +73,8 @@ unsigned long msrval; struct cpufreq_freqs freqs; - if (!powernow_driver) { - printk(KERN_ERR "cpufreq: initialization problem or invalid target frequency\n"); + if (clock_ratio[best_i].index > max_multiplier) { + printk(KERN_ERR "cpufreq: invalid target frequency\n"); return; } @@ -126,11 +122,13 @@ * * sets a new CPUFreq policy */ -static int powernow_k6_setpolicy (struct cpufreq_policy *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_setpolicy(policy, &clock_ratio[0], &newstate)) + if (cpufreq_frequency_table_target(policy, &clock_ratio[0], target_freq, relation, &newstate)) return -EINVAL; powernow_k6_set_state(newstate); @@ -139,6 +137,54 @@ } +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 * @@ -149,65 +195,22 @@ static int __init powernow_k6_init(void) { struct cpuinfo_x86 *c = cpu_data; - struct cpufreq_driver *driver; - unsigned int result; - unsigned int i; if ((c->x86_vendor != X86_VENDOR_AMD) || (c->x86 != 5) || ((c->x86_model != 12) && (c->x86_model != 13))) return -ENODEV; - max_multiplier = powernow_k6_get_cpu_multiplier(); - busfreq = cpu_khz / max_multiplier; - if (!request_region(POWERNOW_IOPORT, 16, "PowerNow!")) { printk("cpufreq: PowerNow IOPORT region already used.\n"); return -EIO; } - /* initialization of main "cpufreq" code*/ - driver = kmalloc(sizeof(struct cpufreq_driver) + - NR_CPUS * sizeof(struct cpufreq_policy), GFP_KERNEL); - if (!driver) { - release_region (POWERNOW_IOPORT, 16); - return -ENOMEM; - } - driver->policy = (struct cpufreq_policy *) (driver + 1); - - /* 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; - } - - driver->verify = &powernow_k6_verify; - driver->setpolicy = &powernow_k6_setpolicy; - - /* cpuinfo and default policy values */ - driver->policy[0].cpu = 0; - driver->policy[0].cpuinfo.transition_latency = CPUFREQ_ETERNAL; - driver->policy[0].policy = CPUFREQ_POLICY_PERFORMANCE; -#ifdef CONFIG_CPU_FREQ_24_API - driver->cpu_cur_freq[0] = busfreq * max_multiplier; -#endif - result = cpufreq_frequency_table_cpuinfo(&driver->policy[0], &clock_ratio[0]); - if (result) { - kfree(driver); - return result; - } - - powernow_driver = driver; - - result = cpufreq_register(driver); - if (result) { + if (cpufreq_register_driver(&powernow_k6_driver)) { release_region (POWERNOW_IOPORT, 16); - powernow_driver = NULL; - kfree(driver); + return -EINVAL; } - return result; + return 0; } @@ -218,20 +221,14 @@ */ static void __exit powernow_k6_exit(void) { - unsigned int i; - - if (powernow_driver) { - for (i=0;i<8;i++) - if (clock_ratio[i].index == max_multiplier) - powernow_k6_set_state(i); - cpufreq_unregister(); - kfree(powernow_driver); - } + cpufreq_unregister_driver(&powernow_k6_driver); + release_region (POWERNOW_IOPORT, 16); } -MODULE_AUTHOR ("Arjan van de Ven , Dave Jones , Dominik Brodowski "); +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 -urN ../kernel-2.4.20/linux-2.4.20/arch/i386/kernel/powernow-k7.c linux-2.4.20/arch/i386/kernel/powernow-k7.c --- ../kernel-2.4.20/linux-2.4.20/arch/i386/kernel/powernow-k7.c 1969-12-31 19:00:00.000000000 -0500 +++ linux-2.4.20/arch/i386/kernel/powernow-k7.c 2003-08-25 10:58:47.000000000 -0400 @@ -0,0 +1,410 @@ +/* + * AMD K7 Powernow driver. + * (C) 2003 Dave Jones on behalf of SuSE Labs. + * + * Licensed under the terms of the GNU GPL License version 2. + * Based upon datasheets & sample CPUs kindly provided by AMD. + * + * 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 + +#include "powernow-k7.h" + +#define DEBUG + +#ifdef DEBUG +#define dprintk(msg...) printk(msg) +#else +#define dprintk(msg...) do { } while(0) +#endif + +#define PFX "powernow: " + + +struct psb_s { + u8 signature[10]; + u8 tableversion; + u8 flags; + u16 settlingtime; + u8 reserved1; + u8 numpst; +}; + +struct pst_s { + u32 cpuid; + u8 fsbspeed; + u8 maxfid; + u8 startvid; + u8 numpstates; +}; + + +/* divide by 1000 to get VID. */ +static int mobile_vid_table[32] = { + 2000, 1950, 1900, 1850, 1800, 1750, 1700, 1650, + 1600, 1550, 1500, 1450, 1400, 1350, 1300, 0, + 1275, 1250, 1225, 1200, 1175, 1150, 1125, 1100, + 1075, 1050, 1024, 1000, 975, 950, 925, 0, +}; + +/* divide by 10 to get FID. */ +static int fid_codes[32] = { + 110, 115, 120, 125, 50, 55, 60, 65, + 70, 75, 80, 85, 90, 95, 100, 105, + 30, 190, 40, 200, 130, 135, 140, 210, + 150, 225, 160, 165, 170, 180, -1, -1, +}; + +static struct cpufreq_frequency_table *powernow_table; + +static unsigned int can_scale_bus; +static unsigned int can_scale_vid; +static unsigned int minimum_speed=-1; +static unsigned int maximum_speed; +static unsigned int number_scales; +static unsigned int fsb; +static unsigned int latency; +static char have_a0; + + +static int check_powernow(void) +{ + struct cpuinfo_x86 *c = cpu_data; + unsigned int maxei, eax, ebx, ecx, edx; + + if (c->x86_vendor != X86_VENDOR_AMD) { + printk (KERN_INFO PFX "AMD processor not detected.\n"); + return 0; + } + + if (c->x86 !=6) { + printk (KERN_INFO PFX "This module only works with AMD K7 CPUs\n"); + return 0; + } + + printk (KERN_INFO PFX "AMD K7 CPU detected.\n"); + + if ((c->x86_model == 6) && (c->x86_mask == 0)) { + printk (KERN_INFO PFX "K7 660[A0] core detected, enabling errata workarounds\n"); + have_a0 = 1; + } + + /* Get maximum capabilities */ + maxei = cpuid_eax (0x80000000); + if (maxei < 0x80000007) { /* Any powernow info ? */ + printk (KERN_INFO PFX "No powernow capabilities detected\n"); + return 0; + } + + cpuid(0x80000007, &eax, &ebx, &ecx, &edx); + printk (KERN_INFO PFX "PowerNOW! Technology present. Can scale: "); + + if (edx & 1 << 1) { + printk ("frequency"); + can_scale_bus=1; + } + + if ((edx & (1 << 1 | 1 << 2)) == 0x6) + printk (" and "); + + if (edx & 1 << 2) { + printk ("voltage"); + can_scale_vid=1; + } + + if (!(edx & (1 << 1 | 1 << 2))) { + printk ("nothing.\n"); + return 0; + } + + printk (".\n"); + return 1; +} + + +static int get_ranges (unsigned char *pst) +{ + unsigned int j, speed; + u8 fid, vid; + + powernow_table = kmalloc((sizeof(struct cpufreq_frequency_table) * (number_scales + 1)), GFP_KERNEL); + if (!powernow_table) + return -ENOMEM; + memset(powernow_table, 0, (sizeof(struct cpufreq_frequency_table) * (number_scales + 1))); + + for (j=0 ; j < number_scales; j++) { + fid = *pst++; + + powernow_table[j].frequency = fsb * fid_codes[fid] * 100; + powernow_table[j].index = fid; /* lower 8 bits */ + + speed = fsb * (fid_codes[fid]/10); + if ((fid_codes[fid] % 10)==5) { + speed += fsb/2; +#if defined(CONFIG_ACPI_PROCESSOR) || defined(CONFIG_ACPI_PROCESSOR_MODULE) + if (have_a0 == 1) + powernow_table[j].frequency = CPUFREQ_ENTRY_INVALID; +#endif + } + + dprintk (KERN_INFO PFX " FID: 0x%x (%d.%dx [%dMHz])\t", fid, + fid_codes[fid] / 10, fid_codes[fid] % 10, speed); + + if (speed < minimum_speed) + minimum_speed = speed; + if (speed > maximum_speed) + maximum_speed = speed; + + vid = *pst++; + powernow_table[j].index |= (vid << 8); /* upper 8 bits */ + dprintk ("VID: 0x%x (%d.%03dV)\n", vid, mobile_vid_table[vid]/1000, + mobile_vid_table[vid]%1000); + } + dprintk ("\n"); + + powernow_table[number_scales].frequency = CPUFREQ_TABLE_END; + powernow_table[number_scales].index = 0; + + return 0; +} + + +static void change_FID(int fid) +{ + union msr_fidvidctl fidvidctl; + + if (fidvidctl.bits.FID != fid) { + rdmsrl (MSR_K7_FID_VID_CTL, fidvidctl.val); + fidvidctl.bits.SGTC = latency; + fidvidctl.bits.FID = fid; + fidvidctl.bits.FIDC = 1; + wrmsrl (MSR_K7_FID_VID_CTL, fidvidctl.val); + } +} + + +static void change_VID(int vid) +{ + union msr_fidvidctl fidvidctl; + + if (fidvidctl.bits.VID != vid) { + rdmsrl (MSR_K7_FID_VID_CTL, fidvidctl.val); + fidvidctl.bits.VID = vid; + fidvidctl.bits.VIDC = 1; + wrmsrl (MSR_K7_FID_VID_CTL, fidvidctl.val); + } +} + + +static void change_speed (unsigned int index) +{ + u8 fid, vid; + struct cpufreq_freqs freqs; + union msr_fidvidstatus fidvidstatus; + int cfid; + + /* fid are the lower 8 bits of the index we stored into + * the cpufreq frequency table in powernow_decode_bios, + * vid are the upper 8 bits. + */ + + fid = powernow_table[index].index & 0xFF; + vid = (powernow_table[index].index & 0xFF00) >> 8; + + freqs.cpu = 0; + + rdmsrl (MSR_K7_FID_VID_STATUS, fidvidstatus.val); + cfid = fidvidstatus.bits.CFID; + freqs.old = fsb * fid_codes[cfid] * 100; + freqs.new = powernow_table[index].frequency; + + cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE); + + /* Now do the magic poking into the MSRs. */ + + if (have_a0 == 1) /* A0 errata 5 */ + local_irq_disable(); + + if (freqs.old > freqs.new) { + /* Going down, so change FID first */ + change_FID(fid); + change_VID(vid); + } else { + /* Going up, so change VID first */ + change_VID(vid); + change_FID(fid); + } + + + if (have_a0 == 1) + local_irq_enable(); + + cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE); +} + + +static int powernow_decode_bios (int maxfid, int startvid) +{ + struct psb_s *psb; + struct pst_s *pst; + struct cpuinfo_x86 *c = cpu_data; + unsigned int i, j; + unsigned char *p; + unsigned int etuple; + unsigned int ret; + + etuple = cpuid_eax(0x80000001); + etuple &= 0xf00; + etuple |= (c->x86_model<<4)|(c->x86_mask); + + for (i=0xC0000; i < 0xffff0 ; i+=16) { + + p = phys_to_virt(i); + + if (memcmp(p, "AMDK7PNOW!", 10) == 0){ + dprintk (KERN_INFO PFX "Found PSB header at %p\n", p); + psb = (struct psb_s *) p; + dprintk (KERN_INFO PFX "Table version: 0x%x\n", psb->tableversion); + if (psb->tableversion != 0x12) { + printk (KERN_INFO PFX "Sorry, only v1.2 tables supported right now\n"); + return -ENODEV; + } + + dprintk (KERN_INFO PFX "Flags: 0x%x (", psb->flags); + if ((psb->flags & 1)==0) { + dprintk ("Mobile"); + } else { + dprintk ("Desktop"); + } + dprintk (" voltage regulator)\n"); + + latency = psb->settlingtime; + 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 -urN ../kernel-2.4.20/linux-2.4.20/arch/i386/kernel/powernow-k7.h linux-2.4.20/arch/i386/kernel/powernow-k7.h --- ../kernel-2.4.20/linux-2.4.20/arch/i386/kernel/powernow-k7.h 1969-12-31 19:00:00.000000000 -0500 +++ linux-2.4.20/arch/i386/kernel/powernow-k7.h 2003-08-25 10:58:47.000000000 -0400 @@ -0,0 +1,44 @@ +/* + * $Id: powernow-k7.h,v 1.1.1.1 2003/08/25 14:58:47 ducrot Exp $ + * (C) 2003 Dave Jones. + * + * Licensed under the terms of the GNU GPL License version 2. + * + * AMD-specific information + * + */ + +union msr_fidvidctl { + struct { + unsigned FID:5, // 4:0 + reserved1:3, // 7:5 + VID:5, // 12:8 + reserved2:3, // 15:13 + FIDC:1, // 16 + VIDC:1, // 17 + reserved3:2, // 19:18 + FIDCHGRATIO:1, // 20 + reserved4:11, // 31-21 + SGTC:20, // 32:51 + reserved5:12; // 63:52 + } bits; + unsigned long long val; +}; + +union msr_fidvidstatus { + struct { + unsigned CFID:5, // 4:0 + reserved1:3, // 7:5 + SFID:5, // 12:8 + reserved2:3, // 15:13 + MFID:5, // 20:16 + reserved3:11, // 31:21 + CVID:5, // 36:32 + reserved4:3, // 39:37 + SVID:5, // 44:40 + reserved5:3, // 47:45 + MVID:5, // 52:48 + reserved6:11; // 63:53 + } bits; + unsigned long long val; +}; diff -urN ../kernel-2.4.20/linux-2.4.20/arch/i386/kernel/setup.c linux-2.4.20/arch/i386/kernel/setup.c --- ../kernel-2.4.20/linux-2.4.20/arch/i386/kernel/setup.c 2003-09-10 00:38:33.000000000 -0400 +++ linux-2.4.20/arch/i386/kernel/setup.c 2003-09-09 19:55:34.000000000 -0400 @@ -119,6 +119,7 @@ #include #include #include +#include /* * Machine setup.. */ @@ -154,6 +155,7 @@ struct drive_info_struct { char dummy[32]; } drive_info; struct screen_info screen_info; struct apm_info apm_info; +struct ist_info ist_info; struct sys_desc_table_struct { unsigned short length; unsigned char table[0]; @@ -187,6 +189,7 @@ #define E820_MAP_NR (*(char*) (PARAM+E820NR)) #define E820_MAP ((struct e820entry *) (PARAM+E820MAP)) #define APM_BIOS_INFO (*(struct apm_bios_info *) (PARAM+0x40)) +#define IST_INFO (*(struct ist_info *) (PARAM+0x60)) #define DRIVE_INFO (*(struct drive_info_struct *) (PARAM+0x80)) #define SYS_DESC_TABLE (*(struct sys_desc_table_struct*)(PARAM+0xa0)) #define MOUNT_ROOT_RDONLY (*(unsigned short *) (PARAM+0x1F2)) @@ -1122,6 +1125,7 @@ drive_info = DRIVE_INFO; screen_info = SCREEN_INFO; apm_info.bios = APM_BIOS_INFO; + ist_info = IST_INFO; if( SYS_DESC_TABLE.length != 0 ) { MCA_bus = SYS_DESC_TABLE.table[3] &0x2; machine_id = SYS_DESC_TABLE.table[0]; diff -urN ../kernel-2.4.20/linux-2.4.20/arch/i386/kernel/speedstep-centrino.c linux-2.4.20/arch/i386/kernel/speedstep-centrino.c --- ../kernel-2.4.20/linux-2.4.20/arch/i386/kernel/speedstep-centrino.c 1969-12-31 19:00:00.000000000 -0500 +++ linux-2.4.20/arch/i386/kernel/speedstep-centrino.c 2003-08-25 10:58:47.000000000 -0400 @@ -0,0 +1,382 @@ +/* + * cpufreq driver for Enhanced SpeedStep, as found in Intel's Pentium + * M (part of the Centrino chipset). + * + * Despite the "SpeedStep" in the name, this is almost entirely unlike + * traditional SpeedStep. + * + * Modelled on speedstep.c + * + * Copyright (C) 2003 Jeremy Fitzhardinge + * + * WARNING WARNING WARNING + * + * This driver manipulates the PERF_CTL MSR, which is only somewhat + * documented. While it seems to work on my laptop, it has not been + * tested anywhere else, and it may not work for you, do strange + * things or simply crash. + */ + +#include +#include +#include +#include +#include /* BACKPORT: for strcmp */ +#include +#include +#include + +#define PFX "speedstep-centrino: " +#define MAINTAINER "Jeremy Fitzhardinge " + +/*#define CENTRINO_DEBUG*/ + +#ifdef CENTRINO_DEBUG +#define dprintk(msg...) printk(msg) +#else +#define dprintk(msg...) do { } while(0) +#endif + +struct cpu_model +{ + const char *model_name; + unsigned max_freq; /* max clock in kHz */ + + struct cpufreq_frequency_table *op_points; /* clock/voltage pairs */ +}; + +/* Operating points for current CPU */ +static const struct cpu_model *centrino_model; + +/* Computes the correct form for IA32_PERF_CTL MSR for a particular + frequency/voltage operating point; frequency in MHz, volts in mV. + This is stored as "index" in the structure. */ +#define OP(mhz, mv) \ + { \ + .frequency = (mhz) * 1000, \ + .index = (((mhz)/100) << 8) | ((mv - 700) / 16) \ + } + +/* + * These voltage tables were derived from the Intel Pentium M + * datasheet, document 25261202.pdf, Table 5. I have verified they + * are consistent with my IBM ThinkPad X31, which has a 1.3GHz Pentium + * M. + */ + +/* Ultra Low Voltage Intel Pentium M processor 900MHz */ +static struct cpufreq_frequency_table op_900[] = +{ + OP(600, 844), + OP(800, 988), + OP(900, 1004), + { .frequency = CPUFREQ_TABLE_END } +}; + +/* Low Voltage Intel Pentium M processor 1.10GHz */ +static struct cpufreq_frequency_table op_1100[] = +{ + OP( 600, 956), + OP( 800, 1020), + OP( 900, 1100), + OP(1000, 1164), + OP(1100, 1180), + { .frequency = CPUFREQ_TABLE_END } +}; + + +/* Low Voltage Intel Pentium M processor 1.20GHz */ +static struct cpufreq_frequency_table op_1200[] = +{ + OP( 600, 956), + OP( 800, 1004), + OP( 900, 1020), + OP(1000, 1100), + OP(1100, 1164), + OP(1200, 1180), + { .frequency = CPUFREQ_TABLE_END } +}; + +/* Intel Pentium M processor 1.30GHz */ +static struct cpufreq_frequency_table op_1300[] = +{ + OP( 600, 956), + OP( 800, 1260), + OP(1000, 1292), + OP(1200, 1356), + OP(1300, 1388), + { .frequency = CPUFREQ_TABLE_END } +}; + +/* Intel Pentium M processor 1.40GHz */ +static struct cpufreq_frequency_table op_1400[] = +{ + OP( 600, 956), + OP( 800, 1180), + OP(1000, 1308), + OP(1200, 1436), + OP(1400, 1484), + { .frequency = CPUFREQ_TABLE_END } +}; + +/* Intel Pentium M processor 1.50GHz */ +static struct cpufreq_frequency_table op_1500[] = +{ + OP( 600, 956), + OP( 800, 1116), + OP(1000, 1228), + OP(1200, 1356), + OP(1400, 1452), + OP(1500, 1484), + { .frequency = CPUFREQ_TABLE_END } +}; + +/* Intel Pentium M processor 1.60GHz */ +static struct cpufreq_frequency_table op_1600[] = +{ + OP( 600, 956), + OP( 800, 1036), + OP(1000, 1164), + OP(1200, 1276), + OP(1400, 1420), + OP(1600, 1484), + { .frequency = CPUFREQ_TABLE_END } +}; + +/* Intel Pentium M processor 1.70GHz */ +static struct cpufreq_frequency_table op_1700[] = +{ + OP( 600, 956), + OP( 800, 1004), + OP(1000, 1116), + OP(1200, 1228), + OP(1400, 1308), + OP(1700, 1484), + { .frequency = CPUFREQ_TABLE_END } +}; +#undef OP + +#define _CPU(max, name) \ + { "Intel(R) Pentium(R) M processor " name "MHz", (max)*1000, op_##max } +#define CPU(max) _CPU(max, #max) + +/* CPU models, their operating frequency range, and freq/voltage + operating points */ +static const struct cpu_model models[] = +{ + _CPU( 900, " 900"), + CPU(1100), + CPU(1200), + CPU(1300), + CPU(1400), + CPU(1500), + CPU(1600), + CPU(1700), + { 0, } +}; +#undef CPU + +/* Extract clock in kHz from PERF_CTL value */ +static unsigned extract_clock(unsigned msr) +{ + msr = (msr >> 8) & 0xff; + return msr * 100000; +} + +/* Return the current CPU frequency in kHz */ +static unsigned get_cur_freq(void) +{ + unsigned l, h; + + rdmsr(MSR_IA32_PERF_STATUS, l, h); + return extract_clock(l); +} + +static int centrino_cpu_init(struct cpufreq_policy *policy) +{ + unsigned freq; + + if (policy->cpu != 0 || centrino_model == NULL) + return -ENODEV; + + freq = get_cur_freq(); + + policy->policy = (freq == centrino_model->max_freq) ? + CPUFREQ_POLICY_PERFORMANCE : + CPUFREQ_POLICY_POWERSAVE; + policy->cpuinfo.transition_latency = 10; /* 10uS transition latency */ + policy->cur = freq; + + dprintk(KERN_INFO PFX "centrino_cpu_init: policy=%d cur=%dkHz\n", + policy->policy, policy->cur); + + return cpufreq_frequency_table_cpuinfo(policy, centrino_model->op_points); +} + +/** + * centrino_verify - verifies a new CPUFreq policy + * @freq: new policy + * + * Limit must be within this model's frequency range at least one + * border included. + */ +static int centrino_verify (struct cpufreq_policy *policy) +{ + return cpufreq_frequency_table_verify(policy, centrino_model->op_points); +} + +/** + * centrino_setpolicy - set a new CPUFreq policy + * @policy: new policy + * + * Sets a new CPUFreq policy. + */ +static int centrino_target (struct cpufreq_policy *policy, + unsigned int target_freq, + unsigned int relation) +{ + unsigned int newstate = 0; + unsigned int msr, oldmsr, h; + struct cpufreq_freqs freqs; + + if (centrino_model == NULL) + return -ENODEV; + + if (cpufreq_frequency_table_target(policy, centrino_model->op_points, target_freq, + relation, &newstate)) + return -EINVAL; + + msr = centrino_model->op_points[newstate].index; + rdmsr(MSR_IA32_PERF_CTL, oldmsr, h); + + if (msr == (oldmsr & 0xffff)) + return 0; + + /* Hm, old frequency can either be the last value we put in + PERF_CTL, or whatever it is now. The trouble is that TM2 + can change it behind our back, which means we never get to + see the speed change. Reading back the current speed would + tell us something happened, but it may leave the things on + the notifier chain confused; we therefore stick to using + the last programmed speed rather than the current speed for + "old". + + TODO: work out how the TCC interrupts work, and try to + catch the CPU changing things under us. + */ + freqs.cpu = 0; + freqs.old = extract_clock(oldmsr); + freqs.new = extract_clock(msr); + + dprintk(KERN_INFO PFX "target=%dkHz old=%d new=%d msr=%04x\n", + target_freq, freqs.old, freqs.new, msr); + + cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE); + + /* all but 16 LSB are "reserved", so treat them with + care */ + oldmsr &= ~0xffff; + msr &= 0xffff; + oldmsr |= msr; + + wrmsr(MSR_IA32_PERF_CTL, oldmsr, h); + + cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE); + + return 0; +} + +static struct cpufreq_driver centrino_driver = { + .name = "centrino", /* should be speedstep-centrino, + but there's a 16 char limit */ + .init = centrino_cpu_init, + .verify = centrino_verify, + .target = centrino_target, +}; + + +/** + * centrino_init - initializes the Enhanced SpeedStep CPUFreq driver + * + * Initializes the Enhanced SpeedStep support. Returns -ENODEV on + * unsupported devices, -ENOENT if there's no voltage table for this + * particular CPU model, -EINVAL on problems during initiatization, + * and zero on success. + * + * This is quite picky. Not only does the CPU have to advertise the + * "est" flag in the cpuid capability flags, we look for a specific + * CPU model and stepping, and we need to have the exact model name in + * our voltage tables. That is, be paranoid about not releasing + * someone's valuable magic smoke. + */ +static int __init centrino_init(void) +{ + struct cpuinfo_x86 *cpu = cpu_data; + const struct cpu_model *model; + unsigned l, h; + int dummy, ecx; + + /* backport info: we can't use cpu_has here, as cpuid(1) isn't + * stored in 2.4 + */ + cpuid(1,&dummy,&dummy,&ecx,&dummy); + if (!(ecx & (1<<7))) + return -ENODEV; + + /* Only Intel Pentium M stepping 5 for now - add new CPUs as + they appear after making sure they use PERF_CTL in the same + way. */ + if (cpu->x86_vendor != X86_VENDOR_INTEL || + cpu->x86 != 6 || + cpu->x86_model != 9 || + cpu->x86_mask != 5) { + printk(KERN_INFO PFX "found unsupported CPU with Enhanced SpeedStep: " + "send /proc/cpuinfo to " MAINTAINER "\n"); + return -ENODEV; + } + + /* Check to see if Enhanced SpeedStep is enabled, and try to + enable it if not. */ + rdmsr(MSR_IA32_MISC_ENABLE, l, h); + + if (!(l & (1<<16))) { + l |= (1<<16); + wrmsr(MSR_IA32_MISC_ENABLE, l, h); + + /* check to see if it stuck */ + rdmsr(MSR_IA32_MISC_ENABLE, l, h); + if (!(l & (1<<16))) { + printk(KERN_INFO PFX "couldn't enable Enhanced SpeedStep\n"); + return -ENODEV; + } + } + + for(model = models; model->model_name != NULL; model++) + if (strcmp(cpu->x86_model_id, model->model_name) == 0) + break; + if (model->model_name == NULL) { + printk(KERN_INFO PFX "no support for CPU model \"%s\": " + "send /proc/cpuinfo to " MAINTAINER "\n", + cpu->x86_model_id); + return -ENOENT; + } + + centrino_model = model; + + printk(KERN_INFO PFX "found \"%s\": max frequency: %dkHz\n", + model->model_name, model->max_freq); + + return cpufreq_register_driver(¢rino_driver); +} + +static void __exit centrino_exit(void) +{ + cpufreq_unregister_driver(¢rino_driver); +} + +MODULE_AUTHOR ("Jeremy Fitzhardinge "); +MODULE_DESCRIPTION ("Enhanced SpeedStep driver for Intel Pentium M processors."); +MODULE_LICENSE ("GPL"); + +module_init(centrino_init); +module_exit(centrino_exit); diff -urN ../kernel-2.4.20/linux-2.4.20/arch/i386/kernel/speedstep-ich.c linux-2.4.20/arch/i386/kernel/speedstep-ich.c --- ../kernel-2.4.20/linux-2.4.20/arch/i386/kernel/speedstep-ich.c 1969-12-31 19:00:00.000000000 -0500 +++ linux-2.4.20/arch/i386/kernel/speedstep-ich.c 2003-08-25 10:58:47.000000000 -0400 @@ -0,0 +1,363 @@ +/* + * (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-lib.h" + + +/* speedstep_chipset: + * It is necessary to know which chipset is used. As accesses to + * this device occur at various places in this module, we need a + * static struct pci_dev * pointing to that device. + */ +static struct pci_dev *speedstep_chipset_dev; + + +/* speedstep_processor + */ +static unsigned int speedstep_processor = 0; + + +/* + * There are only two frequency states for each processor. Values + * are in kHz for the time being. + */ +static struct cpufreq_frequency_table speedstep_freqs[] = { + {SPEEDSTEP_HIGH, 0}, + {SPEEDSTEP_LOW, 0}, + {0, CPUFREQ_TABLE_END}, +}; + + +/* DEBUG + * Define it if you want verbose debug output, e.g. for bug reporting + */ +//#define SPEEDSTEP_DEBUG + +#ifdef SPEEDSTEP_DEBUG +#define dprintk(msg...) printk(msg) +#else +#define dprintk(msg...) do { } while(0) +#endif + + +/** + * speedstep_set_state - set the SpeedStep state + * @state: new processor frequency state (SPEEDSTEP_LOW or SPEEDSTEP_HIGH) + * + * Tries to change the SpeedStep state. + */ +static void speedstep_set_state (unsigned int state, unsigned int notify) +{ + u32 pmbase; + u8 pm2_blk; + u8 value; + unsigned long flags; + struct cpufreq_freqs freqs; + + if (!speedstep_chipset_dev || (state > 0x1)) + return; + + freqs.old = speedstep_get_processor_frequency(speedstep_processor); + freqs.new = speedstep_freqs[stat