linux 2.6.16.38 w/ vs2.0.3-rc1
[linux-2.6.git] / arch / i386 / kernel / cpu / cpufreq / powernow-k8.c
index 9b6ec75..c2719ab 100644 (file)
@@ -1,10 +1,10 @@
 /*
- *   (c) 2003, 2004 Advanced Micro Devices, Inc.
+ *   (c) 2003, 2004, 2005 Advanced Micro Devices, Inc.
  *  Your use of this code is subject to the terms and conditions of the
  *  GNU general public license version 2. See "COPYING" or
  *  http://www.gnu.org/licenses/gpl.html
  *
- *  Support : paul.devriendt@amd.com
+ *  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
  *
  *  Valuable input gratefully received from Dave Jones, Pavel Machek,
  *  Dominik Brodowski, 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 infrerred from
+ *     http://www.amd.com/us-en/assets/content_type/white_papers_and_tech_docs/30430.pdf
  */
 
 #include <linux/kernel.h>
@@ -27,6 +31,8 @@
 #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>
@@ -39,7 +45,7 @@
 
 #define PFX "powernow-k8: "
 #define BFX PFX "BIOS error: "
-#define VERSION "version 1.00.09e"
+#define VERSION "version 1.60.0"
 #include "powernow-k8.h"
 
 /* serialize freq changes  */
@@ -47,6 +53,10 @@ static DECLARE_MUTEX(fidvid_sem);
 
 static struct powernow_k8_data *powernow_data[NR_CPUS];
 
+#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)
 {
@@ -65,7 +75,12 @@ static u32 find_millivolts_from_vid(struct powernow_k8_data *data, u32 vid)
        return 1550-vid*25;
 }
 
-/* Return the vco fid for an input fid */
+/* 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) {
@@ -96,14 +111,13 @@ static int query_current_values_with_pending_wait(struct powernow_k8_data *data)
        u32 lo, hi;
        u32 i = 0;
 
-       lo = MSR_S_LO_CHANGE_PENDING;
-       while (lo & MSR_S_LO_CHANGE_PENDING) {
-               if (i++ > 0x1000000) {
-                       printk(KERN_ERR PFX "detected change pending stuck\n");
+       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;
@@ -146,6 +160,7 @@ 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");
@@ -157,10 +172,13 @@ static int write_new_fid(struct powernow_k8_data *data, u32 fid)
        dprintk("writing fid 0x%x, lo 0x%x, hi 0x%x\n",
                fid, lo, data->plllock * PLL_LOCK_CONVERSION);
 
-       wrmsr(MSR_FIDVID_CTL, lo, data->plllock * PLL_LOCK_CONVERSION);
-
-       if (query_current_values_with_pending_wait(data))
-               return 1;
+       do {
+               wrmsr(MSR_FIDVID_CTL, lo, data->plllock * PLL_LOCK_CONVERSION);
+               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));
 
        count_off_irt(data);
 
@@ -184,6 +202,7 @@ 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");
@@ -195,10 +214,13 @@ static int write_new_vid(struct powernow_k8_data *data, u32 vid)
        dprintk("writing vid 0x%x, lo 0x%x, hi 0x%x\n",
                vid, lo, STOP_GRANT_5NS);
 
-       wrmsr(MSR_FIDVID_CTL, lo, STOP_GRANT_5NS);
-
-       if (query_current_values_with_pending_wait(data))
-               return 1;
+       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",
@@ -218,7 +240,7 @@ static int write_new_vid(struct powernow_k8_data *data, u32 vid)
 /*
  * Reduce the vid by the max of step or reqvid.
  * Decreasing vid codes represent increasing voltages:
- * vid of 0 is 1.550V, vid of 0x1e is 0.800V, vid of 0x1f is off.
+ * 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)
 {
@@ -266,11 +288,18 @@ 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);
@@ -278,8 +307,8 @@ static int core_voltage_pre_transition(struct powernow_k8_data *data, u32 reqvid
                        return 1;
        }
 
-       while (rvosteps > 0) {
-               if (data->currvid == 0) {
+       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",
@@ -307,10 +336,7 @@ static int core_voltage_pre_transition(struct powernow_k8_data *data, u32 reqvid
 /* Phase 2 - core frequency transition */
 static int core_frequency_transition(struct powernow_k8_data *data, u32 reqfid)
 {
-       u32 vcoreqfid;
-       u32 vcocurrfid;
-       u32 vcofiddiff;
-       u32 savevid = data->currvid;
+       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",
@@ -333,9 +359,11 @@ static int core_frequency_transition(struct powernow_k8_data *data, u32 reqfid)
            : 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 + 2)) {
+                               if (write_new_fid(data, data->currfid + fid_interval)) {
                                        return 1;
                                }
                        } else {
@@ -345,7 +373,7 @@ static int core_frequency_transition(struct powernow_k8_data *data, u32 reqfid)
                                }
                        }
                } else {
-                       if (write_new_fid(data, data->currfid - 2))
+                       if (write_new_fid(data, data->currfid - fid_interval))
                                return 1;
                }
 
