Merge master.kernel.org:/pub/scm/linux/kernel/git/davej/cpufreq
authorLinus Torvalds <torvalds@woody.linux-foundation.org>
Fri, 12 Oct 2007 22:42:01 +0000 (15:42 -0700)
committerLinus Torvalds <torvalds@woody.linux-foundation.org>
Fri, 12 Oct 2007 22:42:01 +0000 (15:42 -0700)
* master.kernel.org:/pub/scm/linux/kernel/git/davej/cpufreq:
  [CPUFREQ] Don't take semaphore in cpufreq_quick_get()
  [CPUFREQ] Support different families in fid/did to frequency conversion
  [CPUFREQ] cpufreq_stats: misc cpuinit section annotations
  [CPUFREQ] implement !CONFIG_CPU_FREQ stub for  cpufreq_unregister_notifier()
  [CPUFREQ] mark hotplug notifier callback as __cpuinit
  [CPUFREQ] Only check for transition latency on problematic governors (kconfig fix)
  [CPUFREQ] allow ondemand and conservative cpufreq governors to be used as default
  [CPUFREQ] move policy's governor initialisation out of low-level drivers into cpufreq core
  [CPUFREQ] Longhaul - Add support for PM133 northbridge
  [CPUFREQ] x86: use num_online_nodes to get physical cpus numbers for

15 files changed:
1  2 
arch/powerpc/platforms/cell/cbe_cpufreq.c
arch/x86/kernel/cpu/cpufreq/acpi-cpufreq.c
arch/x86/kernel/cpu/cpufreq/cpufreq-nforce2.c
arch/x86/kernel/cpu/cpufreq/e_powersaver.c
arch/x86/kernel/cpu/cpufreq/elanfreq.c
arch/x86/kernel/cpu/cpufreq/gx-suspmod.c
arch/x86/kernel/cpu/cpufreq/longhaul.c
arch/x86/kernel/cpu/cpufreq/p4-clockmod.c
arch/x86/kernel/cpu/cpufreq/powernow-k6.c
arch/x86/kernel/cpu/cpufreq/powernow-k7.c
arch/x86/kernel/cpu/cpufreq/powernow-k8.c
arch/x86/kernel/cpu/cpufreq/sc520_freq.c
arch/x86/kernel/cpu/cpufreq/speedstep-centrino.c
arch/x86/kernel/cpu/cpufreq/speedstep-ich.c
arch/x86/kernel/cpu/cpufreq/speedstep-smi.c

index b6434a7ef8b2b311f76aad4307f642e64746dd8b,0000000000000000000000000000000000000000..ffd01e5dcb52c2cacf61f8dee9cf2f450543b8a7
mode 100644,000000..100644
--- /dev/null
@@@ -1,799 -1,0 +1,798 @@@
-       policy->governor = CPUFREQ_DEFAULT_GOVERNOR;
 +/*
 + * acpi-cpufreq.c - ACPI Processor P-States Driver ($Revision: 1.4 $)
 + *
 + *  Copyright (C) 2001, 2002 Andy Grover <andrew.grover@intel.com>
 + *  Copyright (C) 2001, 2002 Paul Diefenbaugh <paul.s.diefenbaugh@intel.com>
 + *  Copyright (C) 2002 - 2004 Dominik Brodowski <linux@brodo.de>
 + *  Copyright (C) 2006       Denis Sadykov <denis.m.sadykov@intel.com>
 + *
 + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 + *
 + *  This program is free software; you can redistribute it and/or modify
 + *  it under the terms of the GNU General Public License as published by
 + *  the Free Software Foundation; either version 2 of the License, or (at
 + *  your option) any later version.
 + *
 + *  This program is distributed in the hope that it will be useful, but
 + *  WITHOUT ANY WARRANTY; without even the implied warranty of
 + *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 + *  General Public License for more details.
 + *
 + *  You should have received a copy of the GNU General Public License along
 + *  with this program; if not, write to the Free Software Foundation, Inc.,
 + *  59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
 + *
 + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 + */
 +
 +#include <linux/kernel.h>
 +#include <linux/module.h>
 +#include <linux/init.h>
 +#include <linux/smp.h>
 +#include <linux/sched.h>
 +#include <linux/cpufreq.h>
 +#include <linux/compiler.h>
 +#include <linux/dmi.h>
 +
 +#include <linux/acpi.h>
 +#include <acpi/processor.h>
 +
 +#include <asm/io.h>
 +#include <asm/msr.h>
 +#include <asm/processor.h>
 +#include <asm/cpufeature.h>
 +#include <asm/delay.h>
 +#include <asm/uaccess.h>
 +
 +#define dprintk(msg...) cpufreq_debug_printk(CPUFREQ_DEBUG_DRIVER, "acpi-cpufreq", msg)
 +
 +MODULE_AUTHOR("Paul Diefenbaugh, Dominik Brodowski");
 +MODULE_DESCRIPTION("ACPI Processor P-States Driver");
 +MODULE_LICENSE("GPL");
 +
 +enum {
 +      UNDEFINED_CAPABLE = 0,
 +      SYSTEM_INTEL_MSR_CAPABLE,
 +      SYSTEM_IO_CAPABLE,
 +};
 +
 +#define INTEL_MSR_RANGE               (0xffff)
 +#define CPUID_6_ECX_APERFMPERF_CAPABILITY     (0x1)
 +
 +struct acpi_cpufreq_data {
 +      struct acpi_processor_performance *acpi_data;
 +      struct cpufreq_frequency_table *freq_table;
 +      unsigned int max_freq;
 +      unsigned int resume;
 +      unsigned int cpu_feature;
 +};
 +
 +static struct acpi_cpufreq_data *drv_data[NR_CPUS];
 +/* acpi_perf_data is a pointer to percpu data. */
 +static struct acpi_processor_performance *acpi_perf_data;
 +
 +static struct cpufreq_driver acpi_cpufreq_driver;
 +
 +static unsigned int acpi_pstate_strict;
 +
 +static int check_est_cpu(unsigned int cpuid)
 +{
 +      struct cpuinfo_x86 *cpu = &cpu_data[cpuid];
 +
 +      if (cpu->x86_vendor != X86_VENDOR_INTEL ||
 +          !cpu_has(cpu, X86_FEATURE_EST))
 +              return 0;
 +
 +      return 1;
 +}
 +
 +static unsigned extract_io(u32 value, struct acpi_cpufreq_data *data)
 +{
 +      struct acpi_processor_performance *perf;
 +      int i;
 +
 +      perf = data->acpi_data;
 +
 +      for (i=0; i<perf->state_count; i++) {
 +              if (value == perf->states[i].status)
 +                      return data->freq_table[i].frequency;
 +      }
 +      return 0;
 +}
 +
 +static unsigned extract_msr(u32 msr, struct acpi_cpufreq_data *data)
 +{
 +      int i;
 +      struct acpi_processor_performance *perf;
 +
 +      msr &= INTEL_MSR_RANGE;
 +      perf = data->acpi_data;
 +
 +      for (i=0; data->freq_table[i].frequency != CPUFREQ_TABLE_END; i++) {
 +              if (msr == perf->states[data->freq_table[i].index].status)
 +                      return data->freq_table[i].frequency;
 +      }
 +      return data->freq_table[0].frequency;
 +}
 +
 +static unsigned extract_freq(u32 val, struct acpi_cpufreq_data *data)
 +{
 +      switch (data->cpu_feature) {
 +      case SYSTEM_INTEL_MSR_CAPABLE:
 +              return extract_msr(val, data);
 +      case SYSTEM_IO_CAPABLE:
 +              return extract_io(val, data);
 +      default:
 +              return 0;
 +      }
 +}
 +
 +struct msr_addr {
 +      u32 reg;
 +};
 +
 +struct io_addr {
 +      u16 port;
 +      u8 bit_width;
 +};
 +
 +typedef union {
 +      struct msr_addr msr;
 +      struct io_addr io;
 +} drv_addr_union;
 +
 +struct drv_cmd {
 +      unsigned int type;
 +      cpumask_t mask;
 +      drv_addr_union addr;
 +      u32 val;
 +};
 +
 +static void do_drv_read(struct drv_cmd *cmd)
 +{
 +      u32 h;
 +
 +      switch (cmd->type) {
 +      case SYSTEM_INTEL_MSR_CAPABLE:
 +              rdmsr(cmd->addr.msr.reg, cmd->val, h);
 +              break;
 +      case SYSTEM_IO_CAPABLE:
 +              acpi_os_read_port((acpi_io_address)cmd->addr.io.port,
 +                              &cmd->val,
 +                              (u32)cmd->addr.io.bit_width);
 +              break;
 +      default:
 +              break;
 +      }
 +}
 +
 +static void do_drv_write(struct drv_cmd *cmd)
 +{
 +      u32 lo, hi;
 +
 +      switch (cmd->type) {
 +      case SYSTEM_INTEL_MSR_CAPABLE:
 +              rdmsr(cmd->addr.msr.reg, lo, hi);
 +              lo = (lo & ~INTEL_MSR_RANGE) | (cmd->val & INTEL_MSR_RANGE);
 +              wrmsr(cmd->addr.msr.reg, lo, hi);
 +              break;
 +      case SYSTEM_IO_CAPABLE:
 +              acpi_os_write_port((acpi_io_address)cmd->addr.io.port,
 +                              cmd->val,
 +                              (u32)cmd->addr.io.bit_width);
 +              break;
 +      default:
 +              break;
 +      }
 +}
 +
 +static void drv_read(struct drv_cmd *cmd)
 +{
 +      cpumask_t saved_mask = current->cpus_allowed;
 +      cmd->val = 0;
 +
 +      set_cpus_allowed(current, cmd->mask);
 +      do_drv_read(cmd);
 +      set_cpus_allowed(current, saved_mask);
 +}
 +
 +static void drv_write(struct drv_cmd *cmd)
 +{
 +      cpumask_t saved_mask = current->cpus_allowed;
 +      unsigned int i;
 +
 +      for_each_cpu_mask(i, cmd->mask) {
 +              set_cpus_allowed(current, cpumask_of_cpu(i));
 +              do_drv_write(cmd);
 +      }
 +
 +      set_cpus_allowed(current, saved_mask);
 +      return;
 +}
 +
 +static u32 get_cur_val(cpumask_t mask)
 +{
 +      struct acpi_processor_performance *perf;
 +      struct drv_cmd cmd;
 +
 +      if (unlikely(cpus_empty(mask)))
 +              return 0;
 +
 +      switch (drv_data[first_cpu(mask)]->cpu_feature) {
 +      case SYSTEM_INTEL_MSR_CAPABLE:
 +              cmd.type = SYSTEM_INTEL_MSR_CAPABLE;
 +              cmd.addr.msr.reg = MSR_IA32_PERF_STATUS;
 +              break;
 +      case SYSTEM_IO_CAPABLE:
 +              cmd.type = SYSTEM_IO_CAPABLE;
 +              perf = drv_data[first_cpu(mask)]->acpi_data;
 +              cmd.addr.io.port = perf->control_register.address;
 +              cmd.addr.io.bit_width = perf->control_register.bit_width;
 +              break;
 +      default:
 +              return 0;
 +      }
 +
 +      cmd.mask = mask;
 +
 +      drv_read(&cmd);
 +
 +      dprintk("get_cur_val = %u\n", cmd.val);
 +
 +      return cmd.val;
 +}
 +
 +/*
 + * Return the measured active (C0) frequency on this CPU since last call
 + * to this function.
 + * Input: cpu number
 + * Return: Average CPU frequency in terms of max frequency (zero on error)
 + *
 + * We use IA32_MPERF and IA32_APERF MSRs to get the measured performance
 + * over a period of time, while CPU is in C0 state.
 + * IA32_MPERF counts at the rate of max advertised frequency
 + * IA32_APERF counts at the rate of actual CPU frequency
 + * Only IA32_APERF/IA32_MPERF ratio is architecturally defined and
 + * no meaning should be associated with absolute values of these MSRs.
 + */
 +static unsigned int get_measured_perf(unsigned int cpu)
 +{
 +      union {
 +              struct {
 +                      u32 lo;
 +                      u32 hi;
 +              } split;
 +              u64 whole;
 +      } aperf_cur, mperf_cur;
 +
 +      cpumask_t saved_mask;
 +      unsigned int perf_percent;
 +      unsigned int retval;
 +
 +      saved_mask = current->cpus_allowed;
 +      set_cpus_allowed(current, cpumask_of_cpu(cpu));
 +      if (get_cpu() != cpu) {
 +              /* We were not able to run on requested processor */
 +              put_cpu();
 +              return 0;
 +      }
 +
 +      rdmsr(MSR_IA32_APERF, aperf_cur.split.lo, aperf_cur.split.hi);
 +      rdmsr(MSR_IA32_MPERF, mperf_cur.split.lo, mperf_cur.split.hi);
 +
 +      wrmsr(MSR_IA32_APERF, 0,0);
 +      wrmsr(MSR_IA32_MPERF, 0,0);
 +
 +#ifdef __i386__
 +      /*
 +       * We dont want to do 64 bit divide with 32 bit kernel
 +       * Get an approximate value. Return failure in case we cannot get
 +       * an approximate value.
 +       */
 +      if (unlikely(aperf_cur.split.hi || mperf_cur.split.hi)) {
 +              int shift_count;
 +              u32 h;
 +
 +              h = max_t(u32, aperf_cur.split.hi, mperf_cur.split.hi);
 +              shift_count = fls(h);
 +
 +              aperf_cur.whole >>= shift_count;
 +              mperf_cur.whole >>= shift_count;
 +      }
 +
 +      if (((unsigned long)(-1) / 100) < aperf_cur.split.lo) {
 +              int shift_count = 7;
 +              aperf_cur.split.lo >>= shift_count;
 +              mperf_cur.split.lo >>= shift_count;
 +      }
 +
 +      if (aperf_cur.split.lo && mperf_cur.split.lo)
 +              perf_percent = (aperf_cur.split.lo * 100) / mperf_cur.split.lo;
 +      else
 +              perf_percent = 0;
 +
 +#else
 +      if (unlikely(((unsigned long)(-1) / 100) < aperf_cur.whole)) {
 +              int shift_count = 7;
 +              aperf_cur.whole >>= shift_count;
 +              mperf_cur.whole >>= shift_count;
 +      }
 +
 +      if (aperf_cur.whole && mperf_cur.whole)
 +              perf_percent = (aperf_cur.whole * 100) / mperf_cur.whole;
 +      else
 +              perf_percent = 0;
 +
 +#endif
 +
 +      retval = drv_data[cpu]->max_freq * perf_percent / 100;
 +
 +      put_cpu();
 +      set_cpus_allowed(current, saved_mask);
 +
 +      dprintk("cpu %d: performance percent %d\n", cpu, perf_percent);
 +      return retval;
 +}
 +
 +static unsigned int get_cur_freq_on_cpu(unsigned int cpu)
 +{
 +      struct acpi_cpufreq_data *data = drv_data[cpu];
 +      unsigned int freq;
 +
 +      dprintk("get_cur_freq_on_cpu (%d)\n", cpu);
 +
 +      if (unlikely(data == NULL ||
 +                   data->acpi_data == NULL || data->freq_table == NULL)) {
 +              return 0;
 +      }
 +
 +      freq = extract_freq(get_cur_val(cpumask_of_cpu(cpu)), data);
 +      dprintk("cur freq = %u\n", freq);
 +
 +      return freq;
 +}
 +
 +static unsigned int check_freqs(cpumask_t mask, unsigned int freq,
 +                              struct acpi_cpufreq_data *data)
 +{
 +      unsigned int cur_freq;
 +      unsigned int i;
 +
 +      for (i=0; i<100; i++) {
 +              cur_freq = extract_freq(get_cur_val(mask), data);
 +              if (cur_freq == freq)
 +                      return 1;
 +              udelay(10);
 +      }
 +      return 0;
 +}
 +
 +static int acpi_cpufreq_target(struct cpufreq_policy *policy,
 +                             unsigned int target_freq, unsigned int relation)
 +{
 +      struct acpi_cpufreq_data *data = drv_data[policy->cpu];
 +      struct acpi_processor_performance *perf;
 +      struct cpufreq_freqs freqs;
 +      cpumask_t online_policy_cpus;
 +      struct drv_cmd cmd;
 +      unsigned int next_state = 0; /* Index into freq_table */
 +      unsigned int next_perf_state = 0; /* Index into perf table */
 +      unsigned int i;
 +      int result = 0;
 +
 +      dprintk("acpi_cpufreq_target %d (%d)\n", target_freq, policy->cpu);
 +
 +      if (unlikely(data == NULL ||
 +           data->acpi_data == NULL || data->freq_table == NULL)) {
 +              return -ENODEV;
 +      }
 +
 +      perf = data->acpi_data;
 +      result = cpufreq_frequency_table_target(policy,
 +                                              data->freq_table,
 +                                              target_freq,
 +                                              relation, &next_state);
 +      if (unlikely(result))
 +              return -ENODEV;
 +
 +#ifdef CONFIG_HOTPLUG_CPU
 +      /* cpufreq holds the hotplug lock, so we are safe from here on */
 +      cpus_and(online_policy_cpus, cpu_online_map, policy->cpus);
 +#else
 +      online_policy_cpus = policy->cpus;
 +#endif
 +
 +      next_perf_state = data->freq_table[next_state].index;
 +      if (perf->state == next_perf_state) {
 +              if (unlikely(data->resume)) {
 +                      dprintk("Called after resume, resetting to P%d\n",
 +                              next_perf_state);
 +                      data->resume = 0;
 +              } else {
 +                      dprintk("Already at target state (P%d)\n",
 +                              next_perf_state);
 +                      return 0;
 +              }
 +      }
 +
 +      switch (data->cpu_feature) {
 +      case SYSTEM_INTEL_MSR_CAPABLE:
 +              cmd.type = SYSTEM_INTEL_MSR_CAPABLE;
 +              cmd.addr.msr.reg = MSR_IA32_PERF_CTL;
 +              cmd.val = (u32) perf->states[next_perf_state].control;
 +              break;
 +      case SYSTEM_IO_CAPABLE:
 +              cmd.type = SYSTEM_IO_CAPABLE;
 +              cmd.addr.io.port = perf->control_register.address;
 +              cmd.addr.io.bit_width = perf->control_register.bit_width;
 +              cmd.val = (u32) perf->states[next_perf_state].control;
 +              break;
 +      default:
 +              return -ENODEV;
 +      }
 +
 +      cpus_clear(cmd.mask);
 +
 +      if (policy->shared_type != CPUFREQ_SHARED_TYPE_ANY)
 +              cmd.mask = online_policy_cpus;
 +      else
 +              cpu_set(policy->cpu, cmd.mask);
 +
 +      freqs.old = perf->states[perf->state].core_frequency * 1000;
 +      freqs.new = data->freq_table[next_state].frequency;
 +      for_each_cpu_mask(i, cmd.mask) {
 +              freqs.cpu = i;
 +              cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
 +      }
 +
 +      drv_write(&cmd);
 +
 +      if (acpi_pstate_strict) {
 +              if (!check_freqs(cmd.mask, freqs.new, data)) {
 +                      dprintk("acpi_cpufreq_target failed (%d)\n",
 +                              policy->cpu);
 +                      return -EAGAIN;
 +              }
 +      }
 +
 +      for_each_cpu_mask(i, cmd.mask) {
 +              freqs.cpu = i;
 +              cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
 +      }
 +      perf->state = next_perf_state;
 +
 +      return result;
 +}
 +
 +static int acpi_cpufreq_verify(struct cpufreq_policy *policy)
 +{
 +      struct acpi_cpufreq_data *data = drv_data[policy->cpu];
 +
 +      dprintk("acpi_cpufreq_verify\n");
 +
 +      return cpufreq_frequency_table_verify(policy, data->freq_table);
 +}
 +
 +static unsigned long
 +acpi_cpufreq_guess_freq(struct acpi_cpufreq_data *data, unsigned int cpu)
 +{
 +      struct acpi_processor_performance *perf = data->acpi_data;
 +
 +      if (cpu_khz) {
 +              /* search the closest match to cpu_khz */
 +              unsigned int i;
 +              unsigned long freq;
 +              unsigned long freqn = perf->states[0].core_frequency * 1000;
 +
 +              for (i=0; i<(perf->state_count-1); i++) {
 +                      freq = freqn;
 +                      freqn = perf->states[i+1].core_frequency * 1000;
 +                      if ((2 * cpu_khz) > (freqn + freq)) {
 +                              perf->state = i;
 +                              return freq;
 +                      }
 +              }
 +              perf->state = perf->state_count-1;
 +              return freqn;
 +      } else {
 +              /* assume CPU is at P0... */
 +              perf->state = 0;
 +              return perf->states[0].core_frequency * 1000;
 +      }
 +}
 +
 +/*
 + * acpi_cpufreq_early_init - initialize ACPI P-States library
 + *
 + * Initialize the ACPI P-States library (drivers/acpi/processor_perflib.c)
 + * in order to determine correct frequency and voltage pairings. We can
 + * do _PDC and _PSD and find out the processor dependency for the
 + * actual init that will happen later...
 + */
 +static int __init acpi_cpufreq_early_init(void)
 +{
 +      dprintk("acpi_cpufreq_early_init\n");
 +
 +      acpi_perf_data = alloc_percpu(struct acpi_processor_performance);
 +      if (!acpi_perf_data) {
 +              dprintk("Memory allocation error for acpi_perf_data.\n");
 +              return -ENOMEM;
 +      }
 +
 +      /* Do initialization in ACPI core */
 +      acpi_processor_preregister_performance(acpi_perf_data);
 +      return 0;
 +}
 +
 +#ifdef CONFIG_SMP
 +/*
 + * Some BIOSes do SW_ANY coordination internally, either set it up in hw
 + * or do it in BIOS firmware and won't inform about it to OS. If not
 + * detected, this has a side effect of making CPU run at a different speed
 + * than OS intended it to run at. Detect it and handle it cleanly.
 + */
 +static int bios_with_sw_any_bug;
 +
 +static int sw_any_bug_found(const struct dmi_system_id *d)
 +{
 +      bios_with_sw_any_bug = 1;
 +      return 0;
 +}
 +
 +static const struct dmi_system_id sw_any_bug_dmi_table[] = {
 +      {
 +              .callback = sw_any_bug_found,
 +              .ident = "Supermicro Server X6DLP",
 +              .matches = {
 +                      DMI_MATCH(DMI_SYS_VENDOR, "Supermicro"),
 +                      DMI_MATCH(DMI_BIOS_VERSION, "080010"),
 +                      DMI_MATCH(DMI_PRODUCT_NAME, "X6DLP"),
 +              },
 +      },
 +      { }
 +};
 +#endif
 +
 +static int acpi_cpufreq_cpu_init(struct cpufreq_policy *policy)
 +{
 +      unsigned int i;
 +      unsigned int valid_states = 0;
 +      unsigned int cpu = policy->cpu;
 +      struct acpi_cpufreq_data *data;
 +      unsigned int result = 0;
 +      struct cpuinfo_x86 *c = &cpu_data[policy->cpu];
 +      struct acpi_processor_performance *perf;
 +
 +      dprintk("acpi_cpufreq_cpu_init\n");
 +
 +      data = kzalloc(sizeof(struct acpi_cpufreq_data), GFP_KERNEL);
 +      if (!data)
 +              return -ENOMEM;
 +
 +      data->acpi_data = percpu_ptr(acpi_perf_data, cpu);
 +      drv_data[cpu] = data;
 +
 +      if (cpu_has(c, X86_FEATURE_CONSTANT_TSC))
 +              acpi_cpufreq_driver.flags |= CPUFREQ_CONST_LOOPS;
 +
 +      result = acpi_processor_register_performance(data->acpi_data, cpu);
 +      if (result)
 +              goto err_free;
 +
 +      perf = data->acpi_data;
 +      policy->shared_type = perf->shared_type;
 +
 +      /*
 +       * Will let policy->cpus know about dependency only when software
 +       * coordination is required.
 +       */
 +      if (policy->shared_type == CPUFREQ_SHARED_TYPE_ALL ||
 +          policy->shared_type == CPUFREQ_SHARED_TYPE_ANY) {
 +              policy->cpus = perf->shared_cpu_map;
 +      }
 +
 +#ifdef CONFIG_SMP
 +      dmi_check_system(sw_any_bug_dmi_table);
 +      if (bios_with_sw_any_bug && cpus_weight(policy->cpus) == 1) {
 +              policy->shared_type = CPUFREQ_SHARED_TYPE_ALL;
 +              policy->cpus = cpu_core_map[cpu];
 +      }
 +#endif
 +
 +      /* capability check */
 +      if (perf->state_count <= 1) {
 +              dprintk("No P-States\n");
 +              result = -ENODEV;
 +              goto err_unreg;
 +      }
 +
 +      if (perf->control_register.space_id != perf->status_register.space_id) {
 +              result = -ENODEV;
 +              goto err_unreg;
 +      }
 +
 +      switch (perf->control_register.space_id) {
 +      case ACPI_ADR_SPACE_SYSTEM_IO:
 +              dprintk("SYSTEM IO addr space\n");
 +              data->cpu_feature = SYSTEM_IO_CAPABLE;
 +              break;
 +      case ACPI_ADR_SPACE_FIXED_HARDWARE:
 +              dprintk("HARDWARE addr space\n");
 +              if (!check_est_cpu(cpu)) {
 +                      result = -ENODEV;
 +                      goto err_unreg;
 +              }
 +              data->cpu_feature = SYSTEM_INTEL_MSR_CAPABLE;
 +              break;
 +      default:
 +              dprintk("Unknown addr space %d\n",
 +                      (u32) (perf->control_register.space_id));
 +              result = -ENODEV;
 +              goto err_unreg;
 +      }
 +
 +      data->freq_table = kmalloc(sizeof(struct cpufreq_frequency_table) *
 +                  (perf->state_count+1), GFP_KERNEL);
 +      if (!data->freq_table) {
 +              result = -ENOMEM;
 +              goto err_unreg;
 +      }
 +
 +      /* detect transition latency */
 +      policy->cpuinfo.transition_latency = 0;
 +      for (i=0; i<perf->state_count; i++) {
 +              if ((perf->states[i].transition_latency * 1000) >
 +                  policy->cpuinfo.transition_latency)
 +                      policy->cpuinfo.transition_latency =
 +                          perf->states[i].transition_latency * 1000;
 +      }
 +
 +      data->max_freq = perf->states[0].core_frequency * 1000;
 +      /* table init */
 +      for (i=0; i<perf->state_count; i++) {
 +              if (i>0 && perf->states[i].core_frequency >=
 +                  data->freq_table[valid_states-1].frequency / 1000)
 +                      continue;
 +
 +              data->freq_table[valid_states].index = i;
 +              data->freq_table[valid_states].frequency =
 +                  perf->states[i].core_frequency * 1000;
 +              valid_states++;
 +      }
 +      data->freq_table[valid_states].frequency = CPUFREQ_TABLE_END;
 +      perf->state = 0;
 +
 +      result = cpufreq_frequency_table_cpuinfo(policy, data->freq_table);
 +      if (result)
 +              goto err_freqfree;
 +
 +      switch (perf->control_register.space_id) {
 +      case ACPI_ADR_SPACE_SYSTEM_IO:
 +              /* Current speed is unknown and not detectable by IO port */
 +              policy->cur = acpi_cpufreq_guess_freq(data, policy->cpu);
 +              break;
 +      case ACPI_ADR_SPACE_FIXED_HARDWARE:
 +              acpi_cpufreq_driver.get = get_cur_freq_on_cpu;
 +              policy->cur = get_cur_freq_on_cpu(cpu);
 +              break;
 +      default:
 +              break;
 +      }
 +
 +      /* notify BIOS that we exist */
 +      acpi_processor_notify_smm(THIS_MODULE);
 +
 +      /* Check for APERF/MPERF support in hardware */
 +      if (c->x86_vendor == X86_VENDOR_INTEL && c->cpuid_level >= 6) {
 +              unsigned int ecx;
 +              ecx = cpuid_ecx(6);
 +              if (ecx & CPUID_6_ECX_APERFMPERF_CAPABILITY)
 +                      acpi_cpufreq_driver.getavg = get_measured_perf;
 +      }
 +
 +      dprintk("CPU%u - ACPI performance management activated.\n", cpu);
 +      for (i = 0; i < perf->state_count; i++)
 +              dprintk("     %cP%d: %d MHz, %d mW, %d uS\n",
 +                      (i == perf->state ? '*' : ' '), i,
 +                      (u32) perf->states[i].core_frequency,
 +                      (u32) perf->states[i].power,
 +                      (u32) perf->states[i].transition_latency);
 +
 +      cpufreq_frequency_table_get_attr(data->freq_table, policy->cpu);
 +
 +      /*
 +       * the first call to ->target() should result in us actually
 +       * writing something to the appropriate registers.
 +       */
 +      data->resume = 1;
 +
 +      return result;
 +
 +err_freqfree:
 +      kfree(data->freq_table);
 +err_unreg:
 +      acpi_processor_unregister_performance(perf, cpu);
 +err_free:
 +      kfree(data);
 +      drv_data[cpu] = NULL;
 +
 +      return result;
 +}
 +
 +static int acpi_cpufreq_cpu_exit(struct cpufreq_policy *policy)
 +{
 +      struct acpi_cpufreq_data *data = drv_data[policy->cpu];
 +
 +      dprintk("acpi_cpufreq_cpu_exit\n");
 +
 +      if (data) {
 +              cpufreq_frequency_table_put_attr(policy->cpu);
 +              drv_data[policy->cpu] = NULL;
 +              acpi_processor_unregister_performance(data->acpi_data,
 +                                                    policy->cpu);
 +              kfree(data);
 +      }
 +
 +      return 0;
 +}
 +
 +static int acpi_cpufreq_resume(struct cpufreq_policy *policy)
 +{
 +      struct acpi_cpufreq_data *data = drv_data[policy->cpu];
 +
 +      dprintk("acpi_cpufreq_resume\n");
 +
 +      data->resume = 1;
 +
 +      return 0;
 +}
 +
 +static struct freq_attr *acpi_cpufreq_attr[] = {
 +      &cpufreq_freq_attr_scaling_available_freqs,
 +      NULL,
 +};
 +
 +static struct cpufreq_driver acpi_cpufreq_driver = {
 +      .verify = acpi_cpufreq_verify,
 +      .target = acpi_cpufreq_target,
 +      .init = acpi_cpufreq_cpu_init,
 +      .exit = acpi_cpufreq_cpu_exit,
 +      .resume = acpi_cpufreq_resume,
 +      .name = "acpi-cpufreq",
 +      .owner = THIS_MODULE,
 +      .attr = acpi_cpufreq_attr,
 +};
 +
 +static int __init acpi_cpufreq_init(void)
 +{
 +      int ret;
 +
 +      dprintk("acpi_cpufreq_init\n");
 +
 +      ret = acpi_cpufreq_early_init();
 +      if (ret)
 +              return ret;
 +
 +      return cpufreq_register_driver(&acpi_cpufreq_driver);
 +}
 +
 +static void __exit acpi_cpufreq_exit(void)
 +{
 +      dprintk("acpi_cpufreq_exit\n");
 +
 +      cpufreq_unregister_driver(&acpi_cpufreq_driver);
 +
 +      free_percpu(acpi_perf_data);
 +
 +      return;
 +}
 +
 +module_param(acpi_pstate_strict, uint, 0644);
 +MODULE_PARM_DESC(acpi_pstate_strict,
 +      "value 0 or non-zero. non-zero -> strict ACPI checks are "
 +      "performed during frequency changes.");
 +
 +late_initcall(acpi_cpufreq_init);
 +module_exit(acpi_cpufreq_exit);
 +
 +MODULE_ALIAS("acpi");
