--- /tmp/acpi-cpufreq.c.orig 2010-08-07 09:25:45.510440771 +0200 +++ phc-intel-0.3.2-12-1/phc-intel.c @@ -25,6 +25,10 @@ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ +/* This file has been patched with Linux PHC: www.linux-phc.org +* Patch version: linux-phc-0.3.2 +*/ + #include #include #include @@ -62,12 +66,17 @@ }; #define INTEL_MSR_RANGE (0xffff) +#define INTEL_MSR_VID_MASK (0x00ff) +#define INTEL_MSR_FID_MASK (0xff00) +#define INTEL_MSR_FID_SHIFT (0x8) +#define PHC_VERSION_STRING "0.3.2:2" struct acpi_cpufreq_data { struct acpi_processor_performance *acpi_data; struct cpufreq_frequency_table *freq_table; unsigned int resume; unsigned int cpu_feature; + acpi_integer *original_controls; }; static DEFINE_PER_CPU(struct acpi_cpufreq_data *, acfreq_data); @@ -103,13 +112,14 @@ static unsigned extract_msr(u32 msr, struct acpi_cpufreq_data *data) { int i; + u32 fid; struct acpi_processor_performance *perf; - msr &= INTEL_MSR_RANGE; + fid = msr & INTEL_MSR_FID_MASK; perf = data->acpi_data; for (i = 0; data->freq_table[i].frequency != CPUFREQ_TABLE_END; i++) { - if (msr == perf->states[data->freq_table[i].index].status) + if (fid == (perf->states[data->freq_table[i].index].status & INTEL_MSR_FID_MASK)) return data->freq_table[i].frequency; } return data->freq_table[0].frequency; @@ -704,6 +714,8 @@ per_cpu(acfreq_data, policy->cpu) = NULL; acpi_processor_unregister_performance(data->acpi_data, policy->cpu); + if (data->original_controls) + kfree(data->original_controls); kfree(data); } @@ -721,8 +733,470 @@ return 0; } + +/* sysfs interface to change operating points voltages */ + +static unsigned int extract_fid_from_control(unsigned int control) +{ + return ((control & INTEL_MSR_FID_MASK) >> INTEL_MSR_FID_SHIFT); +} + +static unsigned int extract_vid_from_control(unsigned int control) +{ + return (control & INTEL_MSR_VID_MASK); +} + + +static bool check_cpu_control_capability(struct acpi_cpufreq_data *data) { + /* check if the cpu we are running on is capable of setting new control data + * + */ + if (unlikely(data == NULL || + data->acpi_data == NULL || + data->freq_table == NULL || + data->cpu_feature != SYSTEM_INTEL_MSR_CAPABLE)) { + return false; + } else { + return true; + }; +} + + +static ssize_t check_origial_table (struct acpi_cpufreq_data *data) +{ + + struct acpi_processor_performance *acpi_data; + struct cpufreq_frequency_table *freq_table; + unsigned int state_index; + + acpi_data = data->acpi_data; + freq_table = data->freq_table; + + if (data->original_controls == NULL) { + // Backup original control values + data->original_controls = kcalloc(acpi_data->state_count, + sizeof(acpi_integer), GFP_KERNEL); + if (data->original_controls == NULL) { + printk("failed to allocate memory for original control values\n"); + return -ENOMEM; + } + for (state_index = 0; state_index < acpi_data->state_count; state_index++) { + data->original_controls[state_index] = acpi_data->states[state_index].control; + } + } + return 0; +} + +static ssize_t show_freq_attr_vids(struct cpufreq_policy *policy, char *buf) + /* display phc's voltage id's + * + */ +{ + struct acpi_cpufreq_data *data = per_cpu(acfreq_data, policy->cpu); + struct acpi_processor_performance *acpi_data; + struct cpufreq_frequency_table *freq_table; + unsigned int i; + unsigned int vid; + ssize_t count = 0; + + if (!check_cpu_control_capability(data)) return -ENODEV; //check if CPU is capable of changing controls + + acpi_data = data->acpi_data; + freq_table = data->freq_table; + + for (i = 0; freq_table[i].frequency != CPUFREQ_TABLE_END; i++) { + vid = extract_vid_from_control(acpi_data->states[freq_table[i].index].control); + count += sprintf(&buf[count], "%u ", vid); + } + count += sprintf(&buf[count], "\n"); + + return count; +} + +static ssize_t show_freq_attr_default_vids(struct cpufreq_policy *policy, char *buf) + /* display acpi's default voltage id's + * + */ +{ + struct acpi_cpufreq_data *data = per_cpu(acfreq_data, policy->cpu); + struct cpufreq_frequency_table *freq_table; + unsigned int i; + unsigned int vid; + ssize_t count = 0; + ssize_t retval; + + if (!check_cpu_control_capability(data)) return -ENODEV; //check if CPU is capable of changing controls + + retval = check_origial_table(data); + if (0 != retval) + return retval; + + freq_table = data->freq_table; + + for (i = 0; freq_table[i].frequency != CPUFREQ_TABLE_END; i++) { + vid = extract_vid_from_control(data->original_controls[freq_table[i].index]); + count += sprintf(&buf[count], "%u ", vid); + } + count += sprintf(&buf[count], "\n"); + + return count; +} + +static ssize_t show_freq_attr_fids(struct cpufreq_policy *policy, char *buf) + /* display phc's frequeny id's + * + */ +{ + struct acpi_cpufreq_data *data = per_cpu(acfreq_data, policy->cpu); + struct acpi_processor_performance *acpi_data; + struct cpufreq_frequency_table *freq_table; + unsigned int i; + unsigned int fid; + ssize_t count = 0; + + if (!check_cpu_control_capability(data)) return -ENODEV; //check if CPU is capable of changing controls + + acpi_data = data->acpi_data; + freq_table = data->freq_table; + + for (i = 0; freq_table[i].frequency != CPUFREQ_TABLE_END; i++) { + fid = extract_fid_from_control(acpi_data->states[freq_table[i].index].control); + count += sprintf(&buf[count], "%u ", fid); + } + count += sprintf(&buf[count], "\n"); + + return count; +} + +static ssize_t show_freq_attr_controls(struct cpufreq_policy *policy, char *buf) + /* display phc's controls for the cpu (frequency id's and related voltage id's) + * + */ +{ + struct acpi_cpufreq_data *data = per_cpu(acfreq_data, policy->cpu); + struct acpi_processor_performance *acpi_data; + struct cpufreq_frequency_table *freq_table; + unsigned int i; + unsigned int fid; + unsigned int vid; + ssize_t count = 0; + + if (!check_cpu_control_capability(data)) return -ENODEV; //check if CPU is capable of changing controls + + acpi_data = data->acpi_data; + freq_table = data->freq_table; + + for (i = 0; freq_table[i].frequency != CPUFREQ_TABLE_END; i++) { + fid = extract_fid_from_control(acpi_data->states[freq_table[i].index].control); + vid = extract_vid_from_control(acpi_data->states[freq_table[i].index].control); + count += sprintf(&buf[count], "%u:%u ", fid, vid); + } + count += sprintf(&buf[count], "\n"); + + return count; +} + +static ssize_t show_freq_attr_default_controls(struct cpufreq_policy *policy, char *buf) + /* display acpi's default controls for the cpu (frequency id's and related voltage id's) + * + */ +{ + struct acpi_cpufreq_data *data = per_cpu(acfreq_data, policy->cpu); + struct cpufreq_frequency_table *freq_table; + unsigned int i; + unsigned int fid; + unsigned int vid; + ssize_t count = 0; + ssize_t retval; + + if (!check_cpu_control_capability(data)) return -ENODEV; //check if CPU is capable of changing controls + + retval = check_origial_table(data); + if (0 != retval) + return retval; + + freq_table = data->freq_table; + + for (i = 0; freq_table[i].frequency != CPUFREQ_TABLE_END; i++) { + fid = extract_fid_from_control(data->original_controls[freq_table[i].index]); + vid = extract_vid_from_control(data->original_controls[freq_table[i].index]); + count += sprintf(&buf[count], "%u:%u ", fid, vid); + } + count += sprintf(&buf[count], "\n"); + + return count; +} + + +static ssize_t store_freq_attr_vids(struct cpufreq_policy *policy, const char *buf, size_t count) + /* store the voltage id's for the related frequency + * We are going to do some sanity checks here to prevent users + * from setting higher voltages than the default one. + */ +{ + struct acpi_cpufreq_data *data = per_cpu(acfreq_data, policy->cpu); + struct acpi_processor_performance *acpi_data; + struct cpufreq_frequency_table *freq_table; + unsigned int freq_index; + unsigned int state_index; + unsigned int new_vid; + unsigned int original_vid; + unsigned int new_control; + unsigned int original_control; + const char *curr_buf = buf; + char *next_buf; + ssize_t retval; + + if (!check_cpu_control_capability(data)) return -ENODEV; //check if CPU is capable of changing controls + + retval = check_origial_table(data); + if (0 != retval) + return retval; + + acpi_data = data->acpi_data; + freq_table = data->freq_table; + + /* for each value taken from the sysfs interfalce (phc_vids) get entrys and convert them to unsigned long integers*/ + for (freq_index = 0; freq_table[freq_index].frequency != CPUFREQ_TABLE_END; freq_index++) { + new_vid = simple_strtoul(curr_buf, &next_buf, 10); + if (next_buf == curr_buf) { + if ((curr_buf - buf == count - 1) && (*curr_buf == '\n')) { //end of line? + curr_buf++; + break; + } + //if we didn't got end of line but there is nothing more to read something went wrong... + printk("failed to parse vid value at %i (%s)\n", freq_index, curr_buf); + return -EINVAL; + } + + state_index = freq_table[freq_index].index; + original_control = data->original_controls[state_index]; + original_vid = original_control & INTEL_MSR_VID_MASK; + + /* before we store the values we do some checks to prevent + * users to set up values higher than the default one + */ + if (new_vid <= original_vid) { + new_control = (original_control & ~INTEL_MSR_VID_MASK) | new_vid; + dprintk("setting control at %i to %x (default is %x)\n", + freq_index, new_control, original_control); + acpi_data->states[state_index].control = new_control; + + } else { + printk("skipping vid at %i, %u is greater than default %u\n", + freq_index, new_vid, original_vid); + } + + curr_buf = next_buf; + /* jump over value seperators (space or comma). + * There could be more than one space or comma character + * to separate two values so we better do it using a loop. + */ + while ((curr_buf - buf < count) && ((*curr_buf == ' ') || (*curr_buf == ','))) { + curr_buf++; + } + } + + /* set new voltage for current frequency */ + data->resume = 1; + acpi_cpufreq_target(policy, get_cur_freq_on_cpu(policy->cpu), CPUFREQ_RELATION_L); + + return curr_buf - buf; +} + +static ssize_t store_freq_attr_controls(struct cpufreq_policy *policy, const char *buf, size_t count) + /* store the controls (frequency id's and related voltage id's) + * We are going to do some sanity checks here to prevent users + * from setting higher voltages than the default one. + */ +{ + struct acpi_cpufreq_data *data = per_cpu(acfreq_data, policy->cpu); + struct acpi_processor_performance *acpi_data; + struct cpufreq_frequency_table *freq_table; + const char *curr_buf; + unsigned int op_count; + unsigned int state_index; + int isok; + char *next_buf; + ssize_t retval; + unsigned int new_vid; + unsigned int original_vid; + unsigned int new_fid; + unsigned int old_fid; + unsigned int original_control; + unsigned int old_control; + unsigned int new_control; + int found; + + if (!check_cpu_control_capability(data)) return -ENODEV; + + retval = check_origial_table(data); + if (0 != retval) + return retval; + + acpi_data = data->acpi_data; + freq_table = data->freq_table; + + op_count = 0; + curr_buf = buf; + next_buf = NULL; + isok = 1; + + while ( (isok) && (curr_buf != NULL) ) + { + op_count++; + // Parse fid + new_fid = simple_strtoul(curr_buf, &next_buf, 10); + if ((next_buf != curr_buf) && (next_buf != NULL)) + { + // Parse separator between frequency and voltage + curr_buf = next_buf; + next_buf = NULL; + if (*curr_buf==':') + { + curr_buf++; + // Parse vid + new_vid = simple_strtoul(curr_buf, &next_buf, 10); + if ((next_buf != curr_buf) && (next_buf != NULL)) + { + found = 0; + for (state_index = 0; state_index < acpi_data->state_count; state_index++) { + old_control = acpi_data->states[state_index].control; + old_fid = extract_fid_from_control(old_control); + if (new_fid == old_fid) + { + found = 1; + original_control = data->original_controls[state_index]; + original_vid = extract_vid_from_control(original_control); + if (new_vid <= original_vid) + { + new_control = (original_control & ~INTEL_MSR_VID_MASK) | new_vid; + dprintk("setting control at %i to %x (default is %x)\n", + state_index, new_control, original_control); + acpi_data->states[state_index].control = new_control; + + } else { + printk("skipping vid at %i, %u is greater than default %u\n", + state_index, new_vid, original_vid); + } + } + } + + if (found == 0) + { + printk("operating point # %u not found (FID = %u)\n", op_count, new_fid); + isok = 0; + } + + // Parse seprator before next operating point, if any + curr_buf = next_buf; + next_buf = NULL; + if ((*curr_buf == ',') || (*curr_buf == ' ')) + curr_buf++; + else + curr_buf = NULL; + } + else + { + printk("failed to parse VID of operating point # %u (%s)\n", op_count, curr_buf); + isok = 0; + } + } + else + { + printk("failed to parse operating point # %u (%s)\n", op_count, curr_buf); + isok = 0; + } + } + else + { + printk("failed to parse FID of operating point # %u (%s)\n", op_count, curr_buf); + isok = 0; + } + } + + if (isok) + { + retval = count; + /* set new voltage at current frequency */ + data->resume = 1; + acpi_cpufreq_target(policy, get_cur_freq_on_cpu(policy->cpu), CPUFREQ_RELATION_L); + } + else + { + retval = -EINVAL; + } + + return retval; +} + +static ssize_t show_freq_attr_phc_version(struct cpufreq_policy *policy, char *buf) + /* print out the phc version string set at the beginning of that file + */ +{ + ssize_t count = 0; + count += sprintf(&buf[count], "%s\n", PHC_VERSION_STRING); + return count; +} + + + +static struct freq_attr cpufreq_freq_attr_phc_version = +{ + /*display phc's version string*/ + .attr = { .name = "phc_version", .mode = 0444 }, + .show = show_freq_attr_phc_version, + .store = NULL, +}; + +static struct freq_attr cpufreq_freq_attr_vids = +{ + /*display phc's voltage id's for the cpu*/ + .attr = { .name = "phc_vids", .mode = 0644 }, + .show = show_freq_attr_vids, + .store = store_freq_attr_vids, +}; + +static struct freq_attr cpufreq_freq_attr_default_vids = +{ + /*display acpi's default frequency id's for the cpu*/ + .attr = { .name = "phc_default_vids", .mode = 0444 }, + .show = show_freq_attr_default_vids, + .store = NULL, +}; + +static struct freq_attr cpufreq_freq_attr_fids = +{ + /*display phc's default frequency id's for the cpu*/ + .attr = { .name = "phc_fids", .mode = 0444 }, + .show = show_freq_attr_fids, + .store = NULL, +}; + +static struct freq_attr cpufreq_freq_attr_controls = +{ + /*display phc's current voltage/frequency controls for the cpu*/ + .attr = { .name = "phc_controls", .mode = 0644 }, + .show = show_freq_attr_controls, + .store = store_freq_attr_controls, +}; + +static struct freq_attr cpufreq_freq_attr_default_controls = +{ + /*display acpi's default voltage/frequency controls for the cpu*/ + .attr = { .name = "phc_default_controls", .mode = 0444 }, + .show = show_freq_attr_default_controls, + .store = NULL, +}; + static struct freq_attr *acpi_cpufreq_attr[] = { - &cpufreq_freq_attr_scaling_available_freqs, + &cpufreq_freq_attr_scaling_available_freqs, + &cpufreq_freq_attr_phc_version, + &cpufreq_freq_attr_vids, + &cpufreq_freq_attr_default_vids, + &cpufreq_freq_attr_fids, + &cpufreq_freq_attr_controls, + &cpufreq_freq_attr_default_controls, NULL, };