@@ -436,10 +464,9 @@ static int check_supported_cpu(unsigned int cpu)
 
        oldmask = current->cpus_allowed;
        set_cpus_allowed(current, cpumask_of_cpu(cpu));
-       schedule();
 
        if (smp_processor_id() != cpu) {
-               printk(KERN_ERR "limiting to cpu %u failed\n", cpu);
+               printk(KERN_ERR PFX "limiting to cpu %u failed\n", cpu);
                goto out;
        }
 
@@ -449,7 +476,7 @@ static int check_supported_cpu(unsigned int cpu)
        eax = cpuid_eax(CPUID_PROCESSOR_SIGNATURE);
        if (((eax & CPUID_USE_XFAM_XMOD) != CPUID_USE_XFAM_XMOD) ||
            ((eax & CPUID_XFAM) != CPUID_XFAM_K8) ||
-           ((eax & CPUID_XMOD) > CPUID_XMOD_REV_E)) {
+           ((eax & CPUID_XMOD) > CPUID_XMOD_REV_G)) {
                printk(KERN_INFO PFX "Processor cpuid %x not supported\n", eax);
                goto out;
        }
@@ -471,9 +498,7 @@ static int check_supported_cpu(unsigned int cpu)
 
 out:
        set_cpus_allowed(current, oldmask);
-       schedule();
        return rc;
-
 }
 
 static int check_pst_table(struct powernow_k8_data *data, struct pst_s *pst, u8 maxvid)
@@ -494,22 +519,24 @@ static int check_pst_table(struct powernow_k8_data *data, struct pst_s *pst, u8
                        printk(KERN_ERR BFX "maxvid exceeded with pstate %d\n", j);
                        return -ENODEV;
                }
-               if ((pst[j].fid > MAX_FID)
-                   || (pst[j].fid & 1)
-                   || (j && (pst[j].fid < HI_FID_TABLE_BOTTOM))) {
+               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 PFX "fid %d invalid : 0x%x\n", j, pst[j].fid);
+                       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 PFX "lastfid invalid\n");
+               printk(KERN_ERR BFX "lastfid invalid\n");
                return -EINVAL;
        }
        if (lastfid > LO_FID_TABLE_TOP)
-               printk(KERN_INFO PFX  "first fid not from lo freq table\n");
+               printk(KERN_INFO BFX  "first fid not from lo freq table\n");
 
        return 0;
 }
@@ -608,7 +635,7 @@ static int find_psb_table(struct powernow_k8_data *data)
 
                dprintk("table vers: 0x%x\n", psb->tableversion);
                if (psb->tableversion != PSB_VERSION_1_4) {
-                       printk(KERN_INFO BFX "PSB table is not v1.4\n");
+                       printk(KERN_ERR BFX "PSB table is not v1.4\n");
                        return -ENODEV;
                }
 
