diff --git a/arch/i386/kernel/cpu/cpufreq/speedstep-centrino.c b/arch/i386/kernel/cpu/cpufreq/speedstep-centrino.c
--- a/arch/i386/kernel/cpu/cpufreq/speedstep-centrino.c
+++ b/arch/i386/kernel/cpu/cpufreq/speedstep-centrino.c
@@ -24,6 +24,7 @@
 #include <linux/config.h>
 #include <linux/delay.h>
 #include <linux/compiler.h>
+#include <linux/ctype.h>
 
 #ifdef CONFIG_X86_SPEEDSTEP_CENTRINO_ACPI
 #include <linux/acpi.h>
@@ -86,13 +87,16 @@ static struct cpufreq_driver centrino_dr
 
 #ifdef CONFIG_X86_SPEEDSTEP_CENTRINO_TABLE
 
+#define VOLTAGE_TO_VID(mv) (((mv) - 700) / 16)
+#define VID_TO_VOLTAGE(vid) ((vid) * 16 + 700)
+
 /* Computes the correct form for IA32_PERF_CTL MSR for a particular
    frequency/voltage operating point; frequency in MHz, volts in mV.
    This is stored as "index" in the structure. */
 #define OP(mhz, mv)							\
 	{								\
 		.frequency = (mhz) * 1000,				\
-		.index = (((mhz)/100) << 8) | ((mv - 700) / 16)		\
+		.index = (((mhz)/100) << 8) | VOLTAGE_TO_VID(mv)		\
 	}
 
 /*
@@ -666,7 +670,151 @@ migrate_end:
 	return (retval);
 }
 
+/* sysfs things for altering voltages */
+static ssize_t show_scaling_voltages(struct cpufreq_policy *policy, char *buf)
+{
+	unsigned int cpu;
+	int i, j;
+
+	if (!policy)
+	    return -ENODEV;
+	cpu = policy->cpu;
+	if (!centrino_model[cpu] || !centrino_model[cpu]->op_points)
+	    return -ENODEV;
+
+	j=snprintf(buf, PAGE_SIZE, "# frequency voltage\n");
+
+	for (i=0; centrino_model[cpu]->op_points[i].frequency
+		     != CPUFREQ_TABLE_END; i++) {
+		/* Print lines of frequency and voltage */
+		j += snprintf(&buf[j], PAGE_SIZE-j, "%u %u\n", 
+			      centrino_model[cpu]->op_points[i].frequency,
+			      VID_TO_VOLTAGE(centrino_model[cpu]->op_points[i].index & 0xff));
+	}
+	buf[PAGE_SIZE-1] = 0;
+	return j;
+}
+
+static ssize_t store_scaling_voltages(struct cpufreq_policy *policy, 
+		const char *buf, size_t count)
+{
+	unsigned int cpu;
+	unsigned int frequency, voltage;
+	int ret, i, j;
+	int msr, vid;
+	unsigned int cur_freq;
+	static struct cpufreq_frequency_table **original_table = NULL;
+
+	if (!policy)
+	    return -ENODEV;
+	cpu = policy->cpu;
+	if (!centrino_model[cpu] || !centrino_model[cpu]->op_points)
+	    return -ENODEV;
+
+	/* Remember the original voltages, and don't let the user
+	 * raise the voltages above those. As stated in
+	 * centrino_init(), be paranoid about releasing people's
+	 * valuable magic smoke. */
+
+	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++);
+		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;
+	}
+	
+	/* Don't use sscanf, since we want to return the actual number
+	 * of characters read. I don't feel comfortable using
+	 * simple_stroul either, since I'm not sure that the buf
+	 * really is zero-terminated. */
+
+	/* Eat non-digits */
+	for (ret = 0; ret < count && !isdigit(buf[ret]); ret++);
+	if (ret == count)
+	    return ret;
+	/* Read frequency */
+	for (frequency = 0; ret < count && isdigit(buf[ret]); ret++)
+		frequency = 10*frequency + buf[ret] - '0';
+	/* Eat non-digits */
+	for (; ret < count && !isdigit(buf[ret]); ret++);
+	/* Read new voltage */
+	for (voltage = 0; ret < count && isdigit(buf[ret]); ret++)
+		voltage = 10*voltage + buf[ret] - '0';
+
+	if (frequency == 0 || voltage < 700)
+		return -EINVAL;
+
+	/* Check so that the voltage is not higher than the original one */
+	for (j=0; original_table[cpu][j].frequency != CPUFREQ_TABLE_END; j++)
+		if (frequency == original_table[cpu][j].frequency) 
+			break;
+	if (original_table[cpu][j].frequency == CPUFREQ_TABLE_END)
+		return -EINVAL;
+	if (voltage > VID_TO_VOLTAGE(original_table[cpu][j].index & 0xff)) 
+		return -EINVAL;
+
+	for (i=0; centrino_model[cpu]->op_points[i].frequency
+		     != CPUFREQ_TABLE_END; i++) {
+		if (frequency == centrino_model[cpu]->op_points[i].frequency) 
+			break;
+	}
+
+	if (centrino_model[cpu]->op_points[i].frequency == CPUFREQ_TABLE_END)
+		return -EINVAL;
+
+	msr = centrino_model[cpu]->op_points[i].index;
+	vid = VOLTAGE_TO_VID(voltage);
+	msr = (msr & ~0xff) | (vid & 0xff);
+
+	/* Double check so that the voltage id is not higher than the original one */
+	if (vid > (original_table[cpu][j].index & 0xff)) 
+		return -EINVAL;
+
+	dprintk(KERN_INFO PFX "altering centrino cpufreq table, to make %u kHz have "
+		"voltage %u mV (VID=%u)\n", frequency, voltage, vid);
+
+	centrino_model[cpu]->op_points[i].index = msr;
+
+
+	/* If the updated voltage applies to the frequency currently
+	 * used, tell the CPU about the new voltage */
+	cur_freq = cpufreq_get(policy->cpu);
+	if (cur_freq == frequency)
+	    centrino_target(policy, cur_freq, CPUFREQ_RELATION_L);
+	
+	return ret;
+}
+
+#define define_one_rw(_name) \
+static struct freq_attr _name = \
+__ATTR(_name, 0644, show_##_name, store_##_name)
+
+define_one_rw(scaling_voltages);
+
+/* end of sysfs voltage things */
+
 static struct freq_attr* centrino_attr[] = {
+	&scaling_voltages,
 	&cpufreq_freq_attr_scaling_available_freqs,
 	NULL,
 };