index 66acd5039918350d16819cbf41457053f021fe56,0000000000000000000000000000000000000000..32f0bda3fc953a4568c194b13773f5a70ba22c69
mode 100644,000000..100644
--- /dev/null
@@@ -1,441 -1,0 +1,440 @@@
-       policy->governor = CPUFREQ_DEFAULT_GOVERNOR;
 +/*
 + * (C) 2004-2006  Sebastian Witt <se.witt@gmx.net>
 + *
 + *  Licensed under the terms of the GNU GPL License version 2.
 + *  Based upon reverse engineered information
 + *
 + *  BIG FAT DISCLAIMER: Work in progress code. Possibly *dangerous*
 + */
 +
 +#include <linux/kernel.h>
 +#include <linux/module.h>
 +#include <linux/moduleparam.h>
 +#include <linux/init.h>
 +#include <linux/cpufreq.h>
 +#include <linux/pci.h>
 +#include <linux/delay.h>
 +
 +#define NFORCE2_XTAL 25
 +#define NFORCE2_BOOTFSB 0x48
 +#define NFORCE2_PLLENABLE 0xa8
 +#define NFORCE2_PLLREG 0xa4
 +#define NFORCE2_PLLADR 0xa0
 +#define NFORCE2_PLL(mul, div) (0x100000 | (mul << 8) | div)
 +
 +#define NFORCE2_MIN_FSB 50
 +#define NFORCE2_SAFE_DISTANCE 50
 +
 +/* Delay in ms between FSB changes */
 +//#define NFORCE2_DELAY 10
 +
 +/* nforce2_chipset:
 + * FSB is changed using the chipset
 + */
 +static struct pci_dev *nforce2_chipset_dev;
 +
 +/* fid:
 + * multiplier * 10
 + */
 +static int fid = 0;
 +
 +/* min_fsb, max_fsb:
 + * minimum and maximum FSB (= FSB at boot time)
 + */
 +static int min_fsb = 0;
 +static int max_fsb = 0;
 +
 +MODULE_AUTHOR("Sebastian Witt <se.witt@gmx.net>");
 +MODULE_DESCRIPTION("nForce2 FSB changing cpufreq driver");
 +MODULE_LICENSE("GPL");
 +
 +module_param(fid, int, 0444);
 +module_param(min_fsb, int, 0444);
 +
 +MODULE_PARM_DESC(fid, "CPU multiplier to use (11.5 = 115)");
 +MODULE_PARM_DESC(min_fsb,
 +                 "Minimum FSB to use, if not defined: current FSB - 50");
 +
 +#define dprintk(msg...) cpufreq_debug_printk(CPUFREQ_DEBUG_DRIVER, "cpufreq-nforce2", msg)
 +
 +/**
 + * nforce2_calc_fsb - calculate FSB
 + * @pll: PLL value
 + *
 + *   Calculates FSB from PLL value
 + */
 +static int nforce2_calc_fsb(int pll)
 +{
 +      unsigned char mul, div;
 +
 +      mul = (pll >> 8) & 0xff;
 +      div = pll & 0xff;
 +
 +      if (div > 0)
 +              return NFORCE2_XTAL * mul / div;
 +
 +      return 0;
 +}
 +
 +/**
 + * nforce2_calc_pll - calculate PLL value
 + * @fsb: FSB
 + *
 + *   Calculate PLL value for given FSB
 + */
 +static int nforce2_calc_pll(unsigned int fsb)
 +{
 +      unsigned char xmul, xdiv;
 +      unsigned char mul = 0, div = 0;
 +      int tried = 0;
 +
 +      /* Try to calculate multiplier and divider up to 4 times */
 +      while (((mul == 0) || (div == 0)) && (tried <= 3)) {
 +              for (xdiv = 2; xdiv <= 0x80; xdiv++)
 +                      for (xmul = 1; xmul <= 0xfe; xmul++)
 +                              if (nforce2_calc_fsb(NFORCE2_PLL(xmul, xdiv)) ==
 +                                  fsb + tried) {
 +                                      mul = xmul;
 +                                      div = xdiv;
 +                              }
 +              tried++;
 +      }
 +
 +      if ((mul == 0) || (div == 0))
 +              return -1;
 +
 +      return NFORCE2_PLL(mul, div);
 +}
 +
 +/**
 + * nforce2_write_pll - write PLL value to chipset
 + * @pll: PLL value
 + *
 + *   Writes new FSB PLL value to chipset
 + */
 +static void nforce2_write_pll(int pll)
 +{
 +      int temp;
 +
 +      /* Set the pll addr. to 0x00 */
 +      pci_write_config_dword(nforce2_chipset_dev, NFORCE2_PLLADR, 0);
 +
 +      /* Now write the value in all 64 registers */
 +      for (temp = 0; temp <= 0x3f; temp++)
 +              pci_write_config_dword(nforce2_chipset_dev, NFORCE2_PLLREG, pll);
 +
 +      return;
 +}
 +
 +/**
 + * nforce2_fsb_read - Read FSB
 + *
 + *   Read FSB from chipset
 + *   If bootfsb != 0, return FSB at boot-time
 + */
 +static unsigned int nforce2_fsb_read(int bootfsb)
 +{
 +      struct pci_dev *nforce2_sub5;
 +      u32 fsb, temp = 0;
 +
 +      /* Get chipset boot FSB from subdevice 5 (FSB at boot-time) */
 +      nforce2_sub5 = pci_get_subsys(PCI_VENDOR_ID_NVIDIA,
 +                                              0x01EF,PCI_ANY_ID,PCI_ANY_ID,NULL);
 +      if (!nforce2_sub5)
 +              return 0;
 +
 +      pci_read_config_dword(nforce2_sub5, NFORCE2_BOOTFSB, &fsb);
 +      fsb /= 1000000;
 +
 +      /* Check if PLL register is already set */
 +      pci_read_config_byte(nforce2_chipset_dev,NFORCE2_PLLENABLE, (u8 *)&temp);
 +
 +      if(bootfsb || !temp)
 +              return fsb;
 +              
 +      /* Use PLL register FSB value */
 +      pci_read_config_dword(nforce2_chipset_dev,NFORCE2_PLLREG, &temp);
 +      fsb = nforce2_calc_fsb(temp);
 +
 +      return fsb;
 +}
 +
 +/**
 + * nforce2_set_fsb - set new FSB
 + * @fsb: New FSB
 + *
 + *   Sets new FSB
 + */
 +static int nforce2_set_fsb(unsigned int fsb)
 +{
 +      u32 temp = 0;
 +      unsigned int tfsb;
 +      int diff;
 +      int pll = 0;
 +
 +      if ((fsb > max_fsb) || (fsb < NFORCE2_MIN_FSB)) {
 +              printk(KERN_ERR "cpufreq: FSB %d is out of range!\n", fsb);
 +              return -EINVAL;
 +      }
 +
 +      tfsb = nforce2_fsb_read(0);
 +      if (!tfsb) {
 +              printk(KERN_ERR "cpufreq: Error while reading the FSB\n");
 +              return -EINVAL;
 +      }
 +
 +      /* First write? Then set actual value */
 +      pci_read_config_byte(nforce2_chipset_dev,NFORCE2_PLLENABLE, (u8 *)&temp);
 +      if (!temp) {
 +              pll = nforce2_calc_pll(tfsb);
 +
 +              if (pll < 0)
 +                      return -EINVAL;
 +
 +              nforce2_write_pll(pll);
 +      }
 +
 +      /* Enable write access */
 +      temp = 0x01;
 +      pci_write_config_byte(nforce2_chipset_dev, NFORCE2_PLLENABLE, (u8)temp);
 +
 +      diff = tfsb - fsb;
 +
 +      if (!diff)
 +              return 0;
 +
 +      while ((tfsb != fsb) && (tfsb <= max_fsb) && (tfsb >= min_fsb)) {
 +              if (diff < 0)
 +                      tfsb++;
 +              else
 +                      tfsb--;
 +
 +              /* Calculate the PLL reg. value */
 +              if ((pll = nforce2_calc_pll(tfsb)) == -1)
 +                      return -EINVAL;
 +
 +              nforce2_write_pll(pll);
 +#ifdef NFORCE2_DELAY
 +              mdelay(NFORCE2_DELAY);
 +#endif
 +      }
 +
 +      temp = 0x40;
 +      pci_write_config_byte(nforce2_chipset_dev, NFORCE2_PLLADR, (u8)temp);
 +
 +      return 0;
 +}
 +
 +/**
 + * nforce2_get - get the CPU frequency
 + * @cpu: CPU number
 + *
 + * Returns the CPU frequency
 + */
 +static unsigned int nforce2_get(unsigned int cpu)
 +{
 +      if (cpu)
 +              return 0;
 +      return nforce2_fsb_read(0) * fid * 100;
 +}
 +
 +/**
 + * nforce2_target - set a new CPUFreq policy
 + * @policy: new policy
 + * @target_freq: the target frequency
 + * @relation: how that frequency relates to achieved frequency (CPUFREQ_RELATION_L or CPUFREQ_RELATION_H)
 + *
 + * Sets a new CPUFreq policy.
 + */
 +static int nforce2_target(struct cpufreq_policy *policy,
 +                        unsigned int target_freq, unsigned int relation)
 +{
 +//        unsigned long         flags;
 +      struct cpufreq_freqs freqs;
 +      unsigned int target_fsb;
 +
 +      if ((target_freq > policy->max) || (target_freq < policy->min))
 +              return -EINVAL;
 +
 +      target_fsb = target_freq / (fid * 100);
 +
 +      freqs.old = nforce2_get(policy->cpu);
 +      freqs.new = target_fsb * fid * 100;
 +      freqs.cpu = 0;          /* Only one CPU on nForce2 plattforms */
 +
 +      if (freqs.old == freqs.new)
 +              return 0;
 +
 +      dprintk("Old CPU frequency %d kHz, new %d kHz\n",
 +             freqs.old, freqs.new);
 +
 +      cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
 +
 +      /* Disable IRQs */
 +      //local_irq_save(flags);
 +
 +      if (nforce2_set_fsb(target_fsb) < 0)
 +              printk(KERN_ERR "cpufreq: Changing FSB to %d failed\n",
 +                       target_fsb);
 +      else
 +              dprintk("Changed FSB successfully to %d\n",
 +                       target_fsb);
 +
 +      /* Enable IRQs */
 +      //local_irq_restore(flags);
 +
 +      cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
 +
 +      return 0;
 +}
 +
 +/**
 + * nforce2_verify - verifies a new CPUFreq policy
 + * @policy: new policy
 + */
 +static int nforce2_verify(struct cpufreq_policy *policy)
 +{
 +      unsigned int fsb_pol_max;
 +
 +      fsb_pol_max = policy->max / (fid * 100);
 +
 +      if (policy->min < (fsb_pol_max * fid * 100))
 +              policy->max = (fsb_pol_max + 1) * fid * 100;
 +
 +      cpufreq_verify_within_limits(policy,
 +                                     policy->cpuinfo.min_freq,
 +                                     policy->cpuinfo.max_freq);
 +      return 0;
 +}
 +
 +static int nforce2_cpu_init(struct cpufreq_policy *policy)
 +{
 +      unsigned int fsb;
 +      unsigned int rfid;
 +
 +      /* capability check */
 +      if (policy->cpu != 0)
 +              return -ENODEV;
 +
 +      /* Get current FSB */
 +      fsb = nforce2_fsb_read(0);
 +
 +      if (!fsb)
 +              return -EIO;
 +
 +      /* FIX: Get FID from CPU */
 +      if (!fid) {
 +              if (!cpu_khz) {
 +                      printk(KERN_WARNING
 +                             "cpufreq: cpu_khz not set, can't calculate multiplier!\n");
 +                      return -ENODEV;
 +              }
 +
 +              fid = cpu_khz / (fsb * 100);
 +              rfid = fid % 5;
 +
 +              if (rfid) {
 +                      if (rfid > 2)
 +                              fid += 5 - rfid;
 +                      else
 +                              fid -= rfid;
 +              }
 +      }
 +
 +      printk(KERN_INFO "cpufreq: FSB currently at %i MHz, FID %d.%d\n", fsb,
 +             fid / 10, fid % 10);
 +
 +      /* Set maximum FSB to FSB at boot time */
 +      max_fsb = nforce2_fsb_read(1);
 +
 +      if(!max_fsb)
 +              return -EIO;
 +
 +      if (!min_fsb)
 +              min_fsb = max_fsb - NFORCE2_SAFE_DISTANCE;
 +
 +      if (min_fsb < NFORCE2_MIN_FSB)
 +              min_fsb = NFORCE2_MIN_FSB;
 +
 +      /* cpuinfo and default policy values */
 +      policy->cpuinfo.min_freq = min_fsb * fid * 100;
 +      policy->cpuinfo.max_freq = max_fsb * fid * 100;
 +      policy->cpuinfo.transition_latency = CPUFREQ_ETERNAL;
 +      policy->cur = nforce2_get(policy->cpu);
 +      policy->min = policy->cpuinfo.min_freq;
 +      policy->max = policy->cpuinfo.max_freq;
 +
 +      return 0;
 +}
 +
 +static int nforce2_cpu_exit(struct cpufreq_policy *policy)
 +{
 +      return 0;
 +}
 +
 +static struct cpufreq_driver nforce2_driver = {
 +      .name = "nforce2",
 +      .verify = nforce2_verify,
 +      .target = nforce2_target,
 +      .get = nforce2_get,
 +      .init = nforce2_cpu_init,
 +      .exit = nforce2_cpu_exit,
 +      .owner = THIS_MODULE,
 +};
 +
 +/**
 + * nforce2_detect_chipset - detect the Southbridge which contains FSB PLL logic
 + *
 + * Detects nForce2 A2 and C1 stepping
 + *
 + */
 +static unsigned int nforce2_detect_chipset(void)
 +{
 +      nforce2_chipset_dev = pci_get_subsys(PCI_VENDOR_ID_NVIDIA,
 +                                      PCI_DEVICE_ID_NVIDIA_NFORCE2,
 +                                      PCI_ANY_ID, PCI_ANY_ID, NULL);
 +
 +      if (nforce2_chipset_dev == NULL)
 +              return -ENODEV;
 +
 +      printk(KERN_INFO "cpufreq: Detected nForce2 chipset revision %X\n",
 +             nforce2_chipset_dev->revision);
 +      printk(KERN_INFO
 +             "cpufreq: FSB changing is maybe unstable and can lead to crashes and data loss.\n");
 +
 +      return 0;
 +}
 +
 +/**
 + * nforce2_init - initializes the nForce2 CPUFreq driver
 + *
 + * Initializes the nForce2 FSB support. Returns -ENODEV on unsupported
 + * devices, -EINVAL on problems during initiatization, and zero on
 + * success.
 + */
 +static int __init nforce2_init(void)
 +{
 +      /* TODO: do we need to detect the processor? */
 +
 +      /* detect chipset */
 +      if (nforce2_detect_chipset()) {
 +              printk(KERN_ERR "cpufreq: No nForce2 chipset.\n");
 +              return -ENODEV;
 +      }
 +
 +      return cpufreq_register_driver(&nforce2_driver);
 +}
 +
 +/**
 + * nforce2_exit - unregisters cpufreq module
 + *
 + *   Unregisters nForce2 FSB change support.
 + */
 +static void __exit nforce2_exit(void)
 +{
 +      cpufreq_unregister_driver(&nforce2_driver);
 +}
 +
 +module_init(nforce2_init);
 +module_exit(nforce2_exit);
 +
index f43d98e11cc72bbe72bc66a5b9cbcaac0704e2d7,0000000000000000000000000000000000000000..c11baaf9f2b403f6624e1fa0d4fd5756491d8383
mode 100644,000000..100644
--- /dev/null
@@@ -1,334 -1,0 +1,333 @@@
-       policy->governor = CPUFREQ_DEFAULT_GOVERNOR;
 +/*
 + *  Based on documentation provided by Dave Jones. Thanks!
 + *
 + *  Licensed under the terms of the GNU GPL License version 2.
 + *
 + *  BIG FAT DISCLAIMER: Work in progress code. Possibly *dangerous*
 + */
 +
 +#include <linux/kernel.h>
 +#include <linux/module.h>
 +#include <linux/init.h>
 +#include <linux/cpufreq.h>
 +#include <linux/ioport.h>
 +#include <linux/slab.h>
 +
 +#include <asm/msr.h>
 +#include <asm/tsc.h>
 +#include <asm/timex.h>
 +#include <asm/io.h>
 +#include <asm/delay.h>
 +
 +#define EPS_BRAND_C7M 0
 +#define EPS_BRAND_C7  1
 +#define EPS_BRAND_EDEN        2
 +#define EPS_BRAND_C3  3
 +
 +struct eps_cpu_data {
 +      u32 fsb;
 +      struct cpufreq_frequency_table freq_table[];
 +};
 +
 +static struct eps_cpu_data *eps_cpu[NR_CPUS];
 +
 +
 +static unsigned int eps_get(unsigned int cpu)
 +{
 +      struct eps_cpu_data *centaur;
 +      u32 lo, hi;
 +
 +      if (cpu)
 +              return 0;
 +      centaur = eps_cpu[cpu];
 +      if (centaur == NULL)
 +              return 0;
 +
 +      /* Return current frequency */
 +      rdmsr(MSR_IA32_PERF_STATUS, lo, hi);
 +      return centaur->fsb * ((lo >> 8) & 0xff);
 +}
 +
 +static int eps_set_state(struct eps_cpu_data *centaur,
 +                       unsigned int cpu,
 +                       u32 dest_state)
 +{
 +      struct cpufreq_freqs freqs;
 +      u32 lo, hi;
 +      int err = 0;
 +      int i;
 +
 +      freqs.old = eps_get(cpu);
 +      freqs.new = centaur->fsb * ((dest_state >> 8) & 0xff);
 +      freqs.cpu = cpu;
 +      cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
 +
 +      /* Wait while CPU is busy */
 +      rdmsr(MSR_IA32_PERF_STATUS, lo, hi);
 +      i = 0;
 +      while (lo & ((1 << 16) | (1 << 17))) {
 +              udelay(16);
 +              rdmsr(MSR_IA32_PERF_STATUS, lo, hi);
 +              i++;
 +              if (unlikely(i > 64)) {
 +                      err = -ENODEV;
 +                      goto postchange;
 +              }
 +      }
 +      /* Set new multiplier and voltage */
 +      wrmsr(MSR_IA32_PERF_CTL, dest_state & 0xffff, 0);
 +      /* Wait until transition end */
 +      i = 0;
 +      do {
 +              udelay(16);
 +              rdmsr(MSR_IA32_PERF_STATUS, lo, hi);
 +              i++;
 +              if (unlikely(i > 64)) {
 +                      err = -ENODEV;
 +                      goto postchange;
 +              }
 +      } while (lo & ((1 << 16) | (1 << 17)));
 +
 +      /* Return current frequency */
 +postchange:
 +      rdmsr(MSR_IA32_PERF_STATUS, lo, hi);
 +      freqs.new = centaur->fsb * ((lo >> 8) & 0xff);
 +
 +      cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
 +      return err;
 +}
 +
 +static int eps_target(struct cpufreq_policy *policy,
 +                             unsigned int target_freq,
 +                             unsigned int relation)
 +{
 +      struct eps_cpu_data *centaur;
 +      unsigned int newstate = 0;
 +      unsigned int cpu = policy->cpu;
 +      unsigned int dest_state;
 +      int ret;
 +
 +      if (unlikely(eps_cpu[cpu] == NULL))
 +              return -ENODEV;
 +      centaur = eps_cpu[cpu];
 +
 +      if (unlikely(cpufreq_frequency_table_target(policy,
 +                      &eps_cpu[cpu]->freq_table[0],
 +                      target_freq,
 +                      relation,
 +                      &newstate))) {
 +              return -EINVAL;
 +      }
 +
 +      /* Make frequency transition */
 +      dest_state = centaur->freq_table[newstate].index & 0xffff;
 +      ret = eps_set_state(centaur, cpu, dest_state);
 +      if (ret)
 +              printk(KERN_ERR "eps: Timeout!\n");
 +      return ret;
 +}
 +
 +static int eps_verify(struct cpufreq_policy *policy)
 +{
 +      return cpufreq_frequency_table_verify(policy,
 +                      &eps_cpu[policy->cpu]->freq_table[0]);
 +}
 +
 +static int eps_cpu_init(struct cpufreq_policy *policy)
 +{
 +      unsigned int i;
 +      u32 lo, hi;
 +      u64 val;
 +      u8 current_multiplier, current_voltage;
 +      u8 max_multiplier, max_voltage;
 +      u8 min_multiplier, min_voltage;
 +      u8 brand;
 +      u32 fsb;
 +      struct eps_cpu_data *centaur;
 +      struct cpufreq_frequency_table *f_table;
 +      int k, step, voltage;
 +      int ret;
 +      int states;
 +
 +      if (policy->cpu != 0)
 +              return -ENODEV;
 +
 +      /* Check brand */
 +      printk("eps: Detected VIA ");
 +      rdmsr(0x1153, lo, hi);
 +      brand = (((lo >> 2) ^ lo) >> 18) & 3;
 +      switch(brand) {
 +      case EPS_BRAND_C7M:
 +              printk("C7-M\n");
 +              break;
 +      case EPS_BRAND_C7:
 +              printk("C7\n");
 +              break;
 +      case EPS_BRAND_EDEN:
 +              printk("Eden\n");
 +              break;
 +      case EPS_BRAND_C3:
 +              printk("C3\n");
 +              return -ENODEV;
 +              break;
 +      }
 +      /* Enable Enhanced PowerSaver */
 +      rdmsrl(MSR_IA32_MISC_ENABLE, val);
 +      if (!(val & 1 << 16)) {
 +              val |= 1 << 16;
 +              wrmsrl(MSR_IA32_MISC_ENABLE, val);
 +              /* Can be locked at 0 */
 +              rdmsrl(MSR_IA32_MISC_ENABLE, val);
 +              if (!(val & 1 << 16)) {
 +                      printk("eps: Can't enable Enhanced PowerSaver\n");
 +                      return -ENODEV;
 +              }
 +      }
 +
 +      /* Print voltage and multiplier */
 +      rdmsr(MSR_IA32_PERF_STATUS, lo, hi);
 +      current_voltage = lo & 0xff;
 +      printk("eps: Current voltage = %dmV\n", current_voltage * 16 + 700);
 +      current_multiplier = (lo >> 8) & 0xff;
 +      printk("eps: Current multiplier = %d\n", current_multiplier);
 +
 +      /* Print limits */
 +      max_voltage = hi & 0xff;
 +      printk("eps: Highest voltage = %dmV\n", max_voltage * 16 + 700);
 +      max_multiplier = (hi >> 8) & 0xff;
 +      printk("eps: Highest multiplier = %d\n", max_multiplier);
 +      min_voltage = (hi >> 16) & 0xff;
 +      printk("eps: Lowest voltage = %dmV\n", min_voltage * 16 + 700);
 +      min_multiplier = (hi >> 24) & 0xff;
 +      printk("eps: Lowest multiplier = %d\n", min_multiplier);
 +
 +      /* Sanity checks */
 +      if (current_multiplier == 0 || max_multiplier == 0
 +          || min_multiplier == 0)
 +              return -EINVAL;
 +      if (current_multiplier > max_multiplier
 +          || max_multiplier <= min_multiplier)
 +              return -EINVAL;
 +      if (current_voltage > 0x1c || max_voltage > 0x1c)
 +              return -EINVAL;
 +      if (max_voltage < min_voltage)
 +              return -EINVAL;
 +
 +      /* Calc FSB speed */
 +      fsb = cpu_khz / current_multiplier;
 +      /* Calc number of p-states supported */
 +      if (brand == EPS_BRAND_C7M)
 +              states = max_multiplier - min_multiplier + 1;
 +      else
 +              states = 2;
 +
 +      /* Allocate private data and frequency table for current cpu */
 +      centaur = kzalloc(sizeof(struct eps_cpu_data)
 +                  + (states + 1) * sizeof(struct cpufreq_frequency_table),
 +                  GFP_KERNEL);
 +      if (!centaur)
 +              return -ENOMEM;
 +      eps_cpu[0] = centaur;
 +
 +      /* Copy basic values */
 +      centaur->fsb = fsb;
 +
 +      /* Fill frequency and MSR value table */
 +      f_table = &centaur->freq_table[0];
 +      if (brand != EPS_BRAND_C7M) {
 +              f_table[0].frequency = fsb * min_multiplier;
 +              f_table[0].index = (min_multiplier << 8) | min_voltage;
 +              f_table[1].frequency = fsb * max_multiplier;
 +              f_table[1].index = (max_multiplier << 8) | max_voltage;
 +              f_table[2].frequency = CPUFREQ_TABLE_END;
 +      } else {
 +              k = 0;
 +              step = ((max_voltage - min_voltage) * 256)
 +                      / (max_multiplier - min_multiplier);
 +              for (i = min_multiplier; i <= max_multiplier; i++) {
 +                      voltage = (k * step) / 256 + min_voltage;
 +                      f_table[k].frequency = fsb * i;
 +                      f_table[k].index = (i << 8) | voltage;
 +                      k++;
 +              }
 +              f_table[k].frequency = CPUFREQ_TABLE_END;
 +      }
 +
 +      policy->cpuinfo.transition_latency = 140000; /* 844mV -> 700mV in ns */
 +      policy->cur = fsb * current_multiplier;
 +
 +      ret = cpufreq_frequency_table_cpuinfo(policy, &centaur->freq_table[0]);
 +      if (ret) {
 +              kfree(centaur);
 +              return ret;
 +      }
 +
 +      cpufreq_frequency_table_get_attr(&centaur->freq_table[0], policy->cpu);
 +      return 0;
 +}
 +
 +static int eps_cpu_exit(struct cpufreq_policy *policy)
 +{
 +      unsigned int cpu = policy->cpu;
 +      struct eps_cpu_data *centaur;
 +      u32 lo, hi;
 +
 +      if (eps_cpu[cpu] == NULL)
 +              return -ENODEV;
 +      centaur = eps_cpu[cpu];
 +
 +      /* Get max frequency */
 +      rdmsr(MSR_IA32_PERF_STATUS, lo, hi);
 +      /* Set max frequency */
 +      eps_set_state(centaur, cpu, hi & 0xffff);
 +      /* Bye */
 +      cpufreq_frequency_table_put_attr(policy->cpu);
 +      kfree(eps_cpu[cpu]);
 +      eps_cpu[cpu] = NULL;
 +      return 0;
 +}
 +
 +static struct freq_attr* eps_attr[] = {
 +      &cpufreq_freq_attr_scaling_available_freqs,
 +      NULL,
 +};
 +
 +static struct cpufreq_driver eps_driver = {
 +      .verify         = eps_verify,
 +      .target         = eps_target,
 +      .init           = eps_cpu_init,
 +      .exit           = eps_cpu_exit,
 +      .get            = eps_get,
 +      .name           = "e_powersaver",
 +      .owner          = THIS_MODULE,
 +      .attr           = eps_attr,
 +};
 +
 +static int __init eps_init(void)
 +{
 +      struct cpuinfo_x86 *c = cpu_data;
 +
 +      /* This driver will work only on Centaur C7 processors with
 +       * Enhanced SpeedStep/PowerSaver registers */
 +      if (c->x86_vendor != X86_VENDOR_CENTAUR
 +          || c->x86 != 6 || c->x86_model != 10)
 +              return -ENODEV;
 +      if (!cpu_has(c, X86_FEATURE_EST))
 +              return -ENODEV;
 +
 +      if (cpufreq_register_driver(&eps_driver))
 +              return -EINVAL;
 +      return 0;
 +}
 +
 +static void __exit eps_exit(void)
 +{
 +      cpufreq_unregister_driver(&eps_driver);
 +}
 +
 +MODULE_AUTHOR("Rafa³ Bilski <rafalbilski@interia.pl>");
 +MODULE_DESCRIPTION("Enhanced PowerSaver driver for VIA C7 CPU's.");
 +MODULE_LICENSE("GPL");
 +
 +module_init(eps_init);
 +module_exit(eps_exit);