@@ -618,7 +645,7 @@ static int find_psb_table(struct powernow_k8_data *data)
                        return -ENODEV;
                }
 
-               data->vstable = psb->voltagestabilizationtime;
+               data->vstable = psb->vstable;
                dprintk("voltage stabilization time: %d(*20us)\n", data->vstable);
 
                dprintk("flags2: 0x%x\n", psb->flags2);
@@ -632,8 +659,8 @@ static int find_psb_table(struct powernow_k8_data *data)
                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->numpst);
-               cpst = psb->numpst;
+               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) ) {
@@ -651,7 +678,7 @@ static int find_psb_table(struct powernow_k8_data *data)
                dprintk("maxvid: 0x%x\n", psb->maxvid);
                maxvid = psb->maxvid;
 
-               data->numps = psb->numpstates;
+               data->numps = psb->numps;
                dprintk("numpstates: 0x%x\n", data->numps);
                return fill_powernow_table(data, (struct pst_s *)(psb+1), maxvid);
        }
@@ -666,7 +693,7 @@ static int find_psb_table(struct powernow_k8_data *data)
         * BIOS and Kernel Developer's Guide, which is available on
         * www.amd.com
         */
-       printk(KERN_ERR PFX "BIOS error - no PSB\n");
+       printk(KERN_ERR PFX "BIOS error - no PSB or ACPI _PSS objects\n");
        return -ENODEV;
 }
 
@@ -678,6 +705,7 @@ static void powernow_k8_acpi_pst_values(struct powernow_k8_data *data, unsigned
 
        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;
@@ -690,7 +718,7 @@ static int powernow_k8_cpu_init_acpi(struct powernow_k8_data *data)
        struct cpufreq_frequency_table *powernow_table;
 
        if (acpi_processor_register_performance(&data->acpi_data, data->cpu)) {
-               dprintk("register performance failed\n");
+               dprintk("register performance failed: bad ACPI data\n");
                return -EIO;
        }
 
@@ -717,8 +745,16 @@ static int powernow_k8_cpu_init_acpi(struct powernow_k8_data *data)
        }
 
        for (i = 0; i < data->acpi_data.state_count; i++) {
-               u32 fid = data->acpi_data.states[i].control & FID_MASK;
-               u32 vid = (data->acpi_data.states[i].control >> VID_SHIFT) & VID_MASK;
+               u32 fid;
+               u32 vid;
+
+               if (data->exttype) {
+                       fid = data->acpi_data.states[i].status & FID_MASK;
+                       vid = (data->acpi_data.states[i].status >> VID_SHIFT) & 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);
 
@@ -735,28 +771,29 @@ static int powernow_k8_cpu_init_acpi(struct powernow_k8_data *data)
                }
 
                /* verify voltage is OK - BIOSs are using "off" to indicate invalid */
