[ltp] Re: 2.6.21 / whoopie patches / undervolting patch problem

Whoopie linux-thinkpad@linux-thinkpad.org
Sat, 12 May 2007 19:23:44 +0200


This is a multi-part message in MIME format.
--------------010303000405040207050803
Content-Type: text/plain; charset=ISO-8859-15
Content-Transfer-Encoding: 7bit

Hi,

you're right. The undervolt patch makes problems atm. I asked on
##linux-phc at irc.freenode.net and the developer is looking at it
(although he's quite busy). Btw, they search new developers for linux-phc.

But I was pointed to another patch for acpi-cpufreq as
speedstep-centrino is deprecated. I attach the patch.

It comes from http://forums.gentoo.org/viewtopic-p-3803393.html#3803393

Keep in mind that the usage has changed. The path for the frequency
table is "/sys/devices/system/cpu/cpu0/cpufreq/cpufreq_freq_voltages"
and you need to only echo the voltages like

echo "1100 1020 940 860 796" >
/sys/devices/system/cpu/cpu0/cpufreq/cpufreq_freq_voltages

The first voltage is for the highest CPU frequency.

Best regards,
Whoopie

Fionn Behrens schrieb:
> Finally, after almost a year, I decided I need a new kernel. I took
> vanilla 2.6.21 and patched it with everything considered useful, mainly
> most of the http://whoopie.gmxhome.de/linux/patches/2.6.21/ patches.
> 
> The kernel boots and the computer runs all fine, but as soon as I try to
> feed any undervolting data to the voltage_table oder to the
> op_points_table, sysfs freezes and does never come back! Any subsequent
> access to sysfs results in a permanent freeze of the accessing process.
> I'd rather not fall back to the hardcoding-my-voltages-in-the-kernel
> solution since I'll need the deb for several similar machines with
> slightly different undervolting specs.
> 
> Does anyone of you know who wrote the patch and what could be done for a
> solution? 
> 
> kind regards,
> 		Fionn


--------------010303000405040207050803
Content-Type: text/x-patch;
 name="acpi-cpufreq-undervolt.patch"
Content-Transfer-Encoding: 7bit
Content-Disposition: inline;
 filename="acpi-cpufreq-undervolt.patch"

--- a/arch/i386/kernel/cpu/cpufreq/acpi-cpufreq.c
+++ b/arch/i386/kernel/cpu/cpufreq/acpi-cpufreq.c
@@ -57,6 +57,8 @@ enum {
 };
 
 #define INTEL_MSR_RANGE		(0xffff)
+#define INTEL_MSR_FREQUENCY		(0xff00)
+#define INTEL_MSR_VOLTAGE		(0x00ff)
 #define CPUID_6_ECX_APERFMPERF_CAPABILITY	(0x1)
 
 struct acpi_cpufreq_data {
@@ -69,6 +71,7 @@ struct acpi_cpufreq_data {
 
 static struct acpi_cpufreq_data *drv_data[NR_CPUS];
 static struct acpi_processor_performance *acpi_perf_data[NR_CPUS];
+static acpi_integer *original_acpi_data_control[NR_CPUS];
 
 static struct cpufreq_driver acpi_cpufreq_driver;
 
@@ -104,11 +107,12 @@ static unsigned extract_msr(u32 msr, str
 	int i;
 	struct acpi_processor_performance *perf;
 
-	msr &= INTEL_MSR_RANGE;
+	msr &= INTEL_MSR_FREQUENCY;
 	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 (msr == (perf->states[data->freq_table[i].index].status &
+                   INTEL_MSR_FREQUENCY))
 			return data->freq_table[i].frequency;
 	}
 	return data->freq_table[0].frequency;
@@ -668,7 +672,7 @@ static int acpi_cpufreq_cpu_init(struct 
 	data->max_freq = perf->states[0].core_frequency * 1000;
 	/* table init */
 	for (i=0; i<perf->state_count; i++) {
-		if (i>0 && perf->states[i].core_frequency ==
+		if (i>0 && perf->states[i].core_frequency >=
 		    perf->states[i-1].core_frequency)
 			continue;
 
@@ -749,6 +753,8 @@ static int acpi_cpufreq_cpu_exit(struct 
 		acpi_processor_unregister_performance(data->acpi_data,
 						      policy->cpu);
 		kfree(data);
+		kfree(original_acpi_data_control[policy->cpu]);
+		original_acpi_data_control[policy->cpu] = NULL;
 	}
 
 	return 0;
@@ -765,8 +771,119 @@ static int acpi_cpufreq_resume(struct cp
 	return 0;
 }
 
+static ssize_t show_freq_voltages(struct cpufreq_policy *policy, char *buf)
+{
+	struct acpi_cpufreq_data *data = drv_data[policy->cpu];
+	struct acpi_processor_performance *perf;
+	unsigned int i;
+	unsigned int voltage;
+	ssize_t count = 0;
+
+	if (unlikely(data == NULL ||
+	    data->acpi_data == NULL || data->freq_table == NULL)) {
+		return -ENODEV;
+	}
+
+	perf = data->acpi_data;
+
+	/* show space separated list of current voltages */
+	for (i = 0; data->freq_table[i].frequency != CPUFREQ_TABLE_END; i++) {
+		voltage = perf->states[data->freq_table[i].index].control;
+		voltage = 700 + ((voltage & INTEL_MSR_VOLTAGE) << 4);
+		count += sprintf(&buf[count], "%u ", voltage);
+	}
+	count += sprintf(&buf[count], "\n");
+
+	return count;
+}
+
+static ssize_t store_freq_voltages(struct cpufreq_policy *policy, const char *buf, size_t count)
+{
+	unsigned int cpu = policy->cpu;
+	struct acpi_cpufreq_data *data = drv_data[cpu];
+	struct acpi_processor_performance *perf;
+	unsigned int i, j;
+	unsigned int voltage;
+	unsigned int voltage_index, original_index;
+	const char *curr_buf = buf;
+	char *next_buf;
+	acpi_integer control;
+
+	if (unlikely(data == NULL ||
+	    data->acpi_data == NULL || data->freq_table == NULL)) {
+		return -ENODEV;
+	}
+
+	perf = data->acpi_data;
+
+
+	/* save original control values to disallow overvolting */
+	if (!original_acpi_data_control[cpu]) {
+		original_acpi_data_control[cpu] = kmalloc(sizeof(acpi_integer) *
+		    perf->state_count, GFP_KERNEL);
+		if (!original_acpi_data_control[cpu]) {
+			dprintk("failed to allocate memory for old control values\n");
+			return -ENOMEM;
+		}
+		for (i = 0; i < perf->state_count; i++)
+			original_acpi_data_control[cpu][i] = perf->states[i].control;
+	}
+
+	/* set new voltages from user space */
+	for (i = 0; data->freq_table[i].frequency != CPUFREQ_TABLE_END; i++) {
+		voltage = simple_strtoul(curr_buf, &next_buf, 10);
+		if (next_buf == curr_buf) {
+			if ((curr_buf - buf == count - 1) && (*curr_buf == '\n')) {
+				curr_buf++;
+				break;
+			}
+			dprintk("failed to parse voltage value at %i (%s)\n", i, curr_buf);
+			return -EINVAL;
+		}
+		if (voltage < 700) {
+			dprintk("skipping voltage at %i, "
+				"%u is below the minimum of 700 mV\n", i, voltage);
+		} else {
+			voltage_index = ((voltage - 700) >> 4) & INTEL_MSR_VOLTAGE;
+			j = data->freq_table[i].index;
+			original_index = original_acpi_data_control[cpu][j] &
+			    INTEL_MSR_VOLTAGE;
+			if (voltage_index <= original_index) {
+				control = (original_acpi_data_control[cpu][j] &
+				    INTEL_MSR_FREQUENCY) | voltage_index;
+				dprintk("setting control %x at %i, default is %x\n",
+					(unsigned int) control, i,
+					(unsigned int) original_acpi_data_control[cpu][j]);
+				perf->states[j].control = control;
+			} else {
+				dprintk("skipping voltage at %i, "
+					"%u is greater than the default %u\n",
+					i, voltage,
+					700 + (original_index << 4));
+			}
+		}
+		curr_buf = next_buf;
+		while ((curr_buf - buf < count) && (*curr_buf == ' '))
+			curr_buf++;
+	}
+
+	/* set new voltage at current frequency */
+	data->resume = 1;
+	acpi_cpufreq_target(policy, get_cur_freq_on_cpu(cpu), CPUFREQ_RELATION_L);
+
+	return curr_buf - buf;
+}
+
+static struct freq_attr cpufreq_freq_attr_freq_voltages =
+{
+	.attr = { .name = "cpufreq_freq_voltages", .mode = 0644, .owner = THIS_MODULE },
+	.show = show_freq_voltages,
+	.store = store_freq_voltages,
+};
+
 static struct freq_attr *acpi_cpufreq_attr[] = {
 	&cpufreq_freq_attr_scaling_available_freqs,
+	&cpufreq_freq_attr_freq_voltages,
 	NULL,
 };
 
@@ -800,6 +917,8 @@ static void __exit acpi_cpufreq_exit(voi
 	for_each_possible_cpu(i) {
 		kfree(acpi_perf_data[i]);
 		acpi_perf_data[i] = NULL;
+		kfree(original_acpi_data_control[i]);
+		original_acpi_data_control[i] = NULL;
 	}
 	return;
 }

--------------010303000405040207050803--