[linux-pm] Powerop-x86-centrino.patch

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



This patch adds PowerOp support to the x86 centrino line of processors. 
  The code
has been moved out of the cpufreq directory into its own 
arch/i386/kernel/cpu/powerop
directory.

The patch is different from the first powerop-x86-centrino.patch in 
that it
now supports all the speedstep-centrino processors at boot time, instead
of just the 1.4GHz my laptop was using.

It also uses the new unified naming scheme of operating points.  This 
feature
lets any set of operating points to be supported and lets the PowerOp
daemon be able to make some simple rules based decisions on which
operating points to transition to without having to know too much about
the underlying frequencies or voltages.

The patch also adds a new module to create dynamic, on-the-fly
operating point with the frequency, voltage and transition latency
passed in at insmod time.

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

  arch/i386/kernel/cpu/Makefile                           |    1
  arch/i386/kernel/cpu/powerop/Makefile                   |    2
  arch/i386/kernel/cpu/powerop/centrino-dynamic-powerop.c |   70 ++
  arch/i386/kernel/cpu/powerop/centrino-powerop.c         |  465 
++++++++++++++++
  arch/i386/kernel/i386_ksyms.c                           |    4
  5 files changed, 542 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)       +=      powerop/
Index: linux-2.6.17/arch/i386/kernel/cpu/powerop/Makefile
===================================================================
--- /dev/null
+++ linux-2.6.17/arch/i386/kernel/cpu/powerop/Makefile
@@ -0,0 +1,2 @@
[david-singletons-Computer:~] dsinglet% cat powerop-x86-centrino.patch
Signed-Off-by: David Singleton <dsingleton at mvista.com>

  arch/i386/kernel/cpu/Makefile                           |    1
  arch/i386/kernel/cpu/powerop/Makefile                   |    2
  arch/i386/kernel/cpu/powerop/centrino-dynamic-powerop.c |   70 ++
  arch/i386/kernel/cpu/powerop/centrino-powerop.c         |  465 
++++++++++++++++
  arch/i386/kernel/i386_ksyms.c                           |    4
  5 files changed, 542 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)       +=      powerop/