index f317276afa7af179906f2035705bcf66a91d1824,0000000000000000000000000000000000000000..1e7ae7dafcf6a50921d1271347e2f522ad13ad2b
mode 100644,000000..100644
--- /dev/null
@@@ -1,309 -1,0 +1,308 @@@
-       policy->governor = CPUFREQ_DEFAULT_GOVERNOR;
 +/*
 + *    elanfreq:       cpufreq driver for the AMD ELAN family
 + *
 + *    (c) Copyright 2002 Robert Schwebel <r.schwebel@pengutronix.de>
 + *
 + *    Parts of this code are (c) Sven Geggus <sven@geggus.net>
 + *
 + *      All Rights Reserved.
 + *
 + *    This program is free software; you can redistribute it and/or
 + *    modify it under the terms of the GNU General Public License
 + *    as published by the Free Software Foundation; either version
 + *    2 of the License, or (at your option) any later version.
 + *
 + *    2002-02-13: - initial revision for 2.4.18-pre9 by Robert Schwebel
 + *
 + */
 +
 +#include <linux/kernel.h>
 +#include <linux/module.h>
 +#include <linux/init.h>
 +
 +#include <linux/slab.h>
 +#include <linux/delay.h>
 +#include <linux/cpufreq.h>
 +
 +#include <asm/msr.h>
 +#include <asm/timex.h>
 +#include <asm/io.h>
 +
 +#define REG_CSCIR 0x22                /* Chip Setup and Control Index Register    */
 +#define REG_CSCDR 0x23                /* Chip Setup and Control Data  Register    */
 +
 +/* Module parameter */
 +static int max_freq;
 +
 +struct s_elan_multiplier {
 +      int clock;              /* frequency in kHz                         */
 +      int val40h;             /* PMU Force Mode register                  */
 +      int val80h;             /* CPU Clock Speed Register                 */
 +};
 +
 +/*
 + * It is important that the frequencies
 + * are listed in ascending order here!
 + */
 +struct s_elan_multiplier elan_multiplier[] = {
 +      {1000,  0x02,   0x18},
 +      {2000,  0x02,   0x10},
 +      {4000,  0x02,   0x08},
 +      {8000,  0x00,   0x00},
 +      {16000, 0x00,   0x02},
 +      {33000, 0x00,   0x04},
 +      {66000, 0x01,   0x04},
 +      {99000, 0x01,   0x05}
 +};
 +
 +static struct cpufreq_frequency_table elanfreq_table[] = {
 +      {0,     1000},
 +      {1,     2000},
 +      {2,     4000},
 +      {3,     8000},
 +      {4,     16000},
 +      {5,     33000},
 +      {6,     66000},
 +      {7,     99000},
 +      {0,     CPUFREQ_TABLE_END},
 +};
 +
 +
 +/**
 + *    elanfreq_get_cpu_frequency: determine current cpu speed
 + *
 + *    Finds out at which frequency the CPU of the Elan SOC runs
 + *    at the moment. Frequencies from 1 to 33 MHz are generated
 + *    the normal way, 66 and 99 MHz are called "Hyperspeed Mode"
 + *    and have the rest of the chip running with 33 MHz.
 + */
 +
 +static unsigned int elanfreq_get_cpu_frequency(unsigned int cpu)
 +{
 +      u8 clockspeed_reg;    /* Clock Speed Register */
 +
 +      local_irq_disable();
 +      outb_p(0x80,REG_CSCIR);
 +      clockspeed_reg = inb_p(REG_CSCDR);
 +      local_irq_enable();
 +
 +      if ((clockspeed_reg & 0xE0) == 0xE0)
 +              return 0;
 +
 +      /* Are we in CPU clock multiplied mode (66/99 MHz)? */
 +      if ((clockspeed_reg & 0xE0) == 0xC0) {
 +              if ((clockspeed_reg & 0x01) == 0)
 +                      return 66000;
 +              else
 +                      return 99000;
 +      }
 +
 +      /* 33 MHz is not 32 MHz... */
 +      if ((clockspeed_reg & 0xE0)==0xA0)
 +              return 33000;
 +
 +      return ((1<<((clockspeed_reg & 0xE0) >> 5)) * 1000);
 +}
 +
 +
 +/**
 + *    elanfreq_set_cpu_frequency: Change the CPU core frequency
 + *    @cpu: cpu number
 + *    @freq: frequency in kHz
 + *
 + *    This function takes a frequency value and changes the CPU frequency
 + *    according to this. Note that the frequency has to be checked by
 + *    elanfreq_validatespeed() for correctness!
 + *
 + *    There is no return value.
 + */
 +
 +static void elanfreq_set_cpu_state (unsigned int state)
 +{
 +      struct cpufreq_freqs    freqs;
 +
 +      freqs.old = elanfreq_get_cpu_frequency(0);
 +      freqs.new = elan_multiplier[state].clock;
 +      freqs.cpu = 0; /* elanfreq.c is UP only driver */
 +
 +      cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
 +
 +      printk(KERN_INFO "elanfreq: attempting to set frequency to %i kHz\n",
 +                      elan_multiplier[state].clock);
 +
 +
 +      /*
 +       * Access to the Elan's internal registers is indexed via
 +       * 0x22: Chip Setup & Control Register Index Register (CSCI)
 +       * 0x23: Chip Setup & Control Register Data  Register (CSCD)
 +       *
 +       */
 +
 +      /*
 +       * 0x40 is the Power Management Unit's Force Mode Register.
 +       * Bit 6 enables Hyperspeed Mode (66/100 MHz core frequency)
 +       */
 +
 +      local_irq_disable();
 +      outb_p(0x40,REG_CSCIR);         /* Disable hyperspeed mode */
 +      outb_p(0x00,REG_CSCDR);
 +      local_irq_enable();             /* wait till internal pipelines and */
 +      udelay(1000);                   /* buffers have cleaned up          */
 +
 +      local_irq_disable();
 +
 +      /* now, set the CPU clock speed register (0x80) */
 +      outb_p(0x80,REG_CSCIR);
 +      outb_p(elan_multiplier[state].val80h,REG_CSCDR);
 +
 +      /* now, the hyperspeed bit in PMU Force Mode Register (0x40) */
 +      outb_p(0x40,REG_CSCIR);
 +      outb_p(elan_multiplier[state].val40h,REG_CSCDR);
 +      udelay(10000);
 +      local_irq_enable();
 +
 +      cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
 +};
 +
 +
 +/**
 + *    elanfreq_validatespeed: test if frequency range is valid
 + *    @policy: the policy to validate
 + *
 + *    This function checks if a given frequency range in kHz is valid
 + *    for the hardware supported by the driver.
 + */
 +
 +static int elanfreq_verify (struct cpufreq_policy *policy)
 +{
 +      return cpufreq_frequency_table_verify(policy, &elanfreq_table[0]);
 +}
 +
 +static int elanfreq_target (struct cpufreq_policy *policy,
 +                          unsigned int target_freq,
 +                          unsigned int relation)
 +{
 +      unsigned int newstate = 0;
 +
 +      if (cpufreq_frequency_table_target(policy, &elanfreq_table[0], target_freq, relation, &newstate))
 +              return -EINVAL;
 +
 +      elanfreq_set_cpu_state(newstate);
 +
 +      return 0;
 +}
 +
 +
 +/*
 + *    Module init and exit code
 + */
 +
 +static int elanfreq_cpu_init(struct cpufreq_policy *policy)
 +{
 +      struct cpuinfo_x86 *c = cpu_data;
 +      unsigned int i;
 +      int result;
 +
 +      /* 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(0);
 +
 +      /* 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->cpuinfo.transition_latency = CPUFREQ_ETERNAL;
 +      policy->cur = elanfreq_get_cpu_frequency(0);
 +
 +      result = cpufreq_frequency_table_cpuinfo(policy, elanfreq_table);
 +      if (result)
 +              return (result);
 +
 +      cpufreq_frequency_table_get_attr(elanfreq_table, policy->cpu);
 +      return 0;
 +}
 +
 +
 +static int elanfreq_cpu_exit(struct cpufreq_policy *policy)
 +{
 +      cpufreq_frequency_table_put_attr(policy->cpu);
 +      return 0;
 +}
 +
 +
 +#ifndef MODULE
 +/**
 + * elanfreq_setup - elanfreq command line parameter parsing
 + *
 + * elanfreq command line parameter.  Use:
 + *  elanfreq=66000
 + * to set the maximum CPU frequency to 66 MHz. Note that in
 + * case you do not give this boot parameter, the maximum
 + * frequency will fall back to _current_ CPU frequency which
 + * might be lower. If you build this as a module, use the
 + * max_freq module parameter instead.
 + */
 +static int __init elanfreq_setup(char *str)
 +{
 +      max_freq = simple_strtoul(str, &str, 0);
 +      printk(KERN_WARNING "You're using the deprecated elanfreq command line option. Use elanfreq.max_freq instead, please!\n");
 +      return 1;
 +}
 +__setup("elanfreq=", elanfreq_setup);
 +#endif
 +
 +
 +static struct freq_attr* elanfreq_attr[] = {
 +      &cpufreq_freq_attr_scaling_available_freqs,
 +      NULL,
 +};
 +
 +
 +static struct cpufreq_driver elanfreq_driver = {
 +      .get            = elanfreq_get_cpu_frequency,
 +      .verify         = elanfreq_verify,
 +      .target         = elanfreq_target,
 +      .init           = elanfreq_cpu_init,
 +      .exit           = elanfreq_cpu_exit,
 +      .name           = "elanfreq",
 +      .owner          = THIS_MODULE,
 +      .attr           = elanfreq_attr,
 +};
 +
 +
 +static int __init elanfreq_init(void)
 +{
 +      struct cpuinfo_x86 *c = cpu_data;
 +
 +      /* Test if we have the right hardware */
 +      if ((c->x86_vendor != X86_VENDOR_AMD) ||
 +              (c->x86 != 4) || (c->x86_model!=10)) {
 +              printk(KERN_INFO "elanfreq: error: no Elan processor found!\n");
 +                return -ENODEV;
 +      }
 +      return cpufreq_register_driver(&elanfreq_driver);
 +}
 +
 +
 +static void __exit elanfreq_exit(void)
 +{
 +      cpufreq_unregister_driver(&elanfreq_driver);
 +}
 +
 +
 +module_param (max_freq, int, 0444);
 +
 +MODULE_LICENSE("GPL");
 +MODULE_AUTHOR("Robert Schwebel <r.schwebel@pengutronix.de>, Sven Geggus <sven@geggus.net>");
 +MODULE_DESCRIPTION("cpufreq driver for AMD's Elan CPUs");
 +
 +module_init(elanfreq_init);
 +module_exit(elanfreq_exit);
index 461dabc4e4959d23a7b5411d109b1a239a8c3872,0000000000000000000000000000000000000000..ed2bda127c44b8734fbc88af3bc394ed95290ff9
mode 100644,000000..100644
--- /dev/null
@@@ -1,495 -1,0 +1,494 @@@
-       policy->governor = CPUFREQ_DEFAULT_GOVERNOR;
 +/*
 + *    Cyrix MediaGX and NatSemi Geode Suspend Modulation
 + *    (C) 2002 Zwane Mwaikambo <zwane@commfireservices.com>
 + *    (C) 2002 Hiroshi Miura   <miura@da-cha.org>
 + *    All Rights Reserved
 + *
 + *    This program is free software; you can redistribute it and/or
 + *      modify it under the terms of the GNU General Public License
 + *      version 2 as published by the Free Software Foundation
 + *
 + *      The author(s) of this software shall not be held liable for damages
 + *      of any nature resulting due to the use of this software. This
 + *      software is provided AS-IS with no warranties.
 + *
 + * Theoritical note:
 + *
 + *    (see Geode(tm) CS5530 manual (rev.4.1) page.56)
 + *
 + *    CPU frequency control on NatSemi Geode GX1/GXLV processor and CS55x0
 + *    are based on Suspend Moduration.
 + *
 + *    Suspend Modulation works by asserting and de-asserting the SUSP# pin
 + *    to CPU(GX1/GXLV) for configurable durations. When asserting SUSP#
 + *    the CPU enters an idle state. GX1 stops its core clock when SUSP# is
 + *    asserted then power consumption is reduced.
 + *
 + *    Suspend Modulation's OFF/ON duration are configurable
 + *    with 'Suspend Modulation OFF Count Register'
 + *    and 'Suspend Modulation ON Count Register'.
 + *    These registers are 8bit counters that represent the number of
 + *    32us intervals which the SUSP# pin is asserted(ON)/de-asserted(OFF)
 + *    to the processor.
 + *
 + *    These counters define a ratio which is the effective frequency
 + *    of operation of the system.
 + *
 + *                           OFF Count
 + *    F_eff = Fgx * ----------------------
 + *                    OFF Count + ON Count
 + *
 + *    0 <= On Count, Off Count <= 255
 + *
 + *    From these limits, we can get register values
 + *
 + *    off_duration + on_duration <= MAX_DURATION
 + *    on_duration = off_duration * (stock_freq - freq) / freq
 + *
 + *      off_duration  =  (freq * DURATION) / stock_freq
 + *      on_duration = DURATION - off_duration
 + *
 + *
 + *---------------------------------------------------------------------------
 + *
 + * ChangeLog:
 + *    Dec. 12, 2003   Hiroshi Miura <miura@da-cha.org>
 + *            - fix on/off register mistake
 + *            - fix cpu_khz calc when it stops cpu modulation.
 + *
 + *    Dec. 11, 2002   Hiroshi Miura <miura@da-cha.org>
 + *            - rewrite for Cyrix MediaGX Cx5510/5520 and
 + *              NatSemi Geode Cs5530(A).
 + *
 + *    Jul. ??, 2002  Zwane Mwaikambo <zwane@commfireservices.com>
 + *            - cs5530_mod patch for 2.4.19-rc1.
 + *
 + *---------------------------------------------------------------------------
 + *
 + * Todo
 + *    Test on machines with 5510, 5530, 5530A
 + */
 +
 +/************************************************************************
 + *                    Suspend Modulation - Definitions                *
 + ************************************************************************/
 +
 +#include <linux/kernel.h>
 +#include <linux/module.h>
 +#include <linux/init.h>
 +#include <linux/smp.h>
 +#include <linux/cpufreq.h>
 +#include <linux/pci.h>
 +#include <asm/processor-cyrix.h>
 +#include <asm/errno.h>
 +
 +/* PCI config registers, all at F0 */
 +#define PCI_PMER1     0x80    /* power management enable register 1 */
 +#define PCI_PMER2     0x81    /* power management enable register 2 */
 +#define PCI_PMER3     0x82    /* power management enable register 3 */
 +#define PCI_IRQTC     0x8c    /* irq speedup timer counter register:typical 2 to 4ms */
 +#define PCI_VIDTC     0x8d    /* video speedup timer counter register: typical 50 to 100ms */
 +#define PCI_MODOFF    0x94    /* suspend modulation OFF counter register, 1 = 32us */
 +#define PCI_MODON     0x95    /* suspend modulation ON counter register */
 +#define PCI_SUSCFG    0x96    /* suspend configuration register */
 +
 +/* PMER1 bits */
 +#define GPM           (1<<0)  /* global power management */
 +#define GIT           (1<<1)  /* globally enable PM device idle timers */
 +#define GTR           (1<<2)  /* globally enable IO traps */
 +#define IRQ_SPDUP     (1<<3)  /* disable clock throttle during interrupt handling */
 +#define VID_SPDUP     (1<<4)  /* disable clock throttle during vga video handling */
 +
 +/* SUSCFG bits */
 +#define SUSMOD                (1<<0)  /* enable/disable suspend modulation */
 +/* the belows support only with cs5530 (after rev.1.2)/cs5530A */
 +#define SMISPDUP      (1<<1)  /* select how SMI re-enable suspend modulation: */
 +                              /* IRQTC timer or read SMI speedup disable reg.(F1BAR[08-09h]) */
 +#define SUSCFG                (1<<2)  /* enable powering down a GXLV processor. "Special 3Volt Suspend" mode */
 +/* the belows support only with cs5530A */
 +#define PWRSVE_ISA    (1<<3)  /* stop ISA clock  */
 +#define PWRSVE                (1<<4)  /* active idle */
 +
 +struct gxfreq_params {
 +      u8 on_duration;
 +      u8 off_duration;
 +      u8 pci_suscfg;
 +      u8 pci_pmer1;
 +      u8 pci_pmer2;
 +      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_param (pci_busclk, int, 0444);
 +
 +/* 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_param (max_duration, int, 0444);
 +
 +/* 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
 +
 +
 +#define dprintk(msg...) cpufreq_debug_printk(CPUFREQ_DEBUG_DRIVER, "gx-suspmod", msg)
 +
 +/**
 + * 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)) {
 +              dprintk("error: no MediaGX/Geode processor found!\n");
 +              return NULL;
 +      }
 +
 +      /* detect which companion chip is used */
 +      while ((gx_pci = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, gx_pci)) != NULL) {
 +              if ((pci_match_id(gx_chipset_tbl, gx_pci)) != NULL)
 +                      return gx_pci;
 +      }
 +
 +      dprintk("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 unsigned int gx_get_cpuspeed(unsigned int cpu)
 +{
 +      if ((gx_params->pci_suscfg & SUSMOD) == 0)
 +              return stock_freq;
 +
 +      return (stock_freq * gx_params->off_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;
 +
 +      *off_duration=1;
 +      *on_duration=0;
 +
 +      for (i=max_duration; i>0; i--) {
 +              tmp_off = ((khz * i) / stock_freq) & 0xff;
 +              tmp_on = i - tmp_off;
 +              tmp_freq = (stock_freq * tmp_off) / 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(0);
 +
 +      new_khz = gx_validate_speed(khz, &gx_params->on_duration, &gx_params->off_duration);
 +
 +      freqs.new = new_khz;
 +
 +      cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
 +      local_irq_save(flags);
 +
 +      if (new_khz != stock_freq) {  /* if new khz == 100% of CPU speed, it is special case */
 +              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->cs55x0->revision < 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;
 +              }
 +      } else {
 +              suscfg = gx_params->pci_suscfg & ~(SUSMOD);
 +              gx_params->off_duration = 0;
 +              gx_params->on_duration = 0;
 +              dprintk("suspend modulation disabled: cpu runs 100 percent speed.\n");
 +      }
 +
 +      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)
 +{
 +      unsigned 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(0);
 +
 +      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->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 = {
 +      .get            = gx_get_cpuspeed,
 +      .verify         = cpufreq_gx_verify,
 +      .target         = cpufreq_gx_target,
 +      .init           = cpufreq_gx_cpu_init,
 +      .name           = "gx-suspmod",
 +      .owner          = THIS_MODULE,
 +};
 +
 +static int __init cpufreq_gx_init(void)
 +{
 +      int ret;
 +      struct gxfreq_params *params;
 +      struct pci_dev *gx_pci;
 +
 +      /* 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 = kzalloc(sizeof(struct gxfreq_params), GFP_KERNEL);
 +      if (params == NULL)
 +              return -ENOMEM;
 +
 +      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));
 +
 +      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);
 +      pci_dev_put(gx_params->cs55x0);
 +      kfree(gx_params);
 +}
 +
 +MODULE_AUTHOR ("Hiroshi Miura <miura@da-cha.org>");
 +MODULE_DESCRIPTION ("Cpufreq driver for Cyrix MediaGX and NatSemi Geode");
 +MODULE_LICENSE ("GPL");
 +
 +module_init(cpufreq_gx_init);
 +module_exit(cpufreq_gx_exit);
 +
index f0cce3c2dc3a4fe33916f2c1ff5c34bae77237e6,0000000000000000000000000000000000000000..5045f5d583c81954fa7123df8697188a39012583
mode 100644,000000..100644
--- /dev/null
@@@ -1,1024 -1,0 +1,1027 @@@
-       policy->governor = CPUFREQ_DEFAULT_GOVERNOR;
 +/*
 + *  (C) 2001-2004  Dave Jones. <davej@codemonkey.org.uk>
 + *  (C) 2002  Padraig Brady. <padraig@antefacto.com>
 + *
 + *  Licensed under the terms of the GNU GPL License version 2.
 + *  Based upon datasheets & sample CPUs kindly provided by VIA.
 + *
 + *  VIA have currently 3 different versions of Longhaul.
 + *  Version 1 (Longhaul) uses the BCR2 MSR at 0x1147.
 + *   It is present only in Samuel 1 (C5A), Samuel 2 (C5B) stepping 0.
 + *  Version 2 of longhaul is backward compatible with v1, but adds
 + *   LONGHAUL MSR for purpose of both frequency and voltage scaling.
 + *   Present in Samuel 2 (steppings 1-7 only) (C5B), and Ezra (C5C).
 + *  Version 3 of longhaul got renamed to Powersaver and redesigned
 + *   to use only the POWERSAVER MSR at 0x110a.
 + *   It is present in Ezra-T (C5M), Nehemiah (C5X) and above.
 + *   It's pretty much the same feature wise to longhaul v2, though
 + *   there is provision for scaling FSB too, but this doesn't work
 + *   too well in practice so we don't even try to use this.
 + *
 + *  BIG FAT DISCLAIMER: Work in progress code. Possibly *dangerous*
 + */
 +
 +#include <linux/kernel.h>
 +#include <linux/module.h>
 +#include <linux/moduleparam.h>
 +#include <linux/init.h>
 +#include <linux/cpufreq.h>
 +#include <linux/pci.h>
 +#include <linux/slab.h>
 +#include <linux/string.h>
 +#include <linux/delay.h>
 +
 +#include <asm/msr.h>
 +#include <asm/timex.h>
 +#include <asm/io.h>
 +#include <asm/acpi.h>
 +#include <linux/acpi.h>
 +#include <acpi/processor.h>
 +
 +#include "longhaul.h"
 +
 +#define PFX "longhaul: "
 +
 +#define TYPE_LONGHAUL_V1      1
 +#define TYPE_LONGHAUL_V2      2
 +#define TYPE_POWERSAVER               3
 +
 +#define       CPU_SAMUEL      1
 +#define       CPU_SAMUEL2     2
 +#define       CPU_EZRA        3
 +#define       CPU_EZRA_T      4
 +#define       CPU_NEHEMIAH    5
 +#define       CPU_NEHEMIAH_C  6
 +
 +/* Flags */
 +#define USE_ACPI_C3           (1 << 1)
 +#define USE_NORTHBRIDGE               (1 << 2)
 +
 +static int cpu_model;
 +static unsigned int numscales=16;
 +static unsigned int fsb;
 +
 +static const struct mV_pos *vrm_mV_table;
 +static const unsigned char *mV_vrm_table;
 +
 +static unsigned int highest_speed, lowest_speed; /* kHz */
 +static unsigned int minmult, maxmult;
 +static int can_scale_voltage;
 +static struct acpi_processor *pr = NULL;
 +static struct acpi_processor_cx *cx = NULL;
 +static u32 acpi_regs_addr;
 +static u8 longhaul_flags;
 +static unsigned int longhaul_index;
 +
 +/* Module parameters */
 +static int scale_voltage;
 +static int disable_acpi_c3;
 +static int revid_errata;
 +
 +#define dprintk(msg...) cpufreq_debug_printk(CPUFREQ_DEBUG_DRIVER, "longhaul", msg)
 +
 +
 +/* Clock ratios multiplied by 10 */
 +static int clock_ratio[32];
 +static int eblcr_table[32];
 +static int longhaul_version;
 +static struct cpufreq_frequency_table *longhaul_table;
 +
 +#ifdef CONFIG_CPU_FREQ_DEBUG
 +static char speedbuffer[8];
 +
 +static char *print_speed(int speed)
 +{
 +      if (speed < 1000) {
 +              snprintf(speedbuffer, sizeof(speedbuffer),"%dMHz", speed);
 +              return speedbuffer;
 +      }
 +
 +      if (speed%1000 == 0)
 +              snprintf(speedbuffer, sizeof(speedbuffer),
 +                      "%dGHz", speed/1000);
 +      else
 +              snprintf(speedbuffer, sizeof(speedbuffer),
 +                      "%d.%dGHz", speed/1000, (speed%1000)/100);
 +
 +      return speedbuffer;
 +}
 +#endif
 +
 +
 +static unsigned int calc_speed(int mult)
 +{
 +      int khz;
 +      khz = (mult/10)*fsb;
 +      if (mult%10)
 +              khz += fsb/2;
 +      khz *= 1000;
 +      return khz;
 +}
 +
 +
 +static int longhaul_get_cpu_mult(void)
 +{
 +      unsigned long invalue=0,lo, hi;
 +
 +      rdmsr (MSR_IA32_EBL_CR_POWERON, lo, hi);
 +      invalue = (lo & (1<<22|1<<23|1<<24|1<<25)) >>22;
 +      if (longhaul_version==TYPE_LONGHAUL_V2 || longhaul_version==TYPE_POWERSAVER) {
 +              if (lo & (1<<27))
 +                      invalue+=16;
 +      }
 +      return eblcr_table[invalue];
 +}
 +
 +/* For processor with BCR2 MSR */
 +
 +static void do_longhaul1(unsigned int clock_ratio_index)
 +{
 +      union msr_bcr2 bcr2;
 +
 +      rdmsrl(MSR_VIA_BCR2, bcr2.val);
 +      /* Enable software clock multiplier */
 +      bcr2.bits.ESOFTBF = 1;
 +      bcr2.bits.CLOCKMUL = clock_ratio_index & 0xff;
 +
 +      /* Sync to timer tick */
 +      safe_halt();
 +      /* Change frequency on next halt or sleep */
 +      wrmsrl(MSR_VIA_BCR2, bcr2.val);
 +      /* Invoke transition */
 +      ACPI_FLUSH_CPU_CACHE();
 +      halt();
 +
 +      /* Disable software clock multiplier */
 +      local_irq_disable();
 +      rdmsrl(MSR_VIA_BCR2, bcr2.val);
 +      bcr2.bits.ESOFTBF = 0;
 +      wrmsrl(MSR_VIA_BCR2, bcr2.val);
 +}
 +
 +/* For processor with Longhaul MSR */
 +
 +static void do_powersaver(int cx_address, unsigned int clock_ratio_index,
 +                        unsigned int dir)
 +{
 +      union msr_longhaul longhaul;
 +      u32 t;
 +
 +      rdmsrl(MSR_VIA_LONGHAUL, longhaul.val);
 +      /* Setup new frequency */
 +      if (!revid_errata)
 +              longhaul.bits.RevisionKey = longhaul.bits.RevisionID;
 +      else
 +              longhaul.bits.RevisionKey = 0;
 +      longhaul.bits.SoftBusRatio = clock_ratio_index & 0xf;
 +      longhaul.bits.SoftBusRatio4 = (clock_ratio_index & 0x10) >> 4;
 +      /* Setup new voltage */
 +      if (can_scale_voltage)
 +              longhaul.bits.SoftVID = (clock_ratio_index >> 8) & 0x1f;
 +      /* Sync to timer tick */
 +      safe_halt();
 +      /* Raise voltage if necessary */
 +      if (can_scale_voltage && dir) {
 +              longhaul.bits.EnableSoftVID = 1;
 +              wrmsrl(MSR_VIA_LONGHAUL, longhaul.val);
 +              /* Change voltage */
 +              if (!cx_address) {
 +                      ACPI_FLUSH_CPU_CACHE();
 +                      halt();
 +              } else {
 +                      ACPI_FLUSH_CPU_CACHE();
 +                      /* Invoke C3 */
 +                      inb(cx_address);
 +                      /* Dummy op - must do something useless after P_LVL3
 +                       * read */
 +                      t = inl(acpi_gbl_FADT.xpm_timer_block.address);
 +              }
 +              longhaul.bits.EnableSoftVID = 0;
 +              wrmsrl(MSR_VIA_LONGHAUL, longhaul.val);
 +      }
 +
 +      /* Change frequency on next halt or sleep */
 +      longhaul.bits.EnableSoftBusRatio = 1;
 +      wrmsrl(MSR_VIA_LONGHAUL, longhaul.val);
 +      if (!cx_address) {
 +              ACPI_FLUSH_CPU_CACHE();
 +              halt();
 +      } else {
 +              ACPI_FLUSH_CPU_CACHE();
 +              /* Invoke C3 */
 +              inb(cx_address);
 +              /* Dummy op - must do something useless after P_LVL3 read */
 +              t = inl(acpi_gbl_FADT.xpm_timer_block.address);
 +      }
 +      /* Disable bus ratio bit */
 +      longhaul.bits.EnableSoftBusRatio = 0;
 +      wrmsrl(MSR_VIA_LONGHAUL, longhaul.val);
 +
 +      /* Reduce voltage if necessary */
 +      if (can_scale_voltage && !dir) {
 +              longhaul.bits.EnableSoftVID = 1;
 +              wrmsrl(MSR_VIA_LONGHAUL, longhaul.val);
 +              /* Change voltage */
 +              if (!cx_address) {
 +                      ACPI_FLUSH_CPU_CACHE();
 +                      halt();
 +              } else {
 +                      ACPI_FLUSH_CPU_CACHE();
 +                      /* Invoke C3 */
 +                      inb(cx_address);
 +                      /* Dummy op - must do something useless after P_LVL3
 +                       * read */
 +                      t = inl(acpi_gbl_FADT.xpm_timer_block.address);
 +              }
 +              longhaul.bits.EnableSoftVID = 0;
 +              wrmsrl(MSR_VIA_LONGHAUL, longhaul.val);
 +      }
 +}
 +
 +/**
 + * longhaul_set_cpu_frequency()
 + * @clock_ratio_index : bitpattern of the new multiplier.
 + *
 + * Sets a new clock ratio.
 + */
 +
 +static void longhaul_setstate(unsigned int table_index)
 +{
 +      unsigned int clock_ratio_index;
 +      int speed, mult;
 +      struct cpufreq_freqs freqs;
 +      unsigned long flags;
 +      unsigned int pic1_mask, pic2_mask;
 +      u16 bm_status = 0;
 +      u32 bm_timeout = 1000;
 +      unsigned int dir = 0;
 +
 +      clock_ratio_index = longhaul_table[table_index].index;
 +      /* Safety precautions */
 +      mult = clock_ratio[clock_ratio_index & 0x1f];
 +      if (mult == -1)
 +              return;
 +      speed = calc_speed(mult);
 +      if ((speed > highest_speed) || (speed < lowest_speed))
 +              return;
 +      /* Voltage transition before frequency transition? */
 +      if (can_scale_voltage && longhaul_index < table_index)
 +              dir = 1;
 +
 +      freqs.old = calc_speed(longhaul_get_cpu_mult());
 +      freqs.new = speed;
 +      freqs.cpu = 0; /* longhaul.c is UP only driver */
 +
 +      cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
 +
 +      dprintk ("Setting to FSB:%dMHz Mult:%d.%dx (%s)\n",
 +                      fsb, mult/10, mult%10, print_speed(speed/1000));
 +retry_loop:
 +      preempt_disable();
 +      local_irq_save(flags);
 +
 +      pic2_mask = inb(0xA1);
 +      pic1_mask = inb(0x21);  /* works on C3. save mask. */
 +      outb(0xFF,0xA1);        /* Overkill */
 +      outb(0xFE,0x21);        /* TMR0 only */
 +
 +      /* Wait while PCI bus is busy. */
 +      if (acpi_regs_addr && (longhaul_flags & USE_NORTHBRIDGE
 +          || ((pr != NULL) && pr->flags.bm_control))) {
 +              bm_status = inw(acpi_regs_addr);
 +              bm_status &= 1 << 4;
 +              while (bm_status && bm_timeout) {
 +                      outw(1 << 4, acpi_regs_addr);
 +                      bm_timeout--;
 +                      bm_status = inw(acpi_regs_addr);
 +                      bm_status &= 1 << 4;
 +              }
 +      }
 +
 +      if (longhaul_flags & USE_NORTHBRIDGE) {
 +              /* Disable AGP and PCI arbiters */
 +              outb(3, 0x22);
 +      } else if ((pr != NULL) && pr->flags.bm_control) {
 +              /* Disable bus master arbitration */
 +              acpi_set_register(ACPI_BITREG_ARB_DISABLE, 1);
 +      }
 +      switch (longhaul_version) {
 +
 +      /*
 +       * Longhaul v1. (Samuel[C5A] and Samuel2 stepping 0[C5B])
 +       * Software controlled multipliers only.
 +       */
 +      case TYPE_LONGHAUL_V1:
 +              do_longhaul1(clock_ratio_index);
 +              break;
 +
 +      /*
 +       * Longhaul v2 appears in Samuel2 Steppings 1->7 [C5B] and Ezra [C5C]
 +       *
 +       * Longhaul v3 (aka Powersaver). (Ezra-T [C5M] & Nehemiah [C5N])
 +       * Nehemiah can do FSB scaling too, but this has never been proven
 +       * to work in practice.
 +       */
 +      case TYPE_LONGHAUL_V2:
 +      case TYPE_POWERSAVER:
 +              if (longhaul_flags & USE_ACPI_C3) {
 +                      /* Don't allow wakeup */
 +                      acpi_set_register(ACPI_BITREG_BUS_MASTER_RLD, 0);
 +                      do_powersaver(cx->address, clock_ratio_index, dir);
 +              } else {
 +                      do_powersaver(0, clock_ratio_index, dir);
 +              }
 +              break;
 +      }
 +
 +      if (longhaul_flags & USE_NORTHBRIDGE) {
 +              /* Enable arbiters */
 +              outb(0, 0x22);
 +      } else if ((pr != NULL) && pr->flags.bm_control) {
 +              /* Enable bus master arbitration */
 +              acpi_set_register(ACPI_BITREG_ARB_DISABLE, 0);
 +      }
 +      outb(pic2_mask,0xA1);   /* restore mask */
 +      outb(pic1_mask,0x21);
 +
 +      local_irq_restore(flags);
 +      preempt_enable();
 +
 +      freqs.new = calc_speed(longhaul_get_cpu_mult());
 +      /* Check if requested frequency is set. */
 +      if (unlikely(freqs.new != speed)) {
 +              printk(KERN_INFO PFX "Failed to set requested frequency!\n");
 +              /* Revision ID = 1 but processor is expecting revision key
 +               * equal to 0. Jumpers at the bottom of processor will change
 +               * multiplier and FSB, but will not change bits in Longhaul
 +               * MSR nor enable voltage scaling. */
 +              if (!revid_errata) {
 +                      printk(KERN_INFO PFX "Enabling \"Ignore Revision ID\" "
 +                                              "option.\n");
 +                      revid_errata = 1;
 +                      msleep(200);
 +                      goto retry_loop;
 +              }
 +              /* Why ACPI C3 sometimes doesn't work is a mystery for me.
 +               * But it does happen. Processor is entering ACPI C3 state,
 +               * but it doesn't change frequency. I tried poking various
 +               * bits in northbridge registers, but without success. */
 +              if (longhaul_flags & USE_ACPI_C3) {
 +                      printk(KERN_INFO PFX "Disabling ACPI C3 support.\n");
 +                      longhaul_flags &= ~USE_ACPI_C3;
 +                      if (revid_errata) {
 +                              printk(KERN_INFO PFX "Disabling \"Ignore "
 +                                              "Revision ID\" option.\n");
 +                              revid_errata = 0;
 +                      }
 +                      msleep(200);
 +                      goto retry_loop;
 +              }
 +              /* This shouldn't happen. Longhaul ver. 2 was reported not
 +               * working on processors without voltage scaling, but with
 +               * RevID = 1. RevID errata will make things right. Just
 +               * to be 100% sure. */
 +              if (longhaul_version == TYPE_LONGHAUL_V2) {
 +                      printk(KERN_INFO PFX "Switching to Longhaul ver. 1\n");
 +                      longhaul_version = TYPE_LONGHAUL_V1;
 +                      msleep(200);
 +                      goto retry_loop;
 +              }
 +      }
 +      /* Report true CPU frequency */
 +      cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
 +
 +      if (!bm_timeout)
 +              printk(KERN_INFO PFX "Warning: Timeout while waiting for idle PCI bus.\n");
 +}
 +
 +/*
 + * Centaur decided to make life a little more tricky.
 + * Only longhaul v1 is allowed to read EBLCR BSEL[0:1].
 + * Samuel2 and above have to try and guess what the FSB is.
 + * We do this by assuming we booted at maximum multiplier, and interpolate
 + * between that value multiplied by possible FSBs and cpu_mhz which
 + * was calculated at boot time. Really ugly, but no other way to do this.
 + */
 +
 +#define ROUNDING      0xf
 +
 +static int guess_fsb(int mult)
 +{
 +      int speed = cpu_khz / 1000;
 +      int i;
 +      int speeds[] = { 666, 1000, 1333, 2000 };
 +      int f_max, f_min;
 +
 +      for (i = 0; i < 4; i++) {
 +              f_max = ((speeds[i] * mult) + 50) / 100;
 +              f_max += (ROUNDING / 2);
 +              f_min = f_max - ROUNDING;
 +              if ((speed <= f_max) && (speed >= f_min))
 +                      return speeds[i] / 10;
 +      }
 +      return 0;
 +}
 +
 +
 +static int __init longhaul_get_ranges(void)
 +{
 +      unsigned int i, j, k = 0;
 +      unsigned int ratio;
 +      int mult;
 +
 +      /* Get current frequency */
 +      mult = longhaul_get_cpu_mult();
 +      if (mult == -1) {
 +              printk(KERN_INFO PFX "Invalid (reserved) multiplier!\n");
 +              return -EINVAL;
 +      }
 +      fsb = guess_fsb(mult);
 +      if (fsb == 0) {
 +              printk(KERN_INFO PFX "Invalid (reserved) FSB!\n");
 +              return -EINVAL;
 +      }
 +      /* Get max multiplier - as we always did.
 +       * Longhaul MSR is usefull only when voltage scaling is enabled.
 +       * C3 is booting at max anyway. */
 +      maxmult = mult;
 +      /* Get min multiplier */
 +      switch (cpu_model) {
 +      case CPU_NEHEMIAH:
 +              minmult = 50;
 +              break;
 +      case CPU_NEHEMIAH_C:
 +              minmult = 40;
 +              break;
 +      default:
 +              minmult = 30;
 +              break;
 +      }
 +
 +      dprintk ("MinMult:%d.%dx MaxMult:%d.%dx\n",
 +               minmult/10, minmult%10, maxmult/10, maxmult%10);
 +
 +      highest_speed = calc_speed(maxmult);
 +      lowest_speed = calc_speed(minmult);
 +      dprintk ("FSB:%dMHz  Lowest speed: %s   Highest speed:%s\n", fsb,
 +               print_speed(lowest_speed/1000),
 +               print_speed(highest_speed/1000));
 +
 +      if (lowest_speed == highest_speed) {
 +              printk (KERN_INFO PFX "highestspeed == lowest, aborting.\n");
 +              return -EINVAL;
 +      }
 +      if (lowest_speed > highest_speed) {
 +              printk (KERN_INFO PFX "nonsense! lowest (%d > %d) !\n",
 +                      lowest_speed, highest_speed);
 +              return -EINVAL;
 +      }
 +
 +      longhaul_table = kmalloc((numscales + 1) * sizeof(struct cpufreq_frequency_table), GFP_KERNEL);
 +      if(!longhaul_table)
 +              return -ENOMEM;
 +
 +      for (j = 0; j < numscales; j++) {
 +              ratio = clock_ratio[j];
 +              if (ratio == -1)
 +                      continue;
 +              if (ratio > maxmult || ratio < minmult)
 +                      continue;
 +              longhaul_table[k].frequency = calc_speed(ratio);
 +              longhaul_table[k].index = j;
 +              k++;
 +      }
 +      if (k <= 1) {
 +              kfree(longhaul_table);
 +              return -ENODEV;
 +      }
 +      /* Sort */
 +      for (j = 0; j < k - 1; j++) {
 +              unsigned int min_f, min_i;
 +              min_f = longhaul_table[j].frequency;
 +              min_i = j;
 +              for (i = j + 1; i < k; i++) {
 +                      if (longhaul_table[i].frequency < min_f) {
 +                              min_f = longhaul_table[i].frequency;
 +                              min_i = i;
 +                      }
 +              }
 +              if (min_i != j) {
 +                      unsigned int temp;
 +                      temp = longhaul_table[j].frequency;
 +                      longhaul_table[j].frequency = longhaul_table[min_i].frequency;
 +                      longhaul_table[min_i].frequency = temp;
 +                      temp = longhaul_table[j].index;
 +                      longhaul_table[j].index = longhaul_table[min_i].index;
 +                      longhaul_table[min_i].index = temp;
 +              }
 +      }
 +
 +      longhaul_table[k].frequency = CPUFREQ_TABLE_END;
 +
 +      /* Find index we are running on */
 +      for (j = 0; j < k; j++) {
 +              if (clock_ratio[longhaul_table[j].index & 0x1f] == mult) {
 +                      longhaul_index = j;
 +                      break;
 +              }
 +      }
 +      return 0;
 +}
 +
 +
 +static void __init longhaul_setup_voltagescaling(void)
 +{
 +      union msr_longhaul longhaul;
 +      struct mV_pos minvid, maxvid, vid;
 +      unsigned int j, speed, pos, kHz_step, numvscales;
 +      int min_vid_speed;
 +
 +      rdmsrl(MSR_VIA_LONGHAUL, longhaul.val);
 +      if (!(longhaul.bits.RevisionID & 1)) {
 +              printk(KERN_INFO PFX "Voltage scaling not supported by CPU.\n");
 +              return;
 +      }
 +
 +      if (!longhaul.bits.VRMRev) {
 +              printk(KERN_INFO PFX "VRM 8.5\n");
 +              vrm_mV_table = &vrm85_mV[0];
 +              mV_vrm_table = &mV_vrm85[0];
 +      } else {
 +              printk(KERN_INFO PFX "Mobile VRM\n");
 +              if (cpu_model < CPU_NEHEMIAH)
 +                      return;
 +              vrm_mV_table = &mobilevrm_mV[0];
 +              mV_vrm_table = &mV_mobilevrm[0];
 +      }
 +
 +      minvid = vrm_mV_table[longhaul.bits.MinimumVID];
 +      maxvid = vrm_mV_table[longhaul.bits.MaximumVID];
 +
 +      if (minvid.mV == 0 || maxvid.mV == 0 || minvid.mV > maxvid.mV) {
 +              printk (KERN_INFO PFX "Bogus values Min:%d.%03d Max:%d.%03d. "
 +                                      "Voltage scaling disabled.\n",
 +                                      minvid.mV/1000, minvid.mV%1000, maxvid.mV/1000, maxvid.mV%1000);
 +              return;
 +      }
 +
 +      if (minvid.mV == maxvid.mV) {
 +              printk (KERN_INFO PFX "Claims to support voltage scaling but min & max are "
 +                              "both %d.%03d. Voltage scaling disabled\n",
 +                              maxvid.mV/1000, maxvid.mV%1000);
 +              return;
 +      }
 +
 +      /* How many voltage steps */
 +      numvscales = maxvid.pos - minvid.pos + 1;
 +      printk(KERN_INFO PFX
 +              "Max VID=%d.%03d  "
 +              "Min VID=%d.%03d, "
 +              "%d possible voltage scales\n",
 +              maxvid.mV/1000, maxvid.mV%1000,
 +              minvid.mV/1000, minvid.mV%1000,
 +              numvscales);
 +
 +      /* Calculate max frequency at min voltage */
 +      j = longhaul.bits.MinMHzBR;
 +      if (longhaul.bits.MinMHzBR4)
 +              j += 16;
 +      min_vid_speed = eblcr_table[j];
 +      if (min_vid_speed == -1)
 +              return;
 +      switch (longhaul.bits.MinMHzFSB) {
 +      case 0:
 +              min_vid_speed *= 13333;
 +              break;
 +      case 1:
 +              min_vid_speed *= 10000;
 +              break;
 +      case 3:
 +              min_vid_speed *= 6666;
 +              break;
 +      default:
 +              return;
 +              break;
 +      }
 +      if (min_vid_speed >= highest_speed)
 +              return;
 +      /* Calculate kHz for one voltage step */
 +      kHz_step = (highest_speed - min_vid_speed) / numvscales;
 +
 +      j = 0;
 +      while (longhaul_table[j].frequency != CPUFREQ_TABLE_END) {
 +              speed = longhaul_table[j].frequency;
 +              if (speed > min_vid_speed)
 +                      pos = (speed - min_vid_speed) / kHz_step + minvid.pos;
 +              else
 +                      pos = minvid.pos;
 +              longhaul_table[j].index |= mV_vrm_table[pos] << 8;
 +              vid = vrm_mV_table[mV_vrm_table[pos]];
 +              printk(KERN_INFO PFX "f: %d kHz, index: %d, vid: %d mV\n", speed, j, vid.mV);
 +              j++;
 +      }
 +
 +      can_scale_voltage = 1;
 +      printk(KERN_INFO PFX "Voltage scaling enabled.\n");
 +}
 +
 +
 +static int longhaul_verify(struct cpufreq_policy *policy)
 +{
 +      return cpufreq_frequency_table_verify(policy, longhaul_table);
 +}
 +
 +
 +static int longhaul_target(struct cpufreq_policy *policy,
 +                          unsigned int target_freq, unsigned int relation)
 +{
 +      unsigned int table_index = 0;
 +      unsigned int i;
 +      unsigned int dir = 0;
 +      u8 vid, current_vid;
 +
 +      if (cpufreq_frequency_table_target(policy, longhaul_table, target_freq, relation, &table_index))
 +              return -EINVAL;
 +
 +      /* Don't set same frequency again */
 +      if (longhaul_index == table_index)
 +              return 0;
 +
 +      if (!can_scale_voltage)
 +              longhaul_setstate(table_index);
 +      else {
 +              /* On test system voltage transitions exceeding single
 +               * step up or down were turning motherboard off. Both
 +               * "ondemand" and "userspace" are unsafe. C7 is doing
 +               * this in hardware, C3 is old and we need to do this
 +               * in software. */
 +              i = longhaul_index;
 +              current_vid = (longhaul_table[longhaul_index].index >> 8) & 0x1f;
 +              if (table_index > longhaul_index)
 +                      dir = 1;
 +              while (i != table_index) {
 +                      vid = (longhaul_table[i].index >> 8) & 0x1f;
 +                      if (vid != current_vid) {
 +                              longhaul_setstate(i);
 +                              current_vid = vid;
 +                              msleep(200);
 +                      }
 +                      if (dir)
 +                              i++;
 +                      else
 +                              i--;
 +              }
 +              longhaul_setstate(table_index);
 +      }
 +      longhaul_index = table_index;
 +      return 0;
 +}
 +
 +
 +static unsigned int longhaul_get(unsigned int cpu)
 +{
 +      if (cpu)
 +              return 0;
 +      return calc_speed(longhaul_get_cpu_mult());
 +}
 +
 +static acpi_status longhaul_walk_callback(acpi_handle obj_handle,
 +                                        u32 nesting_level,
 +                                        void *context, void **return_value)
 +{
 +      struct acpi_device *d;
 +
 +      if ( acpi_bus_get_device(obj_handle, &d) ) {
 +              return 0;
 +      }
 +      *return_value = (void *)acpi_driver_data(d);
 +      return 1;
 +}
 +
 +/* VIA don't support PM2 reg, but have something similar */
 +static int enable_arbiter_disable(void)
 +{
 +      struct pci_dev *dev;
 +      int status = 1;
 +      int reg;
 +      u8 pci_cmd;
 +
 +      /* Find PLE133 host bridge */
 +      reg = 0x78;
 +      dev = pci_get_device(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_8601_0,
 +                           NULL);
++      /* Find PM133/VT8605 host bridge */
++      if (dev == NULL)
++              dev = pci_get_device(PCI_VENDOR_ID_VIA,
++                                   PCI_DEVICE_ID_VIA_8605_0, NULL);
 +      /* Find CLE266 host bridge */
 +      if (dev == NULL) {
 +              reg = 0x76;
 +              dev = pci_get_device(PCI_VENDOR_ID_VIA,
 +                                   PCI_DEVICE_ID_VIA_862X_0, NULL);
 +              /* Find CN400 V-Link host bridge */
 +              if (dev == NULL)
 +                      dev = pci_get_device(PCI_VENDOR_ID_VIA, 0x7259, NULL);
 +      }
 +      if (dev != NULL) {
 +              /* Enable access to port 0x22 */
 +              pci_read_config_byte(dev, reg, &pci_cmd);
 +              if (!(pci_cmd & 1<<7)) {
 +                      pci_cmd |= 1<<7;
 +                      pci_write_config_byte(dev, reg, pci_cmd);
 +                      pci_read_config_byte(dev, reg, &pci_cmd);
 +                      if (!(pci_cmd & 1<<7)) {
 +                              printk(KERN_ERR PFX
 +                                      "Can't enable access to port 0x22.\n");
 +                              status = 0;
 +                      }
 +              }
 +              pci_dev_put(dev);
 +              return status;
 +      }
 +      return 0;
 +}
 +
 +static int longhaul_setup_southbridge(void)
 +{
 +      struct pci_dev *dev;
 +      u8 pci_cmd;
 +
 +      /* Find VT8235 southbridge */
 +      dev = pci_get_device(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_8235, NULL);
 +      if (dev == NULL)
 +      /* Find VT8237 southbridge */
 +              dev = pci_get_device(PCI_VENDOR_ID_VIA,
 +                                   PCI_DEVICE_ID_VIA_8237, NULL);
 +      if (dev != NULL) {
 +              /* Set transition time to max */
 +              pci_read_config_byte(dev, 0xec, &pci_cmd);
 +              pci_cmd &= ~(1 << 2);
 +              pci_write_config_byte(dev, 0xec, pci_cmd);
 +              pci_read_config_byte(dev, 0xe4, &pci_cmd);
 +              pci_cmd &= ~(1 << 7);
 +              pci_write_config_byte(dev, 0xe4, pci_cmd);
 +              pci_read_config_byte(dev, 0xe5, &pci_cmd);
 +              pci_cmd |= 1 << 7;
 +              pci_write_config_byte(dev, 0xe5, pci_cmd);
 +              /* Get address of ACPI registers block*/
 +              pci_read_config_byte(dev, 0x81, &pci_cmd);
 +              if (pci_cmd & 1 << 7) {
 +                      pci_read_config_dword(dev, 0x88, &acpi_regs_addr);
 +                      acpi_regs_addr &= 0xff00;
 +                      printk(KERN_INFO PFX "ACPI I/O at 0x%x\n", acpi_regs_addr);
 +              }
 +
 +              pci_dev_put(dev);
 +              return 1;
 +      }
 +      return 0;
 +}
 +
 +static int __init longhaul_cpu_init(struct cpufreq_policy *policy)
 +{
 +      struct cpuinfo_x86 *c = cpu_data;
 +      char *cpuname=NULL;
 +      int ret;
 +      u32 lo, hi;
 +
 +      /* Check what we have on this motherboard */
 +      switch (c->x86_model) {
 +      case 6:
 +              cpu_model = CPU_SAMUEL;
 +              cpuname = "C3 'Samuel' [C5A]";
 +              longhaul_version = TYPE_LONGHAUL_V1;
 +              memcpy (clock_ratio, samuel1_clock_ratio, sizeof(samuel1_clock_ratio));
 +              memcpy (eblcr_table, samuel1_eblcr, sizeof(samuel1_eblcr));
 +              break;
 +
 +      case 7:
 +              switch (c->x86_mask) {
 +              case 0:
 +                      longhaul_version = TYPE_LONGHAUL_V1;
 +                      cpu_model = CPU_SAMUEL2;
 +                      cpuname = "C3 'Samuel 2' [C5B]";
 +                      /* Note, this is not a typo, early Samuel2's had
 +                       * Samuel1 ratios. */
 +                      memcpy(clock_ratio, samuel1_clock_ratio,
 +                              sizeof(samuel1_clock_ratio));
 +                      memcpy(eblcr_table, samuel2_eblcr,
 +                              sizeof(samuel2_eblcr));
 +                      break;
 +              case 1 ... 15:
 +                      longhaul_version = TYPE_LONGHAUL_V1;
 +                      if (c->x86_mask < 8) {
 +                              cpu_model = CPU_SAMUEL2;
 +                              cpuname = "C3 'Samuel 2' [C5B]";
 +                      } else {
 +                              cpu_model = CPU_EZRA;
 +                              cpuname = "C3 'Ezra' [C5C]";
 +                      }
 +                      memcpy(clock_ratio, ezra_clock_ratio,
 +                              sizeof(ezra_clock_ratio));
 +                      memcpy(eblcr_table, ezra_eblcr,
 +                              sizeof(ezra_eblcr));
 +                      break;
 +              }
 +              break;
 +
 +      case 8:
 +              cpu_model = CPU_EZRA_T;
 +              cpuname = "C3 'Ezra-T' [C5M]";
 +              longhaul_version = TYPE_POWERSAVER;
 +              numscales=32;
 +              memcpy (clock_ratio, ezrat_clock_ratio, sizeof(ezrat_clock_ratio));
 +              memcpy (eblcr_table, ezrat_eblcr, sizeof(ezrat_eblcr));
 +              break;
 +
 +      case 9:
 +              longhaul_version = TYPE_POWERSAVER;
 +              numscales = 32;
 +              memcpy(clock_ratio,
 +                     nehemiah_clock_ratio,
 +                     sizeof(nehemiah_clock_ratio));
 +              memcpy(eblcr_table, nehemiah_eblcr, sizeof(nehemiah_eblcr));
 +              switch (c->x86_mask) {
 +              case 0 ... 1:
 +                      cpu_model = CPU_NEHEMIAH;
 +                      cpuname = "C3 'Nehemiah A' [C5XLOE]";
 +                      break;
 +              case 2 ... 4:
 +                      cpu_model = CPU_NEHEMIAH;
 +                      cpuname = "C3 'Nehemiah B' [C5XLOH]";
 +                      break;
 +              case 5 ... 15:
 +                      cpu_model = CPU_NEHEMIAH_C;
 +                      cpuname = "C3 'Nehemiah C' [C5P]";
 +                      break;
 +              }
 +              break;
 +
 +      default:
 +              cpuname = "Unknown";
 +              break;
 +      }
 +      /* Check Longhaul ver. 2 */
 +      if (longhaul_version == TYPE_LONGHAUL_V2) {
 +              rdmsr(MSR_VIA_LONGHAUL, lo, hi);
 +              if (lo == 0 && hi == 0)
 +                      /* Looks like MSR isn't present */
 +                      longhaul_version = TYPE_LONGHAUL_V1;
 +      }
 +
 +      printk (KERN_INFO PFX "VIA %s CPU detected.  ", cpuname);
 +      switch (longhaul_version) {
 +      case TYPE_LONGHAUL_V1:
 +      case TYPE_LONGHAUL_V2:
 +              printk ("Longhaul v%d supported.\n", longhaul_version);
 +              break;
 +      case TYPE_POWERSAVER:
 +              printk ("Powersaver supported.\n");
 +              break;
 +      };
 +
 +      /* Doesn't hurt */
 +      longhaul_setup_southbridge();
 +
 +      /* Find ACPI data for processor */
 +      acpi_walk_namespace(ACPI_TYPE_PROCESSOR, ACPI_ROOT_OBJECT,
 +                              ACPI_UINT32_MAX, &longhaul_walk_callback,
 +                              NULL, (void *)&pr);
 +
 +      /* Check ACPI support for C3 state */
 +      if (pr != NULL && longhaul_version == TYPE_POWERSAVER) {
 +              cx = &pr->power.states[ACPI_STATE_C3];
 +              if (cx->address > 0 && cx->latency <= 1000)
 +                      longhaul_flags |= USE_ACPI_C3;
 +      }
 +      /* Disable if it isn't working */
 +      if (disable_acpi_c3)
 +              longhaul_flags &= ~USE_ACPI_C3;
 +      /* Check if northbridge is friendly */
 +      if (enable_arbiter_disable())
 +              longhaul_flags |= USE_NORTHBRIDGE;
 +
 +      /* Check ACPI support for bus master arbiter disable */
 +      if (!(longhaul_flags & USE_ACPI_C3
 +           || longhaul_flags & USE_NORTHBRIDGE)
 +          && ((pr == NULL) || !(pr->flags.bm_control))) {
 +              printk(KERN_ERR PFX
 +                      "No ACPI support. Unsupported northbridge.\n");
 +              return -ENODEV;
 +      }
 +
 +      if (longhaul_flags & USE_NORTHBRIDGE)
 +              printk(KERN_INFO PFX "Using northbridge support.\n");
 +      if (longhaul_flags & USE_ACPI_C3)
 +              printk(KERN_INFO PFX "Using ACPI support.\n");
 +
 +      ret = longhaul_get_ranges();
 +      if (ret != 0)
 +              return ret;
 +
 +      if ((longhaul_version != TYPE_LONGHAUL_V1) && (scale_voltage != 0))
 +              longhaul_setup_voltagescaling();
 +
 +      policy->cpuinfo.transition_latency = 200000;    /* nsec */
 +      policy->cur = calc_speed(longhaul_get_cpu_mult());
 +
 +      ret = cpufreq_frequency_table_cpuinfo(policy, longhaul_table);
 +      if (ret)
 +              return ret;
 +
 +      cpufreq_frequency_table_get_attr(longhaul_table, policy->cpu);
 +
 +      return 0;
 +}
 +
 +static int __devexit longhaul_cpu_exit(struct cpufreq_policy *policy)
 +{
 +      cpufreq_frequency_table_put_attr(policy->cpu);
 +      return 0;
 +}
 +
 +static struct freq_attr* longhaul_attr[] = {
 +      &cpufreq_freq_attr_scaling_available_freqs,
 +      NULL,
 +};
 +
 +static struct cpufreq_driver longhaul_driver = {
 +      .verify = longhaul_verify,
 +      .target = longhaul_target,
 +      .get    = longhaul_get,
 +      .init   = longhaul_cpu_init,
 +      .exit   = __devexit_p(longhaul_cpu_exit),
 +      .name   = "longhaul",
 +      .owner  = THIS_MODULE,
 +      .attr   = longhaul_attr,
 +};
 +
 +
 +static int __init longhaul_init(void)
 +{
 +      struct cpuinfo_x86 *c = cpu_data;
 +
 +      if (c->x86_vendor != X86_VENDOR_CENTAUR || c->x86 != 6)
 +              return -ENODEV;
 +
 +#ifdef CONFIG_SMP
 +      if (num_online_cpus() > 1) {
 +              printk(KERN_ERR PFX "More than 1 CPU detected, longhaul disabled.\n");
 +              return -ENODEV;
 +      }
 +#endif
 +#ifdef CONFIG_X86_IO_APIC
 +      if (cpu_has_apic) {
 +              printk(KERN_ERR PFX "APIC detected. Longhaul is currently broken in this configuration.\n");
 +              return -ENODEV;
 +      }
 +#endif
 +      switch (c->x86_model) {
 +      case 6 ... 9:
 +              return cpufreq_register_driver(&longhaul_driver);
 +      case 10:
 +              printk(KERN_ERR PFX "Use acpi-cpufreq driver for VIA C7\n");
 +      default:
 +              ;;
 +      }
 +
 +      return -ENODEV;
 +}
 +
 +
 +static void __exit longhaul_exit(void)
 +{
 +      int i;
 +
 +      for (i=0; i < numscales; i++) {
 +              if (clock_ratio[i] == maxmult) {
 +                      longhaul_setstate(i);
 +                      break;
 +              }
 +      }
 +
 +      cpufreq_unregister_driver(&longhaul_driver);
 +      kfree(longhaul_table);
 +}
 +
 +/* Even if BIOS is exporting ACPI C3 state, and it is used
 + * with success when CPU is idle, this state doesn't
 + * trigger frequency transition in some cases. */
 +module_param (disable_acpi_c3, int, 0644);
 +MODULE_PARM_DESC(disable_acpi_c3, "Don't use ACPI C3 support");
 +/* Change CPU voltage with frequency. Very usefull to save
 + * power, but most VIA C3 processors aren't supporting it. */
 +module_param (scale_voltage, int, 0644);
 +MODULE_PARM_DESC(scale_voltage, "Scale voltage of processor");
 +/* Force revision key to 0 for processors which doesn't
 + * support voltage scaling, but are introducing itself as
 + * such. */
 +module_param(revid_errata, int, 0644);
 +MODULE_PARM_DESC(revid_errata, "Ignore CPU Revision ID");
 +
 +MODULE_AUTHOR ("Dave Jones <davej@codemonkey.org.uk>");
 +MODULE_DESCRIPTION ("Longhaul driver for VIA Cyrix processors.");
 +MODULE_LICENSE ("GPL");
 +
 +late_initcall(longhaul_init);
 +module_exit(longhaul_exit);
index 4c76b511e1944d16ed420343cb57db0dafb1f943,0000000000000000000000000000000000000000..8eb414b906d29f7e20f93fdc5d0e6e0edfe21da1
mode 100644,000000..100644
--- /dev/null
@@@ -1,316 -1,0 +1,315 @@@
-       policy->governor = CPUFREQ_DEFAULT_GOVERNOR;
 +/*
 + *    Pentium 4/Xeon CPU on demand clock modulation/speed scaling
 + *    (C) 2002 - 2003 Dominik Brodowski <linux@brodo.de>
 + *    (C) 2002 Zwane Mwaikambo <zwane@commfireservices.com>
 + *    (C) 2002 Arjan van de Ven <arjanv@redhat.com>
 + *    (C) 2002 Tora T. Engstad
 + *    All Rights Reserved
 + *
 + *    This program is free software; you can redistribute it and/or
 + *      modify it under the terms of the GNU General Public License
 + *      as published by the Free Software Foundation; either version
 + *      2 of the License, or (at your option) any later version.
 + *
 + *      The author(s) of this software shall not be held liable for damages
 + *      of any nature resulting due to the use of this software. This
 + *      software is provided AS-IS with no warranties.
 + *
 + *    Date            Errata                  Description
 + *    20020525        N44, O17        12.5% or 25% DC causes lockup
 + *
 + */
 +
 +#include <linux/kernel.h>
 +#include <linux/module.h>
 +#include <linux/init.h>
 +#include <linux/smp.h>
 +#include <linux/cpufreq.h>
 +#include <linux/slab.h>
 +#include <linux/cpumask.h>
 +
 +#include <asm/processor.h>
 +#include <asm/msr.h>
 +#include <asm/timex.h>
 +
 +#include "speedstep-lib.h"
 +
 +#define PFX   "p4-clockmod: "
 +#define dprintk(msg...) cpufreq_debug_printk(CPUFREQ_DEBUG_DRIVER, "p4-clockmod", msg)
 +
 +/*
 + * Duty Cycle (3bits), note DC_DISABLE is not specified in
 + * intel docs i just use it to mean disable
 + */
 +enum {
 +      DC_RESV, DC_DFLT, DC_25PT, DC_38PT, DC_50PT,
 +      DC_64PT, DC_75PT, DC_88PT, DC_DISABLE
 +};
 +
 +#define DC_ENTRIES    8
 +
 +
 +static int has_N44_O17_errata[NR_CPUS];
 +static unsigned int stock_freq;
 +static struct cpufreq_driver p4clockmod_driver;
 +static unsigned int cpufreq_p4_get(unsigned int cpu);
 +
 +static int cpufreq_p4_setdc(unsigned int cpu, unsigned int newstate)
 +{
 +      u32 l, h;
 +
 +      if (!cpu_online(cpu) || (newstate > DC_DISABLE) || (newstate == DC_RESV))
 +              return -EINVAL;
 +
 +      rdmsr_on_cpu(cpu, MSR_IA32_THERM_STATUS, &l, &h);
 +
 +      if (l & 0x01)
 +              dprintk("CPU#%d currently thermal throttled\n", cpu);
 +
 +      if (has_N44_O17_errata[cpu] && (newstate == DC_25PT || newstate == DC_DFLT))
 +              newstate = DC_38PT;
 +
 +      rdmsr_on_cpu(cpu, MSR_IA32_THERM_CONTROL, &l, &h);
 +      if (newstate == DC_DISABLE) {
 +              dprintk("CPU#%d disabling modulation\n", cpu);
 +              wrmsr_on_cpu(cpu, MSR_IA32_THERM_CONTROL, l & ~(1<<4), h);
 +      } else {
 +              dprintk("CPU#%d setting duty cycle to %d%%\n",
 +                      cpu, ((125 * newstate) / 10));
 +              /* bits 63 - 5  : reserved
 +               * bit  4       : enable/disable
 +               * bits 3-1     : duty cycle
 +               * bit  0       : reserved
 +               */
 +              l = (l & ~14);
 +              l = l | (1<<4) | ((newstate & 0x7)<<1);
 +              wrmsr_on_cpu(cpu, MSR_IA32_THERM_CONTROL, l, h);
 +      }
 +
 +      return 0;
 +}
 +
 +
 +static struct cpufreq_frequency_table p4clockmod_table[] = {
 +      {DC_RESV, CPUFREQ_ENTRY_INVALID},
 +      {DC_DFLT, 0},
 +      {DC_25PT, 0},
 +      {DC_38PT, 0},
 +      {DC_50PT, 0},
 +      {DC_64PT, 0},
 +      {DC_75PT, 0},
 +      {DC_88PT, 0},
 +      {DC_DISABLE, 0},
 +      {DC_RESV, CPUFREQ_TABLE_END},
 +};
 +
 +
 +static int cpufreq_p4_target(struct cpufreq_policy *policy,
 +                           unsigned int target_freq,
 +                           unsigned int relation)
 +{
 +      unsigned int    newstate = DC_RESV;
 +      struct cpufreq_freqs freqs;
 +      int i;
 +
 +      if (cpufreq_frequency_table_target(policy, &p4clockmod_table[0], target_freq, relation, &newstate))
 +              return -EINVAL;
 +
 +      freqs.old = cpufreq_p4_get(policy->cpu);
 +      freqs.new = stock_freq * p4clockmod_table[newstate].index / 8;
 +
 +      if (freqs.new == freqs.old)
 +              return 0;
 +
 +      /* notifiers */
 +      for_each_cpu_mask(i, policy->cpus) {
 +              freqs.cpu = i;
 +              cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
 +      }
 +
 +      /* run on each logical CPU, see section 13.15.3 of IA32 Intel Architecture Software
 +       * Developer's Manual, Volume 3
 +       */
 +      for_each_cpu_mask(i, policy->cpus)
 +              cpufreq_p4_setdc(i, p4clockmod_table[newstate].index);
 +
 +      /* notifiers */
 +      for_each_cpu_mask(i, policy->cpus) {
 +              freqs.cpu = i;
 +              cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
 +      }
 +
 +      return 0;
 +}
 +
 +
 +static int cpufreq_p4_verify(struct cpufreq_policy *policy)
 +{
 +      return cpufreq_frequency_table_verify(policy, &p4clockmod_table[0]);
 +}
 +
 +
 +static unsigned int cpufreq_p4_get_frequency(struct cpuinfo_x86 *c)
 +{
 +      if (c->x86 == 0x06) {
 +              if (cpu_has(c, X86_FEATURE_EST))
 +                      printk(KERN_WARNING PFX "Warning: EST-capable CPU detected. "
 +                             "The acpi-cpufreq module offers voltage scaling"
 +                             " in addition of frequency scaling. You should use "
 +                             "that instead of p4-clockmod, if possible.\n");
 +              switch (c->x86_model) {
 +              case 0x0E: /* Core */
 +              case 0x0F: /* Core Duo */
 +                      p4clockmod_driver.flags |= CPUFREQ_CONST_LOOPS;
 +                      return speedstep_get_processor_frequency(SPEEDSTEP_PROCESSOR_PCORE);
 +              case 0x0D: /* Pentium M (Dothan) */
 +                      p4clockmod_driver.flags |= CPUFREQ_CONST_LOOPS;
 +                      /* fall through */
 +              case 0x09: /* Pentium M (Banias) */
 +                      return speedstep_get_processor_frequency(SPEEDSTEP_PROCESSOR_PM);
 +              }
 +      }
 +
 +      if (c->x86 != 0xF) {
 +              printk(KERN_WARNING PFX "Unknown p4-clockmod-capable CPU. Please send an e-mail to <cpufreq@lists.linux.org.uk>\n");
 +              return 0;
 +      }
 +
 +      /* on P-4s, the TSC runs with constant frequency independent whether
 +       * throttling is active or not. */
 +      p4clockmod_driver.flags |= CPUFREQ_CONST_LOOPS;
 +
 +      if (speedstep_detect_processor() == SPEEDSTEP_PROCESSOR_P4M) {
 +              printk(KERN_WARNING PFX "Warning: Pentium 4-M detected. "
 +                     "The speedstep-ich or acpi cpufreq modules offer "
 +                     "voltage scaling in addition of frequency scaling. "
 +                     "You should use either one instead of p4-clockmod, "
 +                     "if possible.\n");
 +              return speedstep_get_processor_frequency(SPEEDSTEP_PROCESSOR_P4M);
 +      }
 +
 +      return speedstep_get_processor_frequency(SPEEDSTEP_PROCESSOR_P4D);
 +}
 +
 +
 +
 +static int cpufreq_p4_cpu_init(struct cpufreq_policy *policy)
 +{
 +      struct cpuinfo_x86 *c = &cpu_data[policy->cpu];
 +      int cpuid = 0;
 +      unsigned int i;
 +
 +#ifdef CONFIG_SMP
 +      policy->cpus = cpu_sibling_map[policy->cpu];
 +#endif
 +
 +      /* Errata workaround */
 +      cpuid = (c->x86 << 8) | (c->x86_model << 4) | c->x86_mask;
 +      switch (cpuid) {
 +      case 0x0f07:
 +      case 0x0f0a:
 +      case 0x0f11:
 +      case 0x0f12:
 +              has_N44_O17_errata[policy->cpu] = 1;
 +              dprintk("has errata -- disabling low frequencies\n");
 +      }
 +
 +      /* get max frequency */
 +      stock_freq = cpufreq_p4_get_frequency(c);
 +      if (!stock_freq)
 +              return -EINVAL;
 +
 +      /* table init */
 +      for (i=1; (p4clockmod_table[i].frequency != CPUFREQ_TABLE_END); i++) {
 +              if ((i<2) && (has_N44_O17_errata[policy->cpu]))
 +                      p4clockmod_table[i].frequency = CPUFREQ_ENTRY_INVALID;
 +              else
 +                      p4clockmod_table[i].frequency = (stock_freq * i)/8;
 +      }
 +      cpufreq_frequency_table_get_attr(p4clockmod_table, policy->cpu);
 +
 +      /* cpuinfo and default policy values */
 +      policy->cpuinfo.transition_latency = 1000000; /* assumed */
 +      policy->cur = stock_freq;
 +
 +      return cpufreq_frequency_table_cpuinfo(policy, &p4clockmod_table[0]);
 +}
 +
 +
 +static int cpufreq_p4_cpu_exit(struct cpufreq_policy *policy)
 +{
 +      cpufreq_frequency_table_put_attr(policy->cpu);
 +      return 0;
 +}
 +
 +static unsigned int cpufreq_p4_get(unsigned int cpu)
 +{
 +      u32 l, h;
 +
 +      rdmsr_on_cpu(cpu, MSR_IA32_THERM_CONTROL, &l, &h);
 +
 +      if (l & 0x10) {
 +              l = l >> 1;
 +              l &= 0x7;
 +      } else
 +              l = DC_DISABLE;
 +
 +      if (l != DC_DISABLE)
 +              return (stock_freq * l / 8);
 +
 +      return stock_freq;
 +}
 +
 +static struct freq_attr* p4clockmod_attr[] = {
 +      &cpufreq_freq_attr_scaling_available_freqs,
 +      NULL,
 +};
 +
 +static struct cpufreq_driver p4clockmod_driver = {
 +      .verify         = cpufreq_p4_verify,
 +      .target         = cpufreq_p4_target,
 +      .init           = cpufreq_p4_cpu_init,
 +      .exit           = cpufreq_p4_cpu_exit,
 +      .get            = cpufreq_p4_get,
 +      .name           = "p4-clockmod",
 +      .owner          = THIS_MODULE,
 +      .attr           = p4clockmod_attr,
 +};
 +
 +
 +static int __init cpufreq_p4_init(void)
 +{
 +      struct cpuinfo_x86 *c = cpu_data;
 +      int ret;
 +
 +      /*
 +       * 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;
 +
 +      ret = cpufreq_register_driver(&p4clockmod_driver);
 +      if (!ret)
 +              printk(KERN_INFO PFX "P4/Xeon(TM) CPU On-Demand Clock Modulation available\n");
 +
 +      return (ret);
 +}
 +
 +
 +static void __exit cpufreq_p4_exit(void)
 +{
 +      cpufreq_unregister_driver(&p4clockmod_driver);
 +}
 +
 +
 +MODULE_AUTHOR ("Zwane Mwaikambo <zwane@commfireservices.com>");
 +MODULE_DESCRIPTION ("cpufreq driver for Pentium(TM) 4/Xeon(TM)");
 +MODULE_LICENSE ("GPL");
 +
 +late_initcall(cpufreq_p4_init);
 +module_exit(cpufreq_p4_exit);
index f89524051e4a3aa78012fd16a4179976764dc7ec,0000000000000000000000000000000000000000..6d02853393175305e8f749b99a8500379d1353d2
mode 100644,000000..100644
--- /dev/null
@@@ -1,256 -1,0 +1,255 @@@
-       policy->governor = CPUFREQ_DEFAULT_GOVERNOR;
 +/*
 + *  This file was based upon code in Powertweak Linux (http://powertweak.sf.net)
 + *  (C) 2000-2003  Dave Jones, Arjan van de Ven, Janne Pänkälä, Dominik Brodowski.
 + *
 + *  Licensed under the terms of the GNU GPL License version 2.
 + *
 + *  BIG FAT DISCLAIMER: Work in progress code. Possibly *dangerous*
 + */
 +
 +#include <linux/kernel.h>
 +#include <linux/module.h>
 +#include <linux/init.h>
 +#include <linux/cpufreq.h>
 +#include <linux/ioport.h>
 +#include <linux/slab.h>
 +
 +#include <asm/msr.h>
 +#include <asm/timex.h>
 +#include <asm/io.h>
 +
 +
 +#define POWERNOW_IOPORT 0xfff0         /* it doesn't matter where, as long
 +                                        as it is unused */
 +
 +static unsigned int                     busfreq;   /* FSB, in 10 kHz */
 +static unsigned int                     max_multiplier;
 +
 +
 +/* Clock ratio multiplied by 10 - see table 27 in AMD#23446 */
 +static struct cpufreq_frequency_table clock_ratio[] = {
 +      {45,  /* 000 -> 4.5x */ 0},
 +      {50,  /* 001 -> 5.0x */ 0},
 +      {40,  /* 010 -> 4.0x */ 0},
 +      {55,  /* 011 -> 5.5x */ 0},
 +      {20,  /* 100 -> 2.0x */ 0},
 +      {30,  /* 101 -> 3.0x */ 0},
 +      {60,  /* 110 -> 6.0x */ 0},
 +      {35,  /* 111 -> 3.5x */ 0},
 +      {0, CPUFREQ_TABLE_END}
 +};
 +
 +
 +/**
 + * powernow_k6_get_cpu_multiplier - returns the current FSB multiplier
 + *
 + *   Returns the current setting of the frequency multiplier. Core clock
 + * speed is frequency of the Front-Side Bus multiplied with this value.
 + */
 +static int powernow_k6_get_cpu_multiplier(void)
 +{
 +      u64             invalue = 0;
 +      u32             msrval;
 +
 +      msrval = POWERNOW_IOPORT + 0x1;
 +      wrmsr(MSR_K6_EPMR, msrval, 0); /* enable the PowerNow port */
 +      invalue=inl(POWERNOW_IOPORT + 0x8);
 +      msrval = POWERNOW_IOPORT + 0x0;
 +      wrmsr(MSR_K6_EPMR, msrval, 0); /* disable it again */
 +
 +      return clock_ratio[(invalue >> 5)&7].index;
 +}
 +
 +
 +/**
 + * powernow_k6_set_state - set the PowerNow! multiplier
 + * @best_i: clock_ratio[best_i] is the target multiplier
 + *
 + *   Tries to change the PowerNow! multiplier
 + */
 +static void powernow_k6_set_state (unsigned int best_i)
 +{
 +      unsigned long           outvalue=0, invalue=0;
 +      unsigned long           msrval;
 +      struct cpufreq_freqs    freqs;
 +
 +      if (clock_ratio[best_i].index > max_multiplier) {
 +              printk(KERN_ERR "cpufreq: invalid target frequency\n");
 +              return;
 +      }
 +
 +      freqs.old = busfreq * powernow_k6_get_cpu_multiplier();
 +      freqs.new = busfreq * clock_ratio[best_i].index;
 +      freqs.cpu = 0; /* powernow-k6.c is UP only driver */
 +
 +      cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
 +
 +      /* we now need to transform best_i to the BVC format, see AMD#23446 */
 +
 +      outvalue = (1<<12) | (1<<10) | (1<<9) | (best_i<<5);
 +
 +      msrval = POWERNOW_IOPORT + 0x1;
 +      wrmsr(MSR_K6_EPMR, msrval, 0); /* enable the PowerNow port */
 +      invalue=inl(POWERNOW_IOPORT + 0x8);
 +      invalue = invalue & 0xf;
 +      outvalue = outvalue | invalue;
 +      outl(outvalue ,(POWERNOW_IOPORT + 0x8));
 +      msrval = POWERNOW_IOPORT + 0x0;
 +      wrmsr(MSR_K6_EPMR, msrval, 0); /* disable it again */
 +
 +      cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
 +
 +      return;
 +}
 +
 +
 +/**
 + * powernow_k6_verify - verifies a new CPUfreq policy
 + * @policy: new policy
 + *
 + * Policy must be within lowest and highest possible CPU Frequency,
 + * and at least one possible state must be within min and max.
 + */
 +static int powernow_k6_verify(struct cpufreq_policy *policy)
 +{
 +      return cpufreq_frequency_table_verify(policy, &clock_ratio[0]);
 +}
 +
 +
 +/**
 + * powernow_k6_setpolicy - sets a new CPUFreq policy
 + * @policy: new policy
 + * @target_freq: the target frequency
 + * @relation: how that frequency relates to achieved frequency (CPUFREQ_RELATION_L or CPUFREQ_RELATION_H)
 + *
 + * sets a new CPUFreq policy
 + */
 +static int powernow_k6_target (struct cpufreq_policy *policy,
 +                             unsigned int target_freq,
 +                             unsigned int relation)
 +{
 +      unsigned int    newstate = 0;
 +
 +      if (cpufreq_frequency_table_target(policy, &clock_ratio[0], target_freq, relation, &newstate))
 +              return -EINVAL;
 +
 +      powernow_k6_set_state(newstate);
 +
 +      return 0;
 +}
 +
 +
 +static int powernow_k6_cpu_init(struct cpufreq_policy *policy)
 +{
 +      unsigned int i;
 +      int result;
 +
 +      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->cpuinfo.transition_latency = CPUFREQ_ETERNAL;
 +      policy->cur = busfreq * max_multiplier;
 +
 +      result = cpufreq_frequency_table_cpuinfo(policy, clock_ratio);
 +      if (result)
 +              return (result);
 +
 +      cpufreq_frequency_table_get_attr(clock_ratio, policy->cpu);
 +
 +      return 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);
 +      }
 +      cpufreq_frequency_table_put_attr(policy->cpu);
 +      return 0;
 +}
 +
 +static unsigned int powernow_k6_get(unsigned int cpu)
 +{
 +      return busfreq * powernow_k6_get_cpu_multiplier();
 +}
 +
 +static struct freq_attr* powernow_k6_attr[] = {
 +      &cpufreq_freq_attr_scaling_available_freqs,
 +      NULL,
 +};
 +
 +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,
 +      .get            = powernow_k6_get,
 +      .name           = "powernow-k6",
 +      .owner          = THIS_MODULE,
 +      .attr           = powernow_k6_attr,
 +};
 +
 +
 +/**
 + * powernow_k6_init - initializes the k6 PowerNow! CPUFreq driver
 + *
 + *   Initializes the K6 PowerNow! support. Returns -ENODEV on unsupported
 + * devices, -EINVAL or -ENOMEM on problems during initiatization, and zero
 + * on success.
 + */
 +static int __init powernow_k6_init(void)
 +{
 +      struct cpuinfo_x86      *c = cpu_data;
 +
 +      if ((c->x86_vendor != X86_VENDOR_AMD) || (c->x86 != 5) ||
 +              ((c->x86_model != 12) && (c->x86_model != 13)))
 +              return -ENODEV;
 +
 +      if (!request_region(POWERNOW_IOPORT, 16, "PowerNow!")) {
 +              printk("cpufreq: PowerNow IOPORT region already used.\n");
 +              return -EIO;
 +      }
 +
 +      if (cpufreq_register_driver(&powernow_k6_driver)) {
 +              release_region (POWERNOW_IOPORT, 16);
 +              return -EINVAL;
 +      }
 +
 +      return 0;
 +}
 +
 +
 +/**
 + * powernow_k6_exit - unregisters AMD K6-2+/3+ PowerNow! support
 + *
 + *   Unregisters AMD K6-2+ / K6-3+ PowerNow! support.
 + */
 +static void __exit powernow_k6_exit(void)
 +{
 +      cpufreq_unregister_driver(&powernow_k6_driver);
 +      release_region (POWERNOW_IOPORT, 16);
 +}
 +
 +
 +MODULE_AUTHOR ("Arjan van de Ven <arjanv@redhat.com>, Dave Jones <davej@codemonkey.org.uk>, Dominik Brodowski <linux@brodo.de>");
 +MODULE_DESCRIPTION ("PowerNow! driver for AMD K6-2+ / K6-3+ processors.");
 +MODULE_LICENSE ("GPL");
 +
 +module_init(powernow_k6_init);
 +module_exit(powernow_k6_exit);
index ca3e1d341889dfc00964fd2d052e13d9108d8562,0000000000000000000000000000000000000000..7decd6a50ffa6f44a83e36dfe443522a923ad15b
mode 100644,000000..100644
--- /dev/null
@@@ -1,703 -1,0 +1,701 @@@
-       policy->governor = CPUFREQ_DEFAULT_GOVERNOR;
 +/*
 + *  AMD K7 Powernow driver.
 + *  (C) 2003 Dave Jones <davej@codemonkey.org.uk> on behalf of SuSE Labs.
 + *  (C) 2003-2004 Dave Jones <davej@redhat.com>
 + *
 + *  Licensed under the terms of the GNU GPL License version 2.
 + *  Based upon datasheets & sample CPUs kindly provided by AMD.
 + *
 + * Errata 5: Processor may fail to execute a FID/VID change in presence of interrupt.
 + * - We cli/sti on stepping A0 CPUs around the FID/VID transition.
 + * Errata 15: Processors with half frequency multipliers may hang upon wakeup from disconnect.
 + * - We disable half multipliers if ACPI is used on A0 stepping CPUs.
 + */
 +
 +#include <linux/kernel.h>
 +#include <linux/module.h>
 +#include <linux/moduleparam.h>
 +#include <linux/init.h>
 +#include <linux/cpufreq.h>
 +#include <linux/slab.h>
 +#include <linux/string.h>
 +#include <linux/dmi.h>
 +
 +#include <asm/msr.h>
 +#include <asm/timer.h>
 +#include <asm/timex.h>
 +#include <asm/io.h>
 +#include <asm/system.h>
 +
 +#ifdef CONFIG_X86_POWERNOW_K7_ACPI
 +#include <linux/acpi.h>
 +#include <acpi/processor.h>
 +#endif
 +
 +#include "powernow-k7.h"
 +
 +#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;
 +};
 +
 +#ifdef CONFIG_X86_POWERNOW_K7_ACPI
 +union powernow_acpi_control_t {
 +      struct {
 +              unsigned long fid:5,
 +              vid:5,
 +              sgtc:20,
 +              res1:2;
 +      } bits;
 +      unsigned long val;
 +};
 +#endif
 +
 +#ifdef CONFIG_CPU_FREQ_DEBUG
 +/* divide by 1000 to get VCore voltage in V. */
 +static const 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, 1025, 1000, 975, 950, 925, 0,
 +};
 +#endif
 +
 +/* divide by 10 to get FID. */
 +static const 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,
 +};
 +
 +/* This parameter is used in order to force ACPI instead of legacy method for
 + * configuration purpose.
 + */
 +
 +static int acpi_force;
 +
 +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;
 +
 +#define dprintk(msg...) cpufreq_debug_printk(CPUFREQ_DEBUG_DRIVER, "powernow-k7", msg)
 +
 +static int check_fsb(unsigned int fsbspeed)
 +{
 +      int delta;
 +      unsigned int f = fsb / 1000;
 +
 +      delta = (fsbspeed > f) ? fsbspeed - f : f - fsbspeed;
 +      return (delta < 5);
 +}
 +
 +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) || (c->x86 !=6)) {
 +#ifdef MODULE
 +              printk (KERN_INFO PFX "This module only works with AMD K7 CPUs\n");
 +#endif
 +              return 0;
 +      }
 +
 +      /* Get maximum capabilities */
 +      maxei = cpuid_eax (0x80000000);
 +      if (maxei < 0x80000007) {       /* Any powernow info ? */
 +#ifdef MODULE
 +              printk (KERN_INFO PFX "No powernow capabilities detected\n");
 +#endif
 +              return 0;
 +      }
 +
 +      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;
 +      }
 +
 +      cpuid(0x80000007, &eax, &ebx, &ecx, &edx);
 +
 +      /* Check we can actually do something before we say anything.*/
 +      if (!(edx & (1 << 1 | 1 << 2)))
 +              return 0;
 +
 +      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;
 +      }
 +
 +      printk (".\n");
 +      return 1;
 +}
 +
 +
 +static int get_ranges (unsigned char *pst)
 +{
 +      unsigned int j;
 +      unsigned int speed;
 +      u8 fid, vid;
 +
 +      powernow_table = kzalloc((sizeof(struct cpufreq_frequency_table) * (number_scales + 1)), GFP_KERNEL);
 +      if (!powernow_table)
 +              return -ENOMEM;
 +
 +      for (j=0 ; j < number_scales; j++) {
 +              fid = *pst++;
 +
 +              powernow_table[j].frequency = (fsb * fid_codes[fid]) / 10;
 +              powernow_table[j].index = fid; /* lower 8 bits */
 +
 +              speed = powernow_table[j].frequency;
 +
 +              if ((fid_codes[fid] % 10)==5) {
 +#ifdef CONFIG_X86_POWERNOW_K7_ACPI
 +                      if (have_a0 == 1)
 +                              powernow_table[j].frequency = CPUFREQ_ENTRY_INVALID;
 +#endif
 +              }
 +
 +              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 ("   FID: 0x%x (%d.%dx [%dMHz])  "
 +                       "VID: 0x%x (%d.%03dV)\n", fid, fid_codes[fid] / 10,
 +                       fid_codes[fid] % 10, speed/1000, vid,
 +                       mobile_vid_table[vid]/1000,
 +                       mobile_vid_table[vid]%1000);
 +      }
 +      powernow_table[number_scales].frequency = CPUFREQ_TABLE_END;
 +      powernow_table[number_scales].index = 0;
 +
 +      return 0;
 +}
 +
 +
 +static void change_FID(int fid)
 +{
 +      union msr_fidvidctl fidvidctl;
 +
 +      rdmsrl (MSR_K7_FID_VID_CTL, fidvidctl.val);
 +      if (fidvidctl.bits.FID != fid) {
 +              fidvidctl.bits.SGTC = latency;
 +              fidvidctl.bits.FID = fid;
 +              fidvidctl.bits.VIDC = 0;
 +              fidvidctl.bits.FIDC = 1;
 +              wrmsrl (MSR_K7_FID_VID_CTL, fidvidctl.val);
 +      }
 +}
 +
 +
 +static void change_VID(int vid)
 +{
 +      union msr_fidvidctl fidvidctl;
 +
 +      rdmsrl (MSR_K7_FID_VID_CTL, fidvidctl.val);
 +      if (fidvidctl.bits.VID != vid) {
 +              fidvidctl.bits.SGTC = latency;
 +              fidvidctl.bits.VID = vid;
 +              fidvidctl.bits.FIDC = 0;
 +              fidvidctl.bits.VIDC = 1;
 +              wrmsrl (MSR_K7_FID_VID_CTL, fidvidctl.val);
 +      }
 +}
 +
 +
 +static void change_speed (unsigned int index)
 +{
 +      u8 fid, vid;
 +      struct cpufreq_freqs freqs;
 +      union msr_fidvidstatus fidvidstatus;
 +      int cfid;
 +
 +      /* fid are the lower 8 bits of the index we stored into
 +       * the cpufreq frequency table in powernow_decode_bios,
 +       * vid are the upper 8 bits.
 +       */
 +
 +      fid = powernow_table[index].index & 0xFF;
 +      vid = (powernow_table[index].index & 0xFF00) >> 8;
 +
 +      freqs.cpu = 0;
 +
 +      rdmsrl (MSR_K7_FID_VID_STATUS, fidvidstatus.val);
 +      cfid = fidvidstatus.bits.CFID;
 +      freqs.old = fsb * fid_codes[cfid] / 10;
 +
 +      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);
 +}
 +
 +
 +#ifdef CONFIG_X86_POWERNOW_K7_ACPI
 +
 +static struct acpi_processor_performance *acpi_processor_perf;
 +
 +static int powernow_acpi_init(void)
 +{
 +      int i;
 +      int retval = 0;
 +      union powernow_acpi_control_t pc;
 +
 +      if (acpi_processor_perf != NULL && powernow_table != NULL) {
 +              retval = -EINVAL;
 +              goto err0;
 +      }
 +
 +      acpi_processor_perf = kzalloc(sizeof(struct acpi_processor_performance),
 +                                    GFP_KERNEL);
 +      if (!acpi_processor_perf) {
 +              retval = -ENOMEM;
 +              goto err0;
 +      }
 +
 +      if (acpi_processor_register_performance(acpi_processor_perf, 0)) {
 +              retval = -EIO;
 +              goto err1;
 +      }
 +
 +      if (acpi_processor_perf->control_register.space_id != ACPI_ADR_SPACE_FIXED_HARDWARE) {
 +              retval = -ENODEV;
 +              goto err2;
 +      }
 +
 +      if (acpi_processor_perf->status_register.space_id != ACPI_ADR_SPACE_FIXED_HARDWARE) {
 +              retval = -ENODEV;
 +              goto err2;
 +      }
 +
 +      number_scales = acpi_processor_perf->state_count;
 +
 +      if (number_scales < 2) {
 +              retval = -ENODEV;
 +              goto err2;
 +      }
 +
 +      powernow_table = kzalloc((number_scales + 1) * (sizeof(struct cpufreq_frequency_table)), GFP_KERNEL);
 +      if (!powernow_table) {
 +              retval = -ENOMEM;
 +              goto err2;
 +      }
 +
 +      pc.val = (unsigned long) acpi_processor_perf->states[0].control;
 +      for (i = 0; i < number_scales; i++) {
 +              u8 fid, vid;
 +              struct acpi_processor_px *state =
 +                      &acpi_processor_perf->states[i];
 +              unsigned int speed, speed_mhz;
 +
 +              pc.val = (unsigned long) state->control;
 +              dprintk ("acpi:  P%d: %d MHz %d mW %d uS control %08x SGTC %d\n",
 +                       i,
 +                       (u32) state->core_frequency,
 +                       (u32) state->power,
 +                       (u32) state->transition_latency,
 +                       (u32) state->control,
 +                       pc.bits.sgtc);
 +
 +              vid = pc.bits.vid;
 +              fid = pc.bits.fid;
 +
 +              powernow_table[i].frequency = fsb * fid_codes[fid] / 10;
 +              powernow_table[i].index = fid; /* lower 8 bits */
 +              powernow_table[i].index |= (vid << 8); /* upper 8 bits */
 +
 +              speed = powernow_table[i].frequency;
 +              speed_mhz = speed / 1000;
 +
 +              /* processor_perflib will multiply the MHz value by 1000 to
 +               * get a KHz value (e.g. 1266000). However, powernow-k7 works
 +               * with true KHz values (e.g. 1266768). To ensure that all
 +               * powernow frequencies are available, we must ensure that
 +               * ACPI doesn't restrict them, so we round up the MHz value
 +               * to ensure that perflib's computed KHz value is greater than
 +               * or equal to powernow's KHz value.
 +               */
 +              if (speed % 1000 > 0)
 +                      speed_mhz++;
 +
 +              if ((fid_codes[fid] % 10)==5) {
 +                      if (have_a0 == 1)
 +                              powernow_table[i].frequency = CPUFREQ_ENTRY_INVALID;
 +              }
 +
 +              dprintk ("   FID: 0x%x (%d.%dx [%dMHz])  "
 +                       "VID: 0x%x (%d.%03dV)\n", fid, fid_codes[fid] / 10,
 +                       fid_codes[fid] % 10, speed_mhz, vid,
 +                       mobile_vid_table[vid]/1000,
 +                       mobile_vid_table[vid]%1000);
 +
 +              if (state->core_frequency != speed_mhz) {
 +                      state->core_frequency = speed_mhz;
 +                      dprintk("   Corrected ACPI frequency to %d\n",
 +                              speed_mhz);
 +              }
 +
 +              if (latency < pc.bits.sgtc)
 +                      latency = pc.bits.sgtc;
 +
 +              if (speed < minimum_speed)
 +                      minimum_speed = speed;
 +              if (speed > maximum_speed)
 +                      maximum_speed = speed;
 +      }
 +
 +      powernow_table[i].frequency = CPUFREQ_TABLE_END;
 +      powernow_table[i].index = 0;
 +
 +      /* notify BIOS that we exist */
 +      acpi_processor_notify_smm(THIS_MODULE);
 +
 +      return 0;
 +
 +err2:
 +      acpi_processor_unregister_performance(acpi_processor_perf, 0);
 +err1:
 +      kfree(acpi_processor_perf);
 +err0:
 +      printk(KERN_WARNING PFX "ACPI perflib can not be used in this platform\n");
 +      acpi_processor_perf = NULL;
 +      return retval;
 +}
 +#else
 +static int powernow_acpi_init(void)
 +{
 +      printk(KERN_INFO PFX "no support for ACPI processor found."
 +             "  Please recompile your kernel with ACPI processor\n");
 +      return -EINVAL;
 +}
 +#endif
 +
 +static int powernow_decode_bios (int maxfid, int startvid)
 +{
 +      struct psb_s *psb;
 +      struct pst_s *pst;
 +      unsigned int i, j;
 +      unsigned char *p;
 +      unsigned int etuple;
 +      unsigned int ret;
 +
 +      etuple = cpuid_eax(0x80000001);
 +
 +      for (i=0xC0000; i < 0xffff0 ; i+=16) {
 +
 +              p = phys_to_virt(i);
 +
 +              if (memcmp(p, "AMDK7PNOW!",  10) == 0){
 +                      dprintk ("Found PSB header at %p\n", p);
 +                      psb = (struct psb_s *) p;
 +                      dprintk ("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 ("Flags: 0x%x\n", psb->flags);
 +                      if ((psb->flags & 1)==0) {
 +                              dprintk ("Mobile voltage regulator\n");
 +                      } else {
 +                              dprintk ("Desktop voltage regulator\n");
 +                      }
 +
 +                      latency = psb->settlingtime;
 +                      if (latency < 100) {
 +                              printk (KERN_INFO PFX "BIOS set settling time to %d microseconds."
 +                                              "Should be at least 100. Correcting.\n", latency);
 +                              latency = 100;
 +                      }
 +                      dprintk ("Settling Time: %d microseconds.\n", psb->settlingtime);
 +                      dprintk ("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 (j=0; j<psb->numpst; j++) {
 +                              pst = (struct pst_s *) p;
 +                              number_scales = pst->numpstates;
 +
 +                              if ((etuple == pst->cpuid) && check_fsb(pst->fsbspeed) &&
 +                                  (maxfid==pst->maxfid) && (startvid==pst->startvid))
 +                              {
 +                                      dprintk ("PST:%d (@%p)\n", j, pst);
 +                                      dprintk (" cpuid: 0x%x  fsb: %d  maxFID: 0x%x  startvid: 0x%x\n",
 +                                               pst->cpuid, pst->fsbspeed, pst->maxfid, pst->startvid);
 +
 +                                      ret = get_ranges ((char *) pst + sizeof (struct pst_s));
 +                                      return ret;
 +                              } else {
 +                                      unsigned int k;
 +                                      p = (char *) pst + sizeof (struct pst_s);
 +                                      for (k=0; k<number_scales; k++)
 +                                              p+=2;
 +                              }
 +                      }
 +                      printk (KERN_INFO PFX "No PST tables match this cpuid (0x%x)\n", etuple);
 +                      printk (KERN_INFO PFX "This is indicative of a broken BIOS.\n");
 +
 +                      return -EINVAL;
 +              }
 +              p++;
 +      }
 +
 +      return -ENODEV;
 +}
 +
 +
 +static int powernow_target (struct cpufreq_policy *policy,
 +                          unsigned int target_freq,
 +                          unsigned int relation)
 +{
 +      unsigned int newstate;
 +
 +      if (cpufreq_frequency_table_target(policy, powernow_table, target_freq, relation, &newstate))
 +              return -EINVAL;
 +
 +      change_speed(newstate);
 +
 +      return 0;
 +}
 +
 +
 +static int powernow_verify (struct cpufreq_policy *policy)
 +{
 +      return cpufreq_frequency_table_verify(policy, powernow_table);
 +}
 +
 +/*
 + * We use the fact that the bus frequency is somehow
 + * a multiple of 100000/3 khz, then we compute sgtc according
 + * to this multiple.
 + * That way, we match more how AMD thinks all of that work.
 + * We will then get the same kind of behaviour already tested under
 + * the "well-known" other OS.
 + */
 +static int __init fixup_sgtc(void)
 +{
 +      unsigned int sgtc;
 +      unsigned int m;
 +
 +      m = fsb / 3333;
 +      if ((m % 10) >= 5)
 +              m += 5;
 +
 +      m /= 10;
 +
 +      sgtc = 100 * m * latency;
 +      sgtc = sgtc / 3;
 +      if (sgtc > 0xfffff) {
 +              printk(KERN_WARNING PFX "SGTC too large %d\n", sgtc);
 +              sgtc = 0xfffff;
 +      }
 +      return sgtc;
 +}
 +
 +static unsigned int powernow_get(unsigned int cpu)
 +{
 +      union msr_fidvidstatus fidvidstatus;
 +      unsigned int cfid;
 +
 +      if (cpu)
 +              return 0;
 +      rdmsrl (MSR_K7_FID_VID_STATUS, fidvidstatus.val);
 +      cfid = fidvidstatus.bits.CFID;
 +
 +      return (fsb * fid_codes[cfid] / 10);
 +}
 +
 +
 +static int __init acer_cpufreq_pst(struct dmi_system_id *d)
 +{
 +      printk(KERN_WARNING "%s laptop with broken PST tables in BIOS detected.\n", d->ident);
 +      printk(KERN_WARNING "You need to downgrade to 3A21 (09/09/2002), or try a newer BIOS than 3A71 (01/20/2003)\n");
 +      printk(KERN_WARNING "cpufreq scaling has been disabled as a result of this.\n");
 +      return 0;
 +}
 +
 +/*
 + * Some Athlon laptops have really fucked PST tables.
 + * A BIOS update is all that can save them.
 + * Mention this, and disable cpufreq.
 + */
 +static struct dmi_system_id __initdata powernow_dmi_table[] = {
 +      {
 +              .callback = acer_cpufreq_pst,
 +              .ident = "Acer Aspire",
 +              .matches = {
 +                      DMI_MATCH(DMI_SYS_VENDOR, "Insyde Software"),
 +                      DMI_MATCH(DMI_BIOS_VERSION, "3A71"),
 +              },
 +      },
 +      { }
 +};
 +
 +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);
 +
 +      recalibrate_cpu_khz();
 +
 +      fsb = (10 * cpu_khz) / fid_codes[fidvidstatus.bits.CFID];
 +      if (!fsb) {
 +              printk(KERN_WARNING PFX "can not determine bus frequency\n");
 +              return -EINVAL;
 +      }
 +      dprintk("FSB: %3dMHz\n", fsb/1000);
 +
 +      if (dmi_check_system(powernow_dmi_table) || acpi_force) {
 +              printk (KERN_INFO PFX "PSB/PST known to be broken.  Trying ACPI instead\n");
 +              result = powernow_acpi_init();
 +      } else {
 +              result = powernow_decode_bios(fidvidstatus.bits.MFID, fidvidstatus.bits.SVID);
 +              if (result) {
 +                      printk (KERN_INFO PFX "Trying ACPI perflib\n");
 +                      maximum_speed = 0;
 +                      minimum_speed = -1;
 +                      latency = 0;
 +                      result = powernow_acpi_init();
 +                      if (result) {
 +                              printk (KERN_INFO PFX "ACPI and legacy methods failed\n");
 +                              printk (KERN_INFO PFX "See http://www.codemonkey.org.uk/projects/cpufreq/powernow-k7.html\n");
 +                      }
 +              } else {
 +                      /* SGTC use the bus clock as timer */
 +                      latency = fixup_sgtc();
 +                      printk(KERN_INFO PFX "SGTC: %d\n", latency);
 +              }
 +      }
 +
 +      if (result)
 +              return result;
 +
 +      printk (KERN_INFO PFX "Minimum speed %d MHz. Maximum speed %d MHz.\n",
 +                              minimum_speed/1000, maximum_speed/1000);
 +
 +      policy->cpuinfo.transition_latency = cpufreq_scale(2000000UL, fsb, latency);
 +
 +      policy->cur = powernow_get(0);
 +
 +      cpufreq_frequency_table_get_attr(powernow_table, policy->cpu);
 +
 +      return cpufreq_frequency_table_cpuinfo(policy, powernow_table);
 +}
 +
 +static int powernow_cpu_exit (struct cpufreq_policy *policy) {
 +      cpufreq_frequency_table_put_attr(policy->cpu);
 +
 +#ifdef CONFIG_X86_POWERNOW_K7_ACPI
 +      if (acpi_processor_perf) {
 +              acpi_processor_unregister_performance(acpi_processor_perf, 0);
 +              kfree(acpi_processor_perf);
 +      }
 +#endif
 +
 +      kfree(powernow_table);
 +      return 0;
 +}
 +
 +static struct freq_attr* powernow_table_attr[] = {
 +      &cpufreq_freq_attr_scaling_available_freqs,
 +      NULL,
 +};
 +
 +static struct cpufreq_driver powernow_driver = {
 +      .verify = powernow_verify,
 +      .target = powernow_target,
 +      .get    = powernow_get,
 +      .init   = powernow_cpu_init,
 +      .exit   = powernow_cpu_exit,
 +      .name   = "powernow-k7",
 +      .owner  = THIS_MODULE,
 +      .attr   = powernow_table_attr,
 +};
 +
 +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);
 +}
 +
 +module_param(acpi_force,  int, 0444);
 +MODULE_PARM_DESC(acpi_force, "Force ACPI to be used.");
 +
 +MODULE_AUTHOR ("Dave Jones <davej@codemonkey.org.uk>");
 +MODULE_DESCRIPTION ("Powernow driver for AMD K7 processors.");
 +MODULE_LICENSE ("GPL");
 +
 +late_initcall(powernow_init);
 +module_exit(powernow_exit);
 +
