diff --git a/arch/i386/Kconfig b/arch/i386/Kconfig
index f71fb4a..62d7523 100644
--- a/arch/i386/Kconfig
+++ b/arch/i386/Kconfig
@@ -814,6 +814,18 @@ source kernel/power/Kconfig
source "drivers/acpi/Kconfig"
+menu "PM Core"
+
+config PM_CORE
+ tristate "PM Core"
+ depends POWEROP
+ help
+ This enables i386 PM Core to control platform freq/voltage via
+ PowerOP interface
+
+endmenu
+
+
menu "APM (Advanced Power Management) BIOS Support"
depends on PM && !X86_VISWS
diff --git a/arch/i386/kernel/cpu/cpufreq/Kconfig b/arch/i386/kernel/cpu/cpufreq/Kconfig
index ccc1edf..a1ce5a3 100644
--- a/arch/i386/kernel/cpu/cpufreq/Kconfig
+++ b/arch/i386/kernel/cpu/cpufreq/Kconfig
@@ -106,17 +106,11 @@ config X86_GX_SUSPMOD
If in doubt, say N.
config X86_SPEEDSTEP_CENTRINO
- tristate "Intel Enhanced SpeedStep"
- select CPU_FREQ_TABLE
+ tristate "SpeedStep operatin points registration"
+ depends on POWEROP
select X86_SPEEDSTEP_CENTRINO_TABLE if (!X86_SPEEDSTEP_CENTRINO_ACPI)
help
- This adds the CPUFreq driver for Enhanced SpeedStep enabled
- mobile CPUs. This means Intel Pentium M (Centrino) CPUs. However,
- you also need to say Y to "Use ACPI tables to decode..." below
- [which might imply enabling ACPI] if you want to use this driver
- on non-Banias CPUs.
-
- For details, take a look at .
+ Opeatin points registration module for SpeedStep Centrino
If in doubt, say N.
diff --git a/arch/i386/kernel/cpu/cpufreq/Makefile b/arch/i386/kernel/cpu/cpufreq/Makefile
index 2e894f1..62753f6 100644
--- a/arch/i386/kernel/cpu/cpufreq/Makefile
+++ b/arch/i386/kernel/cpu/cpufreq/Makefile
@@ -8,6 +8,7 @@ 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_PM_CORE) += speedstep-centrino-pm_core.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
diff --git a/arch/i386/kernel/cpu/cpufreq/speedstep-centrino-pm_core.c b/arch/i386/kernel/cpu/cpufreq/speedstep-centrino-pm_core.c
new file mode 100644
index 0000000..8ed9008
--- /dev/null
+++ b/arch/i386/kernel/cpu/cpufreq/speedstep-centrino-pm_core.c
@@ -0,0 +1,644 @@
+/*
+ * PM Core driver for Enhanced SpeedStep, as found in Intel's Pentium
+ * M (part of the Centrino chipset).
+ *
+ * Since the original Pentium M, most new Intel CPUs support Enhanced
+ * SpeedStep.
+ *
+ * Despite the "SpeedStep" in the name, this is almost entirely unlike
+ * traditional SpeedStep.
+ *
+ * Modelled on speedstep.c
+ *
+ * Author: Eugeny S. Mints
+ *
+ * Copyright (C) 2006 Nomad Global Solutions, 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.
+ *
+ * Based on code by
+ * Copyright (C) 2003 Jeremy Fitzhardinge
+ */
+#include
+#include
+#include
+#include /* current */
+#include
+#include
+#include
+
+#ifdef CONFIG_X86_SPEEDSTEP_CENTRINO_ACPI
+#include
+#include
+#endif
+
+#include
+#include
+#include
+#include
+
+static cpumask_t acpi_get_shared_mask(int cpu);
+
+#ifdef CONFIG_X86_SPEEDSTEP_CENTRINO_ACPI
+struct acpi_processor_performance *acpi_perf_data[NR_CPUS];
+#endif
+
+static long
+get_vtg(int cpu)
+{
+ int v = 0;
+ unsigned l, h;
+ cpumask_t saved_mask;
+
+ saved_mask = current->cpus_allowed;
+ set_cpus_allowed(current, cpumask_of_cpu(cpu));
+ if (smp_processor_id() != cpu)
+ return 0;
+
+ rdmsr(MSR_IA32_PERF_STATUS, l, h);
+ v = (l & 0xff) * 16 + 700;
+
+ set_cpus_allowed(current, saved_mask);
+
+ return v;
+}
+
+/* Return the current CPU frequency in kHz
+ * FIXME: 0 on return means error but the rc almost never is checked
+ * against 0
+ * FIXME: due to direct calculation from msr this may work not for all cases
+ */
+static unsigned int
+get_freq(int cpu)
+{
+ unsigned l, h;
+ unsigned clock_freq;
+ cpumask_t saved_mask;
+
+ saved_mask = current->cpus_allowed;
+ set_cpus_allowed(current, cpumask_of_cpu(cpu));
+ if (smp_processor_id() != cpu)
+ return 0;
+
+ rdmsr(MSR_IA32_PERF_STATUS, l, h);
+ clock_freq = (l >> 8) & 0xff;
+
+ if (unlikely(clock_freq == 0)) {
+ /*
+ * On some CPUs, we can see transient MSR values (which are
+ * not present in _PSS), while CPU is doing some automatic
+ * P-state transition (like TM2). Get the last freq set
+ * in PERF_CTL.
+ */
+ rdmsr(MSR_IA32_PERF_CTL, l, h);
+ clock_freq = (l >> 8) & 0xff;
+ }
+
+ set_cpus_allowed(current, saved_mask);
+
+ return clock_freq * 100 * 1000;
+}
+
+
+/* FIXME: verification is incomplete */
+static int
+sc_pm_core_verify_opt(struct pm_core_point *opt)
+{
+ /*
+ * Let's do some upfront error checking. If we fail any of these,
+ * then the whole operating point is suspect and therefore invalid.
+ *
+ * Vrification includes but is not limited to checking that passed
+ * parameters are withing ranges.
+ */
+ if (opt == NULL)
+ return -EINVAL;
+
+ /* TODO: implement all necessary checking */
+
+ return 0;
+}
+
+#define PWR_PARAM_SET 1
+#define PWR_PARAM_GET 2
+
+/*
+ * FIXME: very temporary implementation, just to prove the concept !!
+ */
+static int
+process_pwr_param(struct pm_core_point *opt, int op, char *param_name,
+ int va_arg)
+{
+ int cpu = 0;
+ char buf[8];
+
+ for (cpu = 0; cpu < NR_CPUS; cpu++)
+ {
+ sprintf(buf, "v%d", cpu);
+
+ if (strcmp(param_name, buf) == 0) {
+ if (op == PWR_PARAM_SET)
+ opt->opt[cpu].pwpr[_I386_PM_CORE_POINT_V] =
+ va_arg;
+ else if (opt != NULL)
+ *(int *)va_arg =
+ opt->opt[cpu].pwpr[_I386_PM_CORE_POINT_V];
+ else if ((*(int *)va_arg = get_vtg(cpu)) <= 0)
+ return -EINVAL;
+ return 0;
+ }
+
+ sprintf(buf, "freq%d", cpu);
+
+ if (strcmp(param_name, buf) == 0) {
+ if (op == PWR_PARAM_SET)
+ opt->opt[cpu].pwpr[_I386_PM_CORE_POINT_FREQ] =
+ va_arg;
+ else if (opt != NULL)
+ *(int *)va_arg =
+ opt->opt[cpu].pwpr[_I386_PM_CORE_POINT_FREQ];
+ else if ((*(int *)va_arg = get_freq(cpu)) <= 0)
+ return -EINVAL;
+
+ return 0;
+ }
+ }
+
+ return -EINVAL;
+}
+
+#define PWR_PARAM_MAX_LENGTH 20
+static void *
+sc_pm_create_point(const char *pwr_params, va_list args)
+{
+ struct pm_core_point *opt;
+ char buf[PWR_PARAM_MAX_LENGTH + 1];
+ int i = 0;
+ int err;
+
+ opt = kmalloc(sizeof(struct pm_core_point), GFP_KERNEL);
+ if (opt == NULL)
+ return NULL;
+ /*
+ * PM Core puts '-1' for all power parameters which are
+ * not listed explicitly
+ */
+ I386_PM_CORE_INIT_OPT(opt);
+
+ i = 0;
+ while (*pwr_params) {
+ if (*pwr_params != ' ') {
+ buf[i] = *pwr_params++;
+ i++;
+ }
+ else {
+ buf[i] = 0;
+ err = process_pwr_param(opt, PWR_PARAM_SET, buf,
+ (int)va_arg(args, int));
+ if (err != 0)
+ goto out;
+ i = 0;
+ }
+ }
+
+ return (void *)opt;
+out:
+ kfree(opt);
+ return NULL;
+}
+
+static int
+sc_pm_core_set_opt(void *md_opt)
+{
+ int rc = 0, i = 0;
+ struct pm_core_point *opt = (struct pm_core_point *)md_opt;
+ unsigned int msr, oldmsr = 0, h = 0, cpu = 0;
+ cpumask_t online_policy_cpus;
+ cpumask_t saved_mask;
+ cpumask_t set_mask, cpus;
+ cpumask_t covered_cpus;
+ int retval = 0;
+ unsigned int j, first_cpu;
+
+ rc = sc_pm_core_verify_opt(opt);
+ if (rc != 0)
+ return rc;
+ /*
+ * here current i386 pm core implenetation relies on the fact that
+ * nowadays all operation points have the following view:
+ * { -1, -1, ..., vtlgN, freqN, -1, -1 }
+ * once the assumption is not true implementation needs to be adjusted
+ * properly
+ */
+ for (i = 0; i < NR_CPUS; i++)
+ if (opt->opt[i].pwpr[_I386_PM_CORE_POINT_FREQ] != -1) {
+ cpu = i;
+ break;
+ }
+
+ cpus = acpi_get_shared_mask(i);
+
+#ifdef CONFIG_HOTPLUG_CPU
+ /* cpufreq holds the hotplug lock, so we are safe from here on */
+ cpus_and(online_policy_cpus, cpu_online_map, cpus);
+#else
+ online_policy_cpus = cpus;
+#endif
+
+ saved_mask = current->cpus_allowed;
+ first_cpu = 1;
+ cpus_clear(covered_cpus);
+ for_each_cpu_mask(j, online_policy_cpus) {
+ /*
+ * Support for SMP systems.
+ * Make sure we are running on CPU that wants to change freq
+ */
+ cpus_clear(set_mask);
+#ifdef CONFIG_X86_SPEEDSTEP_CENTRINO_ACPI
+ if (acpi_perf_data[cpu]->shared_type ==
+ DOMAIN_COORD_TYPE_SW_ANY)
+ cpus_or(set_mask, set_mask, online_policy_cpus);
+ else
+#endif
+ cpu_set(j, set_mask);
+
+ set_cpus_allowed(current, set_mask);
+ if (unlikely(!cpu_isset(smp_processor_id(), set_mask))) {
+ printk("couldn't limit to CPUs in this domain\n");
+ retval = -EAGAIN;
+ if (first_cpu) {
+ /* We haven't started the transition yet. */
+ goto migrate_end;
+ }
+ break;
+ }
+
+ msr =
+ (((opt->opt[cpu].pwpr[_I386_PM_CORE_POINT_FREQ])/100) << 8) |
+ ((opt->opt[cpu].pwpr[_I386_PM_CORE_POINT_V] - 700) / 16);
+
+ if (first_cpu) {
+ rdmsr(MSR_IA32_PERF_CTL, oldmsr, h);
+ if (msr == (oldmsr & 0xffff)) {
+ printk("no change needed - msr was and needs "
+ "to be %x\n", oldmsr);
+ retval = 0;
+ goto migrate_end;
+ }
+
+ first_cpu = 0;
+ /* all but 16 LSB are reserved, treat them with care */
+ oldmsr &= ~0xffff;
+ msr &= 0xffff;
+ oldmsr |= msr;
+ }
+
+ wrmsr(MSR_IA32_PERF_CTL, oldmsr, h);
+#ifdef CONFIG_X86_SPEEDSTEP_CENTRINO_ACPI
+ if (acpi_perf_data[cpu]->shared_type ==
+ DOMAIN_COORD_TYPE_SW_ANY)
+ break;
+#endif
+ cpu_set(j, covered_cpus);
+ }
+
+ if (unlikely(retval)) {
+ /*
+ * We have failed halfway through the frequency change.
+ * We have sent callbacks to policy->cpus and
+ * MSRs have already been written on coverd_cpus.
+ * Best effort undo..
+ */
+
+ if (!cpus_empty(covered_cpus)) {
+ for_each_cpu_mask(j, covered_cpus) {
+ set_cpus_allowed(current, cpumask_of_cpu(j));
+ wrmsr(MSR_IA32_PERF_CTL, oldmsr, h);
+ }
+ }
+ }
+
+migrate_end:
+ set_cpus_allowed(current, saved_mask);
+
+ return retval;
+}
+
+static int
+sc_pm_core_get_opt(void *md_opt, const char *pwr_params, va_list args)
+{
+ int err = 0;
+ int i = 0;
+ struct pm_core_point *opt = (struct pm_core_point *)md_opt;
+ char buf[PWR_PARAM_MAX_LENGTH + 1];
+
+ i = 0;
+ while (*pwr_params) {
+ if (*pwr_params != ' ') {
+ buf[i] = *pwr_params++;
+ i++;
+ }
+ else {
+ buf[i] = 0;
+ err = process_pwr_param(opt, PWR_PARAM_GET, buf,
+ (int)va_arg(args, int));
+ if (err != 0)
+ return err;
+ i = 0;
+ }
+ }
+
+ return 0;
+}
+
+
+static struct powerop_driver sc_pm_core_driver = {
+ .name = "speedstep-centrino pm core driver",
+ .create_point = sc_pm_create_point,
+ .get_point = sc_pm_core_get_opt,
+ .set_point = sc_pm_core_set_opt,
+};
+
+/*
+ * ACPI auxiliary routines are needed since PM Core needs access to
+ * p->shared_cpu_map at set point time
+ * FIXME: all auxiliary routine might be elsewhere, especially
+ * arch_get_shared_mask_on_cpu()
+ */
+#ifdef CONFIG_X86_SPEEDSTEP_CENTRINO_ACPI
+static cpumask_t
+acpi_get_shared_mask(int cpu)
+{
+ struct acpi_processor_performance *p;
+
+ p = acpi_perf_data[cpu];
+ if (p->shared_type == DOMAIN_COORD_TYPE_SW_ALL||
+ p->shared_type == DOMAIN_COORD_TYPE_SW_ANY)
+ return p->shared_cpu_map;
+ else
+ return cpumask_of_cpu(cpu);
+}
+
+/*
+ * cpu_early_init_acpi - Do the preregistering with ACPI P-States
+ * library
+ *
+ * Before doing the actual init, we need to do _PSD related setup whenever
+ * supported by the BIOS. These are handled by this early_init routine.
+ */
+static int
+acpi_early_init(void)
+{
+ unsigned int i, j;
+ struct acpi_processor_performance *data;
+
+ for_each_possible_cpu(i) {
+ data = kzalloc(sizeof(struct acpi_processor_performance),
+ GFP_KERNEL);
+ if (!data) {
+ for_each_possible_cpu(j) {
+ kfree(acpi_perf_data[j]);
+ acpi_perf_data[j] = NULL;
+ }
+ return -ENOMEM;
+ }
+ acpi_perf_data[i] = data;
+ }
+
+ acpi_processor_preregister_performance(acpi_perf_data);
+ return 0;
+}
+
+static int
+acpi_cpu_data_init(int cpu)
+{
+ int result = 0;
+ struct cpuinfo_x86 *x86_cpu = &cpu_data[cpu];
+ int i;
+#ifdef CONFIG_X86_SPEEDSTEP_CENTRINO_ACPI
+ struct acpi_processor_performance *p;
+#endif
+
+ /* Only Intel makes Enhanced Speedstep-capable CPUs */
+ if (x86_cpu->x86_vendor != X86_VENDOR_INTEL ||
+ !cpu_has(x86_cpu, X86_FEATURE_EST))
+ return -ENODEV;
+
+#ifdef CONFIG_X86_SPEEDSTEP_CENTRINO_ACPI
+ p = acpi_perf_data[cpu];
+
+ /* register with ACPI core */
+ if (acpi_processor_register_performance(p, cpu)) {
+ printk("obtaining ACPI data failed\n");
+ return -EIO;
+ }
+
+ /* verify the acpi_data */
+ if (p->state_count <= 1) {
+ printk("No P-States\n");
+ result = -ENODEV;
+ goto err_unreg;
+ }
+
+ if ((p->control_register.space_id != ACPI_ADR_SPACE_FIXED_HARDWARE) ||
+ (p->status_register.space_id != ACPI_ADR_SPACE_FIXED_HARDWARE)) {
+ printk("Invalid control/status registers (%x - %x)\n",
+ p->control_register.space_id,
+ p->status_register.space_id);
+ result = -EIO;
+ goto err_unreg;
+ }
+
+ for (i = 0; i < p->state_count; i++) {
+ if (p->states[i].control != p->states[i].status) {
+ printk("Different control (%llu) and status values"
+ " (%llu)\n",
+ p->states[i].control, p->states[i].status);
+ result = -EINVAL;
+ goto err_unreg;
+ }
+
+ if (!p->states[i].core_frequency) {
+ printk("Zero core frequency for state %u\n", i);
+ result = -EINVAL;
+ goto err_unreg;
+ }
+
+ if (p->states[i].core_frequency > p->states[0].core_frequency) {
+ printk("P%u has larger frequency (%llu) than P0 "
+ "(%llu), skipping\n", i,
+ p->states[i].core_frequency,
+ p->states[0].core_frequency);
+ p->states[i].core_frequency = 0;
+ continue;
+ }
+ }
+
+ return 0;
+
+err_unreg:
+ acpi_processor_unregister_performance(p, cpu);
+ printk("invalid ACPI data\n");
+#endif
+ return result;
+}
+#else
+static inline int acpi_early_init(void) { return 0; }
+static inline int acpi_cpu_data_init(int cpu) { return 0; }
+static cpumask_t acpi_get_shared_mask(int cpu) { return cpumask_of_cpu(cpu); }
+#endif
+
+cpumask_t
+arch_get_dependent_cpus_mask(int cpu)
+{
+ return acpi_get_shared_mask(cpu);
+}
+EXPORT_SYMBOL_GPL(arch_get_dependent_cpus_mask);
+
+static int
+sc_pm_core_add_dev(struct sys_device *sys_dev)
+{
+ unsigned int cpu = sys_dev->id;
+ struct cpuinfo_x86 *x86_cpu = &cpu_data[cpu];
+ int rc = 0, l, h;
+ cpumask_t saved_mask;
+
+ if ((x86_cpu->x86_vendor != X86_VENDOR_INTEL) ||
+ !cpu_has(x86_cpu, X86_FEATURE_EST))
+ return -ENODEV;
+
+ /*
+ * Check to see if Enhanced SpeedStep is enabled, and try to
+ * enable it if not.
+ */
+ saved_mask = current->cpus_allowed;
+ set_cpus_allowed(current, cpumask_of_cpu(cpu));
+
+ if (smp_processor_id() != cpu)
+ return -EAGAIN;
+
+ rdmsr(MSR_IA32_MISC_ENABLE, l, h);
+
+ if (!(l & (1<<16))) {
+ l |= (1<<16);
+
+ printk("trying to enable Enhanced SpeedStep (%x)\n", l);
+ wrmsr(MSR_IA32_MISC_ENABLE, l, h);
+
+ /* check to see if it stuck */
+ rdmsr(MSR_IA32_MISC_ENABLE, l, h);
+ if (!(l & (1<<16))) {
+ printk("couldn't enable Enhanced SpeedStep\n");
+ set_cpus_allowed(current, saved_mask);
+ return -ENODEV;
+ }
+ }
+
+ set_cpus_allowed(current, saved_mask);
+
+ rc = acpi_cpu_data_init(cpu);
+ if (rc != 0)
+ return rc;
+
+ /* notify BIOS that we exist */
+ acpi_processor_notify_smm(THIS_MODULE);
+
+ return rc;
+}
+
+static int
+sc_pm_core_remove_dev (struct sys_device * sys_dev)
+{
+ unsigned int cpu = sys_dev->id;
+#ifdef CONFIG_X86_SPEEDSTEP_CENTRINO_ACPI
+ struct acpi_processor_performance *p;
+
+ if (acpi_perf_data[cpu]) {
+ p = acpi_perf_data[cpu];
+ acpi_processor_unregister_performance(p, cpu);
+ }
+#endif
+ return 0;
+}
+
+static int
+sc_pm_core_suspend(struct sys_device * sysdev, pm_message_t pmsg)
+{
+ return 0;
+}
+
+static int
+sc_pm_core_resume(struct sys_device * sysdev)
+{
+ return 0;
+}
+
+static struct sysdev_driver pm_core_sysdev_driver = {
+ .add = sc_pm_core_add_dev,
+ .remove = sc_pm_core_remove_dev,
+ .suspend = sc_pm_core_suspend,
+ .resume = sc_pm_core_resume,
+};
+
+/* FIXME: will add HOTPLUG handler similar to cpureq as soon as understand
+ * why hotplug cannot be handled via sysdev driver
+ */
+int __init
+sc_pm_core_init(void)
+{
+ int rc = 0, j = 0;
+ rc = acpi_early_init();
+ if (rc != 0)
+ return rc;
+
+ rc = sysdev_driver_register(&cpu_sysdev_class, &pm_core_sysdev_driver);
+ if (rc != 0)
+ goto out_free;
+
+ rc = powerop_driver_register(&sc_pm_core_driver);
+ if (rc != 0) {
+ sysdev_driver_unregister(&cpu_sysdev_class,
+ &pm_core_sysdev_driver);
+ goto out_free;
+ }
+
+ return rc;
+out_free:
+#ifdef CONFIG_X86_SPEEDSTEP_CENTRINO_ACPI
+ for_each_possible_cpu(j) {
+ kfree(acpi_perf_data[j]);
+ acpi_perf_data[j] = NULL;
+ }
+#endif /* CONFIG_X86_SPEEDSTEP_CENTRINO_ACPI */
+ return rc;
+}
+
+
+static void __exit
+sc_pm_core_exit(void)
+{
+#ifdef CONFIG_X86_SPEEDSTEP_CENTRINO_ACPI
+ unsigned int j;
+#endif
+
+ powerop_driver_unregister(&sc_pm_core_driver);
+ sysdev_driver_unregister(&cpu_sysdev_class, &pm_core_sysdev_driver);
+
+#ifdef CONFIG_X86_SPEEDSTEP_CENTRINO_ACPI
+ for_each_possible_cpu(j) {
+ kfree(acpi_perf_data[j]);
+ acpi_perf_data[j] = NULL;
+ }
+#endif
+}
+
+
+module_init(sc_pm_core_init);
+module_exit(sc_pm_core_exit);
+
+
+MODULE_AUTHOR ("Eugeny S. Mints
*
- * Despite the "SpeedStep" in the name, this is almost entirely unlike
- * traditional SpeedStep.
- *
- * Modelled on speedstep.c
+ * Copyright (C) 2006 Nomad Global Solutions, 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.
*
+ * Based on code by
* Copyright (C) 2003 Jeremy Fitzhardinge
*/
-
#include
#include
#include
@@ -20,6 +21,7 @@ #include
#include /* current */
#include
#include
+#include
#ifdef CONFIG_X86_SPEEDSTEP_CENTRINO_ACPI
#include
@@ -30,11 +32,14 @@ #include
#include
#include
-#define PFX "speedstep-centrino: "
-#define MAINTAINER "cpufreq@lists.linux.org.uk"
+#define PFX "speedstep-centrino-op-points: "
+#define MAINTAINER "linux-pm@lists.osld.org"
#define dprintk(msg...) cpufreq_debug_printk(CPUFREQ_DEBUG_DRIVER, "speedstep-centrino", msg)
+#ifdef CONFIG_X86_SPEEDSTEP_CENTRINO_ACPI
+extern struct acpi_processor_performance *acpi_perf_data[];
+#endif
struct cpu_id
{
@@ -62,22 +67,37 @@ static const struct cpu_id cpu_ids[] = {
};
#define N_IDS ARRAY_SIZE(cpu_ids)
+#define CENTRINO_EXTRACT_VLTG(index) (((index) & 0xFF) * 16 + 700)
+#define SC_TABLE_END_MARK ~1
+/*
+ * please note that this structure has nothing to do with pm_core_point, it's
+ * completely internal structure of this particula "registration" driver which
+ * is an instance of a layer above PowerOP
+ * for example 'index' field value is not a voltage value but an aggregated
+ * value initially designed to match format returned by ACPI
+ *
+ * this strucure may be improved to be more straightforward by replacing index
+ * with real voltage value since now PowerOP is underneath interface but left
+ * almost untouched to order to leave cprufreq code recognazible
+ */
+struct sc_opt {
+ unsigned int frequency;
+ unsigned int index;
+};
+
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 */
+ const char *model_name;
+ unsigned max_freq; /* max clock in kHz */
+ struct sc_opt *op_points; /* clock/voltage pairs */
};
static int centrino_verify_cpu_id(const struct cpuinfo_x86 *c, const struct cpu_id *x);
-/* Operating points for current CPU */
static struct cpu_model *centrino_model[NR_CPUS];
static const struct cpu_id *centrino_cpu[NR_CPUS];
-static struct cpufreq_driver centrino_driver;
-
+/* Operating point tables */
#ifdef CONFIG_X86_SPEEDSTEP_CENTRINO_TABLE
/* Computes the correct form for IA32_PERF_CTL MSR for a particular
@@ -97,38 +117,38 @@ #define OP(mhz, mv) \
*/
/* Ultra Low Voltage Intel Pentium M processor 900MHz (Banias) */
-static struct cpufreq_frequency_table banias_900[] =
+static struct sc_opt banias_900[] =
{
OP(600, 844),
OP(800, 988),
OP(900, 1004),
- { .frequency = CPUFREQ_TABLE_END }
+ { .frequency = SC_TABLE_END_MARK }
};
/* Ultra Low Voltage Intel Pentium M processor 1000MHz (Banias) */
-static struct cpufreq_frequency_table banias_1000[] =
+static struct sc_opt banias_1000[] =
{
OP(600, 844),
OP(800, 972),
OP(900, 988),
OP(1000, 1004),
- { .frequency = CPUFREQ_TABLE_END }
+ { .frequency = SC_TABLE_END_MARK }
};
/* Low Voltage Intel Pentium M processor 1.10GHz (Banias) */
-static struct cpufreq_frequency_table banias_1100[] =
+static struct sc_opt banias_1100[] =
{
OP( 600, 956),
OP( 800, 1020),
OP( 900, 1100),
OP(1000, 1164),
OP(1100, 1180),
- { .frequency = CPUFREQ_TABLE_END }
+ { .frequency = SC_TABLE_END_MARK }
};
/* Low Voltage Intel Pentium M processor 1.20GHz (Banias) */
-static struct cpufreq_frequency_table banias_1200[] =
+static struct sc_opt banias_1200[] =
{
OP( 600, 956),
OP( 800, 1004),
@@ -136,33 +156,33 @@ static struct cpufreq_frequency_table ba
OP(1000, 1100),
OP(1100, 1164),
OP(1200, 1180),
- { .frequency = CPUFREQ_TABLE_END }
+ { .frequency = SC_TABLE_END_MARK }
};
/* Intel Pentium M processor 1.30GHz (Banias) */
-static struct cpufreq_frequency_table banias_1300[] =
+static struct sc_opt banias_1300[] =
{
OP( 600, 956),
OP( 800, 1260),
OP(1000, 1292),
OP(1200, 1356),
OP(1300, 1388),
- { .frequency = CPUFREQ_TABLE_END }
+ { .frequency = SC_TABLE_END_MARK }
};
/* Intel Pentium M processor 1.40GHz (Banias) */
-static struct cpufreq_frequency_table banias_1400[] =
+static struct sc_opt banias_1400[] =
{
OP( 600, 956),
OP( 800, 1180),
OP(1000, 1308),
OP(1200, 1436),
OP(1400, 1484),
- { .frequency = CPUFREQ_TABLE_END }
+ { .frequency = SC_TABLE_END_MARK }
};
/* Intel Pentium M processor 1.50GHz (Banias) */
-static struct cpufreq_frequency_table banias_1500[] =
+static struct sc_opt banias_1500[] =
{
OP( 600, 956),
OP( 800, 1116),
@@ -170,11 +190,11 @@ static struct cpufreq_frequency_table ba
OP(1200, 1356),
OP(1400, 1452),
OP(1500, 1484),
- { .frequency = CPUFREQ_TABLE_END }
+ { .frequency = SC_TABLE_END_MARK }
};
/* Intel Pentium M processor 1.60GHz (Banias) */
-static struct cpufreq_frequency_table banias_1600[] =
+static struct sc_opt banias_1600[] =
{
OP( 600, 956),
OP( 800, 1036),
@@ -182,11 +202,11 @@ static struct cpufreq_frequency_table ba
OP(1200, 1276),
OP(1400, 1420),
OP(1600, 1484),
- { .frequency = CPUFREQ_TABLE_END }
+ { .frequency = SC_TABLE_END_MARK }
};
/* Intel Pentium M processor 1.70GHz (Banias) */
-static struct cpufreq_frequency_table banias_1700[] =
+static struct sc_opt banias_1700[] =
{
OP( 600, 956),
OP( 800, 1004),
@@ -194,7 +214,7 @@ static struct cpufreq_frequency_table ba
OP(1200, 1228),
OP(1400, 1308),
OP(1700, 1484),
- { .frequency = CPUFREQ_TABLE_END }
+ { .frequency = SC_TABLE_END_MARK }
};
#undef OP
@@ -232,10 +252,13 @@ static struct cpu_model models[] =
#undef _BANIAS
#undef BANIAS
-static int centrino_cpu_init_table(struct cpufreq_policy *policy)
+
+static int
+centrino_register_table_operating_points(int cpu_idx)
{
- struct cpuinfo_x86 *cpu = &cpu_data[policy->cpu];
+ struct cpuinfo_x86 *cpu = &cpu_data[cpu_idx];
struct cpu_model *model;
+ int i = 0, rc = 0;
for(model = models; model->cpu_id != NULL; model++)
if (centrino_verify_cpu_id(cpu, model->cpu_id) &&
@@ -261,16 +284,36 @@ #endif
return -ENOENT;
}
- centrino_model[policy->cpu] = model;
+ /* register operating points */
+ for (i = 0; model->op_points[i].frequency != SC_TABLE_END_MARK; i++) {
+ char opt_params[CPUFREQ_FREQ_OP_SIZE];
+ char opt_name[CPUFREQ_OP_NAME_SIZE];
+
+ CPUFREQ_CPU_N_FREQ_V(opt_params, cpu_idx);
+ CPUFREQ_CPU_N_GNRT_OP_NAME(opt_name, cpu_idx, i);
+
+ rc = powerop_register_point(opt_name, opt_params,
+ CENTRINO_EXTRACT_VLTG(model->op_points[i].index),
+ model->op_points[i].frequency);
+ if (rc != 0) {
+ dprintk("%s: registration of operating point %s"
+ " failed %d\n", __FUNCTION__, opt_name, rc);
+ return rc;
+ }
+
+ }
+
+ centrino_model[cpu_idx] = model;
dprintk("found \"%s\": max frequency: %dkHz\n",
model->model_name, model->max_freq);
return 0;
}
-
#else
-static inline int centrino_cpu_init_table(struct cpufreq_policy *policy) { return -ENODEV; }
+static inline int
+centrino_register_table_operating_points(int cpu_idx)
+{ return -ENODEV; }
#endif /* CONFIG_X86_SPEEDSTEP_CENTRINO_TABLE */
static int centrino_verify_cpu_id(const struct cpuinfo_x86 *c, const struct cpu_id *x)
@@ -282,8 +325,8 @@ static int centrino_verify_cpu_id(const
return 0;
}
-/* To be called only after centrino_model is initialized */
-static unsigned extract_clock(unsigned msr, unsigned int cpu, int failsafe)
+static unsigned
+decode_clock(unsigned value2decode, unsigned int cpu, int failsafe)
{
int i;
@@ -295,16 +338,19 @@ static unsigned extract_clock(unsigned m
if ((centrino_cpu[cpu] == &cpu_ids[CPU_BANIAS]) ||
(centrino_cpu[cpu] == &cpu_ids[CPU_DOTHAN_A1]) ||
(centrino_cpu[cpu] == &cpu_ids[CPU_DOTHAN_B0])) {
- msr = (msr >> 8) & 0xff;
- return msr * 100000;
+ value2decode = (value2decode >> 8) & 0xff;
+ return value2decode * 100;
}
if ((!centrino_model[cpu]) || (!centrino_model[cpu]->op_points))
return 0;
- msr &= 0xffff;
- for (i=0;centrino_model[cpu]->op_points[i].frequency != CPUFREQ_TABLE_END; i++) {
- if (msr == centrino_model[cpu]->op_points[i].index)
+ value2decode &= 0xffff;
+ for (i = 0;
+ centrino_model[cpu]->op_points[i].frequency != SC_TABLE_END_MARK;
+ i++)
+ {
+ if (value2decode == centrino_model[cpu]->op_points[i].index)
return centrino_model[cpu]->op_points[i].frequency;
}
if (failsafe)
@@ -313,513 +359,138 @@ static unsigned extract_clock(unsigned m
return 0;
}
-/* Return the current CPU frequency in kHz */
-static unsigned int get_cur_freq(unsigned int cpu)
+/*
+ * Return the current CPU frequency in kHz
+ * FIXME: not necessary to be implemented via powerop
+ */
+static unsigned int
+get_cur_freq(unsigned int cpu)
{
- unsigned l, h;
- unsigned clock_freq;
- cpumask_t saved_mask;
+ int clock_freq = 0;
+ char freq_n[CPUFREQ_FREQ_STR_SIZE];
- saved_mask = current->cpus_allowed;
- set_cpus_allowed(current, cpumask_of_cpu(cpu));
- if (smp_processor_id() != cpu)
+ CPUFREQ_CPU_N_FREQ(freq_n, cpu);
+ if (powerop_get_point(NULL, freq_n, &clock_freq))
return 0;
- rdmsr(MSR_IA32_PERF_STATUS, l, h);
- clock_freq = extract_clock(l, cpu, 0);
-
- if (unlikely(clock_freq == 0)) {
- /*
- * On some CPUs, we can see transient MSR values (which are
- * not present in _PSS), while CPU is doing some automatic
- * P-state transition (like TM2). Get the last freq set
- * in PERF_CTL.
- */
- rdmsr(MSR_IA32_PERF_CTL, l, h);
- clock_freq = extract_clock(l, cpu, 1);
- }
-
- set_cpus_allowed(current, saved_mask);
return clock_freq;
}
-
#ifdef CONFIG_X86_SPEEDSTEP_CENTRINO_ACPI
-
-static struct acpi_processor_performance *acpi_perf_data[NR_CPUS];
-
-/*
- * centrino_cpu_early_init_acpi - Do the preregistering with ACPI P-States
- * library
- *
- * Before doing the actual init, we need to do _PSD related setup whenever
- * supported by the BIOS. These are handled by this early_init routine.
- */
-static int centrino_cpu_early_init_acpi(void)
-{
- unsigned int i, j;
- struct acpi_processor_performance *data;
-
- for_each_possible_cpu(i) {
- data = kzalloc(sizeof(struct acpi_processor_performance),
- GFP_KERNEL);
- if (!data) {
- for_each_possible_cpu(j) {
- kfree(acpi_perf_data[j]);
- acpi_perf_data[j] = NULL;
- }
- return (-ENOMEM);
- }
- acpi_perf_data[i] = data;
- }
-
- acpi_processor_preregister_performance(acpi_perf_data);
- return 0;
-}
-
/*
- * centrino_cpu_init_acpi - register with ACPI P-States library
+ * centrino_register_acpi_operating_points - register operating points based
+ * on data coming from ACPI
*
- * Register with the ACPI P-States library (part of drivers/acpi/processor.c)
- * in order to determine correct frequency and voltage pairings by reading
- * the _PSS of the ACPI DSDT or SSDT tables.
+ * ASSUMPTIONS:
+ * acpi_perf_data[] is successfully initialized by PM Core
*/
-static int centrino_cpu_init_acpi(struct cpufreq_policy *policy)
+static int
+centrino_register_acpi_operating_points(int cpu)
{
- unsigned long cur_freq;
- int result = 0, i;
- unsigned int cpu = policy->cpu;
- struct acpi_processor_performance *p;
+ struct acpi_processor_performance *p;
+ unsigned long cur_freq;
+ int i, result = 0;
p = acpi_perf_data[cpu];
- /* register with ACPI core */
- if (acpi_processor_register_performance(p, cpu)) {
- dprintk(PFX "obtaining ACPI data failed\n");
- return -EIO;
- }
- policy->shared_type = p->shared_type;
- /*
- * Will let policy->cpus know about dependency only when software
- * coordination is required.
- */
- if (policy->shared_type == CPUFREQ_SHARED_TYPE_ALL ||
- policy->shared_type == CPUFREQ_SHARED_TYPE_ANY)
- policy->cpus = p->shared_cpu_map;
-
- /* verify the acpi_data */
- if (p->state_count <= 1) {
- dprintk("No P-States\n");
- result = -ENODEV;
- goto err_unreg;
- }
-
- if ((p->control_register.space_id != ACPI_ADR_SPACE_FIXED_HARDWARE) ||
- (p->status_register.space_id != ACPI_ADR_SPACE_FIXED_HARDWARE)) {
- dprintk("Invalid control/status registers (%x - %x)\n",
- p->control_register.space_id, p->status_register.space_id);
- result = -EIO;
- goto err_unreg;
- }
-
- for (i=0; istate_count; i++) {
- if (p->states[i].control != p->states[i].status) {
- dprintk("Different control (%llu) and status values (%llu)\n",
- p->states[i].control, p->states[i].status);
- result = -EINVAL;
- goto err_unreg;
- }
-
- if (!p->states[i].core_frequency) {
- dprintk("Zero core frequency for state %u\n", i);
- result = -EINVAL;
- goto err_unreg;
- }
-
- if (p->states[i].core_frequency > p->states[0].core_frequency) {
- dprintk("P%u has larger frequency (%llu) than P0 (%llu), skipping\n", i,
- p->states[i].core_frequency, p->states[0].core_frequency);
- p->states[i].core_frequency = 0;
- continue;
- }
- }
+ cur_freq = get_cur_freq(cpu);
- centrino_model[cpu] = kzalloc(sizeof(struct cpu_model), GFP_KERNEL);
- if (!centrino_model[cpu]) {
- result = -ENOMEM;
- goto err_unreg;
- }
+ for (i = 0; i < p->state_count; i++) {
+ char opt_params[CPUFREQ_FREQ_OP_SIZE];
+ char opt_name[CPUFREQ_OP_NAME_SIZE];
- centrino_model[cpu]->model_name=NULL;
- centrino_model[cpu]->max_freq = p->states[0].core_frequency * 1000;
- centrino_model[cpu]->op_points = kmalloc(sizeof(struct cpufreq_frequency_table) *
- (p->state_count + 1), GFP_KERNEL);
- if (!centrino_model[cpu]->op_points) {
- result = -ENOMEM;
- goto err_kfree;
- }
-
- for (i=0; istate_count; i++) {
- centrino_model[cpu]->op_points[i].index = p->states[i].control;
- centrino_model[cpu]->op_points[i].frequency = p->states[i].core_frequency * 1000;
- dprintk("adding state %i with frequency %u and control value %04x\n",
- i, centrino_model[cpu]->op_points[i].frequency, centrino_model[cpu]->op_points[i].index);
- }
- centrino_model[cpu]->op_points[p->state_count].frequency = CPUFREQ_TABLE_END;
+ CPUFREQ_CPU_N_FREQ_V(opt_params, cpu);
+ CPUFREQ_CPU_N_GNRT_OP_NAME(opt_name, cpu, i);
- cur_freq = get_cur_freq(cpu);
+ dprintk("%s: adding state %i with name %s, frequency %u and "
+ "voltage value %04x\n (control 0x%x)", __FUNCTION__,
+ opt_name, p->states[i].core_frequency * 1000,
+ CENTRINO_EXTRACT_VLTG(p->states[i].control)
+ p->states[i].control);
- for (i=0; istate_count; i++) {
if (!p->states[i].core_frequency) {
dprintk("skipping state %u\n", i);
- centrino_model[cpu]->op_points[i].frequency = CPUFREQ_ENTRY_INVALID;
continue;
}
-
- 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);
- result = -EINVAL;
- goto err_kfree_all;
- }
-
- if (cur_freq == centrino_model[cpu]->op_points[i].frequency)
- p->state = i;
- }
-
- /* notify BIOS that we exist */
- acpi_processor_notify_smm(THIS_MODULE);
-
- return 0;
-
- err_kfree_all:
- kfree(centrino_model[cpu]->op_points);
- err_kfree:
- kfree(centrino_model[cpu]);
- err_unreg:
- acpi_processor_unregister_performance(p, cpu);
- dprintk(PFX "invalid ACPI data\n");
- return (result);
-}
-#else
-static inline int centrino_cpu_init_acpi(struct cpufreq_policy *policy) { return -ENODEV; }
-static inline int centrino_cpu_early_init_acpi(void) { return 0; }
-#endif
-
-static int centrino_cpu_init(struct cpufreq_policy *policy)
-{
- struct cpuinfo_x86 *cpu = &cpu_data[policy->cpu];
- unsigned freq;
- unsigned l, h;
- int ret;
- int i;
-
- /* Only Intel makes Enhanced Speedstep-capable CPUs */
- if (cpu->x86_vendor != X86_VENDOR_INTEL || !cpu_has(cpu, X86_FEATURE_EST))
- return -ENODEV;
-
- if (cpu_has(cpu, X86_FEATURE_CONSTANT_TSC))
- centrino_driver.flags |= CPUFREQ_CONST_LOOPS;
-
- if (centrino_cpu_init_acpi(policy)) {
- if (policy->cpu != 0)
- return -ENODEV;
-
- for (i = 0; i < N_IDS; i++)
- if (centrino_verify_cpu_id(cpu, &cpu_ids[i]))
- break;
-
- if (i != N_IDS)
- centrino_cpu[policy->cpu] = &cpu_ids[i];
-
- if (!centrino_cpu[policy->cpu]) {
- dprintk("found unsupported CPU with "
- "Enhanced SpeedStep: send /proc/cpuinfo to "
- MAINTAINER "\n");
- return -ENODEV;
- }
- if (centrino_cpu_init_table(policy)) {
- return -ENODEV;
+ if (decode_clock(p->states[i].control, cpu, 0) !=
+ (p->states[i].core_frequency * 1000)) {
+ dprintk("Invalid encoded frequency (%u vs. %u)\n",
+ decode_clock(p->states[i].control, cpu, 0),
+ p->states[i].core_frequency * 1000);
+ continue;
}
- }
-
- /* Check to see if Enhanced SpeedStep is enabled, and try to
- enable it if not. */
- rdmsr(MSR_IA32_MISC_ENABLE, l, h);
-
- if (!(l & (1<<16))) {
- l |= (1<<16);
- dprintk("trying to enable Enhanced SpeedStep (%x)\n", l);
- wrmsr(MSR_IA32_MISC_ENABLE, l, h);
- /* check to see if it stuck */
- rdmsr(MSR_IA32_MISC_ENABLE, l, h);
- if (!(l & (1<<16))) {
- printk(KERN_INFO PFX "couldn't enable Enhanced SpeedStep\n");
- return -ENODEV;
+ /* register operating point w/ PowerOP Core */
+ result = powerop_register_point(opt_name, opt_params,
+ CENTRINO_EXTRACT_VLTG(p->states[i].control),
+ p->states[i].core_frequency * 1000);
+ if (result != 0) {
+ dprintk("%s: registration of operating point %s"
+ " failed %d\n", __FUNCTION__,
+ opt_name, result);
+ continue;
}
- }
-
- freq = get_cur_freq(policy->cpu);
-
- policy->governor = CPUFREQ_DEFAULT_GOVERNOR;
- policy->cpuinfo.transition_latency = 10000; /* 10uS transition latency */
- policy->cur = freq;
-
- dprintk("centrino_cpu_init: cur=%dkHz\n", policy->cur);
- ret = cpufreq_frequency_table_cpuinfo(policy, centrino_model[policy->cpu]->op_points);
- if (ret)
- return (ret);
-
- cpufreq_frequency_table_get_attr(centrino_model[policy->cpu]->op_points, policy->cpu);
-
- return 0;
-}
-
-static int centrino_cpu_exit(struct cpufreq_policy *policy)
-{
- unsigned int cpu = policy->cpu;
-
- if (!centrino_model[cpu])
- return -ENODEV;
-
- cpufreq_frequency_table_put_attr(cpu);
+ /* FIXME: why freq is still what we've got in cur_freq */
+ if (p->states[i].core_frequency * 1000 == cur_freq)
+ p->state = i;
-#ifdef CONFIG_X86_SPEEDSTEP_CENTRINO_ACPI
- if (!centrino_model[cpu]->model_name) {
- static struct acpi_processor_performance *p;
-
- if (acpi_perf_data[cpu]) {
- p = acpi_perf_data[cpu];
- dprintk("unregistering and freeing ACPI data\n");
- acpi_processor_unregister_performance(p, cpu);
- kfree(centrino_model[cpu]->op_points);
- kfree(centrino_model[cpu]);
- }
}
-#endif
-
- centrino_model[cpu] = NULL;
return 0;
}
-
-/**
- * centrino_verify - verifies a new CPUFreq policy
- * @policy: new policy
- *
- * Limit must be within this model's frequency range at least one
- * border included.
- */
-static int centrino_verify (struct cpufreq_policy *policy)
-{
- return cpufreq_frequency_table_verify(policy, centrino_model[policy->cpu]->op_points);
-}
-
-/**
- * centrino_setpolicy - set a new CPUFreq policy
- * @policy: new policy
- * @target_freq: the target frequency
- * @relation: how that frequency relates to achieved frequency (CPUFREQ_RELATION_L or CPUFREQ_RELATION_H)
- *
- * Sets a new CPUFreq policy.
- */
-static int centrino_target (struct cpufreq_policy *policy,
- unsigned int target_freq,
- unsigned int relation)
-{
- unsigned int newstate = 0;
- unsigned int msr, oldmsr = 0, h = 0, cpu = policy->cpu;
- struct cpufreq_freqs freqs;
- cpumask_t online_policy_cpus;
- cpumask_t saved_mask;
- cpumask_t set_mask;
- cpumask_t covered_cpus;
- int retval = 0;
- unsigned int j, k, first_cpu, tmp;
-
- if (unlikely(centrino_model[cpu] == NULL))
- return -ENODEV;
-
- if (unlikely(cpufreq_frequency_table_target(policy,
- centrino_model[cpu]->op_points,
- target_freq,
- relation,
- &newstate))) {
- return -EINVAL;
- }
-
-#ifdef CONFIG_HOTPLUG_CPU
- /* cpufreq holds the hotplug lock, so we are safe from here on */
- cpus_and(online_policy_cpus, cpu_online_map, policy->cpus);
#else
- online_policy_cpus = policy->cpus;
+static inline int
+centrino_register_acpi_operating_points(int cpu)
+{ return -ENODEV; }
#endif
- saved_mask = current->cpus_allowed;
- first_cpu = 1;
- cpus_clear(covered_cpus);
- for_each_cpu_mask(j, online_policy_cpus) {
- /*
- * Support for SMP systems.
- * Make sure we are running on CPU that wants to change freq
- */
- cpus_clear(set_mask);
- if (policy->shared_type == CPUFREQ_SHARED_TYPE_ANY)
- cpus_or(set_mask, set_mask, online_policy_cpus);
- else
- cpu_set(j, set_mask);
-
- set_cpus_allowed(current, set_mask);
- if (unlikely(!cpu_isset(smp_processor_id(), set_mask))) {
- dprintk("couldn't limit to CPUs in this domain\n");
- retval = -EAGAIN;
- if (first_cpu) {
- /* We haven't started the transition yet. */
- goto migrate_end;
- }
- break;
- }
-
- msr = centrino_model[cpu]->op_points[newstate].index;
-
- if (first_cpu) {
- rdmsr(MSR_IA32_PERF_CTL, oldmsr, h);
- if (msr == (oldmsr & 0xffff)) {
- dprintk("no change needed - msr was and needs "
- "to be %x\n", oldmsr);
- retval = 0;
- goto migrate_end;
- }
-
- freqs.old = extract_clock(oldmsr, cpu, 0);
- freqs.new = extract_clock(msr, cpu, 0);
+int
+centrino_register_operating_points(void)
+{
+ int cpu,i;
- dprintk("target=%dkHz old=%d new=%d msr=%04x\n",
- target_freq, freqs.old, freqs.new, msr);
+ for_each_possible_cpu(cpu) {
+ if (centrino_register_acpi_operating_points(cpu)) {
+ if (cpu != 0)
+ return -ENODEV;
- for_each_cpu_mask(k, online_policy_cpus) {
- freqs.cpu = k;
- cpufreq_notify_transition(&freqs,
- CPUFREQ_PRECHANGE);
+ for (i = 0; i < N_IDS; i++)
+ if (centrino_verify_cpu_id(&cpu_data[cpu],
+ &cpu_ids[i]))
+ break;
+
+ if (i != N_IDS)
+ centrino_cpu[cpu] = &cpu_ids[i];
+
+ if (!centrino_cpu[cpu]) {
+ dprintk("found unsupported CPU with "
+ "Enhanced SpeedStep: send /proc/cpuinfo to "
+ MAINTAINER "\n");
+ return -ENODEV;
}
- first_cpu = 0;
- /* all but 16 LSB are reserved, treat them with care */
- oldmsr &= ~0xffff;
- msr &= 0xffff;
- oldmsr |= msr;
- }
-
- wrmsr(MSR_IA32_PERF_CTL, oldmsr, h);
- if (policy->shared_type == CPUFREQ_SHARED_TYPE_ANY)
- break;
-
- cpu_set(j, covered_cpus);
- }
-
- for_each_cpu_mask(k, online_policy_cpus) {
- freqs.cpu = k;
- cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
- }
-
- if (unlikely(retval)) {
- /*
- * We have failed halfway through the frequency change.
- * We have sent callbacks to policy->cpus and
- * MSRs have already been written on coverd_cpus.
- * Best effort undo..
- */
-
- if (!cpus_empty(covered_cpus)) {
- for_each_cpu_mask(j, covered_cpus) {
- set_cpus_allowed(current, cpumask_of_cpu(j));
- wrmsr(MSR_IA32_PERF_CTL, oldmsr, h);
+ if (centrino_register_table_operating_points(cpu)) {
+ return -ENODEV;
}
}
-
- tmp = freqs.new;
- freqs.new = freqs.old;
- freqs.old = tmp;
- for_each_cpu_mask(j, online_policy_cpus) {
- freqs.cpu = j;
- cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
- cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
- }
}
-migrate_end:
- set_cpus_allowed(current, saved_mask);
return 0;
}
-static struct freq_attr* centrino_attr[] = {
- &cpufreq_freq_attr_scaling_available_freqs,
- NULL,
-};
-
-static struct cpufreq_driver centrino_driver = {
- .name = "centrino", /* should be speedstep-centrino,
- but there's a 16 char limit */
- .init = centrino_cpu_init,
- .exit = centrino_cpu_exit,
- .verify = centrino_verify,
- .target = centrino_target,
- .get = get_cur_freq,
- .attr = centrino_attr,
- .owner = THIS_MODULE,
-};
-
-
-/**
- * centrino_init - initializes the Enhanced SpeedStep CPUFreq driver
- *
- * Initializes the Enhanced SpeedStep support. Returns -ENODEV on
- * unsupported devices, -ENOENT if there's no voltage table for this
- * particular CPU model, -EINVAL on problems during initiatization,
- * and zero on success.
- *
- * This is quite picky. Not only does the CPU have to advertise the
- * "est" flag in the cpuid capability flags, we look for a specific
- * CPU model and stepping, and we need to have the exact model name in
- * our voltage tables. That is, be paranoid about not releasing
- * someone's valuable magic smoke.
- */
-static int __init centrino_init(void)
+/* see comments in the file header for deprecated */
+void
+centrino_unregister_operating_points(void)
{
- struct cpuinfo_x86 *cpu = cpu_data;
-
- if (!cpu_has(cpu, X86_FEATURE_EST))
- return -ENODEV;
-
- centrino_cpu_early_init_acpi();
-
- return cpufreq_register_driver(¢rino_driver);
+ /* FIXME: TODO */
}
-static void __exit centrino_exit(void)
-{
-#ifdef CONFIG_X86_SPEEDSTEP_CENTRINO_ACPI
- unsigned int j;
-#endif
-
- cpufreq_unregister_driver(¢rino_driver);
-
-#ifdef CONFIG_X86_SPEEDSTEP_CENTRINO_ACPI
- for_each_possible_cpu(j) {
- kfree(acpi_perf_data[j]);
- acpi_perf_data[j] = NULL;
- }
-#endif
-}
+module_init(centrino_register_operating_points);
+module_exit(centrino_unregister_operating_points);
-MODULE_AUTHOR ("Jeremy Fitzhardinge ");
-MODULE_DESCRIPTION ("Enhanced SpeedStep driver for Intel Pentium M processors.");
+MODULE_AUTHOR ("Eugeny S. Mints ");
+MODULE_DESCRIPTION ("Centrino operating points registration module");
MODULE_LICENSE ("GPL");
-late_initcall(centrino_init);
-module_exit(centrino_exit);
diff --git a/drivers/acpi/processor_perflib.c b/drivers/acpi/processor_perflib.c
index 7ba5e49..277b4d2 100644
--- a/drivers/acpi/processor_perflib.c
+++ b/drivers/acpi/processor_perflib.c
@@ -682,12 +682,8 @@ int acpi_processor_preregister_performan
/* Validate the Domain info */
count_target = pdomain->num_processors;
count = 1;
- if (pdomain->coord_type == DOMAIN_COORD_TYPE_SW_ALL)
- pr->performance->shared_type = CPUFREQ_SHARED_TYPE_ALL;
- else if (pdomain->coord_type == DOMAIN_COORD_TYPE_HW_ALL)
- pr->performance->shared_type = CPUFREQ_SHARED_TYPE_HW;
- else if (pdomain->coord_type == DOMAIN_COORD_TYPE_SW_ANY)
- pr->performance->shared_type = CPUFREQ_SHARED_TYPE_ANY;
+
+ pr->performance->shared_type = pdomain->coord_type;
for_each_possible_cpu(j) {
if (i == j)
@@ -751,7 +747,7 @@ err_ret:
if (retval) {
cpus_clear(pr->performance->shared_cpu_map);
cpu_set(i, pr->performance->shared_cpu_map);
- pr->performance->shared_type = CPUFREQ_SHARED_TYPE_ALL;
+ pr->performance->shared_type = DOMAIN_COORD_TYPE_SW_ALL;
}
pr->performance = NULL; /* Will be set for real in register */
}
diff --git a/include/asm-i386/pm_core.h b/include/asm-i386/pm_core.h
new file mode 100644
index 0000000..ea56c40
--- /dev/null
+++ b/include/asm-i386/pm_core.h
@@ -0,0 +1,72 @@
+/*
+ * Definition of operating point structure (power parameters set) for i386
+ *
+ * Author: Eugeny S. Mints
+ *
+ * 2006 (c) Nomad Global Solutions. 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.
+ *
+ */
+#ifndef __ASM_I386_PM_CORE_H__
+#define __ASM_I386_PM_CORE_H__
+
+#define _I386_PM_CORE_POINT_V 0
+#define _I386_PM_CORE_POINT_FREQ 1
+
+#define _I386_PM_CORE_PWRP_LAST (_I386_PM_CORE_POINT_FREQ + 1)
+#define _I386_PWRP_NUM _I386_PM_CORE_PWRP_LAST
+
+/* per CPU parameters: voltage and frquency */
+struct _cpu_pwr_params {
+ int pwpr[_I386_PWRP_NUM]; /* voltage in mV, freq in KHz */
+};
+
+/*
+ * an i386 platfrom operating point structure. an operating point
+ * contains volage/frequency pair for each possible CPU on a certain
+ * platform. Any additional power parameters may be defined here if
+ * supported by a platform.
+ *
+ * Instances of this structure define operating points
+ *
+ * Any power parameter value may be a positive value, zero, or reserved '-1'
+ * value.
+ *
+ * IMPORTANT:
+ *
+ * '-1' means any value from a range of valid values for a certain
+ * parameter may be used during switch to this particular point. I.e.
+ * assuming a platform power parameters are: p1, p2 (i.e.
+ * struct pm_core_point {
+ * int p1;
+ * int p2;
+ * }
+ * ) and that value ranges are {v1, v2} and {v3, v4} for p1 and p2 respectivly
+ * defining a point
+ * struct pm_core_point point1 = {
+ * .p1 = v1;
+ * .p2 = -1;
+ * }
+ * _must_ mean that both combinations {v1, v3} and {v1, v4} are valid (i.e.
+ * system will now hang or crash).
+ *
+ * It's strickly upto a platform power designer to utilize '-1' value
+ * properly given the above assumption.
+ */
+struct pm_core_point {
+ struct _cpu_pwr_params opt[NR_CPUS];
+};
+
+#define I386_PM_CORE_INIT_OPT(point) \
+ do { \
+ int i = 0, j = 0; \
+ for (j = 0; j < _I386_PWRP_NUM; j++) \
+ (point)->opt[i].pwpr[j] = -1; \
+ i++; \
+ } while (i < NR_CPUS)
+
+
+#endif /* __ASM_I386_PM_CORE_H__ */
+