diff options
Diffstat (limited to 'packages/linux/linux-ezx/dpm-core-2.6.16.patch')
-rw-r--r-- | packages/linux/linux-ezx/dpm-core-2.6.16.patch | 5197 |
1 files changed, 5197 insertions, 0 deletions
diff --git a/packages/linux/linux-ezx/dpm-core-2.6.16.patch b/packages/linux/linux-ezx/dpm-core-2.6.16.patch new file mode 100644 index 0000000000..d0d34d1225 --- /dev/null +++ b/packages/linux/linux-ezx/dpm-core-2.6.16.patch @@ -0,0 +1,5197 @@ +This is the Dynamic Power Management Core, as released by the upstream +project for 2.6.16. + +Index: linux-2.6.16/arch/arm/Kconfig +=================================================================== +--- linux-2.6.16.orig/arch/arm/Kconfig 2006-03-20 05:53:29.000000000 +0000 ++++ linux-2.6.16/arch/arm/Kconfig 2006-04-11 06:34:10.000000000 +0000 +@@ -832,3 +832,5 @@ + source "crypto/Kconfig" + + source "lib/Kconfig" ++ ++source "drivers/dpm/Kconfig" +Index: linux-2.6.16/arch/i386/Kconfig +=================================================================== +--- linux-2.6.16.orig/arch/i386/Kconfig 2006-03-20 05:53:29.000000000 +0000 ++++ linux-2.6.16/arch/i386/Kconfig 2006-04-11 06:34:10.000000000 +0000 +@@ -908,6 +908,7 @@ + endmenu + + source "arch/i386/kernel/cpu/cpufreq/Kconfig" ++source "arch/i386/kernel/cpu/dpm/Kconfig" + + endmenu + +Index: linux-2.6.16/arch/i386/kernel/cpu/Makefile +=================================================================== +--- linux-2.6.16.orig/arch/i386/kernel/cpu/Makefile 2006-03-20 05:53:29.000000000 +0000 ++++ linux-2.6.16/arch/i386/kernel/cpu/Makefile 2006-04-11 06:34:10.000000000 +0000 +@@ -17,3 +17,4 @@ + + obj-$(CONFIG_MTRR) += mtrr/ + obj-$(CONFIG_CPU_FREQ) += cpufreq/ ++obj-$(CONFIG_DPM) += dpm/ +Index: linux-2.6.16/arch/i386/kernel/cpu/dpm/Kconfig +=================================================================== +--- linux-2.6.16.orig/arch/i386/kernel/cpu/dpm/Kconfig 1970-01-01 00:00:00.000000000 +0000 ++++ linux-2.6.16/arch/i386/kernel/cpu/dpm/Kconfig 2006-04-11 06:34:10.000000000 +0000 +@@ -0,0 +1,10 @@ ++# ++# Dynamic Power Management ++# ++ ++source "drivers/dpm/Kconfig" ++ ++config DPM_CENTRINO ++ tristate "DPM for Intel Centrino Enhanced Speedstep" ++ depends on DPM ++ default n +Index: linux-2.6.16/arch/i386/kernel/cpu/dpm/Makefile +=================================================================== +--- linux-2.6.16.orig/arch/i386/kernel/cpu/dpm/Makefile 1970-01-01 00:00:00.000000000 +0000 ++++ linux-2.6.16/arch/i386/kernel/cpu/dpm/Makefile 2006-04-11 06:34:10.000000000 +0000 +@@ -0,0 +1,3 @@ ++obj-$(CONFIG_DPM) += x86_dpm.o ++obj-$(CONFIG_DPM_CENTRINO) += centrino_dpm.o ++ +Index: linux-2.6.16/arch/i386/kernel/cpu/dpm/centrino_dpm.c +=================================================================== +--- linux-2.6.16.orig/arch/i386/kernel/cpu/dpm/centrino_dpm.c 1970-01-01 00:00:00.000000000 +0000 ++++ linux-2.6.16/arch/i386/kernel/cpu/dpm/centrino_dpm.c 2006-04-11 06:34:10.000000000 +0000 +@@ -0,0 +1,133 @@ ++/* ++ * 2003 (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. ++ * ++ * Based on speedstep-centrino.c by Jeremy Fitzhardinge <jeremy@goop.org> ++ */ ++ ++#include <linux/config.h> ++#include <linux/dpm.h> ++#include <linux/errno.h> ++#include <linux/init.h> ++#include <linux/kernel.h> ++#include <linux/delay.h> ++ ++#include <asm/msr.h> ++#include <asm/processor.h> ++#include <asm/cpufeature.h> ++ ++/* Extract clock in kHz from PERF_CTL value */ ++static unsigned extract_clock(unsigned msr) ++{ ++ msr = (msr >> 8) & 0xff; ++ return msr * 100000; ++} ++ ++/* Return the current CPU frequency in kHz */ ++static unsigned get_cur_freq(void) ++{ ++ unsigned l, h; ++ ++ rdmsr(MSR_IA32_PERF_STATUS, l, h); ++ return extract_clock(l); ++} ++ ++static int ++dpm_centrino_init_opt(struct dpm_opt *opt) ++{ ++ int v = opt->pp[DPM_MD_V]; ++ int cpu = opt->pp[DPM_MD_CPU_FREQ]; ++ ++ struct dpm_md_opt *md_opt = &opt->md_opt; ++ ++ md_opt->v = v; ++ md_opt->cpu = cpu; ++ return 0; ++} ++ ++/* Fully determine the current machine-dependent operating point, and fill in a ++ structure presented by the caller. This should only be called when the ++ dpm_sem is held. This call can return an error if the system is currently at ++ an operating point that could not be constructed by dpm_md_init_opt(). */ ++ ++static int ++dpm_centrino_get_opt(struct dpm_opt *opt) ++{ ++ struct dpm_md_opt *md_opt = &opt->md_opt; ++ ++ md_opt->v = 100; /* TODO. */ ++ md_opt->cpu = get_cur_freq(); ++ return 0; ++} ++ ++static int ++dpm_centrino_set_opt(struct dpm_md_opt *md_opt) ++{ ++ unsigned int msr = 0, oldmsr, h, mask = 0; ++ ++ if (md_opt->cpu != -1) { ++ msr |= ((md_opt->cpu)/100) << 8; ++ mask |= 0xff00; ++ } ++ ++ if (md_opt->v != -1) { ++ msr |= ((md_opt->v - 700) / 16); ++ mask |= 0xff; ++ } ++ ++ rdmsr(MSR_IA32_PERF_CTL, oldmsr, h); ++ ++ if (msr == (oldmsr & mask)) ++ return 0; ++ ++ /* all but 16 LSB are "reserved", so treat them with ++ care */ ++ oldmsr &= ~mask; ++ msr &= mask; ++ oldmsr |= msr; ++ ++ wrmsr(MSR_IA32_PERF_CTL, oldmsr, h); ++ return 0; ++} ++ ++static int dpm_centrino_startup(void) ++{ ++ struct cpuinfo_x86 *cpu = cpu_data; ++ unsigned l, h; ++ ++ if (!cpu_has(cpu, X86_FEATURE_EST)) ++ return -ENODEV; ++ ++ /* 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); ++ 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 "DPM: Couldn't enable Enhanced SpeedStep\n"); ++ return -ENODEV; ++ } ++ } ++ ++ return 0; ++} ++ ++int __init dpm_centrino_init(void) ++{ ++ printk("Dynamic Power Management for Intel Centrino Enhanced SpeedStep.\n"); ++ ++ dpm_bd.startup = dpm_centrino_startup; ++ dpm_bd.init_opt = dpm_centrino_init_opt; ++ dpm_bd.get_opt = dpm_centrino_get_opt; ++ dpm_bd.set_opt = dpm_centrino_set_opt; ++ return 0; ++} ++ ++__initcall(dpm_centrino_init); +Index: linux-2.6.16/arch/i386/kernel/cpu/dpm/x86_dpm.c +=================================================================== +--- linux-2.6.16.orig/arch/i386/kernel/cpu/dpm/x86_dpm.c 1970-01-01 00:00:00.000000000 +0000 ++++ linux-2.6.16/arch/i386/kernel/cpu/dpm/x86_dpm.c 2006-04-11 06:34:10.000000000 +0000 +@@ -0,0 +1,133 @@ ++/* ++ * 2003 (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/config.h> ++#include <linux/dpm.h> ++#include <linux/errno.h> ++#include <linux/init.h> ++#include <linux/kernel.h> ++#include <linux/delay.h> ++#include <linux/interrupt.h> ++ ++#include <asm/page.h> ++#include <asm/uaccess.h> ++ ++struct dpm_bd dpm_bd; ++ ++static int ++dpm_x86_init_opt(struct dpm_opt *opt) ++{ ++ return dpm_bd.init_opt ? dpm_bd.init_opt(opt) : -1; ++} ++ ++/* Fully determine the current machine-dependent operating point, and fill in a ++ structure presented by the caller. This should only be called when the ++ dpm_sem is held. This call can return an error if the system is currently at ++ an operating point that could not be constructed by dpm_md_init_opt(). */ ++ ++static unsigned long loops_per_jiffy_ref = 0; ++ ++static int ++dpm_x86_get_opt(struct dpm_opt *opt) ++{ ++ return dpm_bd.get_opt ? dpm_bd.get_opt(opt) : -1; ++} ++ ++int ++dpm_x86_set_opt(struct dpm_opt *cur, struct dpm_opt *new) ++{ ++ struct cpuinfo_x86 *cpu = cpu_data; ++ ++ if (! new->md_opt.cpu) { ++#ifdef CONFIG_PM ++ pm_suspend(PM_SUSPEND_STANDBY); ++ ++ /* Here when we wake up. Recursive call to switch back to ++ * to task state. ++ */ ++ ++ dpm_set_os(DPM_TASK_STATE); ++#endif ++ return 0; ++ } ++ ++ if (dpm_bd.set_opt){ ++ dpm_bd.set_opt(&new->md_opt); ++ ++ }else{ ++ return -1; ++ } ++ ++ if (cur->md_opt.cpu && new->md_opt.cpu){ ++ loops_per_jiffy_ref = cpu->loops_per_jiffy; ++ cpu->loops_per_jiffy = ++ dpm_compute_lpj(loops_per_jiffy_ref , ++ cur->md_opt.cpu, ++ new->md_opt.cpu); ++ ++ loops_per_jiffy = cpu->loops_per_jiffy; ++ if (cpu_khz) ++ cpu_khz = dpm_compute_lpj(cpu_khz, ++ cur->md_opt.cpu, ++ new->md_opt.cpu); ++ } ++ return 0; ++} ++ ++/* ++ * idle loop ++ */ ++ ++static void (*orig_idle)(void); ++ ++void dpm_x86_idle(void) ++{ ++ extern void default_idle(void); ++ ++ if (orig_idle) ++ orig_idle(); ++ else ++ default_idle(); ++} ++ ++/**************************************************************************** ++ * Initialization/Exit ++ ****************************************************************************/ ++ ++void ++dpm_x86_startup(void) ++{ ++ orig_idle = pm_idle; ++ pm_idle = dpm_idle; ++ ++ if (dpm_bd.startup) ++ dpm_bd.startup(); ++} ++ ++void ++dpm_x86_cleanup(void) ++{ ++ pm_idle = orig_idle; ++ ++ if (dpm_bd.cleanup) ++ dpm_bd.cleanup(); ++} ++ ++int __init ++dpm_x86_init(void) ++{ ++ printk("Dynamic Power Management for x86.\n"); ++ ++ dpm_md.init_opt = dpm_x86_init_opt; ++ dpm_md.set_opt = dpm_x86_set_opt; ++ dpm_md.get_opt = dpm_x86_get_opt; ++ dpm_md.idle = dpm_x86_idle; ++ dpm_md.startup = dpm_x86_startup; ++ dpm_md.cleanup = dpm_x86_cleanup; ++ return 0; ++} ++__initcall(dpm_x86_init); +Index: linux-2.6.16/arch/i386/kernel/process.c +=================================================================== +--- linux-2.6.16.orig/arch/i386/kernel/process.c 2006-03-20 05:53:29.000000000 +0000 ++++ linux-2.6.16/arch/i386/kernel/process.c 2006-04-11 06:34:10.000000000 +0000 +@@ -58,6 +58,8 @@ + #include <asm/tlbflush.h> + #include <asm/cpu.h> + ++#include <linux/dpm.h> ++ + asmlinkage void ret_from_fork(void) __asm__("ret_from_fork"); + + static int hlt_counter; +@@ -697,6 +699,7 @@ + + disable_tsc(prev_p, next_p); + ++ dpm_set_os(next_p->dpm_state); + return prev_p; + } + +Index: linux-2.6.16/drivers/Makefile +=================================================================== +--- linux-2.6.16.orig/drivers/Makefile 2006-03-20 05:53:29.000000000 +0000 ++++ linux-2.6.16/drivers/Makefile 2006-04-11 06:34:10.000000000 +0000 +@@ -67,6 +67,7 @@ + obj-$(CONFIG_MCA) += mca/ + obj-$(CONFIG_EISA) += eisa/ + obj-$(CONFIG_CPU_FREQ) += cpufreq/ ++obj-$(CONFIG_DPM) += dpm/ + obj-$(CONFIG_MMC) += mmc/ + obj-$(CONFIG_INFINIBAND) += infiniband/ + obj-$(CONFIG_SGI_SN) += sn/ +Index: linux-2.6.16/drivers/base/core.c +=================================================================== +--- linux-2.6.16.orig/drivers/base/core.c 2006-03-20 05:53:29.000000000 +0000 ++++ linux-2.6.16/drivers/base/core.c 2006-04-11 06:34:10.000000000 +0000 +@@ -282,6 +282,8 @@ + if (parent) + klist_add_tail(&dev->knode_parent, &parent->klist_children); + ++ assert_constraints(dev->constraints); ++ + /* notify platform of device entry */ + if (platform_notify) + platform_notify(dev); +@@ -367,6 +369,8 @@ + klist_del(&dev->knode_parent); + device_remove_file(dev, &dev->uevent_attr); + ++ deassert_constraints(dev->constraints); ++ + /* Notify the platform of the removal, in case they + * need to do anything... + */ +Index: linux-2.6.16/drivers/base/power/Makefile +=================================================================== +--- linux-2.6.16.orig/drivers/base/power/Makefile 2006-03-20 05:53:29.000000000 +0000 ++++ linux-2.6.16/drivers/base/power/Makefile 2006-04-11 06:34:10.000000000 +0000 +@@ -1,4 +1,4 @@ +-obj-y := shutdown.o ++obj-y := shutdown.o power-dpm.o + obj-$(CONFIG_PM) += main.o suspend.o resume.o runtime.o sysfs.o + + ifeq ($(CONFIG_DEBUG_DRIVER),y) +Index: linux-2.6.16/drivers/base/power/power-dpm.c +=================================================================== +--- linux-2.6.16.orig/drivers/base/power/power-dpm.c 1970-01-01 00:00:00.000000000 +0000 ++++ linux-2.6.16/drivers/base/power/power-dpm.c 2006-04-11 06:35:28.000000000 +0000 +@@ -0,0 +1,473 @@ ++/* ++ * power-dpm.c -- Dynamic Power Management LDM power hooks ++ * ++ * (c) 2003 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/device.h> ++#include <linux/pm.h> ++#include <linux/dpm.h> ++#include <linux/sched.h> ++#include <linux/init.h> ++#include <linux/mm.h> ++#include <linux/slab.h> ++ ++#include "power.h" ++ ++/* ++ * power hotplug events ++ */ ++ ++#define BUFFER_SIZE 1024 /* should be enough memory for the env */ ++#define NUM_ENVP 32 /* number of env pointers */ ++static unsigned long sequence_num; ++static spinlock_t sequence_lock = SPIN_LOCK_UNLOCKED; ++ ++void power_event(char *eventstr) ++{ ++ char *argv [3]; ++ char **envp = NULL; ++ char *buffer = NULL; ++ char *scratch; ++ int i = 0; ++ int retval; ++ unsigned long seq; ++ ++ if (!uevent_helper[0]) ++ return; ++ ++ envp = kmalloc(NUM_ENVP * sizeof (char *), GFP_KERNEL); ++ if (!envp) ++ return; ++ memset (envp, 0x00, NUM_ENVP * sizeof (char *)); ++ ++ buffer = kmalloc(BUFFER_SIZE, GFP_KERNEL); ++ if (!buffer) ++ goto exit; ++ ++ argv [0] = uevent_helper; ++ argv [1] = "power"; ++ argv [2] = 0; ++ ++ /* minimal command environment */ ++ envp [i++] = "HOME=/"; ++ envp [i++] = "PATH=/sbin:/bin:/usr/sbin:/usr/bin"; ++ ++ scratch = buffer; ++ ++ envp [i++] = scratch; ++ scratch += sprintf(scratch, "ACTION=event") + 1; ++ ++ spin_lock(&sequence_lock); ++ seq = sequence_num++; ++ spin_unlock(&sequence_lock); ++ ++ envp [i++] = scratch; ++ scratch += sprintf(scratch, "SEQNUM=%ld", seq) + 1; ++ envp [i++] = scratch; ++ scratch += sprintf(scratch, "EVENT=%s", eventstr) + 1; ++ ++ pr_debug ("%s: %s %s %s %s %s %s %s\n", __FUNCTION__, argv[0], argv[1], ++ envp[0], envp[1], envp[2], envp[3], envp[4]); ++ retval = call_usermodehelper (argv[0], argv, envp, 0); ++ if (retval) ++ pr_debug ("%s - call_usermodehelper returned %d\n", ++ __FUNCTION__, retval); ++ ++exit: ++ kfree(buffer); ++ kfree(envp); ++ return; ++} ++ ++void device_power_event(struct device * dev, char *eventstr) ++{ ++ char *argv [3]; ++ char **envp = NULL; ++ char *buffer = NULL; ++ char *scratch; ++ int i = 0; ++ int retval; ++ unsigned long seq; ++ ++ if (!uevent_helper[0]) ++ return; ++ ++ envp = kmalloc(NUM_ENVP * sizeof (char *), GFP_KERNEL); ++ if (!envp) ++ return; ++ memset (envp, 0x00, NUM_ENVP * sizeof (char *)); ++ ++ buffer = kmalloc(BUFFER_SIZE, GFP_KERNEL); ++ if (!buffer) ++ goto exit; ++ ++ argv [0] = uevent_helper; ++ argv [1] = "power"; ++ argv [2] = 0; ++ ++ /* minimal command environment */ ++ envp [i++] = "HOME=/"; ++ envp [i++] = "PATH=/sbin:/bin:/usr/sbin:/usr/bin"; ++ ++ scratch = buffer; ++ ++ envp [i++] = scratch; ++ scratch += sprintf(scratch, "ACTION=device-event") + 1; ++ ++ spin_lock(&sequence_lock); ++ seq = sequence_num++; ++ spin_unlock(&sequence_lock); ++ ++ envp [i++] = scratch; ++ scratch += sprintf(scratch, "SEQNUM=%ld", seq) + 1; ++ envp [i++] = scratch; ++ scratch += sprintf(scratch, "DEVICE=%s", dev->bus_id) + 1; ++ envp [i++] = scratch; ++ scratch += sprintf(scratch, "EVENT=%s", eventstr) + 1; ++ envp [i++] = scratch; ++ scratch += sprintf(scratch, "SUBSYSTEM=power") + 1; ++ ++ pr_debug ("%s: %s %s %s %s %s %s %s %s %2\n", __FUNCTION__, argv[0], argv[1], ++ envp[0], envp[1], envp[2], envp[3], envp[4], envp[5], ++ envp[6]); ++ retval = call_usermodehelper (argv[0], argv, envp, 0); ++ if (retval) ++ pr_debug ("%s - call_usermodehelper returned %d\n", ++ __FUNCTION__, retval); ++ ++exit: ++ kfree(buffer); ++ kfree(envp); ++ return; ++} ++ ++/* ++ * Device constraints ++ */ ++ ++#ifdef CONFIG_DPM ++LIST_HEAD(dpm_constraints); ++DECLARE_MUTEX(dpm_constraints_sem); ++ ++void assert_constraints(struct constraints *constraints) ++{ ++ if (! constraints || constraints->asserted) ++ return; ++ ++ down(&dpm_constraints_sem); ++ constraints->asserted = 1; ++ list_add_tail(&constraints->entry, &dpm_constraints); ++ up(&dpm_constraints_sem); ++ ++ /* DPM-PM-TODO: Check against DPM state. */ ++ ++} ++ ++ ++void deassert_constraints(struct constraints *constraints) ++{ ++ if (! constraints || ! constraints->asserted) ++ return; ++ ++ down(&dpm_constraints_sem); ++ constraints->asserted = 0; ++ list_del_init(&constraints->entry); ++ up(&dpm_constraints_sem); ++} ++ ++ ++EXPORT_SYMBOL(assert_constraints); ++EXPORT_SYMBOL(deassert_constraints); ++ ++static ssize_t ++constraints_show(struct device * dev, struct device_attribute *attr, ++ char * buf) ++{ ++ int i, cnt = 0; ++ ++ if (dev->constraints) { ++ for (i = 0; i < dev->constraints->count; i++) { ++ cnt += sprintf(buf + cnt,"%s: min=%d max=%d\n", ++ dpm_param_names[dev->constraints->param[i].id], ++ dev->constraints->param[i].min, ++ dev->constraints->param[i].max); ++ } ++ ++ cnt += sprintf(buf + cnt,"asserted=%s violations=%d\n", ++ dev->constraints->asserted ? ++ "yes" : "no", dev->constraints->violations); ++ } else { ++ cnt += sprintf(buf + cnt,"none\n"); ++ } ++ ++ return cnt; ++} ++ ++static ssize_t ++constraints_store(struct device * dev, struct device_attribute *attr, ++ const char * buf, size_t count) ++{ ++ int num_args, paramid, min, max; ++ int cidx; ++ const char *cp, *paramname; ++ int paramnamelen; ++ int provisional = 0; ++ int ret = 0; ++ ++ if (!dev->constraints) { ++ if (! (dev->constraints = kmalloc(sizeof(struct constraints), ++ GFP_KERNEL))) ++ return -EINVAL; ++ ++ memset(dev->constraints, 0, ++ sizeof(struct constraints)); ++ provisional = 1; ++ } ++ ++ cp = buf; ++ while((cp - buf < count) && *cp && (*cp == ' ')) ++ cp++; ++ ++ paramname = cp; ++ ++ while((cp - buf < count) && *cp && (*cp != ' ')) ++ cp++; ++ ++ paramnamelen = cp - paramname; ++ num_args = sscanf(cp, "%d %d", &min, &max); ++ ++ if (num_args != 2) { ++ printk("DPM: Need 2 integer parameters for constraint min/max.\n"); ++ ret = -EINVAL; ++ goto out; ++ } ++ ++ for (paramid = 0; paramid < DPM_PP_NBR; paramid++) { ++ if (strncmp(paramname, dpm_param_names[paramid], paramnamelen) == 0) ++ break; ++ } ++ ++ if (paramid >= DPM_PP_NBR) { ++ printk("DPM: Unknown power parameter name in device constraints\n"); ++ ret = -EINVAL; ++ goto out; ++ } ++ ++ for (cidx = 0; cidx < dev->constraints->count; cidx++) ++ /* ++ * If the new range overlaps an existing range, ++ * modify the existing one. ++ */ ++ ++ if ((dev->constraints->param[cidx].id == paramid) && ++ ((max == -1) || ++ (max >= dev->constraints->param[cidx].min)) && ++ ((min == -1) || ++ (min <= dev->constraints->param[cidx].max))) ++ break; ++ ++ if (cidx >= DPM_CONSTRAINT_PARAMS_MAX) { ++ ret = -EINVAL; ++ goto out; ++ } ++ ++ /* Error if max is less than min */ ++ if (max < min) { ++ printk("DPM: Max value of the constraint should not be less than min\n"); ++ ret = -EINVAL; ++ goto out; ++ } ++ ++ dev->constraints->param[cidx].id = paramid; ++ dev->constraints->param[cidx].max = max; ++ dev->constraints->param[cidx].min = min; ++ ++ if (cidx == dev->constraints->count) ++ dev->constraints->count++; ++ ++ /* New constraints should start off with same state as power ++ state */ ++ if (provisional && (dev->power.power_state.event == PM_EVENT_ON)) ++ assert_constraints(dev->constraints); ++ ++out: ++ ++ if (provisional && (ret < 0)) { ++ kfree(dev->constraints); ++ dev->constraints = NULL; ++ } ++ ++ return ret < 0 ? ret : count; ++} ++ ++DEVICE_ATTR(constraints,S_IWUSR | S_IRUGO, ++ constraints_show,constraints_store); ++ ++#else /* CONFIG_DPM */ ++void assert_constraints(struct constraints *constraints) ++{ ++} ++ ++void deassert_constraints(struct constraints *constraints) ++{ ++} ++#endif /* CONFIG_DPM */ ++ ++#ifdef CONFIG_DPM ++ ++/* ++ * Driver scale callbacks ++ */ ++ ++static struct notifier_block *dpm_scale_notifier_list[SCALE_MAX]; ++static DECLARE_MUTEX(dpm_scale_sem); ++ ++/* This function may be called by the platform frequency scaler before ++ or after a frequency change, in order to let drivers adjust any ++ clocks or calculations for the new frequency. */ ++ ++void dpm_driver_scale(int level, struct dpm_opt *newop) ++{ ++ if (down_trylock(&dpm_scale_sem)) ++ return; ++ ++ notifier_call_chain(&dpm_scale_notifier_list[level], level, newop); ++ up(&dpm_scale_sem); ++} ++ ++void dpm_register_scale(struct notifier_block *nb, int level) ++{ ++ down(&dpm_scale_sem); ++ notifier_chain_register(&dpm_scale_notifier_list[level], nb); ++ up(&dpm_scale_sem); ++} ++ ++void dpm_unregister_scale(struct notifier_block *nb, int level) ++{ ++ down(&dpm_scale_sem); ++ notifier_chain_unregister(&dpm_scale_notifier_list[level], nb); ++ up(&dpm_scale_sem); ++} ++ ++int dpm_constraint_rejects = 0; ++ ++ ++int ++dpm_default_check_constraint(struct constraint_param *param, ++ struct dpm_opt *opt) ++{ ++ return (opt->pp[param->id] == -1) || ++ ((param->min == -1 || opt->pp[param->id] >= param->min) && ++ (param->max == -1 || opt->pp[param->id] <= param->max)); ++} ++ ++static int ++dpm_check_a_constraint(struct constraints *constraints, struct dpm_opt *opt) ++{ ++ int i; ++ int failid = -1; ++ int ppconstraint[DPM_PP_NBR]; ++ ++ ++ if (! constraints || !constraints->asserted) ++ return 1; ++ ++ /* ++ * ppconstraint[ppid] == 0 means power param has not been checked ++ * for a constraint ++ * == -1 means power param has matched a constraint ++ * > 0 means constraint #n-1 mismatched ++ * ++ * failid == pp id of (a) failed constraint ++ */ ++ ++ memset(ppconstraint, 0, sizeof(ppconstraint)); ++ ++ for (i = 0; i < constraints->count; i++) { ++ struct constraint_param *param = &constraints->param[i]; ++ ++ if (! dpm_md_check_constraint(param, opt)) { ++ if (ppconstraint[param->id] == 0) { ++ failid = param->id; ++ ppconstraint[failid] = i+1; ++ } ++ } else ++ ppconstraint[param->id] = -1; ++ } ++ ++ if ((failid >= 0) && (ppconstraint[failid] > 0)) { ++#ifdef CONFIG_DPM_TRACE ++ struct constraint_param *param = ++ &constraints->param[ppconstraint[failid]-1]; ++ ++ dpm_trace(DPM_TRACE_CONSTRAINT_ASSERTED, ++ param->id, param->min, param->max, ++ opt); ++#endif ++ return 0; ++ } ++ ++ return 1; ++} ++ ++int dpm_check_constraints(struct dpm_opt *opt) ++{ ++ struct list_head * entry; ++ int valid = 1; ++ ++ list_for_each(entry,&dpm_constraints) { ++ struct constraints *constraints = ++ list_entry(entry, struct constraints, entry); ++ if (!dpm_check_a_constraint(constraints, opt)) { ++ constraints->violations++; ++ dpm_constraint_rejects++; ++ valid = 0; ++ } ++ } ++ ++ return valid; ++} ++ ++int dpm_show_opconstraints(struct dpm_opt *opt, char * buf) ++{ ++#ifdef CONFIG_PM ++ struct list_head * entry; ++ int len = 0; ++ ++ list_for_each_prev(entry,&dpm_active) { ++ struct device * dev = to_device(entry); ++ ++ if (!dpm_check_a_constraint(dev->constraints, opt)) { ++ len += sprintf(buf + len, "%s/%s\n", dev->bus->name, ++ dev->bus_id); ++ } ++ } ++ ++ return len; ++#else /* CONFIG_PM */ ++ return 0; ++#endif /* CONFIG_PM */ ++} ++ ++void dpm_force_off_constrainers(struct dpm_opt *opt) ++{ ++#ifdef CONFIG_PM ++ struct list_head * entry; ++ ++ list_for_each_prev(entry,&dpm_active) { ++ struct device * dev = to_device(entry); ++ ++ if (!dpm_check_a_constraint(dev->constraints, opt)) { ++ suspend_device(dev, PMSG_SUSPEND); ++ } ++ } ++#endif ++} ++ ++EXPORT_SYMBOL(dpm_force_off_constrainers); ++#endif /* CONFIG_DPM */ ++ +Index: linux-2.6.16/drivers/base/power/resume.c +=================================================================== +--- linux-2.6.16.orig/drivers/base/power/resume.c 2006-03-20 05:53:29.000000000 +0000 ++++ linux-2.6.16/drivers/base/power/resume.c 2006-04-11 06:34:10.000000000 +0000 +@@ -34,6 +34,8 @@ + if (dev->bus && dev->bus->resume) { + dev_dbg(dev,"resuming\n"); + error = dev->bus->resume(dev); ++ if (!error) ++ assert_constraints(dev->constraints); + } + up(&dev->sem); + return error; +Index: linux-2.6.16/drivers/base/power/suspend.c +=================================================================== +--- linux-2.6.16.orig/drivers/base/power/suspend.c 2006-03-20 05:53:29.000000000 +0000 ++++ linux-2.6.16/drivers/base/power/suspend.c 2006-04-11 06:34:10.000000000 +0000 +@@ -57,6 +57,9 @@ + if (dev->bus && dev->bus->suspend && !dev->power.power_state.event) { + dev_dbg(dev, "suspending\n"); + error = dev->bus->suspend(dev, state); ++ ++ if (! error) ++ deassert_constraints(dev->constraints); + } + up(&dev->sem); + return error; +Index: linux-2.6.16/drivers/base/power/sysfs.c +=================================================================== +--- linux-2.6.16.orig/drivers/base/power/sysfs.c 2006-03-20 05:53:29.000000000 +0000 ++++ linux-2.6.16/drivers/base/power/sysfs.c 2006-04-11 06:34:10.000000000 +0000 +@@ -56,7 +56,6 @@ + + static DEVICE_ATTR(state, 0644, state_show, state_store); + +- + /* + * wakeup - Report/change current wakeup option for device + * +@@ -128,10 +127,14 @@ + + static DEVICE_ATTR(wakeup, 0644, wake_show, wake_store); + ++extern struct device_attribute dev_attr_constraints; + + static struct attribute * power_attrs[] = { + &dev_attr_state.attr, + &dev_attr_wakeup.attr, ++#ifdef CONFIG_DPM ++ &dev_attr_constraints.attr, ++#endif + NULL, + }; + static struct attribute_group pm_attr_group = { +Index: linux-2.6.16/drivers/dpm/Kconfig +=================================================================== +--- linux-2.6.16.orig/drivers/dpm/Kconfig 1970-01-01 00:00:00.000000000 +0000 ++++ linux-2.6.16/drivers/dpm/Kconfig 2006-04-11 06:34:10.000000000 +0000 +@@ -0,0 +1,43 @@ ++# ++# Dynamic Power Management ++# ++ ++menu "Dynamic Power Management" ++ ++config DPM ++ bool "Dynamic Power Management" ++ help ++ Enable Dynamic Power Management, if implemented for your platform. ++ DPM conserves power by adjusting power parameters according to ++ system state (such as idle, running a high-power-usage task, etc.) ++ and enables associated power management features such as device ++ constraints on power parameters. DPM relies on power policy and ++ machine-dependent power operating points and such to be configured ++ from userspace after boot. ++ ++ If in doubt, say N. ++ ++config DPM_STATS ++ bool " Enable DPM Statistics Gathering" ++ depends on DPM ++ help ++ This enables gathering and reporting statistics for DPM. ++ This can be useful during development of DPM platform code or ++ in other situations where information on the operation of DPM is ++ needed. ++ ++ If in doubt, say N. ++ ++ ++config DPM_PROCFS ++ bool " Enable old DPM /proc interface (deprecated)" ++ depends on DPM && PROC_FS ++ help ++ This enables the /proc/driver/dpm interface for controlling ++ DPM. Please note that it is recommended to use the sysfs ++ interface instead (which is built automatically). ++ ++ If in doubt, say N. ++ ++endmenu ++ +Index: linux-2.6.16/drivers/dpm/Makefile +=================================================================== +--- linux-2.6.16.orig/drivers/dpm/Makefile 1970-01-01 00:00:00.000000000 +0000 ++++ linux-2.6.16/drivers/dpm/Makefile 2006-04-11 06:34:10.000000000 +0000 +@@ -0,0 +1,7 @@ ++# ++# Makefile for the kernel DPM driver. ++# ++ ++obj-$(CONFIG_DPM) += dpm.o dpm-idle.o dpm-ui.o ++obj-$(CONFIG_DPM_PROCFS) += proc.o ++ +Index: linux-2.6.16/drivers/dpm/dpm-idle.c +=================================================================== +--- linux-2.6.16.orig/drivers/dpm/dpm-idle.c 1970-01-01 00:00:00.000000000 +0000 ++++ linux-2.6.16/drivers/dpm/dpm-idle.c 2006-04-11 06:34:10.000000000 +0000 +@@ -0,0 +1,167 @@ ++/* ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ * ++ * Copyright (C) 2002, MontaVista Software <source@mvista.com>. ++ * ++ * Based on ibm405lp_dpm.c by Bishop Brock, Copyright (C) 2002, ++ * International Business Machines Corporation. ++ */ ++ ++#include <linux/config.h> ++#include <linux/dpm.h> ++#include <linux/errno.h> ++#include <linux/init.h> ++#include <linux/kernel.h> ++#include <linux/kmod.h> ++#include <linux/module.h> ++#include <linux/proc_fs.h> ++#include <linux/stat.h> ++#include <linux/string.h> ++ ++#include <asm/delay.h> ++#include <asm/hardirq.h> ++#include <asm/page.h> ++#include <asm/processor.h> ++#include <asm/system.h> ++#include <asm/uaccess.h> ++ ++/**************************************************************************** ++ * DPM Idle Handler ++ ****************************************************************************/ ++ ++/* ++ The idle handler is one of the most important parts of DPM, as very ++ significant amounts of energy are saved by moving to a low-power idle state ++ whenever possible. The basic coding of the core of this routine is simply: ++ ++ dpm_set_os(DPM_IDLE_STATE); ++ machine-dependent-idle-routine(); ++ dpm_set_os(DPM_IDLE_TASK_STATE); ++ ++ The added complexity found here is introduced to avoid unnecessary work, and ++ especially to reduce the latencies associated with going in and out of idle. ++ Idle power can be greatly reduced by moving to a very low-frequency ++ operating point, but we also need to be aware of the impact on interrupt ++ latencies. The DPM implementation of idle attempts to balance these ++ competing needs. ++ ++ We support 2 "idle" states: DPM_IDLE_TASK_STATE and DPM_IDLE_STATE. The ++ idle thread is marked as a "no-state" task, so that operating point changes ++ are not automatically made when the idle thread is scheduled. The ++ "idle-task" state is used for the majority of the idle thread. Interrupts ++ that occur during idle are handled in this state as well. The "idle" state ++ is only entered from the idle-task state, and only for the express purpose ++ of allowing an ultra-low-power operating point. ++ ++ The introduction of the idle-task state supports a stepped voltage and ++ frequency scaling at idle. On the IBM 405LP we would not want to go from, ++ e.g., 266/133 @ 1.8 V directly to 8/8 @ 1.0 V and back. Why not? Because ++ we would get "stuck" at 8MHz even though we need to wake up and resume ++ useful work, e.g., we would have to set the 266/133 operating point while ++ running at 8/8. So instead when going idle first step down to idle-task, ++ e.g., 100/50 @ 1.0 V, and then step down to e.g. 8/8 to halt. The interrupt ++ that takes us out of idle takes us back to idle-task (100/50) for interrupt ++ processing and the potential return to 266/133. ++ ++ The best policies for this implementation will be able to transition between ++ idle-task and idle without voltage scaling or driver notification. In these ++ cases the transitions are handled with minimal latency by simple frequency ++ scaling. */ ++ ++static inline void ++quick_idle(void) ++{ ++ dpm_quick_enter_state(DPM_IDLE_STATE); ++ dpm_md_idle(); ++ dpm_quick_enter_state(DPM_IDLE_TASK_STATE); ++} ++ ++static void ++full_idle(struct dpm_opt *idle_task_opt, struct dpm_opt *idle_opt) ++{ ++ dpm_quick_enter_state(DPM_IDLE_STATE); ++#ifdef CONFIG_DPM_STATS ++ dpm_update_stats(&idle_opt->stats, &idle_task_opt->stats); ++#endif ++ dpm_set_opt(idle_opt, DPM_SYNC); ++ dpm_md_idle(); ++ dpm_set_opt(idle_task_opt, DPM_SYNC); ++ dpm_quick_enter_state(DPM_IDLE_TASK_STATE); ++#ifdef CONFIG_DPM_STATS ++ dpm_update_stats(&idle_task_opt->stats, &idle_opt->stats); ++#endif ++} ++ ++ ++/* If DPM is currently disabled here we simply do the standard ++ idle wait. ++ ++ If we're not actually in DPM_IDLE_TASK_STATE, we need to go back and get ++ into this state. This could happen in rare instances - an interrupt between ++ dpm_set_os() and the critical section. ++ ++ If we are not yet at the idle-task operating point, or if there is no ++ difference between idle-task and idle, we can enter/exit the idle state ++ quickly since it's only for statistical purposes. This is also true if for ++ some reason we can't get the DPM lock, since obviously an asynchronous event ++ is going to have to occur to clear the lock, and this event is going to take ++ us out of idle. ++ ++ Otherwise the full idle shutdown is done. */ ++ ++ ++void ++dpm_idle(void) ++{ ++ unsigned long flags; ++ struct dpm_opt *idle_task_opt, *idle_opt; ++ ++ current->dpm_state = DPM_NO_STATE; ++ dpm_set_os(DPM_IDLE_TASK_STATE); ++ local_irq_save(flags); ++ ++ if (! need_resched()) { ++ if (!dpm_enabled) { ++ dpm_md_idle(); ++ ++ } else if (dpm_active_state != DPM_IDLE_TASK_STATE) { ++ ++ ++ } else { ++ idle_task_opt = dpm_choose_opt(dpm_active_policy, ++ DPM_IDLE_TASK_STATE); ++ idle_opt = dpm_choose_opt(dpm_active_policy, ++ DPM_IDLE_STATE); ++ ++ if (dpm_trylock()) { ++ dpm_md_idle(); ++ } else { ++ ++ if ((dpm_active_opt != idle_task_opt) || ++ (idle_task_opt == idle_opt)) { ++ ++ quick_idle(); ++ dpm_unlock(); ++ } else { ++ dpm_unlock(); ++ full_idle(idle_task_opt, idle_opt); ++ } ++ } ++ } ++ } ++ local_irq_restore(flags); ++} ++ +Index: linux-2.6.16/drivers/dpm/dpm-ui.c +=================================================================== +--- linux-2.6.16.orig/drivers/dpm/dpm-ui.c 1970-01-01 00:00:00.000000000 +0000 ++++ linux-2.6.16/drivers/dpm/dpm-ui.c 2006-04-11 06:35:40.000000000 +0000 +@@ -0,0 +1,1249 @@ ++/* ++ * drivers/dpm/dpm-ui.c - userspace interface to Dynamic Power Management ++ * ++ * (c) 2003 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/dpm.h> ++#include <linux/device.h> ++#include <linux/init.h> ++#include <linux/errno.h> ++ ++/* Common sysfs/proc support */ ++ ++char *dpm_state_names[DPM_STATES] = DPM_STATE_NAMES; ++char *dpm_param_names[DPM_PP_NBR] = DPM_PARAM_NAMES; ++ ++#define MAXTOKENS 80 ++ ++static int ++tokenizer(char **tbuf, const char *userbuf, ssize_t n, char **tokptrs, ++ int maxtoks) ++{ ++ char *cp, *tok; ++ char *whitespace = " \t\r\n"; ++ int ntoks = 0; ++ ++ if (!(cp = kmalloc(n + 1, GFP_KERNEL))) ++ return -ENOMEM; ++ ++ *tbuf = cp; ++ memcpy(cp, userbuf, n); ++ cp[n] = '\0'; ++ ++ do { ++ cp = cp + strspn(cp, whitespace); ++ tok = strsep(&cp, whitespace); ++ if ((*tok == '\0') || (ntoks == maxtoks)) ++ break; ++ tokptrs[ntoks++] = tok; ++ } while(cp); ++ ++ return ntoks; ++} ++ ++ ++/* SysFS Interface */ ++ ++#define dpm_attr(_name,_prefix) \ ++static struct subsys_attribute _prefix##_attr = { \ ++ .attr = { \ ++ .name = __stringify(_name), \ ++ .mode = 0644, \ ++ }, \ ++ .show = _prefix##_show, \ ++ .store = _prefix##_store, \ ++} ++ ++ ++static void dpm_kobj_release(struct kobject *kobj) ++{ ++ /* ++ * No sysfs/kobject state to release, DPM layer will handle the ++ * the containing object. ++ */ ++ ++ return; ++} ++ ++/* ++ * Top-level control ++ */ ++ ++static ssize_t dpm_control_show(struct subsystem * subsys, char * buf) ++{ ++ unsigned long flags; ++ ssize_t len = 0; ++ ++ if (dpm_lock_interruptible()) ++ return -ERESTARTSYS; ++ ++ if (!dpm_enabled) { ++ len += sprintf(buf, "disabled\n"); ++ } else { ++ spin_lock_irqsave(&dpm_policy_lock, flags); ++ len += sprintf(buf,"enabled %s %d %s %s %s\n", ++ dpm_active_policy->name, ++ dpm_active_state, ++ dpm_state_names[dpm_active_state], ++ dpm_classopt_name(dpm_active_policy, ++ dpm_active_state), ++ dpm_active_opt ? dpm_active_opt->name : "[none]"); ++ spin_unlock_irqrestore(&dpm_policy_lock, flags); ++ } ++ ++ dpm_unlock(); ++ return len; ++} ++ ++static ssize_t dpm_control_store(struct subsystem * subsys, const char * buf, ++ size_t n) ++{ ++ int error = 0; ++ ++ if (strncmp(buf, "init", 4) == 0) { ++ error = dynamicpower_init(); ++ } else if (strncmp(buf, "enable", 6) == 0) { ++ error = dynamicpower_enable(); ++ } else if (strncmp(buf, "disable", 7) == 0) { ++ error = dynamicpower_disable(); ++ } else if (strncmp(buf, "terminate", 9) == 0) { ++ error = dynamicpower_terminate(); ++ } else ++ error = -EINVAL; ++ ++ return error ? error : n; ++} ++ ++dpm_attr(control,dpm_control); ++ ++static struct attribute * g[] = { ++ &dpm_control_attr.attr, ++ NULL, ++}; ++ ++static struct attribute_group dpm_attr_group = { ++ .attrs = g, ++}; ++ ++decl_subsys(dpm, NULL, NULL); ++ ++/* ++ * policy ++ */ ++ ++struct dpm_policy_attribute { ++ struct attribute attr; ++ ssize_t (*show)(struct kobject * kobj, char * buf); ++ ssize_t (*store)(struct kobject * kobj, const char * buf, size_t count); ++}; ++ ++#define to_policy(obj) container_of(obj,struct dpm_policy,kobj) ++#define to_policy_attr(_attr) container_of(_attr,struct dpm_policy_attribute,attr) ++ ++static struct kobject dpm_policy_kobj = { ++ .kset = &dpm_subsys.kset, ++}; ++ ++static ssize_t policy_control_show(struct subsystem * subsys, char * buf) ++{ ++ ssize_t len = 0; ++ struct list_head * p; ++ ++ if (dpm_lock_interruptible()) ++ return -ERESTARTSYS; ++ ++ len += sprintf(buf + len, "policies: "); ++ ++ list_for_each(p, &dpm_policies) { ++ len += sprintf(buf + len, "%s ", ++ ((struct dpm_policy *) ++ list_entry(p, struct dpm_policy, list))->name); ++ } ++ ++ len += sprintf(buf + len, "\n"); ++ dpm_unlock(); ++ return len; ++} ++ ++static ssize_t policy_control_store(struct subsystem * subsys, const char * buf, ++ size_t n) ++{ ++ int error = 0; ++ char *tbuf = NULL; ++ char *token[MAXTOKENS]; ++ int ntoks = tokenizer(&tbuf, buf, n, (char **) &token, MAXTOKENS); ++ ++ if (ntoks <= 0) { ++ error = ntoks; ++ goto out; ++ } ++ ++ if (strcmp(token[0],"create") == 0) { ++ error = dpm_create_policy(token[1], &token[2], ntoks - 2); ++ } else if (strcmp(token[0],"set") == 0) { ++ if (ntoks != 2) ++ printk("dpm: policy set requires 1 policy name argument\n"); ++ else ++ error = dpm_set_policy(token[1]); ++ } else ++ error = -EINVAL; ++ ++ out: ++ if (tbuf) ++ kfree(tbuf); ++ return error ? error : n; ++} ++ ++static ssize_t active_policy_show(struct subsystem * subsys, char * buf) ++{ ++ unsigned long flags; ++ ssize_t len = 0; ++ ++ if (dpm_lock_interruptible()) ++ return -ERESTARTSYS; ++ ++ if (!dpm_enabled || (dpm_active_state == DPM_NO_STATE)) { ++ len += sprintf(buf + len, "[none]\n"); ++ } else { ++ spin_lock_irqsave(&dpm_policy_lock, flags); ++ len += sprintf(buf + len,"%s\n", ++ dpm_active_policy->name); ++ spin_unlock_irqrestore(&dpm_policy_lock, flags); ++ } ++ ++ dpm_unlock(); ++ return len; ++} ++ ++static ssize_t active_policy_store(struct subsystem * subsys, const char * buf, ++ size_t n) ++{ ++ int error = 0; ++ char *tbuf = NULL; ++ char *token[MAXTOKENS]; ++ int ntoks = tokenizer(&tbuf, buf, n, (char **) &token, MAXTOKENS); ++ ++ if (ntoks <= 0) { ++ error = ntoks; ++ goto out; ++ } ++ ++ error = dpm_set_policy(token[0]); ++ ++ out: ++ if (tbuf) ++ kfree(tbuf); ++ return error ? error : n; ++} ++ ++dpm_attr(control,policy_control); ++dpm_attr(active,active_policy); ++ ++#ifdef CONFIG_DPM_STATS ++static ssize_t policy_stats_show(struct subsystem * subsys, char * buf) ++{ ++ int len = 0; ++ struct dpm_policy *policy; ++ struct list_head *p; ++ unsigned long long total_time; ++ ++ if (dpm_lock_interruptible()) ++ return -ERESTARTSYS; ++ ++ if (!dpm_enabled) { ++ dpm_unlock(); ++ len += sprintf(buf + len, "DPM IS DISABLED\n"); ++ return len; ++ } ++ ++ for (p = dpm_policies.next; p != &dpm_policies; p = p->next) { ++ policy = list_entry(p, struct dpm_policy, list); ++ len += sprintf(buf + len, "policy: %s", policy->name); ++ total_time = policy->stats.total_time; ++ if (policy == dpm_active_policy) ++ total_time += (unsigned long long) dpm_time() - ++ policy->stats.start_time; ++ len += sprintf(buf + len, " ticks: %Lu times: %lu\n", ++ (unsigned long long) dpm_time_to_usec(total_time), ++ policy->stats.count); ++ } ++ ++ dpm_unlock(); ++ return len; ++} ++ ++static ssize_t policy_stats_store(struct subsystem * subsys, const char * buf, ++ size_t n) ++{ ++ return n; ++} ++ ++dpm_attr(stats, policy_stats); ++#endif /* CONFIG_DPM_STATS */ ++ ++static ssize_t a_policy_control_show(struct kobject * kobj, char * buf) ++{ ++ struct dpm_policy *policy = to_policy(kobj); ++ ssize_t len = 0; ++ int i; ++ ++ len += sprintf(buf + len, "ops/classes: "); ++ ++ for (i = 0; i < DPM_STATES; i++) ++ len += sprintf(buf + len, "%s ", dpm_classopt_name(policy,i)); ++ ++ len += sprintf(buf + len, "\n"); ++ return len; ++} ++ ++static ssize_t a_policy_control_store(struct kobject * kobj, const char * buf, ++ size_t n) ++{ ++ struct dpm_policy *policy = to_policy(kobj); ++ int error = 0; ++ char *tbuf = NULL; ++ char *token[MAXTOKENS]; ++ int ntoks = tokenizer(&tbuf, buf, n, (char **) &token, MAXTOKENS); ++ ++ if (ntoks <= 0) { ++ error = ntoks; ++ goto out; ++ } ++ ++ if (strcmp(token[0],"destroy") == 0) { ++ dpm_destroy_policy(policy->name); ++ } else ++ error = -EINVAL; ++ ++ out: ++ if (tbuf) ++ kfree(tbuf); ++ return error ? error : n; ++} ++ ++#define POLICY_STATE_ATTR(index) \ ++static ssize_t policy_state ## index ## _show(struct kobject * kobj, \ ++ char * buf) \ ++{ \ ++ ssize_t len = 0; \ ++ struct dpm_policy *policy = to_policy(kobj); \ ++ len += sprintf(buf + len, "%s\n", policy->classopt[index].opt ? policy->classopt[index].opt->name :policy->classopt[index].class->name ); \ ++ return len; \ ++} \ ++static ssize_t policy_state ## index ## _store(struct kobject * kobj, \ ++ const char * buf, \ ++ size_t n) \ ++{ \ ++ struct dpm_policy *policy = to_policy(kobj); \ ++ struct dpm_classopt old_classopt; \ ++ int ret; \ ++ \ ++ dpm_lock(); \ ++ old_classopt = policy->classopt[index]; \ ++ if ((ret = dpm_map_policy_state(policy,index,(char *)buf))) \ ++ policy->classopt[index] = old_classopt; \ ++ dpm_unlock(); \ ++ return ret ? -EINVAL : n; \ ++} \ ++static struct dpm_policy_attribute policy_state ## index ## _attr = { \ ++ .attr = { \ ++ .mode = 0644, \ ++ }, \ ++ .show = policy_state ## index ## _show, \ ++ .store = policy_state ## index ## _store, \ ++}; \ ++ ++#define MAX_POLICY_STATES 20 ++POLICY_STATE_ATTR(0); ++POLICY_STATE_ATTR(1); ++POLICY_STATE_ATTR(2); ++POLICY_STATE_ATTR(3); ++POLICY_STATE_ATTR(4); ++POLICY_STATE_ATTR(5); ++POLICY_STATE_ATTR(6); ++POLICY_STATE_ATTR(7); ++POLICY_STATE_ATTR(8); ++POLICY_STATE_ATTR(9); ++POLICY_STATE_ATTR(10); ++POLICY_STATE_ATTR(11); ++POLICY_STATE_ATTR(12); ++POLICY_STATE_ATTR(13); ++POLICY_STATE_ATTR(14); ++POLICY_STATE_ATTR(15); ++POLICY_STATE_ATTR(16); ++POLICY_STATE_ATTR(17); ++POLICY_STATE_ATTR(18); ++POLICY_STATE_ATTR(19); ++ ++static struct dpm_policy_attribute *policy_state_attr[MAX_POLICY_STATES] = { ++ &policy_state0_attr, ++ &policy_state1_attr, ++ &policy_state2_attr, ++ &policy_state3_attr, ++ &policy_state4_attr, ++ &policy_state5_attr, ++ &policy_state6_attr, ++ &policy_state7_attr, ++ &policy_state8_attr, ++ &policy_state9_attr, ++ &policy_state10_attr, ++ &policy_state11_attr, ++ &policy_state12_attr, ++ &policy_state13_attr, ++ &policy_state14_attr, ++ &policy_state15_attr, ++ &policy_state16_attr, ++ &policy_state17_attr, ++ &policy_state18_attr, ++ &policy_state19_attr, ++}; ++ ++static ssize_t ++policy_attr_show(struct kobject * kobj, struct attribute * attr, char * buf) ++{ ++ struct dpm_policy_attribute * policy_attr = to_policy_attr(attr); ++ ssize_t ret = 0; ++ ++ if (policy_attr->show) ++ ret = policy_attr->show(kobj,buf); ++ return ret; ++} ++ ++static ssize_t ++policy_attr_store(struct kobject * kobj, struct attribute * attr, ++ const char * buf, size_t count) ++{ ++ struct dpm_policy_attribute * policy_attr = to_policy_attr(attr); ++ ssize_t ret = 0; ++ ++ if (policy_attr->store) ++ ret = policy_attr->store(kobj,buf,count); ++ return ret; ++} ++ ++static struct dpm_policy_attribute a_policy_control_attr = { ++ .attr = { ++ .name = "control", ++ .mode = 0644, ++ }, ++ .show = a_policy_control_show, ++ .store = a_policy_control_store, ++}; ++ ++static struct sysfs_ops policy_sysfs_ops = { ++ .show = policy_attr_show, ++ .store = policy_attr_store, ++}; ++ ++static struct attribute * policy_default_attrs[] = { ++ &a_policy_control_attr.attr, ++ NULL, ++}; ++ ++static struct kobj_type ktype_policy = { ++ .release = dpm_kobj_release, ++ .sysfs_ops = &policy_sysfs_ops, ++ .default_attrs = policy_default_attrs, ++}; ++ ++void dpm_sysfs_new_policy(struct dpm_policy *policy) ++{ ++ int i; ++ ++ memset(&policy->kobj, 0, sizeof(struct kobject)); ++ policy->kobj.kset = &dpm_subsys.kset, ++ kobject_set_name(&policy->kobj,policy->name); ++ policy->kobj.parent = &dpm_policy_kobj; ++ policy->kobj.ktype = &ktype_policy; ++ kobject_register(&policy->kobj); ++ ++ for (i = 0; (i < DPM_STATES) && (i < MAX_POLICY_STATES); i++) { ++ policy_state_attr[i]->attr.name = dpm_state_names[i]; ++ sysfs_create_file(&policy->kobj, &policy_state_attr[i]->attr); ++ } ++ ++ return; ++} ++ ++void dpm_sysfs_destroy_policy(struct dpm_policy *policy) ++{ ++ kobject_unregister(&policy->kobj); ++ return; ++} ++ ++/* ++ * class ++ */ ++ ++struct dpm_class_attribute { ++ struct attribute attr; ++ ssize_t (*show)(struct kobject * kobj, char * buf); ++ ssize_t (*store)(struct kobject * kobj, const char * buf, size_t count); ++}; ++ ++#define to_class(obj) container_of(obj,struct dpm_class,kobj) ++#define to_class_attr(_attr) container_of(_attr,struct dpm_class_attribute,attr) ++ ++static ssize_t class_control_show(struct subsystem * subsys, char * buf) ++{ ++ ssize_t len = 0; ++ struct list_head * p; ++ ++ len += sprintf(buf + len, "classes: "); ++ ++ list_for_each(p, &dpm_classes) { ++ len += sprintf(buf + len, "%s ", ++ ((struct dpm_class *) ++ list_entry(p, struct dpm_class, list))->name); ++ } ++ ++ len += sprintf(buf + len, "\nactive: %s\n", ++ (dpm_enabled && dpm_active_class) ? ++ dpm_active_class->name : "[none]"); ++ return len; ++} ++ ++static ssize_t class_control_store(struct subsystem * subsys, const char * buf, ++ size_t n) ++{ ++ int error = 0; ++ char *tbuf = NULL; ++ char *token[MAXTOKENS]; ++ int ntoks = tokenizer(&tbuf, buf, n, (char **) &token, MAXTOKENS); ++ ++ if (ntoks <= 0) { ++ error = ntoks; ++ goto out; ++ } ++ ++ if (strcmp(token[0],"create") == 0) { ++ if (ntoks < 3) ++ printk("dpm: class create requires 1 name and at least one operating point argument\n"); ++ else ++ error = dpm_create_class(token[1], &token[2], ntoks-2); ++ } else ++ error = -EINVAL; ++ ++ out: ++ if (tbuf) ++ kfree(tbuf); ++ return error ? error : n; ++} ++ ++static struct kobject dpm_class_kobj = { ++ .kset = &dpm_subsys.kset, ++}; ++ ++dpm_attr(control,class_control); ++ ++static ssize_t a_class_control_show(struct kobject * kobj, char * buf) ++{ ++ ssize_t len = 0; ++ struct dpm_class *class = to_class(kobj); ++ int i; ++ ++ len += sprintf(buf + len, "ops: "); ++ ++ for (i = 0; i < class->nops; i++) ++ len += sprintf(buf + len, "%s ", class->ops[i]->name); ++ ++ ++ len += sprintf(buf + len, "\n"); ++ return len; ++} ++ ++static ssize_t a_class_control_store(struct kobject * kobj, const char * buf, ++ size_t n) ++{ ++ return n; ++} ++ ++static ssize_t ++class_attr_show(struct kobject * kobj, struct attribute * attr, char * buf) ++{ ++ struct dpm_class_attribute * class_attr = to_class_attr(attr); ++ ssize_t ret = 0; ++ ++ if (class_attr->show) ++ ret = class_attr->show(kobj,buf); ++ return ret; ++} ++ ++static ssize_t ++class_attr_store(struct kobject * kobj, struct attribute * attr, ++ const char * buf, size_t count) ++{ ++ struct dpm_class_attribute * class_attr = to_class_attr(attr); ++ ssize_t ret = 0; ++ ++ if (class_attr->store) ++ ret = class_attr->store(kobj,buf,count); ++ return ret; ++} ++ ++static struct dpm_class_attribute a_class_control_attr = { ++ .attr = { ++ .name = "control", ++ .mode = 0644, ++ }, ++ .show = a_class_control_show, ++ .store = a_class_control_store, ++}; ++ ++static struct sysfs_ops class_sysfs_ops = { ++ .show = class_attr_show, ++ .store = class_attr_store, ++}; ++ ++static struct attribute * class_default_attrs[] = { ++ &a_class_control_attr.attr, ++ NULL, ++}; ++ ++static struct kobj_type ktype_class = { ++ .release = dpm_kobj_release, ++ .sysfs_ops = &class_sysfs_ops, ++ .default_attrs = class_default_attrs, ++}; ++ ++void dpm_sysfs_new_class(struct dpm_class *class) ++{ ++ memset(&class->kobj, 0, sizeof(struct kobject)); ++ class->kobj.kset = &dpm_subsys.kset, ++ kobject_set_name(&class->kobj,class->name); ++ class->kobj.parent = &dpm_class_kobj; ++ class->kobj.ktype = &ktype_class; ++ kobject_register(&class->kobj); ++ return; ++} ++ ++void dpm_sysfs_destroy_class(struct dpm_class *class) ++{ ++ kobject_unregister(&class->kobj); ++ return; ++} ++ ++ ++/* ++ * op ++ */ ++ ++struct dpm_op_attribute { ++ struct attribute attr; ++ ssize_t (*show)(struct kobject * kobj, char * buf); ++ ssize_t (*store)(struct kobject * kobj, const char * buf, size_t count); ++}; ++ ++#define to_op(obj) container_of(obj,struct dpm_opt,kobj) ++#define to_op_attr(_attr) container_of(_attr,struct dpm_op_attribute,attr) ++ ++static ssize_t op_control_show(struct subsystem * subsys, char * buf) ++{ ++ unsigned long flags; ++ ssize_t len = 0; ++ ++ if (dpm_lock_interruptible()) ++ return -ERESTARTSYS; ++ ++ len += sprintf(buf + len, "active: "); ++ ++ if (!dpm_enabled) { ++ len += sprintf(buf + len, "[none]\n"); ++ } else { ++ spin_lock_irqsave(&dpm_policy_lock, flags); ++ len += sprintf(buf + len,"%s\n", ++ dpm_active_opt ? dpm_active_opt->name : "[none]"); ++ spin_unlock_irqrestore(&dpm_policy_lock, flags); ++ } ++ ++ dpm_unlock(); ++ ++ len += sprintf(buf + len, "params: %d\n", DPM_PP_NBR); ++ return len; ++} ++ ++static ssize_t op_control_store(struct subsystem * subsys, const char * buf, ++ size_t n) ++{ ++ int error = 0; ++ char *tbuf = NULL; ++ char *token[MAXTOKENS]; ++ int ntoks = tokenizer(&tbuf, buf, n, (char **) &token, MAXTOKENS); ++ ++ if (ntoks <= 0) { ++ error = ntoks; ++ goto out; ++ } ++ ++ if ((strcmp(token[0],"create") == 0) && (ntoks >= 2)) { ++ dpm_md_pp_t pp[DPM_PP_NBR]; ++ int i; ++ ++ for (i = 0; i < DPM_PP_NBR; i++) { ++ if (i >= ntoks - 2) ++ pp[i] = -1; ++ else ++ pp[i] = simple_strtol(token[i + 2], ++ NULL, 0); ++ } ++ ++ error = dpm_create_opt(token[1], pp, DPM_PP_NBR); ++ } else ++ error = -EINVAL; ++ ++ out: ++ if (tbuf) ++ kfree(tbuf); ++ return error ? error : n; ++ ++} ++ ++dpm_attr(control,op_control); ++ ++#ifdef CONFIG_DPM_STATS ++static ssize_t op_stats_show(struct subsystem * subsys, char * buf) ++{ ++ int len = 0; ++ struct dpm_opt *opt; ++ struct list_head *p; ++ unsigned long long total_time; ++ ++ if (dpm_lock_interruptible()) ++ return -ERESTARTSYS; ++ ++ if (!dpm_enabled) { ++ dpm_unlock(); ++ len += sprintf(buf + len, "DPM IS DISABLED\n"); ++ return len; ++ } ++ ++ for (p = dpm_opts.next; p != &dpm_opts; p = p->next) { ++ opt = list_entry(p, struct dpm_opt, list); ++ len += sprintf(buf + len, "op: %s", opt->name); ++ total_time = opt->stats.total_time; ++ if (opt == dpm_active_opt) ++ total_time += (unsigned long long) dpm_time() - ++ opt->stats.start_time; ++ len += sprintf(buf + len, " ticks: %Lu times: %lu\n", ++ (unsigned long long) dpm_time_to_usec(total_time), ++ opt->stats.count); ++ } ++ ++ dpm_unlock(); ++ return len; ++} ++ ++static ssize_t op_stats_store(struct subsystem * subsys, const char * buf, ++ size_t n) ++{ ++ return n; ++} ++ ++dpm_attr(stats, op_stats); ++#endif /* CONFIG_DPM_STATS */ ++ ++ ++static struct kobject dpm_op_kobj = { ++ .kset = &dpm_subsys.kset, ++}; ++ ++static ssize_t an_op_control_show(struct kobject * kobj, char * buf) ++{ ++ ssize_t len = 0; ++ // struct dpm_opt *opt = to_op(kobj); ++ ++ len += sprintf(buf + len, "\n"); ++ return len; ++} ++ ++static ssize_t an_op_control_store(struct kobject * kobj, const char * buf, ++ size_t n) ++{ ++ return n; ++} ++ ++static struct dpm_op_attribute an_op_control_attr = { ++ .attr = { ++ .name = "control", ++ .mode = 0644, ++ }, ++ .show = an_op_control_show, ++ .store = an_op_control_store, ++}; ++ ++static ssize_t op_force_show(struct kobject * kobj, char * buf) ++{ ++ ssize_t len = 0; ++ struct dpm_opt *opt = to_op(kobj); ++ ++ len += sprintf(buf + len, "%d\n", opt->flags & DPM_OP_FORCE ? 1 : 0); ++ return len; ++} ++ ++static ssize_t op_force_store(struct kobject * kobj, const char * buf, ++ size_t n) ++{ ++ struct dpm_opt *opt = to_op(kobj); ++ ++ opt->flags = (opt->flags & ~DPM_OP_FORCE) | ++ (simple_strtol(buf, NULL, 0) ? DPM_OP_FORCE : 0); ++ return n; ++} ++ ++static struct dpm_op_attribute op_force_attr = { ++ .attr = { ++ .name = "force", ++ .mode = 0644, ++ }, ++ .show = op_force_show, ++ .store = op_force_store, ++}; ++ ++#define OP_PARAM_ATTR(index) \ ++static ssize_t op_param ## index ## _show(struct kobject * kobj, char * buf) \ ++{ \ ++ ssize_t len = 0; \ ++ struct dpm_opt *opt = to_op(kobj); \ ++ len += sprintf(buf + len, "%d\n", opt->pp[index]); \ ++ return len; \ ++} \ ++static ssize_t op_param ## index ## _store(struct kobject * kobj, const char * buf, \ ++ size_t n) \ ++{ \ ++ struct dpm_opt *opt = to_op(kobj); \ ++ int ret, oldval; \ ++ \ ++ oldval = opt->pp[index]; \ ++ opt->pp[index] = simple_strtol(buf, NULL, 0); \ ++ ret = dpm_md_init_opt(opt); \ ++ if (ret) \ ++ opt->pp[index] = oldval; \ ++ return ret ? ret : n; \ ++} \ ++static struct dpm_op_attribute op_param ## index ## _attr = { \ ++ .attr = { \ ++ .mode = 0644, \ ++ }, \ ++ .show = op_param ## index ## _show, \ ++ .store = op_param ## index ## _store, \ ++}; \ ++ ++#define MAX_OP_PARAMS 20 ++OP_PARAM_ATTR(0); ++OP_PARAM_ATTR(1); ++OP_PARAM_ATTR(2); ++OP_PARAM_ATTR(3); ++OP_PARAM_ATTR(4); ++OP_PARAM_ATTR(5); ++OP_PARAM_ATTR(6); ++OP_PARAM_ATTR(7); ++OP_PARAM_ATTR(8); ++OP_PARAM_ATTR(9); ++OP_PARAM_ATTR(10); ++OP_PARAM_ATTR(11); ++OP_PARAM_ATTR(12); ++OP_PARAM_ATTR(13); ++OP_PARAM_ATTR(14); ++OP_PARAM_ATTR(15); ++OP_PARAM_ATTR(16); ++OP_PARAM_ATTR(17); ++OP_PARAM_ATTR(18); ++OP_PARAM_ATTR(19); ++ ++static struct dpm_op_attribute *op_param_attr[MAX_OP_PARAMS] = { ++ &op_param0_attr, ++ &op_param1_attr, ++ &op_param2_attr, ++ &op_param3_attr, ++ &op_param4_attr, ++ &op_param5_attr, ++ &op_param6_attr, ++ &op_param7_attr, ++ &op_param8_attr, ++ &op_param9_attr, ++ &op_param10_attr, ++ &op_param11_attr, ++ &op_param12_attr, ++ &op_param13_attr, ++ &op_param14_attr, ++ &op_param15_attr, ++ &op_param16_attr, ++ &op_param17_attr, ++ &op_param18_attr, ++ &op_param19_attr, ++}; ++ ++static ssize_t ++op_attr_show(struct kobject * kobj, struct attribute * attr, char * buf) ++{ ++ struct dpm_op_attribute * op_attr = to_op_attr(attr); ++ ssize_t ret = 0; ++ ++ if (op_attr->show) ++ ret = op_attr->show(kobj,buf); ++ return ret; ++} ++ ++static ssize_t ++op_attr_store(struct kobject * kobj, struct attribute * attr, ++ const char * buf, size_t count) ++{ ++ struct dpm_op_attribute * op_attr = to_op_attr(attr); ++ ssize_t ret = 0; ++ ++ if (op_attr->store) ++ ret = op_attr->store(kobj,buf,count); ++ return ret; ++} ++ ++static struct sysfs_ops op_sysfs_ops = { ++ .show = op_attr_show, ++ .store = op_attr_store, ++}; ++ ++static struct attribute * op_default_attrs[] = { ++ &an_op_control_attr.attr, ++ &op_force_attr.attr, ++ NULL, ++}; ++ ++static struct kobj_type ktype_op = { ++ .release = dpm_kobj_release, ++ .sysfs_ops = &op_sysfs_ops, ++ .default_attrs = op_default_attrs, ++}; ++ ++void dpm_sysfs_new_op(struct dpm_opt *opt) ++{ ++ int i; ++ ++ memset(&opt->kobj, 0, sizeof(struct kobject)); ++ opt->kobj.kset = &dpm_subsys.kset, ++ kobject_set_name(&opt->kobj,opt->name); ++ opt->kobj.parent = &dpm_op_kobj; ++ opt->kobj.ktype = &ktype_op; ++ kobject_register(&opt->kobj); ++ ++ for (i = 0; (i < DPM_PP_NBR) && (i < MAX_OP_PARAMS); i++) { ++ op_param_attr[i]->attr.name = dpm_param_names[i]; ++ sysfs_create_file(&opt->kobj, &op_param_attr[i]->attr); ++ } ++ ++ return; ++} ++ ++void dpm_sysfs_destroy_op(struct dpm_opt *opt) ++{ ++ kobject_unregister(&opt->kobj); ++ return; ++} ++ ++ ++/* ++ * state ++ */ ++ ++ ++static ssize_t state_control_show(struct subsystem * subsys, char * buf) ++{ ++ ssize_t len = 0; ++ int i; ++ ++ len += sprintf(buf + len, "states: "); ++ ++ for (i = 0; i < DPM_STATES; i++) { ++ len += sprintf(buf + len, "%s ", dpm_state_names[i]); ++ } ++ ++ len += sprintf(buf + len, "\ntask-states: min=%s norm=%s max=%s\n", ++ dpm_state_names[DPM_TASK_STATE - DPM_TASK_STATE_LIMIT], ++ dpm_state_names[DPM_TASK_STATE], ++ dpm_state_names[DPM_TASK_STATE + DPM_TASK_STATE_LIMIT]); ++ ++ return len; ++} ++ ++static ssize_t state_control_store(struct subsystem * subsys, const char * buf, ++ size_t n) ++{ ++ return -EINVAL; ++} ++ ++static ssize_t active_state_show(struct subsystem * subsys, char * buf) ++{ ++ unsigned long flags; ++ ssize_t len = 0; ++ ++ if (dpm_lock_interruptible()) ++ return -ERESTARTSYS; ++ ++ if (!dpm_enabled || (dpm_active_state == DPM_NO_STATE)) { ++ len += sprintf(buf + len, "[none]\n"); ++ } else { ++ spin_lock_irqsave(&dpm_policy_lock, flags); ++ len += sprintf(buf + len,"%s\n", ++ dpm_state_names[dpm_active_state]); ++ spin_unlock_irqrestore(&dpm_policy_lock, flags); ++ } ++ ++ dpm_unlock(); ++ return len; ++} ++ ++static ssize_t active_state_store(struct subsystem * subsys, const char * buf, ++ size_t n) ++{ ++ int error = 0; ++ char *tbuf = NULL; ++ char *token[MAXTOKENS]; ++ int ntoks = tokenizer(&tbuf, buf, n, (char **) &token, MAXTOKENS); ++ ++ if (ntoks <= 0) { ++ error = ntoks; ++ goto out; ++ } ++ ++ error = dpm_set_op_state(token[0]); ++ ++ out: ++ if (tbuf) ++ kfree(tbuf); ++ return error ? error : n; ++} ++ ++#ifdef CONFIG_DPM_STATS ++static ssize_t state_stats_show(struct subsystem * subsys, char * buf) ++{ ++ unsigned long flags; ++ ssize_t len = 0; ++ int i; ++ ++ spin_lock_irqsave(&dpm_policy_lock, flags); ++ ++ for (i = 0; i < DPM_STATES; i++) { ++ unsigned long long total_time = dpm_state_stats[i].total_time; ++ ++ if (i == dpm_active_state) ++ total_time += (unsigned long long) dpm_time() - ++ dpm_state_stats[i].start_time; ++ ++ len += sprintf(buf + len, "state: %s", dpm_state_names[i]); ++ len += sprintf(buf + len, " ticks: %Lu", ++ (unsigned long long) dpm_time_to_usec(total_time)); ++ len += sprintf(buf + len, " times: %lu\n", ++ dpm_state_stats[i].count); ++ } ++ ++ spin_unlock_irqrestore(&dpm_policy_lock, flags); ++ return len; ++} ++ ++static ssize_t state_stats_store(struct subsystem * subsys, const char * buf, ++ size_t n) ++{ ++ return n; ++} ++#endif /* CONFIG_DPM_STATS */ ++ ++static struct kobject dpm_state_kobj = { ++ .kset = &dpm_subsys.kset, ++}; ++ ++dpm_attr(control, state_control); ++dpm_attr(active, active_state); ++#ifdef CONFIG_DPM_STATS ++dpm_attr(stats, state_stats); ++#endif ++ ++struct astate { ++ int index; ++ struct kobject kobj; ++}; ++ ++struct astate_attribute { ++ struct attribute attr; ++ ssize_t (*show)(struct kobject * kobj, char * buf); ++ ssize_t (*store)(struct kobject * kobj, const char * buf, size_t count); ++}; ++ ++#define to_astate(obj) container_of(obj,struct astate,kobj) ++#define to_astate_attr(_attr) container_of(_attr,struct astate_attribute,attr) ++ ++static ssize_t ++astate_attr_show(struct kobject * kobj, struct attribute * attr, char * buf) ++{ ++ struct astate_attribute * astate_attr = to_astate_attr(attr); ++ ssize_t ret = 0; ++ ++ if (astate_attr->show) ++ ret = astate_attr->show(kobj,buf); ++ return ret; ++} ++ ++static ssize_t ++astate_attr_store(struct kobject * kobj, struct attribute * attr, ++ const char * buf, size_t count) ++{ ++ struct astate_attribute * astate_attr = to_astate_attr(attr); ++ ssize_t ret = 0; ++ ++ if (astate_attr->store) ++ ret = astate_attr->store(kobj,buf,count); ++ return ret; ++} ++ ++static int show_opconstrains(int state, char *buf) ++{ ++ struct dpm_opt *opt; ++ int len = 0; ++ ++ if (dpm_active_policy->classopt[state].opt) { ++ opt = dpm_active_policy->classopt[state].opt; ++ ++ len += dpm_show_opconstraints(opt, buf); ++ } ++ else { ++ int i; ++ ++ for (i = 0; ++ i < dpm_active_policy->classopt[state].class->nops; i++) { ++ len += dpm_show_opconstraints( ++ dpm_active_policy->classopt[state].class->ops[i], buf); ++ } ++ } ++ ++ return len; ++} ++static ssize_t astate_constraints_show(struct kobject * kobj, char * buf) ++{ ++ struct astate *astate = to_astate(kobj); ++ ssize_t len = 0; ++ ++ if (dpm_enabled && dpm_active_policy) ++ len = show_opconstrains(astate->index, buf); ++ ++ return len; ++} ++ ++static ssize_t astate_constraints_store(struct kobject * kobj, ++ const char * buf, size_t n) ++{ ++ return n; ++} ++ ++static struct astate_attribute astate_constraints_attr = { ++ .attr = { ++ .name = "constraints", ++ .mode = 0644, ++ }, ++ .show = astate_constraints_show, ++ .store = astate_constraints_store, ++}; ++ ++static struct sysfs_ops astate_sysfs_ops = { ++ .show = astate_attr_show, ++ .store = astate_attr_store, ++}; ++ ++static struct attribute * astate_default_attrs[] = { ++ &astate_constraints_attr.attr, ++ NULL, ++}; ++ ++static struct kobj_type ktype_astate = { ++ .release = dpm_kobj_release, ++ .sysfs_ops = &astate_sysfs_ops, ++ .default_attrs = astate_default_attrs, ++}; ++ ++static struct astate astate[DPM_STATES]; ++ ++/* ++ * Init ++ */ ++ ++static int __init dpm_sysfs_init(void) ++{ ++ int error, i; ++ ++ error = subsystem_register(&dpm_subsys); ++ if (!error) ++ error = sysfs_create_group(&dpm_subsys.kset.kobj,&dpm_attr_group); ++ if (!error) { ++ kobject_set_name(&dpm_policy_kobj, "policy"); ++ kobject_register(&dpm_policy_kobj); ++ sysfs_create_file(&dpm_policy_kobj, &policy_control_attr.attr); ++ sysfs_create_file(&dpm_policy_kobj, &active_policy_attr.attr); ++#ifdef CONFIG_DPM_STATS ++ sysfs_create_file(&dpm_policy_kobj, &policy_stats_attr.attr); ++#endif ++ kobject_set_name(&dpm_class_kobj, "class"); ++ kobject_register(&dpm_class_kobj); ++ sysfs_create_file(&dpm_class_kobj, &class_control_attr.attr); ++ kobject_set_name(&dpm_op_kobj, "op"); ++ kobject_register(&dpm_op_kobj); ++ sysfs_create_file(&dpm_op_kobj, &op_control_attr.attr); ++#ifdef CONFIG_DPM_STATS ++ sysfs_create_file(&dpm_op_kobj, &op_stats_attr.attr); ++#endif ++ kobject_set_name(&dpm_state_kobj, "state"); ++ kobject_register(&dpm_state_kobj); ++ sysfs_create_file(&dpm_state_kobj, &state_control_attr.attr); ++ sysfs_create_file(&dpm_state_kobj, &active_state_attr.attr); ++#ifdef CONFIG_DPM_STATS ++ sysfs_create_file(&dpm_state_kobj, &state_stats_attr.attr); ++#endif ++ ++ for (i = 0; i < DPM_STATES; i++) { ++ astate[i].index = i; ++ astate[i].kobj.kset = &dpm_subsys.kset; ++ kobject_set_name(&astate[i].kobj,dpm_state_names[i]); ++ astate[i].kobj.parent = &dpm_state_kobj; ++ astate[i].kobj.ktype = &ktype_astate; ++ kobject_register(&astate[i].kobj); ++ } ++ } ++ ++ return error; ++} ++ ++__initcall(dpm_sysfs_init); ++ ++/* /proc interface */ ++ ++int dpm_set_task_state_by_name(struct task_struct *task, char *buf, ssize_t n) ++{ ++ int task_state; ++ int ret = 0; ++ char *tbuf = NULL; ++ char *token[MAXTOKENS]; ++ int ntoks = tokenizer(&tbuf, buf, n, (char **) &token, MAXTOKENS); ++ ++ if (ntoks <= 0) { ++ ret = ntoks; ++ goto out; ++ } ++ ++ for (task_state = DPM_TASK_STATE - DPM_TASK_STATE_LIMIT; ++ task_state <= DPM_TASK_STATE + DPM_TASK_STATE_LIMIT; ++ task_state++) ++ if (strcmp(token[0], dpm_state_names[task_state]) == 0) { ++ task->dpm_state = task_state; ++ ++ if (task == current) ++ dpm_set_os(task_state); ++ ++ ret = 0; ++ break; ++ } ++ ++out: ++ if (tbuf) ++ kfree(tbuf); ++ ++ return ret; ++} +Index: linux-2.6.16/drivers/dpm/dpm.c +=================================================================== +--- linux-2.6.16.orig/drivers/dpm/dpm.c 1970-01-01 00:00:00.000000000 +0000 ++++ linux-2.6.16/drivers/dpm/dpm.c 2006-04-11 06:35:40.000000000 +0000 +@@ -0,0 +1,1117 @@ ++/* ++ * drivers/dpm/policy.c Dynamic Power Management Policies ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ * ++ * Copyright (C) 2002, International Business Machines Corporation ++ * All Rights Reserved ++ * ++ * Robert Paulsen ++ * IBM Linux Technology Center ++ * rpaulsen@us.ibm.com ++ * August, 2002 ++ * ++ */ ++ ++/* TODO: ++ ++ Rethink init/enable/disable: It may be redundant and/or unsafe ++ Fix initialization and stats ++*/ ++ ++#include <linux/dpm.h> ++#include <linux/init.h> ++#include <linux/interrupt.h> ++#include <linux/module.h> ++#include <linux/proc_fs.h> ++#include <linux/sched.h> ++#include <linux/slab.h> ++#include <linux/spinlock.h> ++#include <linux/delay.h> ++#include <linux/preempt.h> ++ ++#include <asm/semaphore.h> ++#include <asm/system.h> ++#include <asm/uaccess.h> ++ ++#undef TRACE ++#if defined(TRACE) ++#define trace(args...) do { printk("TRACE: "); printk(args); } while(0) ++#else ++#define trace(args...) do {} while(0) ++#endif ++ ++struct dpm_md dpm_md; ++ ++static struct dpm_opt nop_op = { ++ .name = "[nop]", ++ .flags = DPM_OP_NOP, ++}; ++ ++extern void dpm_force_off_constrainers(struct dpm_opt *opt); ++ ++unsigned long dpm_compute_lpj(unsigned long ref, u_int div, u_int mult) ++{ ++ unsigned long new_jiffy_l, new_jiffy_h; ++ ++ /* ++ * Recalculate loops_per_jiffy. We do it this way to ++ * avoid math overflow on 32-bit machines. Maybe we ++ * should make this architecture dependent? If you have ++ * a better way of doing this, please replace! ++ * ++ * new = old * mult / div ++ */ ++ new_jiffy_h = ref / div; ++ new_jiffy_l = (ref % div) / 100; ++ new_jiffy_h *= mult; ++ new_jiffy_l = new_jiffy_l * mult / div; ++ ++ return new_jiffy_h + new_jiffy_l * 100; ++} ++ ++/**************************************************************************** ++ ++DPM Synchronization and Operating Point Changes ++=============================================== ++ ++There are 2 aspects to synchronization in DPM: First, the usual requirement of ++serializing access to shared data structures, and second, the idea of ++synchronizing the operating point and the current operating state. The second ++condition arises because setting an operating point may complete asynchronously ++for a number of reasons, whereas the operating state change that causes the ++operating point change succeeds immediately. ++ ++Access to most of the global variables representing the current state of DPM ++and the current policy are protected by a spinlock, dpm_policy_lock. The use ++of this lock appears in only a few critical places. ++ ++Setting the operating point, reading the value of the current operating point ++or changing the current policy may only be done while holding the semaphore ++_dpm_lock. Access to the _dpm_lock is abstracted by the dpm_lock() and ++dpm_unlock() calls as explained below. (The semaphore should only be accessed ++this way to simplify future development). ++ ++The _dpm_lock must be held (by a call to a dpm_lock function) by any caller of ++the interfaces that set the operating point, change the policy, or enable or ++disable DPM. Note that the corresponding call to dpm_unlock() may be ++explicitly required, or implicit (see dpm_set_opt_async() below). ++ ++For simplicity, the calls that create operating points and policies also use ++dpm_lock() and dpm_unlock() to protect access to the non-active policies as ++well. Since these are normally initialization calls, this should not interfere ++with the operation of the system once initialized. ++ ++Three interfaces are provided for obtaining the _dpm_lock: ++ ++void dpm_lock(); ++int dpm_lock_interruptible(); ++int dpm_trylock(); ++ ++dpm_lock_interruptible() returns -ERESTARTSYS if the wait for the _dpm_lock was ++interrupted, and dpm_trylock() returns -EBUSY if the semaphore is currently ++held. ++ ++Once the _dpm_lock is held, two interfaces are provided for setting the ++operating point: ++ ++int dpm_set_opt_async() ++int dpm_set_opt_sync(); ++ ++Neither of these interfaces takes parameters since under DPM the operating ++point to select is always implied by the current policy and operating state. ++If the system is already at the correct operating point then no change is ++required or made. To avoid deadlock, the caller must not be holding the ++dpm_policy_lock when either of these calls is made. ++ ++dpm_set_opt_async() launches a change in the operating point that will ++potentially terminate asynchronously. This interface never blocks the caller, ++thus there is no guarantee that the system is actually running at the implied ++operating point when control returns to the caller. This call is used by ++dpm_set_os() during an operating state change. Note since this call terminates ++asynchronously, the call to dpm_unlock() is implicitly made when the operating ++point change is complete. I.e., the caller obtains the _dpm_lock with ++dpm_lock(), calls dpm_set_opt_async(), then continues. ++ ++dpm_set_opt_sync() launches a synchronous change in the operating point. This ++call will block the caller as necessary during the call, thus it can only be ++issued from a process context. When control returns to the caller, the caller ++can be sure that the implied operating point was set, and that the system is ++currently running at the correct operating point for the given policy and ++operating state. This call is used by dpm_set_policy() and the device ++constraint update code to guarantee that the change to a new policy, or changes ++to operating point classes as a result of device constraits are reflected in ++the operating point. ++ ++Note that regardless of whether an operating point change is synchrounous or ++asynchronous, it is still possible that the operating state may change during ++the call. Setting the operating point is (currently) not preemptible, ++therefore at the time that the operating point change is complete, it may no ++longer be the correct operating point for the operating state. This condition ++is always handled by the dpm_set_opt*() routines, which will launch a tasklet ++to re-synchronize the operating point to the operating state. ++ ++It is possible that due to poorly designed policies and asynchronous ++termination of operating point changes that the operating point will always lag ++behind the operating state. This is only a performance issue, not a ++correctness issue. Since a valid policy has a valid operating point for every ++operating state, and changes to the policy and changes in devices constraints ++always use dpm_set_opt_sync(), there will never be a case where the current ++operating point does not support device constraints. ++ ++****************************************************************************/ ++ ++/* curently installed policies and operating points */ ++LIST_HEAD(dpm_policies); ++LIST_HEAD(dpm_classes); ++LIST_HEAD(dpm_opts); ++ ++DECLARE_MUTEX(_dpm_lock); ++spinlock_t dpm_policy_lock = SPIN_LOCK_UNLOCKED; ++ ++/* the currently active policy */ ++struct dpm_policy *dpm_active_policy; ++ ++/* the currently active operating state, class, and operating point */ ++dpm_state_t dpm_active_state = DPM_NO_STATE; ++struct dpm_opt *dpm_active_opt; ++struct dpm_class *dpm_active_class; ++ ++/* is DPM initialized and enabled? */ ++int dpm_enabled; ++int dpm_initialized; ++ ++#ifdef CONFIG_DPM_STATS ++#include <asm/div64.h> ++ ++struct dpm_stats dpm_state_stats[DPM_STATES]; ++ ++/* ++ * Start counting DPM stats from the time DPM was enabled... in the case of ++ * operating states the stats are updated from the time userspace is started. ++ */ ++ ++void ++dpm_stats_reset(void) ++{ ++ int i; ++ ++ preempt_disable(); ++ for (i = 0; i < DPM_STATES; i++) { ++ dpm_state_stats[i].total_time = 0; ++ dpm_state_stats[i].start_time = 0; ++ dpm_state_stats[i].count = 0; ++ } ++ ++ if (dpm_active_state != DPM_NO_STATE) { ++ dpm_state_stats[dpm_active_state].start_time = dpm_time(); ++ dpm_state_stats[dpm_active_state].count = 1; ++ } ++ ++ preempt_enable(); ++} ++ ++ ++unsigned long long ++dpm_update_stats(struct dpm_stats *new, struct dpm_stats *old) ++{ ++ unsigned long long now = dpm_time(); ++ ++ if (old) ++ old->total_time += now - old->start_time; ++ ++ if (new) { ++ new->start_time = now; ++ new->count += 1; ++ } ++ ++ return now; ++} ++#else ++#define dpm_update_stats(a,b) do {} while (0) ++#define dpm_stats_reset() do {} while (0) ++#endif /* CONFIG_DPM_STATS */ ++ ++struct dpm_opt * ++dpm_choose_opt(struct dpm_policy *policy, int state) ++{ ++ struct dpm_opt *opt = NULL; ++ ++ if (policy->classopt[state].opt) { ++ opt = policy->classopt[state].opt; ++ ++ if (opt->flags & DPM_OP_FORCE) ++ dpm_force_off_constrainers(opt); ++ else if (! dpm_check_constraints(opt)) ++ opt = NULL; ++ ++ dpm_active_class = NULL; ++ } ++ else { ++ int i; ++ ++ for (i = 0; i < policy->classopt[state].class->nops; i++) { ++ if (dpm_check_constraints( ++ policy->classopt[state].class->ops[i])) { ++ opt = policy->classopt[state].class->ops[i]; ++ break; ++ } ++ } ++ ++ dpm_active_class = policy->classopt[state].class; ++ } ++ ++ return opt; ++} ++ ++ ++ ++/***************************************************************************** ++ * dpm_next_opt() returns the operating point that needs to be activated next, ++ * or NULL if the operating point is up-to-date or the DPM system is disabled. ++ * Since this call looks at the value of the current operating point, it can ++ * only be made when the _dpm_lock is held. ++ *****************************************************************************/ ++ ++static inline struct dpm_opt * ++dpm_next_opt(void) ++{ ++ struct dpm_opt *opt = NULL; ++ ++ if (! spin_trylock(&dpm_policy_lock)) ++ return NULL; ++ if (dpm_enabled && dpm_active_state != DPM_NO_STATE) { ++ opt = dpm_choose_opt(dpm_active_policy,dpm_active_state); ++ if (opt == dpm_active_opt) ++ opt = NULL; ++ } ++ spin_unlock(&dpm_policy_lock); ++ return opt; ++} ++ ++/***************************************************************************** ++ * Set the operating point implied by the current DPM policy. These calls can ++ * only be made while holding _dpm_lock, and the release of ++ * _dpm_lock is implied by the call (see below). ++ *****************************************************************************/ ++ ++static struct dpm_opt temp_opt = { name : "[System Operating Point]" }; ++ ++int ++dpm_set_opt(struct dpm_opt *new, unsigned flags) ++{ ++ int error; ++ ++ if (new->flags & DPM_OP_NOP) { ++ if (flags & DPM_UNLOCK) ++ dpm_unlock(); ++ return 0; ++ } ++ ++ /* Support for setting the operating point when DPM is not running, and ++ setting the first operating point. */ ++ ++ if (!dpm_enabled || !dpm_active_opt) { ++ if (dpm_md_get_opt(&temp_opt)) { ++ printk(KERN_ERR "dpm_default_set_opt: " ++ "DPM disabled and system " ++ "operating point is illegal!\n"); ++ ++ if (flags & DPM_UNLOCK) ++ dpm_unlock(); ++ return -EINVAL; ++ } ++ dpm_active_opt = &temp_opt; ++ dpm_active_class = NULL; ++ } ++ ++ /* ++ * Remove the IRQ disable since in some cases scheduling is needed ++ * to set an operating point (only sleep mode). The spinlock ++ * should suffice. If the machine-dependent code needs interrupts ++ * turned off during the code used for that platform for that ++ * operating point set sequence then IRQs will need to be disabled ++ * in that code instead. ++ */ ++ error = dpm_md.set_opt(dpm_active_opt, new); ++ ++ if (error == 0) { ++ dpm_update_stats(&new->stats, &dpm_active_opt->stats); ++ dpm_active_opt = new; ++ mb(); ++ } ++ ++ if (flags & DPM_UNLOCK) ++ dpm_unlock(); ++ ++ return error; ++} ++ ++/***************************************************************************** ++ * Set operating point asynchronously. The _dpm_lock will be cleared whenever ++ * the change in operating point is complete. ++ *****************************************************************************/ ++ ++int ++dpm_set_opt_async(void) ++{ ++ struct dpm_opt *opt = dpm_next_opt(); ++ ++ if (opt) { ++ dpm_trace(DPM_TRACE_SET_OPT_ASYNC, opt); ++ return dpm_set_opt(opt, DPM_UNLOCK); ++ } else { ++ dpm_trace(DPM_TRACE_SET_OPT_ASYNC, NULL); ++ dpm_unlock(); ++ return 0; ++ } ++} ++ ++/***************************************************************************** ++ * Set operating point synchronously. The caller must clear _dpm_lock after the ++ * call returns. ++ *****************************************************************************/ ++ ++int ++dpm_set_opt_sync(void) ++{ ++ struct dpm_opt *opt = dpm_next_opt(); ++ ++ if (opt) { ++ dpm_trace(DPM_TRACE_SET_OPT_SYNC, opt); ++ return dpm_set_opt(opt, DPM_SYNC); ++ } else ++ dpm_trace(DPM_TRACE_SET_OPT_SYNC, NULL); ++ return 0; ++} ++ ++/***************************************************************************** ++ * Resynchronize the operating state and the operating point without ++ * blocking. If we don't get the lock it doesn't matter, since whenever the ++ * lock holder releases the lock the resynchronization will be tried again. ++ *****************************************************************************/ ++ ++static inline void ++dpm_resync(void) ++{ ++ ++ dpm_trace(DPM_TRACE_RESYNC); ++ if (! dpm_trylock()) ++ dpm_set_opt_async(); ++} ++ ++void ++dpm_resync_task(unsigned long ignore) ++{ ++ dpm_resync(); ++} ++ ++/***************************************************************************** ++ * unlock the DPM ++ * ++ * If the operating point and operating state are not in sync when _dpm_lock is ++ * released, a tasklet is launched to resynchronize them. A tasklet is used ++ * rather than simply calling dpm_set_op directly to avoid deep recursions. ++ * (I'm not sure this has worked, though). ++ * ++ * (The locking functions are inline in dpm_policy.h) ++ * ++ * This is not static since it needs to be called from dpm_policy.c ++ *****************************************************************************/ ++ ++DECLARE_TASKLET(dpm_resync_tasklet, dpm_resync_task, 0); ++ ++void ++dpm_unlock(void) ++{ ++ int retry; ++ ++ retry = dpm_next_opt() != NULL; ++ dpm_trace(DPM_TRACE_UNLOCK, retry); ++ up(&_dpm_lock); ++ if (retry) ++ tasklet_schedule(&dpm_resync_tasklet); ++} ++ ++/***************************************************************************** ++ * Enter a new operating state for statistical purposes. Returns 1 if the new ++ * state may require a change in operating point and 0 otherwise. ++ * ++ * The normal case that occurs during task scheduling, where we go from task ++ * state to task state, is quickly ignored, as are changes to the ++ * DPM_NO_STATE and changes when DPM is not running. Otherwise, ++ * dpm_enter_state() has advertised that we are in a new state, and indicates ++ * whether an operating point change is required. ++ * ++ * Note the system invariant that the operating point always eventually ++ * catches up with changes to the operating state. This is what makes it ++ * correct here to check for common operating points. We know ++ * that if a common operating point is not the current operating point, it ++ * will be soon. ++ * ++ * The 'quick' variant (in dpm.h) is called out separately to reduce latency ++ * for critical operating state changes where the following are known: 1) The ++ * dpm_policy_lock is held and/or interrupts are properly disabled. 2) DPM is ++ * enabled. 3) The new state is neither DPM_NO_STATE nor the same as the ++ * active state. 4) Any operating point change is being handled elsewhere. ++ *****************************************************************************/ ++ ++static int ++dpm_enter_state(int new_state) ++{ ++ int ret = 0; ++ ++ if (! spin_trylock(&dpm_policy_lock)) { ++ dpm_quick_enter_state(new_state); ++ return 0; ++ } ++ ++ if ((new_state == dpm_active_state) || ++ (new_state == DPM_NO_STATE) || ++ !dpm_enabled) { ++ spin_unlock(&dpm_policy_lock); ++ return ret; ++ } ++ ++ if ((dpm_active_policy->classopt[new_state].class != ++ dpm_active_policy->classopt[dpm_active_state].class) || ++ (dpm_active_policy->classopt[new_state].opt != ++ dpm_active_policy->classopt[dpm_active_state].opt)) ++ ret = 1; ++ ++ dpm_quick_enter_state(new_state); ++ spin_unlock(&dpm_policy_lock); ++ return ret; ++} ++ ++ ++/***************************************************************************** ++ * set operating state ++ * ++ * This is used by the kernel to inform the DPM that the operating state has ++ * changed and that a new operating point should (possibly) be set as a ++ * result. ++ * ++ * If an operating point change is required it is attempted. If we can't get ++ * the lock here, then the operating point change will be activated when the ++ * current lock holder releases the lock. ++ *****************************************************************************/ ++ ++void ++dpm_set_os(dpm_state_t new_state) ++{ ++ dpm_trace(DPM_TRACE_SET_OS, new_state); ++ if (dpm_enter_state(new_state)) ++ dpm_resync(); ++} ++ ++EXPORT_SYMBOL(dpm_set_os); ++ ++/***************************************************************************** ++ * initialize the DPM ++ *****************************************************************************/ ++int ++dynamicpower_init(void) ++{ ++ trace("in dynamicpower_init\n"); ++ ++ if (dpm_initialized) { ++ trace("DPM already initialized"); ++ return -EALREADY; ++ } ++ ++ /* mutex-style semaphore for access to policies and opts */ ++ init_MUTEX(&_dpm_lock); ++ ++ dpm_active_policy = 0; /* this leaves the DPM temporarily ++ disabled until a policy is ++ activated */ ++ dpm_enabled = 0; ++ dpm_initialized = 1; ++ dpm_active_state = DPM_TASK_STATE; ++ ++ ++ trace("DPM is now initialized\n"); ++ ++ return 0; ++} ++ ++/***************************************************************************** ++ * (temporarily) disable the DPM ++ *****************************************************************************/ ++int ++dynamicpower_disable(void) ++{ ++ ++ trace("in dynamicpower_disable\n"); ++ ++ if (! dpm_enabled) { ++ trace("DPM already disabled"); ++ return -EALREADY; ++ } ++ ++ dpm_lock(); ++ ++ dpm_enabled = 0; ++ dpm_md_cleanup(); ++ dpm_active_opt = NULL; ++ dpm_active_class = NULL; ++ ++ dpm_unlock(); ++ ++ trace("DPM is now disabled\n"); ++ ++ return 0; ++} ++ ++/***************************************************************************** ++ * re-enable the DPM ++ * dpm_enabled = 1 implies that DPM is initialized and there is an active ++ * policy. The 'enable' call is really designed to be used after a temporary ++ * 'disable'. All that's required to start DPM is to initialize it and set a ++ * policy. ++ *****************************************************************************/ ++ ++/* Need to think through enable/disable */ ++ ++int ++dynamicpower_enable(void) ++{ ++ ++ trace("in dynamicpower_enable\n"); ++ ++ if (dpm_enabled) { ++ trace("DPM already enabled"); ++ return -EALREADY; ++ } ++ ++ dpm_lock(); ++ ++ if (dpm_active_policy) { ++ dpm_enabled = 1; ++ mb(); ++ dpm_md_startup(); ++ dpm_stats_reset(); ++ dpm_set_opt_sync(); ++ trace("DPM is now enabled\n"); ++ } else { ++ trace("No active policy, dpm_enable is ignored\n"); ++ } ++ ++ dpm_unlock(); ++ return 0; ++} ++ ++/***************************************************************************** ++ * Suspend/Resume DPM ++ * The current operating point is saved and restored. This ++ * interface is designed to be used by system suspend/resume code, to safely ++ * save/restore the DPM operating point across a system power-down, where the ++ * firmware may resume the system at a random operating point. This does not ++ * require DPM to be enabled. Note that DPM remains locked across the ++ * suspend/resume. ++ *****************************************************************************/ ++ ++static struct dpm_opt suspend_opt = { name : "[Suspended Op. Point]" }; ++struct dpm_opt *suspended_opt; ++ ++int ++dynamicpm_suspend(void) ++{ ++ int err; ++ ++ trace("in dpm_suspend\n"); ++ ++ dpm_lock(); ++ ++ if (dpm_enabled && dpm_active_opt) { ++ suspended_opt = dpm_active_opt; ++ } else { ++ suspended_opt = &suspend_opt; ++ if ((err = dpm_md_get_opt(suspended_opt))) { ++ printk(KERN_CRIT ++ "DPM can not suspend the current op. point!\n"); ++ suspended_opt = NULL; ++ return err; ++ } ++ } ++ return 0; ++} ++ ++void ++dynamicpm_resume(void) ++{ ++ trace("in dpm_resume\n"); ++ ++ if (suspended_opt) { ++ dpm_active_opt = NULL; /* Force reinitialization of DPM */ ++ dpm_active_class = NULL; ++ dpm_set_opt(suspended_opt, DPM_SYNC); ++ suspended_opt = NULL; ++ } ++ dpm_unlock(); ++} ++ ++ ++/***************************************************************************** ++ * Create a named operating point ++ * The alternate entry point can be used to create anonymous operating points ++ *****************************************************************************/ ++ ++int ++_dpm_create_opt(struct dpm_opt **p, const char *name, ++ const dpm_md_pp_t * md_pp, int npp) ++{ ++ struct dpm_opt *opt; ++ int ret; ++ ++ /* get memory for opt */ ++ if (! ++ (opt = ++ (struct dpm_opt *) kmalloc(sizeof (struct dpm_opt), GFP_KERNEL))) { ++ return -ENOMEM; ++ } ++ trace("%s @ 0x%08lx\n", name, (unsigned long)opt); ++ memset(opt, 0, sizeof(struct dpm_opt)); ++ if (!(opt->name = (char *) kmalloc(strlen(name) + 1, GFP_KERNEL))) { ++ kfree(opt); ++ return -ENOMEM; ++ } ++ ++ /* initialize and validate the opt */ ++ strcpy(opt->name, name); ++ memcpy(&opt->pp, md_pp, npp * sizeof(dpm_md_pp_t)); ++ ret = dpm_md_init_opt(opt); ++ if (ret) { ++ kfree(opt->name); ++ kfree(opt); ++ return ret; ++ } ++ INIT_LIST_HEAD(&opt->list); ++ *p = opt; ++ dpm_sysfs_new_op(opt); ++ return 0; ++} ++ ++int ++dpm_create_opt(const char *name, const dpm_md_pp_t * md_pp, int npp) ++{ ++ int ret; ++ struct dpm_opt *opt; ++ ++ trace("in dpm_create_opt for \"%s\"\n", name); ++ ++ dpm_lock(); ++ ++ /* ensure name is unique */ ++ list_find(opt, name, dpm_opts, struct dpm_opt); ++ if (opt) { ++ dpm_unlock(); ++ return -EEXIST; ++ } ++ ++ /* create the opt */ ++ ret = _dpm_create_opt(&opt, name, md_pp, npp); ++ ++ /* add opt to our list */ ++ if (!ret) ++ list_add(&opt->list, &dpm_opts); ++ ++ dpm_unlock(); ++ return ret; ++} ++ ++/***************************************************************************** ++ * destroy an operating point ++ * Assumes _dpm_lock is held and the opt is no longer needed *anywhere* ++ *****************************************************************************/ ++void ++destroy_opt(struct dpm_opt *opt) ++{ ++ dpm_sysfs_destroy_op(opt); ++ list_del(&opt->list); ++ kfree(opt->name); ++ kfree(opt); ++} ++ ++/***************************************************************************** ++ * create a named class of operating points (to be used to map to an operating ++ * state) ++ *****************************************************************************/ ++ ++int ++dpm_create_class(const char *name, char **op_names, unsigned nops) ++{ ++ int i; ++ struct dpm_class *cls; ++ ++ trace("in dpm_create_class for \"%s\"\n", name); ++ ++ dpm_lock(); ++ ++ /* ensure class is not empty */ ++ if (nops == 0) { ++ dpm_unlock(); ++ return -EINVAL; ++ } ++ ++ /* ensure name is unique */ ++ list_find(cls, name, dpm_classes, struct dpm_class); ++ if (cls) { ++ dpm_unlock(); ++ return -EEXIST; ++ } ++ ++ /* get memory for class */ ++ cls = (struct dpm_class *) kmalloc(sizeof (struct dpm_class), GFP_KERNEL); ++ if (!cls) { ++ dpm_unlock(); ++ return -ENOMEM; ++ } ++ trace("%s @ 0x%08lx\n", name, (unsigned long)cls); ++ memset(cls, 0, sizeof (struct dpm_class)); ++ /* get memory for array of pointers to operating points */ ++ cls->ops = ++ (struct dpm_opt **) kmalloc(nops * sizeof (struct dpm_opt *), ++ GFP_KERNEL); ++ if (!cls->ops) { ++ kfree(cls); ++ dpm_unlock(); ++ return -ENOMEM; ++ } ++ ++ /* get memory for class name */ ++ cls->name = (char *) kmalloc(strlen(name) + 1, GFP_KERNEL); ++ if (!cls->name) { ++ kfree(cls->ops); ++ kfree(cls); ++ dpm_unlock(); ++ return -ENOMEM; ++ } ++ ++ /* find named op points and put their pointers in the class */ ++ for (i = 0; i < nops; ++i) { ++ struct dpm_opt *opt; ++ list_find(opt, op_names[i], dpm_opts, struct dpm_opt); ++ if (!opt) { ++ kfree(cls->name); ++ kfree(cls->ops); ++ kfree(cls); ++ dpm_unlock(); ++ return -ENOENT; ++ } ++ cls->ops[i] = opt; ++ } ++ strcpy(cls->name, name); ++ cls->nops = nops; ++ /* add class to our list */ ++ list_add(&cls->list, &dpm_classes); ++ ++ dpm_unlock(); ++ dpm_sysfs_new_class(cls); ++ ++ return 0; ++} ++ ++/***************************************************************************** ++ * destroy a class ++ * Assumes _dpm_lock is held and the class is no longer needed *anywhere* ++ *****************************************************************************/ ++void ++destroy_class(struct dpm_class *cls) ++{ ++ dpm_sysfs_destroy_class(cls); ++ list_del(&cls->list); ++ kfree(cls->ops); ++ kfree(cls->name); ++ kfree(cls); ++} ++ ++int ++dpm_map_policy_state(struct dpm_policy *policy, int state, char *classopt) ++{ ++ list_find(policy->classopt[state].opt, classopt, dpm_opts, ++ struct dpm_opt); ++ ++ if(!policy->classopt[state].opt) { ++ list_find(policy->classopt[state].class, classopt, ++ dpm_classes, struct dpm_class); ++ if(!policy->classopt[state].class) ++ return -1; ++ } ++ ++ return 0; ++} ++ ++/***************************************************************************** ++ * create power policy ++ *****************************************************************************/ ++int ++dpm_create_policy(const char *name, char **classopt_names, int nopts) ++{ ++ int i; ++ struct dpm_policy *policy; ++ ++ trace("in dpm_install_policy for \"%s\" policy\n", name); ++ ++ dpm_lock(); ++ ++ /* ensure unique name */ ++ list_find(policy, name, dpm_policies, struct dpm_policy); ++ if (policy) { ++ dpm_unlock(); ++ return -EEXIST; ++ } ++ ++ /* get memory for policy */ ++ policy = ++ (struct dpm_policy *) kmalloc(sizeof (struct dpm_policy), ++ GFP_KERNEL); ++ if (!policy) { ++ dpm_unlock(); ++ return -ENOMEM; ++ } ++ trace("%s @ 0x%08lx\n", name, (unsigned long)policy); ++ memset(policy, 0, sizeof (struct dpm_policy)); ++ /* get memory for policy name */ ++ policy->name = (char *) kmalloc(strlen(name) + 1, GFP_KERNEL); ++ if (!policy->name) { ++ kfree(policy); ++ dpm_unlock(); ++ return -ENOMEM; ++ } ++ ++ /* initialize the policy */ ++ for (i = 0; i < DPM_STATES; ++i) { ++ if ((i >= nopts) || !classopt_names[i]) { ++ policy->classopt[i].opt = &nop_op; ++ } else { ++ if (dpm_map_policy_state(policy, i, classopt_names[i]) ++ < 0) { ++ kfree(policy->name); ++ kfree(policy); ++ dpm_unlock(); ++ return -ENOENT; ++ } ++ } ++ } ++ strcpy(policy->name, name); ++ ++ /* add policy to our list */ ++ list_add(&policy->list, &dpm_policies); ++ dpm_sysfs_new_policy(policy); ++ trace("installed \"%s\" policy\n", name); ++ dpm_unlock(); ++ return 0; ++} ++ ++/***************************************************************************** ++ * destroy a power policy ++ * Assumes _dpm_lock is held and the policy is no longer needed *anywhere* ++ *****************************************************************************/ ++void ++destroy_policy(struct dpm_policy *policy) ++{ ++ dpm_sysfs_destroy_policy(policy); ++ list_del(&policy->list); ++ kfree(policy->name); ++ kfree(policy); ++} ++ ++/***************************************************************************** ++ * uninstall power policy ++ *****************************************************************************/ ++int ++dpm_destroy_policy(const char *name) ++{ ++ struct dpm_policy *policy; ++ ++ trace("processing destroy request for \"%s\"\n", name); ++ ++ dpm_lock(); ++ ++ /* find the named policy */ ++ list_find(policy, name, dpm_policies, struct dpm_policy); ++ if (!policy) { ++ dpm_unlock(); ++ return -ENOENT; ++ } ++ ++ /* can't uninstall active policy */ ++ if (policy == dpm_active_policy) { ++ dpm_unlock(); ++ return -EBUSY; ++ } ++ ++ /* remove the policy */ ++ destroy_policy(policy); ++ ++ dpm_unlock(); ++ trace("destroyed \"%s\" policy\n", name); ++ return 0; ++} ++ ++/* ++ * set active power policy ++ */ ++int ++dpm_set_policy(const char *name) ++{ ++ struct dpm_policy *new_p; ++ ++ trace("in dpm_set_policy for \"%s\" policy\n", name); ++ ++ dpm_lock(); ++ ++ list_find(new_p, name, dpm_policies, struct dpm_policy); ++ if (!new_p) { ++ dpm_trace(DPM_TRACE_SET_POLICY, name, -ENOENT); ++ dpm_unlock(); ++ return -ENOENT; /* invalid name */ ++ } ++ if (new_p == dpm_active_policy) { ++ dpm_trace(DPM_TRACE_SET_POLICY, name, 0); ++ trace("\"%s\" policy already activated\n", name); ++ dpm_unlock(); ++ return 0; ++ } ++ ++ dpm_update_stats(&new_p->stats, ++ dpm_active_policy ? &dpm_active_policy->stats ++ : NULL); ++ ++ dpm_active_policy = new_p; ++ ++ if (! dpm_enabled) { ++ dpm_enabled = 1; ++ dpm_md_startup(); ++ dpm_stats_reset(); ++ } ++ ++ /* Start the policy synchronously */ ++ ++ mb(); ++ dpm_trace(DPM_TRACE_SET_POLICY, name, 0); ++ dpm_set_opt_sync(); ++ dpm_unlock(); ++ ++ return 0; ++} ++ ++/***************************************************************************** ++ * set a raw op state ++ *****************************************************************************/ ++ ++int ++dpm_set_op_state(const char *name) ++{ ++ int op_state; ++ ++ for (op_state = 0; op_state < DPM_STATES; op_state++) ++ if (strcmp(dpm_state_names[op_state], name) == 0) { ++ dpm_set_os(op_state); ++ return 0; ++ } ++ ++ return -ENOENT; ++} ++ ++/***************************************************************************** ++ * terminate the DPM ++ *****************************************************************************/ ++int ++dynamicpower_terminate(void) ++{ ++ trace("in dynamicpower_terminate\n"); ++ ++ if (!dpm_initialized) ++ return 0; ++ ++ dpm_lock(); ++ ++ dpm_md_cleanup(); ++ ++ dpm_initialized = 0; ++ dpm_enabled = 0; ++ dpm_active_opt = NULL; ++ dpm_active_class = NULL; ++ ++ /* destroy all entities */ ++ while (!list_empty(&dpm_policies)) ++ destroy_policy(list_entry ++ (dpm_policies.next, struct dpm_policy, list)); ++ while (!list_empty(&dpm_opts)) ++ destroy_opt(list_entry(dpm_opts.next, struct dpm_opt, list)); ++ while (!list_empty(&dpm_classes)) ++ destroy_class(list_entry(dpm_classes.next, struct dpm_class, ++ list)); ++ ++ ++ mb(); ++ dpm_unlock(); ++ ++ trace("DPM is now terminated\n"); ++ printk("Dynamic Power Management is now terminated\n"); ++ ++ return 0; ++} ++ ++EXPORT_SYMBOL(dynamicpower_init); ++EXPORT_SYMBOL(dynamicpower_terminate); ++EXPORT_SYMBOL(dynamicpower_disable); ++EXPORT_SYMBOL(dynamicpower_enable); ++EXPORT_SYMBOL(dpm_create_opt); ++EXPORT_SYMBOL(dpm_create_class); ++EXPORT_SYMBOL(dpm_create_policy); ++EXPORT_SYMBOL(dpm_destroy_policy); ++EXPORT_SYMBOL(dpm_set_policy); ++ ++/**************************************************************************** ++ * install dynamic power policy support ++ ****************************************************************************/ ++static int __init ++dpm_init_module(void) ++{ ++ int i; ++ ++ /* Set the NOP operating point params to all -1. */ ++ ++ for (i = 0; i < DPM_PP_NBR; i++) ++ nop_op.pp[i] = -1; ++ ++ trace("DPM is now installed\n"); ++ return 0; ++} ++ ++/**************************************************************************** ++ * remove dynamic power policy support ++ ****************************************************************************/ ++static void __exit ++dpm_exit_module(void) ++{ ++ /* disable power management policy system */ ++ dynamicpower_terminate(); ++ ++ trace("DPM module is now unloaded\n"); ++} ++ ++module_init(dpm_init_module); ++module_exit(dpm_exit_module); ++ ++/* ++ * Local variables: ++ * c-basic-offset: 8 ++ * End: ++ */ +Index: linux-2.6.16/drivers/dpm/proc.c +=================================================================== +--- linux-2.6.16.orig/drivers/dpm/proc.c 1970-01-01 00:00:00.000000000 +0000 ++++ linux-2.6.16/drivers/dpm/proc.c 2006-04-11 06:34:11.000000000 +0000 +@@ -0,0 +1,601 @@ ++/* ++ * drivers/dpm/proc.c Dynamic Power Management /proc ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ * ++ * Copyright (C) 2002, International Business Machines Corporation ++ * All Rights Reserved ++ * ++ * Bishop Brock ++ * IBM Research, Austin Center for Low-Power Computing ++ * bcbrock@us.ibm.com ++ * September, 2002 ++ * ++ */ ++ ++#include <linux/dpm.h> ++#include <linux/init.h> ++#include <linux/interrupt.h> ++#include <linux/mm.h> ++#include <linux/module.h> ++#include <linux/proc_fs.h> ++#include <linux/sched.h> ++#include <linux/slab.h> ++#include <linux/spinlock.h> ++#include <asm/semaphore.h> ++#include <asm/system.h> ++#include <asm/uaccess.h> ++ ++#define DEBUG ++#ifdef DEBUG ++#define DPRINT(args...) printk(KERN_CRIT args) ++#else ++#define DPRINT(args...) do {} while (0) ++#endif ++ ++/**************************************************************************** ++ * /proc/driver/dpm interfaces ++ * ++ * NB: Some of these are borrowed from the 405LP, and may need to be made ++ * machine independent. ++ ****************************************************************************/ ++ ++/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ++ * /proc/driver/dpm/cmd (Write-Only) ++ * ++ * Writing a string to this file is equivalent to issuing a DPM command. ++ * Currently only one command per "write" is allowed, and there is a maximum on ++ * the number of tokens that will be accepted (PAGE_SIZE / sizeof(char *)). ++ * DPM can be initialized by a linewise copy of a configuration file to this ++ * /proc file. ++ * ++ * DPM Control ++ * ----------- ++ * ++ * init : dynamicpower_init() ++ * enable : dynamicpower_enable() ++ * disable : dynamicpower_disable() ++ * terminate : dynamicpower_terminate() ++ * ++ * Policy Control ++ * -------------- ++ * ++ * set_policy <policy> : Set the policy by name ++ * set_task_state <pid> <state> : Set the task state for a given pid, 0 = self ++ * ++ * Policy Creation ++ * --------------- ++ * ++ * create_opt <name> <pp0> ... <ppn> ++ * Create a named operating point from DPM_PP_NBR paramaters. All ++ * parameters must be given. Parameter order and meaning are machine ++ * dependent. ++ * ++ * create_class <name> <opt0> [ ... <optn> ] ++ * Create a named class from 1 or more named operating points. All ++ * operating points must be defined before the call. ++ * ++ * create_policy <name> <classopt0> [ ... <classoptn> ] ++ * Create a named policy from DPM_STATES classes or operating ++ * points. All operating points must be defined before the call. ++ * The order is machine dependent. ++ * ++ *+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ ++ ++static void ++pwarn(char *command, int ntoks, char *requirement, int require) ++{ ++ printk(KERN_WARNING "/proc/driver/dpm/cmd: " ++ "Command %s requires %s%d arguments - %d were given\n", ++ command, requirement, require - 1, ntoks - 1); ++} ++ ++/***************************************************************************** ++ * set a task state ++ *****************************************************************************/ ++ ++static int ++dpm_set_task_state(pid_t pid, dpm_state_t task_state) ++{ ++ struct task_struct *p; ++ ++ if (task_state == -(DPM_TASK_STATE_LIMIT + 1)) ++ task_state = DPM_NO_STATE; ++ else if (abs(task_state) > DPM_TASK_STATE_LIMIT) { ++ dpm_trace(DPM_TRACE_SET_TASK_STATE, pid, task_state, -EINVAL); ++ return -EINVAL; ++ } else ++ task_state += DPM_TASK_STATE; ++ ++ read_lock(&tasklist_lock); ++ ++ if (pid == 0) ++ p = current; ++ else ++ p = find_task_by_pid(pid); ++ ++ if (!p) { ++ read_unlock(&tasklist_lock); ++ dpm_trace(DPM_TRACE_SET_TASK_STATE, pid, task_state, -ENOENT); ++ return -ENOENT; ++ } ++ ++ p->dpm_state = task_state; ++ read_unlock(&tasklist_lock); ++ ++ dpm_trace(DPM_TRACE_SET_TASK_STATE, pid, task_state, 0); ++ ++ if (pid == 0) ++ dpm_set_os(p->dpm_state); ++ ++ ++ return 0; ++} ++ ++ ++static int ++write_proc_dpm_cmd (struct file *file, const char *buffer, ++ unsigned long count, void *data) ++{ ++ char *buf, *tok, **tokptrs; ++ char *whitespace = " \t\r\n"; ++ int ret = 0, ntoks; ++ ++ if (current->uid != 0) ++ return -EACCES; ++ if (count == 0) ++ return 0; ++ if (!(buf = kmalloc(count + 1, GFP_KERNEL))) ++ return -ENOMEM; ++ if (copy_from_user(buf, buffer, count)) { ++ ret = -EFAULT; ++ goto out0; ++ } ++ ++ buf[count] = '\0'; ++ ++ if (!(tokptrs = (char **)__get_free_page(GFP_KERNEL))) { ++ ret = -ENOMEM; ++ goto out1; ++ } ++ ++ ret = -EINVAL; ++ ntoks = 0; ++ do { ++ buf = buf + strspn(buf, whitespace); ++ tok = strsep(&buf, whitespace); ++ if (*tok == '\0') { ++ if (ntoks == 0) { ++ ret = 0; ++ goto out1; ++ } else ++ break; ++ } ++ if (ntoks == (PAGE_SIZE / sizeof(char **))) ++ goto out1; ++ tokptrs[ntoks++] = tok; ++ } while(buf); ++ ++ if (ntoks == 1) { ++ if (strcmp(tokptrs[0], "init") == 0) { ++ ret = dynamicpower_init(); ++ } else if (strcmp(tokptrs[0], "enable") == 0) { ++ ret = dynamicpower_enable(); ++ } else if (strcmp(tokptrs[0], "disable") == 0) { ++ ret = dynamicpower_disable(); ++ } else if (strcmp(tokptrs[0], "terminate") == 0) { ++ ret = dynamicpower_terminate(); ++ } ++ } else if (ntoks == 2) { ++ if (strcmp(tokptrs[0], "set_policy") == 0) ++ ret = dpm_set_policy(tokptrs[1]); ++ else if (strcmp(tokptrs[0], "set_state") == 0) ++ ret = dpm_set_op_state(tokptrs[1]); ++ } else { ++ if (strcmp(tokptrs[0], "set_task_state") == 0) { ++ if (ntoks != 3) ++ pwarn("set_task_state", ntoks, "", 3); ++ else ++ ret = dpm_set_task_state(simple_strtol(tokptrs[1], ++ NULL, 0), ++ simple_strtol(tokptrs[2], ++ NULL, 0)); ++ } else if (strcmp(tokptrs[0], "create_opt") == 0) { ++ if (ntoks != DPM_PP_NBR + 2) ++ pwarn("create_opt", ntoks, ++ "", DPM_PP_NBR + 2); ++ else { ++ dpm_md_pp_t pp[DPM_PP_NBR]; ++ int i; ++ ++ for (i = 0; i < DPM_PP_NBR; i++) ++ pp[i] = simple_strtol(tokptrs[i + 2], ++ NULL, 0); ++ ret = dpm_create_opt(tokptrs[1], pp, DPM_PP_NBR); ++ } ++ ++ } else if (strcmp(tokptrs[0], "create_class") == 0) { ++ if (ntoks < 3) ++ pwarn("create_class", ntoks, ">= ", 3); ++ else ++ ret = dpm_create_class(tokptrs[1], &tokptrs[2], ++ ntoks - 2); ++ ++ } else if (strcmp(tokptrs[0], "create_policy") == 0) { ++ if (ntoks != (DPM_STATES + 2)) ++ pwarn("create_policy", ntoks, "", ++ DPM_STATES + 2); ++ else ++ ret = dpm_create_policy(tokptrs[1], ++ &tokptrs[2], ntoks-2); ++ } ++ } ++out1: ++ free_page((unsigned long)tokptrs); ++out0: ++ kfree(buf); ++ if (ret == 0) ++ return count; ++ else ++ return ret; ++} ++ ++#ifdef CONFIG_DPM_STATS ++ ++/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ++ * /proc/driver/dpm/stats (Read-Only) ++ * ++ * Reading this file produces the following line for each defined operating ++ * state: ++ * ++ * state_name total_time count opt_name ++ * ++ * Where: ++ * ++ * state_name = The operating state name. ++ * total_time = The 64-bit number of microseconds spent in this ++ * operating state. ++ * count = The 64-bit number of times this operating state was entered. ++ * opt_name = The name of the operating point currently assigned to this ++ * operating state. ++ * ++ *+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ ++ ++static int ++sprintf_u64(char *buf, int fill, char *s, u64 ul) ++{ ++ int len = 0; ++ u32 u, l; ++ ++ u = (u32)((ul >> 32) & 0xffffffffU); ++ l = (u32)(ul & 0xffffffffU); ++ ++ len += sprintf(buf + len, s); ++ if (fill) ++ len += sprintf(buf + len, "0x%08x%08x", u, l); ++ else { ++ if (u) ++ len += sprintf(buf + len, "0x%x%x", u, l); ++ else ++ len += sprintf(buf + len, "0x%x", l); ++ } ++ return len; ++} ++ ++/***************************************************************************** ++ * get statistics for all operating states ++ *****************************************************************************/ ++ ++int ++dpm_get_os_stats(struct dpm_stats *stats) ++{ ++ unsigned long flags; ++ ++ spin_lock_irqsave(&dpm_policy_lock, flags); ++ memcpy(stats, dpm_state_stats, DPM_STATES * sizeof (struct dpm_stats)); ++ stats[dpm_active_state].total_time += ++ dpm_time() - stats[dpm_active_state].start_time; ++ spin_unlock_irqrestore(&dpm_policy_lock, flags); ++ return 0; ++} ++ ++static int ++read_proc_dpm_stats(char *page, char **start, off_t offset, ++ int count, int *eof, void *data) ++{ ++ int i, len = 0; ++ struct dpm_stats stats[DPM_STATES]; ++ ++ if (!dpm_enabled) { ++ len += sprintf(page + len, "DPM IS DISABLED\n"); ++ *eof = 1; ++ return len; ++ } ++ ++ dpm_get_os_stats(stats); ++ ++ for (i = 0; i < DPM_STATES; i++) { ++ len += sprintf(page + len, "%20s", dpm_state_names[i]); ++ len += sprintf_u64(page + len, 1, " ", ++ (u64)stats[i].total_time); ++ len += sprintf_u64(page + len, 1, " ", (u64)stats[i].count); ++ len += sprintf(page + len, " %s\n", ++ dpm_classopt_name(dpm_active_policy,i)); ++ } ++ ++ *eof = 1; ++ return len; ++} ++ ++/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ++ * /proc/driver/dpm/opt_stats (Read-Only) ++ * ++ * Reading this file produces the following line for each defined operating ++ * point: ++ * ++ * name total_time count ++ * ++ * Where: ++ * ++ * name = The operating point name. ++ * total_time = The 64-bit number of microseconds spent in this ++ * operating state. ++ * count = The 64-bit number of times this operating point was entered. ++ * ++ *+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ ++ ++static int ++read_proc_dpm_opt_stats(char *page, char **start, off_t offset, ++ int count, int *eof, void *data) ++{ ++ int len = 0; ++ struct dpm_opt *opt; ++ struct list_head *p; ++ unsigned long long total_time; ++ ++ if (dpm_lock_interruptible()) ++ return -ERESTARTSYS; ++ ++ if (!dpm_enabled) { ++ dpm_unlock(); ++ len += sprintf(page + len, "DPM IS DISABLED\n"); ++ *eof = 1; ++ return len; ++ } ++ ++ for (p = dpm_opts.next; p != &dpm_opts; p = p->next) { ++ opt = list_entry(p, struct dpm_opt, list); ++ len += sprintf(page + len, "%s", opt->name); ++ total_time = opt->stats.total_time; ++ if (opt == dpm_active_opt) ++ total_time += dpm_time() - opt->stats.start_time; ++ len += sprintf_u64(page + len, 0, " ", opt->stats.total_time); ++ len += sprintf_u64(page + len, 0, " ", opt->stats.count); ++ len += sprintf(page + len, "\n"); ++ } ++ ++ dpm_unlock(); ++ *eof = 1; ++ return len; ++} ++#endif /* CONFIG_DPM_STATS */ ++ ++/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ++ * /proc/driver/dpm/state (Read-Only) ++ * ++ * Reading this file produces the following: ++ * ++ * policy_name os os_name os_opt_name opt_name hz ++ * ++ * Where: ++ * ++ * policy_name = The name of the current policy ++ * os = The curret operating state index ++ * os_name = The current operating state name ++ * os_opt_name = The name of the implied operating point for the policy and ++ * state. ++ * opt_name = The name of the actual operating point; may be different if ++ * the operating state and operating point are out of sync. ++ * hz = The frequency of the statistics timer ++ * ++ * If DPM is disabled the line will appear as: ++ * ++ * N/A -1 N/A N/A N/A <hz> ++ * ++ *+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ ++ ++static int ++read_proc_dpm_state(char *page, char **start, off_t offset, ++ int count, int *eof, void *data) ++{ ++ unsigned long flags; ++ ++ int len = 0; ++ ++ if (dpm_lock_interruptible()) ++ return -ERESTARTSYS; ++ ++ if (!dpm_enabled) { ++ len += sprintf(page + len, "N/A -1 N/A N/A N/A N/A\n"); ++ } else { ++ ++ spin_lock_irqsave(&dpm_policy_lock, flags); ++ len += sprintf(page + len,"%s %d %s %s %s\n", ++ dpm_active_policy->name, ++ dpm_active_state, ++ dpm_state_names[dpm_active_state], ++ dpm_classopt_name(dpm_active_policy, ++ dpm_active_state), ++ dpm_active_opt ? dpm_active_opt->name : "none"); ++ spin_unlock_irqrestore(&dpm_policy_lock, flags); ++ } ++ ++ dpm_unlock(); ++ *eof = 1; ++ return len; ++} ++ ++ ++/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ++ * /proc/driver/dpm/debug (Read-Only) ++ * ++ * Whatever it needs to be ++ *++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ ++ ++#ifdef DEBUG ++static int ++read_proc_dpm_debug(char *page, char **start, off_t offset, ++ int count, int *eof, void *data) ++{ ++ int len = 0; ++ ++ len += sprintf(page + len, "No DEBUG info\n"); ++ *eof = 1; ++ return len; ++} ++#endif /* DEBUG */ ++ ++static struct proc_dir_entry *proc_dpm; ++static struct proc_dir_entry *proc_dpm_cmd; ++static struct proc_dir_entry *proc_dpm_state; ++ ++#ifdef CONFIG_DPM_STATS ++static struct proc_dir_entry *proc_dpm_stats; ++static struct proc_dir_entry *proc_dpm_opt_stats; ++#endif ++ ++#ifdef DEBUG ++static struct proc_dir_entry *proc_dpm_debug; ++#endif ++ ++#ifdef CONFIG_DPM_TRACE ++static struct proc_dir_entry *proc_dpm_trace; ++#endif ++ ++static int __init ++dpm_proc_init(void) ++{ ++ proc_dpm = proc_mkdir("driver/dpm", NULL); ++ ++ if (proc_dpm) { ++ ++ proc_dpm_cmd = ++ create_proc_entry("cmd", ++ S_IWUSR, ++ proc_dpm); ++ if (proc_dpm_cmd) ++ proc_dpm_cmd->write_proc = write_proc_dpm_cmd; ++ ++ proc_dpm_state = ++ create_proc_read_entry("state", ++ S_IRUGO, ++ proc_dpm, ++ read_proc_dpm_state, ++ NULL); ++#ifdef CONFIG_DPM_STATS ++ proc_dpm_stats = ++ create_proc_read_entry("stats", ++ S_IRUGO, ++ proc_dpm, ++ read_proc_dpm_stats, ++ NULL); ++ proc_dpm_opt_stats = ++ create_proc_read_entry("opt_stats", ++ S_IRUGO, ++ proc_dpm, ++ read_proc_dpm_opt_stats, ++ NULL); ++ ++#endif /* CONFIG_DPM_STATS */ ++ ++#ifdef DEBUG ++ proc_dpm_debug = ++ create_proc_read_entry("debug", ++ S_IRUGO, ++ proc_dpm, ++ read_proc_dpm_debug, ++ NULL); ++#endif ++ ++#ifdef CONFIG_DPM_TRACE ++ proc_dpm_trace = ++ create_proc_read_entry("trace", ++ S_IWUSR | S_IRUGO, ++ proc_dpm, ++ read_proc_dpm_trace, ++ NULL); ++ if (proc_dpm_trace) ++ proc_dpm_trace->write_proc = write_proc_dpm_trace; ++#endif ++ } else { ++ printk(KERN_ERR "Attempt to create /proc/driver/dpm failed\n"); ++ ++ } ++ return 0; ++} ++ ++static void __exit ++dpm_proc_exit(void) ++{ ++ if (proc_dpm_cmd) { ++ remove_proc_entry("cmd", proc_dpm); ++ proc_dpm_cmd = NULL; ++ } ++ ++ if (proc_dpm_state) { ++ remove_proc_entry("state", proc_dpm); ++ proc_dpm_state = NULL; ++ } ++ ++#ifdef CONFIG_DPM_STATS ++ if (proc_dpm_stats) { ++ remove_proc_entry("stats", proc_dpm); ++ proc_dpm_stats = NULL; ++ } ++ ++ if (proc_dpm_opt_stats) { ++ remove_proc_entry("opt_stats", proc_dpm); ++ proc_dpm_opt_stats = NULL; ++ } ++#endif /* CONFIG_DPM_STATS */ ++ ++#ifdef DEBUG ++ if (proc_dpm_debug) { ++ remove_proc_entry("debug", proc_dpm); ++ proc_dpm_debug = NULL; ++ } ++#endif ++ ++#ifdef CONFIG_DPM_TRACE ++ if (proc_dpm_trace) { ++ remove_proc_entry("trace", proc_dpm); ++ proc_dpm_trace = NULL; ++ } ++#endif ++ ++ remove_proc_entry("driver/dpm", NULL); ++} ++ ++ ++ ++module_init(dpm_proc_init); ++module_exit(dpm_proc_exit); ++ ++/* ++ * Local variables: ++ * c-basic-offset: 8 ++ * End: ++ */ ++ +Index: linux-2.6.16/fs/proc/base.c +=================================================================== +--- linux-2.6.16.orig/fs/proc/base.c 2006-03-20 05:53:29.000000000 +0000 ++++ linux-2.6.16/fs/proc/base.c 2006-04-11 06:34:11.000000000 +0000 +@@ -167,6 +167,10 @@ + PROC_TID_OOM_SCORE, + PROC_TID_OOM_ADJUST, + ++#ifdef CONFIG_DPM ++ PROC_TGID_DPM, ++#endif ++ + /* Add new entries before this */ + PROC_TID_FD_DIR = 0x8000, /* 0x8000-0xffff */ + }; +@@ -221,6 +225,9 @@ + #ifdef CONFIG_AUDITSYSCALL + E(PROC_TGID_LOGINUID, "loginuid", S_IFREG|S_IWUSR|S_IRUGO), + #endif ++#ifdef CONFIG_DPM ++ E(PROC_TGID_DPM, "dpmstate", S_IFREG|S_IRUGO|S_IWUSR), ++#endif + {0,0,NULL,0} + }; + static struct pid_entry tid_base_stuff[] = { +@@ -432,10 +439,10 @@ + goto out_mm; /* Shh! No looking before we're done */ + + len = mm->arg_end - mm->arg_start; +- ++ + if (len > PAGE_SIZE) + len = PAGE_SIZE; +- ++ + res = access_process_vm(task, mm->arg_start, buffer, len, 0); + + // If the nul at the end of args has been overwritten, then +@@ -783,18 +790,18 @@ + goto out; + + ret = 0; +- ++ + mm = get_task_mm(task); + if (!mm) + goto out_free; + + ret = -EIO; +- ++ + if (file->private_data != (void*)((long)current->self_exec_id)) + goto out_put; + + ret = 0; +- ++ + while (count > 0) { + int this_len, retval; + +@@ -810,7 +817,7 @@ + ret = -EFAULT; + break; + } +- ++ + ret += retval; + src += retval; + buf += retval; +@@ -862,7 +869,7 @@ + copied += retval; + buf += retval; + dst += retval; +- count -= retval; ++ count -= retval; + } + *ppos = dst; + free_page((unsigned long) page); +@@ -1096,7 +1103,7 @@ + + if (!tmp) + return -ENOMEM; +- ++ + inode = dentry->d_inode; + path = d_path(dentry, mnt, tmp, PAGE_SIZE); + len = PTR_ERR(path); +@@ -1304,7 +1311,7 @@ + struct proc_inode *ei; + + /* We need a new inode */ +- ++ + inode = new_inode(sb); + if (!inode) + goto out; +@@ -1462,6 +1469,56 @@ + return ~0U; + } + ++#ifdef CONFIG_DPM ++#include <linux/dpm.h> ++ ++extern int dpm_set_task_state_by_name(struct task_struct *, char *, ssize_t); ++ ++static ssize_t proc_dpm_read(struct file * file, char __user * buf, ++ size_t count, loff_t *ppos) ++{ ++ struct task_struct *task = proc_task(file->f_dentry->d_inode); ++ int len; ++ char lbuf[80]; ++ ++ if (*ppos != 0) ++ return 0; ++ ++ len = sprintf(lbuf,"%s\n", task->dpm_state == DPM_NO_STATE ? ++ "none" : dpm_state_names[task->dpm_state]); ++ ++ if (copy_to_user(buf, lbuf, len)) ++ return -EFAULT; ++ ++ *ppos += len; ++ return len; ++} ++ ++static ssize_t proc_dpm_write(struct file * file, const char * buf, ++ size_t count, loff_t *ppos) ++{ ++ struct task_struct *task = proc_task(file->f_dentry->d_inode); ++ char lbuf[80]; ++ int error; ++ ssize_t len; ++ ++ len = (count < 80) ? count : 79; ++ ++ if (copy_from_user(lbuf, buf, len)) ++ return -EFAULT; ++ ++ lbuf[79] = 0; ++ error = dpm_set_task_state_by_name(task, lbuf, len); ++ *ppos += count; ++ return error ? error : count; ++} ++ ++static struct file_operations proc_dpm_operations = { ++ .read = proc_dpm_read, ++ .write = proc_dpm_write, ++}; ++#endif ++ + /* SMP-safe */ + static struct dentry *proc_lookupfd(struct inode * dir, struct dentry * dentry, struct nameidata *nd) + { +@@ -1551,8 +1608,8 @@ + if (!(page = __get_free_page(GFP_KERNEL))) + return -ENOMEM; + +- length = security_getprocattr(task, +- (char*)file->f_dentry->d_name.name, ++ length = security_getprocattr(task, ++ (char*)file->f_dentry->d_name.name, + (void*)page, count); + if (length >= 0) + length = simple_read_from_buffer(buf, count, ppos, (char *)page, length); +@@ -1562,32 +1619,32 @@ + + static ssize_t proc_pid_attr_write(struct file * file, const char __user * buf, + size_t count, loff_t *ppos) +-{ ++{ + struct inode * inode = file->f_dentry->d_inode; +- char *page; +- ssize_t length; +- struct task_struct *task = proc_task(inode); ++ char *page; ++ ssize_t length; ++ struct task_struct *task = proc_task(inode); + +- if (count > PAGE_SIZE) +- count = PAGE_SIZE; ++ if (count > PAGE_SIZE) ++ count = PAGE_SIZE; + if (*ppos != 0) { + /* No partial writes. */ + return -EINVAL; + } +- page = (char*)__get_free_page(GFP_USER); +- if (!page) ++ page = (char*)__get_free_page(GFP_USER); ++ if (!page) + return -ENOMEM; +- length = -EFAULT; +- if (copy_from_user(page, buf, count)) ++ length = -EFAULT; ++ if (copy_from_user(page, buf, count)) + goto out; + +- length = security_setprocattr(task, +- (char*)file->f_dentry->d_name.name, ++ length = security_setprocattr(task, ++ (char*)file->f_dentry->d_name.name, + (void*)page, count); + out: + free_page((unsigned long) page); + return length; +-} ++} + + static struct file_operations proc_pid_attr_operations = { + .read = proc_pid_attr_read, +@@ -1603,7 +1660,7 @@ + static int get_tid_list(int index, unsigned int *tids, struct inode *dir); + + /* SMP-safe */ +-static struct dentry *proc_pident_lookup(struct inode *dir, ++static struct dentry *proc_pident_lookup(struct inode *dir, + struct dentry *dentry, + struct pid_entry *ents) + { +@@ -1787,6 +1844,12 @@ + inode->i_fop = &proc_loginuid_operations; + break; + #endif ++#ifdef CONFIG_DPM ++ case PROC_TGID_DPM: ++ inode->i_op = &proc_fd_inode_operations; ++ inode->i_fop = &proc_dpm_operations; ++ break; ++#endif + default: + printk("procfs: impossible type (%d)",p->type); + iput(inode); +@@ -1888,7 +1951,7 @@ + char tmp[30]; + sprintf(tmp, "%d", current->tgid); + return ERR_PTR(vfs_follow_link(nd,tmp)); +-} ++} + + static struct inode_operations proc_self_inode_operations = { + .readlink = proc_self_readlink, +@@ -1939,7 +2002,7 @@ + * + * Shrink the /proc directory that was used by the just killed thread. + */ +- ++ + void proc_pid_flush(struct dentry *proc_dentry) + { + might_sleep(); +Index: linux-2.6.16/include/asm-arm/dpm.h +=================================================================== +--- linux-2.6.16.orig/include/asm-arm/dpm.h 1970-01-01 00:00:00.000000000 +0000 ++++ linux-2.6.16/include/asm-arm/dpm.h 2006-04-11 06:34:11.000000000 +0000 +@@ -0,0 +1,30 @@ ++/* ++ * include/asm-arm/dpm.h Arch-dependent DPM defines for ARM ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ * ++ * Copyright (C) 2002, MontaVista Software <source@mvista.com> ++ * ++ * Based on include/asm-ppc/dpm.h by Robert Paulsen. Copyright (C) ++ * 2002, International Business Machines Corporation, All Rights ++ * Reserved. ++ * */ ++ ++#ifndef __ASM_DPM_H__ ++#define __ASM_DPM_H__ ++ ++#include <asm/arch/dpm.h> ++ ++#endif /* __ASM_DPM_H__ */ +Index: linux-2.6.16/include/asm-arm/system.h +=================================================================== +--- linux-2.6.16.orig/include/asm-arm/system.h 2006-03-20 05:53:29.000000000 +0000 ++++ linux-2.6.16/include/asm-arm/system.h 2006-04-11 06:34:11.000000000 +0000 +@@ -169,6 +169,7 @@ + #define switch_to(prev,next,last) \ + do { \ + last = __switch_to(prev,task_thread_info(prev), task_thread_info(next)); \ ++ dpm_set_os(current->dpm_state); \ + } while (0) + + /* +Index: linux-2.6.16/include/asm-i386/dpm-centrino.h +=================================================================== +--- linux-2.6.16.orig/include/asm-i386/dpm-centrino.h 1970-01-01 00:00:00.000000000 +0000 ++++ linux-2.6.16/include/asm-i386/dpm-centrino.h 2006-04-11 06:34:11.000000000 +0000 +@@ -0,0 +1,30 @@ ++/* ++ * include/asm-i386/dpm-centrino.h DPM defines for Intel Centrino ++ * ++ * 2003 (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. ++ */ ++ ++#ifndef __ASM_DPM_CENTRINO_H__ ++#define __ASM_DPM_CENTRINO_H__ ++ ++/* MD operating point parameters */ ++#define DPM_MD_CPU_FREQ 0 /* CPU freq */ ++#define DPM_MD_V 1 /* core voltage */ ++ ++#define DPM_PP_NBR 2 ++ ++#define DPM_PARAM_NAMES \ ++{ "cpu", "v" } ++ ++/* Instances of this structure define valid Innovator operating points for DPM. ++ Voltages are represented in mV, and frequencies are represented in KHz. */ ++ ++struct dpm_md_opt { ++ unsigned int v; /* Target voltage in mV */ ++ unsigned int cpu; /* CPU frequency in KHz */ ++}; ++ ++#endif /* __ASM_DPM_CENTRINO_H__ */ +Index: linux-2.6.16/include/asm-i386/dpm.h +=================================================================== +--- linux-2.6.16.orig/include/asm-i386/dpm.h 1970-01-01 00:00:00.000000000 +0000 ++++ linux-2.6.16/include/asm-i386/dpm.h 2006-04-11 06:35:40.000000000 +0000 +@@ -0,0 +1,86 @@ ++/* ++ * include/asm-i386/dpm.h Platform-dependent DPM defines for x86 ++ * ++ * 2003 (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. ++ */ ++ ++#ifndef __ASM_DPM_H__ ++#define __ASM_DPM_H__ ++ ++/* ++ * machine dependent operating state ++ * ++ * An operating state is a cpu execution state that has implications for power ++ * management. The DPM will select operating points based largely on the ++ * current operating state. ++ * ++ * DPM_STATES is the number of supported operating states. Valid operating ++ * states are from 0 to DPM_STATES-1 but when setting an operating state the ++ * kernel should only specify a state from the set of "base states" and should ++ * do so by name. During the context switch the new operating state is simply ++ * extracted from current->dpm_state. ++ * ++ * task states: ++ * ++ * APIs that reference task states use the range -(DPM_TASK_STATE_LIMIT + 1) ++ * through +DPM_TASK_STATE_LIMIT. This value is added to DPM_TASK_STATE to ++ * obtain the downward or upward adjusted task state value. The ++ * -(DPM_TASK_STATE_LIMIT + 1) value is interpreted specially, and equates to ++ * DPM_NO_STATE. ++ * ++ * Tasks inherit their task operating states across calls to ++ * fork(). DPM_TASK_STATE is the default operating state for all tasks, and is ++ * inherited from init. Tasks can change (or have changed) their tasks states ++ * using the DPM_SET_TASK_STATE variant of the sys_dpm() system call. */ ++ ++#define DPM_IDLE_TASK_STATE 0 ++#define DPM_IDLE_STATE 1 ++#define DPM_BASE_STATES 2 ++ ++#define DPM_TASK_STATE_LIMIT 4 ++#define DPM_TASK_STATE (DPM_BASE_STATES + DPM_TASK_STATE_LIMIT) ++#define DPM_STATES (DPM_TASK_STATE + DPM_TASK_STATE_LIMIT + 1) ++#define DPM_TASK_STATES (DPM_STATES - DPM_BASE_STATES) ++ ++#define DPM_STATE_NAMES \ ++{ "idle-task", "idle",\ ++ "task-4", "task-3", "task-2", "task-1",\ ++ "task", \ ++ "task+1", "task+2", "task+3", "task+4" \ ++} ++ ++#ifdef CONFIG_DPM_CENTRINO ++#include <asm/dpm-centrino.h> ++#endif ++ ++#ifndef __ASSEMBLER__ ++ ++#include <linux/types.h> ++#include <asm/timex.h> ++ ++#define dpm_time() get_cycles() ++ ++#define dpm_time_to_usec(ticks) ({ \ ++ unsigned long long quot = (unsigned long long) ticks * 10; \ ++ do_div(quot, (unsigned long) (cpu_khz / 100)); \ ++ quot; }) ++ ++/* Board-dependent routines. */ ++ ++struct dpm_opt; ++ ++struct dpm_bd { ++ int (*startup)(void); /* startup */ ++ void (*cleanup)(void); /* terminate */ ++ int (*init_opt)(struct dpm_opt *opt); /* init an opt */ ++ int (*get_opt)(struct dpm_opt *opt); /* get current opt */ ++ int (*set_opt)(struct dpm_md_opt *md_opt); /* set opt */ ++}; ++ ++extern struct dpm_bd dpm_bd; ++ ++#endif /* __ASSEMBLER__ */ ++#endif /* __ASM_DPM_H__ */ +Index: linux-2.6.16/include/linux/device.h +=================================================================== +--- linux-2.6.16.orig/include/linux/device.h 2006-03-20 05:53:29.000000000 +0000 ++++ linux-2.6.16/include/linux/device.h 2006-04-11 06:34:11.000000000 +0000 +@@ -336,6 +336,8 @@ + + struct list_head dma_pools; /* dma pools (if dma'ble) */ + ++ struct constraints *constraints; ++ + struct dma_coherent_mem *dma_mem; /* internal for coherent mem + override */ + +Index: linux-2.6.16/include/linux/dpm-trace.h +=================================================================== +--- linux-2.6.16.orig/include/linux/dpm-trace.h 1970-01-01 00:00:00.000000000 +0000 ++++ linux-2.6.16/include/linux/dpm-trace.h 2006-04-11 06:34:11.000000000 +0000 +@@ -0,0 +1,65 @@ ++/* ++ * include/linux/dpm.h DPM policy management ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ * ++ * Copyright (C) 2002, International Business Machines Corporation ++ * All Rights Reserved ++ * ++ * Robert Paulsen ++ * IBM Linux Technology Center ++ * rpaulsen@us.ibm.com ++ * August, 2002 ++ * ++ */ ++ ++#ifndef __DPM_TRACE_H_ ++#define __DPM_TRACE_H_ ++ ++#include <linux/config.h> ++ ++#ifdef CONFIG_DPM_TRACE ++ ++#define DPM_TRACE_SET_OPT_ASYNC 0x00000001 ++#define DPM_TRACE_SET_OPT_SYNC 0x00000002 ++#define DPM_TRACE_RESYNC 0x00000004 ++#define DPM_TRACE_UNLOCK 0x00000008 ++#define DPM_TRACE_SET_OS 0x00000010 ++#define DPM_TRACE_SET_POLICY 0x00000020 ++#define DPM_TRACE_START 0x00000040 ++#define DPM_TRACE_STOP 0x00000080 ++#define DPM_TRACE_SET_TASK_STATE 0x00000100 ++ ++#define DPM_TRACE_ALL 0x000001ff ++ ++void dpm_trace(unsigned event, ...); ++void dpm_trace_start(unsigned events); ++void dpm_trace_stop(void); ++void dpm_trace_reset(void); ++ ++int ++read_proc_dpm_trace(char *page, char **start, off_t offset, ++ int count, int *eof, void *data); ++int ++write_proc_dpm_trace(struct file *file, const char *buffer, ++ unsigned long count, void *data); ++ ++#else ++ ++#define dpm_trace(args...) do {} while (0) ++ ++#endif /* CONFIG_DPM_TRACE */ ++ ++#endif /*__DPM_TRACE_H_*/ +Index: linux-2.6.16/include/linux/dpm.h +=================================================================== +--- linux-2.6.16.orig/include/linux/dpm.h 1970-01-01 00:00:00.000000000 +0000 ++++ linux-2.6.16/include/linux/dpm.h 2006-04-11 06:35:40.000000000 +0000 +@@ -0,0 +1,409 @@ ++/* ++ * include/linux/dpm.h DPM policy management ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ * ++ * Copyright (C) 2002, International Business Machines Corporation ++ * All Rights Reserved ++ * ++ * Robert Paulsen ++ * IBM Linux Technology Center ++ * rpaulsen@us.ibm.com ++ * August, 2002 ++ * ++ */ ++ ++#ifndef __DPM_H__ ++#define __DPM_H__ ++ ++#include <linux/config.h> ++#include <linux/device.h> ++ ++#define DPM_NO_STATE -1 ++ ++#ifndef CONFIG_DPM ++ ++/* The above and following constants must always be defined for the ++ benefit of the init task and system tasks, although they are ++ otherwise ignored if DPM is not configured. */ ++ ++#define DPM_TASK_STATE 0 ++#define dpm_set_os(task_state) do {} while (0); ++ ++#else /* CONFIG_DPM */ ++ ++#include <asm/dpm.h> ++#include <linux/errno.h> ++#include <linux/types.h> ++#include <linux/unistd.h> ++#include <linux/notifier.h> ++ ++/* max size of DPM names */ ++enum {DPM_NAME_SIZE=256}; ++ ++#include <linux/dpm-trace.h> ++#include <linux/list.h> ++#include <asm/semaphore.h> ++#include <asm/atomic.h> ++ ++/* statistics */ ++struct dpm_stats { ++ unsigned long count; ++ unsigned long long total_time; ++ unsigned long long start_time; ++}; ++ ++extern struct dpm_stats dpm_state_stats[DPM_STATES]; ++ ++/* update statistics structures */ ++extern unsigned long long dpm_update_stats(struct dpm_stats *new, ++ struct dpm_stats *old); ++ ++typedef int dpm_state_t; ++typedef int dpm_md_pp_t; ++ ++/* A table of processor-dependent routines, must be initialized by ++ platform-dependent boot code. None of the entries (that will actually be ++ called) are allowed to be NULL if DPM is enabled. */ ++ ++struct dpm_opt; ++ ++struct dpm_md { ++ int (*init_opt)(struct dpm_opt *opt); ++ int (*set_opt)(struct dpm_opt *cur, struct dpm_opt *new); ++ int (*get_opt)(struct dpm_opt *opt); ++ int (*check_constraint)(struct constraint_param *param, ++ struct dpm_opt *opt); ++ void (*idle)(void); ++ void (*startup)(void); ++ void (*cleanup)(void); ++}; ++ ++ ++/***************************************************************************** ++ * Search a list looking for a named entity. ++ * A pointer to the found element is put in the variable named by the ++ * "answer" argument (or it is set to zero if not found). ++ * The structure's type name is given by the "element_type" argument. ++ * The name being looked for is given by the "find_me" argument. ++ * The name of the stand-alone list_head is given by the "list_name" argument. ++ * Assumes the proper semaphore is held. ++ * Assumes the structure's list_head is named "list". ++ * Assumes the structure's name is in a field called "name" ++ *****************************************************************************/ ++#define list_find(answer,find_me,list_name,element_type) \ ++ do { \ ++ element_type *elm; \ ++ struct list_head *scan; \ ++ (answer)=0; \ ++ for(scan=list_name.next;scan!=&list_name; \ ++ scan=scan->next) { \ ++ elm=list_entry(scan,element_type,list); \ ++ if (strncmp((find_me),elm->name, \ ++ DPM_NAME_SIZE)==0) { \ ++ (answer)=elm; \ ++ break; \ ++ } \ ++ } \ ++ } while(0) ++ ++/* internal representation of an operating point */ ++ ++#define DPM_OP_FORCE 0x0001 ++#define DPM_OP_NOP 0x0002 ++ ++struct dpm_opt { ++ char *name; /* name */ ++ struct list_head list; /* all installed op points */ ++ dpm_md_pp_t pp[DPM_PP_NBR]; /* initialization params */ ++ struct dpm_md_opt md_opt; /* machine dependent part */ ++ int constrained; /* is this opt constrained? */ ++ struct kobject kobj; /* kobject */ ++ struct dpm_stats stats; /* statistics */ ++ int flags; ++}; ++ ++/* internal representation of a class of op points (to be mapped to an ++ * operating state */ ++struct dpm_class { ++ char *name; /* name */ ++ struct list_head list; /* all installed classes */ ++ unsigned nops; /* nbr ops in this class */ ++ struct dpm_opt **ops; /* the ops in this class */ ++ struct kobject kobj; /* kobject */ ++ struct dpm_stats stats; /* statistics */ ++}; ++ ++/* ++ * temporary support for policies to map operating points to either ++ * operating pts or classes. Only one field allowed to be set. ++ */ ++ ++struct dpm_classopt { ++ struct dpm_opt *opt; ++ struct dpm_class *class; ++}; ++ ++/* internal representation of an installed power policy */ ++struct dpm_policy { ++ char *name; /* name */ ++ struct list_head list; /* all installed policies */ ++ struct dpm_classopt classopt[DPM_STATES]; /* classes/op pts */ ++ struct kobject kobj; /* kobject */ ++ struct dpm_stats stats; /* statistics */ ++}; ++ ++/* ++ * internal use utility functions for use by DPM ++ */ ++ ++/* DPM semaphore locking. To simplify future expansion, don't 'down' _dpm_lock ++ directly. Also, _dpm_lock must be 'up'ed only by dpm_unlock(). */ ++ ++extern struct semaphore _dpm_lock; ++ ++static inline void ++dpm_lock(void) ++{ ++ down(&_dpm_lock); ++} ++ ++static inline int ++dpm_lock_interruptible(void) ++{ ++ if (down_interruptible(&_dpm_lock)) ++ return -ERESTARTSYS; ++ return 0; ++} ++ ++static inline int ++dpm_trylock(void) ++{ ++ if (down_trylock(&_dpm_lock)) ++ return -EBUSY; ++ return 0; ++} ++ ++void dpm_unlock(void); ++void dpm_idle(void); ++ ++/* set operating state */ ++void dpm_set_os(dpm_state_t state); ++ ++/* ++ * names of DPM stuff for userspace interfaces ++ */ ++ ++extern char *dpm_state_names[DPM_STATES]; ++extern char *dpm_param_names[DPM_PP_NBR]; ++ ++/* initialize/terminate the DPM */ ++int dynamicpower_init(void); ++int dynamicpower_terminate(void); ++ ++/* (temporarily) disable the DPM */ ++int dynamicpower_disable(void); ++ ++/* re-enable the DPM */ ++int dynamicpower_enable(void); ++ ++/* suspend/resume DPM across a system shutdown */ ++int dynamicpm_suspend(void); ++void dynamicpm_resume(void); ++ ++/* create operating point */ ++int dpm_create_opt(const char *name, const dpm_md_pp_t *pp, int npp); ++ ++/* create class of operating points */ ++int dpm_create_class(const char *name, char **op_names, unsigned nops); ++ ++/* create policy */ ++int dpm_create_policy(const char *name, char **opt_names, int nopts); ++int dpm_map_policy_state(struct dpm_policy *policy, int state, char *classopt); ++ ++/* destroy policy */ ++int dpm_destroy_policy(const char *name); ++ ++/* activate a power policy */ ++int dpm_set_policy(const char *name); ++ ++/* get name of active power policy */ ++int dpm_get_policy(char *name); ++ ++/* set a raw operating state */ ++int dpm_set_op_state(const char *name); ++int dpm_set_opt(struct dpm_opt *opt, unsigned flags); ++ ++/* choose unconstrained operating point from policy */ ++extern struct dpm_opt *dpm_choose_opt(struct dpm_policy *policy, int state); ++ ++ ++/* constraints */ ++int dpm_check_constraints(struct dpm_opt *opt); ++int dpm_default_check_constraint(struct constraint_param *param, ++ struct dpm_opt *opt); ++int dpm_show_opconstraints(struct dpm_opt *opt, char * buf); ++ ++/* driver scale callbacks */ ++void dpm_driver_scale(int level, struct dpm_opt *newop); ++void dpm_register_scale(struct notifier_block *nb, int level); ++void dpm_unregister_scale(struct notifier_block *nb, int level); ++ ++/* utils */ ++extern void dpm_udelay(unsigned uS); ++extern void dpm_udelay_from(u64 start, unsigned uS); ++extern unsigned long dpm_compute_lpj(unsigned long ref, u_int div, u_int mult); ++ ++/* ++ * sysfs interface ++ */ ++ ++extern void dpm_sysfs_new_policy(struct dpm_policy *policy); ++extern void dpm_sysfs_destroy_policy(struct dpm_policy *policy); ++extern void dpm_sysfs_new_class(struct dpm_class *class); ++extern void dpm_sysfs_destroy_class(struct dpm_class *class); ++extern void dpm_sysfs_new_op(struct dpm_opt *opt); ++extern void dpm_sysfs_destroy_op(struct dpm_opt *opt); ++ ++extern int proc_pid_dpm_read(struct task_struct*,char*); ++ ++ ++/* ++ * global data for power management system ++ */ ++ ++/* curently installed policies, classes and operating points */ ++extern struct list_head dpm_policies; ++extern struct list_head dpm_classes; ++extern struct list_head dpm_opts; ++extern struct semaphore dpm_policy_sem; ++extern spinlock_t dpm_policy_lock; ++ ++/* the currently active policy, class, state, point */ ++extern struct dpm_policy *dpm_active_policy; ++extern struct dpm_class *dpm_active_class; ++extern dpm_state_t dpm_active_state; ++extern struct dpm_opt *dpm_active_opt; ++ ++/* is DPM initialized and enabled? */ ++extern int dpm_initialized; ++extern int dpm_enabled; ++ ++extern inline void ++dpm_quick_enter_state(int new_state) ++{ ++#ifdef CONFIG_DPM_STATS ++ dpm_update_stats(new_state != DPM_NO_STATE ? ++ &dpm_state_stats[new_state] : NULL, ++ dpm_active_state != DPM_NO_STATE ? ++ &dpm_state_stats[dpm_active_state] : NULL); ++#endif ++ ++ dpm_active_state = new_state; ++} ++ ++/* Flags for dpm_set_opt(). By default, dpm_set_op() is guaranteed not ++ to block the caller, and will arrange to complete asynchronously if ++ necessary. ++ ++ DPM_SYNC The operating point is guaranteed to be set when the call ++ returns. The call may block. ++ ++ DPM_UNLOCK The caller requires dpm_md_set_opt() to unlock the DPM system ++ once the operating point is set. ++*/ ++ ++#define DPM_SYNC 0x01 ++#define DPM_UNLOCK 0x02 ++ ++/* ++ * Common machine-dependent and board-dependent function wrappers. ++ */ ++ ++extern struct dpm_md dpm_md; ++ ++static inline void ++dpm_md_startup(void) ++{ ++ if (dpm_md.startup) ++ dpm_md.startup(); ++} ++ ++ ++static inline void ++dpm_md_cleanup(void) ++{ ++ if (dpm_md.cleanup) ++ dpm_md.cleanup(); ++} ++ ++ ++static inline void ++dpm_md_idle(void) ++{ ++ if (dpm_md.idle) ++ dpm_md.idle(); ++} ++ ++ ++/* Machine-dependent operating point creating/query/setting */ ++ ++ ++static inline int ++dpm_md_init_opt(struct dpm_opt *opt) ++{ ++ if (dpm_md.init_opt) ++ return dpm_md.init_opt(opt); ++ return 0; ++} ++ ++static inline int ++dpm_md_set_opt(struct dpm_opt *cur, struct dpm_opt *new) ++{ ++ if (dpm_md.set_opt) { ++ return dpm_md.set_opt(cur, new); ++ } ++ return 0; ++} ++ ++static inline int ++dpm_md_get_opt(struct dpm_opt *opt) ++{ ++ if (dpm_md.get_opt) ++ return dpm_md.get_opt(opt); ++ return 0; ++} ++ ++static inline int ++dpm_md_check_constraint(struct constraint_param *param, struct dpm_opt *opt) ++{ ++ return dpm_md.check_constraint ? ++ dpm_md.check_constraint(param, opt) : 1; ++} ++ ++/* ++ * Helper functions ++ */ ++ ++static inline char * ++dpm_classopt_name(struct dpm_policy *policy, int state) ++{ ++ return policy->classopt[state].opt ? ++ policy->classopt[state].opt->name : ++ policy->classopt[state].class->name; ++} ++ ++#endif /* CONFIG_DPM */ ++#endif /*__DPM_H__*/ +Index: linux-2.6.16/include/linux/init_task.h +=================================================================== +--- linux-2.6.16.orig/include/linux/init_task.h 2006-03-20 05:53:29.000000000 +0000 ++++ linux-2.6.16/include/linux/init_task.h 2006-04-11 06:34:11.000000000 +0000 +@@ -1,6 +1,7 @@ + #ifndef _LINUX__INIT_TASK_H + #define _LINUX__INIT_TASK_H + ++#include <linux/dpm.h> + #include <linux/file.h> + #include <linux/rcupdate.h> + +@@ -116,6 +117,7 @@ + .list = LIST_HEAD_INIT(tsk.pending.list), \ + .signal = {{0}}}, \ + .blocked = {{0}}, \ ++ .dpm_state = DPM_TASK_STATE, \ + .alloc_lock = SPIN_LOCK_UNLOCKED, \ + .proc_lock = SPIN_LOCK_UNLOCKED, \ + .journal_info = NULL, \ +Index: linux-2.6.16/include/linux/pm.h +=================================================================== +--- linux-2.6.16.orig/include/linux/pm.h 2006-03-20 05:53:29.000000000 +0000 ++++ linux-2.6.16/include/linux/pm.h 2006-04-11 06:35:28.000000000 +0000 +@@ -131,6 +131,34 @@ + extern struct pm_ops *pm_ops; + extern int pm_suspend(suspend_state_t state); + ++struct device; ++ ++struct constraint_param { ++ int id; ++ int min; ++ int max; ++}; ++ ++#define DPM_CONSTRAINT_PARAMS_MAX 20 ++ ++struct constraints { ++ int asserted; ++ int count; ++ int violations; ++ struct constraint_param param[DPM_CONSTRAINT_PARAMS_MAX]; ++ struct list_head entry; ++}; ++ ++enum { ++ SCALE_PRECHANGE, ++ SCALE_POSTCHANGE, ++ SCALE_MAX ++}; ++ ++extern void assert_constraints(struct constraints *); ++extern void deassert_constraints(struct constraints *); ++extern void power_event(char *eventstr); ++extern void device_power_event(struct device * dev, char *eventstr); + + /* + * Device power management +Index: linux-2.6.16/include/linux/sched.h +=================================================================== +--- linux-2.6.16.orig/include/linux/sched.h 2006-03-20 05:53:29.000000000 +0000 ++++ linux-2.6.16/include/linux/sched.h 2006-04-11 06:34:11.000000000 +0000 +@@ -860,6 +860,7 @@ + u64 acct_vm_mem1; /* accumulated virtual memory usage */ + clock_t acct_stimexpd; /* clock_t-converted stime since last update */ + #endif ++ int dpm_state; /* DPM operating state to use for this task */ + #ifdef CONFIG_NUMA + struct mempolicy *mempolicy; + short il_next; +Index: linux-2.6.16/kernel/sched.c +=================================================================== +--- linux-2.6.16.orig/kernel/sched.c 2006-03-20 05:53:29.000000000 +0000 ++++ linux-2.6.16/kernel/sched.c 2006-04-11 06:34:11.000000000 +0000 +@@ -49,6 +49,8 @@ + #include <linux/syscalls.h> + #include <linux/times.h> + #include <linux/acct.h> ++#include <linux/dpm.h> ++ + #include <asm/tlb.h> + + #include <asm/unistd.h> +Index: linux-2.6.16/kernel/softirq.c +=================================================================== +--- linux-2.6.16.orig/kernel/softirq.c 2006-03-20 05:53:29.000000000 +0000 ++++ linux-2.6.16/kernel/softirq.c 2006-04-11 06:34:11.000000000 +0000 +@@ -45,6 +45,8 @@ + + static DEFINE_PER_CPU(struct task_struct *, ksoftirqd); + ++#include <linux/dpm.h> ++ + /* + * we cannot loop indefinitely here to avoid userspace starvation, + * but we also don't want to introduce a worst case 1/HZ latency +@@ -352,6 +354,11 @@ + set_user_nice(current, 19); + current->flags |= PF_NOFREEZE; + ++#ifdef CONFIG_DPM ++ /* Identify as a system task for DPM purposes */ ++ current->dpm_state = DPM_NO_STATE; ++#endif ++ + set_current_state(TASK_INTERRUPTIBLE); + + while (!kthread_should_stop()) { +Index: linux-2.6.16/kernel/workqueue.c +=================================================================== +--- linux-2.6.16.orig/kernel/workqueue.c 2006-03-20 05:53:29.000000000 +0000 ++++ linux-2.6.16/kernel/workqueue.c 2006-04-11 06:34:11.000000000 +0000 +@@ -23,6 +23,7 @@ + #include <linux/signal.h> + #include <linux/completion.h> + #include <linux/workqueue.h> ++#include <linux/dpm.h> + #include <linux/slab.h> + #include <linux/cpu.h> + #include <linux/notifier.h> +@@ -195,6 +196,11 @@ + + set_user_nice(current, -5); + ++#ifdef CONFIG_DPM ++ /* Identify as a system task for DPM purposes */ ++ current->dpm_state = DPM_NO_STATE; ++#endif ++ + /* Block and flush all signals */ + sigfillset(&blocked); + sigprocmask(SIG_BLOCK, &blocked, NULL); |