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-03-24 00:12:14.000000000 +0100 +++ b/arch/x86/kernel/cpu/cpufreq/powernow-k8.c 2009-05-19 21:59:29.000000000 +0200 @@ -1,12 +1,19 @@ /* - * (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. + * 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 @@ -46,8 +53,17 @@ #define PFX "powernow-k8: " #define VERSION "version 2.20.00" +#define PHC_VERSION "0.4.0" #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"); + /* serialize freq changes */ static DEFINE_MUTEX(fidvid_mutex); @@ -363,50 +379,59 @@ /* Phase 2 - core frequency transition */ static int core_frequency_transition(struct powernow_k8_data *data, u32 reqfid) { - u32 vcoreqfid, vcocurrfid, vcofiddiff, fid_interval, savevid = data->currvid; - - if ((reqfid < HI_FID_TABLE_BOTTOM) && (data->currfid < HI_FID_TABLE_BOTTOM)) { - printk(KERN_ERR PFX "ph2: illegal lo-lo transition 0x%x 0x%x\n", - reqfid, data->currfid); - return 1; - } + u32 savevid = data->currvid; if (data->currfid == reqfid) { printk(KERN_ERR PFX "ph2 null fid transition 0x%x\n", data->currfid); return 0; } - dprintk("ph2 (cpu%d): starting, currfid 0x%x, currvid 0x%x, reqfid 0x%x\n", - smp_processor_id(), - data->currfid, data->currvid, reqfid); + /* + * 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; + 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; + + 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, 2 + convert_fid_to_vco_fid(data->currfid))) { + if (write_new_fid(data, data->currfid - fid_interval)) 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; + vcocurrfid = convert_fid_to_vco_fid(data->currfid); + vcofiddiff = vcocurrfid > vcoreqfid ? vcocurrfid - vcoreqfid + : vcoreqfid - vcocurrfid; + } } if (write_new_fid(data, reqfid)) @@ -565,7 +590,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); @@ -599,7 +624,7 @@ j, data->powernow_table[j].index & 0xff, data->powernow_table[j].frequency/1000, - data->powernow_table[j].index >> 8); + (data->powernow_table[j].index >> 8) & 0xff); } } } @@ -766,7 +791,10 @@ 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; + size_t numps = 0; + size_t table_size = 0; if (acpi_processor_register_performance(&data->acpi_data, data->cpu)) { dprintk("register performance failed: bad ACPI data\n"); @@ -787,9 +815,36 @@ goto err_out; } + if (cpu_family == CPU_HW_PSTATE || !direct_transitions){ + numps = data->acpi_data.state_count; + } else { + u32 maxfid; + u32 minfid; + + /* 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 (min_800MHz) + minfid = 0; + + 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; @@ -797,21 +852,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); 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 (first_cpu(per_cpu(cpu_core_map, 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); @@ -885,8 +952,8 @@ dprintk(" %d : fid 0x%x, vid 0x%x\n", i, fid, vid); - powernow_table[i].index = fid; /* lower 8 bits */ - powernow_table[i].index |= (vid << 8); /* upper 8 bits */ + powernow_table[i].index = (fid & 0xFF) + |((vid & 0xFF) << 8); powernow_table[i].frequency = find_khz_freq_from_fid(fid); /* verify frequency is OK */ @@ -932,6 +999,80 @@ return 0; } +static int fill_powernow_table_fidvid_dt(struct powernow_k8_data *data, struct cpufreq_frequency_table *powernow_table, size_t numps) +{ + int i; + u32 fid; + u32 vid = VID_OFF; + size_t bmindex = 0; + + /* Get the first FID/VID. First FID will be the higest */ + const u32 maxfid = (data->exttype) ? + data->acpi_data.states[0].status & EXT_FID_MASK + : data->acpi_data.states[0].control & FID_MASK; + + 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) @@ -989,7 +1130,12 @@ return 0; } - if ((fid < HI_FID_TABLE_BOTTOM) && (data->currfid < 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", data->currfid, fid); @@ -1135,6 +1281,423 @@ 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++; + + /* 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++; + + /* 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; +} + /* per CPU init entry point to the driver */ static int __cpuinit powernowk8_cpu_init(struct cpufreq_policy *pol) { @@ -1237,6 +1800,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; @@ -1252,7 +1816,10 @@ per_cpu(powernow_data, pol->cpu) = data; - return 0; + /* initialize the sysfs entrys */ + if (!powernowk8_init_attr(pol)) { + return 0; + } err_out: set_cpus_allowed_ptr(current, &oldmask); @@ -1273,6 +1840,7 @@ cpufreq_frequency_table_put_attr(pol->cpu); + kfree(data->powernow_table_default); kfree(data->powernow_table); kfree(data); @@ -1315,11 +1883,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, 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-03-24 00:12:14.000000000 +0100 +++ b/arch/x86/kernel/cpu/cpufreq/powernow-k8.h 2009-05-19 22:00:10.000000000 +0200 @@ -5,6 +5,10 @@ * http://www.gnu.org/licenses/gpl.html */ +/* + * This file has been patched with linux-PHC + */ + enum pstate { HW_PSTATE_INVALID = 0xff, @@ -36,14 +40,13 @@ u32 exttype; /* extended interface = 1 */ /* keep track of the current fid / vid or pstate */ - u32 currvid; - u32 currfid; - enum pstate currpstate; + u32 currvid, currfid, currpstate; /* 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_default; #ifdef CONFIG_X86_POWERNOW_K8_ACPI /* the acpi table needs to be kept. it's only available if ACPI was @@ -225,6 +228,7 @@ #ifdef CONFIG_X86_POWERNOW_K8_ACPI 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); #endif #ifdef CONFIG_SMP