-               if (vid == 0x1f) {
+               if (vid == VID_OFF) {
                        dprintk("invalid vid %u, ignoring\n", vid);
                        powernow_table[i].frequency = CPUFREQ_ENTRY_INVALID;
                        continue;
                }
 
-               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");
-                                       goto err_out_mem;
-                               }
-                               
-                               dprintk("double low frequency table entry, ignoring it.\n");
-                               powernow_table[i].frequency = CPUFREQ_ENTRY_INVALID;
-                               continue;
-                       } else
-                               cntlofreq = i;
+               /* 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");
+                                       goto err_out_mem;
+                               }
+
+                               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)) {
@@ -811,7 +848,7 @@ static int transition_frequency(struct powernow_k8_data *data, unsigned int inde
 {
        u32 fid;
        u32 vid;
-       int res;
+       int res, i;
        struct cpufreq_freqs freqs;
 
        dprintk("cpu %d transition to index %u\n", smp_processor_id(), index);
@@ -836,7 +873,8 @@ static int transition_frequency(struct powernow_k8_data *data, unsigned int inde
        }
 
        if ((fid < HI_FID_TABLE_BOTTOM) && (data->currfid < HI_FID_TABLE_BOTTOM)) {
-               printk("ignoring illegal change in lo freq table-%x to 0x%x\n",
+               printk(KERN_ERR PFX
+                      "ignoring illegal change in lo freq table-%x to 0x%x\n",
                       data->currfid, fid);
                return 1;
        }
@@ -845,18 +883,20 @@ static int transition_frequency(struct powernow_k8_data *data, unsigned int inde
                smp_processor_id(), fid, vid);
 
        freqs.cpu = data->cpu;
-
        freqs.old = find_khz_freq_from_fid(data->currfid);
        freqs.new = find_khz_freq_from_fid(fid);
-       cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
+       for_each_cpu_mask(i, cpu_core_map[data->cpu]) {
+               freqs.cpu = i;
+               cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
+       }
 
-       down(&fidvid_sem);
        res = transition_fid_vid(data, fid, vid);
-       up(&fidvid_sem);
 
        freqs.new = find_khz_freq_from_fid(data->currfid);
-       cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
-
+       for_each_cpu_mask(i, cpu_core_map[data->cpu]) {
+               freqs.cpu = i;
+               cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
+        }
        return res;
 }
 
@@ -869,14 +909,14 @@ static int powernowk8_target(struct cpufreq_policy *pol, unsigned targfreq, unsi
        u32 checkvid = data->currvid;
        unsigned int newstate;
        int ret = -EIO;
+       int i;
 
        /* only run on specific CPU from here on */
        oldmask = current->cpus_allowed;
        set_cpus_allowed(current, cpumask_of_cpu(pol->cpu));
-       schedule();
 
        if (smp_processor_id() != pol->cpu) {
-               printk(KERN_ERR "limiting to cpu %u failed\n", pol->cpu);
+               printk(KERN_ERR PFX "limiting to cpu %u failed\n", pol->cpu);
                goto err_out;
        }
 
@@ -897,29 +937,37 @@ static int powernowk8_target(struct cpufreq_policy *pol, unsigned targfreq, unsi
                data->currfid, data->currvid);
 
        if ((checkvid != data->currvid) || (checkfid != data->currfid)) {
-               printk(KERN_ERR PFX
-                      "error - out of sync, fid 0x%x 0x%x, vid 0x%x 0x%x\n",
-                      checkfid, data->currfid, checkvid, data->currvid);
+               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;
 
+       down(&fidvid_sem);
+
        powernow_k8_acpi_pst_values(data, newstate);
 
        if (transition_frequency(data, newstate)) {
                printk(KERN_ERR PFX "transition frequency failed\n");
                ret = 1;
+               up(&fidvid_sem);
                goto err_out;
        }
 
+       /* Update all the fid/vids of our siblings */
+       for_each_cpu_mask(i, cpu_core_map[pol->cpu]) {
+               powernow_data[i]->currvid = data->currvid;
+               powernow_data[i]->currfid = data->currfid;
+       }       
+       up(&fidvid_sem);
+
        pol->cur = find_khz_freq_from_fid(data->currfid);
        ret = 0;
 
 err_out:
        set_cpus_allowed(current, oldmask);
-       schedule();
-
        return ret;
 }
 
@@ -932,21 +980,23 @@ static int powernowk8_verify(struct cpufreq_policy *pol)
 }
 
 /* per CPU init entry point to the driver */
