Re: So, what's the status on the recent patches here?

David Singleton
2006-08-19T06:19:37Z

[-- Attachment #1: Type: text/plain, Size: 1800 bytes --]

On 8/14/06, Greg KH <greg@kroah.com> wrote:
> On Mon, Aug 14, 2006 at 07:48:01PM -0400, Dave Jones wrote:
> >
> > This adds a whole bunch of new code, and doesn't seem to make any
> > existing code any simpler (to me at least). From a cpufreq point of view,
> > what does adding this buy us? What problem do we have today that is
> > being solved by all this?

Greg and Dave,

Here is the patch the provides the cpufreq functionality in an
operating point
fashion for the centrino-speedstep. Cpufreq tables are transformed
into operating
points which can be simply set by writing the name of the operating
point into /sys/power/state.

These two patches implement the cpufreq functionality of changing
processsor
frequency and voltage. The huge amount of code that tries to make decisions
about what operating point to set and which devices can be suspended
or not is left to
the power manager.

David

> >
> > Every explanation of powerop I've seen so far dives into microdetails,
> > whilst the 10,000ft view has always passed me by other than "this is
> > what we've had in the embedded world".
> >
> > The diagram at http://lists.osdl.org/pipermail/linux-pm/2006-August/003196.html
> > also confuses me. I was under the impression that powerop was adding additional
> > userspace interfaces. If we're not changing how things from a userspace
> > point of view, we're churning a lot of kernel code,.. why?
> >
> > Clue me in here, I'm feeling thick.
>
> You're not alone, I really don't get it either.
>
> But I guess we'll just wait for the next round of unified patches and
> then go from there.
>
> thanks,
>
> greg k-h
> _______________________________________________
> linux-pm mailing list
> linux-pm@lists.osdl.org
> https://lists.osdl.org/mailman/listinfo/linux-pm
>

[-- Attachment #2: oppoint-x86-centrino.patch --]
[-- Type: application/octet-stream, Size: 16865 bytes --]

Signed-Off-by: David Singleton <dsingleton@mvista.com>

arch/i386/kernel/cpu/Makefile | 1
arch/i386/kernel/cpu/oppoint/Makefile | 2
arch/i386/kernel/cpu/oppoint/centrino-dynamic-oppoint.c | 71 ++
arch/i386/kernel/cpu/oppoint/centrino-oppoint.c | 460 ++++++++++++++++
arch/i386/kernel/i386_ksyms.c | 4
5 files changed, 538 insertions(+)

Index: linux-2.6.17/arch/i386/kernel/cpu/Makefile
===================================================================
--- linux-2.6.17.orig/arch/i386/kernel/cpu/Makefile
+++ linux-2.6.17/arch/i386/kernel/cpu/Makefile
@@ -17,3 +17,4 @@ obj-$(CONFIG_X86_MCE) += mcheck/

obj-$(CONFIG_MTRR) += mtrr/
obj-$(CONFIG_CPU_FREQ) += cpufreq/
+obj-$(CONFIG_PM) += oppoint/
Index: linux-2.6.17/arch/i386/kernel/i386_ksyms.c
===================================================================
--- linux-2.6.17.orig/arch/i386/kernel/i386_ksyms.c
+++ linux-2.6.17/arch/i386/kernel/i386_ksyms.c
@@ -28,3 +28,7 @@ EXPORT_SYMBOL(__read_lock_failed);
#endif

EXPORT_SYMBOL(csum_partial);
+#ifdef CONFIG_PM
+#include <linux/pm.h>
+EXPORT_SYMBOL(pm_states);
+#endif
Index: linux-2.6.17/arch/i386/kernel/cpu/oppoint/Makefile
===================================================================
--- /dev/null
+++ linux-2.6.17/arch/i386/kernel/cpu/oppoint/Makefile
@@ -0,0 +1,2 @@
+obj-$(CONFIG_X86_SPEEDSTEP_CENTRINO) += centrino-oppoint.o
+obj-m += centrino-dynamic-oppoint.o
Index: linux-2.6.17/arch/i386/kernel/cpu/oppoint/centrino-dynamic-oppoint.c
===================================================================
--- /dev/null
+++ linux-2.6.17/arch/i386/kernel/cpu/oppoint/centrino-dynamic-oppoint.c
@@ -0,0 +1,71 @@
+/*
+ * oppoint/centrino-dynamic-oppoint.c
+ *
+ * This is the template to create dynamic operating points for power management.
+ *
+ * Author: David Singleton dsingleton@mvista.com MontaVista Software, Inc.
+ *
+ * 2006 (c) MontaVista Software, Inc. This file is licensed under
+ * the terms of the GNU General Public License version 2. This program
+ * is licensed "as is" without any warranty of any kind, whether express
+ * or implied.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/pm.h>
+#include <linux/cpufreq.h>
+#include <linux/moduleparam.h>
+#include <linux/moduleloader.h>
+
+int centrino_transition(struct oppoint *cur, struct oppoint *new);
+
+static char oppoint_name[PM_NAME_SIZE] = "dynamic";
+static unsigned int voltage = 1308;
+static unsigned int latency = 100;
+module_param_named(name, oppoint_name, char *, 0);
+module_param_named(frequency, frequency, uint, 0);
+module_param_named(voltage, voltage, uint, 0);
+module_param_named(latency, latency, uint, 0);
+MODULE_PARM_DESC(frequency, "cpu frequency in kHz");
+MODULE_PARM_DESC(voltage, "cpu voltage in mV");
+MODULE_PARM_DESC(latency, "transition latency in us");
+
+/* Register both the driver and the device */
+
+static struct oppoint dynamic_op = {
+ .type = PM_FREQ_CHANGE,
+ .name = "Dynamic",
+ .prepare_transition = cpufreq_prepare_transition,
+ .transition = centrino_transition,
+ .finish_transition = cpufreq_finish_transition,
+};
+
+extern void centrino_set_frequency(struct oppoint *op, uint freq, uint volt);
+
+int __init dynamic_oppoint_init(void)
+{
+
+ printk("Dynamic PowerOp operating point for speedstep centrino\n");
+ dynamic_op.name = name;
+ dynamic_op.frequency = frequency;
+ dynamic_op.voltage = voltage;
+ dynamic_op.latency = latency;
+ centrino_set_frequency(&dynamic_op, frequency / 1000, voltage);
+ printk("freq %d volt %d msr 0x%x\n", dynamic_op.frequency,
+ dynamic_op.voltage, (unsigned int)dynamic_op.md_data);
+ list_add_tail(&dynamic_op.list, &pm_states.list);
+ return 0;
+}
+
+void __exit dynamic_oppoint_cleanup(void)
+{
+ list_del_init(&dynamic_op.list);
+}
+
+module_init(dynamic_oppoint_init);
+module_exit(dynamic_oppoint_cleanup);
+
+MODULE_DESCRIPTION("Dynamic Powerop module");
+MODULE_LICENSE("GPL");
Index: linux-2.6.17/arch/i386/kernel/cpu/oppoint/centrino-oppoint.c
===================================================================
--- /dev/null
+++ linux-2.6.17/arch/i386/kernel/cpu/oppoint/centrino-oppoint.c
@@ -0,0 +1,460 @@
+/*
+ * PowerOp support for Enhanced SpeedStep, as found in Intel's Pentium
+ * M (part of the Centrino chipset).
+ *
+ * Modelled on speedstep-centrino.c
+ *
+ * Copyright (C) 2006 David Singleton <dsingleton@mvista.com>
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/cpufreq.h>
+#include <linux/delay.h>
+#include <linux/compiler.h>
+
+#include <asm/msr.h>
+#include <asm/processor.h>
+#include <asm/cpufeature.h>
+
+struct cpu_id
+{
+ __u8 x86; /* CPU family */
+ __u8 x86_model; /* model */
+ __u8 x86_mask; /* stepping */
+};
+
+enum {
+ CPU_BANIAS,
+ CPU_DOTHAN_A1,
+ CPU_DOTHAN_A2,
+ CPU_DOTHAN_B0,
+ CPU_MP4HT_D0,
+ CPU_MP4HT_E0,
+};
+
+static const struct cpu_id cpu_ids[] = {
+ [CPU_BANIAS] = { 6, 9, 5 },
+ [CPU_DOTHAN_A1] = { 6, 13, 1 },
+ [CPU_DOTHAN_A2] = { 6, 13, 2 },
+ [CPU_DOTHAN_B0] = { 6, 13, 6 },
+ [CPU_MP4HT_D0] = {15, 3, 4 },
+ [CPU_MP4HT_E0] = {15, 4, 1 },
+};
+#define N_IDS ARRAY_SIZE(cpu_ids)
+
+struct cpu_model
+{
+ const struct cpu_id *cpu_id;
+ const char *model_name;
+ unsigned max_freq; /* max clock in kHz */
+
+ struct cpufreq_frequency_table *op_points; /* clock/voltage pairs */
+};
+static int centrino_verify_cpu_id(const struct cpuinfo_x86 *c, const struct cpu_id *x);
+
+void centrino_set_frequency(struct oppoint *op, uint freq, uint volt)
+{
+ op->frequency = freq * 1000;
+ op->voltage = volt;
+ op->md_data = (void *)(((freq / 100) << 8) | (volt - 700) / 16);
+ printk("freq %d volt %d msr 0x%x\n", op->frequency, op->voltage,
+ (unsigned int)op->md_data);
+}
+EXPORT_SYMBOL(centrino_set_frequency);
+
+int centrino_transition(struct oppoint *cur, struct oppoint *new)
+{
+ unsigned int msr, oldmsr = 0, h = 0;
+
+ if (cur == new)
+ return 0;
+
+ msr = (unsigned int)new->md_data;
+ rdmsr(MSR_IA32_PERF_CTL, oldmsr, h);
+
+ /* all but 16 LSB are reserved, treat them with care */
+ oldmsr &= ~0xffff;
+ msr &= 0xffff;
+ oldmsr |= msr;
+
+ wrmsr(MSR_IA32_PERF_CTL, oldmsr, h);
+
+ udelay(new->latency);
+
+ return 0;
+}
+EXPORT_SYMBOL(centrino_transition);
+
+#define OP(mhz, mv) \
+ { \
+ .frequency = (mhz) * 1000, \
+ .index = (((mhz)/100) << 8) | ((mv - 700) / 16) \
+ }
+
+/*
+ * These voltage tables were derived from the Intel Pentium M
+ * datasheet, document 25261202.pdf, Table 5. I have verified they
+ * are consistent with my IBM ThinkPad X31, which has a 1.3GHz Pentium
+ * M.
+ */
+
+/* Ultra Low Voltage Intel Pentium M processor 900MHz (Banias) */
+static struct cpufreq_frequency_table banias_900[] =
+{
+ OP(600, 844),
+ OP(800, 988),
+ OP(900, 1004),
+ { .frequency = CPUFREQ_TABLE_END }
+};
+/* Ultra Low Voltage Intel Pentium M processor 1000MHz (Banias) */
+static struct cpufreq_frequency_table banias_1000[] =
+{
+ OP(600, 844),
+ OP(800, 972),
+ OP(900, 988),
+ OP(1000, 1004),
+ { .frequency = CPUFREQ_TABLE_END }
+};
+
+/* Low Voltage Intel Pentium M processor 1.10GHz (Banias) */
+static struct cpufreq_frequency_table banias_1100[] =
+{
+ OP( 600, 956),
+ OP( 800, 1020),
+ OP( 900, 1100),
+ OP(1000, 1164),
+ OP(1100, 1180),
+ { .frequency = CPUFREQ_TABLE_END }
+};
+
+
+/* Low Voltage Intel Pentium M processor 1.20GHz (Banias) */
+static struct cpufreq_frequency_table banias_1200[] =
+{
+ OP( 600, 956),
+ OP( 800, 1004),
+ OP( 900, 1020),
+ OP(1000, 1100),
+ OP(1100, 1164),
+ OP(1200, 1180),
+ { .frequency = CPUFREQ_TABLE_END }
+};
+
+/* Intel Pentium M processor 1.30GHz (Banias) */
+static struct cpufreq_frequency_table banias_1300[] =
+{
+ OP( 600, 956),
+ OP( 800, 1260),
+ OP(1000, 1292),
+ OP(1200, 1356),
+ OP(1300, 1388),
+ { .frequency = CPUFREQ_TABLE_END }
+};
+
+/* Intel Pentium M processor 1.40GHz (Banias) */
+static struct cpufreq_frequency_table banias_1400[] =
+{
+ OP( 600, 956),
+ OP( 800, 1180),
+ OP(1000, 1308),
+ OP(1200, 1436),
+ OP(1400, 1484),
+ { .frequency = CPUFREQ_TABLE_END }
+};
+
+/* Intel Pentium M processor 1.50GHz (Banias) */
+static struct cpufreq_frequency_table banias_1500[] =
+{
+ OP( 600, 956),
+ OP( 800, 1116),
+ OP(1000, 1228),
+ OP(1200, 1356),
+ OP(1400, 1452),
+ OP(1500, 1484),
+ { .frequency = CPUFREQ_TABLE_END }
+};
+
+/* Intel Pentium M processor 1.60GHz (Banias) */
+static struct cpufreq_frequency_table banias_1600[] =
+{
+ OP( 600, 956),
+ OP( 800, 1036),
+ OP(1000, 1164),
+ OP(1200, 1276),
+ OP(1400, 1420),
+ OP(1600, 1484),
+ { .frequency = CPUFREQ_TABLE_END }
+};
+
+/* Intel Pentium M processor 1.70GHz (Banias) */
+static struct cpufreq_frequency_table banias_1700[] =
+{
+ OP( 600, 956),
+ OP( 800, 1004),
+ OP(1000, 1116),
+ OP(1200, 1228),
+ OP(1400, 1308),
+ OP(1700, 1484),
+ { .frequency = CPUFREQ_TABLE_END }
+};
+
+#define _BANIAS(cpuid, max, name) \
+{ .cpu_id = cpuid, \
+ .model_name = "Intel(R) Pentium(R) M processor " name "MHz", \
+ .max_freq = (max)*1000, \
+ .op_points = banias_##max, \
+}
+#define BANIAS(max) _BANIAS(&cpu_ids[CPU_BANIAS], max, #max)
+
+static struct oppoint lowest = {
+ .name = "lowest",
+ .type = PM_FREQ_CHANGE,
+ .frequency = 0,
+ .voltage = 0,
+ .latency = 15,
+ .prepare_transition = cpufreq_prepare_transition,
+ .transition = centrino_transition,
+ .finish_transition = cpufreq_finish_transition,
+};
+
+static struct oppoint low = {
+ .name = "low",
+ .type = PM_FREQ_CHANGE,
+ .latency = 15,
+ .prepare_transition = cpufreq_prepare_transition,
+ .transition = centrino_transition,
+ .finish_transition = cpufreq_finish_transition,
+};
+
+static struct oppoint mediumlow = {
+ .name = "mediumlow",
+ .type = PM_FREQ_CHANGE,
+ .latency = 15,
+ .prepare_transition = cpufreq_prepare_transition,
+ .transition = centrino_transition,
+ .finish_transition = cpufreq_finish_transition,
+};
+
+static struct oppoint medium = {
+ .name = "medium",
+ .type = PM_FREQ_CHANGE,
+ .latency = 15,
+ .prepare_transition = cpufreq_prepare_transition,
+ .transition = centrino_transition,
+ .finish_transition = cpufreq_finish_transition,
+};
+
+static struct oppoint mediumhigh = {
+ .name = "mediumhigh",
+ .type = PM_FREQ_CHANGE,
+ .latency = 15,
+ .prepare_transition = cpufreq_prepare_transition,
+ .transition = centrino_transition,
+ .finish_transition = cpufreq_finish_transition,
+};
+
+static struct oppoint high = {
+ .name = "high",
+ .type = PM_FREQ_CHANGE,
+ .latency = 15,
+ .prepare_transition = cpufreq_prepare_transition,
+ .transition = centrino_transition,
+ .finish_transition = cpufreq_finish_transition,
+};
+
+static struct oppoint highest = {
+ .name = "highest",
+ .type = PM_FREQ_CHANGE,
+ .latency = 15,
+ .prepare_transition = cpufreq_prepare_transition,
+ .transition = centrino_transition,
+ .finish_transition = cpufreq_finish_transition,
+};
+
+/* CPU models, their operating frequency range, and freq/voltage
+ operating points */
+static struct cpu_model models[] =
+{
+ _BANIAS(&cpu_ids[CPU_BANIAS], 900, " 900"),
+ BANIAS(1000),
+ BANIAS(1100),
+ BANIAS(1200),
+ BANIAS(1300),
+ BANIAS(1400),
+ BANIAS(1500),
+ BANIAS(1600),
+ BANIAS(1700),
+
+ /* NULL model_name is a wildcard */
+ { &cpu_ids[CPU_DOTHAN_A1], NULL, 0, NULL },
+ { &cpu_ids[CPU_DOTHAN_A2], NULL, 0, NULL },
+ { &cpu_ids[CPU_DOTHAN_B0], NULL, 0, NULL },
+ { &cpu_ids[CPU_MP4HT_D0], NULL, 0, NULL },
+ { &cpu_ids[CPU_MP4HT_E0], NULL, 0, NULL },
+
+ { NULL, }
+};
+#undef _BANIAS
+#undef BANIAS
+
+static int __init centrino_init_oppoint(void)
+{
+ struct cpuinfo_x86 *cpu = &cpu_data[0];
+ struct cpu_model *model;
+
+ for(model = models; model->cpu_id != NULL; model++) {
+ if (centrino_verify_cpu_id(cpu, model->cpu_id) &&
+ (model->model_name == NULL ||
+ strcmp(cpu->x86_model_id, model->model_name) == 0))
+ break;
+ }
+
+ if (model->cpu_id == NULL) {
+ /* No match at all */
+ printk("no support for CPU model %s\n", cpu->x86_model_id);
+ return -ENOENT;
+ }
+
+ printk("found \"%s\": max frequency: %dkHz\n",
+ model->model_name, model->max_freq);
+ switch (model->max_freq) {
+ case (900000) :
+ {
+ centrino_set_frequency(&low, 600, 844);
+ centrino_set_frequency(&medium, 800, 988);
+ centrino_set_frequency(&high, 900, 1004);
+ break;
+ }
+ case (1000000) :
+ {
+ centrino_set_frequency(&low, 600, 844);
+ centrino_set_frequency(&medium, 800, 972);
+ centrino_set_frequency(&high, 900, 988);
+ centrino_set_frequency(&highest, 1000, 1004);
+ break;
+ }
+ case (1100000) :
+ {
+ centrino_set_frequency(&lowest, 600, 956);
+ centrino_set_frequency(&low, 800, 1020);
+ centrino_set_frequency(&medium, 900, 1100);
+ centrino_set_frequency(&high, 1000, 1164);
+ centrino_set_frequency(&highest, 1100, 1180);
+ break;
+ }
+ case (1200000) :
+ {
+ centrino_set_frequency(&lowest, 600, 956);
+ centrino_set_frequency(&low, 800, 1004);
+ centrino_set_frequency(&medium, 900, 1020);
+ centrino_set_frequency(&mediumhigh, 1000, 1100);
+ centrino_set_frequency(&high, 1100, 1164);
+ centrino_set_frequency(&highest, 1200, 1180);
+ break;
+ }
+ case (1300000) :
+ {
+ centrino_set_frequency(&lowest, 600, 956);
+ centrino_set_frequency(&low, 800, 1260);
+ centrino_set_frequency(&medium, 1000, 1292);
+ centrino_set_frequency(&high, 1200, 1356);
+ centrino_set_frequency(&highest, 1300, 1388);
+ break;
+ }
+ case (1400000) :
+ {
+ centrino_set_frequency(&lowest, 600, 956);
+ centrino_set_frequency(&low, 800, 1180);
+ centrino_set_frequency(&medium, 1000, 1308);
+ centrino_set_frequency(&high, 1200, 1436);
+ centrino_set_frequency(&highest, 1400, 1484);
+ break;
+ }
+ case (1500000) :
+ {
+ centrino_set_frequency(&lowest, 600, 956);
+ centrino_set_frequency(&low, 800, 1116);
+ centrino_set_frequency(&medium, 1000, 1228);
+ centrino_set_frequency(&mediumhigh, 1200, 1356);
+ centrino_set_frequency(&high, 1400, 1452);
+ centrino_set_frequency(&highest, 1500, 1484);
+ break;
+ }
+ case (1600000) :
+ {
+ centrino_set_frequency(&lowest, 600, 956);
+ centrino_set_frequency(&low, 800, 1036);
+ centrino_set_frequency(&medium, 1000, 1164);
+ centrino_set_frequency(&mediumhigh, 1200, 1276);
+ centrino_set_frequency(&high, 1400, 1420);
+ centrino_set_frequency(&highest, 1600, 1484);
+ break;
+ }
+ case (1700000) :
+ {
+ centrino_set_frequency(&lowest, 600, 956);
+ centrino_set_frequency(&low, 800, 1004);
+ centrino_set_frequency(&medium, 1000, 1116);
+ centrino_set_frequency(&mediumhigh, 1200, 1228);
+ centrino_set_frequency(&high, 1400, 1308);
+ centrino_set_frequency(&highest, 1700, 1484);
+ break;
+ }
+ }
+ if (lowest.frequency)
+ list_add_tail(&lowest.list, &pm_states.list);
+ if (low.frequency)
+ list_add_tail(&low.list, &pm_states.list);
+ if (mediumlow.frequency)
+ list_add_tail(&mediumlow.list, &pm_states.list);
+ if (medium.frequency)
+ list_add_tail(&medium.list, &pm_states.list);
+ if (mediumhigh.frequency)
+ list_add_tail(&mediumhigh.list, &pm_states.list);
+ if (high.frequency) {
+ list_add_tail(&high.list, &pm_states.list);
+ current_state = &high;
+ }
+ if (highest.frequency) {
+ list_add_tail(&highest.list, &pm_states.list);
+ current_state = &highest;
+ }
+ return 0;
+}
+
+static void centrino_exit_oppoint(void)
+{
+ if (lowest.frequency)
+ list_del_init(&lowest.list);
+ if (low.frequency)
+ list_del_init(&low.list);
+ if (mediumlow.frequency)
+ list_del_init(&mediumlow.list);
+ if (medium.frequency)
+ list_del_init(&medium.list);
+ if (mediumhigh.frequency)
+ list_del_init(&mediumhigh.list);
+ if (high.frequency)
+ list_del_init(&high.list);
+ if (highest.frequency)
+ list_del_init(&highest.list);
+ return;
+}
+
+static int centrino_verify_cpu_id(const struct cpuinfo_x86 *c, const struct cpu_id *x)
+{
+ if ((c->x86 == x->x86) &&
+ (c->x86_model == x->x86_model) &&
+ (c->x86_mask == x->x86_mask))
+ return 1;
+ return 0;
+}
+
+MODULE_AUTHOR ("David Singleton <dsingleton@mvista.com>");
+MODULE_DESCRIPTION ("PowerOp operting points for Intel Pentium M processors.");
+MODULE_LICENSE ("GPL");
+
+late_initcall(centrino_init_oppoint);
+module_exit(centrino_exit_oppoint);

[-- Attachment #3: Type: text/plain, Size: 0 bytes --]