index 34ed53a067307d78166019961f5e431c26f83c1f,0000000000000000000000000000000000000000..b273b69cfddf0532d18b4db6646fafd077cdc31e
mode 100644,000000..100644
--- /dev/null
@@@ -1,1363 -1,0 +1,1360 @@@
-       return 100 * (fid + 0x10) >> did;
 +/*
 + *   (c) 2003-2006 Advanced Micro Devices, Inc.
 + *  Your use of this code is subject to the terms and conditions of the
 + *  GNU general public license version 2. See "COPYING" or
 + *  http://www.gnu.org/licenses/gpl.html
 + *
 + *  Support : mark.langsdorf@amd.com
 + *
 + *  Based on the powernow-k7.c module written by Dave Jones.
 + *  (C) 2003 Dave Jones <davej@codemonkey.org.uk> on behalf of SuSE Labs
 + *  (C) 2004 Dominik Brodowski <linux@brodo.de>
 + *  (C) 2004 Pavel Machek <pavel@suse.cz>
 + *  Licensed under the terms of the GNU GPL License version 2.
 + *  Based upon datasheets & sample CPUs kindly provided by AMD.
 + *
 + *  Valuable input gratefully received from Dave Jones, Pavel Machek,
 + *  Dominik Brodowski, Jacob Shin, and others.
 + *  Originally developed by Paul Devriendt.
 + *  Processor information obtained from Chapter 9 (Power and Thermal Management)
 + *  of the "BIOS and Kernel Developer's Guide for the AMD Athlon 64 and AMD
 + *  Opteron Processors" available for download from www.amd.com
 + *
 + *  Tables for specific CPUs can be inferred from
 + *     http://www.amd.com/us-en/assets/content_type/white_papers_and_tech_docs/30430.pdf
 + */
 +
 +#include <linux/kernel.h>
 +#include <linux/smp.h>
 +#include <linux/module.h>
 +#include <linux/init.h>
 +#include <linux/cpufreq.h>
 +#include <linux/slab.h>
 +#include <linux/string.h>
 +#include <linux/cpumask.h>
 +#include <linux/sched.h>      /* for current / set_cpus_allowed() */
 +
 +#include <asm/msr.h>
 +#include <asm/io.h>
 +#include <asm/delay.h>
 +
 +#ifdef CONFIG_X86_POWERNOW_K8_ACPI
 +#include <linux/acpi.h>
 +#include <linux/mutex.h>
 +#include <acpi/processor.h>
 +#endif
 +
 +#define PFX "powernow-k8: "
 +#define BFX PFX "BIOS error: "
 +#define VERSION "version 2.00.00"
 +#include "powernow-k8.h"
 +
 +/* serialize freq changes  */
 +static DEFINE_MUTEX(fidvid_mutex);
 +
 +static struct powernow_k8_data *powernow_data[NR_CPUS];
 +
 +static int cpu_family = CPU_OPTERON;
 +
 +#ifndef CONFIG_SMP
 +static cpumask_t cpu_core_map[1];
 +#endif
 +
 +/* Return a frequency in MHz, given an input fid */
 +static u32 find_freq_from_fid(u32 fid)
 +{
 +      return 800 + (fid * 100);
 +}
 +
 +
 +/* Return a frequency in KHz, given an input fid */
 +static u32 find_khz_freq_from_fid(u32 fid)
 +{
 +      return 1000 * find_freq_from_fid(fid);
 +}
 +
 +/* Return a frequency in MHz, given an input fid and did */
 +static u32 find_freq_from_fiddid(u32 fid, u32 did)
 +{
-       pol->governor = CPUFREQ_DEFAULT_GOVERNOR;
++      if (current_cpu_data.x86 == 0x10)
++              return 100 * (fid + 0x10) >> did;
++      else
++              return 100 * (fid + 0x8) >> did;
 +}
 +
 +static u32 find_khz_freq_from_fiddid(u32 fid, u32 did)
 +{
 +      return 1000 * find_freq_from_fiddid(fid, did);
 +}
 +
 +static u32 find_fid_from_pstate(u32 pstate)
 +{
 +      u32 hi, lo;
 +      rdmsr(MSR_PSTATE_DEF_BASE + pstate, lo, hi);
 +      return lo & HW_PSTATE_FID_MASK;
 +}
 +
 +static u32 find_did_from_pstate(u32 pstate)
 +{
 +      u32 hi, lo;
 +      rdmsr(MSR_PSTATE_DEF_BASE + pstate, lo, hi);
 +      return (lo & HW_PSTATE_DID_MASK) >> HW_PSTATE_DID_SHIFT;
 +}
 +
 +/* Return the vco fid for an input fid
 + *
 + * Each "low" fid has corresponding "high" fid, and you can get to "low" fids
 + * only from corresponding high fids. This returns "high" fid corresponding to
 + * "low" one.
 + */
 +static u32 convert_fid_to_vco_fid(u32 fid)
 +{
 +      if (fid < HI_FID_TABLE_BOTTOM)
 +              return 8 + (2 * fid);
 +      else
 +              return fid;
 +}
 +
 +/*
 + * Return 1 if the pending bit is set. Unless we just instructed the processor
 + * to transition to a new state, seeing this bit set is really bad news.
 + */
 +static int pending_bit_stuck(void)
 +{
 +      u32 lo, hi;
 +
 +      if (cpu_family == CPU_HW_PSTATE)
 +              return 0;
 +
 +      rdmsr(MSR_FIDVID_STATUS, lo, hi);
 +      return lo & MSR_S_LO_CHANGE_PENDING ? 1 : 0;
 +}
 +
 +/*
 + * Update the global current fid / vid values from the status msr.
 + * Returns 1 on error.
 + */
 +static int query_current_values_with_pending_wait(struct powernow_k8_data *data)
 +{
 +      u32 lo, hi;
 +      u32 i = 0;
 +
 +      if (cpu_family == CPU_HW_PSTATE) {
 +              rdmsr(MSR_PSTATE_STATUS, lo, hi);
 +              i = lo & HW_PSTATE_MASK;
 +              rdmsr(MSR_PSTATE_DEF_BASE + i, lo, hi);
 +              data->currfid = lo & HW_PSTATE_FID_MASK;
 +              data->currdid = (lo & HW_PSTATE_DID_MASK) >> HW_PSTATE_DID_SHIFT;
 +              return 0;
 +      }
 +      do {
 +              if (i++ > 10000) {
 +                      dprintk("detected change pending stuck\n");
 +                      return 1;
 +              }
 +              rdmsr(MSR_FIDVID_STATUS, lo, hi);
 +      } while (lo & MSR_S_LO_CHANGE_PENDING);
 +
 +      data->currvid = hi & MSR_S_HI_CURRENT_VID;
 +      data->currfid = lo & MSR_S_LO_CURRENT_FID;
 +
 +      return 0;
 +}
 +
 +/* the isochronous relief time */
 +static void count_off_irt(struct powernow_k8_data *data)
 +{
 +      udelay((1 << data->irt) * 10);
 +      return;
 +}
 +
 +/* the voltage stabalization time */
 +static void count_off_vst(struct powernow_k8_data *data)
 +{
 +      udelay(data->vstable * VST_UNITS_20US);
 +      return;
 +}
 +
 +/* need to init the control msr to a safe value (for each cpu) */
 +static void fidvid_msr_init(void)
 +{
 +      u32 lo, hi;
 +      u8 fid, vid;
 +
 +      rdmsr(MSR_FIDVID_STATUS, lo, hi);
 +      vid = hi & MSR_S_HI_CURRENT_VID;
 +      fid = lo & MSR_S_LO_CURRENT_FID;
 +      lo = fid | (vid << MSR_C_LO_VID_SHIFT);
 +      hi = MSR_C_HI_STP_GNT_BENIGN;
 +      dprintk("cpu%d, init lo 0x%x, hi 0x%x\n", smp_processor_id(), lo, hi);
 +      wrmsr(MSR_FIDVID_CTL, lo, hi);
 +}
 +
 +
 +/* write the new fid value along with the other control fields to the msr */
 +static int write_new_fid(struct powernow_k8_data *data, u32 fid)
 +{
 +      u32 lo;
 +      u32 savevid = data->currvid;
 +      u32 i = 0;
 +
 +      if ((fid & INVALID_FID_MASK) || (data->currvid & INVALID_VID_MASK)) {
 +              printk(KERN_ERR PFX "internal error - overflow on fid write\n");
 +              return 1;
 +      }
 +
 +      lo = fid | (data->currvid << MSR_C_LO_VID_SHIFT) | MSR_C_LO_INIT_FID_VID;
 +
 +      dprintk("writing fid 0x%x, lo 0x%x, hi 0x%x\n",
 +              fid, lo, data->plllock * PLL_LOCK_CONVERSION);
 +
 +      do {
 +              wrmsr(MSR_FIDVID_CTL, lo, data->plllock * PLL_LOCK_CONVERSION);
 +              if (i++ > 100) {
 +                      printk(KERN_ERR PFX "Hardware error - pending bit very stuck - no further pstate changes possible\n");
 +                      return 1;
 +              }
 +      } while (query_current_values_with_pending_wait(data));
 +
 +      count_off_irt(data);
 +
 +      if (savevid != data->currvid) {
 +              printk(KERN_ERR PFX "vid change on fid trans, old 0x%x, new 0x%x\n",
 +                     savevid, data->currvid);
 +              return 1;
 +      }
 +
 +      if (fid != data->currfid) {
 +              printk(KERN_ERR PFX "fid trans failed, fid 0x%x, curr 0x%x\n", fid,
 +                      data->currfid);
 +              return 1;
 +      }
 +
 +      return 0;
 +}
 +
 +/* Write a new vid to the hardware */
 +static int write_new_vid(struct powernow_k8_data *data, u32 vid)
 +{
 +      u32 lo;
 +      u32 savefid = data->currfid;
 +      int i = 0;
 +
 +      if ((data->currfid & INVALID_FID_MASK) || (vid & INVALID_VID_MASK)) {
 +              printk(KERN_ERR PFX "internal error - overflow on vid write\n");
 +              return 1;
 +      }
 +
 +      lo = data->currfid | (vid << MSR_C_LO_VID_SHIFT) | MSR_C_LO_INIT_FID_VID;
 +
 +      dprintk("writing vid 0x%x, lo 0x%x, hi 0x%x\n",
 +              vid, lo, STOP_GRANT_5NS);
 +
 +      do {
 +              wrmsr(MSR_FIDVID_CTL, lo, STOP_GRANT_5NS);
 +              if (i++ > 100) {
 +                      printk(KERN_ERR PFX "internal error - pending bit very stuck - no further pstate changes possible\n");
 +                      return 1;
 +              }
 +      } while (query_current_values_with_pending_wait(data));
 +
 +      if (savefid != data->currfid) {
 +              printk(KERN_ERR PFX "fid changed on vid trans, old 0x%x new 0x%x\n",
 +                     savefid, data->currfid);
 +              return 1;
 +      }
 +
 +      if (vid != data->currvid) {
 +              printk(KERN_ERR PFX "vid trans failed, vid 0x%x, curr 0x%x\n", vid,
 +                              data->currvid);
 +              return 1;
 +      }
 +
 +      return 0;
 +}
 +
 +/*
 + * Reduce the vid by the max of step or reqvid.
 + * Decreasing vid codes represent increasing voltages:
 + * vid of 0 is 1.550V, vid of 0x1e is 0.800V, vid of VID_OFF is off.
 + */
 +static int decrease_vid_code_by_step(struct powernow_k8_data *data, u32 reqvid, u32 step)
 +{
 +      if ((data->currvid - reqvid) > step)
 +              reqvid = data->currvid - step;
 +
 +      if (write_new_vid(data, reqvid))
 +              return 1;
 +
 +      count_off_vst(data);
 +
 +      return 0;
 +}
 +
 +/* Change hardware pstate by single MSR write */
 +static int transition_pstate(struct powernow_k8_data *data, u32 pstate)
 +{
 +      wrmsr(MSR_PSTATE_CTRL, pstate, 0);
 +      data->currfid = find_fid_from_pstate(pstate);
 +      return 0;
 +}
 +
 +/* Change Opteron/Athlon64 fid and vid, by the 3 phases. */
 +static int transition_fid_vid(struct powernow_k8_data *data, u32 reqfid, u32 reqvid)
 +{
 +      if (core_voltage_pre_transition(data, reqvid))
 +              return 1;
 +
 +      if (core_frequency_transition(data, reqfid))
 +              return 1;
 +
 +      if (core_voltage_post_transition(data, reqvid))
 +              return 1;
 +
 +      if (query_current_values_with_pending_wait(data))
 +              return 1;
 +
 +      if ((reqfid != data->currfid) || (reqvid != data->currvid)) {
 +              printk(KERN_ERR PFX "failed (cpu%d): req 0x%x 0x%x, curr 0x%x 0x%x\n",
 +                              smp_processor_id(),
 +                              reqfid, reqvid, data->currfid, data->currvid);
 +              return 1;
 +      }
 +
 +      dprintk("transitioned (cpu%d): new fid 0x%x, vid 0x%x\n",
 +              smp_processor_id(), data->currfid, data->currvid);
 +
 +      return 0;
 +}
 +
 +/* Phase 1 - core voltage transition ... setup voltage */
 +static int core_voltage_pre_transition(struct powernow_k8_data *data, u32 reqvid)
 +{
 +      u32 rvosteps = data->rvo;
 +      u32 savefid = data->currfid;
 +      u32 maxvid, lo;
 +
 +      dprintk("ph1 (cpu%d): start, currfid 0x%x, currvid 0x%x, reqvid 0x%x, rvo 0x%x\n",
 +              smp_processor_id(),
 +              data->currfid, data->currvid, reqvid, data->rvo);
 +
 +      rdmsr(MSR_FIDVID_STATUS, lo, maxvid);
 +      maxvid = 0x1f & (maxvid >> 16);
 +      dprintk("ph1 maxvid=0x%x\n", maxvid);
 +      if (reqvid < maxvid) /* lower numbers are higher voltages */
 +              reqvid = maxvid;
 +
 +      while (data->currvid > reqvid) {
 +              dprintk("ph1: curr 0x%x, req vid 0x%x\n",
 +                      data->currvid, reqvid);
 +              if (decrease_vid_code_by_step(data, reqvid, data->vidmvs))
 +                      return 1;
 +      }
 +
 +      while ((rvosteps > 0) && ((data->rvo + data->currvid) > reqvid)) {
 +              if (data->currvid == maxvid) {
 +                      rvosteps = 0;
 +              } else {
 +                      dprintk("ph1: changing vid for rvo, req 0x%x\n",
 +                              data->currvid - 1);
 +                      if (decrease_vid_code_by_step(data, data->currvid - 1, 1))
 +                              return 1;
 +                      rvosteps--;
 +              }
 +      }
 +
 +      if (query_current_values_with_pending_wait(data))
 +              return 1;
 +
 +      if (savefid != data->currfid) {
 +              printk(KERN_ERR PFX "ph1 err, currfid changed 0x%x\n", data->currfid);
 +              return 1;
 +      }
 +
 +      dprintk("ph1 complete, currfid 0x%x, currvid 0x%x\n",
 +              data->currfid, data->currvid);
 +
 +      return 0;
 +}
 +
 +/* Phase 2 - core frequency transition */
 +static int core_frequency_transition(struct powernow_k8_data *data, u32 reqfid)
 +{
 +      u32 vcoreqfid, vcocurrfid, vcofiddiff, fid_interval, savevid = data->currvid;
 +
 +      if ((reqfid < HI_FID_TABLE_BOTTOM) && (data->currfid < HI_FID_TABLE_BOTTOM)) {
 +              printk(KERN_ERR PFX "ph2: illegal lo-lo transition 0x%x 0x%x\n",
 +                      reqfid, data->currfid);
 +              return 1;
 +      }
 +
 +      if (data->currfid == reqfid) {
 +              printk(KERN_ERR PFX "ph2 null fid transition 0x%x\n", data->currfid);
 +              return 0;
 +      }
 +
 +      dprintk("ph2 (cpu%d): starting, currfid 0x%x, currvid 0x%x, reqfid 0x%x\n",
 +              smp_processor_id(),
 +              data->currfid, data->currvid, reqfid);
 +
 +      vcoreqfid = convert_fid_to_vco_fid(reqfid);
 +      vcocurrfid = convert_fid_to_vco_fid(data->currfid);
 +      vcofiddiff = vcocurrfid > vcoreqfid ? vcocurrfid - vcoreqfid
 +          : vcoreqfid - vcocurrfid;
 +
 +      while (vcofiddiff > 2) {
 +              (data->currfid & 1) ? (fid_interval = 1) : (fid_interval = 2);
 +
 +              if (reqfid > data->currfid) {
 +                      if (data->currfid > LO_FID_TABLE_TOP) {
 +                              if (write_new_fid(data, data->currfid + fid_interval)) {
 +                                      return 1;
 +                              }
 +                      } else {
 +                              if (write_new_fid
 +                                  (data, 2 + convert_fid_to_vco_fid(data->currfid))) {
 +                                      return 1;
 +                              }
 +                      }
 +              } else {
 +                      if (write_new_fid(data, data->currfid - fid_interval))
 +                              return 1;
 +              }
 +
 +              vcocurrfid = convert_fid_to_vco_fid(data->currfid);
 +              vcofiddiff = vcocurrfid > vcoreqfid ? vcocurrfid - vcoreqfid
 +                  : vcoreqfid - vcocurrfid;
 +      }
 +
 +      if (write_new_fid(data, reqfid))
 +              return 1;
 +
 +      if (query_current_values_with_pending_wait(data))
 +              return 1;
 +
 +      if (data->currfid != reqfid) {
 +              printk(KERN_ERR PFX
 +                      "ph2: mismatch, failed fid transition, curr 0x%x, req 0x%x\n",
 +                      data->currfid, reqfid);
 +              return 1;
 +      }
 +
 +      if (savevid != data->currvid) {
 +              printk(KERN_ERR PFX "ph2: vid changed, save 0x%x, curr 0x%x\n",
 +                      savevid, data->currvid);
 +              return 1;
 +      }
 +
 +      dprintk("ph2 complete, currfid 0x%x, currvid 0x%x\n",
 +              data->currfid, data->currvid);
 +
 +      return 0;
 +}
 +
 +/* Phase 3 - core voltage transition flow ... jump to the final vid. */
 +static int core_voltage_post_transition(struct powernow_k8_data *data, u32 reqvid)
 +{
 +      u32 savefid = data->currfid;
 +      u32 savereqvid = reqvid;
 +
 +      dprintk("ph3 (cpu%d): starting, currfid 0x%x, currvid 0x%x\n",
 +              smp_processor_id(),
 +              data->currfid, data->currvid);
 +
 +      if (reqvid != data->currvid) {
 +              if (write_new_vid(data, reqvid))
 +                      return 1;
 +
 +              if (savefid != data->currfid) {
 +                      printk(KERN_ERR PFX
 +                             "ph3: bad fid change, save 0x%x, curr 0x%x\n",
 +                             savefid, data->currfid);
 +                      return 1;
 +              }
 +
 +              if (data->currvid != reqvid) {
 +                      printk(KERN_ERR PFX
 +                             "ph3: failed vid transition\n, req 0x%x, curr 0x%x",
 +                             reqvid, data->currvid);
 +                      return 1;
 +              }
 +      }
 +
 +      if (query_current_values_with_pending_wait(data))
 +              return 1;
 +
 +      if (savereqvid != data->currvid) {
 +              dprintk("ph3 failed, currvid 0x%x\n", data->currvid);
 +              return 1;
 +      }
 +
 +      if (savefid != data->currfid) {
 +              dprintk("ph3 failed, currfid changed 0x%x\n",
 +                      data->currfid);
 +              return 1;
 +      }
 +
 +      dprintk("ph3 complete, currfid 0x%x, currvid 0x%x\n",
 +              data->currfid, data->currvid);
 +
 +      return 0;
 +}
 +
 +static int check_supported_cpu(unsigned int cpu)
 +{
 +      cpumask_t oldmask = CPU_MASK_ALL;
 +      u32 eax, ebx, ecx, edx;
 +      unsigned int rc = 0;
 +
 +      oldmask = current->cpus_allowed;
 +      set_cpus_allowed(current, cpumask_of_cpu(cpu));
 +
 +      if (smp_processor_id() != cpu) {
 +              printk(KERN_ERR PFX "limiting to cpu %u failed\n", cpu);
 +              goto out;
 +      }
 +
 +      if (current_cpu_data.x86_vendor != X86_VENDOR_AMD)
 +              goto out;
 +
 +      eax = cpuid_eax(CPUID_PROCESSOR_SIGNATURE);
 +      if (((eax & CPUID_XFAM) != CPUID_XFAM_K8) &&
 +          ((eax & CPUID_XFAM) < CPUID_XFAM_10H))
 +              goto out;
 +
 +      if ((eax & CPUID_XFAM) == CPUID_XFAM_K8) {
 +              if (((eax & CPUID_USE_XFAM_XMOD) != CPUID_USE_XFAM_XMOD) ||
 +                  ((eax & CPUID_XMOD) > CPUID_XMOD_REV_MASK)) {
 +                      printk(KERN_INFO PFX "Processor cpuid %x not supported\n", eax);
 +                      goto out;
 +              }
 +
 +              eax = cpuid_eax(CPUID_GET_MAX_CAPABILITIES);
 +              if (eax < CPUID_FREQ_VOLT_CAPABILITIES) {
 +                      printk(KERN_INFO PFX
 +                             "No frequency change capabilities detected\n");
 +                      goto out;
 +              }
 +
 +              cpuid(CPUID_FREQ_VOLT_CAPABILITIES, &eax, &ebx, &ecx, &edx);
 +              if ((edx & P_STATE_TRANSITION_CAPABLE) != P_STATE_TRANSITION_CAPABLE) {
 +                      printk(KERN_INFO PFX "Power state transitions not supported\n");
 +                      goto out;
 +              }
 +      } else { /* must be a HW Pstate capable processor */
 +              cpuid(CPUID_FREQ_VOLT_CAPABILITIES, &eax, &ebx, &ecx, &edx);
 +              if ((edx & USE_HW_PSTATE) == USE_HW_PSTATE)
 +                      cpu_family = CPU_HW_PSTATE;
 +              else
 +                      goto out;
 +      }
 +
 +      rc = 1;
 +
 +out:
 +      set_cpus_allowed(current, oldmask);
 +      return rc;
 +}
 +
 +static int check_pst_table(struct powernow_k8_data *data, struct pst_s *pst, u8 maxvid)
 +{
 +      unsigned int j;
 +      u8 lastfid = 0xff;
 +
 +      for (j = 0; j < data->numps; j++) {
 +              if (pst[j].vid > LEAST_VID) {
 +                      printk(KERN_ERR PFX "vid %d invalid : 0x%x\n", j, pst[j].vid);
 +                      return -EINVAL;
 +              }
 +              if (pst[j].vid < data->rvo) {   /* vid + rvo >= 0 */
 +                      printk(KERN_ERR BFX "0 vid exceeded with pstate %d\n", j);
 +                      return -ENODEV;
 +              }
 +              if (pst[j].vid < maxvid + data->rvo) {  /* vid + rvo >= maxvid */
 +                      printk(KERN_ERR BFX "maxvid exceeded with pstate %d\n", j);
 +                      return -ENODEV;
 +              }
 +              if (pst[j].fid > MAX_FID) {
 +                      printk(KERN_ERR BFX "maxfid exceeded with pstate %d\n", j);
 +                      return -ENODEV;
 +              }
 +              if (j && (pst[j].fid < HI_FID_TABLE_BOTTOM)) {
 +                      /* Only first fid is allowed to be in "low" range */
 +                      printk(KERN_ERR BFX "two low fids - %d : 0x%x\n", j, pst[j].fid);
 +                      return -EINVAL;
 +              }
 +              if (pst[j].fid < lastfid)
 +                      lastfid = pst[j].fid;
 +      }
 +      if (lastfid & 1) {
 +              printk(KERN_ERR BFX "lastfid invalid\n");
 +              return -EINVAL;
 +      }
 +      if (lastfid > LO_FID_TABLE_TOP)
 +              printk(KERN_INFO BFX  "first fid not from lo freq table\n");
 +
 +      return 0;
 +}
 +
 +static void print_basics(struct powernow_k8_data *data)
 +{
 +      int j;
 +      for (j = 0; j < data->numps; j++) {
 +              if (data->powernow_table[j].frequency != CPUFREQ_ENTRY_INVALID) {
 +                      if (cpu_family == CPU_HW_PSTATE) {
 +                              printk(KERN_INFO PFX "   %d : fid 0x%x did 0x%x (%d MHz)\n",
 +                                      j,
 +                                      (data->powernow_table[j].index & 0xff00) >> 8,
 +                                      (data->powernow_table[j].index & 0xff0000) >> 16,
 +                                      data->powernow_table[j].frequency/1000);
 +                      } else {
 +                              printk(KERN_INFO PFX "   %d : fid 0x%x (%d MHz), vid 0x%x\n",
 +                                      j,
 +                                      data->powernow_table[j].index & 0xff,
 +                                      data->powernow_table[j].frequency/1000,
 +                                      data->powernow_table[j].index >> 8);
 +                      }
 +              }
 +      }
 +      if (data->batps)
 +              printk(KERN_INFO PFX "Only %d pstates on battery\n", data->batps);
 +}
 +
 +static int fill_powernow_table(struct powernow_k8_data *data, struct pst_s *pst, u8 maxvid)
 +{
 +      struct cpufreq_frequency_table *powernow_table;
 +      unsigned int j;
 +
 +      if (data->batps) {    /* use ACPI support to get full speed on mains power */
 +              printk(KERN_WARNING PFX "Only %d pstates usable (use ACPI driver for full range\n", data->batps);
 +              data->numps = data->batps;
 +      }
 +
 +      for ( j=1; j<data->numps; j++ ) {
 +              if (pst[j-1].fid >= pst[j].fid) {
 +                      printk(KERN_ERR PFX "PST out of sequence\n");
 +                      return -EINVAL;
 +              }
 +      }
 +
 +      if (data->numps < 2) {
 +              printk(KERN_ERR PFX "no p states to transition\n");
 +              return -ENODEV;
 +      }
 +
 +      if (check_pst_table(data, pst, maxvid))
 +              return -EINVAL;
 +
 +      powernow_table = kmalloc((sizeof(struct cpufreq_frequency_table)
 +              * (data->numps + 1)), GFP_KERNEL);
 +      if (!powernow_table) {
 +              printk(KERN_ERR PFX "powernow_table memory alloc failure\n");
 +              return -ENOMEM;
 +      }
 +
 +      for (j = 0; j < data->numps; j++) {
 +              powernow_table[j].index = pst[j].fid; /* lower 8 bits */
 +              powernow_table[j].index |= (pst[j].vid << 8); /* upper 8 bits */
 +              powernow_table[j].frequency = find_khz_freq_from_fid(pst[j].fid);
 +      }
 +      powernow_table[data->numps].frequency = CPUFREQ_TABLE_END;
 +      powernow_table[data->numps].index = 0;
 +
 +      if (query_current_values_with_pending_wait(data)) {
 +              kfree(powernow_table);
 +              return -EIO;
 +      }
 +
 +      dprintk("cfid 0x%x, cvid 0x%x\n", data->currfid, data->currvid);
 +      data->powernow_table = powernow_table;
 +      if (first_cpu(cpu_core_map[data->cpu]) == data->cpu)
 +              print_basics(data);
 +
 +      for (j = 0; j < data->numps; j++)
 +              if ((pst[j].fid==data->currfid) && (pst[j].vid==data->currvid))
 +                      return 0;
 +
 +      dprintk("currfid/vid do not match PST, ignoring\n");
 +      return 0;
 +}
 +
 +/* Find and validate the PSB/PST table in BIOS. */
 +static int find_psb_table(struct powernow_k8_data *data)
 +{
 +      struct psb_s *psb;
 +      unsigned int i;
 +      u32 mvs;
 +      u8 maxvid;
 +      u32 cpst = 0;
 +      u32 thiscpuid;
 +
 +      for (i = 0xc0000; i < 0xffff0; i += 0x10) {
 +              /* Scan BIOS looking for the signature. */
 +              /* It can not be at ffff0 - it is too big. */
 +
 +              psb = phys_to_virt(i);
 +              if (memcmp(psb, PSB_ID_STRING, PSB_ID_STRING_LEN) != 0)
 +                      continue;
 +
 +              dprintk("found PSB header at 0x%p\n", psb);
 +
 +              dprintk("table vers: 0x%x\n", psb->tableversion);
 +              if (psb->tableversion != PSB_VERSION_1_4) {
 +                      printk(KERN_ERR BFX "PSB table is not v1.4\n");
 +                      return -ENODEV;
 +              }
 +
 +              dprintk("flags: 0x%x\n", psb->flags1);
 +              if (psb->flags1) {
 +                      printk(KERN_ERR BFX "unknown flags\n");
 +                      return -ENODEV;
 +              }
 +
 +              data->vstable = psb->vstable;
 +              dprintk("voltage stabilization time: %d(*20us)\n", data->vstable);
 +
 +              dprintk("flags2: 0x%x\n", psb->flags2);
 +              data->rvo = psb->flags2 & 3;
 +              data->irt = ((psb->flags2) >> 2) & 3;
 +              mvs = ((psb->flags2) >> 4) & 3;
 +              data->vidmvs = 1 << mvs;
 +              data->batps = ((psb->flags2) >> 6) & 3;
 +
 +              dprintk("ramp voltage offset: %d\n", data->rvo);
 +              dprintk("isochronous relief time: %d\n", data->irt);
 +              dprintk("maximum voltage step: %d - 0x%x\n", mvs, data->vidmvs);
 +
 +              dprintk("numpst: 0x%x\n", psb->num_tables);
 +              cpst = psb->num_tables;
 +              if ((psb->cpuid == 0x00000fc0) || (psb->cpuid == 0x00000fe0) ){
 +                      thiscpuid = cpuid_eax(CPUID_PROCESSOR_SIGNATURE);
 +                      if ((thiscpuid == 0x00000fc0) || (thiscpuid == 0x00000fe0) ) {
 +                              cpst = 1;
 +                      }
 +              }
 +              if (cpst != 1) {
 +                      printk(KERN_ERR BFX "numpst must be 1\n");
 +                      return -ENODEV;
 +              }
 +
 +              data->plllock = psb->plllocktime;
 +              dprintk("plllocktime: 0x%x (units 1us)\n", psb->plllocktime);
 +              dprintk("maxfid: 0x%x\n", psb->maxfid);
 +              dprintk("maxvid: 0x%x\n", psb->maxvid);
 +              maxvid = psb->maxvid;
 +
 +              data->numps = psb->numps;
 +              dprintk("numpstates: 0x%x\n", data->numps);
 +              return fill_powernow_table(data, (struct pst_s *)(psb+1), maxvid);
 +      }
 +      /*
 +       * If you see this message, complain to BIOS manufacturer. If
 +       * he tells you "we do not support Linux" or some similar
 +       * nonsense, remember that Windows 2000 uses the same legacy
 +       * mechanism that the old Linux PSB driver uses. Tell them it
 +       * is broken with Windows 2000.
 +       *
 +       * The reference to the AMD documentation is chapter 9 in the
 +       * BIOS and Kernel Developer's Guide, which is available on
 +       * www.amd.com
 +       */
 +      printk(KERN_ERR PFX "BIOS error - no PSB or ACPI _PSS objects\n");
 +      return -ENODEV;
 +}
 +
 +#ifdef CONFIG_X86_POWERNOW_K8_ACPI
 +static void powernow_k8_acpi_pst_values(struct powernow_k8_data *data, unsigned int index)
 +{
 +      if (!data->acpi_data.state_count || (cpu_family == CPU_HW_PSTATE))
 +              return;
 +
 +      data->irt = (data->acpi_data.states[index].control >> IRT_SHIFT) & IRT_MASK;
 +      data->rvo = (data->acpi_data.states[index].control >> RVO_SHIFT) & RVO_MASK;
 +      data->exttype = (data->acpi_data.states[index].control >> EXT_TYPE_SHIFT) & EXT_TYPE_MASK;
 +      data->plllock = (data->acpi_data.states[index].control >> PLL_L_SHIFT) & PLL_L_MASK;
 +      data->vidmvs = 1 << ((data->acpi_data.states[index].control >> MVS_SHIFT) & MVS_MASK);
 +      data->vstable = (data->acpi_data.states[index].control >> VST_SHIFT) & VST_MASK;
 +}
 +
 +static int powernow_k8_cpu_init_acpi(struct powernow_k8_data *data)
 +{
 +      struct cpufreq_frequency_table *powernow_table;
 +      int ret_val;
 +
 +      if (acpi_processor_register_performance(&data->acpi_data, data->cpu)) {
 +              dprintk("register performance failed: bad ACPI data\n");
 +              return -EIO;
 +      }
 +
 +      /* verify the data contained in the ACPI structures */
 +      if (data->acpi_data.state_count <= 1) {
 +              dprintk("No ACPI P-States\n");
 +              goto err_out;
 +      }
 +
 +      if ((data->acpi_data.control_register.space_id != ACPI_ADR_SPACE_FIXED_HARDWARE) ||
 +              (data->acpi_data.status_register.space_id != ACPI_ADR_SPACE_FIXED_HARDWARE)) {
 +              dprintk("Invalid control/status registers (%x - %x)\n",
 +                      data->acpi_data.control_register.space_id,
 +                      data->acpi_data.status_register.space_id);
 +              goto err_out;
 +      }
 +
 +      /* fill in data->powernow_table */
 +      powernow_table = kmalloc((sizeof(struct cpufreq_frequency_table)
 +              * (data->acpi_data.state_count + 1)), GFP_KERNEL);
 +      if (!powernow_table) {
 +              dprintk("powernow_table memory alloc failure\n");
 +              goto err_out;
 +      }
 +
 +      if (cpu_family == CPU_HW_PSTATE)
 +              ret_val = fill_powernow_table_pstate(data, powernow_table);
 +      else
 +              ret_val = fill_powernow_table_fidvid(data, powernow_table);
 +      if (ret_val)
 +              goto err_out_mem;
 +
 +      powernow_table[data->acpi_data.state_count].frequency = CPUFREQ_TABLE_END;
 +      powernow_table[data->acpi_data.state_count].index = 0;
 +      data->powernow_table = powernow_table;
 +
 +      /* fill in data */
 +      data->numps = data->acpi_data.state_count;
 +      if (first_cpu(cpu_core_map[data->cpu]) == data->cpu)
 +              print_basics(data);
 +      powernow_k8_acpi_pst_values(data, 0);
 +
 +      /* notify BIOS that we exist */
 +      acpi_processor_notify_smm(THIS_MODULE);
 +
 +      return 0;
 +
 +err_out_mem:
 +      kfree(powernow_table);
 +
 +err_out:
 +      acpi_processor_unregister_performance(&data->acpi_data, data->cpu);
 +
 +      /* data->acpi_data.state_count informs us at ->exit() whether ACPI was used */
 +      data->acpi_data.state_count = 0;
 +
 +      return -ENODEV;
 +}
 +
 +static int fill_powernow_table_pstate(struct powernow_k8_data *data, struct cpufreq_frequency_table *powernow_table)
 +{
 +      int i;
 +
 +      for (i = 0; i < data->acpi_data.state_count; i++) {
 +              u32 index;
 +              u32 hi = 0, lo = 0;
 +              u32 fid;
 +              u32 did;
 +
 +              index = data->acpi_data.states[i].control & HW_PSTATE_MASK;
 +              if (index > MAX_HW_PSTATE) {
 +                      printk(KERN_ERR PFX "invalid pstate %d - bad value %d.\n", i, index);
 +                      printk(KERN_ERR PFX "Please report to BIOS manufacturer\n");
 +              }
 +              rdmsr(MSR_PSTATE_DEF_BASE + index, lo, hi);
 +              if (!(hi & HW_PSTATE_VALID_MASK)) {
 +                      dprintk("invalid pstate %d, ignoring\n", index);
 +                      powernow_table[i].frequency = CPUFREQ_ENTRY_INVALID;
 +                      continue;
 +              }
 +
 +              fid = lo & HW_PSTATE_FID_MASK;
 +              did = (lo & HW_PSTATE_DID_MASK) >> HW_PSTATE_DID_SHIFT;
 +
 +              dprintk("   %d : fid 0x%x, did 0x%x\n", index, fid, did);
 +
 +              powernow_table[i].index = index | (fid << HW_FID_INDEX_SHIFT) | (did << HW_DID_INDEX_SHIFT);
 +
 +              powernow_table[i].frequency = find_khz_freq_from_fiddid(fid, did);
 +
 +              if (powernow_table[i].frequency != (data->acpi_data.states[i].core_frequency * 1000)) {
 +                      printk(KERN_INFO PFX "invalid freq entries %u kHz vs. %u kHz\n",
 +                              powernow_table[i].frequency,
 +                              (unsigned int) (data->acpi_data.states[i].core_frequency * 1000));
 +                      powernow_table[i].frequency = CPUFREQ_ENTRY_INVALID;
 +                      continue;
 +              }
 +      }
 +      return 0;
 +}
 +
 +static int fill_powernow_table_fidvid(struct powernow_k8_data *data, struct cpufreq_frequency_table *powernow_table)
 +{
 +      int i;
 +      int cntlofreq = 0;
 +      for (i = 0; i < data->acpi_data.state_count; i++) {
 +              u32 fid;
 +              u32 vid;
 +
 +              if (data->exttype) {
 +                      fid = data->acpi_data.states[i].status & EXT_FID_MASK;
 +                      vid = (data->acpi_data.states[i].status >> VID_SHIFT) & EXT_VID_MASK;
 +              } else {
 +                      fid = data->acpi_data.states[i].control & FID_MASK;
 +                      vid = (data->acpi_data.states[i].control >> VID_SHIFT) & VID_MASK;
 +              }
 +
 +              dprintk("   %d : fid 0x%x, vid 0x%x\n", i, fid, vid);
 +
 +              powernow_table[i].index = fid; /* lower 8 bits */
 +              powernow_table[i].index |= (vid << 8); /* upper 8 bits */
 +              powernow_table[i].frequency = find_khz_freq_from_fid(fid);
 +
 +              /* verify frequency is OK */
 +              if ((powernow_table[i].frequency > (MAX_FREQ * 1000)) ||
 +                      (powernow_table[i].frequency < (MIN_FREQ * 1000))) {
 +                      dprintk("invalid freq %u kHz, ignoring\n", powernow_table[i].frequency);
 +                      powernow_table[i].frequency = CPUFREQ_ENTRY_INVALID;
 +                      continue;
 +              }
 +
 +              /* verify voltage is OK - BIOSs are using "off" to indicate invalid */
 +              if (vid == VID_OFF) {
 +                      dprintk("invalid vid %u, ignoring\n", vid);
 +                      powernow_table[i].frequency = CPUFREQ_ENTRY_INVALID;
 +                      continue;
 +              }
 +
 +              /* verify only 1 entry from the lo frequency table */
 +              if (fid < HI_FID_TABLE_BOTTOM) {
 +                      if (cntlofreq) {
 +                              /* if both entries are the same, ignore this one ... */
 +                              if ((powernow_table[i].frequency != powernow_table[cntlofreq].frequency) ||
 +                                  (powernow_table[i].index != powernow_table[cntlofreq].index)) {
 +                                      printk(KERN_ERR PFX "Too many lo freq table entries\n");
 +                                      return 1;
 +                              }
 +
 +                              dprintk("double low frequency table entry, ignoring it.\n");
 +                              powernow_table[i].frequency = CPUFREQ_ENTRY_INVALID;
 +                              continue;
 +                      } else
 +                              cntlofreq = i;
 +              }
 +
 +              if (powernow_table[i].frequency != (data->acpi_data.states[i].core_frequency * 1000)) {
 +                      printk(KERN_INFO PFX "invalid freq entries %u kHz vs. %u kHz\n",
 +                              powernow_table[i].frequency,
 +                              (unsigned int) (data->acpi_data.states[i].core_frequency * 1000));
 +                      powernow_table[i].frequency = CPUFREQ_ENTRY_INVALID;
 +                      continue;
 +              }
 +      }
 +      return 0;
 +}
 +
 +static void powernow_k8_cpu_exit_acpi(struct powernow_k8_data *data)
 +{
 +      if (data->acpi_data.state_count)
 +              acpi_processor_unregister_performance(&data->acpi_data, data->cpu);
 +}
 +
 +#else
 +static int powernow_k8_cpu_init_acpi(struct powernow_k8_data *data) { return -ENODEV; }
 +static void powernow_k8_cpu_exit_acpi(struct powernow_k8_data *data) { return; }
 +static void powernow_k8_acpi_pst_values(struct powernow_k8_data *data, unsigned int index) { return; }
 +#endif /* CONFIG_X86_POWERNOW_K8_ACPI */
 +
 +/* Take a frequency, and issue the fid/vid transition command */
 +static int transition_frequency_fidvid(struct powernow_k8_data *data, unsigned int index)
 +{
 +      u32 fid = 0;
 +      u32 vid = 0;
 +      int res, i;
 +      struct cpufreq_freqs freqs;
 +
 +      dprintk("cpu %d transition to index %u\n", smp_processor_id(), index);
 +
 +      /* fid/vid correctness check for k8 */
 +      /* fid are the lower 8 bits of the index we stored into
 +       * the cpufreq frequency table in find_psb_table, vid
 +       * are the upper 8 bits.
 +       */
 +      fid = data->powernow_table[index].index & 0xFF;
 +      vid = (data->powernow_table[index].index & 0xFF00) >> 8;
 +
 +      dprintk("table matched fid 0x%x, giving vid 0x%x\n", fid, vid);
 +
 +      if (query_current_values_with_pending_wait(data))
 +              return 1;
 +
 +      if ((data->currvid == vid) && (data->currfid == fid)) {
 +              dprintk("target matches current values (fid 0x%x, vid 0x%x)\n",
 +                      fid, vid);
 +              return 0;
 +      }
 +
 +      if ((fid < HI_FID_TABLE_BOTTOM) && (data->currfid < HI_FID_TABLE_BOTTOM)) {
 +              printk(KERN_ERR PFX
 +                     "ignoring illegal change in lo freq table-%x to 0x%x\n",
 +                     data->currfid, fid);
 +              return 1;
 +      }
 +
 +      dprintk("cpu %d, changing to fid 0x%x, vid 0x%x\n",
 +              smp_processor_id(), fid, vid);
 +      freqs.old = find_khz_freq_from_fid(data->currfid);
 +      freqs.new = find_khz_freq_from_fid(fid);
 +
 +      for_each_cpu_mask(i, *(data->available_cores)) {
 +              freqs.cpu = i;
 +              cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
 +      }
 +
 +      res = transition_fid_vid(data, fid, vid);
 +      freqs.new = find_khz_freq_from_fid(data->currfid);
 +
 +      for_each_cpu_mask(i, *(data->available_cores)) {
 +              freqs.cpu = i;
 +              cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
 +      }
 +      return res;
 +}
 +
 +/* Take a frequency, and issue the hardware pstate transition command */
 +static int transition_frequency_pstate(struct powernow_k8_data *data, unsigned int index)
 +{
 +      u32 fid = 0;
 +      u32 did = 0;
 +      u32 pstate = 0;
 +      int res, i;
 +      struct cpufreq_freqs freqs;
 +
 +      dprintk("cpu %d transition to index %u\n", smp_processor_id(), index);
 +
 +      /* get fid did for hardware pstate transition */
 +      pstate = index & HW_PSTATE_MASK;
 +      if (pstate > MAX_HW_PSTATE)
 +              return 0;
 +      fid = (index & HW_FID_INDEX_MASK) >> HW_FID_INDEX_SHIFT;
 +      did = (index & HW_DID_INDEX_MASK) >> HW_DID_INDEX_SHIFT;
 +      freqs.old = find_khz_freq_from_fiddid(data->currfid, data->currdid);
 +      freqs.new = find_khz_freq_from_fiddid(fid, did);
 +
 +      for_each_cpu_mask(i, *(data->available_cores)) {
 +              freqs.cpu = i;
 +              cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
 +      }
 +
 +      res = transition_pstate(data, pstate);
 +      data->currfid = find_fid_from_pstate(pstate);
 +      data->currdid = find_did_from_pstate(pstate);
 +      freqs.new = find_khz_freq_from_fiddid(data->currfid, data->currdid);
 +
 +      for_each_cpu_mask(i, *(data->available_cores)) {
 +              freqs.cpu = i;
 +              cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
 +      }
 +      return res;
 +}
 +
 +/* Driver entry point to switch to the target frequency */
 +static int powernowk8_target(struct cpufreq_policy *pol, unsigned targfreq, unsigned relation)
 +{
 +      cpumask_t oldmask = CPU_MASK_ALL;
 +      struct powernow_k8_data *data = powernow_data[pol->cpu];
 +      u32 checkfid;
 +      u32 checkvid;
 +      unsigned int newstate;
 +      int ret = -EIO;
 +
 +      if (!data)
 +              return -EINVAL;
 +
 +      checkfid = data->currfid;
 +      checkvid = data->currvid;
 +
 +      /* only run on specific CPU from here on */
 +      oldmask = current->cpus_allowed;
 +      set_cpus_allowed(current, cpumask_of_cpu(pol->cpu));
 +
 +      if (smp_processor_id() != pol->cpu) {
 +              printk(KERN_ERR PFX "limiting to cpu %u failed\n", pol->cpu);
 +              goto err_out;
 +      }
 +
 +      if (pending_bit_stuck()) {
 +              printk(KERN_ERR PFX "failing targ, change pending bit set\n");
 +              goto err_out;
 +      }
 +
 +      dprintk("targ: cpu %d, %d kHz, min %d, max %d, relation %d\n",
 +              pol->cpu, targfreq, pol->min, pol->max, relation);
 +
 +      if (query_current_values_with_pending_wait(data))
 +              goto err_out;
 +
 +      if (cpu_family == CPU_HW_PSTATE)
 +              dprintk("targ: curr fid 0x%x, did 0x%x\n",
 +                      data->currfid, data->currdid);
 +      else {
 +              dprintk("targ: curr fid 0x%x, vid 0x%x\n",
 +              data->currfid, data->currvid);
 +
 +              if ((checkvid != data->currvid) || (checkfid != data->currfid)) {
 +                      printk(KERN_INFO PFX
 +                              "error - out of sync, fix 0x%x 0x%x, vid 0x%x 0x%x\n",
 +                              checkfid, data->currfid, checkvid, data->currvid);
 +              }
 +      }
 +
 +      if (cpufreq_frequency_table_target(pol, data->powernow_table, targfreq, relation, &newstate))
 +              goto err_out;
 +
 +      mutex_lock(&fidvid_mutex);
 +
 +      powernow_k8_acpi_pst_values(data, newstate);
 +
 +      if (cpu_family == CPU_HW_PSTATE)
 +              ret = transition_frequency_pstate(data, newstate);
 +      else
 +              ret = transition_frequency_fidvid(data, newstate);
 +      if (ret) {
 +              printk(KERN_ERR PFX "transition frequency failed\n");
 +              ret = 1;
 +              mutex_unlock(&fidvid_mutex);
 +              goto err_out;
 +      }
 +      mutex_unlock(&fidvid_mutex);
 +
 +      if (cpu_family == CPU_HW_PSTATE)
 +              pol->cur = find_khz_freq_from_fiddid(data->currfid, data->currdid);
 +      else
 +              pol->cur = find_khz_freq_from_fid(data->currfid);
 +      ret = 0;
 +
 +err_out:
 +      set_cpus_allowed(current, oldmask);
 +      return ret;
 +}
 +
 +/* Driver entry point to verify the policy and range of frequencies */
 +static int powernowk8_verify(struct cpufreq_policy *pol)
 +{
 +      struct powernow_k8_data *data = powernow_data[pol->cpu];
 +
 +      if (!data)
 +              return -EINVAL;
 +
 +      return cpufreq_frequency_table_verify(pol, data->powernow_table);
 +}
 +
 +/* per CPU init entry point to the driver */
 +static int __cpuinit powernowk8_cpu_init(struct cpufreq_policy *pol)
 +{
 +      struct powernow_k8_data *data;
 +      cpumask_t oldmask = CPU_MASK_ALL;
 +      int rc;
 +
 +      if (!cpu_online(pol->cpu))
 +              return -ENODEV;
 +
 +      if (!check_supported_cpu(pol->cpu))
 +              return -ENODEV;
 +
 +      data = kzalloc(sizeof(struct powernow_k8_data), GFP_KERNEL);
 +      if (!data) {
 +              printk(KERN_ERR PFX "unable to alloc powernow_k8_data");
 +              return -ENOMEM;
 +      }
 +
 +      data->cpu = pol->cpu;
 +
 +      if (powernow_k8_cpu_init_acpi(data)) {
 +              /*
 +               * Use the PSB BIOS structure. This is only availabe on
 +               * an UP version, and is deprecated by AMD.
 +               */
 +              if (num_online_cpus() != 1) {
 +                      printk(KERN_ERR PFX "MP systems not supported by PSB BIOS structure\n");
 +                      kfree(data);
 +                      return -ENODEV;
 +              }
 +              if (pol->cpu != 0) {
 +                      printk(KERN_ERR PFX "No _PSS objects for CPU other than CPU0\n");
 +                      kfree(data);
 +                      return -ENODEV;
 +              }
 +              rc = find_psb_table(data);
 +              if (rc) {
 +                      kfree(data);
 +                      return -ENODEV;
 +              }
 +      }
 +
 +      /* only run on specific CPU from here on */
 +      oldmask = current->cpus_allowed;
 +      set_cpus_allowed(current, cpumask_of_cpu(pol->cpu));
 +
 +      if (smp_processor_id() != pol->cpu) {
 +              printk(KERN_ERR PFX "limiting to cpu %u failed\n", pol->cpu);
 +              goto err_out;
 +      }
 +
 +      if (pending_bit_stuck()) {
 +              printk(KERN_ERR PFX "failing init, change pending bit set\n");
 +              goto err_out;
 +      }
 +
 +      if (query_current_values_with_pending_wait(data))
 +              goto err_out;
 +
 +      if (cpu_family == CPU_OPTERON)
 +              fidvid_msr_init();
 +
 +      /* run on any CPU again */
 +      set_cpus_allowed(current, oldmask);
 +
-       unsigned int booted_cores = 1;
 +      if (cpu_family == CPU_HW_PSTATE)
 +              pol->cpus = cpumask_of_cpu(pol->cpu);
 +      else
 +              pol->cpus = cpu_core_map[pol->cpu];
 +      data->available_cores = &(pol->cpus);
 +
 +      /* Take a crude guess here.
 +       * That guess was in microseconds, so multiply with 1000 */
 +      pol->cpuinfo.transition_latency = (((data->rvo + 8) * data->vstable * VST_UNITS_20US)
 +          + (3 * (1 << data->irt) * 10)) * 1000;
 +
 +      if (cpu_family == CPU_HW_PSTATE)
 +              pol->cur = find_khz_freq_from_fiddid(data->currfid, data->currdid);
 +      else
 +              pol->cur = find_khz_freq_from_fid(data->currfid);
 +      dprintk("policy current frequency %d kHz\n", pol->cur);
 +
 +      /* min/max the cpu is capable of */
 +      if (cpufreq_frequency_table_cpuinfo(pol, data->powernow_table)) {
 +              printk(KERN_ERR PFX "invalid powernow_table\n");
 +              powernow_k8_cpu_exit_acpi(data);
 +              kfree(data->powernow_table);
 +              kfree(data);
 +              return -EINVAL;
 +      }
 +
 +      cpufreq_frequency_table_get_attr(data->powernow_table, pol->cpu);
 +
 +      if (cpu_family == CPU_HW_PSTATE)
 +              dprintk("cpu_init done, current fid 0x%x, did 0x%x\n",
 +                      data->currfid, data->currdid);
 +      else
 +              dprintk("cpu_init done, current fid 0x%x, vid 0x%x\n",
 +                      data->currfid, data->currvid);
 +
 +      powernow_data[pol->cpu] = data;
 +
 +      return 0;
 +
 +err_out:
 +      set_cpus_allowed(current, oldmask);
 +      powernow_k8_cpu_exit_acpi(data);
 +
 +      kfree(data);
 +      return -ENODEV;
 +}
 +
 +static int __devexit powernowk8_cpu_exit (struct cpufreq_policy *pol)
 +{
 +      struct powernow_k8_data *data = powernow_data[pol->cpu];
 +
 +      if (!data)
 +              return -EINVAL;
 +
 +      powernow_k8_cpu_exit_acpi(data);
 +
 +      cpufreq_frequency_table_put_attr(pol->cpu);
 +
 +      kfree(data->powernow_table);
 +      kfree(data);
 +
 +      return 0;
 +}
 +
 +static unsigned int powernowk8_get (unsigned int cpu)
 +{
 +      struct powernow_k8_data *data;
 +      cpumask_t oldmask = current->cpus_allowed;
 +      unsigned int khz = 0;
 +
 +      data = powernow_data[first_cpu(cpu_core_map[cpu])];
 +
 +      if (!data)
 +              return -EINVAL;
 +
 +      set_cpus_allowed(current, cpumask_of_cpu(cpu));
 +      if (smp_processor_id() != cpu) {
 +              printk(KERN_ERR PFX "limiting to CPU %d failed in powernowk8_get\n", cpu);
 +              set_cpus_allowed(current, oldmask);
 +              return 0;
 +      }
 +
 +      if (query_current_values_with_pending_wait(data))
 +              goto out;
 +
 +      if (cpu_family == CPU_HW_PSTATE)
 +              khz = find_khz_freq_from_fiddid(data->currfid, data->currdid);
 +      else
 +              khz = find_khz_freq_from_fid(data->currfid);
 +
 +
 +out:
 +      set_cpus_allowed(current, oldmask);
 +      return khz;
 +}
 +
 +static struct freq_attr* powernow_k8_attr[] = {
 +      &cpufreq_freq_attr_scaling_available_freqs,
 +      NULL,
 +};
 +
 +static struct cpufreq_driver cpufreq_amd64_driver = {
 +      .verify = powernowk8_verify,
 +      .target = powernowk8_target,
 +      .init = powernowk8_cpu_init,
 +      .exit = __devexit_p(powernowk8_cpu_exit),
 +      .get = powernowk8_get,
 +      .name = "powernow-k8",
 +      .owner = THIS_MODULE,
 +      .attr = powernow_k8_attr,
 +};
 +
 +/* driver entry point for init */
 +static int __cpuinit powernowk8_init(void)
 +{
 +      unsigned int i, supported_cpus = 0;
- #ifdef CONFIG_SMP
-       booted_cores = cpu_data[0].booted_cores;
- #endif
 +
 +      for_each_online_cpu(i) {
 +              if (check_supported_cpu(i))
 +                      supported_cpus++;
 +      }
 +
-                       supported_cpus/booted_cores,
 +      if (supported_cpus == num_online_cpus()) {
 +              printk(KERN_INFO PFX "Found %d %s "
 +                      "processors (%d cpu cores) (" VERSION ")\n",
++                      num_online_nodes(),
 +                      boot_cpu_data.x86_model_id, supported_cpus);
 +              return cpufreq_register_driver(&cpufreq_amd64_driver);
 +      }
 +
 +      return -ENODEV;
 +}
 +
 +/* driver entry point for term */
 +static void __exit powernowk8_exit(void)
 +{
 +      dprintk("exit\n");
 +
 +      cpufreq_unregister_driver(&cpufreq_amd64_driver);
 +}
 +
 +MODULE_AUTHOR("Paul Devriendt <paul.devriendt@amd.com> and Mark Langsdorf <mark.langsdorf@amd.com>");
 +MODULE_DESCRIPTION("AMD Athlon 64 and Opteron processor frequency driver.");
 +MODULE_LICENSE("GPL");
 +
 +late_initcall(powernowk8_init);
 +module_exit(powernowk8_exit);