-static int __init powernowk8_cpu_init(struct cpufreq_policy *pol)
+static int __cpuinit powernowk8_cpu_init(struct cpufreq_policy *pol)
 {
        struct powernow_k8_data *data;
        cpumask_t oldmask = CPU_MASK_ALL;
-       int rc;
+       int rc, i;
+
+       if (!cpu_online(pol->cpu))
+               return -ENODEV;
 
        if (!check_supported_cpu(pol->cpu))
                return -ENODEV;
 
-       data = kmalloc(sizeof(struct powernow_k8_data), GFP_KERNEL);
+       data = kzalloc(sizeof(struct powernow_k8_data), GFP_KERNEL);
        if (!data) {
                printk(KERN_ERR PFX "unable to alloc powernow_k8_data");
                return -ENOMEM;
        }
-       memset(data,0,sizeof(struct powernow_k8_data));
 
        data->cpu = pol->cpu;
 
@@ -956,8 +1006,8 @@ static int __init powernowk8_cpu_init(struct cpufreq_policy *pol)
                 * an UP version, and is deprecated by AMD.
                 */
 
-               if ((num_online_cpus() != 1) || (num_possible_cpus() != 1)) {
-                       printk(KERN_INFO PFX "MP systems not supported by PSB BIOS structure\n");
+               if (num_online_cpus() != 1) {
+                       printk(KERN_ERR PFX "MP systems not supported by PSB BIOS structure\n");
                        kfree(data);
                        return -ENODEV;
                }
@@ -976,10 +1026,9 @@ static int __init powernowk8_cpu_init(struct cpufreq_policy *pol)
        /* only run on specific CPU from here on */
        oldmask = current->cpus_allowed;
        set_cpus_allowed(current, cpumask_of_cpu(pol->cpu));
-       schedule();
 
        if (smp_processor_id() != pol->cpu) {
-               printk(KERN_ERR "limiting to cpu %u failed\n", pol->cpu);
+               printk(KERN_ERR PFX "limiting to cpu %u failed\n", pol->cpu);
                goto err_out;
        }
 
@@ -995,9 +1044,9 @@ static int __init powernowk8_cpu_init(struct cpufreq_policy *pol)
 
        /* run on any CPU again */
        set_cpus_allowed(current, oldmask);
-       schedule();
 
        pol->governor = CPUFREQ_DEFAULT_GOVERNOR;
+       pol->cpus = cpu_core_map[pol->cpu];
 
        /* Take a crude guess here. 
         * That guess was in microseconds, so multiply with 1000 */
@@ -1010,6 +1059,7 @@ static int __init powernowk8_cpu_init(struct cpufreq_policy *pol)
        /* 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;
@@ -1020,13 +1070,15 @@ static int __init powernowk8_cpu_init(struct cpufreq_policy *pol)
        printk("cpu_init done, current fid 0x%x, vid 0x%x\n",
               data->currfid, data->currvid);
 
-       powernow_data[pol->cpu] = data;
+       for_each_cpu_mask(i, cpu_core_map[pol->cpu]) {
+               powernow_data[i] = data;
+       }
 
        return 0;
 
 err_out:
        set_cpus_allowed(current, oldmask);
-       schedule();
+       powernow_k8_cpu_exit_acpi(data);
 
        kfree(data);
        return -ENODEV;
@@ -1061,17 +1113,14 @@ static unsigned int powernowk8_get (unsigned int cpu)
                set_cpus_allowed(current, oldmask);
                return 0;
        }
-       preempt_disable();
 
        if (query_current_values_with_pending_wait(data))
                goto out;
 
        khz = find_khz_freq_from_fid(data->currfid);
 
- out:
-       preempt_enable_no_resched();
+out:
        set_cpus_allowed(current, oldmask);
-
        return khz;
 }
 
@@ -1092,7 +1141,7 @@ static struct cpufreq_driver cpufreq_amd64_driver = {
 };
 
 /* driver entry point for init */
-static int __init powernowk8_init(void)
+static int __cpuinit powernowk8_init(void)
 {
        unsigned int i, supported_cpus = 0;
 
@@ -1120,7 +1169,7 @@ static void __exit powernowk8_exit(void)
        cpufreq_unregister_driver(&cpufreq_amd64_driver);
 }
 
-MODULE_AUTHOR("Paul Devriendt <paul.devriendt@amd.com>");
+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");