Index: linux-2.6.17/arch/i386/kernel/cpu/powerop/Makefile
===================================================================
--- /dev/null
+++ linux-2.6.17/arch/i386/kernel/cpu/powerop/Makefile
@@ -0,0 +1,2 @@
+obj-$(CONFIG_X86_SPEEDSTEP_CENTRINO)   += centrino-powerop.o
+obj-m                                  += centrino-dynamic-powerop.o
Index: 
linux-2.6.17/arch/i386/kernel/cpu/powerop/centrino-dynamic-powerop.c
===================================================================
--- /dev/null
+++ linux-2.6.17/arch/i386/kernel/cpu/powerop/centrino-dynamic-powerop.c
@@ -0,0 +1,70 @@
+/*
+ * powerop/centrino-dynamic-powerop.c
+ *
+ * This is the template to create dynamic operating points for power 
management.
+ *
+ * Author: David Singleton dsingleton at 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 powerop *cur, struct powerop *new);
+
+static unsigned int frequency = 1000000;
+static unsigned int voltage = 1308;
+static unsigned int latency = 100;
+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 powerop dynamic_op = {
+       .type = PM_FREQ_CHANGE,
+       .name = "Dynamic",
+       .description = "Dynamic PowerOp point for Speedstep Centrino",
+       .prepare_transition = cpufreq_prepare_transition,
+       .transition = centrino_transition,
+       .finish_transition = cpufreq_finish_transition,
+};
+
+extern void centrino_set_frequency(struct powerop *op, uint freq, uint 
volt);
+
+int __init dynamic_powerop_init(void)
+{
+
+       printk("Dynamic PowerOp operating point for speedstep 
centrino\n");
+       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_powerop_cleanup(void)
+{
+       list_del_init(&dynamic_op.list);
+}
+
+module_init(dynamic_powerop_init);
+module_exit(dynamic_powerop_cleanup);
+
+MODULE_DESCRIPTION("Dynamic Powerop module");
+MODULE_LICENSE("GPL");
Index: linux-2.6.17/arch/i386/kernel/cpu/powerop/centrino-powerop.c
===================================================================
--- /dev/null
+++ linux-2.6.17/arch/i386/kernel/cpu/powerop/centrino-powerop.c
@@ -0,0 +1,465 @@
+/*
+ * 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 at 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 powerop *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 powerop *cur, struct powerop *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);
+
+       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 powerop lowest = {
+       .name = "lowest",
+       .description = "Lowest Frequency state",
+       .type = PM_FREQ_CHANGE,
+       .frequency = 0,
+       .voltage = 0,
+       .latency = 100,
+       .prepare_transition  = cpufreq_prepare_transition,
+       .transition = centrino_transition,
+       .finish_transition = cpufreq_finish_transition,
+};
+
+static struct powerop low = {
+       .name = "low",
+       .description = "Low Frequency state",
+       .type = PM_FREQ_CHANGE,
+       .latency = 100,
+       .prepare_transition  = cpufreq_prepare_transition,
+       .transition = centrino_transition,
+       .finish_transition = cpufreq_finish_transition,
+};
+
+static struct powerop medlow = {
+       .name = "medlow",
+       .description = "Medium Low Frequency state",
+       .type = PM_FREQ_CHANGE,
+       .latency = 100,
+       .prepare_transition  = cpufreq_prepare_transition,
+       .transition = centrino_transition,
+       .finish_transition = cpufreq_finish_transition,
+};
+
+static struct powerop med = {
+       .name = "med",
+       .description = "Medium Frequency state",
+       .type = PM_FREQ_CHANGE,
+       .latency = 100,
+       .prepare_transition  = cpufreq_prepare_transition,
+       .transition = centrino_transition,
+       .finish_transition = cpufreq_finish_transition,
+};
+
+static struct powerop medhigh = {
+       .name = "medhigh",
+       .description = "Medium High Frequency state",
+       .type = PM_FREQ_CHANGE,
+       .latency = 100,
+       .prepare_transition  = cpufreq_prepare_transition,
+       .transition = centrino_transition,
+       .finish_transition = cpufreq_finish_transition,
+};
+
+static struct powerop high = {
+       .name = "high",
+       .description = "High Frequency state",
+       .type = PM_FREQ_CHANGE,
+       .latency = 100,
+       .prepare_transition  = cpufreq_prepare_transition,
+       .transition = centrino_transition,
+       .finish_transition = cpufreq_finish_transition,
+};
+
+static struct powerop highest = {
+       .name = "highest",
+       .description = "Highest Frequency state",
+       .type = PM_FREQ_CHANGE,
+       .latency = 100,
+       .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_powerop(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(&med, 800, 988);
+               centrino_set_frequency(&high, 900, 1004);
+               break;
+           }
+           case (1000000) :
+           {
+               centrino_set_frequency(&low, 600, 844);
+               centrino_set_frequency(&med, 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(&med, 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(&med, 900, 1020);
+               centrino_set_frequency(&medhigh, 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(&med, 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(&med, 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(&med, 1000, 1228);
+               centrino_set_frequency(&medhigh, 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(&med, 1000, 1164);
+               centrino_set_frequency(&medhigh, 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(&med, 1000, 1116);
+               centrino_set_frequency(&medhigh, 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 (medlow.frequency)
+               list_add_tail(&medlow.list, &pm_states.list);
+       if (med.frequency)
+               list_add_tail(&med.list, &pm_states.list);
+       if (medhigh.frequency)
+               list_add_tail(&medhigh.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_powerop(void)
+{
+       if (lowest.frequency)
+               list_del_init(&lowest.list);
+       if (low.frequency)
+               list_del_init(&low.list);
+       if (medlow.frequency)
+               list_del_init(&medlow.list);
+       if (med.frequency)
+               list_del_init(&med.list);
+       if (medhigh.frequency)
+               list_del_init(&medhigh.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 at mvista.com>");
+MODULE_DESCRIPTION ("PowerOp operting points for Intel Pentium M 
processors.");
+MODULE_LICENSE ("GPL");
+
+late_initcall(centrino_init_powerop);
+module_exit(centrino_exit_powerop);
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 of Archives]     [Linux ACPI]     [Netdev]     [Ethernet Bridging]     [Linux Wireless]     [CPU Freq]     [Kernel Newbies]     [Fedora Kernel]     [Security]     [Linux for Hams]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux RAID]     [Linux Admin]     [Samba]

  Powered by Linux