index b8fb4b521c62806a2ec8e3134a281590e0dc8b7a,0000000000000000000000000000000000000000..d9f3e90a7ae080c9ba1e316c56ccb3afe6022231
mode 100644,000000..100644
--- /dev/null
@@@ -1,191 -1,0 +1,190 @@@
-       policy->governor = CPUFREQ_DEFAULT_GOVERNOR;
 +/*
 + *    sc520_freq.c: cpufreq driver for the AMD Elan sc520
 + *
 + *    Copyright (C) 2005 Sean Young <sean@mess.org>
 + *
 + *    This program is free software; you can redistribute it and/or
 + *    modify it under the terms of the GNU General Public License
 + *    as published by the Free Software Foundation; either version
 + *    2 of the License, or (at your option) any later version.
 + *
 + *    Based on elanfreq.c
 + *
 + *    2005-03-30: - initial revision
 + */
 +
 +#include <linux/kernel.h>
 +#include <linux/module.h>
 +#include <linux/init.h>
 +
 +#include <linux/delay.h>
 +#include <linux/cpufreq.h>
 +
 +#include <asm/msr.h>
 +#include <asm/timex.h>
 +#include <asm/io.h>
 +
 +#define MMCR_BASE     0xfffef000      /* The default base address */
 +#define OFFS_CPUCTL   0x2   /* CPU Control Register */
 +
 +static __u8 __iomem *cpuctl;
 +
 +#define dprintk(msg...) cpufreq_debug_printk(CPUFREQ_DEBUG_DRIVER, "sc520_freq", msg)
 +
 +static struct cpufreq_frequency_table sc520_freq_table[] = {
 +      {0x01,  100000},
 +      {0x02,  133000},
 +      {0,     CPUFREQ_TABLE_END},
 +};
 +
 +static unsigned int sc520_freq_get_cpu_frequency(unsigned int cpu)
 +{
 +      u8 clockspeed_reg = *cpuctl;
 +
 +      switch (clockspeed_reg & 0x03) {
 +      default:
 +              printk(KERN_ERR "sc520_freq: error: cpuctl register has unexpected value %02x\n", clockspeed_reg);
 +      case 0x01:
 +              return 100000;
 +      case 0x02:
 +              return 133000;
 +      }
 +}
 +
 +static void sc520_freq_set_cpu_state (unsigned int state)
 +{
 +
 +      struct cpufreq_freqs    freqs;
 +      u8 clockspeed_reg;
 +
 +      freqs.old = sc520_freq_get_cpu_frequency(0);
 +      freqs.new = sc520_freq_table[state].frequency;
 +      freqs.cpu = 0; /* AMD Elan is UP */
 +
 +      cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
 +
 +      dprintk("attempting to set frequency to %i kHz\n",
 +                      sc520_freq_table[state].frequency);
 +
 +      local_irq_disable();
 +
 +      clockspeed_reg = *cpuctl & ~0x03;
 +      *cpuctl = clockspeed_reg | sc520_freq_table[state].index;
 +
 +      local_irq_enable();
 +
 +      cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
 +};
 +
 +static int sc520_freq_verify (struct cpufreq_policy *policy)
 +{
 +      return cpufreq_frequency_table_verify(policy, &sc520_freq_table[0]);
 +}
 +
 +static int sc520_freq_target (struct cpufreq_policy *policy,
 +                          unsigned int target_freq,
 +                          unsigned int relation)
 +{
 +      unsigned int newstate = 0;
 +
 +      if (cpufreq_frequency_table_target(policy, sc520_freq_table, target_freq, relation, &newstate))
 +              return -EINVAL;
 +
 +      sc520_freq_set_cpu_state(newstate);
 +
 +      return 0;
 +}
 +
 +
 +/*
 + *    Module init and exit code
 + */
 +
 +static int sc520_freq_cpu_init(struct cpufreq_policy *policy)
 +{
 +      struct cpuinfo_x86 *c = cpu_data;
 +      int result;
 +
 +      /* capability check */
 +      if (c->x86_vendor != X86_VENDOR_AMD ||
 +          c->x86 != 4 || c->x86_model != 9)
 +              return -ENODEV;
 +
 +      /* cpuinfo and default policy values */
 +      policy->cpuinfo.transition_latency = 1000000; /* 1ms */
 +      policy->cur = sc520_freq_get_cpu_frequency(0);
 +
 +      result = cpufreq_frequency_table_cpuinfo(policy, sc520_freq_table);
 +      if (result)
 +              return (result);
 +
 +      cpufreq_frequency_table_get_attr(sc520_freq_table, policy->cpu);
 +
 +      return 0;
 +}
 +
 +
 +static int sc520_freq_cpu_exit(struct cpufreq_policy *policy)
 +{
 +      cpufreq_frequency_table_put_attr(policy->cpu);
 +      return 0;
 +}
 +
 +
 +static struct freq_attr* sc520_freq_attr[] = {
 +      &cpufreq_freq_attr_scaling_available_freqs,
 +      NULL,
 +};
 +
 +
 +static struct cpufreq_driver sc520_freq_driver = {
 +      .get    = sc520_freq_get_cpu_frequency,
 +      .verify = sc520_freq_verify,
 +      .target = sc520_freq_target,
 +      .init   = sc520_freq_cpu_init,
 +      .exit   = sc520_freq_cpu_exit,
 +      .name   = "sc520_freq",
 +      .owner  = THIS_MODULE,
 +      .attr   = sc520_freq_attr,
 +};
 +
 +
 +static int __init sc520_freq_init(void)
 +{
 +      struct cpuinfo_x86 *c = cpu_data;
 +      int err;
 +
 +      /* Test if we have the right hardware */
 +      if(c->x86_vendor != X86_VENDOR_AMD ||
 +                              c->x86 != 4 || c->x86_model != 9) {
 +              dprintk("no Elan SC520 processor found!\n");
 +              return -ENODEV;
 +      }
 +      cpuctl = ioremap((unsigned long)(MMCR_BASE + OFFS_CPUCTL), 1);
 +      if(!cpuctl) {
 +              printk(KERN_ERR "sc520_freq: error: failed to remap memory\n");
 +              return -ENOMEM;
 +      }
 +
 +      err = cpufreq_register_driver(&sc520_freq_driver);
 +      if (err)
 +              iounmap(cpuctl);
 +
 +      return err;
 +}
 +
 +
 +static void __exit sc520_freq_exit(void)
 +{
 +      cpufreq_unregister_driver(&sc520_freq_driver);
 +      iounmap(cpuctl);
 +}
 +
 +
 +MODULE_LICENSE("GPL");
 +MODULE_AUTHOR("Sean Young <sean@mess.org>");
 +MODULE_DESCRIPTION("cpufreq driver for AMD's Elan sc520 CPU");
 +
 +module_init(sc520_freq_init);
 +module_exit(sc520_freq_exit);
 +
