diff -uNr a/arch/x86/kernel/cpu/cpufreq/powernow-k8.c b/arch/x86/kernel/cpu/cpufreq/powernow-k8.c --- a/arch/x86/kernel/cpu/cpufreq/powernow-k8.c 2009-06-28 14:51:43.513032423 +0200 +++ b/arch/x86/kernel/cpu/cpufreq/powernow-k8.c 2009-06-28 16:18:39.275659837 +0200 @@ -1,27 +1,25 @@ /* - * (c) 2003-2006 Advanced Micro Devices, Inc. + * (C) 2007-2009 David Gaarenstroom, for the Linux-PHC project. + * * 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 + * Support: Linux-PHC forum http://phc.athousandnights.de + * and/or David Gaarenstroom + * + * Partly based on work done by Holger Bocklet + * and Dean Gaudet . + * Inspired by CPU Rightmark Clock Utility. * - * Based on the powernow-k7.c module written by Dave Jones. - * (C) 2003 Dave Jones on behalf of SuSE Labs + * Based on the powernow-k8.c module written by Mark Langsdorf. + * (C) 2003-2006 Advanced Micro Devices, Inc. + * (C) 2003 Dave Jones on behalf of SuSE Labs * (C) 2004 Dominik Brodowski * (C) 2004 Pavel Machek * 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 @@ -42,10 +40,23 @@ #include #include +#define PHC_VERSION "0.4.0" #define PFX "powernow-k8: " #define VERSION "version 2.20.00" #include "powernow-k8.h" +/* Module parameters */ +static int direct_transitions = 0; +module_param(direct_transitions, bool, 0444); +MODULE_PARM_DESC(direct_transitions, "Use direct P-States transitions"); +static int min_800MHz = 0; +module_param(min_800MHz, bool, 0444); +MODULE_PARM_DESC(min_800MHz, "Allow 800MHz as lowest frequency with direct transitions enabled"); +static int be_maxfid = -1; +module_param(be_maxfid, int, 0444); +MODULE_PARM_DESC(be_maxfid, "Override max FID for Black Edition processors"); + + /* serialize freq changes */ static DEFINE_MUTEX(fidvid_mutex); @@ -60,19 +71,23 @@ } #endif +#ifndef FW_BUG +#define FW_BUG "[Firmware Bug]: " +#endif + /* Return a frequency in MHz, given an input fid */ -static u32 find_freq_from_fid(u32 fid) +static inline 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) +static inline u32 find_khz_freq_from_fid(u32 fid) { return 1000 * find_freq_from_fid(fid); } -static u32 find_khz_freq_from_pstate(struct cpufreq_frequency_table *data, +static inline u32 find_khz_freq_from_pstate(struct cpufreq_frequency_table *data, u32 pstate) { return data[pstate].frequency; @@ -84,7 +99,7 @@ * only from corresponding high fids. This returns "high" fid corresponding to * "low" one. */ -static u32 convert_fid_to_vco_fid(u32 fid) +static inline u32 convert_fid_to_vco_fid(u32 fid) { if (fid < HI_FID_TABLE_BOTTOM) return 8 + (2 * fid); @@ -96,7 +111,7 @@ * 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) +static inline int pending_bit_stuck(void) { u32 lo, hi; @@ -148,17 +163,15 @@ } /* the isochronous relief time */ -static void count_off_irt(struct powernow_k8_data *data) +static inline void count_off_irt(struct powernow_k8_data *data) { udelay((1 << data->irt) * 10); - return; } /* the voltage stabilization time */ -static void count_off_vst(struct powernow_k8_data *data) +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) */ @@ -290,7 +303,7 @@ } /* Change hardware pstate by single MSR write */ -static int transition_pstate(struct powernow_k8_data *data, u32 pstate) +static inline int transition_pstate(struct powernow_k8_data *data, u32 pstate) { wrmsr(MSR_PSTATE_CTRL, pstate, 0); data->currpstate = pstate; @@ -383,15 +396,7 @@ /* Phase 2 - core frequency transition */ static int core_frequency_transition(struct powernow_k8_data *data, u32 reqfid) { - u32 vcoreqfid, vcocurrfid, vcofiddiff; - u32 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; - } + u32 savevid = data->currvid; if (data->currfid == reqfid) { printk(KERN_ERR PFX "ph2 null fid transition 0x%x\n", @@ -399,38 +404,54 @@ 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); + /* + * Unless direct transitions are allowed, follow the algorithm provided + * by the AMD documentation. Extend this by allowing every frequency to hop + * by 200MHz. + */ + if (!direct_transitions) { + u32 vcoreqfid, vcocurrfid, vcofiddiff; - 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; + 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; } + 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; + : vcoreqfid - vcocurrfid; + + while (vcofiddiff > 2) { + u32 fid_interval = (data->currfid & 1) ? 1 : 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)) @@ -598,7 +619,7 @@ " %d\n", j); return -ENODEV; } - if (j && (pst[j].fid < HI_FID_TABLE_BOTTOM)) { + if (j && (pst[j].fid < HI_FID_TABLE_BOTTOM) && (!direct_transitions)) { /* Only first fid is allowed to be in "low" range */ printk(KERN_ERR FW_BUG PFX "two low fids - %d : " "0x%x\n", j, pst[j].fid); @@ -618,7 +639,7 @@ return 0; } -static void invalidate_entry(struct powernow_k8_data *data, unsigned int entry) +static inline void invalidate_entry(struct powernow_k8_data *data, unsigned int entry) { data->powernow_table[entry].frequency = CPUFREQ_ENTRY_INVALID; } @@ -626,6 +647,7 @@ 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) { @@ -823,19 +845,26 @@ if (!data->acpi_data.state_count || (cpu_family == CPU_HW_PSTATE)) return; - control = data->acpi_data.states[index].control; data->irt = (control - >> IRT_SHIFT) & IRT_MASK; data->rvo = (control >> - RVO_SHIFT) & RVO_MASK; data->exttype = (control - >> EXT_TYPE_SHIFT) & EXT_TYPE_MASK; - data->plllock = (control >> PLL_L_SHIFT) & PLL_L_MASK; data->vidmvs = 1 - << ((control >> MVS_SHIFT) & MVS_MASK); data->vstable = - (control >> VST_SHIFT) & VST_MASK; } + control = data->acpi_data.states[index].control; + data->irt = (control >> IRT_SHIFT) & IRT_MASK; + data->rvo = (control >> RVO_SHIFT) & RVO_MASK; + data->exttype = (control >> EXT_TYPE_SHIFT) & EXT_TYPE_MASK; + data->plllock = (control >> PLL_L_SHIFT) & PLL_L_MASK; + data->vidmvs = 1 << ((control >> MVS_SHIFT) & MVS_MASK); + data->vstable = (control >> VST_SHIFT) & VST_MASK; +} static int powernow_k8_cpu_init_acpi(struct powernow_k8_data *data) { struct cpufreq_frequency_table *powernow_table; + struct cpufreq_frequency_table *powernow_table_default; int ret_val = -ENODEV; acpi_integer control, status; + size_t numps = 0; + size_t table_size = 0; + u32 maxfid; + u32 minfid; + if (acpi_processor_register_performance(&data->acpi_data, data->cpu)) { dprintk("register performance failed: bad ACPI data\n"); @@ -858,9 +887,41 @@ goto err_out; } + if (cpu_family == CPU_HW_PSTATE || !direct_transitions){ + numps = data->acpi_data.state_count; + } else { + /* Calculate the needed tablesize to hold every single frequency. + Since with direct_transitions we can use every fid from fid[max] + to fid[min] including fid[min], the size is fid[max] - fid[min] + 1. + Since lowest FID is 0, the needed arraysize is fid[max] + 1 + */ + /* Get the first FID/VID. First FID is assumed to be the higest */ + if (data->exttype) { + maxfid = data->acpi_data.states[0].status & EXT_FID_MASK; + minfid = data->acpi_data.states[data->acpi_data.state_count - 1].status & EXT_FID_MASK; + } else { + maxfid = data->acpi_data.states[0].control & FID_MASK; + minfid = data->acpi_data.states[data->acpi_data.state_count - 1].control & FID_MASK; + } + + /* If we explicitly enable a FID as maximum, do so. (risky! Only works on Black Edition processors) */ + if ((be_maxfid >= 0) && (be_maxfid <= MAX_FID)) + maxfid = be_maxfid; + + /* If we explicitly enable 800MHz as minimum, do so. (risky!) */ + if (min_800MHz) + minfid = 0; + + /* 1000MHz should always be supported */ + else if (minfid > 2) + minfid = 2; + + numps = maxfid - minfid + 1; + } + /* fill in data->powernow_table */ - powernow_table = kmalloc((sizeof(struct cpufreq_frequency_table) - * (data->acpi_data.state_count + 1)), GFP_KERNEL); + table_size = sizeof(struct cpufreq_frequency_table) * (numps + 1); + powernow_table = kmalloc(table_size, GFP_KERNEL); if (!powernow_table) { dprintk("powernow_table memory alloc failure\n"); goto err_out; @@ -868,22 +929,33 @@ if (cpu_family == CPU_HW_PSTATE) ret_val = fill_powernow_table_pstate(data, powernow_table); + else if (direct_transitions) + ret_val = fill_powernow_table_fidvid_dt(data, powernow_table, numps, maxfid); 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; + powernow_table[numps].frequency = CPUFREQ_TABLE_END; + powernow_table[numps].index = 0; data->powernow_table = powernow_table; /* fill in data */ - data->numps = data->acpi_data.state_count; + data->numps = numps; if (cpumask_first(cpu_core_mask(data->cpu)) == data->cpu) print_basics(data); powernow_k8_acpi_pst_values(data, 0); + /* save default table */ + powernow_table_default = kmalloc(table_size, GFP_KERNEL); + if (!powernow_table_default) { + printk(KERN_ERR PFX "powernow_table_default memory alloc failure\n"); + goto err_out_mem; + } + memcpy(powernow_table_default, powernow_table, table_size); + + data->powernow_table_default = powernow_table_default; + /* notify BIOS that we exist */ acpi_processor_notify_smm(THIS_MODULE); @@ -1028,6 +1100,76 @@ return 0; } +static int fill_powernow_table_fidvid_dt(struct powernow_k8_data *data, + struct cpufreq_frequency_table *powernow_table, size_t numps, u32 maxfid) +{ + int i; + u32 fid; + u32 vid = VID_OFF; + size_t bmindex = 0; + + for (i = 0, fid = maxfid; i < numps; i++, fid--) { + int j; + u32 acpi_fid = MAX_FID; + + for (j = bmindex; j < data->acpi_data.state_count; j++) { + u32 acpi_vid; + + if (data->exttype) { + acpi_fid = data->acpi_data.states[j].status & EXT_FID_MASK; + acpi_vid = (data->acpi_data.states[j].status >> VID_SHIFT) & EXT_VID_MASK; + } else { + acpi_fid = data->acpi_data.states[j].control & FID_MASK; + acpi_vid = (data->acpi_data.states[j].control >> VID_SHIFT) & VID_MASK; + } + + if (acpi_fid >= fid) { + bmindex = j; + vid = acpi_vid; + } + if (acpi_fid <= fid) + break; + } + + dprintk(" %d : fid 0x%x, vid 0x%x\n", i, fid, vid); + + powernow_table[i].index = (fid & 0xFF) + |((vid & 0xFF) << 8); + + /* For now, mark all entries not in the acpi_data.states as invalid */ + if (acpi_fid == fid) { + powernow_table[i].frequency = find_khz_freq_from_fid(acpi_fid); + } else { + powernow_table[i].frequency = CPUFREQ_ENTRY_INVALID; + continue; + } + + /* 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; + } + + if (powernow_table[i].frequency != (data->acpi_data.states[bmindex].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[bmindex].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) @@ -1054,8 +1196,7 @@ static int transition_frequency_fidvid(struct powernow_k8_data *data, unsigned int index) { - u32 fid = 0; - u32 vid = 0; + u32 fid, vid; int res, i; struct cpufreq_freqs freqs; @@ -1068,7 +1209,6 @@ */ 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)) @@ -1080,7 +1220,12 @@ return 0; } - if ((fid < HI_FID_TABLE_BOTTOM) && + /* If we do not allow direct transitions, and follow the algorithm + * provided by AMD, we cannot transition from a low frequency + * to another low frequency + */ + if ((!direct_transitions) && + (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", @@ -1235,6 +1380,433 @@ return cpufreq_frequency_table_verify(pol, data->powernow_table); } +static inline int first_valid_entry(struct cpufreq_frequency_table* table, int entry) +{ + while (table[entry].frequency == CPUFREQ_ENTRY_INVALID) + entry++; + + return entry; +} + +static ssize_t show_controls(struct cpufreq_frequency_table *table, int entries, char *buf) +{ + ssize_t count = 0; + int j; + + for (j = first_valid_entry(table, 0); j < entries; j = first_valid_entry(table, j + 1)) { + unsigned int fid = table[j].index & 0xff; + unsigned int vid = (table[j].index >> 8) & 0xff; + count += snprintf(&buf[count], PAGE_SIZE - count, "%u:%u ", fid, vid); + } + if (count > 0) { + /* overwrite last space with LF and ''\0' */ + snprintf(&buf[count-1], PAGE_SIZE - count, "\n"); + } + return count; +} + +static ssize_t show_powernow_k8_phc_default_controls(struct cpufreq_policy *pol, char *buf) +{ + struct powernow_k8_data *data = per_cpu(powernow_data, pol->cpu); + return show_controls(data->powernow_table_default, data->numps, buf); +} + +static ssize_t show_powernow_k8_phc_controls(struct cpufreq_policy *pol, char *buf) +{ + struct powernow_k8_data *data = per_cpu(powernow_data, pol->cpu); + return show_controls(data->powernow_table, data->numps, buf); +} + +/* + * store_powernow_k8_phc_controls: + * Verify and store user supplied FID:VID pairs. + * This table is allowed to change, cpufreq is noted afterwards. + */ +static ssize_t store_powernow_k8_phc_controls(struct cpufreq_policy *pol, const char *buf, size_t count) +{ + int i, entry; + const char *curr_buf = buf; + struct powernow_k8_data *data = per_cpu(powernow_data, pol->cpu); + + if (unlikely(!data || !data->powernow_table)) + return -ENODEV; + + /* parse supplied fid/vid pairs */ + for (i = 0, entry = 0; i < data->numps && *curr_buf != '\n'; i = entry) { + char *next_buf = NULL; + unsigned long fid, vid; + + /* parse the supplied fid */ + fid = simple_strtoul(curr_buf, &next_buf, 0); + if ((next_buf == curr_buf) || next_buf == NULL) { + printk(KERN_ERR PFX "failed to parse fid value %i\n", i); + return -EINVAL; + } + + curr_buf = next_buf; + if (*curr_buf==':') + curr_buf++; + + /* parse the supplied vid */ + vid = simple_strtoul(curr_buf, &next_buf, 0); + if ((next_buf == curr_buf) || next_buf == NULL) { + printk(KERN_ERR PFX "failed to parse vid value %i\n", i); + return -EINVAL; + } + + /* lookup matching fid in the existing powernow table and + mark prepending entries as invalid */ + while (fid < (data->powernow_table[entry].index & 0xFF) + && entry < data->numps) { + data->powernow_table[entry].frequency = CPUFREQ_ENTRY_INVALID; + entry++; + } + + /* if we are beyond the last entry, bail out */ + if (entry >= data->numps) + break; + /* If the supplied frequency cannot be found in the frequencies + table, the printk an error, but still continue */ + else if (fid != (data->powernow_table[entry].index & 0xFF)) { + printk(KERN_ERR PFX "invalid fid value %lu\n", fid); + continue; + } + + data->powernow_table[entry].index = (fid & 0xFF)|((vid & 0xFF) << 8); + data->powernow_table[entry].frequency = (vid <= LEAST_VID) + ? find_khz_freq_from_fid(fid) + : CPUFREQ_ENTRY_INVALID; + entry++; + + curr_buf = next_buf; + if (*curr_buf==' ') + curr_buf++; + } + if (*curr_buf=='\n') + curr_buf++; + + /* If we did not get valid input, bail out */ + if (!i) { + return -EINVAL; + } + + /* mark any remaining entry as disabled (CPUFREQ_ENTRY_INVALID) */ + for (;entry < data->numps; entry++) + data->powernow_table[entry].frequency = CPUFREQ_ENTRY_INVALID; + + /* Recalculate the min/max frequencies */ + cpufreq_frequency_table_cpuinfo(pol, data->powernow_table); + /* Publish our new cpufreq frequency table to the cpufreq subsystem */ + cpufreq_frequency_table_get_attr(data->powernow_table, pol->cpu); + /* Print basic info on our fresh settings */ + print_basics(data); + /* Start using new fid/vid table */ + powernowk8_target(pol, pol->cur, CPUFREQ_RELATION_L); + + return (ssize_t)(curr_buf - buf); +} + +static ssize_t show_fids(struct cpufreq_frequency_table *table, int entries, char *buf) +{ + ssize_t count = 0; + int j; + + for (j = first_valid_entry(table, 0); j < entries; j = first_valid_entry(table, j + 1)) { + unsigned int fid = table[j].index & 0xff; + count += snprintf(&buf[count], PAGE_SIZE - count, "%u ", fid); + } + if (count > 0) { + /* overwrite last space with LF and ''\0' */ + snprintf(&buf[count-1],PAGE_SIZE - count, "\n"); + } + return count; +} + +static ssize_t show_powernow_k8_phc_available_fids(struct cpufreq_policy *pol, char *buf) +{ + struct powernow_k8_data *data = per_cpu(powernow_data, pol->cpu); + ssize_t count = 0; + int j; + + for (j = 0; j < data->numps; j++) { + unsigned int fid = data->powernow_table[j].index & 0xff; + count += snprintf(&buf[count], PAGE_SIZE - count, "%u ", fid); + } + if (count > 0) { + /* overwrite last space with LF and ''\0' */ + snprintf(&buf[count-1],PAGE_SIZE - count, "\n"); + } + return count; +} + +static ssize_t show_powernow_k8_phc_default_fids(struct cpufreq_policy *pol, char *buf) +{ + struct powernow_k8_data *data = per_cpu(powernow_data, pol->cpu); + return show_fids(data->powernow_table_default, data->numps, buf); +} + +static ssize_t show_powernow_k8_phc_fids(struct cpufreq_policy *pol, char *buf) +{ + struct powernow_k8_data *data = per_cpu(powernow_data, pol->cpu); + return show_fids(data->powernow_table, data->numps, buf); +} + +static ssize_t +store_powernow_k8_phc_fids(struct cpufreq_policy *pol, const char *buf, size_t count) +{ + int i, entry = 0; + const char *curr_buf = buf; + struct powernow_k8_data *data = per_cpu(powernow_data, pol->cpu); + + if (unlikely(!data || !data->powernow_table)) + return -ENODEV; + + /* parse the supplied fids */ + for (i = 0; i < data->numps && *curr_buf != '\n'; i = entry) { + char *next_buf = NULL; + unsigned long fid, vid; + + fid = simple_strtoul(curr_buf, &next_buf, 0); + if ((next_buf == curr_buf) || next_buf == NULL) { + printk(KERN_ERR PFX "failed to parse fid value %i\n", i); + return -EINVAL; + } + + /* lookup matching fid in the existing powernow table and + mark prepending entries as invalid */ + while (entry < data->numps + && fid < (data->powernow_table[entry].index & 0xFF)) { + data->powernow_table[entry].frequency = CPUFREQ_ENTRY_INVALID; + entry++; + } + + /* if we are beyond the last entry, bail out */ + if (entry >= data->numps) + break; + /* If the supplied frequency cannot be found in the frequencies + table, the printk an error, but still continue */ + else if (fid != (data->powernow_table[entry].index & 0xFF)) { + printk(KERN_ERR PFX "invalid fid value %lu\n", fid); + continue; + } + + vid = (data->powernow_table[entry].index >> 8) & 0xFF; + data->powernow_table[entry].index = (fid & 0xFF)|((vid & 0xFF) << 8); + data->powernow_table[entry].frequency = (vid <= LEAST_VID) + ? find_khz_freq_from_fid(fid) + : CPUFREQ_ENTRY_INVALID; + entry++; + + curr_buf = next_buf; + if (*curr_buf==' ') + curr_buf++; + } + if (*curr_buf == '\n') + curr_buf++; + + /* If we did not get valid input, bail out */ + if (!i) { + return -EINVAL; + } + + /* mark any remaining entry as disabled (CPUFREQ_ENTRY_INVALID) */ + for (;entry < data->numps; entry++) + data->powernow_table[entry].frequency = CPUFREQ_ENTRY_INVALID; + + /* Recalculate the min/max frequencies */ + cpufreq_frequency_table_cpuinfo(pol, data->powernow_table); + /* Publish our new cpufreq frequency table to the cpufreq subsystem */ + cpufreq_frequency_table_get_attr(data->powernow_table, pol->cpu); + /* Print basic info on our fresh settings */ + print_basics(data); + /* Start using new fid/vid table */ + powernowk8_target(pol, pol->cur, CPUFREQ_RELATION_L); + + return (ssize_t)(curr_buf - buf); +} + +static ssize_t show_vids(struct cpufreq_frequency_table *table, int entries, char *buf) +{ + ssize_t count = 0; + int j; + + for (j = 0; j < entries; j++) { + unsigned int vid = (table[j].index >> 8) & 0xff; + if (table[j].frequency != CPUFREQ_ENTRY_INVALID && vid <= LEAST_VID) + count += snprintf(&buf[count], PAGE_SIZE - count, "%u ", vid); + } + if (count > 0) { + /* overwrite last space with LF and ''\0' */ + snprintf(&buf[count-1], PAGE_SIZE - count, "\n"); + } + return count; +} + +static ssize_t show_powernow_k8_phc_default_vids(struct cpufreq_policy *pol, char *buf) +{ + struct powernow_k8_data *data = per_cpu(powernow_data, pol->cpu); + return show_vids(data->powernow_table_default, data->numps, buf); +} + +static ssize_t show_powernow_k8_phc_vids(struct cpufreq_policy *pol, char *buf) +{ + struct powernow_k8_data *data = per_cpu(powernow_data, pol->cpu); + return show_vids(data->powernow_table, data->numps, buf); +} + +static ssize_t store_powernow_k8_phc_vids(struct cpufreq_policy *pol, const char *buf, size_t count) +{ + const char *curr_buf = buf; + struct powernow_k8_data *data = per_cpu(powernow_data, pol->cpu); + int i; + + if (unlikely(!data || !data->powernow_table)) + return -ENODEV; + + for (i = first_valid_entry(data->powernow_table, 0); i < data->numps; + i = first_valid_entry(data->powernow_table, i + 1)) { + char *next_buf; + unsigned long vid = simple_strtoul(curr_buf, &next_buf, 0); + + if ((next_buf == curr_buf) || next_buf == NULL) { + printk(KERN_ERR PFX "failed to parse vid value %i\n", i); + return -EINVAL; + } + + /* Least vid is actually the highest value allowed */ + if (vid <= LEAST_VID || vid == VID_OFF) { + u32 fid = data->powernow_table[i].index & 0xFF; + data->powernow_table[i].index = fid | ((vid & 0xFF) << 8); + data->powernow_table[i].frequency = (vid <= LEAST_VID) + ? find_khz_freq_from_fid(fid) + : CPUFREQ_ENTRY_INVALID; + } else { + printk(KERN_ERR PFX + "vid value %i is out of bounds: %lu\n", i, vid); + vid = VID_OFF; + } + + curr_buf = next_buf; + if (*curr_buf==' ') + curr_buf++; + } + if (*curr_buf=='\n') + curr_buf++; + + powernowk8_target(pol, pol->cur, CPUFREQ_RELATION_L); + print_basics(data); + + return (ssize_t)(curr_buf - buf); +} + +static ssize_t show_powernow_k8_phc_version(struct cpufreq_policy *pol, char *buf) +{ + return sprintf(buf,"%s\n",PHC_VERSION); +} + +static struct freq_attr *powernow_k8_attr[10]; + +static struct freq_attr powernow_k8_phc_default_controls = { + .attr = { .name = "phc_default_controls", + .mode = 0444, + .owner=THIS_MODULE + }, + .show = show_powernow_k8_phc_default_controls, +}; + +static struct freq_attr powernow_k8_phc_controls = { + .attr = { .name = "phc_controls", + .mode = 0644, + .owner=THIS_MODULE + }, + .show = show_powernow_k8_phc_controls, + .store = store_powernow_k8_phc_controls, +}; + +static struct freq_attr powernow_k8_phc_default_vids = { + .attr = { .name = "phc_default_vids", + .mode = 0444, + .owner=THIS_MODULE + }, + .show = show_powernow_k8_phc_default_vids, +}; + +static struct freq_attr powernow_k8_phc_vids = { + .attr = { .name = "phc_vids", + .mode = 0644, + .owner=THIS_MODULE + }, + .show = show_powernow_k8_phc_vids, + .store = store_powernow_k8_phc_vids, +}; + +static struct freq_attr powernow_k8_phc_default_fids = { + .attr = { .name = "phc_default_fids", + .mode = 0444, + .owner=THIS_MODULE + }, + .show = show_powernow_k8_phc_default_fids, +}; + +static struct freq_attr powernow_k8_phc_available_fids = { + .attr = { .name = "phc_available_fids", + .mode = 0444, + .owner=THIS_MODULE + }, + .show = show_powernow_k8_phc_available_fids, +}; + +/* + * When using direct_transitions, this will be made writable in + * powernowk8_init_attr +*/ +static struct freq_attr powernow_k8_phc_fids = { + .attr = { .name = "phc_fids", + .mode = 0444, + .owner=THIS_MODULE + }, + .show = show_powernow_k8_phc_fids, +}; + +static struct freq_attr powernow_k8_phc_version = { + .attr = { .name = "phc_version", + .mode = 0444, + .owner=THIS_MODULE + }, + .show = show_powernow_k8_phc_version, +}; + + +/* set up sysfs-entries */ +static int powernowk8_init_attr(struct cpufreq_policy *pol) +{ + /* standard-entry */ + powernow_k8_attr[0] = &cpufreq_freq_attr_scaling_available_freqs; + + if (cpu_family == CPU_HW_PSTATE) { + powernow_k8_attr[1] = NULL; + return 0; + } + + powernow_k8_attr[1] = &powernow_k8_phc_version; + powernow_k8_attr[2] = &powernow_k8_phc_controls; + powernow_k8_attr[3] = &powernow_k8_phc_default_controls; + powernow_k8_attr[4] = &powernow_k8_phc_default_vids; + powernow_k8_attr[5] = &powernow_k8_phc_default_fids; + powernow_k8_attr[6] = &powernow_k8_phc_vids; + powernow_k8_attr[7] = &powernow_k8_phc_fids; + powernow_k8_attr[8] = &powernow_k8_phc_available_fids; + + /* When using direct_transitions, phc_fids can be set writable */ + if (direct_transitions) { + powernow_k8_phc_fids.store = store_powernow_k8_phc_fids; + powernow_k8_phc_fids.attr.mode = 0644; + } + + powernow_k8_attr[9] = NULL; + return 0; +} + static const char ACPI_PSS_BIOS_BUG_MSG[] = KERN_ERR FW_BUG PFX "No compatible ACPI _PSS objects found.\n" KERN_ERR FW_BUG PFX "Try again with latest BIOS.\n"; @@ -1328,6 +1900,7 @@ if (cpufreq_frequency_table_cpuinfo(pol, data->powernow_table)) { printk(KERN_ERR FW_BUG PFX "invalid powernow_table\n"); powernow_k8_cpu_exit_acpi(data); + kfree(data->powernow_table_default); kfree(data->powernow_table); kfree(data); return -EINVAL; @@ -1344,7 +1917,10 @@ per_cpu(powernow_data, pol->cpu) = data; - return 0; + /* initialize the sysfs entrys */ + if (!powernowk8_init_attr(pol)) { + return 0; + } err_out_unmask: set_cpus_allowed_ptr(current, &oldmask); @@ -1366,6 +1942,7 @@ cpufreq_frequency_table_put_attr(pol->cpu); + kfree(data->powernow_table_default); kfree(data->powernow_table); kfree(data); @@ -1379,6 +1956,7 @@ unsigned int khz = 0; unsigned int first; + //first = first_cpu(per_cpu(cpu_core_map, cpu)); first = cpumask_first(cpu_core_mask(cpu)); data = per_cpu(powernow_data, first); @@ -1408,11 +1986,6 @@ 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, @@ -1453,9 +2026,9 @@ cpufreq_unregister_driver(&cpufreq_amd64_driver); } -MODULE_AUTHOR("Paul Devriendt and " - "Mark Langsdorf "); -MODULE_DESCRIPTION("AMD Athlon 64 and Opteron processor frequency driver."); +MODULE_AUTHOR("David Gaarenstroom "); +MODULE_DESCRIPTION("AMD Athlon 64 and Opteron processor frequency driver " + "(Linux-PHC version)."); MODULE_LICENSE("GPL"); late_initcall(powernowk8_init); diff -uNr a/arch/x86/kernel/cpu/cpufreq/powernow-k8.h b/arch/x86/kernel/cpu/cpufreq/powernow-k8.h --- a/arch/x86/kernel/cpu/cpufreq/powernow-k8.h 2009-06-28 14:51:43.513032423 +0200 +++ b/arch/x86/kernel/cpu/cpufreq/powernow-k8.h 2009-06-28 15:08:39.425286904 +0200 @@ -5,6 +5,9 @@ * http://www.gnu.org/licenses/gpl.html */ +/* + * This file has been patched with linux-PHC + */ enum pstate { HW_PSTATE_INVALID = 0xff, @@ -44,6 +47,7 @@ * 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_default; /* the acpi table needs to be kept. it's only available if ACPI was * used to determine valid frequency/vid/fid states */ @@ -223,6 +227,7 @@ static int fill_powernow_table_pstate(struct powernow_k8_data *data, struct cpufreq_frequency_table *powernow_table); static int fill_powernow_table_fidvid(struct powernow_k8_data *data, struct cpufreq_frequency_table *powernow_table); +static int fill_powernow_table_fidvid_dt(struct powernow_k8_data *data, struct cpufreq_frequency_table *powernow_table, size_t numps, u32 maxfid); #ifdef CONFIG_SMP static inline void define_siblings(int cpu, cpumask_t cpu_sharedcore_mask[])