diff options
Diffstat (limited to 'arch/x86/kernel/apm_32.c')
| -rw-r--r-- | arch/x86/kernel/apm_32.c | 174 | 
1 files changed, 80 insertions, 94 deletions
diff --git a/arch/x86/kernel/apm_32.c b/arch/x86/kernel/apm_32.c index 0e4f24c2a74..58487445141 100644 --- a/arch/x86/kernel/apm_32.c +++ b/arch/x86/kernel/apm_32.c @@ -66,7 +66,7 @@   *    1.5: Fix segment register reloading (in case of bad segments saved   *         across BIOS call).   *         Stephen Rothwell - *    1.6: Cope with complier/assembler differences. + *    1.6: Cope with compiler/assembler differences.   *         Only try to turn off the first display device.   *         Fix OOPS at power off with no APM BIOS by Jan Echternach   *                   <echter@informatik.uni-rostock.de> @@ -201,6 +201,8 @@   *    http://www.microsoft.com/whdc/archive/amp_12.mspx]   */ +#define pr_fmt(fmt) "apm: " fmt +  #include <linux/module.h>  #include <linux/poll.h> @@ -227,11 +229,13 @@  #include <linux/suspend.h>  #include <linux/kthread.h>  #include <linux/jiffies.h> +#include <linux/acpi.h> +#include <linux/syscore_ops.h> +#include <linux/i8253.h> +#include <linux/cpuidle.h> -#include <asm/system.h>  #include <asm/uaccess.h>  #include <asm/desc.h> -#include <asm/i8253.h>  #include <asm/olpc.h>  #include <asm/paravirt.h>  #include <asm/reboot.h> @@ -247,8 +251,6 @@ extern int (*console_blank_hook)(int);  #define	APM_MINOR_DEV	134  /* - * See Documentation/Config.help for the configuration options. - *   * Various options can be changed at boot time as follows:   * (We allow underscores for compatibility with the modules code)   *	apm=on/off			enable/disable APM @@ -365,38 +367,59 @@ struct apm_user {  #endif  #define DEFAULT_IDLE_PERIOD	(100 / 3) +static int apm_cpu_idle(struct cpuidle_device *dev, +			struct cpuidle_driver *drv, int index); + +static struct cpuidle_driver apm_idle_driver = { +	.name = "apm_idle", +	.owner = THIS_MODULE, +	.states = { +		{ /* entry 0 is for polling */ }, +		{ /* entry 1 is for APM idle */ +			.name = "APM", +			.desc = "APM idle", +			.flags = CPUIDLE_FLAG_TIME_VALID, +			.exit_latency = 250,	/* WAG */ +			.target_residency = 500,	/* WAG */ +			.enter = &apm_cpu_idle +		}, +	}, +	.state_count = 2, +}; + +static struct cpuidle_device apm_cpuidle_device; +  /*   * Local variables   */ -static struct { +__visible struct {  	unsigned long	offset;  	unsigned short	segment;  } apm_bios_entry;  static int clock_slowed;  static int idle_threshold __read_mostly = DEFAULT_IDLE_THRESHOLD;  static int idle_period __read_mostly = DEFAULT_IDLE_PERIOD; -static int set_pm_idle;  static int suspends_pending;  static int standbys_pending;  static int ignore_sys_suspend;  static int ignore_normal_resume;  static int bounce_interval __read_mostly = DEFAULT_BOUNCE_INTERVAL; -static int debug __read_mostly; -static int smp __read_mostly; +static bool debug __read_mostly; +static bool smp __read_mostly;  static int apm_disabled = -1;  #ifdef CONFIG_SMP -static int power_off; +static bool power_off;  #else -static int power_off = 1; +static bool power_off = 1;  #endif -static int realmode_power_off; +static bool realmode_power_off;  #ifdef CONFIG_APM_ALLOW_INTS -static int allow_ints = 1; +static bool allow_ints = 1;  #else -static int allow_ints; +static bool allow_ints;  #endif -static int broken_psr; +static bool broken_psr;  static DECLARE_WAIT_QUEUE_HEAD(apm_waitqueue);  static DECLARE_WAIT_QUEUE_HEAD(apm_suspend_waitqueue); @@ -485,11 +508,11 @@ static void apm_error(char *str, int err)  		if (error_table[i].key == err)  			break;  	if (i < ERROR_COUNT) -		printk(KERN_NOTICE "apm: %s: %s\n", str, error_table[i].msg); +		pr_notice("%s: %s\n", str, error_table[i].msg);  	else if (err < 0) -		printk(KERN_NOTICE "apm: %s: linux error code %i\n", str, err); +		pr_notice("%s: linux error code %i\n", str, err);  	else -		printk(KERN_NOTICE "apm: %s: unknown error code %#2.2x\n", +		pr_notice("%s: unknown error code %#2.2x\n",  		       str, err);  } @@ -818,24 +841,12 @@ static int apm_do_idle(void)  	u32 eax;  	u8 ret = 0;  	int idled = 0; -	int polling;  	int err = 0; -	polling = !!(current_thread_info()->status & TS_POLLING); -	if (polling) { -		current_thread_info()->status &= ~TS_POLLING; -		/* -		 * TS_POLLING-cleared state must be visible before we -		 * test NEED_RESCHED: -		 */ -		smp_mb(); -	}  	if (!need_resched()) {  		idled = 1;  		ret = apm_bios_call_simple(APM_FUNC_IDLE, 0, 0, &eax, &err);  	} -	if (polling) -		current_thread_info()->status |= TS_POLLING;  	if (!idled)  		return 0; @@ -882,8 +893,6 @@ static void apm_do_busy(void)  #define IDLE_CALC_LIMIT	(HZ * 100)  #define IDLE_LEAKY_MAX	16 -static void (*original_pm_idle)(void) __read_mostly; -  /**   * apm_cpu_idle		-	cpu idling for APM capable Linux   * @@ -892,34 +901,36 @@ static void (*original_pm_idle)(void) __read_mostly;   * Furthermore it calls the system default idle routine.   */ -static void apm_cpu_idle(void) +static int apm_cpu_idle(struct cpuidle_device *dev, +	struct cpuidle_driver *drv, int index)  {  	static int use_apm_idle; /* = 0 */  	static unsigned int last_jiffies; /* = 0 */  	static unsigned int last_stime; /* = 0 */ +	cputime_t stime;  	int apm_idle_done = 0;  	unsigned int jiffies_since_last_check = jiffies - last_jiffies;  	unsigned int bucket;  recalc: +	task_cputime(current, NULL, &stime);  	if (jiffies_since_last_check > IDLE_CALC_LIMIT) {  		use_apm_idle = 0; -		last_jiffies = jiffies; -		last_stime = current->stime;  	} else if (jiffies_since_last_check > idle_period) {  		unsigned int idle_percentage; -		idle_percentage = current->stime - last_stime; +		idle_percentage = stime - last_stime;  		idle_percentage *= 100;  		idle_percentage /= jiffies_since_last_check;  		use_apm_idle = (idle_percentage > idle_threshold);  		if (apm_info.forbid_idle)  			use_apm_idle = 0; -		last_jiffies = jiffies; -		last_stime = current->stime;  	} +	last_jiffies = jiffies; +	last_stime = stime; +  	bucket = IDLE_LEAKY_MAX;  	while (!need_resched()) { @@ -947,10 +958,7 @@ recalc:  				break;  			}  		} -		if (original_pm_idle) -			original_pm_idle(); -		else -			default_idle(); +		default_idle();  		local_irq_disable();  		jiffies_since_last_check = jiffies - last_jiffies;  		if (jiffies_since_last_check > idle_period) @@ -960,7 +968,7 @@ recalc:  	if (apm_idle_done)  		apm_do_busy(); -	local_irq_enable(); +	return index;  }  /** @@ -975,20 +983,10 @@ recalc:  static void apm_power_off(void)  { -	unsigned char po_bios_call[] = { -		0xb8, 0x00, 0x10,	/* movw  $0x1000,ax  */ -		0x8e, 0xd0,		/* movw  ax,ss       */ -		0xbc, 0x00, 0xf0,	/* movw  $0xf000,sp  */ -		0xb8, 0x07, 0x53,	/* movw  $0x5307,ax  */ -		0xbb, 0x01, 0x00,	/* movw  $0x0001,bx  */ -		0xb9, 0x03, 0x00,	/* movw  $0x0003,cx  */ -		0xcd, 0x15		/* int   $0x15       */ -	}; -  	/* Some bioses don't like being called from CPU != 0 */  	if (apm_info.realmode_power_off) {  		set_cpus_allowed_ptr(current, cpumask_of(0)); -		machine_real_restart(po_bios_call, sizeof(po_bios_call)); +		machine_real_restart(MRR_APM);  	} else {  		(void)set_system_power_state(APM_STATE_OFF);  	} @@ -1193,7 +1191,7 @@ static void queue_event(apm_event_t event, struct apm_user *sender)  			static int notified;  			if (notified++ == 0) -			    printk(KERN_ERR "apm: an event queue overflowed\n"); +				pr_err("an event queue overflowed\n");  			if (++as->event_tail >= APM_MAX_EVENTS)  				as->event_tail = 0;  		} @@ -1226,11 +1224,11 @@ static void reinit_timer(void)  	raw_spin_lock_irqsave(&i8253_lock, flags);  	/* set the clock to HZ */ -	outb_pit(0x34, PIT_MODE);		/* binary, mode 2, LSB/MSB, ch 0 */ +	outb_p(0x34, PIT_MODE);		/* binary, mode 2, LSB/MSB, ch 0 */  	udelay(10); -	outb_pit(LATCH & 0xff, PIT_CH0);	/* LSB */ +	outb_p(LATCH & 0xff, PIT_CH0);	/* LSB */  	udelay(10); -	outb_pit(LATCH >> 8, PIT_CH0);	/* MSB */ +	outb_p(LATCH >> 8, PIT_CH0);	/* MSB */  	udelay(10);  	raw_spin_unlock_irqrestore(&i8253_lock, flags);  #endif @@ -1242,11 +1240,10 @@ static int suspend(int vetoable)  	struct apm_user	*as;  	dpm_suspend_start(PMSG_SUSPEND); - -	dpm_suspend_noirq(PMSG_SUSPEND); +	dpm_suspend_end(PMSG_SUSPEND);  	local_irq_disable(); -	sysdev_suspend(PMSG_SUSPEND); +	syscore_suspend();  	local_irq_enable(); @@ -1264,12 +1261,12 @@ static int suspend(int vetoable)  		apm_error("suspend", err);  	err = (err == APM_SUCCESS) ? 0 : -EIO; -	sysdev_resume(); +	syscore_resume();  	local_irq_enable(); -	dpm_resume_noirq(PMSG_RESUME); - +	dpm_resume_start(PMSG_RESUME);  	dpm_resume_end(PMSG_RESUME); +  	queue_event(APM_NORMAL_RESUME, NULL);  	spin_lock(&user_list_lock);  	for (as = user_list; as != NULL; as = as->next) { @@ -1285,10 +1282,10 @@ static void standby(void)  {  	int err; -	dpm_suspend_noirq(PMSG_SUSPEND); +	dpm_suspend_end(PMSG_SUSPEND);  	local_irq_disable(); -	sysdev_suspend(PMSG_SUSPEND); +	syscore_suspend();  	local_irq_enable();  	err = set_system_power_state(APM_STATE_STANDBY); @@ -1296,10 +1293,10 @@ static void standby(void)  		apm_error("standby", err);  	local_irq_disable(); -	sysdev_resume(); +	syscore_resume();  	local_irq_enable(); -	dpm_resume_noirq(PMSG_RESUME); +	dpm_resume_start(PMSG_RESUME);  }  static apm_event_t get_event(void) @@ -1457,7 +1454,7 @@ static void apm_mainloop(void)  static int check_apm_user(struct apm_user *as, const char *func)  {  	if (as == NULL || as->magic != APM_BIOS_MAGIC) { -		printk(KERN_ERR "apm: %s passed bad filp\n", func); +		pr_err("%s passed bad filp\n", func);  		return 1;  	}  	return 0; @@ -1596,7 +1593,7 @@ static int do_release(struct inode *inode, struct file *filp)  		     as1 = as1->next)  			;  		if (as1 == NULL) -			printk(KERN_ERR "apm: filp not in user list\n"); +			pr_err("filp not in user list\n");  		else  			as1->next = as->next;  	} @@ -1610,11 +1607,9 @@ static int do_open(struct inode *inode, struct file *filp)  	struct apm_user *as;  	as = kmalloc(sizeof(*as), GFP_KERNEL); -	if (as == NULL) { -		printk(KERN_ERR "apm: cannot allocate struct of size %d bytes\n", -		       sizeof(*as)); +	if (as == NULL)  		return -ENOMEM; -	} +  	as->magic = APM_BIOS_MAGIC;  	as->event_tail = as->event_head = 0;  	as->suspends_pending = as->standbys_pending = 0; @@ -2323,20 +2318,19 @@ static int __init apm_init(void)  	}  	if (apm_info.disabled) { -		printk(KERN_NOTICE "apm: disabled on user request.\n"); +		pr_notice("disabled on user request.\n");  		return -ENODEV;  	}  	if ((num_online_cpus() > 1) && !power_off && !smp) { -		printk(KERN_NOTICE "apm: disabled - APM is not SMP safe.\n"); +		pr_notice("disabled - APM is not SMP safe.\n");  		apm_info.disabled = 1;  		return -ENODEV;  	} -	if (pm_flags & PM_ACPI) { -		printk(KERN_NOTICE "apm: overridden by ACPI.\n"); +	if (!acpi_disabled) { +		pr_notice("overridden by ACPI.\n");  		apm_info.disabled = 1;  		return -ENODEV;  	} -	pm_flags |= PM_APM;  	/*  	 * Set up the long jump entry point to the APM BIOS, which is called @@ -2367,8 +2361,7 @@ static int __init apm_init(void)  	kapmd_task = kthread_create(apm, NULL, "kapmd");  	if (IS_ERR(kapmd_task)) { -		printk(KERN_ERR "apm: disabled - Unable to start kernel " -				"thread.\n"); +		pr_err("disabled - Unable to start kernel thread\n");  		err = PTR_ERR(kapmd_task);  		kapmd_task = NULL;  		remove_proc_entry("apm", NULL); @@ -2393,9 +2386,9 @@ static int __init apm_init(void)  	if (HZ != 100)  		idle_period = (idle_period * HZ) / 100;  	if (idle_threshold < 100) { -		original_pm_idle = pm_idle; -		pm_idle  = apm_cpu_idle; -		set_pm_idle = 1; +		if (!cpuidle_register_driver(&apm_idle_driver)) +			if (cpuidle_register_device(&apm_cpuidle_device)) +				cpuidle_unregister_driver(&apm_idle_driver);  	}  	return 0; @@ -2405,15 +2398,9 @@ static void __exit apm_exit(void)  {  	int error; -	if (set_pm_idle) { -		pm_idle = original_pm_idle; -		/* -		 * We are about to unload the current idle thread pm callback -		 * (pm_idle), Wait for all processors to update cached/local -		 * copies of pm_idle before proceeding. -		 */ -		cpu_idle_wait(); -	} +	cpuidle_unregister_device(&apm_cpuidle_device); +	cpuidle_unregister_driver(&apm_idle_driver); +  	if (((apm_info.bios.flags & APM_BIOS_DISENGAGED) == 0)  	    && (apm_info.connection_version > 0x0100)) {  		error = apm_engage_power_management(APM_DEVICE_ALL, 0); @@ -2428,7 +2415,6 @@ static void __exit apm_exit(void)  		kthread_stop(kapmd_task);  		kapmd_task = NULL;  	} -	pm_flags &= ~PM_APM;  }  module_init(apm_init);  | 