index 6c5dc2c85aeb99241ef85fac0cd20bfcc1e522a2,0000000000000000000000000000000000000000..811d47438546974e41651794cecf380a26e53c33
mode 100644,000000..100644
--- /dev/null
@@@ -1,634 -1,0 +1,633 @@@
-       policy->governor = CPUFREQ_DEFAULT_GOVERNOR;
 +/*
 + * cpufreq driver for Enhanced SpeedStep, as found in Intel's Pentium
 + * M (part of the Centrino chipset).
 + *
 + * Since the original Pentium M, most new Intel CPUs support Enhanced
 + * SpeedStep.
 + *
 + * Despite the "SpeedStep" in the name, this is almost entirely unlike
 + * traditional SpeedStep.
 + *
 + * Modelled on speedstep.c
 + *
 + * Copyright (C) 2003 Jeremy Fitzhardinge <jeremy@goop.org>
 + */
 +
 +#include <linux/kernel.h>
 +#include <linux/module.h>
 +#include <linux/init.h>
 +#include <linux/cpufreq.h>
 +#include <linux/sched.h>      /* current */
 +#include <linux/delay.h>
 +#include <linux/compiler.h>
 +
 +#include <asm/msr.h>
 +#include <asm/processor.h>
 +#include <asm/cpufeature.h>
 +
 +#define PFX           "speedstep-centrino: "
 +#define MAINTAINER    "cpufreq@lists.linux.org.uk"
 +
 +#define dprintk(msg...) cpufreq_debug_printk(CPUFREQ_DEBUG_DRIVER, "speedstep-centrino", msg)
 +
 +#define INTEL_MSR_RANGE       (0xffff)
 +
 +struct cpu_id
 +{
 +      __u8    x86;            /* CPU family */
 +      __u8    x86_model;      /* model */
 +      __u8    x86_mask;       /* stepping */
 +};
 +
 +enum {
 +      CPU_BANIAS,
 +      CPU_DOTHAN_A1,
 +      CPU_DOTHAN_A2,
 +      CPU_DOTHAN_B0,
 +      CPU_MP4HT_D0,
 +      CPU_MP4HT_E0,
 +};
 +
 +static const struct cpu_id cpu_ids[] = {
 +      [CPU_BANIAS]    = { 6,  9, 5 },
 +      [CPU_DOTHAN_A1] = { 6, 13, 1 },
 +      [CPU_DOTHAN_A2] = { 6, 13, 2 },
 +      [CPU_DOTHAN_B0] = { 6, 13, 6 },
 +      [CPU_MP4HT_D0]  = {15,  3, 4 },
 +      [CPU_MP4HT_E0]  = {15,  4, 1 },
 +};
 +#define N_IDS ARRAY_SIZE(cpu_ids)
 +
 +struct cpu_model
 +{
 +      const struct cpu_id *cpu_id;
 +      const char      *model_name;
 +      unsigned        max_freq; /* max clock in kHz */
 +
 +      struct cpufreq_frequency_table *op_points; /* clock/voltage pairs */
 +};
 +static int centrino_verify_cpu_id(const struct cpuinfo_x86 *c, const struct cpu_id *x);
 +
 +/* Operating points for current CPU */
 +static struct cpu_model *centrino_model[NR_CPUS];
 +static const struct cpu_id *centrino_cpu[NR_CPUS];
 +
 +static struct cpufreq_driver centrino_driver;
 +
 +#ifdef CONFIG_X86_SPEEDSTEP_CENTRINO_TABLE
 +
 +/* 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 (Banias) */
 +static struct cpufreq_frequency_table banias_900[] =
 +{
 +      OP(600,  844),
 +      OP(800,  988),
 +      OP(900, 1004),
 +      { .frequency = CPUFREQ_TABLE_END }
 +};
 +
 +/* Ultra Low Voltage Intel Pentium M processor 1000MHz (Banias) */
 +static struct cpufreq_frequency_table banias_1000[] =
 +{
 +      OP(600,   844),
 +      OP(800,   972),
 +      OP(900,   988),
 +      OP(1000, 1004),
 +      { .frequency = CPUFREQ_TABLE_END }
 +};
 +
 +/* Low Voltage Intel Pentium M processor 1.10GHz (Banias) */
 +static struct cpufreq_frequency_table banias_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 (Banias) */
 +static struct cpufreq_frequency_table banias_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 (Banias) */
 +static struct cpufreq_frequency_table banias_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 (Banias) */
 +static struct cpufreq_frequency_table banias_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 (Banias) */
 +static struct cpufreq_frequency_table banias_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 (Banias) */
 +static struct cpufreq_frequency_table banias_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 (Banias) */
 +static struct cpufreq_frequency_table banias_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 _BANIAS(cpuid, max, name)     \
 +{     .cpu_id         = cpuid,        \
 +      .model_name     = "Intel(R) Pentium(R) M processor " name "MHz", \
 +      .max_freq       = (max)*1000,   \
 +      .op_points      = banias_##max, \
 +}
 +#define BANIAS(max)   _BANIAS(&cpu_ids[CPU_BANIAS], max, #max)
 +
 +/* CPU models, their operating frequency range, and freq/voltage
 +   operating points */
 +static struct cpu_model models[] =
 +{
 +      _BANIAS(&cpu_ids[CPU_BANIAS], 900, " 900"),
 +      BANIAS(1000),
 +      BANIAS(1100),
 +      BANIAS(1200),
 +      BANIAS(1300),
 +      BANIAS(1400),
 +      BANIAS(1500),
 +      BANIAS(1600),
 +      BANIAS(1700),
 +
 +      /* NULL model_name is a wildcard */
 +      { &cpu_ids[CPU_DOTHAN_A1], NULL, 0, NULL },
 +      { &cpu_ids[CPU_DOTHAN_A2], NULL, 0, NULL },
 +      { &cpu_ids[CPU_DOTHAN_B0], NULL, 0, NULL },
 +      { &cpu_ids[CPU_MP4HT_D0], NULL, 0, NULL },
 +      { &cpu_ids[CPU_MP4HT_E0], NULL, 0, NULL },
 +
 +      { NULL, }
 +};
 +#undef _BANIAS
 +#undef BANIAS
 +
 +static int centrino_cpu_init_table(struct cpufreq_policy *policy)
 +{
 +      struct cpuinfo_x86 *cpu = &cpu_data[policy->cpu];
 +      struct cpu_model *model;
 +
 +      for(model = models; model->cpu_id != NULL; model++)
 +              if (centrino_verify_cpu_id(cpu, model->cpu_id) &&
 +                  (model->model_name == NULL ||
 +                   strcmp(cpu->x86_model_id, model->model_name) == 0))
 +                      break;
 +
 +      if (model->cpu_id == NULL) {
 +              /* No match at all */
 +              dprintk("no support for CPU model \"%s\": "
 +                     "send /proc/cpuinfo to " MAINTAINER "\n",
 +                     cpu->x86_model_id);
 +              return -ENOENT;
 +      }
 +
 +      if (model->op_points == NULL) {
 +              /* Matched a non-match */
 +              dprintk("no table support for CPU model \"%s\"\n",
 +                     cpu->x86_model_id);
 +              dprintk("try using the acpi-cpufreq driver\n");
 +              return -ENOENT;
 +      }
 +
 +      centrino_model[policy->cpu] = model;
 +
 +      dprintk("found \"%s\": max frequency: %dkHz\n",
 +             model->model_name, model->max_freq);
 +
 +      return 0;
 +}
 +
 +#else
 +static inline int centrino_cpu_init_table(struct cpufreq_policy *policy) { return -ENODEV; }
 +#endif /* CONFIG_X86_SPEEDSTEP_CENTRINO_TABLE */
 +
 +static int centrino_verify_cpu_id(const struct cpuinfo_x86 *c, const struct cpu_id *x)
 +{
 +      if ((c->x86 == x->x86) &&
 +          (c->x86_model == x->x86_model) &&
 +          (c->x86_mask == x->x86_mask))
 +              return 1;
 +      return 0;
 +}
 +
 +/* To be called only after centrino_model is initialized */
 +static unsigned extract_clock(unsigned msr, unsigned int cpu, int failsafe)
 +{
 +      int i;
 +
 +      /*
 +       * Extract clock in kHz from PERF_CTL value
 +       * for centrino, as some DSDTs are buggy.
 +       * Ideally, this can be done using the acpi_data structure.
 +       */
 +      if ((centrino_cpu[cpu] == &cpu_ids[CPU_BANIAS]) ||
 +          (centrino_cpu[cpu] == &cpu_ids[CPU_DOTHAN_A1]) ||
 +          (centrino_cpu[cpu] == &cpu_ids[CPU_DOTHAN_B0])) {
 +              msr = (msr >> 8) & 0xff;
 +              return msr * 100000;
 +      }
 +
 +      if ((!centrino_model[cpu]) || (!centrino_model[cpu]->op_points))
 +              return 0;
 +
 +      msr &= 0xffff;
 +      for (i=0;centrino_model[cpu]->op_points[i].frequency != CPUFREQ_TABLE_END; i++) {
 +              if (msr == centrino_model[cpu]->op_points[i].index)
 +                      return centrino_model[cpu]->op_points[i].frequency;
 +      }
 +      if (failsafe)
 +              return centrino_model[cpu]->op_points[i-1].frequency;
 +      else
 +              return 0;
 +}
 +
 +/* Return the current CPU frequency in kHz */
 +static unsigned int get_cur_freq(unsigned int cpu)
 +{
 +      unsigned l, h;
 +      unsigned clock_freq;
 +      cpumask_t saved_mask;
 +
 +      saved_mask = current->cpus_allowed;
 +      set_cpus_allowed(current, cpumask_of_cpu(cpu));
 +      if (smp_processor_id() != cpu)
 +              return 0;
 +
 +      rdmsr(MSR_IA32_PERF_STATUS, l, h);
 +      clock_freq = extract_clock(l, cpu, 0);
 +
 +      if (unlikely(clock_freq == 0)) {
 +              /*
 +               * On some CPUs, we can see transient MSR values (which are
 +               * not present in _PSS), while CPU is doing some automatic
 +               * P-state transition (like TM2). Get the last freq set 
 +               * in PERF_CTL.
 +               */
 +              rdmsr(MSR_IA32_PERF_CTL, l, h);
 +              clock_freq = extract_clock(l, cpu, 1);
 +      }
 +
 +      set_cpus_allowed(current, saved_mask);
 +      return clock_freq;
 +}
 +
 +
 +static int centrino_cpu_init(struct cpufreq_policy *policy)
 +{
 +      struct cpuinfo_x86 *cpu = &cpu_data[policy->cpu];
 +      unsigned freq;
 +      unsigned l, h;
 +      int ret;
 +      int i;
 +
 +      /* Only Intel makes Enhanced Speedstep-capable CPUs */
 +      if (cpu->x86_vendor != X86_VENDOR_INTEL || !cpu_has(cpu, X86_FEATURE_EST))
 +              return -ENODEV;
 +
 +      if (cpu_has(cpu, X86_FEATURE_CONSTANT_TSC))
 +              centrino_driver.flags |= CPUFREQ_CONST_LOOPS;
 +
 +      if (policy->cpu != 0)
 +              return -ENODEV;
 +
 +      for (i = 0; i < N_IDS; i++)
 +              if (centrino_verify_cpu_id(cpu, &cpu_ids[i]))
 +                      break;
 +
 +      if (i != N_IDS)
 +              centrino_cpu[policy->cpu] = &cpu_ids[i];
 +
 +      if (!centrino_cpu[policy->cpu]) {
 +              dprintk("found unsupported CPU with "
 +              "Enhanced SpeedStep: send /proc/cpuinfo to "
 +              MAINTAINER "\n");
 +              return -ENODEV;
 +      }
 +
 +      if (centrino_cpu_init_table(policy)) {
 +              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);
 +              dprintk("trying to enable Enhanced SpeedStep (%x)\n", l);
 +              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;
 +              }
 +      }
 +
 +      freq = get_cur_freq(policy->cpu);
 +
 +      policy->cpuinfo.transition_latency = 10000; /* 10uS transition latency */
 +      policy->cur = freq;
 +
 +      dprintk("centrino_cpu_init: cur=%dkHz\n", policy->cur);
 +
 +      ret = cpufreq_frequency_table_cpuinfo(policy, centrino_model[policy->cpu]->op_points);
 +      if (ret)
 +              return (ret);
 +
 +      cpufreq_frequency_table_get_attr(centrino_model[policy->cpu]->op_points, policy->cpu);
 +
 +      return 0;
 +}
 +
 +static int centrino_cpu_exit(struct cpufreq_policy *policy)
 +{
 +      unsigned int cpu = policy->cpu;
 +
 +      if (!centrino_model[cpu])
 +              return -ENODEV;
 +
 +      cpufreq_frequency_table_put_attr(cpu);
 +
 +      centrino_model[cpu] = NULL;
 +
 +      return 0;
 +}
 +
 +/**
 + * centrino_verify - verifies a new CPUFreq policy
 + * @policy: 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[policy->cpu]->op_points);
 +}
 +
 +/**
 + * centrino_setpolicy - set a new CPUFreq policy
 + * @policy: new policy
 + * @target_freq: the target frequency
 + * @relation: how that frequency relates to achieved frequency (CPUFREQ_RELATION_L or CPUFREQ_RELATION_H)
 + *
 + * 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 = 0, h = 0, cpu = policy->cpu;
 +      struct cpufreq_freqs    freqs;
 +      cpumask_t               online_policy_cpus;
 +      cpumask_t               saved_mask;
 +      cpumask_t               set_mask;
 +      cpumask_t               covered_cpus;
 +      int                     retval = 0;
 +      unsigned int            j, k, first_cpu, tmp;
 +
 +      if (unlikely(centrino_model[cpu] == NULL))
 +              return -ENODEV;
 +
 +      if (unlikely(cpufreq_frequency_table_target(policy,
 +                      centrino_model[cpu]->op_points,
 +                      target_freq,
 +                      relation,
 +                      &newstate))) {
 +              return -EINVAL;
 +      }
 +
 +#ifdef CONFIG_HOTPLUG_CPU
 +      /* cpufreq holds the hotplug lock, so we are safe from here on */
 +      cpus_and(online_policy_cpus, cpu_online_map, policy->cpus);
 +#else
 +      online_policy_cpus = policy->cpus;
 +#endif
 +
 +      saved_mask = current->cpus_allowed;
 +      first_cpu = 1;
 +      cpus_clear(covered_cpus);
 +      for_each_cpu_mask(j, online_policy_cpus) {
 +              /*
 +               * Support for SMP systems.
 +               * Make sure we are running on CPU that wants to change freq
 +               */
 +              cpus_clear(set_mask);
 +              if (policy->shared_type == CPUFREQ_SHARED_TYPE_ANY)
 +                      cpus_or(set_mask, set_mask, online_policy_cpus);
 +              else
 +                      cpu_set(j, set_mask);
 +
 +              set_cpus_allowed(current, set_mask);
 +              preempt_disable();
 +              if (unlikely(!cpu_isset(smp_processor_id(), set_mask))) {
 +                      dprintk("couldn't limit to CPUs in this domain\n");
 +                      retval = -EAGAIN;
 +                      if (first_cpu) {
 +                              /* We haven't started the transition yet. */
 +                              goto migrate_end;
 +                      }
 +                      preempt_enable();
 +                      break;
 +              }
 +
 +              msr = centrino_model[cpu]->op_points[newstate].index;
 +
 +              if (first_cpu) {
 +                      rdmsr(MSR_IA32_PERF_CTL, oldmsr, h);
 +                      if (msr == (oldmsr & 0xffff)) {
 +                              dprintk("no change needed - msr was and needs "
 +                                      "to be %x\n", oldmsr);
 +                              retval = 0;
 +                              goto migrate_end;
 +                      }
 +
 +                      freqs.old = extract_clock(oldmsr, cpu, 0);
 +                      freqs.new = extract_clock(msr, cpu, 0);
 +
 +                      dprintk("target=%dkHz old=%d new=%d msr=%04x\n",
 +                              target_freq, freqs.old, freqs.new, msr);
 +
 +                      for_each_cpu_mask(k, online_policy_cpus) {
 +                              freqs.cpu = k;
 +                              cpufreq_notify_transition(&freqs,
 +                                      CPUFREQ_PRECHANGE);
 +                      }
 +
 +                      first_cpu = 0;
 +                      /* all but 16 LSB are reserved, treat them with care */
 +                      oldmsr &= ~0xffff;
 +                      msr &= 0xffff;
 +                      oldmsr |= msr;
 +              }
 +
 +              wrmsr(MSR_IA32_PERF_CTL, oldmsr, h);
 +              if (policy->shared_type == CPUFREQ_SHARED_TYPE_ANY) {
 +                      preempt_enable();
 +                      break;
 +              }
 +
 +              cpu_set(j, covered_cpus);
 +              preempt_enable();
 +      }
 +
 +      for_each_cpu_mask(k, online_policy_cpus) {
 +              freqs.cpu = k;
 +              cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
 +      }
 +
 +      if (unlikely(retval)) {
 +              /*
 +               * We have failed halfway through the frequency change.
 +               * We have sent callbacks to policy->cpus and
 +               * MSRs have already been written on coverd_cpus.
 +               * Best effort undo..
 +               */
 +
 +              if (!cpus_empty(covered_cpus)) {
 +                      for_each_cpu_mask(j, covered_cpus) {
 +                              set_cpus_allowed(current, cpumask_of_cpu(j));
 +                              wrmsr(MSR_IA32_PERF_CTL, oldmsr, h);
 +                      }
 +              }
 +
 +              tmp = freqs.new;
 +              freqs.new = freqs.old;
 +              freqs.old = tmp;
 +              for_each_cpu_mask(j, online_policy_cpus) {
 +                      freqs.cpu = j;
 +                      cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
 +                      cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
 +              }
 +      }
 +      set_cpus_allowed(current, saved_mask);
 +      return 0;
 +
 +migrate_end:
 +      preempt_enable();
 +      set_cpus_allowed(current, saved_mask);
 +      return 0;
 +}
 +
 +static struct freq_attr* centrino_attr[] = {
 +      &cpufreq_freq_attr_scaling_available_freqs,
 +      NULL,
 +};
 +
 +static struct cpufreq_driver centrino_driver = {
 +      .name           = "centrino", /* should be speedstep-centrino,
 +                                       but there's a 16 char limit */
 +      .init           = centrino_cpu_init,
 +      .exit           = centrino_cpu_exit,
 +      .verify         = centrino_verify,
 +      .target         = centrino_target,
 +      .get            = get_cur_freq,
 +      .attr           = centrino_attr,
 +      .owner          = THIS_MODULE,
 +};
 +
 +
 +/**
 + * 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;
 +
 +      if (!cpu_has(cpu, X86_FEATURE_EST))
 +              return -ENODEV;
 +
 +      return cpufreq_register_driver(&centrino_driver);
 +}
 +
 +static void __exit centrino_exit(void)
 +{
 +      cpufreq_unregister_driver(&centrino_driver);
 +}
 +
 +MODULE_AUTHOR ("Jeremy Fitzhardinge <jeremy@goop.org>");
 +MODULE_DESCRIPTION ("Enhanced SpeedStep driver for Intel Pentium M processors.");
 +MODULE_LICENSE ("GPL");
 +
 +late_initcall(centrino_init);
 +module_exit(centrino_exit);
index a5b2346faf1fd2bba8d01bcbedb2253f4d3529cb,0000000000000000000000000000000000000000..36685e8f7be1171cf260d75646be0dff7f00a472
mode 100644,000000..100644
--- /dev/null
@@@ -1,440 -1,0 +1,439 @@@
-       policy->governor = CPUFREQ_DEFAULT_GOVERNOR;
 +/*
 + * (C) 2001  Dave Jones, Arjan van de ven.
 + * (C) 2002 - 2003  Dominik Brodowski <linux@brodo.de>
 + *
 + *  Licensed under the terms of the GNU GPL License version 2.
 + *  Based upon reverse engineered information, and on Intel documentation
 + *  for chipsets ICH2-M and ICH3-M.
 + *
 + *  Many thanks to Ducrot Bruno for finding and fixing the last
 + *  "missing link" for ICH2-M/ICH3-M support, and to Thomas Winkler
 + *  for extensive testing.
 + *
 + *  BIG FAT DISCLAIMER: Work in progress code. Possibly *dangerous*
 + */
 +
 +
 +/*********************************************************************
 + *                        SPEEDSTEP - DEFINITIONS                    *
 + *********************************************************************/
 +
 +#include <linux/kernel.h>
 +#include <linux/module.h>
 +#include <linux/init.h>
 +#include <linux/cpufreq.h>
 +#include <linux/pci.h>
 +#include <linux/slab.h>
 +#include <linux/sched.h>
 +
 +#include "speedstep-lib.h"
 +
 +
 +/* speedstep_chipset:
 + *   It is necessary to know which chipset is used. As accesses to
 + * this device occur at various places in this module, we need a
 + * static struct pci_dev * pointing to that device.
 + */
 +static struct pci_dev *speedstep_chipset_dev;
 +
 +
 +/* speedstep_processor
 + */
 +static unsigned int speedstep_processor = 0;
 +
 +static u32 pmbase;
 +
 +/*
 + *   There are only two frequency states for each processor. Values
 + * are in kHz for the time being.
 + */
 +static struct cpufreq_frequency_table speedstep_freqs[] = {
 +      {SPEEDSTEP_HIGH,        0},
 +      {SPEEDSTEP_LOW,         0},
 +      {0,                     CPUFREQ_TABLE_END},
 +};
 +
 +
 +#define dprintk(msg...) cpufreq_debug_printk(CPUFREQ_DEBUG_DRIVER, "speedstep-ich", msg)
 +
 +
 +/**
 + * speedstep_find_register - read the PMBASE address
 + *
 + * Returns: -ENODEV if no register could be found
 + */
 +static int speedstep_find_register (void)
 +{
 +      if (!speedstep_chipset_dev)
 +              return -ENODEV;
 +
 +      /* get PMBASE */
 +      pci_read_config_dword(speedstep_chipset_dev, 0x40, &pmbase);
 +      if (!(pmbase & 0x01)) {
 +              printk(KERN_ERR "speedstep-ich: could not find speedstep register\n");
 +              return -ENODEV;
 +      }
 +
 +      pmbase &= 0xFFFFFFFE;
 +      if (!pmbase) {
 +              printk(KERN_ERR "speedstep-ich: could not find speedstep register\n");
 +              return -ENODEV;
 +      }
 +
 +      dprintk("pmbase is 0x%x\n", pmbase);
 +      return 0;
 +}
 +
 +/**
 + * 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)
 +{
 +      u8 pm2_blk;
 +      u8 value;
 +      unsigned long flags;
 +
 +      if (state > 0x1)
 +              return;
 +
 +      /* Disable IRQs */
 +      local_irq_save(flags);
 +
 +      /* read state */
 +      value = inb(pmbase + 0x50);
 +
 +      dprintk("read at pmbase 0x%x + 0x50 returned 0x%x\n", pmbase, value);
 +
 +      /* write new state */
 +      value &= 0xFE;
 +      value |= state;
 +
 +      dprintk("writing 0x%x to pmbase 0x%x + 0x50\n", value, pmbase);
 +
 +      /* Disable bus master arbitration */
 +      pm2_blk = inb(pmbase + 0x20);
 +      pm2_blk |= 0x01;
 +      outb(pm2_blk, (pmbase + 0x20));
 +
 +      /* Actual transition */
 +      outb(value, (pmbase + 0x50));
 +
 +      /* Restore bus master arbitration */
 +      pm2_blk &= 0xfe;
 +      outb(pm2_blk, (pmbase + 0x20));
 +
 +      /* check if transition was successful */
 +      value = inb(pmbase + 0x50);
 +
 +      /* Enable IRQs */
 +      local_irq_restore(flags);
 +
 +      dprintk("read at pmbase 0x%x + 0x50 returned 0x%x\n", pmbase, value);
 +
 +      if (state == (value & 0x1)) {
 +              dprintk("change to %u MHz succeeded\n", (speedstep_get_processor_frequency(speedstep_processor) / 1000));
 +      } else {
 +              printk (KERN_ERR "cpufreq: change failed - I/O error\n");
 +      }
 +
 +      return;
 +}
 +
 +
 +/**
 + * speedstep_activate - activate SpeedStep control in the chipset
 + *
 + *   Tries to activate the SpeedStep status and control registers.
 + * Returns -EINVAL on an unsupported chipset, and zero on success.
 + */
 +static int speedstep_activate (void)
 +{
 +      u16 value = 0;
 +
 +      if (!speedstep_chipset_dev)
 +              return -EINVAL;
 +
 +      pci_read_config_word(speedstep_chipset_dev, 0x00A0, &value);
 +      if (!(value & 0x08)) {
 +              value |= 0x08;
 +              dprintk("activating SpeedStep (TM) registers\n");
 +              pci_write_config_word(speedstep_chipset_dev, 0x00A0, value);
 +      }
 +
 +      return 0;
 +}
 +
 +
 +/**
 + * speedstep_detect_chipset - detect the Southbridge which contains SpeedStep logic
 + *
 + *   Detects ICH2-M, ICH3-M and ICH4-M so far. The pci_dev points to
 + * the LPC bridge / PM module which contains all power-management
 + * functions. Returns the SPEEDSTEP_CHIPSET_-number for the detected
 + * chipset, or zero on failure.
 + */
 +static unsigned int speedstep_detect_chipset (void)
 +{
 +      speedstep_chipset_dev = pci_get_subsys(PCI_VENDOR_ID_INTEL,
 +                            PCI_DEVICE_ID_INTEL_82801DB_12,
 +                            PCI_ANY_ID,
 +                            PCI_ANY_ID,
 +                            NULL);
 +      if (speedstep_chipset_dev)
 +              return 4; /* 4-M */
 +
 +      speedstep_chipset_dev = pci_get_subsys(PCI_VENDOR_ID_INTEL,
 +                            PCI_DEVICE_ID_INTEL_82801CA_12,
 +                            PCI_ANY_ID,
 +                            PCI_ANY_ID,
 +                            NULL);
 +      if (speedstep_chipset_dev)
 +              return 3; /* 3-M */
 +
 +
 +      speedstep_chipset_dev = pci_get_subsys(PCI_VENDOR_ID_INTEL,
 +                            PCI_DEVICE_ID_INTEL_82801BA_10,
 +                            PCI_ANY_ID,
 +                            PCI_ANY_ID,
 +                            NULL);
 +      if (speedstep_chipset_dev) {
 +              /* speedstep.c causes lockups on Dell Inspirons 8000 and
 +               * 8100 which use a pretty old revision of the 82815
 +               * host brige. Abort on these systems.
 +               */
 +              static struct pci_dev *hostbridge;
 +
 +              hostbridge  = pci_get_subsys(PCI_VENDOR_ID_INTEL,
 +                            PCI_DEVICE_ID_INTEL_82815_MC,
 +                            PCI_ANY_ID,
 +                            PCI_ANY_ID,
 +                            NULL);
 +
 +              if (!hostbridge)
 +                      return 2; /* 2-M */
 +
 +              if (hostbridge->revision < 5) {
 +                      dprintk("hostbridge does not support speedstep\n");
 +                      speedstep_chipset_dev = NULL;
 +                      pci_dev_put(hostbridge);
 +                      return 0;
 +              }
 +
 +              pci_dev_put(hostbridge);
 +              return 2; /* 2-M */
 +      }
 +
 +      return 0;
 +}
 +
 +static unsigned int _speedstep_get(cpumask_t cpus)
 +{
 +      unsigned int speed;
 +      cpumask_t cpus_allowed;
 +
 +      cpus_allowed = current->cpus_allowed;
 +      set_cpus_allowed(current, cpus);
 +      speed = speedstep_get_processor_frequency(speedstep_processor);
 +      set_cpus_allowed(current, cpus_allowed);
 +      dprintk("detected %u kHz as current frequency\n", speed);
 +      return speed;
 +}
 +
 +static unsigned int speedstep_get(unsigned int cpu)
 +{
 +      return _speedstep_get(cpumask_of_cpu(cpu));
 +}
 +
 +/**
 + * speedstep_target - set a new CPUFreq policy
 + * @policy: new policy
 + * @target_freq: the target frequency
 + * @relation: how that frequency relates to achieved frequency (CPUFREQ_RELATION_L or CPUFREQ_RELATION_H)
 + *
 + * Sets a new CPUFreq policy.
 + */
 +static int speedstep_target (struct cpufreq_policy *policy,
 +                           unsigned int target_freq,
 +                           unsigned int relation)
 +{
 +      unsigned int newstate = 0;
 +      struct cpufreq_freqs freqs;
 +      cpumask_t cpus_allowed;
 +      int i;
 +
 +      if (cpufreq_frequency_table_target(policy, &speedstep_freqs[0], target_freq, relation, &newstate))
 +              return -EINVAL;
 +
 +      freqs.old = _speedstep_get(policy->cpus);
 +      freqs.new = speedstep_freqs[newstate].frequency;
 +      freqs.cpu = policy->cpu;
 +
 +      dprintk("transiting from %u to %u kHz\n", freqs.old, freqs.new);
 +
 +      /* no transition necessary */
 +      if (freqs.old == freqs.new)
 +              return 0;
 +
 +      cpus_allowed = current->cpus_allowed;
 +
 +      for_each_cpu_mask(i, policy->cpus) {
 +              freqs.cpu = i;
 +              cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
 +      }
 +
 +      /* switch to physical CPU where state is to be changed */
 +      set_cpus_allowed(current, policy->cpus);
 +
 +      speedstep_set_state(newstate);
 +
 +      /* allow to be run on all CPUs */
 +      set_cpus_allowed(current, cpus_allowed);
 +
 +      for_each_cpu_mask(i, policy->cpus) {
 +              freqs.cpu = i;
 +              cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
 +      }
 +
 +      return 0;
 +}
 +
 +
 +/**
 + * speedstep_verify - verifies a new CPUFreq policy
 + * @policy: new policy
 + *
 + * Limit must be within speedstep_low_freq and speedstep_high_freq, with
 + * at least one border included.
 + */
 +static int speedstep_verify (struct cpufreq_policy *policy)
 +{
 +      return cpufreq_frequency_table_verify(policy, &speedstep_freqs[0]);
 +}
 +
 +
 +static int speedstep_cpu_init(struct cpufreq_policy *policy)
 +{
 +      int result = 0;
 +      unsigned int speed;
 +      cpumask_t cpus_allowed;
 +
 +      /* only run on CPU to be set, or on its sibling */
 +#ifdef CONFIG_SMP
 +      policy->cpus = cpu_sibling_map[policy->cpu];
 +#endif
 +
 +      cpus_allowed = current->cpus_allowed;
 +      set_cpus_allowed(current, policy->cpus);
 +
 +      /* detect low and high frequency and transition latency */
 +      result = speedstep_get_freqs(speedstep_processor,
 +                                   &speedstep_freqs[SPEEDSTEP_LOW].frequency,
 +                                   &speedstep_freqs[SPEEDSTEP_HIGH].frequency,
 +                                   &policy->cpuinfo.transition_latency,
 +                                   &speedstep_set_state);
 +      set_cpus_allowed(current, cpus_allowed);
 +      if (result)
 +              return result;
 +
 +      /* get current speed setting */
 +      speed = _speedstep_get(policy->cpus);
 +      if (!speed)
 +              return -EIO;
 +
 +      dprintk("currently at %s speed setting - %i MHz\n",
 +              (speed == speedstep_freqs[SPEEDSTEP_LOW].frequency) ? "low" : "high",
 +              (speed / 1000));
 +
 +      /* cpuinfo and default policy values */
 +      policy->cur = speed;
 +
 +      result = cpufreq_frequency_table_cpuinfo(policy, speedstep_freqs);
 +      if (result)
 +              return (result);
 +
 +        cpufreq_frequency_table_get_attr(speedstep_freqs, policy->cpu);
 +
 +      return 0;
 +}
 +
 +
 +static int speedstep_cpu_exit(struct cpufreq_policy *policy)
 +{
 +      cpufreq_frequency_table_put_attr(policy->cpu);
 +      return 0;
 +}
 +
 +static struct freq_attr* speedstep_attr[] = {
 +      &cpufreq_freq_attr_scaling_available_freqs,
 +      NULL,
 +};
 +
 +
 +static struct cpufreq_driver speedstep_driver = {
 +      .name   = "speedstep-ich",
 +      .verify = speedstep_verify,
 +      .target = speedstep_target,
 +      .init   = speedstep_cpu_init,
 +      .exit   = speedstep_cpu_exit,
 +      .get    = speedstep_get,
 +      .owner  = THIS_MODULE,
 +      .attr   = speedstep_attr,
 +};
 +
 +
 +/**
 + * speedstep_init - initializes the SpeedStep CPUFreq driver
 + *
 + *   Initializes the SpeedStep support. Returns -ENODEV on unsupported
 + * devices, -EINVAL on problems during initiatization, and zero on
 + * success.
 + */
 +static int __init speedstep_init(void)
 +{
 +      /* detect processor */
 +      speedstep_processor = speedstep_detect_processor();
 +      if (!speedstep_processor) {
 +              dprintk("Intel(R) SpeedStep(TM) capable processor not found\n");
 +              return -ENODEV;
 +      }
 +
 +      /* detect chipset */
 +      if (!speedstep_detect_chipset()) {
 +              dprintk("Intel(R) SpeedStep(TM) for this chipset not (yet) available.\n");
 +              return -ENODEV;
 +      }
 +
 +      /* activate speedstep support */
 +      if (speedstep_activate()) {
 +              pci_dev_put(speedstep_chipset_dev);
 +              return -EINVAL;
 +      }
 +
 +      if (speedstep_find_register())
 +              return -ENODEV;
 +
 +      return cpufreq_register_driver(&speedstep_driver);
 +}
 +
 +
 +/**
 + * speedstep_exit - unregisters SpeedStep support
 + *
 + *   Unregisters SpeedStep support.
 + */
 +static void __exit speedstep_exit(void)
 +{
 +      pci_dev_put(speedstep_chipset_dev);
 +      cpufreq_unregister_driver(&speedstep_driver);
 +}
 +
 +
 +MODULE_AUTHOR ("Dave Jones <davej@codemonkey.org.uk>, Dominik Brodowski <linux@brodo.de>");
 +MODULE_DESCRIPTION ("Speedstep driver for Intel mobile processors on chipsets with ICH-M southbridges.");
 +MODULE_LICENSE ("GPL");
 +
 +module_init(speedstep_init);
 +module_exit(speedstep_exit);
