blob: e32f6c37db9b273145db8f71859e3650d6d405fa [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/* -*- linux-c -*-
2 * APM BIOS driver for Linux
3 * Copyright 1994-2001 Stephen Rothwell (sfr@canb.auug.org.au)
4 *
5 * Initial development of this driver was funded by NEC Australia P/L
6 * and NEC Corporation
7 *
8 * This program is free software; you can redistribute it and/or modify it
9 * under the terms of the GNU General Public License as published by the
10 * Free Software Foundation; either version 2, or (at your option) any
11 * later version.
12 *
13 * This program is distributed in the hope that it will be useful, but
14 * WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * General Public License for more details.
17 *
18 * October 1995, Rik Faith (faith@cs.unc.edu):
19 * Minor enhancements and updates (to the patch set) for 1.3.x
20 * Documentation
21 * January 1996, Rik Faith (faith@cs.unc.edu):
22 * Make /proc/apm easy to format (bump driver version)
23 * March 1996, Rik Faith (faith@cs.unc.edu):
24 * Prohibit APM BIOS calls unless apm_enabled.
25 * (Thanks to Ulrich Windl <Ulrich.Windl@rz.uni-regensburg.de>)
26 * April 1996, Stephen Rothwell (sfr@canb.auug.org.au)
27 * Version 1.0 and 1.1
28 * May 1996, Version 1.2
29 * Feb 1998, Version 1.3
30 * Feb 1998, Version 1.4
31 * Aug 1998, Version 1.5
32 * Sep 1998, Version 1.6
33 * Nov 1998, Version 1.7
34 * Jan 1999, Version 1.8
35 * Jan 1999, Version 1.9
36 * Oct 1999, Version 1.10
37 * Nov 1999, Version 1.11
38 * Jan 2000, Version 1.12
39 * Feb 2000, Version 1.13
40 * Nov 2000, Version 1.14
41 * Oct 2001, Version 1.15
42 * Jan 2002, Version 1.16
43 * Oct 2002, Version 1.16ac
44 *
45 * History:
46 * 0.6b: first version in official kernel, Linux 1.3.46
47 * 0.7: changed /proc/apm format, Linux 1.3.58
48 * 0.8: fixed gcc 2.7.[12] compilation problems, Linux 1.3.59
49 * 0.9: only call bios if bios is present, Linux 1.3.72
50 * 1.0: use fixed device number, consolidate /proc/apm into this file,
51 * Linux 1.3.85
52 * 1.1: support user-space standby and suspend, power off after system
53 * halted, Linux 1.3.98
54 * 1.2: When resetting RTC after resume, take care so that the time
55 * is only incorrect by 30-60mS (vs. 1S previously) (Gabor J. Toth
56 * <jtoth@princeton.edu>); improve interaction between
57 * screen-blanking and gpm (Stephen Rothwell); Linux 1.99.4
58 * 1.2a:Simple change to stop mysterious bug reports with SMP also added
59 * levels to the printk calls. APM is not defined for SMP machines.
Simon Arlott27b46d72007-10-20 01:13:56 +020060 * The new replacement for it is, but Linux doesn't yet support this.
Linus Torvalds1da177e2005-04-16 15:20:36 -070061 * Alan Cox Linux 2.1.55
62 * 1.3: Set up a valid data descriptor 0x40 for buggy BIOS's
63 * 1.4: Upgraded to support APM 1.2. Integrated ThinkPad suspend patch by
64 * Dean Gaudet <dgaudet@arctic.org>.
65 * C. Scott Ananian <cananian@alumni.princeton.edu> Linux 2.1.87
66 * 1.5: Fix segment register reloading (in case of bad segments saved
67 * across BIOS call).
68 * Stephen Rothwell
69 * 1.6: Cope with complier/assembler differences.
70 * Only try to turn off the first display device.
71 * Fix OOPS at power off with no APM BIOS by Jan Echternach
72 * <echter@informatik.uni-rostock.de>
73 * Stephen Rothwell
74 * 1.7: Modify driver's cached copy of the disabled/disengaged flags
75 * to reflect current state of APM BIOS.
76 * Chris Rankin <rankinc@bellsouth.net>
77 * Reset interrupt 0 timer to 100Hz after suspend
78 * Chad Miller <cmiller@surfsouth.com>
79 * Add CONFIG_APM_IGNORE_SUSPEND_BOUNCE
80 * Richard Gooch <rgooch@atnf.csiro.au>
81 * Allow boot time disabling of APM
82 * Make boot messages far less verbose by default
83 * Make asm safer
84 * Stephen Rothwell
85 * 1.8: Add CONFIG_APM_RTC_IS_GMT
86 * Richard Gooch <rgooch@atnf.csiro.au>
87 * change APM_NOINTS to CONFIG_APM_ALLOW_INTS
88 * remove dependency on CONFIG_PROC_FS
89 * Stephen Rothwell
90 * 1.9: Fix small typo. <laslo@wodip.opole.pl>
91 * Try to cope with BIOS's that need to have all display
92 * devices blanked and not just the first one.
93 * Ross Paterson <ross@soi.city.ac.uk>
94 * Fix segment limit setting it has always been wrong as
95 * the segments needed to have byte granularity.
96 * Mark a few things __init.
97 * Add hack to allow power off of SMP systems by popular request.
98 * Use CONFIG_SMP instead of __SMP__
99 * Ignore BOUNCES for three seconds.
100 * Stephen Rothwell
101 * 1.10: Fix for Thinkpad return code.
102 * Merge 2.2 and 2.3 drivers.
103 * Remove APM dependencies in arch/i386/kernel/process.c
104 * Remove APM dependencies in drivers/char/sysrq.c
105 * Reset time across standby.
106 * Allow more inititialisation on SMP.
107 * Remove CONFIG_APM_POWER_OFF and make it boot time
108 * configurable (default on).
109 * Make debug only a boot time parameter (remove APM_DEBUG).
110 * Try to blank all devices on any error.
111 * 1.11: Remove APM dependencies in drivers/char/console.c
112 * Check nr_running to detect if we are idle (from
113 * Borislav Deianov <borislav@lix.polytechnique.fr>)
114 * Fix for bioses that don't zero the top part of the
115 * entrypoint offset (Mario Sitta <sitta@al.unipmn.it>)
116 * (reported by Panos Katsaloulis <teras@writeme.com>).
117 * Real mode power off patch (Walter Hofmann
118 * <Walter.Hofmann@physik.stud.uni-erlangen.de>).
119 * 1.12: Remove CONFIG_SMP as the compiler will optimize
120 * the code away anyway (smp_num_cpus == 1 in UP)
121 * noted by Artur Skawina <skawina@geocities.com>.
122 * Make power off under SMP work again.
123 * Fix thinko with initial engaging of BIOS.
124 * Make sure power off only happens on CPU 0
125 * (Paul "Rusty" Russell <rusty@rustcorp.com.au>).
126 * Do error notification to user mode if BIOS calls fail.
127 * Move entrypoint offset fix to ...boot/setup.S
128 * where it belongs (Cosmos <gis88564@cis.nctu.edu.tw>).
129 * Remove smp-power-off. SMP users must now specify
130 * "apm=power-off" on the kernel command line. Suggested
131 * by Jim Avera <jima@hal.com>, modified by Alan Cox
132 * <alan@lxorguk.ukuu.org.uk>.
133 * Register the /proc/apm entry even on SMP so that
134 * scripts that check for it before doing power off
135 * work (Jim Avera <jima@hal.com>).
136 * 1.13: Changes for new pm_ interfaces (Andy Henroid
137 * <andy_henroid@yahoo.com>).
138 * Modularize the code.
139 * Fix the Thinkpad (again) :-( (CONFIG_APM_IGNORE_MULTIPLE_SUSPENDS
140 * is now the way life works).
141 * Fix thinko in suspend() (wrong return).
142 * Notify drivers on critical suspend.
143 * Make kapmd absorb more idle time (Pavel Machek <pavel@suse.cz>
144 * modified by sfr).
145 * Disable interrupts while we are suspended (Andy Henroid
146 * <andy_henroid@yahoo.com> fixed by sfr).
147 * Make power off work on SMP again (Tony Hoyle
148 * <tmh@magenta-logic.com> and <zlatko@iskon.hr>) modified by sfr.
149 * Remove CONFIG_APM_SUSPEND_BOUNCE. The bounce ignore
150 * interval is now configurable.
151 * 1.14: Make connection version persist across module unload/load.
152 * Enable and engage power management earlier.
153 * Disengage power management on module unload.
154 * Changed to use the sysrq-register hack for registering the
155 * power off function called by magic sysrq based upon discussions
156 * in irc://irc.openprojects.net/#kernelnewbies
157 * (Crutcher Dunnavant <crutcher+kernel@datastacks.com>).
158 * Make CONFIG_APM_REAL_MODE_POWER_OFF run time configurable.
159 * (Arjan van de Ven <arjanv@redhat.com>) modified by sfr.
160 * Work around byte swap bug in one of the Vaio's BIOS's
161 * (Marc Boucher <marc@mbsi.ca>).
162 * Exposed the disable flag to dmi so that we can handle known
163 * broken APM (Alan Cox <alan@redhat.com>).
164 * 1.14ac: If the BIOS says "I slowed the CPU down" then don't spin
165 * calling it - instead idle. (Alan Cox <alan@redhat.com>)
166 * If an APM idle fails log it and idle sensibly
167 * 1.15: Don't queue events to clients who open the device O_WRONLY.
168 * Don't expect replies from clients who open the device O_RDONLY.
169 * (Idea from Thomas Hood)
170 * Minor waitqueue cleanups. (John Fremlin <chief@bandits.org>)
171 * 1.16: Fix idle calling. (Andreas Steinmetz <ast@domdv.de> et al.)
172 * Notify listeners of standby or suspend events before notifying
173 * drivers. Return EBUSY to ioctl() if suspend is rejected.
174 * (Russell King <rmk@arm.linux.org.uk> and Thomas Hood)
175 * Ignore first resume after we generate our own resume event
176 * after a suspend (Thomas Hood)
177 * Daemonize now gets rid of our controlling terminal (sfr).
178 * CONFIG_APM_CPU_IDLE now just affects the default value of
179 * idle_threshold (sfr).
180 * Change name of kernel apm daemon (as it no longer idles) (sfr).
181 * 1.16ac: Fix up SMP support somewhat. You can now force SMP on and we
182 * make _all_ APM calls on the CPU#0. Fix unsafe sign bug.
183 * TODO: determine if its "boot CPU" or "CPU0" we want to lock to.
184 *
185 * APM 1.1 Reference:
186 *
187 * Intel Corporation, Microsoft Corporation. Advanced Power Management
188 * (APM) BIOS Interface Specification, Revision 1.1, September 1993.
189 * Intel Order Number 241704-001. Microsoft Part Number 781-110-X01.
190 *
191 * [This document is available free from Intel by calling 800.628.8686 (fax
192 * 916.356.6100) or 800.548.4725; or via anonymous ftp from
193 * ftp://ftp.intel.com/pub/IAL/software_specs/apmv11.doc. It is also
194 * available from Microsoft by calling 206.882.8080.]
195 *
196 * APM 1.2 Reference:
197 * Intel Corporation, Microsoft Corporation. Advanced Power Management
198 * (APM) BIOS Interface Specification, Revision 1.2, February 1996.
199 *
200 * [This document is available from Microsoft at:
Kristian Mueller3f4b23e2006-10-29 22:46:46 -0800201 * http://www.microsoft.com/whdc/archive/amp_12.mspx]
Linus Torvalds1da177e2005-04-16 15:20:36 -0700202 */
203
Linus Torvalds1da177e2005-04-16 15:20:36 -0700204#include <linux/module.h>
205
206#include <linux/poll.h>
207#include <linux/types.h>
208#include <linux/stddef.h>
209#include <linux/timer.h>
210#include <linux/fcntl.h>
211#include <linux/slab.h>
212#include <linux/stat.h>
213#include <linux/proc_fs.h>
Alexey Dobriyan016d6f32007-02-13 13:26:23 +0100214#include <linux/seq_file.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -0700215#include <linux/miscdevice.h>
216#include <linux/apm_bios.h>
217#include <linux/init.h>
218#include <linux/time.h>
219#include <linux/sched.h>
220#include <linux/pm.h>
Jeff Garzikbca73e42005-11-13 16:06:25 -0800221#include <linux/pm_legacy.h>
Randy Dunlapa9415642006-01-11 12:17:48 -0800222#include <linux/capability.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -0700223#include <linux/device.h>
224#include <linux/kernel.h>
Rafael J. Wysocki83144182007-07-17 04:03:35 -0700225#include <linux/freezer.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -0700226#include <linux/smp.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -0700227#include <linux/dmi.h>
228#include <linux/suspend.h>
Serge E. Hallynfc095612006-09-29 02:00:04 -0700229#include <linux/kthread.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -0700230
231#include <asm/system.h>
232#include <asm/uaccess.h>
233#include <asm/desc.h>
Ingo Molnar306e4402005-06-30 02:58:55 -0700234#include <asm/i8253.h>
Rusty Russell6020c8f2006-12-07 02:14:08 +0100235#include <asm/paravirt.h>
Jeremy Fitzhardinge07f33312007-05-02 19:27:11 +0200236#include <asm/reboot.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -0700237
Linus Torvalds1da177e2005-04-16 15:20:36 -0700238#if defined(CONFIG_APM_DISPLAY_BLANK) && defined(CONFIG_VT)
239extern int (*console_blank_hook)(int);
240#endif
241
242/*
243 * The apm_bios device is one of the misc char devices.
244 * This is its minor number.
245 */
246#define APM_MINOR_DEV 134
247
248/*
249 * See Documentation/Config.help for the configuration options.
250 *
251 * Various options can be changed at boot time as follows:
252 * (We allow underscores for compatibility with the modules code)
253 * apm=on/off enable/disable APM
254 * [no-]allow[-_]ints allow interrupts during BIOS calls
255 * [no-]broken[-_]psr BIOS has a broken GetPowerStatus call
256 * [no-]realmode[-_]power[-_]off switch to real mode before
257 * powering off
258 * [no-]debug log some debugging messages
259 * [no-]power[-_]off power off on shutdown
260 * [no-]smp Use apm even on an SMP box
261 * bounce[-_]interval=<n> number of ticks to ignore suspend
262 * bounces
263 * idle[-_]threshold=<n> System idle percentage above which to
264 * make APM BIOS idle calls. Set it to
265 * 100 to disable.
266 * idle[-_]period=<n> Period (in 1/100s of a second) over
267 * which the idle percentage is
268 * calculated.
269 */
270
271/* KNOWN PROBLEM MACHINES:
272 *
273 * U: TI 4000M TravelMate: BIOS is *NOT* APM compliant
274 * [Confirmed by TI representative]
275 * ?: ACER 486DX4/75: uses dseg 0040, in violation of APM specification
276 * [Confirmed by BIOS disassembly]
277 * [This may work now ...]
278 * P: Toshiba 1950S: battery life information only gets updated after resume
279 * P: Midwest Micro Soundbook Elite DX2/66 monochrome: screen blanking
280 * broken in BIOS [Reported by Garst R. Reese <reese@isn.net>]
281 * ?: AcerNote-950: oops on reading /proc/apm - workaround is a WIP
282 * Neale Banks <neale@lowendale.com.au> December 2000
283 *
284 * Legend: U = unusable with APM patches
285 * P = partially usable with APM patches
286 */
287
288/*
289 * Define as 1 to make the driver always call the APM BIOS busy
290 * routine even if the clock was not reported as slowed by the
291 * idle routine. Otherwise, define as 0.
292 */
293#define ALWAYS_CALL_BUSY 1
294
295/*
296 * Define to make the APM BIOS calls zero all data segment registers (so
297 * that an incorrect BIOS implementation will cause a kernel panic if it
298 * tries to write to arbitrary memory).
299 */
300#define APM_ZERO_SEGS
301
302#include "apm.h"
303
304/*
Linus Torvalds1da177e2005-04-16 15:20:36 -0700305 * Define to re-initialize the interrupt 0 timer to 100 Hz after a suspend.
306 * This patched by Chad Miller <cmiller@surfsouth.com>, original code by
307 * David Chen <chen@ctpa04.mit.edu>
308 */
309#undef INIT_TIMER_AFTER_SUSPEND
310
311#ifdef INIT_TIMER_AFTER_SUSPEND
312#include <linux/timex.h>
313#include <asm/io.h>
314#include <linux/delay.h>
315#endif
316
317/*
318 * Need to poll the APM BIOS every second
319 */
320#define APM_CHECK_TIMEOUT (HZ)
321
322/*
323 * Ignore suspend events for this amount of time after a resume
324 */
325#define DEFAULT_BOUNCE_INTERVAL (3 * HZ)
326
327/*
328 * Maximum number of events stored
329 */
330#define APM_MAX_EVENTS 20
331
332/*
333 * The per-file APM data
334 */
335struct apm_user {
336 int magic;
337 struct apm_user * next;
Domen Puncer77617bd2005-06-25 14:58:45 -0700338 unsigned int suser: 1;
339 unsigned int writer: 1;
340 unsigned int reader: 1;
341 unsigned int suspend_wait: 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700342 int suspend_result;
343 int suspends_pending;
344 int standbys_pending;
345 int suspends_read;
346 int standbys_read;
347 int event_head;
348 int event_tail;
349 apm_event_t events[APM_MAX_EVENTS];
350};
351
352/*
353 * The magic number in apm_user
354 */
355#define APM_BIOS_MAGIC 0x4101
356
357/*
358 * idle percentage above which bios idle calls are done
359 */
360#ifdef CONFIG_APM_CPU_IDLE
361#define DEFAULT_IDLE_THRESHOLD 95
362#else
363#define DEFAULT_IDLE_THRESHOLD 100
364#endif
365#define DEFAULT_IDLE_PERIOD (100 / 3)
366
367/*
368 * Local variables
369 */
370static struct {
371 unsigned long offset;
372 unsigned short segment;
373} apm_bios_entry;
374static int clock_slowed;
Andreas Mohr87af2ff2006-06-23 02:04:17 -0700375static int idle_threshold __read_mostly = DEFAULT_IDLE_THRESHOLD;
376static int idle_period __read_mostly = DEFAULT_IDLE_PERIOD;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700377static int set_pm_idle;
378static int suspends_pending;
379static int standbys_pending;
380static int ignore_sys_suspend;
381static int ignore_normal_resume;
Andreas Mohr87af2ff2006-06-23 02:04:17 -0700382static int bounce_interval __read_mostly = DEFAULT_BOUNCE_INTERVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700383
Andreas Mohr87af2ff2006-06-23 02:04:17 -0700384static int debug __read_mostly;
385static int smp __read_mostly;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700386static int apm_disabled = -1;
387#ifdef CONFIG_SMP
388static int power_off;
389#else
390static int power_off = 1;
391#endif
392#ifdef CONFIG_APM_REAL_MODE_POWER_OFF
393static int realmode_power_off = 1;
394#else
395static int realmode_power_off;
396#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -0700397#ifdef CONFIG_APM_ALLOW_INTS
398static int allow_ints = 1;
399#else
400static int allow_ints;
401#endif
402static int broken_psr;
403
404static DECLARE_WAIT_QUEUE_HEAD(apm_waitqueue);
405static DECLARE_WAIT_QUEUE_HEAD(apm_suspend_waitqueue);
406static struct apm_user * user_list;
407static DEFINE_SPINLOCK(user_list_lock);
Glauber de Oliveira Costa6842ef02008-01-30 13:31:11 +0100408static const struct desc_struct bad_bios_desc = { { { 0, 0x00409200 } } };
Linus Torvalds1da177e2005-04-16 15:20:36 -0700409
Andreas Mohr87af2ff2006-06-23 02:04:17 -0700410static const char driver_version[] = "1.16ac"; /* no spaces */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700411
Serge E. Hallynfc095612006-09-29 02:00:04 -0700412static struct task_struct *kapmd_task;
413
Linus Torvalds1da177e2005-04-16 15:20:36 -0700414/*
415 * APM event names taken from the APM 1.2 specification. These are
416 * the message codes that the BIOS uses to tell us about events
417 */
Andreas Mohr87af2ff2006-06-23 02:04:17 -0700418static const char * const apm_event_name[] = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700419 "system standby",
420 "system suspend",
421 "normal resume",
422 "critical resume",
423 "low battery",
424 "power status change",
425 "update time",
426 "critical suspend",
427 "user standby",
428 "user suspend",
429 "system standby resume",
430 "capabilities change"
431};
Tobias Klauser38e548e2005-11-07 00:58:31 -0800432#define NR_APM_EVENT_NAME ARRAY_SIZE(apm_event_name)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700433
434typedef struct lookup_t {
435 int key;
436 char * msg;
437} lookup_t;
438
439/*
440 * The BIOS returns a set of standard error codes in AX when the
441 * carry flag is set.
442 */
443
444static const lookup_t error_table[] = {
445/* N/A { APM_SUCCESS, "Operation succeeded" }, */
446 { APM_DISABLED, "Power management disabled" },
447 { APM_CONNECTED, "Real mode interface already connected" },
448 { APM_NOT_CONNECTED, "Interface not connected" },
449 { APM_16_CONNECTED, "16 bit interface already connected" },
450/* N/A { APM_16_UNSUPPORTED, "16 bit interface not supported" }, */
451 { APM_32_CONNECTED, "32 bit interface already connected" },
452 { APM_32_UNSUPPORTED, "32 bit interface not supported" },
453 { APM_BAD_DEVICE, "Unrecognized device ID" },
454 { APM_BAD_PARAM, "Parameter out of range" },
455 { APM_NOT_ENGAGED, "Interface not engaged" },
456 { APM_BAD_FUNCTION, "Function not supported" },
457 { APM_RESUME_DISABLED, "Resume timer disabled" },
458 { APM_BAD_STATE, "Unable to enter requested state" },
459/* N/A { APM_NO_EVENTS, "No events pending" }, */
460 { APM_NO_ERROR, "BIOS did not set a return code" },
461 { APM_NOT_PRESENT, "No APM present" }
462};
Tobias Klauser38e548e2005-11-07 00:58:31 -0800463#define ERROR_COUNT ARRAY_SIZE(error_table)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700464
465/**
466 * apm_error - display an APM error
467 * @str: information string
468 * @err: APM BIOS return code
469 *
470 * Write a meaningful log entry to the kernel log in the event of
471 * an APM error.
472 */
473
474static void apm_error(char *str, int err)
475{
476 int i;
477
478 for (i = 0; i < ERROR_COUNT; i++)
479 if (error_table[i].key == err) break;
480 if (i < ERROR_COUNT)
481 printk(KERN_NOTICE "apm: %s: %s\n", str, error_table[i].msg);
482 else
483 printk(KERN_NOTICE "apm: %s: unknown error code %#2.2x\n",
484 str, err);
485}
486
487/*
488 * Lock APM functionality to physical CPU 0
489 */
490
491#ifdef CONFIG_SMP
492
493static cpumask_t apm_save_cpus(void)
494{
495 cpumask_t x = current->cpus_allowed;
496 /* Some bioses don't like being called from CPU != 0 */
497 set_cpus_allowed(current, cpumask_of_cpu(0));
498 BUG_ON(smp_processor_id() != 0);
499 return x;
500}
501
502static inline void apm_restore_cpus(cpumask_t mask)
503{
504 set_cpus_allowed(current, mask);
505}
506
507#else
508
509/*
510 * No CPU lockdown needed on a uniprocessor
511 */
512
513#define apm_save_cpus() (current->cpus_allowed)
514#define apm_restore_cpus(x) (void)(x)
515
516#endif
517
518/*
519 * These are the actual BIOS calls. Depending on APM_ZERO_SEGS and
520 * apm_info.allow_ints, we are being really paranoid here! Not only
521 * are interrupts disabled, but all the segment registers (except SS)
522 * are saved and zeroed this means that if the BIOS tries to reference
523 * any data without explicitly loading the segment registers, the kernel
524 * will fault immediately rather than have some unforeseen circumstances
525 * for the rest of the kernel. And it will be very obvious! :-) Doing
526 * this depends on CS referring to the same physical memory as DS so that
527 * DS can be zeroed before the call. Unfortunately, we can't do anything
528 * about the stack segment/pointer. Also, we tell the compiler that
529 * everything could change.
530 *
531 * Also, we KNOW that for the non error case of apm_bios_call, there
532 * is no useful data returned in the low order 8 bits of eax.
533 */
Peter Zijlstra3864c482006-10-17 00:10:26 -0700534
535static inline unsigned long __apm_irq_save(void)
536{
537 unsigned long flags;
538 local_save_flags(flags);
539 if (apm_info.allow_ints) {
540 if (irqs_disabled_flags(flags))
541 local_irq_enable();
542 } else
Linus Torvalds1da177e2005-04-16 15:20:36 -0700543 local_irq_disable();
544
Peter Zijlstra3864c482006-10-17 00:10:26 -0700545 return flags;
546}
547
548#define apm_irq_save(flags) \
549 do { flags = __apm_irq_save(); } while (0)
550
551static inline void apm_irq_restore(unsigned long flags)
552{
553 if (irqs_disabled_flags(flags))
554 local_irq_disable();
555 else if (irqs_disabled())
556 local_irq_enable();
557}
558
Linus Torvalds1da177e2005-04-16 15:20:36 -0700559#ifdef APM_ZERO_SEGS
560# define APM_DECL_SEGS \
561 unsigned int saved_fs; unsigned int saved_gs;
562# define APM_DO_SAVE_SEGS \
563 savesegment(fs, saved_fs); savesegment(gs, saved_gs)
564# define APM_DO_RESTORE_SEGS \
565 loadsegment(fs, saved_fs); loadsegment(gs, saved_gs)
566#else
567# define APM_DECL_SEGS
568# define APM_DO_SAVE_SEGS
569# define APM_DO_RESTORE_SEGS
570#endif
571
572/**
573 * apm_bios_call - Make an APM BIOS 32bit call
574 * @func: APM function to execute
575 * @ebx_in: EBX register for call entry
576 * @ecx_in: ECX register for call entry
577 * @eax: EAX register return
578 * @ebx: EBX register return
579 * @ecx: ECX register return
580 * @edx: EDX register return
581 * @esi: ESI register return
582 *
583 * Make an APM call using the 32bit protected mode interface. The
584 * caller is responsible for knowing if APM BIOS is configured and
585 * enabled. This call can disable interrupts for a long period of
586 * time on some laptops. The return value is in AH and the carry
587 * flag is loaded into AL. If there is an error, then the error
588 * code is returned in AH (bits 8-15 of eax) and this function
589 * returns non-zero.
590 */
591
592static u8 apm_bios_call(u32 func, u32 ebx_in, u32 ecx_in,
593 u32 *eax, u32 *ebx, u32 *ecx, u32 *edx, u32 *esi)
594{
595 APM_DECL_SEGS
596 unsigned long flags;
597 cpumask_t cpus;
598 int cpu;
599 struct desc_struct save_desc_40;
Zachary Amsden251e6912005-10-30 14:59:34 -0800600 struct desc_struct *gdt;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700601
602 cpus = apm_save_cpus();
603
604 cpu = get_cpu();
Zachary Amsden251e6912005-10-30 14:59:34 -0800605 gdt = get_cpu_gdt_table(cpu);
606 save_desc_40 = gdt[0x40 / 8];
607 gdt[0x40 / 8] = bad_bios_desc;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700608
Peter Zijlstra3864c482006-10-17 00:10:26 -0700609 apm_irq_save(flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700610 APM_DO_SAVE_SEGS;
611 apm_bios_call_asm(func, ebx_in, ecx_in, eax, ebx, ecx, edx, esi);
612 APM_DO_RESTORE_SEGS;
Peter Zijlstra3864c482006-10-17 00:10:26 -0700613 apm_irq_restore(flags);
Zachary Amsden251e6912005-10-30 14:59:34 -0800614 gdt[0x40 / 8] = save_desc_40;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700615 put_cpu();
616 apm_restore_cpus(cpus);
617
618 return *eax & 0xff;
619}
620
621/**
622 * apm_bios_call_simple - make a simple APM BIOS 32bit call
623 * @func: APM function to invoke
624 * @ebx_in: EBX register value for BIOS call
625 * @ecx_in: ECX register value for BIOS call
626 * @eax: EAX register on return from the BIOS call
627 *
Andreas Mohr87af2ff2006-06-23 02:04:17 -0700628 * Make a BIOS call that returns one value only, or just status.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700629 * If there is an error, then the error code is returned in AH
630 * (bits 8-15 of eax) and this function returns non-zero. This is
631 * used for simpler BIOS operations. This call may hold interrupts
632 * off for a long time on some laptops.
633 */
634
635static u8 apm_bios_call_simple(u32 func, u32 ebx_in, u32 ecx_in, u32 *eax)
636{
637 u8 error;
638 APM_DECL_SEGS
639 unsigned long flags;
640 cpumask_t cpus;
641 int cpu;
642 struct desc_struct save_desc_40;
Zachary Amsden251e6912005-10-30 14:59:34 -0800643 struct desc_struct *gdt;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700644
645 cpus = apm_save_cpus();
646
647 cpu = get_cpu();
Zachary Amsden251e6912005-10-30 14:59:34 -0800648 gdt = get_cpu_gdt_table(cpu);
649 save_desc_40 = gdt[0x40 / 8];
650 gdt[0x40 / 8] = bad_bios_desc;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700651
Peter Zijlstra3864c482006-10-17 00:10:26 -0700652 apm_irq_save(flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700653 APM_DO_SAVE_SEGS;
654 error = apm_bios_call_simple_asm(func, ebx_in, ecx_in, eax);
655 APM_DO_RESTORE_SEGS;
Peter Zijlstra3864c482006-10-17 00:10:26 -0700656 apm_irq_restore(flags);
Zachary Amsden251e6912005-10-30 14:59:34 -0800657 gdt[0x40 / 8] = save_desc_40;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700658 put_cpu();
659 apm_restore_cpus(cpus);
660 return error;
661}
662
663/**
664 * apm_driver_version - APM driver version
665 * @val: loaded with the APM version on return
666 *
667 * Retrieve the APM version supported by the BIOS. This is only
668 * supported for APM 1.1 or higher. An error indicates APM 1.0 is
669 * probably present.
670 *
671 * On entry val should point to a value indicating the APM driver
672 * version with the high byte being the major and the low byte the
673 * minor number both in BCD
674 *
675 * On return it will hold the BIOS revision supported in the
676 * same format.
677 */
678
679static int apm_driver_version(u_short *val)
680{
681 u32 eax;
682
683 if (apm_bios_call_simple(APM_FUNC_VERSION, 0, *val, &eax))
684 return (eax >> 8) & 0xff;
685 *val = eax;
686 return APM_SUCCESS;
687}
688
689/**
690 * apm_get_event - get an APM event from the BIOS
691 * @event: pointer to the event
692 * @info: point to the event information
693 *
694 * The APM BIOS provides a polled information for event
695 * reporting. The BIOS expects to be polled at least every second
696 * when events are pending. When a message is found the caller should
697 * poll until no more messages are present. However, this causes
698 * problems on some laptops where a suspend event notification is
699 * not cleared until it is acknowledged.
700 *
701 * Additional information is returned in the info pointer, providing
702 * that APM 1.2 is in use. If no messges are pending the value 0x80
703 * is returned (No power management events pending).
704 */
705
706static int apm_get_event(apm_event_t *event, apm_eventinfo_t *info)
707{
708 u32 eax;
709 u32 ebx;
710 u32 ecx;
711 u32 dummy;
712
713 if (apm_bios_call(APM_FUNC_GET_EVENT, 0, 0, &eax, &ebx, &ecx,
714 &dummy, &dummy))
715 return (eax >> 8) & 0xff;
716 *event = ebx;
717 if (apm_info.connection_version < 0x0102)
718 *info = ~0; /* indicate info not valid */
719 else
720 *info = ecx;
721 return APM_SUCCESS;
722}
723
724/**
725 * set_power_state - set the power management state
726 * @what: which items to transition
727 * @state: state to transition to
728 *
729 * Request an APM change of state for one or more system devices. The
730 * processor state must be transitioned last of all. what holds the
731 * class of device in the upper byte and the device number (0xFF for
732 * all) for the object to be transitioned.
733 *
734 * The state holds the state to transition to, which may in fact
735 * be an acceptance of a BIOS requested state change.
736 */
737
738static int set_power_state(u_short what, u_short state)
739{
740 u32 eax;
741
742 if (apm_bios_call_simple(APM_FUNC_SET_STATE, what, state, &eax))
743 return (eax >> 8) & 0xff;
744 return APM_SUCCESS;
745}
746
747/**
748 * set_system_power_state - set system wide power state
749 * @state: which state to enter
750 *
751 * Transition the entire system into a new APM power state.
752 */
753
754static int set_system_power_state(u_short state)
755{
756 return set_power_state(APM_DEVICE_ALL, state);
757}
758
759/**
760 * apm_do_idle - perform power saving
761 *
762 * This function notifies the BIOS that the processor is (in the view
763 * of the OS) idle. It returns -1 in the event that the BIOS refuses
764 * to handle the idle request. On a success the function returns 1
765 * if the BIOS did clock slowing or 0 otherwise.
766 */
767
768static int apm_do_idle(void)
769{
770 u32 eax;
Nick Piggin64c7c8f2005-11-08 21:39:04 -0800771 u8 ret = 0;
772 int idled = 0;
773 int polling;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700774
Andi Kleen495ab9c2006-06-26 13:59:11 +0200775 polling = !!(current_thread_info()->status & TS_POLLING);
Nick Piggin64c7c8f2005-11-08 21:39:04 -0800776 if (polling) {
Andi Kleen495ab9c2006-06-26 13:59:11 +0200777 current_thread_info()->status &= ~TS_POLLING;
Ingo Molnar0888f062006-12-22 01:11:56 -0800778 /*
779 * TS_POLLING-cleared state must be visible before we
780 * test NEED_RESCHED:
781 */
782 smp_mb();
Nick Piggin64c7c8f2005-11-08 21:39:04 -0800783 }
784 if (!need_resched()) {
785 idled = 1;
786 ret = apm_bios_call_simple(APM_FUNC_IDLE, 0, 0, &eax);
787 }
788 if (polling)
Andi Kleen495ab9c2006-06-26 13:59:11 +0200789 current_thread_info()->status |= TS_POLLING;
Nick Piggin64c7c8f2005-11-08 21:39:04 -0800790
791 if (!idled)
792 return 0;
793
794 if (ret) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700795 static unsigned long t;
796
797 /* This always fails on some SMP boards running UP kernels.
798 * Only report the failure the first 5 times.
799 */
800 if (++t < 5)
801 {
802 printk(KERN_DEBUG "apm_do_idle failed (%d)\n",
803 (eax >> 8) & 0xff);
804 t = jiffies;
805 }
806 return -1;
807 }
808 clock_slowed = (apm_info.bios.flags & APM_IDLE_SLOWS_CLOCK) != 0;
809 return clock_slowed;
810}
811
812/**
813 * apm_do_busy - inform the BIOS the CPU is busy
814 *
815 * Request that the BIOS brings the CPU back to full performance.
816 */
817
818static void apm_do_busy(void)
819{
820 u32 dummy;
821
822 if (clock_slowed || ALWAYS_CALL_BUSY) {
823 (void) apm_bios_call_simple(APM_FUNC_BUSY, 0, 0, &dummy);
824 clock_slowed = 0;
825 }
826}
827
828/*
829 * If no process has really been interested in
830 * the CPU for some time, we want to call BIOS
831 * power management - we probably want
832 * to conserve power.
833 */
834#define IDLE_CALC_LIMIT (HZ * 100)
835#define IDLE_LEAKY_MAX 16
836
Andreas Mohr87af2ff2006-06-23 02:04:17 -0700837static void (*original_pm_idle)(void) __read_mostly;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700838
Linus Torvalds1da177e2005-04-16 15:20:36 -0700839/**
840 * apm_cpu_idle - cpu idling for APM capable Linux
841 *
842 * This is the idling function the kernel executes when APM is available. It
843 * tries to do BIOS powermanagement based on the average system idle time.
844 * Furthermore it calls the system default idle routine.
845 */
846
847static void apm_cpu_idle(void)
848{
849 static int use_apm_idle; /* = 0 */
850 static unsigned int last_jiffies; /* = 0 */
851 static unsigned int last_stime; /* = 0 */
852
853 int apm_idle_done = 0;
854 unsigned int jiffies_since_last_check = jiffies - last_jiffies;
855 unsigned int bucket;
856
857recalc:
858 if (jiffies_since_last_check > IDLE_CALC_LIMIT) {
859 use_apm_idle = 0;
860 last_jiffies = jiffies;
861 last_stime = current->stime;
862 } else if (jiffies_since_last_check > idle_period) {
863 unsigned int idle_percentage;
864
865 idle_percentage = current->stime - last_stime;
866 idle_percentage *= 100;
867 idle_percentage /= jiffies_since_last_check;
868 use_apm_idle = (idle_percentage > idle_threshold);
869 if (apm_info.forbid_idle)
870 use_apm_idle = 0;
871 last_jiffies = jiffies;
872 last_stime = current->stime;
873 }
874
875 bucket = IDLE_LEAKY_MAX;
876
877 while (!need_resched()) {
878 if (use_apm_idle) {
879 unsigned int t;
880
881 t = jiffies;
882 switch (apm_do_idle()) {
883 case 0: apm_idle_done = 1;
884 if (t != jiffies) {
885 if (bucket) {
886 bucket = IDLE_LEAKY_MAX;
887 continue;
888 }
889 } else if (bucket) {
890 bucket--;
891 continue;
892 }
893 break;
894 case 1: apm_idle_done = 1;
895 break;
896 default: /* BIOS refused */
897 break;
898 }
899 }
900 if (original_pm_idle)
901 original_pm_idle();
902 else
903 default_idle();
904 jiffies_since_last_check = jiffies - last_jiffies;
905 if (jiffies_since_last_check > idle_period)
906 goto recalc;
907 }
908
909 if (apm_idle_done)
910 apm_do_busy();
911}
912
913/**
914 * apm_power_off - ask the BIOS to power off
915 *
916 * Handle the power off sequence. This is the one piece of code we
917 * will execute even on SMP machines. In order to deal with BIOS
918 * bugs we support real mode APM BIOS power off calls. We also make
919 * the SMP call on CPU0 as some systems will only honour this call
920 * on their first cpu.
921 */
922
923static void apm_power_off(void)
924{
925 unsigned char po_bios_call[] = {
926 0xb8, 0x00, 0x10, /* movw $0x1000,ax */
927 0x8e, 0xd0, /* movw ax,ss */
928 0xbc, 0x00, 0xf0, /* movw $0xf000,sp */
929 0xb8, 0x07, 0x53, /* movw $0x5307,ax */
930 0xbb, 0x01, 0x00, /* movw $0x0001,bx */
931 0xb9, 0x03, 0x00, /* movw $0x0003,cx */
932 0xcd, 0x15 /* int $0x15 */
933 };
934
Linus Torvalds1da177e2005-04-16 15:20:36 -0700935 /* Some bioses don't like being called from CPU != 0 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700936 if (apm_info.realmode_power_off)
937 {
938 (void)apm_save_cpus();
939 machine_real_restart(po_bios_call, sizeof(po_bios_call));
940 }
941 else
942 (void) set_system_power_state(APM_STATE_OFF);
943}
944
945#ifdef CONFIG_APM_DO_ENABLE
946
947/**
948 * apm_enable_power_management - enable BIOS APM power management
949 * @enable: enable yes/no
950 *
951 * Enable or disable the APM BIOS power services.
952 */
953
954static int apm_enable_power_management(int enable)
955{
956 u32 eax;
957
958 if ((enable == 0) && (apm_info.bios.flags & APM_BIOS_DISENGAGED))
959 return APM_NOT_ENGAGED;
960 if (apm_bios_call_simple(APM_FUNC_ENABLE_PM, APM_DEVICE_BALL,
961 enable, &eax))
962 return (eax >> 8) & 0xff;
963 if (enable)
964 apm_info.bios.flags &= ~APM_BIOS_DISABLED;
965 else
966 apm_info.bios.flags |= APM_BIOS_DISABLED;
967 return APM_SUCCESS;
968}
969#endif
970
971/**
972 * apm_get_power_status - get current power state
973 * @status: returned status
974 * @bat: battery info
975 * @life: estimated life
976 *
977 * Obtain the current power status from the APM BIOS. We return a
978 * status which gives the rough battery status, and current power
979 * source. The bat value returned give an estimate as a percentage
980 * of life and a status value for the battery. The estimated life
981 * if reported is a lifetime in secodnds/minutes at current powwer
982 * consumption.
983 */
984
985static int apm_get_power_status(u_short *status, u_short *bat, u_short *life)
986{
987 u32 eax;
988 u32 ebx;
989 u32 ecx;
990 u32 edx;
991 u32 dummy;
992
993 if (apm_info.get_power_status_broken)
994 return APM_32_UNSUPPORTED;
995 if (apm_bios_call(APM_FUNC_GET_STATUS, APM_DEVICE_ALL, 0,
996 &eax, &ebx, &ecx, &edx, &dummy))
997 return (eax >> 8) & 0xff;
998 *status = ebx;
999 *bat = ecx;
1000 if (apm_info.get_power_status_swabinminutes) {
1001 *life = swab16((u16)edx);
1002 *life |= 0x8000;
1003 } else
1004 *life = edx;
1005 return APM_SUCCESS;
1006}
1007
1008#if 0
1009static int apm_get_battery_status(u_short which, u_short *status,
1010 u_short *bat, u_short *life, u_short *nbat)
1011{
1012 u32 eax;
1013 u32 ebx;
1014 u32 ecx;
1015 u32 edx;
1016 u32 esi;
1017
1018 if (apm_info.connection_version < 0x0102) {
1019 /* pretend we only have one battery. */
1020 if (which != 1)
1021 return APM_BAD_DEVICE;
1022 *nbat = 1;
1023 return apm_get_power_status(status, bat, life);
1024 }
1025
1026 if (apm_bios_call(APM_FUNC_GET_STATUS, (0x8000 | (which)), 0, &eax,
1027 &ebx, &ecx, &edx, &esi))
1028 return (eax >> 8) & 0xff;
1029 *status = ebx;
1030 *bat = ecx;
1031 *life = edx;
1032 *nbat = esi;
1033 return APM_SUCCESS;
1034}
1035#endif
1036
1037/**
1038 * apm_engage_power_management - enable PM on a device
1039 * @device: identity of device
1040 * @enable: on/off
1041 *
1042 * Activate or deactive power management on either a specific device
1043 * or the entire system (%APM_DEVICE_ALL).
1044 */
1045
1046static int apm_engage_power_management(u_short device, int enable)
1047{
1048 u32 eax;
1049
1050 if ((enable == 0) && (device == APM_DEVICE_ALL)
1051 && (apm_info.bios.flags & APM_BIOS_DISABLED))
1052 return APM_DISABLED;
1053 if (apm_bios_call_simple(APM_FUNC_ENGAGE_PM, device, enable, &eax))
1054 return (eax >> 8) & 0xff;
1055 if (device == APM_DEVICE_ALL) {
1056 if (enable)
1057 apm_info.bios.flags &= ~APM_BIOS_DISENGAGED;
1058 else
1059 apm_info.bios.flags |= APM_BIOS_DISENGAGED;
1060 }
1061 return APM_SUCCESS;
1062}
1063
1064#if defined(CONFIG_APM_DISPLAY_BLANK) && defined(CONFIG_VT)
1065
1066/**
1067 * apm_console_blank - blank the display
1068 * @blank: on/off
1069 *
1070 * Attempt to blank the console, firstly by blanking just video device
1071 * zero, and if that fails (some BIOSes don't support it) then it blanks
1072 * all video devices. Typically the BIOS will do laptop backlight and
1073 * monitor powerdown for us.
1074 */
1075
1076static int apm_console_blank(int blank)
1077{
Andreas Mohr87af2ff2006-06-23 02:04:17 -07001078 int error = APM_NOT_ENGAGED; /* silence gcc */
1079 int i;
Jordan Crouse3841b0a2006-01-06 00:12:16 -08001080 u_short state;
1081 static const u_short dev[3] = { 0x100, 0x1FF, 0x101 };
Linus Torvalds1da177e2005-04-16 15:20:36 -07001082
1083 state = blank ? APM_STATE_STANDBY : APM_STATE_READY;
Jordan Crouse3841b0a2006-01-06 00:12:16 -08001084
1085 for (i = 0; i < ARRAY_SIZE(dev); i++) {
1086 error = set_power_state(dev[i], state);
1087
1088 if ((error == APM_SUCCESS) || (error == APM_NO_ERROR))
1089 return 1;
1090
1091 if (error == APM_NOT_ENGAGED)
1092 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001093 }
Jordan Crouse3841b0a2006-01-06 00:12:16 -08001094
Samuel Thibault73374452006-04-18 22:21:50 -07001095 if (error == APM_NOT_ENGAGED) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001096 static int tried;
1097 int eng_error;
1098 if (tried++ == 0) {
1099 eng_error = apm_engage_power_management(APM_DEVICE_ALL, 1);
1100 if (eng_error) {
1101 apm_error("set display", error);
1102 apm_error("engage interface", eng_error);
1103 return 0;
1104 } else
1105 return apm_console_blank(blank);
1106 }
1107 }
1108 apm_error("set display", error);
1109 return 0;
1110}
1111#endif
1112
1113static int queue_empty(struct apm_user *as)
1114{
1115 return as->event_head == as->event_tail;
1116}
1117
1118static apm_event_t get_queued_event(struct apm_user *as)
1119{
Andreas Mohr87af2ff2006-06-23 02:04:17 -07001120 if (++as->event_tail >= APM_MAX_EVENTS)
1121 as->event_tail = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001122 return as->events[as->event_tail];
1123}
1124
1125static void queue_event(apm_event_t event, struct apm_user *sender)
1126{
1127 struct apm_user * as;
1128
1129 spin_lock(&user_list_lock);
1130 if (user_list == NULL)
1131 goto out;
1132 for (as = user_list; as != NULL; as = as->next) {
1133 if ((as == sender) || (!as->reader))
1134 continue;
Andreas Mohr87af2ff2006-06-23 02:04:17 -07001135 if (++as->event_head >= APM_MAX_EVENTS)
1136 as->event_head = 0;
1137
Linus Torvalds1da177e2005-04-16 15:20:36 -07001138 if (as->event_head == as->event_tail) {
1139 static int notified;
1140
1141 if (notified++ == 0)
1142 printk(KERN_ERR "apm: an event queue overflowed\n");
Andreas Mohr87af2ff2006-06-23 02:04:17 -07001143 if (++as->event_tail >= APM_MAX_EVENTS)
1144 as->event_tail = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001145 }
1146 as->events[as->event_head] = event;
1147 if ((!as->suser) || (!as->writer))
1148 continue;
1149 switch (event) {
1150 case APM_SYS_SUSPEND:
1151 case APM_USER_SUSPEND:
1152 as->suspends_pending++;
1153 suspends_pending++;
1154 break;
1155
1156 case APM_SYS_STANDBY:
1157 case APM_USER_STANDBY:
1158 as->standbys_pending++;
1159 standbys_pending++;
1160 break;
1161 }
1162 }
1163 wake_up_interruptible(&apm_waitqueue);
1164out:
1165 spin_unlock(&user_list_lock);
1166}
1167
Linus Torvalds1da177e2005-04-16 15:20:36 -07001168static void reinit_timer(void)
1169{
1170#ifdef INIT_TIMER_AFTER_SUSPEND
Ingo Molnar306e4402005-06-30 02:58:55 -07001171 unsigned long flags;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001172
1173 spin_lock_irqsave(&i8253_lock, flags);
Alan Coxc9919382007-05-08 00:29:58 -07001174 /* set the clock to HZ */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001175 outb_p(0x34, PIT_MODE); /* binary, mode 2, LSB/MSB, ch 0 */
1176 udelay(10);
1177 outb_p(LATCH & 0xff, PIT_CH0); /* LSB */
1178 udelay(10);
1179 outb(LATCH >> 8, PIT_CH0); /* MSB */
1180 udelay(10);
1181 spin_unlock_irqrestore(&i8253_lock, flags);
1182#endif
1183}
1184
1185static int suspend(int vetoable)
1186{
1187 int err;
1188 struct apm_user *as;
1189
1190 if (pm_send_all(PM_SUSPEND, (void *)3)) {
1191 /* Vetoed */
1192 if (vetoable) {
1193 if (apm_info.connection_version > 0x100)
1194 set_system_power_state(APM_STATE_REJECT);
1195 err = -EBUSY;
1196 ignore_sys_suspend = 0;
1197 printk(KERN_WARNING "apm: suspend was vetoed.\n");
1198 goto out;
1199 }
1200 printk(KERN_CRIT "apm: suspend was vetoed, but suspending anyway.\n");
1201 }
1202
1203 device_suspend(PMSG_SUSPEND);
1204 local_irq_disable();
1205 device_power_down(PMSG_SUSPEND);
1206
Linus Torvalds1da177e2005-04-16 15:20:36 -07001207 local_irq_enable();
1208
1209 save_processor_state();
1210 err = set_system_power_state(APM_STATE_SUSPEND);
Thomas Hood92c6dc52005-06-13 22:58:04 -07001211 ignore_normal_resume = 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001212 restore_processor_state();
1213
1214 local_irq_disable();
john stultzc7f40ff2006-09-25 23:32:35 -07001215 reinit_timer();
Linus Torvalds1da177e2005-04-16 15:20:36 -07001216
1217 if (err == APM_NO_ERROR)
1218 err = APM_SUCCESS;
1219 if (err != APM_SUCCESS)
1220 apm_error("suspend", err);
1221 err = (err == APM_SUCCESS) ? 0 : -EIO;
1222 device_power_up();
1223 local_irq_enable();
1224 device_resume();
1225 pm_send_all(PM_RESUME, (void *)0);
1226 queue_event(APM_NORMAL_RESUME, NULL);
1227 out:
1228 spin_lock(&user_list_lock);
1229 for (as = user_list; as != NULL; as = as->next) {
1230 as->suspend_wait = 0;
1231 as->suspend_result = err;
1232 }
1233 spin_unlock(&user_list_lock);
1234 wake_up_interruptible(&apm_suspend_waitqueue);
1235 return err;
1236}
1237
1238static void standby(void)
1239{
1240 int err;
1241
1242 local_irq_disable();
1243 device_power_down(PMSG_SUSPEND);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001244 local_irq_enable();
1245
1246 err = set_system_power_state(APM_STATE_STANDBY);
1247 if ((err != APM_SUCCESS) && (err != APM_NO_ERROR))
1248 apm_error("standby", err);
1249
1250 local_irq_disable();
1251 device_power_up();
1252 local_irq_enable();
1253}
1254
1255static apm_event_t get_event(void)
1256{
1257 int error;
Andreas Mohr87af2ff2006-06-23 02:04:17 -07001258 apm_event_t event = APM_NO_EVENTS; /* silence gcc */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001259 apm_eventinfo_t info;
1260
1261 static int notified;
1262
1263 /* we don't use the eventinfo */
1264 error = apm_get_event(&event, &info);
1265 if (error == APM_SUCCESS)
1266 return event;
1267
1268 if ((error != APM_NO_EVENTS) && (notified++ == 0))
1269 apm_error("get_event", error);
1270
1271 return 0;
1272}
1273
1274static void check_events(void)
1275{
1276 apm_event_t event;
1277 static unsigned long last_resume;
1278 static int ignore_bounce;
1279
1280 while ((event = get_event()) != 0) {
1281 if (debug) {
1282 if (event <= NR_APM_EVENT_NAME)
1283 printk(KERN_DEBUG "apm: received %s notify\n",
1284 apm_event_name[event - 1]);
1285 else
1286 printk(KERN_DEBUG "apm: received unknown "
1287 "event 0x%02x\n", event);
1288 }
1289 if (ignore_bounce
1290 && ((jiffies - last_resume) > bounce_interval))
1291 ignore_bounce = 0;
1292
1293 switch (event) {
1294 case APM_SYS_STANDBY:
1295 case APM_USER_STANDBY:
1296 queue_event(event, NULL);
1297 if (standbys_pending <= 0)
1298 standby();
1299 break;
1300
1301 case APM_USER_SUSPEND:
1302#ifdef CONFIG_APM_IGNORE_USER_SUSPEND
1303 if (apm_info.connection_version > 0x100)
1304 set_system_power_state(APM_STATE_REJECT);
1305 break;
1306#endif
1307 case APM_SYS_SUSPEND:
1308 if (ignore_bounce) {
1309 if (apm_info.connection_version > 0x100)
1310 set_system_power_state(APM_STATE_REJECT);
1311 break;
1312 }
1313 /*
1314 * If we are already processing a SUSPEND,
1315 * then further SUSPEND events from the BIOS
1316 * will be ignored. We also return here to
1317 * cope with the fact that the Thinkpads keep
1318 * sending a SUSPEND event until something else
1319 * happens!
1320 */
1321 if (ignore_sys_suspend)
1322 return;
1323 ignore_sys_suspend = 1;
1324 queue_event(event, NULL);
1325 if (suspends_pending <= 0)
1326 (void) suspend(1);
1327 break;
1328
1329 case APM_NORMAL_RESUME:
1330 case APM_CRITICAL_RESUME:
1331 case APM_STANDBY_RESUME:
1332 ignore_sys_suspend = 0;
1333 last_resume = jiffies;
1334 ignore_bounce = 1;
1335 if ((event != APM_NORMAL_RESUME)
1336 || (ignore_normal_resume == 0)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001337 device_resume();
1338 pm_send_all(PM_RESUME, (void *)0);
1339 queue_event(event, NULL);
1340 }
1341 ignore_normal_resume = 0;
1342 break;
1343
1344 case APM_CAPABILITY_CHANGE:
1345 case APM_LOW_BATTERY:
1346 case APM_POWER_STATUS_CHANGE:
1347 queue_event(event, NULL);
1348 /* If needed, notify drivers here */
1349 break;
1350
1351 case APM_UPDATE_TIME:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001352 break;
1353
1354 case APM_CRITICAL_SUSPEND:
1355 /*
1356 * We are not allowed to reject a critical suspend.
1357 */
1358 (void) suspend(0);
1359 break;
1360 }
1361 }
1362}
1363
1364static void apm_event_handler(void)
1365{
1366 static int pending_count = 4;
1367 int err;
1368
1369 if ((standbys_pending > 0) || (suspends_pending > 0)) {
1370 if ((apm_info.connection_version > 0x100) &&
1371 (pending_count-- <= 0)) {
1372 pending_count = 4;
1373 if (debug)
1374 printk(KERN_DEBUG "apm: setting state busy\n");
1375 err = set_system_power_state(APM_STATE_BUSY);
1376 if (err)
1377 apm_error("busy", err);
1378 }
1379 } else
1380 pending_count = 4;
1381 check_events();
1382}
1383
1384/*
1385 * This is the APM thread main loop.
1386 */
1387
1388static void apm_mainloop(void)
1389{
1390 DECLARE_WAITQUEUE(wait, current);
1391
1392 add_wait_queue(&apm_waitqueue, &wait);
1393 set_current_state(TASK_INTERRUPTIBLE);
1394 for (;;) {
1395 schedule_timeout(APM_CHECK_TIMEOUT);
Serge E. Hallynfc095612006-09-29 02:00:04 -07001396 if (kthread_should_stop())
Linus Torvalds1da177e2005-04-16 15:20:36 -07001397 break;
1398 /*
1399 * Ok, check all events, check for idle (and mark us sleeping
1400 * so as not to count towards the load average)..
1401 */
1402 set_current_state(TASK_INTERRUPTIBLE);
1403 apm_event_handler();
1404 }
1405 remove_wait_queue(&apm_waitqueue, &wait);
1406}
1407
1408static int check_apm_user(struct apm_user *as, const char *func)
1409{
1410 if ((as == NULL) || (as->magic != APM_BIOS_MAGIC)) {
1411 printk(KERN_ERR "apm: %s passed bad filp\n", func);
1412 return 1;
1413 }
1414 return 0;
1415}
1416
1417static ssize_t do_read(struct file *fp, char __user *buf, size_t count, loff_t *ppos)
1418{
1419 struct apm_user * as;
1420 int i;
1421 apm_event_t event;
1422
1423 as = fp->private_data;
1424 if (check_apm_user(as, "read"))
1425 return -EIO;
1426 if ((int)count < sizeof(apm_event_t))
1427 return -EINVAL;
1428 if ((queue_empty(as)) && (fp->f_flags & O_NONBLOCK))
1429 return -EAGAIN;
1430 wait_event_interruptible(apm_waitqueue, !queue_empty(as));
1431 i = count;
1432 while ((i >= sizeof(event)) && !queue_empty(as)) {
1433 event = get_queued_event(as);
1434 if (copy_to_user(buf, &event, sizeof(event))) {
1435 if (i < count)
1436 break;
1437 return -EFAULT;
1438 }
1439 switch (event) {
1440 case APM_SYS_SUSPEND:
1441 case APM_USER_SUSPEND:
1442 as->suspends_read++;
1443 break;
1444
1445 case APM_SYS_STANDBY:
1446 case APM_USER_STANDBY:
1447 as->standbys_read++;
1448 break;
1449 }
1450 buf += sizeof(event);
1451 i -= sizeof(event);
1452 }
1453 if (i < count)
1454 return count - i;
1455 if (signal_pending(current))
1456 return -ERESTARTSYS;
1457 return 0;
1458}
1459
1460static unsigned int do_poll(struct file *fp, poll_table * wait)
1461{
1462 struct apm_user * as;
1463
1464 as = fp->private_data;
1465 if (check_apm_user(as, "poll"))
1466 return 0;
1467 poll_wait(fp, &apm_waitqueue, wait);
1468 if (!queue_empty(as))
1469 return POLLIN | POLLRDNORM;
1470 return 0;
1471}
1472
1473static int do_ioctl(struct inode * inode, struct file *filp,
1474 u_int cmd, u_long arg)
1475{
1476 struct apm_user * as;
1477
1478 as = filp->private_data;
1479 if (check_apm_user(as, "ioctl"))
1480 return -EIO;
1481 if ((!as->suser) || (!as->writer))
1482 return -EPERM;
1483 switch (cmd) {
1484 case APM_IOC_STANDBY:
1485 if (as->standbys_read > 0) {
1486 as->standbys_read--;
1487 as->standbys_pending--;
1488 standbys_pending--;
1489 } else
1490 queue_event(APM_USER_STANDBY, as);
1491 if (standbys_pending <= 0)
1492 standby();
1493 break;
1494 case APM_IOC_SUSPEND:
1495 if (as->suspends_read > 0) {
1496 as->suspends_read--;
1497 as->suspends_pending--;
1498 suspends_pending--;
1499 } else
1500 queue_event(APM_USER_SUSPEND, as);
1501 if (suspends_pending <= 0) {
1502 return suspend(1);
1503 } else {
1504 as->suspend_wait = 1;
1505 wait_event_interruptible(apm_suspend_waitqueue,
1506 as->suspend_wait == 0);
1507 return as->suspend_result;
1508 }
1509 break;
1510 default:
1511 return -EINVAL;
1512 }
1513 return 0;
1514}
1515
1516static int do_release(struct inode * inode, struct file * filp)
1517{
1518 struct apm_user * as;
1519
1520 as = filp->private_data;
1521 if (check_apm_user(as, "release"))
1522 return 0;
1523 filp->private_data = NULL;
1524 if (as->standbys_pending > 0) {
1525 standbys_pending -= as->standbys_pending;
1526 if (standbys_pending <= 0)
1527 standby();
1528 }
1529 if (as->suspends_pending > 0) {
1530 suspends_pending -= as->suspends_pending;
1531 if (suspends_pending <= 0)
1532 (void) suspend(1);
1533 }
1534 spin_lock(&user_list_lock);
1535 if (user_list == as)
1536 user_list = as->next;
1537 else {
1538 struct apm_user * as1;
1539
1540 for (as1 = user_list;
1541 (as1 != NULL) && (as1->next != as);
1542 as1 = as1->next)
1543 ;
1544 if (as1 == NULL)
1545 printk(KERN_ERR "apm: filp not in user list\n");
1546 else
1547 as1->next = as->next;
1548 }
1549 spin_unlock(&user_list_lock);
1550 kfree(as);
1551 return 0;
1552}
1553
1554static int do_open(struct inode * inode, struct file * filp)
1555{
1556 struct apm_user * as;
1557
Robert P. J. Day5cbded52006-12-13 00:35:56 -08001558 as = kmalloc(sizeof(*as), GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001559 if (as == NULL) {
1560 printk(KERN_ERR "apm: cannot allocate struct of size %d bytes\n",
1561 sizeof(*as));
1562 return -ENOMEM;
1563 }
1564 as->magic = APM_BIOS_MAGIC;
1565 as->event_tail = as->event_head = 0;
1566 as->suspends_pending = as->standbys_pending = 0;
1567 as->suspends_read = as->standbys_read = 0;
1568 /*
1569 * XXX - this is a tiny bit broken, when we consider BSD
1570 * process accounting. If the device is opened by root, we
1571 * instantly flag that we used superuser privs. Who knows,
1572 * we might close the device immediately without doing a
1573 * privileged operation -- cevans
1574 */
1575 as->suser = capable(CAP_SYS_ADMIN);
1576 as->writer = (filp->f_mode & FMODE_WRITE) == FMODE_WRITE;
1577 as->reader = (filp->f_mode & FMODE_READ) == FMODE_READ;
1578 spin_lock(&user_list_lock);
1579 as->next = user_list;
1580 user_list = as;
1581 spin_unlock(&user_list_lock);
1582 filp->private_data = as;
1583 return 0;
1584}
1585
Alexey Dobriyan016d6f32007-02-13 13:26:23 +01001586static int proc_apm_show(struct seq_file *m, void *v)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001587{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001588 unsigned short bx;
1589 unsigned short cx;
1590 unsigned short dx;
1591 int error;
1592 unsigned short ac_line_status = 0xff;
1593 unsigned short battery_status = 0xff;
1594 unsigned short battery_flag = 0xff;
1595 int percentage = -1;
1596 int time_units = -1;
1597 char *units = "?";
1598
Linus Torvalds1da177e2005-04-16 15:20:36 -07001599 if ((num_online_cpus() == 1) &&
1600 !(error = apm_get_power_status(&bx, &cx, &dx))) {
1601 ac_line_status = (bx >> 8) & 0xff;
1602 battery_status = bx & 0xff;
1603 if ((cx & 0xff) != 0xff)
1604 percentage = cx & 0xff;
1605
1606 if (apm_info.connection_version > 0x100) {
1607 battery_flag = (cx >> 8) & 0xff;
1608 if (dx != 0xffff) {
1609 units = (dx & 0x8000) ? "min" : "sec";
1610 time_units = dx & 0x7fff;
1611 }
1612 }
1613 }
1614 /* Arguments, with symbols from linux/apm_bios.h. Information is
1615 from the Get Power Status (0x0a) call unless otherwise noted.
1616
1617 0) Linux driver version (this will change if format changes)
1618 1) APM BIOS Version. Usually 1.0, 1.1 or 1.2.
1619 2) APM flags from APM Installation Check (0x00):
1620 bit 0: APM_16_BIT_SUPPORT
1621 bit 1: APM_32_BIT_SUPPORT
1622 bit 2: APM_IDLE_SLOWS_CLOCK
1623 bit 3: APM_BIOS_DISABLED
1624 bit 4: APM_BIOS_DISENGAGED
1625 3) AC line status
1626 0x00: Off-line
1627 0x01: On-line
1628 0x02: On backup power (BIOS >= 1.1 only)
1629 0xff: Unknown
1630 4) Battery status
1631 0x00: High
1632 0x01: Low
1633 0x02: Critical
1634 0x03: Charging
1635 0x04: Selected battery not present (BIOS >= 1.2 only)
1636 0xff: Unknown
1637 5) Battery flag
1638 bit 0: High
1639 bit 1: Low
1640 bit 2: Critical
1641 bit 3: Charging
1642 bit 7: No system battery
1643 0xff: Unknown
1644 6) Remaining battery life (percentage of charge):
1645 0-100: valid
1646 -1: Unknown
1647 7) Remaining battery life (time units):
1648 Number of remaining minutes or seconds
1649 -1: Unknown
1650 8) min = minutes; sec = seconds */
1651
Alexey Dobriyan016d6f32007-02-13 13:26:23 +01001652 seq_printf(m, "%s %d.%d 0x%02x 0x%02x 0x%02x 0x%02x %d%% %d %s\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -07001653 driver_version,
1654 (apm_info.bios.version >> 8) & 0xff,
1655 apm_info.bios.version & 0xff,
1656 apm_info.bios.flags,
1657 ac_line_status,
1658 battery_status,
1659 battery_flag,
1660 percentage,
1661 time_units,
1662 units);
Alexey Dobriyan016d6f32007-02-13 13:26:23 +01001663 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001664}
1665
Alexey Dobriyan016d6f32007-02-13 13:26:23 +01001666static int proc_apm_open(struct inode *inode, struct file *file)
1667{
1668 return single_open(file, proc_apm_show, NULL);
1669}
1670
1671static const struct file_operations apm_file_ops = {
1672 .owner = THIS_MODULE,
1673 .open = proc_apm_open,
1674 .read = seq_read,
1675 .llseek = seq_lseek,
1676 .release = single_release,
1677};
1678
Linus Torvalds1da177e2005-04-16 15:20:36 -07001679static int apm(void *unused)
1680{
1681 unsigned short bx;
1682 unsigned short cx;
1683 unsigned short dx;
1684 int error;
1685 char * power_stat;
1686 char * bat_stat;
1687
Linus Torvalds1da177e2005-04-16 15:20:36 -07001688#ifdef CONFIG_SMP
1689 /* 2002/08/01 - WT
1690 * This is to avoid random crashes at boot time during initialization
1691 * on SMP systems in case of "apm=power-off" mode. Seen on ASUS A7M266D.
1692 * Some bioses don't like being called from CPU != 0.
1693 * Method suggested by Ingo Molnar.
1694 */
1695 set_cpus_allowed(current, cpumask_of_cpu(0));
1696 BUG_ON(smp_processor_id() != 0);
1697#endif
1698
1699 if (apm_info.connection_version == 0) {
1700 apm_info.connection_version = apm_info.bios.version;
1701 if (apm_info.connection_version > 0x100) {
1702 /*
1703 * We only support BIOSs up to version 1.2
1704 */
1705 if (apm_info.connection_version > 0x0102)
1706 apm_info.connection_version = 0x0102;
1707 error = apm_driver_version(&apm_info.connection_version);
1708 if (error != APM_SUCCESS) {
1709 apm_error("driver version", error);
1710 /* Fall back to an APM 1.0 connection. */
1711 apm_info.connection_version = 0x100;
1712 }
1713 }
1714 }
1715
1716 if (debug)
1717 printk(KERN_INFO "apm: Connection version %d.%d\n",
1718 (apm_info.connection_version >> 8) & 0xff,
1719 apm_info.connection_version & 0xff);
1720
1721#ifdef CONFIG_APM_DO_ENABLE
1722 if (apm_info.bios.flags & APM_BIOS_DISABLED) {
1723 /*
1724 * This call causes my NEC UltraLite Versa 33/C to hang if it
1725 * is booted with PM disabled but not in the docking station.
1726 * Unfortunate ...
1727 */
1728 error = apm_enable_power_management(1);
1729 if (error) {
1730 apm_error("enable power management", error);
1731 return -1;
1732 }
1733 }
1734#endif
1735
1736 if ((apm_info.bios.flags & APM_BIOS_DISENGAGED)
1737 && (apm_info.connection_version > 0x0100)) {
1738 error = apm_engage_power_management(APM_DEVICE_ALL, 1);
1739 if (error) {
1740 apm_error("engage power management", error);
1741 return -1;
1742 }
1743 }
1744
1745 if (debug && (num_online_cpus() == 1 || smp )) {
1746 error = apm_get_power_status(&bx, &cx, &dx);
1747 if (error)
1748 printk(KERN_INFO "apm: power status not available\n");
1749 else {
1750 switch ((bx >> 8) & 0xff) {
1751 case 0: power_stat = "off line"; break;
1752 case 1: power_stat = "on line"; break;
1753 case 2: power_stat = "on backup power"; break;
1754 default: power_stat = "unknown"; break;
1755 }
1756 switch (bx & 0xff) {
1757 case 0: bat_stat = "high"; break;
1758 case 1: bat_stat = "low"; break;
1759 case 2: bat_stat = "critical"; break;
1760 case 3: bat_stat = "charging"; break;
1761 default: bat_stat = "unknown"; break;
1762 }
1763 printk(KERN_INFO
1764 "apm: AC %s, battery status %s, battery life ",
1765 power_stat, bat_stat);
1766 if ((cx & 0xff) == 0xff)
1767 printk("unknown\n");
1768 else
1769 printk("%d%%\n", cx & 0xff);
1770 if (apm_info.connection_version > 0x100) {
1771 printk(KERN_INFO
1772 "apm: battery flag 0x%02x, battery life ",
1773 (cx >> 8) & 0xff);
1774 if (dx == 0xffff)
1775 printk("unknown\n");
1776 else
1777 printk("%d %s\n", dx & 0x7fff,
1778 (dx & 0x8000) ?
1779 "minutes" : "seconds");
1780 }
1781 }
1782 }
1783
1784 /* Install our power off handler.. */
1785 if (power_off)
1786 pm_power_off = apm_power_off;
1787
1788 if (num_online_cpus() == 1 || smp) {
1789#if defined(CONFIG_APM_DISPLAY_BLANK) && defined(CONFIG_VT)
1790 console_blank_hook = apm_console_blank;
1791#endif
1792 apm_mainloop();
1793#if defined(CONFIG_APM_DISPLAY_BLANK) && defined(CONFIG_VT)
1794 console_blank_hook = NULL;
1795#endif
1796 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001797
1798 return 0;
1799}
1800
1801#ifndef MODULE
1802static int __init apm_setup(char *str)
1803{
1804 int invert;
1805
1806 while ((str != NULL) && (*str != '\0')) {
1807 if (strncmp(str, "off", 3) == 0)
1808 apm_disabled = 1;
1809 if (strncmp(str, "on", 2) == 0)
1810 apm_disabled = 0;
1811 if ((strncmp(str, "bounce-interval=", 16) == 0) ||
1812 (strncmp(str, "bounce_interval=", 16) == 0))
1813 bounce_interval = simple_strtol(str + 16, NULL, 0);
1814 if ((strncmp(str, "idle-threshold=", 15) == 0) ||
1815 (strncmp(str, "idle_threshold=", 15) == 0))
1816 idle_threshold = simple_strtol(str + 15, NULL, 0);
1817 if ((strncmp(str, "idle-period=", 12) == 0) ||
1818 (strncmp(str, "idle_period=", 12) == 0))
1819 idle_period = simple_strtol(str + 12, NULL, 0);
1820 invert = (strncmp(str, "no-", 3) == 0) ||
1821 (strncmp(str, "no_", 3) == 0);
1822 if (invert)
1823 str += 3;
1824 if (strncmp(str, "debug", 5) == 0)
1825 debug = !invert;
1826 if ((strncmp(str, "power-off", 9) == 0) ||
1827 (strncmp(str, "power_off", 9) == 0))
1828 power_off = !invert;
1829 if (strncmp(str, "smp", 3) == 0)
1830 {
1831 smp = !invert;
1832 idle_threshold = 100;
1833 }
1834 if ((strncmp(str, "allow-ints", 10) == 0) ||
1835 (strncmp(str, "allow_ints", 10) == 0))
1836 apm_info.allow_ints = !invert;
1837 if ((strncmp(str, "broken-psr", 10) == 0) ||
1838 (strncmp(str, "broken_psr", 10) == 0))
1839 apm_info.get_power_status_broken = !invert;
1840 if ((strncmp(str, "realmode-power-off", 18) == 0) ||
1841 (strncmp(str, "realmode_power_off", 18) == 0))
1842 apm_info.realmode_power_off = !invert;
1843 str = strchr(str, ',');
1844 if (str != NULL)
1845 str += strspn(str, ", \t");
1846 }
1847 return 1;
1848}
1849
1850__setup("apm=", apm_setup);
1851#endif
1852
Arjan van de Ven5dfe4c92007-02-12 00:55:31 -08001853static const struct file_operations apm_bios_fops = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001854 .owner = THIS_MODULE,
1855 .read = do_read,
1856 .poll = do_poll,
1857 .ioctl = do_ioctl,
1858 .open = do_open,
1859 .release = do_release,
1860};
1861
1862static struct miscdevice apm_device = {
1863 APM_MINOR_DEV,
1864 "apm_bios",
1865 &apm_bios_fops
1866};
1867
1868
1869/* Simple "print if true" callback */
Jeff Garzik18552562007-10-03 15:15:40 -04001870static int __init print_if_true(const struct dmi_system_id *d)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001871{
1872 printk("%s\n", d->ident);
1873 return 0;
1874}
1875
1876/*
1877 * Some Bioses enable the PS/2 mouse (touchpad) at resume, even if it was
1878 * disabled before the suspend. Linux used to get terribly confused by that.
1879 */
Jeff Garzik18552562007-10-03 15:15:40 -04001880static int __init broken_ps2_resume(const struct dmi_system_id *d)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001881{
1882 printk(KERN_INFO "%s machine detected. Mousepad Resume Bug workaround hopefully not needed.\n", d->ident);
1883 return 0;
1884}
1885
1886/* Some bioses have a broken protected mode poweroff and need to use realmode */
Jeff Garzik18552562007-10-03 15:15:40 -04001887static int __init set_realmode_power_off(const struct dmi_system_id *d)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001888{
1889 if (apm_info.realmode_power_off == 0) {
1890 apm_info.realmode_power_off = 1;
1891 printk(KERN_INFO "%s bios detected. Using realmode poweroff only.\n", d->ident);
1892 }
1893 return 0;
1894}
1895
1896/* Some laptops require interrupts to be enabled during APM calls */
Jeff Garzik18552562007-10-03 15:15:40 -04001897static int __init set_apm_ints(const struct dmi_system_id *d)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001898{
1899 if (apm_info.allow_ints == 0) {
1900 apm_info.allow_ints = 1;
1901 printk(KERN_INFO "%s machine detected. Enabling interrupts during APM calls.\n", d->ident);
1902 }
1903 return 0;
1904}
1905
1906/* Some APM bioses corrupt memory or just plain do not work */
Jeff Garzik18552562007-10-03 15:15:40 -04001907static int __init apm_is_horked(const struct dmi_system_id *d)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001908{
1909 if (apm_info.disabled == 0) {
1910 apm_info.disabled = 1;
1911 printk(KERN_INFO "%s machine detected. Disabling APM.\n", d->ident);
1912 }
1913 return 0;
1914}
1915
Jeff Garzik18552562007-10-03 15:15:40 -04001916static int __init apm_is_horked_d850md(const struct dmi_system_id *d)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001917{
1918 if (apm_info.disabled == 0) {
1919 apm_info.disabled = 1;
1920 printk(KERN_INFO "%s machine detected. Disabling APM.\n", d->ident);
1921 printk(KERN_INFO "This bug is fixed in bios P15 which is available for \n");
1922 printk(KERN_INFO "download from support.intel.com \n");
1923 }
1924 return 0;
1925}
1926
1927/* Some APM bioses hang on APM idle calls */
Jeff Garzik18552562007-10-03 15:15:40 -04001928static int __init apm_likes_to_melt(const struct dmi_system_id *d)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001929{
1930 if (apm_info.forbid_idle == 0) {
1931 apm_info.forbid_idle = 1;
1932 printk(KERN_INFO "%s machine detected. Disabling APM idle calls.\n", d->ident);
1933 }
1934 return 0;
1935}
1936
1937/*
1938 * Check for clue free BIOS implementations who use
1939 * the following QA technique
1940 *
1941 * [ Write BIOS Code ]<------
1942 * | ^
1943 * < Does it Compile >----N--
1944 * |Y ^
1945 * < Does it Boot Win98 >-N--
1946 * |Y
1947 * [Ship It]
1948 *
1949 * Phoenix A04 08/24/2000 is known bad (Dell Inspiron 5000e)
1950 * Phoenix A07 09/29/2000 is known good (Dell Inspiron 5000)
1951 */
Jeff Garzik18552562007-10-03 15:15:40 -04001952static int __init broken_apm_power(const struct dmi_system_id *d)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001953{
1954 apm_info.get_power_status_broken = 1;
1955 printk(KERN_WARNING "BIOS strings suggest APM bugs, disabling power status reporting.\n");
1956 return 0;
1957}
1958
1959/*
1960 * This bios swaps the APM minute reporting bytes over (Many sony laptops
1961 * have this problem).
1962 */
Jeff Garzik18552562007-10-03 15:15:40 -04001963static int __init swab_apm_power_in_minutes(const struct dmi_system_id *d)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001964{
1965 apm_info.get_power_status_swabinminutes = 1;
1966 printk(KERN_WARNING "BIOS strings suggest APM reports battery life in minutes and wrong byte order.\n");
1967 return 0;
1968}
1969
1970static struct dmi_system_id __initdata apm_dmi_table[] = {
1971 {
1972 print_if_true,
1973 KERN_WARNING "IBM T23 - BIOS 1.03b+ and controller firmware 1.02+ may be needed for Linux APM.",
1974 { DMI_MATCH(DMI_SYS_VENDOR, "IBM"),
1975 DMI_MATCH(DMI_BIOS_VERSION, "1AET38WW (1.01b)"), },
1976 },
1977 { /* Handle problems with APM on the C600 */
1978 broken_ps2_resume, "Dell Latitude C600",
1979 { DMI_MATCH(DMI_SYS_VENDOR, "Dell"),
1980 DMI_MATCH(DMI_PRODUCT_NAME, "Latitude C600"), },
1981 },
1982 { /* Allow interrupts during suspend on Dell Latitude laptops*/
1983 set_apm_ints, "Dell Latitude",
1984 { DMI_MATCH(DMI_SYS_VENDOR, "Dell Computer Corporation"),
1985 DMI_MATCH(DMI_PRODUCT_NAME, "Latitude C510"), }
1986 },
1987 { /* APM crashes */
1988 apm_is_horked, "Dell Inspiron 2500",
1989 { DMI_MATCH(DMI_SYS_VENDOR, "Dell Computer Corporation"),
1990 DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 2500"),
1991 DMI_MATCH(DMI_BIOS_VENDOR,"Phoenix Technologies LTD"),
1992 DMI_MATCH(DMI_BIOS_VERSION,"A11"), },
1993 },
1994 { /* Allow interrupts during suspend on Dell Inspiron laptops*/
1995 set_apm_ints, "Dell Inspiron", {
1996 DMI_MATCH(DMI_SYS_VENDOR, "Dell Computer Corporation"),
1997 DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 4000"), },
1998 },
1999 { /* Handle problems with APM on Inspiron 5000e */
2000 broken_apm_power, "Dell Inspiron 5000e",
2001 { DMI_MATCH(DMI_BIOS_VENDOR, "Phoenix Technologies LTD"),
2002 DMI_MATCH(DMI_BIOS_VERSION, "A04"),
2003 DMI_MATCH(DMI_BIOS_DATE, "08/24/2000"), },
2004 },
2005 { /* Handle problems with APM on Inspiron 2500 */
2006 broken_apm_power, "Dell Inspiron 2500",
2007 { DMI_MATCH(DMI_BIOS_VENDOR, "Phoenix Technologies LTD"),
2008 DMI_MATCH(DMI_BIOS_VERSION, "A12"),
2009 DMI_MATCH(DMI_BIOS_DATE, "02/04/2002"), },
2010 },
2011 { /* APM crashes */
2012 apm_is_horked, "Dell Dimension 4100",
2013 { DMI_MATCH(DMI_SYS_VENDOR, "Dell Computer Corporation"),
2014 DMI_MATCH(DMI_PRODUCT_NAME, "XPS-Z"),
2015 DMI_MATCH(DMI_BIOS_VENDOR,"Intel Corp."),
2016 DMI_MATCH(DMI_BIOS_VERSION,"A11"), },
2017 },
2018 { /* Allow interrupts during suspend on Compaq Laptops*/
2019 set_apm_ints, "Compaq 12XL125",
2020 { DMI_MATCH(DMI_SYS_VENDOR, "Compaq"),
2021 DMI_MATCH(DMI_PRODUCT_NAME, "Compaq PC"),
2022 DMI_MATCH(DMI_BIOS_VENDOR, "Phoenix Technologies LTD"),
2023 DMI_MATCH(DMI_BIOS_VERSION,"4.06"), },
2024 },
2025 { /* Allow interrupts during APM or the clock goes slow */
2026 set_apm_ints, "ASUSTeK",
2027 { DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer Inc."),
2028 DMI_MATCH(DMI_PRODUCT_NAME, "L8400K series Notebook PC"), },
2029 },
2030 { /* APM blows on shutdown */
2031 apm_is_horked, "ABIT KX7-333[R]",
2032 { DMI_MATCH(DMI_BOARD_VENDOR, "ABIT"),
2033 DMI_MATCH(DMI_BOARD_NAME, "VT8367-8233A (KX7-333[R])"), },
2034 },
2035 { /* APM crashes */
2036 apm_is_horked, "Trigem Delhi3",
2037 { DMI_MATCH(DMI_SYS_VENDOR, "TriGem Computer, Inc"),
2038 DMI_MATCH(DMI_PRODUCT_NAME, "Delhi3"), },
2039 },
2040 { /* APM crashes */
2041 apm_is_horked, "Fujitsu-Siemens",
2042 { DMI_MATCH(DMI_BIOS_VENDOR, "hoenix/FUJITSU SIEMENS"),
2043 DMI_MATCH(DMI_BIOS_VERSION, "Version1.01"), },
2044 },
2045 { /* APM crashes */
2046 apm_is_horked_d850md, "Intel D850MD",
2047 { DMI_MATCH(DMI_BIOS_VENDOR, "Intel Corp."),
2048 DMI_MATCH(DMI_BIOS_VERSION, "MV85010A.86A.0016.P07.0201251536"), },
2049 },
2050 { /* APM crashes */
2051 apm_is_horked, "Intel D810EMO",
2052 { DMI_MATCH(DMI_BIOS_VENDOR, "Intel Corp."),
2053 DMI_MATCH(DMI_BIOS_VERSION, "MO81010A.86A.0008.P04.0004170800"), },
2054 },
2055 { /* APM crashes */
2056 apm_is_horked, "Dell XPS-Z",
2057 { DMI_MATCH(DMI_BIOS_VENDOR, "Intel Corp."),
2058 DMI_MATCH(DMI_BIOS_VERSION, "A11"),
2059 DMI_MATCH(DMI_PRODUCT_NAME, "XPS-Z"), },
2060 },
2061 { /* APM crashes */
2062 apm_is_horked, "Sharp PC-PJ/AX",
2063 { DMI_MATCH(DMI_SYS_VENDOR, "SHARP"),
2064 DMI_MATCH(DMI_PRODUCT_NAME, "PC-PJ/AX"),
2065 DMI_MATCH(DMI_BIOS_VENDOR,"SystemSoft"),
2066 DMI_MATCH(DMI_BIOS_VERSION,"Version R2.08"), },
2067 },
2068 { /* APM crashes */
2069 apm_is_horked, "Dell Inspiron 2500",
2070 { DMI_MATCH(DMI_SYS_VENDOR, "Dell Computer Corporation"),
2071 DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 2500"),
2072 DMI_MATCH(DMI_BIOS_VENDOR,"Phoenix Technologies LTD"),
2073 DMI_MATCH(DMI_BIOS_VERSION,"A11"), },
2074 },
2075 { /* APM idle hangs */
2076 apm_likes_to_melt, "Jabil AMD",
2077 { DMI_MATCH(DMI_BIOS_VENDOR, "American Megatrends Inc."),
2078 DMI_MATCH(DMI_BIOS_VERSION, "0AASNP06"), },
2079 },
2080 { /* APM idle hangs */
2081 apm_likes_to_melt, "AMI Bios",
2082 { DMI_MATCH(DMI_BIOS_VENDOR, "American Megatrends Inc."),
2083 DMI_MATCH(DMI_BIOS_VERSION, "0AASNP05"), },
2084 },
2085 { /* Handle problems with APM on Sony Vaio PCG-N505X(DE) */
2086 swab_apm_power_in_minutes, "Sony VAIO",
2087 { DMI_MATCH(DMI_BIOS_VENDOR, "Phoenix Technologies LTD"),
2088 DMI_MATCH(DMI_BIOS_VERSION, "R0206H"),
2089 DMI_MATCH(DMI_BIOS_DATE, "08/23/99"), },
2090 },
2091 { /* Handle problems with APM on Sony Vaio PCG-N505VX */
2092 swab_apm_power_in_minutes, "Sony VAIO",
2093 { DMI_MATCH(DMI_BIOS_VENDOR, "Phoenix Technologies LTD"),
2094 DMI_MATCH(DMI_BIOS_VERSION, "W2K06H0"),
2095 DMI_MATCH(DMI_BIOS_DATE, "02/03/00"), },
2096 },
2097 { /* Handle problems with APM on Sony Vaio PCG-XG29 */
2098 swab_apm_power_in_minutes, "Sony VAIO",
2099 { DMI_MATCH(DMI_BIOS_VENDOR, "Phoenix Technologies LTD"),
2100 DMI_MATCH(DMI_BIOS_VERSION, "R0117A0"),
2101 DMI_MATCH(DMI_BIOS_DATE, "04/25/00"), },
2102 },
2103 { /* Handle problems with APM on Sony Vaio PCG-Z600NE */
2104 swab_apm_power_in_minutes, "Sony VAIO",
2105 { DMI_MATCH(DMI_BIOS_VENDOR, "Phoenix Technologies LTD"),
2106 DMI_MATCH(DMI_BIOS_VERSION, "R0121Z1"),
2107 DMI_MATCH(DMI_BIOS_DATE, "05/11/00"), },
2108 },
2109 { /* Handle problems with APM on Sony Vaio PCG-Z600NE */
2110 swab_apm_power_in_minutes, "Sony VAIO",
2111 { DMI_MATCH(DMI_BIOS_VENDOR, "Phoenix Technologies LTD"),
2112 DMI_MATCH(DMI_BIOS_VERSION, "WME01Z1"),
2113 DMI_MATCH(DMI_BIOS_DATE, "08/11/00"), },
2114 },
2115 { /* Handle problems with APM on Sony Vaio PCG-Z600LEK(DE) */
2116 swab_apm_power_in_minutes, "Sony VAIO",
2117 { DMI_MATCH(DMI_BIOS_VENDOR, "Phoenix Technologies LTD"),
2118 DMI_MATCH(DMI_BIOS_VERSION, "R0206Z3"),
2119 DMI_MATCH(DMI_BIOS_DATE, "12/25/00"), },
2120 },
2121 { /* Handle problems with APM on Sony Vaio PCG-Z505LS */
2122 swab_apm_power_in_minutes, "Sony VAIO",
2123 { DMI_MATCH(DMI_BIOS_VENDOR, "Phoenix Technologies LTD"),
2124 DMI_MATCH(DMI_BIOS_VERSION, "R0203D0"),
2125 DMI_MATCH(DMI_BIOS_DATE, "05/12/00"), },
2126 },
2127 { /* Handle problems with APM on Sony Vaio PCG-Z505LS */
2128 swab_apm_power_in_minutes, "Sony VAIO",
2129 { DMI_MATCH(DMI_BIOS_VENDOR, "Phoenix Technologies LTD"),
2130 DMI_MATCH(DMI_BIOS_VERSION, "R0203Z3"),
2131 DMI_MATCH(DMI_BIOS_DATE, "08/25/00"), },
2132 },
2133 { /* Handle problems with APM on Sony Vaio PCG-Z505LS (with updated BIOS) */
2134 swab_apm_power_in_minutes, "Sony VAIO",
2135 { DMI_MATCH(DMI_BIOS_VENDOR, "Phoenix Technologies LTD"),
2136 DMI_MATCH(DMI_BIOS_VERSION, "R0209Z3"),
2137 DMI_MATCH(DMI_BIOS_DATE, "05/12/01"), },
2138 },
2139 { /* Handle problems with APM on Sony Vaio PCG-F104K */
2140 swab_apm_power_in_minutes, "Sony VAIO",
2141 { DMI_MATCH(DMI_BIOS_VENDOR, "Phoenix Technologies LTD"),
2142 DMI_MATCH(DMI_BIOS_VERSION, "R0204K2"),
2143 DMI_MATCH(DMI_BIOS_DATE, "08/28/00"), },
2144 },
2145
2146 { /* Handle problems with APM on Sony Vaio PCG-C1VN/C1VE */
2147 swab_apm_power_in_minutes, "Sony VAIO",
2148 { DMI_MATCH(DMI_BIOS_VENDOR, "Phoenix Technologies LTD"),
2149 DMI_MATCH(DMI_BIOS_VERSION, "R0208P1"),
2150 DMI_MATCH(DMI_BIOS_DATE, "11/09/00"), },
2151 },
2152 { /* Handle problems with APM on Sony Vaio PCG-C1VE */
2153 swab_apm_power_in_minutes, "Sony VAIO",
2154 { DMI_MATCH(DMI_BIOS_VENDOR, "Phoenix Technologies LTD"),
2155 DMI_MATCH(DMI_BIOS_VERSION, "R0204P1"),
2156 DMI_MATCH(DMI_BIOS_DATE, "09/12/00"), },
2157 },
2158 { /* Handle problems with APM on Sony Vaio PCG-C1VE */
2159 swab_apm_power_in_minutes, "Sony VAIO",
2160 { DMI_MATCH(DMI_BIOS_VENDOR, "Phoenix Technologies LTD"),
2161 DMI_MATCH(DMI_BIOS_VERSION, "WXPO1Z3"),
2162 DMI_MATCH(DMI_BIOS_DATE, "10/26/01"), },
2163 },
2164 { /* broken PM poweroff bios */
2165 set_realmode_power_off, "Award Software v4.60 PGMA",
2166 { DMI_MATCH(DMI_BIOS_VENDOR, "Award Software International, Inc."),
2167 DMI_MATCH(DMI_BIOS_VERSION, "4.60 PGMA"),
2168 DMI_MATCH(DMI_BIOS_DATE, "134526184"), },
2169 },
2170
2171 /* Generic per vendor APM settings */
2172
2173 { /* Allow interrupts during suspend on IBM laptops */
2174 set_apm_ints, "IBM",
2175 { DMI_MATCH(DMI_SYS_VENDOR, "IBM"), },
2176 },
2177
2178 { }
2179};
2180
2181/*
2182 * Just start the APM thread. We do NOT want to do APM BIOS
2183 * calls from anything but the APM thread, if for no other reason
2184 * than the fact that we don't trust the APM BIOS. This way,
2185 * most common APM BIOS problems that lead to protection errors
2186 * etc will have at least some level of being contained...
2187 *
2188 * In short, if something bad happens, at least we have a choice
2189 * of just killing the apm thread..
2190 */
2191static int __init apm_init(void)
2192{
2193 struct proc_dir_entry *apm_proc;
Zachary Amsden92f17f02006-01-06 00:11:58 -08002194 struct desc_struct *gdt;
Serge E. Hallynfc095612006-09-29 02:00:04 -07002195 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002196
2197 dmi_check_system(apm_dmi_table);
2198
Rusty Russell6020c8f2006-12-07 02:14:08 +01002199 if (apm_info.bios.version == 0 || paravirt_enabled()) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002200 printk(KERN_INFO "apm: BIOS not found.\n");
2201 return -ENODEV;
2202 }
2203 printk(KERN_INFO
2204 "apm: BIOS version %d.%d Flags 0x%02x (Driver version %s)\n",
2205 ((apm_info.bios.version >> 8) & 0xff),
2206 (apm_info.bios.version & 0xff),
2207 apm_info.bios.flags,
2208 driver_version);
2209 if ((apm_info.bios.flags & APM_32_BIT_SUPPORT) == 0) {
2210 printk(KERN_INFO "apm: no 32 bit BIOS support\n");
2211 return -ENODEV;
2212 }
2213
2214 if (allow_ints)
2215 apm_info.allow_ints = 1;
2216 if (broken_psr)
2217 apm_info.get_power_status_broken = 1;
2218 if (realmode_power_off)
2219 apm_info.realmode_power_off = 1;
2220 /* User can override, but default is to trust DMI */
2221 if (apm_disabled != -1)
2222 apm_info.disabled = apm_disabled;
2223
2224 /*
2225 * Fix for the Compaq Contura 3/25c which reports BIOS version 0.1
2226 * but is reportedly a 1.0 BIOS.
2227 */
2228 if (apm_info.bios.version == 0x001)
2229 apm_info.bios.version = 0x100;
2230
2231 /* BIOS < 1.2 doesn't set cseg_16_len */
2232 if (apm_info.bios.version < 0x102)
2233 apm_info.bios.cseg_16_len = 0; /* 64k */
2234
2235 if (debug) {
Eugene Teo07a30462007-07-31 00:38:15 -07002236 printk(KERN_INFO "apm: entry %x:%x cseg16 %x dseg %x",
Linus Torvalds1da177e2005-04-16 15:20:36 -07002237 apm_info.bios.cseg, apm_info.bios.offset,
2238 apm_info.bios.cseg_16, apm_info.bios.dseg);
2239 if (apm_info.bios.version > 0x100)
2240 printk(" cseg len %x, dseg len %x",
2241 apm_info.bios.cseg_len,
2242 apm_info.bios.dseg_len);
2243 if (apm_info.bios.version > 0x101)
2244 printk(" cseg16 len %x", apm_info.bios.cseg_16_len);
2245 printk("\n");
2246 }
2247
2248 if (apm_info.disabled) {
2249 printk(KERN_NOTICE "apm: disabled on user request.\n");
2250 return -ENODEV;
2251 }
2252 if ((num_online_cpus() > 1) && !power_off && !smp) {
2253 printk(KERN_NOTICE "apm: disabled - APM is not SMP safe.\n");
2254 apm_info.disabled = 1;
2255 return -ENODEV;
2256 }
Len Brown9f9adec2007-12-13 17:38:03 -05002257 if (pm_flags & PM_ACPI) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002258 printk(KERN_NOTICE "apm: overridden by ACPI.\n");
2259 apm_info.disabled = 1;
2260 return -ENODEV;
2261 }
Len Brown9f9adec2007-12-13 17:38:03 -05002262 pm_flags |= PM_APM;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002263
2264 /*
2265 * Set up a segment that references the real mode segment 0x40
2266 * that extends up to the end of page zero (that we have reserved).
2267 * This is for buggy BIOS's that refer to (real mode) segment 0x40
2268 * even though they are called in protected mode.
2269 */
2270 set_base(bad_bios_desc, __va((unsigned long)0x40 << 4));
2271 _set_limit((char *)&bad_bios_desc, 4095 - (0x40 << 4));
2272
Zachary Amsden3012d2d2006-01-06 00:11:53 -08002273 /*
2274 * Set up the long jump entry point to the APM BIOS, which is called
2275 * from inline assembly.
2276 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002277 apm_bios_entry.offset = apm_info.bios.offset;
2278 apm_bios_entry.segment = APM_CS;
2279
Zachary Amsden3012d2d2006-01-06 00:11:53 -08002280 /*
2281 * The APM 1.1 BIOS is supposed to provide limit information that it
2282 * recognizes. Many machines do this correctly, but many others do
2283 * not restrict themselves to their claimed limit. When this happens,
2284 * they will cause a segmentation violation in the kernel at boot time.
2285 * Most BIOS's, however, will respect a 64k limit, so we use that.
Zachary Amsden92f17f02006-01-06 00:11:58 -08002286 *
2287 * Note we only set APM segments on CPU zero, since we pin the APM
2288 * code to that CPU.
Zachary Amsden3012d2d2006-01-06 00:11:53 -08002289 */
Zachary Amsden92f17f02006-01-06 00:11:58 -08002290 gdt = get_cpu_gdt_table(0);
2291 set_base(gdt[APM_CS >> 3],
2292 __va((unsigned long)apm_info.bios.cseg << 4));
2293 set_base(gdt[APM_CS_16 >> 3],
2294 __va((unsigned long)apm_info.bios.cseg_16 << 4));
2295 set_base(gdt[APM_DS >> 3],
2296 __va((unsigned long)apm_info.bios.dseg << 4));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002297
Alexey Dobriyan016d6f32007-02-13 13:26:23 +01002298 apm_proc = create_proc_entry("apm", 0, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002299 if (apm_proc)
Alexey Dobriyan016d6f32007-02-13 13:26:23 +01002300 apm_proc->proc_fops = &apm_file_ops;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002301
Serge E. Hallynfc095612006-09-29 02:00:04 -07002302 kapmd_task = kthread_create(apm, NULL, "kapmd");
2303 if (IS_ERR(kapmd_task)) {
2304 printk(KERN_ERR "apm: disabled - Unable to start kernel "
2305 "thread.\n");
2306 err = PTR_ERR(kapmd_task);
2307 kapmd_task = NULL;
Neil Hormanc94a62a2006-09-25 23:32:21 -07002308 remove_proc_entry("apm", NULL);
Serge E. Hallynfc095612006-09-29 02:00:04 -07002309 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002310 }
Serge E. Hallynfc095612006-09-29 02:00:04 -07002311 wake_up_process(kapmd_task);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002312
2313 if (num_online_cpus() > 1 && !smp ) {
2314 printk(KERN_NOTICE
2315 "apm: disabled - APM is not SMP safe (power off active).\n");
2316 return 0;
2317 }
2318
Neil Hormanc94a62a2006-09-25 23:32:21 -07002319 /*
2320 * Note we don't actually care if the misc_device cannot be registered.
2321 * this driver can do its job without it, even if userspace can't
2322 * control it. just log the error
2323 */
2324 if (misc_register(&apm_device))
2325 printk(KERN_WARNING "apm: Could not register misc device.\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002326
2327 if (HZ != 100)
2328 idle_period = (idle_period * HZ) / 100;
2329 if (idle_threshold < 100) {
2330 original_pm_idle = pm_idle;
2331 pm_idle = apm_cpu_idle;
2332 set_pm_idle = 1;
2333 }
2334
2335 return 0;
2336}
2337
2338static void __exit apm_exit(void)
2339{
2340 int error;
2341
2342 if (set_pm_idle) {
2343 pm_idle = original_pm_idle;
2344 /*
2345 * We are about to unload the current idle thread pm callback
2346 * (pm_idle), Wait for all processors to update cached/local
2347 * copies of pm_idle before proceeding.
2348 */
2349 cpu_idle_wait();
2350 }
2351 if (((apm_info.bios.flags & APM_BIOS_DISENGAGED) == 0)
2352 && (apm_info.connection_version > 0x0100)) {
2353 error = apm_engage_power_management(APM_DEVICE_ALL, 0);
2354 if (error)
2355 apm_error("disengage power management", error);
2356 }
2357 misc_deregister(&apm_device);
2358 remove_proc_entry("apm", NULL);
2359 if (power_off)
2360 pm_power_off = NULL;
Serge E. Hallynfc095612006-09-29 02:00:04 -07002361 if (kapmd_task) {
2362 kthread_stop(kapmd_task);
2363 kapmd_task = NULL;
2364 }
Len Brown9f9adec2007-12-13 17:38:03 -05002365 pm_flags &= ~PM_APM;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002366}
2367
2368module_init(apm_init);
2369module_exit(apm_exit);
2370
2371MODULE_AUTHOR("Stephen Rothwell");
2372MODULE_DESCRIPTION("Advanced Power Management");
2373MODULE_LICENSE("GPL");
2374module_param(debug, bool, 0644);
2375MODULE_PARM_DESC(debug, "Enable debug mode");
2376module_param(power_off, bool, 0444);
2377MODULE_PARM_DESC(power_off, "Enable power off");
2378module_param(bounce_interval, int, 0444);
2379MODULE_PARM_DESC(bounce_interval,
2380 "Set the number of ticks to ignore suspend bounces");
2381module_param(allow_ints, bool, 0444);
2382MODULE_PARM_DESC(allow_ints, "Allow interrupts during BIOS calls");
2383module_param(broken_psr, bool, 0444);
2384MODULE_PARM_DESC(broken_psr, "BIOS has a broken GetPowerStatus call");
2385module_param(realmode_power_off, bool, 0444);
2386MODULE_PARM_DESC(realmode_power_off,
2387 "Switch to real mode before powering off");
2388module_param(idle_threshold, int, 0444);
2389MODULE_PARM_DESC(idle_threshold,
2390 "System idle percentage above which to make APM BIOS idle calls");
2391module_param(idle_period, int, 0444);
2392MODULE_PARM_DESC(idle_period,
2393 "Period (in sec/100) over which to caculate the idle percentage");
2394module_param(smp, bool, 0444);
2395MODULE_PARM_DESC(smp,
2396 "Set this to enable APM use on an SMP platform. Use with caution on older systems");
2397MODULE_ALIAS_MISCDEV(APM_MINOR_DEV);