diff -uprN org/arch/i386/kernel/cpu/cpufreq/Kconfig new/arch/i386/kernel/cpu/cpufreq/Kconfig
--- org/arch/i386/kernel/cpu/cpufreq/Kconfig 2007-04-12 19:15:46.000000000 +0200
+++ new/arch/i386/kernel/cpu/cpufreq/Kconfig 2007-04-22 08:41:05.000000000 +0200
@@ -1,6 +1,10 @@
#
# CPU Frequency scaling
#
+# This file has been patched with Linux PHC: https://www.dedigentoo.org/trac/linux-phc
+# Patch version: linux-phc-0.2.9-kernel-ubuntu-2.6.20_sph.patch
+# This special patch is not official, but created by Spoilerhead (spoilerhead@gmail.com)
+
menu "CPU Frequency scaling"
@@ -97,7 +101,6 @@ config X86_POWERNOW_K8_ACPI
config X86_GX_SUSPMOD
tristate "Cyrix MediaGX/NatSemi Geode Suspend Modulation"
- depends on PCI
help
This add the CPUFreq driver for NatSemi Geode processors which
support suspend modulation.
@@ -121,12 +124,27 @@ config X86_SPEEDSTEP_CENTRINO
driver will first try to use ACPI. Then if it fails it will try to use
a built-in table if there is one matching the CPU.
+config X86_SPEEDSTEP_CENTRINO_SYSFS
+ bool "Userspace control of CPU frequency/voltage table"
+ depends on X86_SPEEDSTEP_CENTRINO
+ depends on SYSFS
+ depends on (X86_SPEEDSTEP_CENTRINO_BUILTIN && (X86_SPEEDSTEP_CENTRINO_BUILTIN_BANIAS || X86_SPEEDSTEP_CENTRINO_BUILTIN_DOTHAN || X86_SPEEDSTEP_CENTRINO_BUILTIN_SONOMA )) || X86_SPEEDSTEP_CENTRINO_ACPI || X86_SPEEDSTEP_CENTRINO_DEFAULT
+ default y
+ help
+ Add support for user space control of the CPU frequency/voltage
+ operating points table through a sysfs interface.
+
+ If you say Y here files will be created in
+ /sys/devices/system/cpu/cpu*/cpufreq/op_points_table
+ allowing reading and writing of the current table values as well as
+ adding or removing operating points.
+
For details, take a look at .
If in doubt, say N.
config X86_SPEEDSTEP_CENTRINO_ACPI
- bool "Use ACPI tables to decode valid frequency/voltage (deprecated)"
+ bool "Use ACPI tables to decode valid frequency/voltage pairs (deprecated)"
depends on X86_SPEEDSTEP_CENTRINO && ACPI_PROCESSOR
depends on !(X86_SPEEDSTEP_CENTRINO = y && ACPI_PROCESSOR = m)
default y
@@ -264,7 +282,7 @@ config X86_LONGRUN
config X86_LONGHAUL
tristate "VIA Cyrix III Longhaul"
select CPU_FREQ_TABLE
- depends on ACPI_PROCESSOR
+ depends on BROKEN
help
This adds the CPUFreq driver for VIA Samuel/CyrixIII,
VIA Cyrix Samuel/C3, VIA Cyrix Ezra and VIA Cyrix Ezra-T
diff -uprN org/arch/i386/kernel/cpu/cpufreq/Makefile new/arch/i386/kernel/cpu/cpufreq/Makefile
--- org/arch/i386/kernel/cpu/cpufreq/Makefile 2007-04-12 19:15:46.000000000 +0200
+++ new/arch/i386/kernel/cpu/cpufreq/Makefile 2007-04-20 19:00:41.000000000 +0200
@@ -7,9 +7,9 @@ obj-$(CONFIG_SC520_CPUFREQ) += sc520_fr
obj-$(CONFIG_X86_LONGRUN) += longrun.o
obj-$(CONFIG_X86_GX_SUSPMOD) += gx-suspmod.o
obj-$(CONFIG_X86_SPEEDSTEP_ICH) += speedstep-ich.o
+obj-$(CONFIG_X86_SPEEDSTEP_CENTRINO) += speedstep-centrino.o
obj-$(CONFIG_X86_SPEEDSTEP_LIB) += speedstep-lib.o
obj-$(CONFIG_X86_SPEEDSTEP_SMI) += speedstep-smi.o
obj-$(CONFIG_X86_ACPI_CPUFREQ) += acpi-cpufreq.o
-obj-$(CONFIG_X86_SPEEDSTEP_CENTRINO) += speedstep-centrino.o
obj-$(CONFIG_X86_P4_CLOCKMOD) += p4-clockmod.o
obj-$(CONFIG_X86_CPUFREQ_NFORCE2) += cpufreq-nforce2.o
diff -uprN org/arch/i386/kernel/cpu/cpufreq/speedstep-centrino.c new/arch/i386/kernel/cpu/cpufreq/speedstep-centrino.c
--- org/arch/i386/kernel/cpu/cpufreq/speedstep-centrino.c 2007-04-12 19:15:46.000000000 +0200
+++ new/arch/i386/kernel/cpu/cpufreq/speedstep-centrino.c 2007-04-20 19:00:41.000000000 +0200
@@ -13,6 +13,11 @@
* Copyright (C) 2003 Jeremy Fitzhardinge
*/
+/*
+ * This file has been patched with Linux PHC: https://www.dedigentoo.org/trac/linux-phc
+ * Patch version: linux-phc-0.2.9-kernel-vanilla-2.6.20.patch
+ */
+
#include
#include
#include
@@ -795,8 +800,8 @@ static int centrino_cpu_init_acpi(struct
if (extract_clock(centrino_model[cpu]->op_points[i].index, cpu, 0) !=
(centrino_model[cpu]->op_points[i].frequency)) {
dprintk("Invalid encoded frequency (%u vs. %u)\n",
- extract_clock(centrino_model[cpu]->op_points[i].index, cpu, 0),
- centrino_model[cpu]->op_points[i].frequency);
+ extract_clock(centrino_model[cpu]->op_points[i].index, cpu, 0),
+ centrino_model[cpu]->op_points[i].frequency);
result = -EINVAL;
goto err_kfree_all;
}
@@ -832,6 +837,454 @@ static int centrino_target (struct cpufr
unsigned int relation);
+#ifdef CONFIG_X86_SPEEDSTEP_CENTRINO_SYSFS
+/************************** sysfs interface for user defined voltage table ************************/
+
+static struct cpufreq_frequency_table **original_table = NULL;
+
+static void check_origial_table (unsigned int cpu)
+{
+ int i;
+
+ if (!original_table)
+ {
+ original_table = kmalloc(sizeof(struct cpufreq_frequency_table *)*NR_CPUS, GFP_KERNEL);
+ for (i=0; i < NR_CPUS; i++)
+ {
+ original_table[i] = NULL;
+ }
+ }
+
+ if (!original_table[cpu])
+ {
+ /* Count number of frequencies and allocate memory for a copy */
+ for (i=0; centrino_model[cpu]->op_points[i].frequency != CPUFREQ_TABLE_END; i++);
+ /* Allocate memory to store the copy */
+ original_table[cpu] = (struct cpufreq_frequency_table*) kmalloc(sizeof(struct cpufreq_frequency_table)*(i+1), GFP_KERNEL);
+ /* Make copy of frequency/voltage pairs */
+ for (i=0; centrino_model[cpu]->op_points[i].frequency != CPUFREQ_TABLE_END; i++)
+ {
+ original_table[cpu][i].frequency = centrino_model[cpu]->op_points[i].frequency;
+ original_table[cpu][i].index = centrino_model[cpu]->op_points[i].index;
+ }
+ original_table[cpu][i].frequency = CPUFREQ_TABLE_END;
+ }
+}
+
+
+static ssize_t show_user_voltage (struct cpufreq_policy *policy, char *buf)
+{
+ ssize_t bytes_written = 0;
+ unsigned int cpu = policy->cpu;
+ unsigned int op_index = 0;
+ unsigned int op_count = 0;
+ unsigned int voltage = 0;
+ unsigned int frequency = 0;
+
+ //dprintk("showing user voltage table in sysfs\n");
+
+ while ( (centrino_model[cpu]->op_points[op_index].frequency != CPUFREQ_TABLE_END)
+ && (bytes_writtenop_points[op_index].frequency;
+ if (frequency != CPUFREQ_ENTRY_INVALID)
+ {
+ op_count++;
+ if (op_count>1)
+ bytes_written += snprintf (&buf[bytes_written],PAGE_SIZE-bytes_written-1, ",");
+ voltage = centrino_model[cpu]->op_points[op_index].index;
+ voltage = 700 + ((voltage & 0xFF) << 4);
+ //dprintk("writing voltage %i: %u mV \n", op_index, voltage);
+ bytes_written += snprintf (&buf[bytes_written],PAGE_SIZE-bytes_written-1, "%u",voltage);
+ }
+ else
+ {
+ // This operating point of the table is invalid, ignoring it.
+ dprintk("Ignoring invalid operating point %i \n", op_index);
+ }
+ op_index++;
+ }
+ bytes_written += snprintf (&buf[bytes_written],PAGE_SIZE-bytes_written-1, "\n");
+ buf[PAGE_SIZE-1] = 0;
+ return bytes_written;
+}
+
+static ssize_t
+store_user_voltage (struct cpufreq_policy *policy, const char *buf, size_t count)
+{
+ unsigned int cpu;
+ const char *curr_buf;
+ unsigned int curr_freq;
+ unsigned int op_index;
+ int isok;
+ char *next_buf;
+ unsigned int op_point;
+ ssize_t retval;
+ unsigned int voltage;
+
+ if (!policy)
+ return -ENODEV;
+ cpu = policy->cpu;
+ if (!centrino_model[cpu] || !centrino_model[cpu]->op_points)
+ return -ENODEV;
+
+ check_origial_table(cpu);
+
+ op_index = 0;
+ curr_buf = buf;
+ next_buf = NULL;
+ isok = 1;
+
+ while ((centrino_model[cpu]->op_points[op_index].frequency != CPUFREQ_TABLE_END)
+ && (isok))
+ {
+ if (centrino_model[cpu]->op_points[op_index].frequency != CPUFREQ_ENTRY_INVALID)
+ {
+ voltage = simple_strtoul(curr_buf, &next_buf, 10);
+ if ((next_buf != curr_buf) && (next_buf != NULL))
+ {
+ if ((voltage >= 700) && (voltage<=1600))
+ {
+ voltage = ((voltage - 700) >> 4) & 0xFF;
+ op_point = (original_table[cpu])[op_index].index;
+ if (voltage <= (op_point & 0xFF))
+ {
+ //dprintk("setting control value %i to %04x\n", op_index, op_point);
+ op_point = (op_point & 0xFFFFFF00) | voltage;
+ centrino_model[cpu]->op_points[op_index].index = op_point;
+ }
+ else
+ {
+ op_point = (op_point & 0xFFFFFF00) | voltage;
+ dprintk("not setting control value %i to %04x because requested voltage is not lower than the default value\n", op_index, op_point);
+ //isok = 0;
+ }
+ }
+ else
+ {
+ dprintk("voltage value %i is out of bounds: %u mV\n", op_index, voltage);
+ isok = 0;
+ }
+ curr_buf = next_buf;
+ if (*curr_buf==',')
+ curr_buf++;
+ next_buf = NULL;
+ }
+ else
+ {
+ dprintk("failed to parse voltage value %i\n", op_index);
+ isok = 0;
+ }
+ }
+ else
+ {
+ // This operating point of the table is invalid, ignoring it.
+ dprintk("Ignoring invalid operating point %i \n", op_index);
+ }
+ op_index++;
+ }
+
+ if (isok)
+ {
+ retval = count;
+ curr_freq = cpufreq_get(policy->cpu);
+ centrino_target(policy, curr_freq, CPUFREQ_RELATION_L);
+ }
+ else
+ {
+ retval = -EINVAL;
+ }
+
+ return retval;
+}
+
+static struct freq_attr centrino_freq_attr_voltage_table =
+{
+ .attr = { .name = "voltage_table", .mode = 0644, .owner = THIS_MODULE },
+ .show = show_user_voltage,
+ .store = store_user_voltage,
+};
+
+
+static ssize_t show_user_op_points (struct cpufreq_policy *policy, char *buf)
+{
+ ssize_t bytes_written = 0;
+ unsigned int cpu = policy->cpu;
+ unsigned int op_index = 0;
+ unsigned int op_count = 0;
+ unsigned int voltage = 0;
+ unsigned int frequency = 0;
+
+ //dprintk("showing user voltage table in sysfs\n");
+
+ while ( (centrino_model[cpu]->op_points[op_index].frequency != CPUFREQ_TABLE_END)
+ && (bytes_writtenop_points[op_index].frequency;
+ if (frequency != CPUFREQ_ENTRY_INVALID)
+ {
+ op_count++;
+ if (op_count>1)
+ bytes_written += snprintf (&buf[bytes_written],PAGE_SIZE-bytes_written-1, ",");
+ voltage = centrino_model[cpu]->op_points[op_index].index;
+ voltage = 700 + ((voltage & 0xFF) << 4);
+ //dprintk("writing voltage %i: %u mV \n", i, voltage);
+ bytes_written += snprintf (&buf[bytes_written],PAGE_SIZE-bytes_written-2, "%u:%u",frequency,voltage);
+ }
+ else
+ {
+ // This operating point of the table is invalid, ignoring it.
+ dprintk("Ignoring invalid operating point %i \n", op_index);
+ }
+ op_index++;
+ }
+ bytes_written += snprintf (&buf[bytes_written],PAGE_SIZE-bytes_written-1, "\n");
+ buf[PAGE_SIZE-1] = 0;
+ return bytes_written;
+}
+
+static ssize_t
+store_user_op_points (struct cpufreq_policy *policy, const char *buf, size_t count)
+{
+ unsigned int cpu;
+ const char *curr_buf;
+ unsigned int curr_freq;
+ unsigned int op_index;
+ unsigned int op_count;
+ int isok;
+ char *next_buf;
+ unsigned int op_point;
+ ssize_t retval;
+ unsigned int voltage;
+ unsigned int frequency;
+ int found;
+
+ if (!policy)
+ return -ENODEV;
+ cpu = policy->cpu;
+ if (!centrino_model[cpu] || !centrino_model[cpu]->op_points)
+ return -ENODEV;
+
+ check_origial_table(cpu);
+
+ op_count = 0;
+ curr_buf = buf;
+ next_buf = NULL;
+ isok = 1;
+
+ while ( (isok) && (curr_buf != NULL) )
+ {
+ op_count++;
+ // Parse frequency
+ frequency = 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 voltage
+ voltage = simple_strtoul(curr_buf, &next_buf, 10);
+ if ((next_buf != curr_buf) && (next_buf != NULL))
+ {
+ if ((voltage >= 700) && (voltage<=1600))
+ {
+ voltage = ((voltage - 700) >> 4) & 0xFF;
+ op_index = 0;
+ found = 0;
+ while (centrino_model[cpu]->op_points[op_index].frequency != CPUFREQ_TABLE_END)
+ {
+ if ((centrino_model[cpu]->op_points[op_index].frequency == frequency)
+ && (centrino_model[cpu]->op_points[op_index].frequency != CPUFREQ_ENTRY_INVALID))
+ {
+ found = 1;
+ op_point = (original_table[cpu])[op_index].index;
+ if (voltage <= (op_point & 0xFF))
+ {
+ //dprintk("setting control value %i to %04x\n", op_index, op_point);
+ op_point = (op_point & 0xFFFFFF00) | voltage;
+ centrino_model[cpu]->op_points[op_index].index = op_point;
+ }
+ else
+ {
+ op_point = (op_point & 0xFFFFFF00) | voltage;
+ dprintk("not setting control value %i to %04x because requested voltage is not lower than the default value (%u MHz)\n", op_index, op_point, frequency);
+ }
+ }
+ op_index++;
+ }
+ if (found == 0)
+ {
+ dprintk("operating point # %u not found: %u MHz\n", op_count, frequency);
+ isok = 0;
+ }
+ }
+ else
+ {
+ dprintk("operating point # %u voltage value is out of bounds: %u mV\n", op_count, voltage);
+ isok = 0;
+ }
+ // Parse seprator before next operating point, if any
+ curr_buf = next_buf;
+ next_buf = NULL;
+ if (*curr_buf==',')
+ curr_buf++;
+ else
+ curr_buf = NULL;
+ }
+ else
+ {
+ dprintk("failed to parse operating point # %u voltage\n", op_count);
+ isok = 0;
+ }
+ }
+ else
+ {
+ dprintk("failed to parse operating point # %u\n", op_count);
+ isok = 0;
+ }
+ }
+ else
+ {
+ dprintk("failed to parse operating point # %u frequency\n", op_count);
+ isok = 0;
+ }
+ }
+
+ if (isok)
+ {
+ retval = count;
+ curr_freq = cpufreq_get(policy->cpu);
+ centrino_target(policy, curr_freq, CPUFREQ_RELATION_L);
+ }
+ else
+ {
+ retval = -EINVAL;
+ }
+
+ return retval;
+}
+
+static struct freq_attr centrino_freq_attr_op_points_table =
+{
+ .attr = { .name = "op_points_table", .mode = 0644, .owner = THIS_MODULE },
+ .show = show_user_op_points,
+ .store = store_user_op_points,
+};
+
+unsigned long rounded_div(unsigned long x, unsigned long y)
+{
+ return (((x*2) / y)+1)/2;
+}
+
+static ssize_t show_FSB_base_freq (struct cpufreq_policy *policy, char *buf)
+{
+ ssize_t bytes_written = 0;
+ unsigned int cpu = policy->cpu;
+ unsigned int frequency;
+ unsigned int index;
+ unsigned int op_index = 0;
+
+ frequency = centrino_model[cpu]->base_freq;
+ if (frequency!=0)
+ {
+ bytes_written += snprintf (buf, PAGE_SIZE-2, "User defined base FSB frequency:\n%u kHz\n",frequency);
+ }
+
+ bytes_written += snprintf (buf+bytes_written, PAGE_SIZE-bytes_written-2,
+ "Base FSB frequency computed from operating points table:\n");
+
+ check_origial_table(cpu);
+ while ((original_table[cpu][op_index].frequency != CPUFREQ_TABLE_END)
+ && (bytes_written < PAGE_SIZE-3))
+ {
+ index = original_table[cpu][op_index].index;
+ index = (index >> 8) & 0xFF;
+ if (index > 0)
+ {
+ frequency = rounded_div((original_table[cpu][op_index].frequency), index);
+ bytes_written += snprintf (buf+bytes_written, PAGE_SIZE-bytes_written-2, "%u kHz (%u / %u)\n",
+ frequency, original_table[cpu][op_index].frequency, index);
+ }
+ op_index++;
+ }
+
+ buf[PAGE_SIZE-1] = 0;
+ return bytes_written;
+}
+
+static ssize_t
+store_FSB_base_freq (struct cpufreq_policy *policy, const char *buf, size_t count)
+{
+ unsigned int cpu;
+ const char *curr_buf;
+ unsigned int curr_freq;
+ unsigned int frequency;
+ unsigned int index;
+ char *next_buf;
+ unsigned int op_index = 0;
+ ssize_t retval;
+
+ if (!policy)
+ return -ENODEV;
+ cpu = policy->cpu;
+ if (!centrino_model[cpu] || !centrino_model[cpu]->op_points)
+ return -ENODEV;
+
+ curr_buf = buf;
+ next_buf = NULL;
+ frequency = simple_strtoul(curr_buf, &next_buf, 10);
+ if ((next_buf != curr_buf) && (next_buf != NULL))
+ {
+ if (centrino_model[cpu]->base_freq != frequency)
+ {
+ centrino_model[cpu]->base_freq = frequency;
+
+ check_origial_table(cpu);
+ while (centrino_model[cpu]->op_points[op_index].frequency != CPUFREQ_TABLE_END)
+ {
+ if (frequency>0)
+ {
+ index = original_table[cpu][op_index].index;
+ index = (index >> 8) & 0xFF;
+ if (index > 0)
+ {
+ centrino_model[cpu]->op_points[op_index].frequency = frequency * index;
+ }
+ }
+ else
+ {
+ centrino_model[cpu]->op_points[op_index].frequency = original_table[cpu][op_index].frequency;
+ }
+ op_index++;
+ }
+ }
+
+ retval = count;
+ curr_freq = cpufreq_get(policy->cpu);
+ centrino_target(policy, curr_freq, CPUFREQ_RELATION_L);
+ }
+ else
+ {
+ retval = -EINVAL;
+ }
+
+ return retval;
+}
+
+static struct freq_attr centrino_freq_attr_FSB_Base_Freq =
+{
+ .attr = { .name = "FSB_base_frequency", .mode = 0644, .owner = THIS_MODULE },
+ .show = show_FSB_base_freq,
+ .store = store_FSB_base_freq,
+};
+
+#endif /* CONFIG_X86_SPEEDSTEP_CENTRINO_SYSFS */
+
static int centrino_cpu_init(struct cpufreq_policy *policy)
{
struct cpuinfo_x86 *cpu = &cpu_data[policy->cpu];
@@ -1083,6 +1536,11 @@ migrate_end:
static struct freq_attr* centrino_attr[] = {
&cpufreq_freq_attr_scaling_available_freqs,
+#ifdef CONFIG_X86_SPEEDSTEP_CENTRINO_SYSFS
+ ¢rino_freq_attr_voltage_table,
+ ¢rino_freq_attr_op_points_table,
+ ¢rino_freq_attr_FSB_Base_Freq,
+#endif
NULL,
};