index e1c509aa3054ef5dc7f453c3f665b5dcf1cdfa8e,0000000000000000000000000000000000000000..f2b5a621d27b39e4ece42418719655406b220e17
mode 100644,000000..100644
--- /dev/null
@@@ -1,424 -1,0 +1,423 @@@
-       policy->governor = CPUFREQ_DEFAULT_GOVERNOR;
 +/*
 + * Intel SpeedStep SMI driver.
 + *
 + * (C) 2003  Hiroshi Miura <miura@da-cha.org>
 + *
 + *  Licensed under the terms of the GNU GPL License version 2.
 + *
 + */
 +
 +
 +/*********************************************************************
 + *                        SPEEDSTEP - DEFINITIONS                    *
 + *********************************************************************/
 +
 +#include <linux/kernel.h>
 +#include <linux/module.h>
 +#include <linux/moduleparam.h>
 +#include <linux/init.h>
 +#include <linux/cpufreq.h>
 +#include <linux/slab.h>
 +#include <linux/delay.h>
 +#include <asm/ist.h>
 +#include <asm/io.h>
 +
 +#include "speedstep-lib.h"
 +
 +/* speedstep system management interface port/command.
 + *
 + * These parameters are got from IST-SMI BIOS call.
 + * If user gives it, these are used.
 + *
 + */
 +static int smi_port = 0;
 +static int smi_cmd = 0;
 +static unsigned int smi_sig = 0;
 +
 +/* info about the 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},
 +};
 +
 +#define GET_SPEEDSTEP_OWNER 0
 +#define GET_SPEEDSTEP_STATE 1
 +#define SET_SPEEDSTEP_STATE 2
 +#define GET_SPEEDSTEP_FREQS 4
 +
 +/* how often shall the SMI call be tried if it failed, e.g. because
 + * of DMA activity going on? */
 +#define SMI_TRIES 5
 +
 +#define dprintk(msg...) cpufreq_debug_printk(CPUFREQ_DEBUG_DRIVER, "speedstep-smi", msg)
 +
 +/**
 + * speedstep_smi_ownership
 + */
 +static int speedstep_smi_ownership (void)
 +{
 +      u32 command, result, magic;
 +      u32 function = GET_SPEEDSTEP_OWNER;
 +      unsigned char magic_data[] = "Copyright (c) 1999 Intel Corporation";
 +
 +      command = (smi_sig & 0xffffff00) | (smi_cmd & 0xff);
 +      magic = virt_to_phys(magic_data);
 +
 +      dprintk("trying to obtain ownership with command %x at port %x\n", command, smi_port);
 +
 +      __asm__ __volatile__(
 +              "out %%al, (%%dx)\n"
 +              : "=D" (result)
 +              : "a" (command), "b" (function), "c" (0), "d" (smi_port),
 +                      "D" (0), "S" (magic)
 +              : "memory"
 +      );
 +
 +      dprintk("result is %x\n", result);
 +
 +      return result;
 +}
 +
 +/**
 + * speedstep_smi_get_freqs - get SpeedStep preferred & current freq.
 + * @low: the low frequency value is placed here
 + * @high: the high frequency value is placed here
 + *
 + * Only available on later SpeedStep-enabled systems, returns false results or
 + * even hangs [cf. bugme.osdl.org # 1422] on earlier systems. Empirical testing
 + * shows that the latter occurs if !(ist_info.event & 0xFFFF).
 + */
 +static int speedstep_smi_get_freqs (unsigned int *low, unsigned int *high)
 +{
 +      u32 command, result = 0, edi, high_mhz, low_mhz;
 +      u32 state=0;
 +      u32 function = GET_SPEEDSTEP_FREQS;
 +
 +      if (!(ist_info.event & 0xFFFF)) {
 +              dprintk("bug #1422 -- can't read freqs from BIOS\n");
 +              return -ENODEV;
 +      }
 +
 +      command = (smi_sig & 0xffffff00) | (smi_cmd & 0xff);
 +
 +      dprintk("trying to determine frequencies with command %x at port %x\n", command, smi_port);
 +
 +      __asm__ __volatile__("movl $0, %%edi\n"
 +              "out %%al, (%%dx)\n"
 +              : "=a" (result), "=b" (high_mhz), "=c" (low_mhz), "=d" (state), "=D" (edi)
 +              : "a" (command), "b" (function), "c" (state), "d" (smi_port), "S" (0)
 +      );
 +
 +      dprintk("result %x, low_freq %u, high_freq %u\n", result, low_mhz, high_mhz);
 +
 +      /* abort if results are obviously incorrect... */
 +      if ((high_mhz + low_mhz) < 600)
 +              return -EINVAL;
 +
 +      *high = high_mhz * 1000;
 +      *low  = low_mhz  * 1000;
 +
 +      return result;
 +}
 +
 +/**
 + * speedstep_get_state - set the SpeedStep state
 + * @state: processor frequency state (SPEEDSTEP_LOW or SPEEDSTEP_HIGH)
 + *
 + */
 +static int speedstep_get_state (void)
 +{
 +      u32 function=GET_SPEEDSTEP_STATE;
 +      u32 result, state, edi, command;
 +
 +      command = (smi_sig & 0xffffff00) | (smi_cmd & 0xff);
 +
 +      dprintk("trying to determine current setting with command %x at port %x\n", command, smi_port);
 +
 +      __asm__ __volatile__("movl $0, %%edi\n"
 +              "out %%al, (%%dx)\n"
 +              : "=a" (result), "=b" (state), "=D" (edi)
 +              : "a" (command), "b" (function), "c" (0), "d" (smi_port), "S" (0)
 +      );
 +
 +      dprintk("state is %x, result is %x\n", state, result);
 +
 +      return (state & 1);
 +}
 +
 +
 +/**
 + * speedstep_set_state - set the SpeedStep state
 + * @state: new processor frequency state (SPEEDSTEP_LOW or SPEEDSTEP_HIGH)
 + *
 + */
 +static void speedstep_set_state (unsigned int state)
 +{
 +      unsigned int result = 0, command, new_state;
 +      unsigned long flags;
 +      unsigned int function=SET_SPEEDSTEP_STATE;
 +      unsigned int retry = 0;
 +
 +      if (state > 0x1)
 +              return;
 +
 +      /* Disable IRQs */
 +      local_irq_save(flags);
 +
 +      command = (smi_sig & 0xffffff00) | (smi_cmd & 0xff);
 +
 +      dprintk("trying to set frequency to state %u with command %x at port %x\n", state, command, smi_port);
 +
 +      do {
 +              if (retry) {
 +                      dprintk("retry %u, previous result %u, waiting...\n", retry, result);
 +                      mdelay(retry * 50);
 +              }
 +              retry++;
 +              __asm__ __volatile__(
 +                      "movl $0, %%edi\n"
 +                      "out %%al, (%%dx)\n"
 +                      : "=b" (new_state), "=D" (result)
 +                      : "a" (command), "b" (function), "c" (state), "d" (smi_port), "S" (0)
 +                      );
 +      } while ((new_state != state) && (retry <= SMI_TRIES));
 +
 +      /* enable IRQs */
 +      local_irq_restore(flags);
 +
 +      if (new_state == state) {
 +              dprintk("change to %u MHz succeeded after %u tries with result %u\n", (speedstep_freqs[new_state].frequency / 1000), retry, result);
 +      } else {
 +              printk(KERN_ERR "cpufreq: change failed with new_state %u and result %u\n", new_state, result);
 +      }
 +
 +      return;
 +}
 +
 +
 +/**
 + * speedstep_target - set a new CPUFreq policy
 + * @policy: new policy
 + * @target_freq: new freq
 + * @relation:
 + *
 + * Sets a new CPUFreq policy/freq.
 + */
 +static int speedstep_target (struct cpufreq_policy *policy,
 +                      unsigned int target_freq, unsigned int relation)
 +{
 +      unsigned int newstate = 0;
 +      struct cpufreq_freqs freqs;
 +
 +      if (cpufreq_frequency_table_target(policy, &speedstep_freqs[0], target_freq, relation, &newstate))
 +              return -EINVAL;
 +
 +      freqs.old = speedstep_freqs[speedstep_get_state()].frequency;
 +      freqs.new = speedstep_freqs[newstate].frequency;
 +      freqs.cpu = 0; /* speedstep.c is UP only driver */
 +
 +      if (freqs.old == freqs.new)
 +              return 0;
 +
 +      cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
 +      speedstep_set_state(newstate);
 +      cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
 +
 +      return 0;
 +}
 +
 +
 +/**
 + * speedstep_verify - verifies a new CPUFreq policy
 + * @policy: new policy
 + *
 + * Limit must be within speedstep_low_freq and speedstep_high_freq, with
 + * at least one border included.
 + */
 +static int speedstep_verify (struct cpufreq_policy *policy)
 +{
 +      return cpufreq_frequency_table_verify(policy, &speedstep_freqs[0]);
 +}
 +
 +
 +static int speedstep_cpu_init(struct cpufreq_policy *policy)
 +{
 +      int result;
 +      unsigned int speed,state;
 +
 +      /* capability check */
 +      if (policy->cpu != 0)
 +              return -ENODEV;
 +
 +      result = speedstep_smi_ownership();
 +      if (result) {
 +              dprintk("fails in aquiring ownership of a SMI interface.\n");
 +              return -EINVAL;
 +      }
 +
 +      /* detect low and high frequency */
 +      result = speedstep_smi_get_freqs(&speedstep_freqs[SPEEDSTEP_LOW].frequency,
 +                              &speedstep_freqs[SPEEDSTEP_HIGH].frequency);
 +      if (result) {
 +              /* fall back to speedstep_lib.c dection mechanism: try both states out */
 +              dprintk("could not detect low and high frequencies by SMI call.\n");
 +              result = speedstep_get_freqs(speedstep_processor,
 +                              &speedstep_freqs[SPEEDSTEP_LOW].frequency,
 +                              &speedstep_freqs[SPEEDSTEP_HIGH].frequency,
 +                              NULL,
 +                              &speedstep_set_state);
 +
 +              if (result) {
 +                      dprintk("could not detect two different speeds -- aborting.\n");
 +                      return result;
 +              } else
 +                      dprintk("workaround worked.\n");
 +      }
 +
 +      /* get current speed setting */
 +      state = speedstep_get_state();
 +      speed = speedstep_freqs[state].frequency;
 +
 +      dprintk("currently at %s speed setting - %i MHz\n",
 +              (speed == speedstep_freqs[SPEEDSTEP_LOW].frequency) ? "low" : "high",
 +              (speed / 1000));
 +
 +      /* cpuinfo and default policy values */
 +      policy->cpuinfo.transition_latency = CPUFREQ_ETERNAL;
 +      policy->cur = speed;
 +
 +      result = cpufreq_frequency_table_cpuinfo(policy, speedstep_freqs);
 +      if (result)
 +              return (result);
 +
 +      cpufreq_frequency_table_get_attr(speedstep_freqs, policy->cpu);
 +
 +      return 0;
 +}
 +
 +static int speedstep_cpu_exit(struct cpufreq_policy *policy)
 +{
 +      cpufreq_frequency_table_put_attr(policy->cpu);
 +      return 0;
 +}
 +
 +static unsigned int speedstep_get(unsigned int cpu)
 +{
 +      if (cpu)
 +              return -ENODEV;
 +      return speedstep_get_processor_frequency(speedstep_processor);
 +}
 +
 +
 +static int speedstep_resume(struct cpufreq_policy *policy)
 +{
 +      int result = speedstep_smi_ownership();
 +
 +      if (result)
 +              dprintk("fails in re-aquiring ownership of a SMI interface.\n");
 +
 +      return result;
 +}
 +
 +static struct freq_attr* speedstep_attr[] = {
 +      &cpufreq_freq_attr_scaling_available_freqs,
 +      NULL,
 +};
 +
 +static struct cpufreq_driver speedstep_driver = {
 +      .name           = "speedstep-smi",
 +      .verify         = speedstep_verify,
 +      .target         = speedstep_target,
 +      .init           = speedstep_cpu_init,
 +      .exit           = speedstep_cpu_exit,
 +      .get            = speedstep_get,
 +      .resume         = speedstep_resume,
 +      .owner          = THIS_MODULE,
 +      .attr           = speedstep_attr,
 +};
 +
 +/**
 + * speedstep_init - initializes the SpeedStep CPUFreq driver
 + *
 + *   Initializes the SpeedStep support. Returns -ENODEV on unsupported
 + * BIOS, -EINVAL on problems during initiatization, and zero on
 + * success.
 + */
 +static int __init speedstep_init(void)
 +{
 +      speedstep_processor = speedstep_detect_processor();
 +
 +      switch (speedstep_processor) {
 +      case SPEEDSTEP_PROCESSOR_PIII_T:
 +      case SPEEDSTEP_PROCESSOR_PIII_C:
 +      case SPEEDSTEP_PROCESSOR_PIII_C_EARLY:
 +              break;
 +      default:
 +              speedstep_processor = 0;
 +      }
 +
 +      if (!speedstep_processor) {
 +              dprintk ("No supported Intel CPU detected.\n");
 +              return -ENODEV;
 +      }
 +
 +      dprintk("signature:0x%.8lx, command:0x%.8lx, event:0x%.8lx, perf_level:0x%.8lx.\n",
 +              ist_info.signature, ist_info.command, ist_info.event, ist_info.perf_level);
 +
 +      /* Error if no IST-SMI BIOS or no PARM
 +               sig= 'ISGE' aka 'Intel Speedstep Gate E' */
 +      if ((ist_info.signature !=  0x47534943) && (
 +          (smi_port == 0) || (smi_cmd == 0)))
 +              return -ENODEV;
 +
 +      if (smi_sig == 1)
 +              smi_sig = 0x47534943;
 +      else
 +              smi_sig = ist_info.signature;
 +
 +      /* setup smi_port from MODLULE_PARM or BIOS */
 +      if ((smi_port > 0xff) || (smi_port < 0))
 +              return -EINVAL;
 +      else if (smi_port == 0)
 +              smi_port = ist_info.command & 0xff;
 +
 +      if ((smi_cmd > 0xff) || (smi_cmd < 0))
 +              return -EINVAL;
 +      else if (smi_cmd == 0)
 +              smi_cmd = (ist_info.command >> 16) & 0xff;
 +
 +      return cpufreq_register_driver(&speedstep_driver);
 +}
 +
 +
 +/**
 + * speedstep_exit - unregisters SpeedStep support
 + *
 + *   Unregisters SpeedStep support.
 + */
 +static void __exit speedstep_exit(void)
 +{
 +      cpufreq_unregister_driver(&speedstep_driver);
 +}
 +
 +module_param(smi_port,  int, 0444);
 +module_param(smi_cmd,   int, 0444);
 +module_param(smi_sig,  uint, 0444);
 +
 +MODULE_PARM_DESC(smi_port, "Override the BIOS-given IST port with this value -- Intel's default setting is 0xb2");
 +MODULE_PARM_DESC(smi_cmd, "Override the BIOS-given IST command with this value -- Intel's default setting is 0x82");
 +MODULE_PARM_DESC(smi_sig, "Set to 1 to fake the IST signature when using the SMI interface.");
 +
 +MODULE_AUTHOR ("Hiroshi Miura");
 +MODULE_DESCRIPTION ("Speedstep driver for IST applet SMI interface.");
 +MODULE_LICENSE ("GPL");
 +
 +module_init(speedstep_init);
 +module_exit(speedstep_exit);