From mboxrd@z Thu Jan 1 00:00:00 1970 From: Pavel Machek Subject: Re: powernow-k8: support acpi Date: Fri, 2 Apr 2004 19:09:26 +0200 Sender: cpufreq-bounces@www.linux.org.uk Message-ID: <20040402170926.GA289@elf.ucw.cz> References: <99F2150714F93F448942F9A9F112634C1163C8D7@txexmtae.amd.com> <20040321185417.GA7969@dominikbrodowski.de> <20040326122931.GA321@elf.ucw.cz> <20040401235635.GA25744@redhat.com> <20040402162447.GP19001@poupinou.org> <20040402163902.GC5688@redhat.com> Mime-Version: 1.0 Return-path: Content-Disposition: inline In-Reply-To: <20040402163902.GC5688@redhat.com> List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: cpufreq-bounces+glkc-cpufreq=gmane.org@www.linux.org.uk Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit To: Dave Jones Cc: Andrew Morton , Bruno Ducrot , mark.langsdorf@amd.com, Cpufreq mailing list , paul.devriendt@amd.com, Dominik Brodowski Hi! > > > Would this driver be better off using that ? > > > > At first, yes, it's needed anyway for the SMP case if I'm correct (there is > > a flaw in how the v1.4 psb header have been changed wrt 1.2 that make that > > impossible apparently). > > > > But I can't say for sure which have to be favored: legacy or ACPI. > > I don't have enough dump of ACPI tables and PSB/PST from machines shipped > > with AMD64. > > Ok. I think for the time being we'll roll with what Pavel has come up with. > We can always improve on it later, whichever way we decide to turn. Yes, that looks best. At least patches should be way smaller from now on. > I'm about to disappear for a day or two. I'll look into integrating whats > outstanding on Sunday. Pavel, if you have time between now and then > to do a diff of whats remaining between your tree and the driver I posted > this afternoon, that would make my life a little easier. Here it is: (I was not able to locate your changes, so it is possible I'm reverting some of those; but few extra inlines should not hurt that bad :-). Pavel --- clean-dj//arch/i386/kernel/cpu/cpufreq/powernow-k8.c 2004-04-02 17:46:20.000000000 +0200 +++ linux-pn/arch/i386/kernel/cpu/cpufreq/powernow-k8.c 2004-04-02 19:05:25.000000000 +0200 @@ -13,8 +13,9 @@ * 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, and others. + * Valuable input gratefully received from Dave Jones, Pavel Machek, Dominik + * Brodowski, and others. + * * 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 @@ -32,42 +33,41 @@ #include #include +#ifdef CONFIG_X86_POWERNOW_K8_ACPI + +#include +#include + +#endif /* CONFIG_X86_POWERNOW_K8_ACPI */ + + #define PFX "powernow-k8: " -#define BFX PFX "BIOS error: " -#define VERSION "version 1.00.08a" +#define VERSION "version 1.20.08b - March 20, 2004" #include "powernow-k8.h" +/* serialize freq changes */ +static DECLARE_MUTEX(fidvid_sem); + static struct powernow_k8_data *powernow_data[NR_CPUS]; -/* -The PSB table supplied by BIOS allows for the definition of the number of -p-states that can be used when running on a/c, and the number of p-states -that can be used when running on battery. This allows laptop manufacturers -to force the system to save power when running from battery. The relationship -is : - 1 <= number_of_battery_p_states <= maximum_number_of_p_states - -This driver does NOT have the support in it to detect transitions from -a/c power to battery power, and thus trigger the transition to a lower -p-state if required. This is because I need ACPI and the 2.6 kernel to do -this, and this is a 2.4 kernel driver. Check back for a new improved driver -for the 2.6 kernel soon. - -This code therefore assumes it is on battery at all times, and thus -restricts performance to number_of_battery_p_states. For desktops, - number_of_battery_p_states == maximum_number_of_pstates, -so this is not actually a restriction. -*/ -static u32 batps; /* limit on the number of p states when on battery */ - /* - set by BIOS in the PSB/PST */ +/* Return a frequency in MHz, given an input fid */ +static inline u32 find_freq_from_fid(u32 fid) +{ + return 800 + (fid * 100); +} - /* Return a frequency in MHz, given an input fid */ -static u32 find_freq_from_fid(u32 fid) +/* Return a frequency in KHz, given an input fid */ +static inline u32 find_khz_freq_from_fid(u32 fid) { - return 800 + (fid * 100); + return 1000 * find_freq_from_fid(fid); } +/* Return a voltage in miliVolts, given an input vid */ +static inline u32 find_milivolts_from_vid(struct powernow_k8_data *data, u32 vid) +{ + return 1550-vid*25; +} /* Return the vco fid for an input fid */ static u32 convert_fid_to_vco_fid(u32 fid) @@ -92,8 +95,8 @@ } /* - * Update the global current fid / vid values from the status msr. - * Returns 1 on error. + * 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) { @@ -122,13 +125,28 @@ return; } -/* the voltage stabalization time */ +/* the voltage stabilization time */ static inline 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(PFX "cpu%d, init lo %x, hi %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) { @@ -141,15 +159,11 @@ } lo = fid | (data->currvid << MSR_C_LO_VID_SHIFT) | MSR_C_LO_INIT_FID_VID; - dprintk(KERN_DEBUG PFX "writing fid %x, lo %x, hi %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; - count_off_irt(data); if (savevid != data->currvid) { @@ -160,7 +174,7 @@ if (fid != data->currfid) { printk(KERN_ERR PFX "fid trans failed, fid %x, curr %x\n", fid, - data->currfid); + data->currfid); return 1; } @@ -179,12 +193,9 @@ } lo = data->currfid | (vid << MSR_C_LO_VID_SHIFT) | MSR_C_LO_INIT_FID_VID; - dprintk(KERN_DEBUG PFX "writing vid %x, lo %x, hi %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; @@ -196,8 +207,8 @@ if (vid != data->currvid) { printk(KERN_ERR PFX "vid trans failed, vid %x, curr %x\n", vid, - data->currvid); - return 1; + data->currvid); + return 1; } return 0; @@ -212,56 +223,49 @@ { if ((data->currvid - reqvid) > step) reqvid = data->currvid - step; - if (write_new_vid(data, reqvid)) return 1; - count_off_vst(data); - return 0; } /* Change the fid and vid, by the 3 phases. */ -static int transition_fid_vid(struct powernow_k8_data *data, u32 reqfid, u32 reqvid) +static inline 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); + printk(KERN_ERR PFX "failed (cpu%d): req %x %x, curr %x %x\n", + smp_processor_id(), + reqfid, reqvid, data->currfid, data->currvid); return 1; } - dprintk(KERN_INFO PFX "transitioned (cpu%d): new fid 0x%x, vid 0x%x\n", + dprintk(KERN_INFO PFX "transitioned (cpu%d): new fid %x, vid %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) +static inline int core_voltage_pre_transition(struct powernow_k8_data *data, u32 reqvid) { u32 rvosteps = data->rvo; u32 savefid = data->currfid; dprintk(KERN_DEBUG PFX - "ph1 (cpu%d): start, currfid 0x%x, currvid 0x%x, reqvid 0x%x, rvo 0x%x\n", - smp_processor_id(); + "ph1 (cpu%d): start, currfid %x, currvid %x, reqvid %x, rvo %x\n", + smp_processor_id(), data->currfid, data->currvid, reqvid, data->rvo); while (data->currvid > reqvid) { - dprintk(KERN_DEBUG PFX "ph1: curr 0x%x, req vid 0x%x\n", + dprintk(KERN_DEBUG PFX "ph1: curr %x, req vid %x\n", data->currvid, reqvid); if (decrease_vid_code_by_step(data, reqvid, data->vidmvs)) return 1; @@ -272,7 +276,7 @@ rvosteps = 0; } else { dprintk(KERN_DEBUG PFX - "ph1: changing vid for rvo, req 0x%x\n", + "ph1: changing vid for rvo, req %x\n", data->currvid - 1); if (decrease_vid_code_by_step(data, data->currvid - 1, 1)) return 1; @@ -284,18 +288,18 @@ return 1; if (savefid != data->currfid) { - printk(KERN_ERR PFX "ph1 err, currfid changed 0x%x\n", data->currfid); + printk(KERN_ERR PFX "ph1: err, currfid changed %x\n", data->currfid); return 1; } - dprintk(KERN_DEBUG PFX "ph1 complete, currfid 0x%x, currvid 0x%x\n", + dprintk(KERN_DEBUG PFX "ph1: complete, currfid %x, currvid %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) +static inline int core_frequency_transition(struct powernow_k8_data *data, u32 reqfid) { u32 vcoreqfid; u32 vcocurrfid; @@ -303,18 +307,18 @@ u32 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); + printk(KERN_ERR PFX "ph2: illegal lo-lo transition %x %x\n", + reqfid, data->currfid); return 1; } if (data->currfid == reqfid) { - printk(KERN_ERR PFX "ph2 null fid transition 0x%x\n", data->currfid); + printk(KERN_ERR PFX "ph2: null fid transition %x\n", data->currfid); return 0; } dprintk(KERN_DEBUG PFX - "ph2 (cpu%d): starting, currfid 0x%x, currvid 0x%x, reqfid 0x%x\n", + "ph2 (cpu%d): starting, currfid %x, currvid %x, reqfid %x\n", smp_processor_id(), data->currfid, data->currvid, reqfid); @@ -347,36 +351,35 @@ 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); + "ph2: mismatch, failed fid transition, curr %x, req %x\n", + data->currfid, reqfid); return 1; } if (savevid != data->currvid) { printk(KERN_ERR PFX "ph2: vid changed, save %x, curr %x\n", - savevid, data->currvid); + savevid, data->currvid); return 1; } - dprintk(KERN_DEBUG PFX "ph2 complete, currfid 0x%x, currvid 0x%x\n", + dprintk(KERN_DEBUG PFX "ph2: complete, currfid %x, currvid %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) +static inline int core_voltage_post_transition(struct powernow_k8_data *data, u32 reqvid) { u32 savefid = data->currfid; u32 savereqvid = reqvid; - dprintk(KERN_DEBUG PFX "ph3 (cpu%d): starting, currfid 0x%x, currvid 0x%x\n", + dprintk(KERN_DEBUG PFX "ph3 (cpu%d): starting, currfid %x, currvid %x\n", smp_processor_id(), data->currfid, data->currvid); @@ -403,99 +406,92 @@ return 1; if (savereqvid != data->currvid) { - dprintk(KERN_ERR PFX "ph3 failed, currvid 0x%x\n", data->currvid); + printk(KERN_ERR PFX "ph3: failed, currvid %x\n", data->currvid); return 1; } if (savefid != data->currfid) { - dprintk(KERN_ERR PFX "ph3 failed, currfid changed 0x%x\n", + printk(KERN_ERR PFX "ph3: failed, currfid changed %x\n", data->currfid); return 1; } - dprintk(KERN_DEBUG PFX "ph3 complete, currfid 0x%x, currvid 0x%x\n", + dprintk(KERN_DEBUG PFX "ph3: complete, currfid %x, currvid %x\n", data->currfid, data->currvid); - return 0; } -static int check_supported_cpu(unsigned int cpu) +static inline int check_supported_cpu(unsigned int cpu) { - struct cpuinfo_x86 *c = cpu_data; + cpumask_t oldmask = CPU_MASK_ALL; u32 eax, ebx, ecx, edx; + unsigned int rc = 0; - if (num_online_cpus() != 1) { - printk(KERN_INFO PFX "multiprocessor systems not supported\n"); - return 0; - } + oldmask = current->cpus_allowed; + set_cpus_allowed(current, cpumask_of_cpu(cpu)); + schedule(); - if (c->x86_vendor != X86_VENDOR_AMD) { -#ifdef MODULE - printk(KERN_INFO PFX "Not an AMD processor\n"); -#endif - return 0; + if (smp_processor_id() != cpu) { + printk(KERN_ERR "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_MOD) == ATHLON64_XFAM_MOD) { dprintk(KERN_DEBUG PFX "AMD Althon 64 Processor found\n"); - if ((eax & CPUID_F1_STEP) < ATHLON64_REV_C0) { - printk(KERN_INFO PFX "Revision C0 or better " - "AMD Athlon 64 processor required\n"); - return 0; - } } else if ((eax & CPUID_XFAM_MOD) == OPTERON_XFAM_MOD) { dprintk(KERN_DEBUG PFX "AMD Opteron Processor found\n"); } else { printk(KERN_INFO PFX "AMD Athlon 64 or AMD Opteron processor required\n"); - return 0; + 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"); - return 0; + printk(KERN_INFO PFX "No freq change capabilities\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"); - return 0; + goto out; } - printk(KERN_INFO PFX "Found AMD64 processor supporting PowerNow (" VERSION ")\n"); - return 1; + rc = 1; + + 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) { unsigned int j; - u8 lastfid = 0xFF; + 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); + printk(KERN_ERR PFX "vid %d bad: %x\n", j, pst[j].vid); return -EINVAL; } if (pst[j].vid < data->rvo) { /* vid + rvo >= 0 */ - printk(KERN_ERR PFX - "BIOS error - 0 vid exceeded with pstate %d\n", - j); + printk(KERN_ERR PFX "0 vid exceeded with pst %d\n", j); return -ENODEV; } - if (pst[j].vid < maxvid + data->rvo) { /* vid + rvo >= maxvid */ - printk(KERN_ERR PFX - "BIOS error - maxvid exceeded with pstate %d\n", - j); + if (pst[j].vid < maxvid + data->rvo) { /* vid + rvo >= maxvid */ + printk(KERN_ERR PFX "maxvid exceeded with pst %d\n", j); return -ENODEV; } if ((pst[j].fid > MAX_FID) || (pst[j].fid & 1) || (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 PFX "fid %d bad: %x\n", j, pst[j].fid); return -EINVAL; } if (pst[j].fid < lastfid) @@ -505,20 +501,87 @@ printk(KERN_ERR PFX "lastfid invalid\n"); return -EINVAL; } - if (lastfid > LO_FID_TABLE_TOP) { - printk(KERN_INFO PFX "first fid not from lo freq table\n"); + if (lastfid > LO_FID_TABLE_TOP) + printk(KERN_INFO PFX "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++) { + printk(KERN_INFO PFX " %d : fid %x (%d MHz), vid %x (%d mV)\n", j, + data->powernow_table[j].index & 0xff, + data->powernow_table[j].frequency/1000, + data->powernow_table[j].index >> 8, + find_milivolts_from_vid(data, data->powernow_table[j].index >> 8)); + } + if (data->batps) + printk(KERN_INFO PFX "Only %d pstates on battery\n", data->batps); +} + +static inline 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; jnumps; 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(KERN_INFO PFX "cfid %x, cvid %x\n", data->currfid, data->currvid); + data->powernow_table = powernow_table; + print_basics(data); + + for (j = 0; j < data->numps; j++) + if ((pst[j].fid==data->currfid) && (pst[j].vid==data->currvid)) + return 0; + + dprintk(KERN_ERR PFX "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) +static inline int find_psb_table(struct powernow_k8_data *data) { - struct cpufreq_frequency_table *powernow_table; struct psb_s *psb; - struct pst_s *pst; - unsigned int i, j; + unsigned int i; u32 mvs; u8 maxvid; @@ -530,133 +593,192 @@ if (memcmp(psb, PSB_ID_STRING, PSB_ID_STRING_LEN) != 0) continue; - dprintk(KERN_DEBUG PFX "found PSB header at 0x%p\n", psb); - - dprintk(KERN_DEBUG PFX "table vers: 0x%x\n", psb->tableversion); + dprintk(KERN_DEBUG PFX "found PSB header at %p\n", psb); + dprintk(KERN_DEBUG PFX "table version: %x\n", + psb->tableversion); if (psb->tableversion != PSB_VERSION_1_4) { - printk(KERN_INFO BFX "PSB table is not v1.4\n"); + printk(KERN_INFO PFX "PSB table is not v1.4\n"); return -ENODEV; } - dprintk(KERN_DEBUG PFX "flags: 0x%x\n", psb->flags1); + dprintk(KERN_DEBUG PFX "flags: %x\n", psb->flags1); if (psb->flags1) { - printk(KERN_ERR BFX "unknown flags\n"); + printk(KERN_ERR PFX "unknown flags\n"); return -ENODEV; } data->vstable = psb->voltagestabilizationtime; dprintk(KERN_INFO PFX "voltage stabilization time: %d(*20us)\n", data->vstable); - dprintk(KERN_DEBUG PFX "flags2: 0x%x\n", psb->flags2); + dprintk(KERN_DEBUG PFX "flags2: %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; - printk(KERN_INFO PFX "voltage stable in %d usec", data->vstable * 20); - if (data->batps) - printk(", only %d lowest states on battery", batps); - printk(", ramp voltage offset: %d", data->rvo); - printk(", isochronous relief time: %d", data->irt); - printk(", maximum voltage step: %d\n", mvs); + dprintk(KERN_INFO PFX "ramp voltage offset: %d\n", data->rvo); + dprintk(KERN_INFO PFX "isochronous relief time: %d\n", data->irt); + dprintk(KERN_INFO PFX "maximum voltage step: %d - %x\n", + mvs, data->vidmvs); dprintk(KERN_DEBUG PFX "numpst: 0x%x\n", psb->numpst); if (psb->numpst != 1) { - printk(KERN_ERR BFX "numpst must be 1\n"); + printk(KERN_ERR PFX "numpst must be 1\n"); return -ENODEV; } - dprintk(KERN_DEBUG PFX "cpuid: 0x%x\n", psb->cpuid); - data->plllock = psb->plllocktime; - printk(KERN_INFO PFX "pll lock time: 0x%x, ", data->plllock); - + dprintk(KERN_INFO PFX "plllocktime: %x (units 1us)\n", + psb->plllocktime); + dprintk(KERN_INFO PFX "maxfid: %x\n", psb->maxfid); + dprintk(KERN_INFO PFX "maxvid: %x\n", psb->maxvid); maxvid = psb->maxvid; - printk("maxfid 0x%x (%d MHz), maxvid 0x%x\n", - psb->maxfid, find_freq_from_fid(psb->maxfid), maxvid); data->numps = psb->numpstates; - if (data->numps < 2) { - printk(KERN_ERR BFX "no p states to transition\n"); - return -ENODEV; - } + dprintk(KERN_INFO PFX "numpstates: %x\n", data->numps); + return fill_powernow_table(data, (struct pst_s *)(psb+1), maxvid); + } - if (batps == 0) { - batps = data->numps; - } else if (batps > data->numps) { - printk(KERN_ERR BFX "batterypstates > numpstates\n"); - batps = data->numps; - } else { - printk(KERN_ERR PFX - "Restricting operation to %d p-states\n", batps); - printk(KERN_ERR PFX - "Check for an updated driver to access all " - "%d p-states\n", data->numps); - } + /* + * 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\n"); + return -ENODEV; +} - if (data->numps <= 1) { - printk(KERN_ERR PFX "only 1 p-state to transition\n"); - return -ENODEV; - } - pst = (struct pst_s *) (psb + 1); - if (check_pst_table(data, pst, maxvid)) - return -EINVAL; +#ifdef CONFIG_X86_POWERNOW_K8_ACPI - 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; - } +static inline void powernow_k8_acpi_pst_values(struct powernow_k8_data *data, unsigned int index) +{ + if (!data->acpi_data.state_count) + return; - for (j = 0; j < psb->numpstates; j++) { - powernow_table[j].index = pst[j].fid; /* lower 8 bits */ - powernow_table[j].index |= (pst[j].vid << 8); /* upper 8 bits */ - } + 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->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) +{ + int i; + int cntlofreq = 0; + struct cpufreq_frequency_table *powernow_table; + + if (acpi_processor_register_performance(&data->acpi_data, data->cpu)) { + dprintk(KERN_DEBUG PFX "register performance failed\n"); + return -EIO; + } + + /* verify the data contained in the ACPI structures */ + if (data->acpi_data.state_count <= 1) { + dprintk(KERN_DEBUG PFX "No ACPI P-States\n"); + goto err_out; + } - /* If you want to override your frequency tables, this - is right place. */ + 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(KERN_DEBUG PFX "Invalid control/status registers\n"); + 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(KERN_ERR PFX "powernow_table memory alloc failure\n"); + goto err_out; + } - for (j = 0; j < data->numps; j++) { - powernow_table[j].frequency = find_freq_from_fid(powernow_table[j].index & 0xff)*1000; - printk(KERN_INFO PFX " %d : fid 0x%x (%d MHz), vid 0x%x\n", j, - powernow_table[j].index & 0xff, - powernow_table[j].frequency/1000, - powernow_table[j].index >> 8); + 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; + + dprintk(KERN_INFO PFX " %d : fid %x, vid %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(KERN_INFO PFX "invalid freq %u kHz\n", powernow_table[i].frequency); + powernow_table[i].frequency = CPUFREQ_ENTRY_INVALID; + continue; } - powernow_table[data->numps].frequency = CPUFREQ_TABLE_END; - powernow_table[data->numps].index = 0; + /* verify only 1 entry from the lo frequency table */ + if ((fid < HI_FID_TABLE_BOTTOM) && (cntlofreq++)) { + printk(KERN_ERR PFX "Too many lo freq table entries\n"); + goto err_out; + } - if (query_current_values_with_pending_wait(data)) { - kfree(powernow_table); - return -EIO; + 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; } + } + 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; + print_basics(data); - printk(KERN_INFO PFX "currfid 0x%x (%d MHz), currvid 0x%x\n", - data->currfid, find_freq_from_fid(data->currfid), data->currvid); + powernow_k8_acpi_pst_values(data, 0); - for (j = 0; j < data->numps; j++) - if ((pst[j].fid==data->currfid) && (pst[j].vid==data->currvid)) - return 0; + return 0; - printk(KERN_ERR BFX "currfid/vid do not match PST, ignoring\n"); - return 0; - } + 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; - printk(KERN_ERR BFX "no PSB\n"); return -ENODEV; } +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 inline int powernow_k8_cpu_init_acpi(struct powernow_k8_data *data) { return -ENODEV; } +static inline void powernow_k8_cpu_exit_acpi(struct powernow_k8_data *data) { return; } +static inline 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(struct powernow_k8_data *data, unsigned int index) +static inline int transition_frequency(struct powernow_k8_data *data, unsigned int index) { u32 fid; u32 vid; int res; struct cpufreq_freqs freqs; + dprintk(KERN_DEBUG PFX "cpu %d transition to index %u\n", + smp_processor_id(), index ); + /* 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. @@ -665,15 +787,13 @@ fid = data->powernow_table[index].index & 0xFF; vid = (data->powernow_table[index].index & 0xFF00) >> 8; - dprintk(KERN_DEBUG PFX "table matched fid 0x%x, giving vid 0x%x\n", - fid, vid); + dprintk(KERN_DEBUG PFX "matched fid %x, giving vid %x\n", fid, vid); if (query_current_values_with_pending_wait(data)) return 1; if ((data->currvid == vid) && (data->currfid == fid)) { - dprintk(KERN_DEBUG PFX - "target matches current values (fid 0x%x, vid 0x%x)\n", + dprintk(KERN_DEBUG PFX "target matches curr (fid %x, vid %x)\n", fid, vid); return 0; } @@ -685,17 +805,19 @@ return 1; } - dprintk(KERN_DEBUG PFX "changing to fid 0x%x, vid 0x%x\n", fid, vid); - + dprintk(KERN_DEBUG PFX "cpu %d, changing to fid %x, vid %x\n", + smp_processor_id(), fid, vid); freqs.cpu = data->cpu; - freqs.old = find_freq_from_fid(data->currfid); - freqs.new = find_freq_from_fid(fid); + freqs.old = find_khz_freq_from_fid(data->currfid); + freqs.new = find_khz_freq_from_fid(fid); cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE); + down(&fidvid_sem); res = transition_fid_vid(data, fid, vid); + up(&fidvid_sem); - freqs.new = find_freq_from_fid(data->currfid); + freqs.new = find_khz_freq_from_fid(data->currfid); cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE); return res; @@ -704,42 +826,68 @@ /* 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 = data->currfid; u32 checkvid = data->currvid; unsigned int newstate; + int ret = -EIO; + + /* 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); + goto err_out; + } + + /* from this point, do not exit without restoring preempt and cpu */ + preempt_disable(); if (pending_bit_stuck()) { - printk(KERN_ERR PFX "drv targ fail: change pending bit set\n"); - return -EIO; + printk(KERN_ERR PFX "failing targ, change pending bit set\n"); + goto err_out; } - dprintk(KERN_DEBUG PFX "targ: %d kHz, min %d, max %d, relation %d\n", - targfreq, pol->min, pol->max, relation); + dprintk(KERN_DEBUG PFX "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)) - return -EIO; + if (query_current_values_with_pending_wait(data)) { + ret = -EIO; + goto err_out; + } - dprintk(KERN_DEBUG PFX "targ: curr fid 0x%x, vid 0x%x\n", + dprintk(KERN_DEBUG PFX "targ: curr fid %x, vid %x\n", 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", + printk(KERN_ERR PFX "out of sync, fid %x %x, vid %x %x\n", checkfid, data->currfid, checkvid, data->currvid); } - if (cpufreq_frequency_table_target(pol, data->powernow_table, targfreq, relation, &newstate)) - return -EINVAL; - - if (transition_frequency(data, newstate)) { + if (cpufreq_frequency_table_target(pol, data->powernow_table, targfreq, relation, &newstate)) + goto err_out; + + powernow_k8_acpi_pst_values(data, newstate); + + if (transition_frequency(data, newstate)) + { printk(KERN_ERR PFX "transition frequency failed\n"); - return 1; + ret = 1; + goto err_out; } - pol->cur = 1000 * find_freq_from_fid(data->currfid); + pol->cur = find_khz_freq_from_fid(data->currfid); + ret = 0; - return 0; + err_out: + preempt_enable_no_resched(); + set_cpus_allowed(current, oldmask); + schedule(); + + return ret; } /* Driver entry point to verify the policy and range of frequencies */ @@ -747,19 +895,18 @@ { struct powernow_k8_data *data = powernow_data[pol->cpu]; - if (pending_bit_stuck()) { - printk(KERN_ERR PFX "failing verify, change pending bit set\n"); - return -EIO; - } - return cpufreq_frequency_table_verify(pol, data->powernow_table); } /* per CPU init entry point to the driver */ static int __init powernowk8_cpu_init(struct cpufreq_policy *pol) { - struct powernow_k8_data *data; + cpumask_t oldmask = CPU_MASK_ALL; int rc; + struct powernow_k8_data *data; + + if (!check_supported_cpu(pol->cpu)) + return -ENODEV; data = kmalloc(sizeof(struct powernow_k8_data), GFP_KERNEL); if (!data) { @@ -770,18 +917,39 @@ data->cpu = pol->cpu; - if (pol->cpu != 0) { - printk(KERN_ERR PFX "init not cpu 0\n"); - kfree(data); - return -ENODEV; + 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 (pol->cpu != 0) { + printk(KERN_ERR PFX "init - cpu 0\n"); + kfree(data); + return -ENODEV; + } + + if ((num_online_cpus() != 1) || (num_possible_cpus() != 1)) { + printk(KERN_INFO PFX "MP systems not supported by PSB BIOS structure\n"); + kfree(data); + return 0; + } + + rc = find_psb_table(data); + if (rc) { + kfree(data); + return -ENODEV; + } } - pol->governor = CPUFREQ_DEFAULT_GOVERNOR; + /* only run on specific CPU from here on */ + oldmask = current->cpus_allowed; + set_cpus_allowed(current, cpumask_of_cpu(pol->cpu)); + schedule(); - rc = find_psb_table(data); - if (rc) { - kfree(data); - return -ENODEV; + if (smp_processor_id() != pol->cpu) { + printk(KERN_ERR "limiting to cpu %u failed\n", pol->cpu); + goto err_out; } if (pending_bit_stuck()) { @@ -789,15 +957,25 @@ goto err_out; } + if (query_current_values_with_pending_wait(data)) { + goto err_out; + } + + fidvid_msr_init(); + + + /* run on any CPU again */ + set_cpus_allowed(current, oldmask); + schedule(); + + pol->governor = CPUFREQ_DEFAULT_GOVERNOR; + /* Take a crude guess here. - * That guess was in microseconds, so multply with 1000 */ + * 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 (query_current_values_with_pending_wait(data)) - return -EIO; - - pol->cur = 1000 * find_freq_from_fid(data->currfid); + pol->cur = find_khz_freq_from_fid(data->currfid); dprintk(KERN_DEBUG PFX "policy current frequency %d kHz\n", pol->cur); /* min/max the cpu is capable of */ @@ -807,17 +985,18 @@ kfree(data); return -EINVAL; } - cpufreq_frequency_table_get_attr(data->powernow_table, pol->cpu); - printk(KERN_INFO PFX "cpu_init done, current fid 0x%x, vid 0x%x\n", - data->currfid, data->currvid); + dprintk(KERN_INFO PFX "init, curr fid %x vid %x\n", data->currfid, data->currvid); powernow_data[pol->cpu] = data; return 0; -err_out: + err_out: + set_cpus_allowed(current, oldmask); + schedule(); + kfree(data); return -ENODEV; } @@ -829,6 +1008,8 @@ if (!data) return -EINVAL; + powernow_k8_cpu_exit_acpi(data); + cpufreq_frequency_table_put_attr(pol->cpu); kfree(data->powernow_table); @@ -866,7 +1047,7 @@ if (supported_cpus == num_online_cpus()) { printk(KERN_INFO PFX "Found %d AMD Athlon 64 / Opteron processors (" VERSION ")\n", - supported_cpus); + supported_cpus); return cpufreq_register_driver(&cpufreq_amd64_driver); } @@ -885,5 +1066,5 @@ MODULE_DESCRIPTION("AMD Athlon 64 and Opteron processor frequency driver."); MODULE_LICENSE("GPL"); -module_init(powernowk8_init); +late_initcall(powernowk8_init); module_exit(powernowk8_exit); --- clean-dj//arch/i386/kernel/cpu/cpufreq/powernow-k8.h 2004-04-02 18:05:38.000000000 +0200 +++ linux-pn/arch/i386/kernel/cpu/cpufreq/powernow-k8.h 2004-04-02 18:39:13.000000000 +0200 @@ -1,46 +1,46 @@ /* - * (c) 2003 Advanced Micro Devices, Inc. + * (c) 2003, 2004 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 + * GNU general public license version 2. See "COPYING" or * http://www.gnu.org/licenses/gpl.html */ struct powernow_k8_data { - unsigned int cpu; + unsigned int cpu; - u32 numps; /* number of p-states */ - u32 batps; /* number of p-states supported on battery */ + u32 numps; /* number of p-states */ + u32 batps; /* number of p-states supported on battery */ /* these values are constant when the PSB is used to determine * vid/fid pairings, but are modified during the ->target() call * when ACPI is used */ - u32 rvo; /* ramp voltage offset */ - u32 irt; /* isochronous relief time */ - u32 vidmvs; /* usable value calculated from mvs */ - u32 vstable; /* voltage stabilization time, units 20 us */ - u32 plllock; /* pll lock time, units 1 us */ + u32 rvo; /* ramp voltage offset */ + u32 irt; /* isochronous relief time */ + u32 vidmvs; /* usable value calculated from mvs */ + u32 vstable; /* voltage stabilization time, units 20 us */ + u32 plllock; /* pll lock time, units 1 us */ /* keep track of the current fid / vid */ - u32 currvid; - u32 currfid; + u32 currvid; + u32 currfid; /* the powernow_table includes all frequency and vid/fid pairings: * fid are the lower 8 bits of the index, vid are the upper 8 bits. * frequency is in kHz */ - struct cpufreq_frequency_table *powernow_table; -}; + struct cpufreq_frequency_table *powernow_table; +#ifdef CONFIG_X86_POWERNOW_K8_ACPI + /* the acpi table needs to be kept. it's only available if ACPI was + * used to determine valid frequency/vid/fid states */ + struct acpi_processor_performance acpi_data; +#endif +}; /* processor's cpuid instruction support */ -#define CPUID_PROCESSOR_SIGNATURE 1 /* function 1 */ -#define CPUID_F1_FAM 0x00000f00 /* family mask */ -#define CPUID_F1_XFAM 0x0ff00000 /* extended family mask */ -#define CPUID_F1_MOD 0x000000f0 /* model mask */ -#define CPUID_F1_STEP 0x0000000f /* stepping level mask */ -#define CPUID_XFAM_MOD 0x0ff00ff0 /* xtended fam, fam + model */ -#define ATHLON64_XFAM_MOD 0x00000f40 /* xtended fam, fam + model */ -#define OPTERON_XFAM_MOD 0x00000f50 /* xtended fam, fam + model */ -#define ATHLON64_REV_C0 8 +#define CPUID_PROCESSOR_SIGNATURE 1 /* function 1 */ +#define CPUID_XFAM_MOD 0x0ff00ff0 /* extended fam, fam + model */ +#define ATHLON64_XFAM_MOD 0x00000f40 /* extended fam, fam + model */ +#define OPTERON_XFAM_MOD 0x00000f50 /* extended fam, fam + model */ #define CPUID_GET_MAX_CAPABILITIES 0x80000000 #define CPUID_FREQ_VOLT_CAPABILITIES 0x80000007 #define P_STATE_TRANSITION_CAPABLE 6 @@ -49,7 +49,6 @@ /* writes (wrmsr - opcode 0f 30), the register number is placed in ecx, and */ /* the value to write is placed in edx:eax. For reads (rdmsr - opcode 0f 32), */ /* the register number is placed in ecx, and the data is returned in edx:eax. */ - #define MSR_FIDVID_CTL 0xc0010041 #define MSR_FIDVID_STATUS 0xc0010042 @@ -73,10 +72,24 @@ #define MSR_S_HI_MAX_WORKING_VID 0x001f0000 #define MSR_S_HI_START_VID 0x00001f00 #define MSR_S_HI_CURRENT_VID 0x0000001f +#define MSR_C_HI_STP_GNT_BENIGN 0x00000001 + +/* + There are restrictions frequencies have to follow: + - only 1 entry in the low fid table ( <=1.4GHz ) + - lowest entry in the high fid table must be >= 2 * the + entry in the low fid table + - lowest entry in the high fid table must be a <= 200MHz + + 2 * the entry in the low fid table + - the parts can only step at 200 MHz intervals, so 1.9 GHz is + never valid + - lowest frequency must be >= interprocessor hypertransport link + speed (only applies to MP systems obviously) + */ /* fids (frequency identifiers) are arranged in 2 tables - lo and hi */ -#define LO_FID_TABLE_TOP 6 -#define HI_FID_TABLE_BOTTOM 8 +#define LO_FID_TABLE_TOP 6 /* fid values marking the boundary */ +#define HI_FID_TABLE_BOTTOM 8 /* between the low and high tables */ #define LO_VCOFREQ_TABLE_TOP 1400 /* corresponding vco frequency values */ #define HI_VCOFREQ_TABLE_BOTTOM 1600 @@ -84,33 +97,44 @@ #define MIN_FREQ_RESOLUTION 200 /* fids jump by 2 matching freq jumps by 200 */ #define MAX_FID 0x2a /* Spec only gives FID values as far as 5 GHz */ - #define LEAST_VID 0x1e /* Lowest (numerically highest) useful vid value */ - #define MIN_FREQ 800 /* Min and max freqs, per spec */ #define MAX_FREQ 5000 -#define INVALID_FID_MASK 0xffffffc1 /* not a valid fid if these bits are set */ - -#define INVALID_VID_MASK 0xffffffe0 /* not a valid vid if these bits are set */ +#define INVALID_FID_MASK 0xffffffc1 /* not a valid fid if these bits are set */ +#define INVALID_VID_MASK 0xffffffe0 /* not a valid vid if these bits are set */ #define STOP_GRANT_5NS 1 /* min poss memory access latency for voltage change */ - #define PLL_LOCK_CONVERSION (1000/5) /* ms to ns, then divide by clock period */ - #define MAXIMUM_VID_STEPS 1 /* Current cpus only allow a single step of 25mV */ +#define VST_UNITS_20US 20 /* Voltage Stabilization Time is in units of 20us */ -#define VST_UNITS_20US 20 /* Voltage Stabalization Time is in units of 20us */ +/* + * Most values of interest are enocoded in a single field of the _PSS + * entries: the "control" value. + */ +#define IRT_SHIFT 30 +#define RVO_SHIFT 28 +#define PLL_L_SHIFT 20 +#define MVS_SHIFT 18 +#define VST_SHIFT 11 +#define VID_SHIFT 6 +#define IRT_MASK 3 +#define RVO_MASK 3 +#define PLL_L_MASK 0x7f +#define MVS_MASK 3 +#define VST_MASK 0x7f +#define VID_MASK 0x1f +#define FID_MASK 0x3f + /* -Version 1.4 of the PSB table. This table is constructed by BIOS and is -to tell the OS's power management driver which VIDs and FIDs are -supported by this particular processor. This information is obtained from -the data sheets for each processor model by the system vendor and -incorporated into the BIOS. -If the data in the PSB / PST is wrong, then this driver will program the -wrong values into hardware, which is very likely to lead to a crash. -*/ + * Version 1.4 of the PSB table. This table is constructed by BIOS and is + * to tell the OS's power management driver which VIDs and FIDs are + * supported by this particular processor. + * If the data in the PSB / PST is wrong, then this driver will program the + * wrong values into hardware, which is very likely to lead to a crash. + */ #define PSB_ID_STRING "AMDK7PNOW!" #define PSB_ID_STRING_LEN 10 @@ -146,4 +171,4 @@ static inline int core_voltage_pre_transition(struct powernow_k8_data *data, u32 reqvid); static inline int core_voltage_post_transition(struct powernow_k8_data *data, u32 reqvid); static inline int core_frequency_transition(struct powernow_k8_data *data, u32 reqfid); - +static inline void powernow_k8_acpi_pst_values(struct powernow_k8_data *data, unsigned int index); -- When do you have a heart between your knees? [Johanka's followup: and *two* hearts?]