Import 2.3.12pre3
[davej-history.git] / arch / i386 / kernel / apm.c
blob7931e8df8e7ec105934a3d88cd74589d30f0be70
1 /* -*- linux-c -*-
2 * APM BIOS driver for Linux
3 * Copyright 1994-1998 Stephen Rothwell
4 * (Stephen.Rothwell@canb.auug.org.au)
5 * Development of this driver was funded by NEC Australia P/L
6 * and NEC Corporation
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.
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.
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 (Stephen.Rothwell@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
37 * History:
38 * 0.6b: first version in official kernel, Linux 1.3.46
39 * 0.7: changed /proc/apm format, Linux 1.3.58
40 * 0.8: fixed gcc 2.7.[12] compilation problems, Linux 1.3.59
41 * 0.9: only call bios if bios is present, Linux 1.3.72
42 * 1.0: use fixed device number, consolidate /proc/apm into this file,
43 * Linux 1.3.85
44 * 1.1: support user-space standby and suspend, power off after system
45 * halted, Linux 1.3.98
46 * 1.2: When resetting RTC after resume, take care so that the time
47 * is only incorrect by 30-60mS (vs. 1S previously) (Gabor J. Toth
48 * <jtoth@princeton.edu>); improve interaction between
49 * screen-blanking and gpm (Stephen Rothwell); Linux 1.99.4
50 * 1.2a:Simple change to stop mysterious bug reports with SMP also added
51 * levels to the printk calls. APM is not defined for SMP machines.
52 * The new replacment for it is, but Linux doesn't yet support this.
53 * Alan Cox Linux 2.1.55
54 * 1.3: Set up a valid data descriptor 0x40 for buggy BIOS's
55 * 1.4: Upgraded to support APM 1.2. Integrated ThinkPad suspend patch by
56 * Dean Gaudet <dgaudet@arctic.org>.
57 * C. Scott Ananian <cananian@alumni.princeton.edu> Linux 2.1.87
58 * 1.5: Fix segment register reloading (in case of bad segments saved
59 * across BIOS call).
60 * Stephen Rothwell
61 * 1.6: Cope with complier/assembler differences.
62 * Only try to turn off the first display device.
63 * Fix OOPS at power off with no APM BIOS by Jan Echternach
64 * <echter@informatik.uni-rostock.de>
65 * Stephen Rothwell
66 * 1.7: Modify driver's cached copy of the disabled/disengaged flags
67 * to reflect current state of APM BIOS.
68 * Chris Rankin <rankinc@bellsouth.net>
69 * Reset interrupt 0 timer to 100Hz after suspend
70 * Chad Miller <cmiller@surfsouth.com>
71 * Add CONFIG_APM_IGNORE_SUSPEND_BOUNCE
72 * Richard Gooch <rgooch@atnf.csiro.au>
73 * Allow boot time disabling of APM
74 * Make boot messages far less verbose by default
75 * Make asm safer
76 * Stephen Rothwell
77 * 1.8: Add CONFIG_APM_RTC_IS_GMT
78 * Richard Gooch <rgooch@atnf.csiro.au>
79 * change APM_NOINTS to CONFIG_APM_ALLOW_INTS
80 * remove dependency on CONFIG_PROC_FS
81 * Stephen Rothwell
82 * 1.9: Fix small typo. <laslo@ilo.opole.pl>
83 * Try to cope with BIOS's that need to have all display
84 * devices blanked and not just the first one.
85 * Ross Paterson <ross@soi.city.ac.uk>
86 * Fix segment limit setting it has always been wrong as
87 * the segments needed to have byte granularity.
88 * Mark a few things __init.
89 * Add hack to allow power off of SMP systems by popular request.
90 * Use CONFIG_SMP instead of __SMP__
91 * Ignore BOUNCES for three seconds.
92 * Stephen Rothwell
94 * APM 1.1 Reference:
96 * Intel Corporation, Microsoft Corporation. Advanced Power Management
97 * (APM) BIOS Interface Specification, Revision 1.1, September 1993.
98 * Intel Order Number 241704-001. Microsoft Part Number 781-110-X01.
100 * [This document is available free from Intel by calling 800.628.8686 (fax
101 * 916.356.6100) or 800.548.4725; or via anonymous ftp from
102 * ftp://ftp.intel.com/pub/IAL/software_specs/apmv11.doc. It is also
103 * available from Microsoft by calling 206.882.8080.]
105 * APM 1.2 Reference:
106 * Intel Corporation, Microsoft Corporation. Advanced Power Management
107 * (APM) BIOS Interface Specification, Revision 1.2, February 1996.
109 * [This document is available from Intel at:
110 * http://www.intel.com/IAL/powermgm
111 * or Microsoft at
112 * http://www.microsoft.com/windows/thirdparty/hardware/pcfuture.htm
116 #include <linux/config.h>
117 #include <linux/module.h>
119 #include <linux/poll.h>
120 #include <linux/types.h>
121 #include <linux/stddef.h>
122 #include <linux/timer.h>
123 #include <linux/fcntl.h>
124 #include <linux/malloc.h>
125 #include <linux/linkage.h>
126 #include <linux/stat.h>
127 #include <linux/proc_fs.h>
128 #include <linux/miscdevice.h>
129 #include <linux/apm_bios.h>
130 #include <linux/init.h>
132 #include <asm/system.h>
133 #include <asm/uaccess.h>
134 #include <asm/desc.h>
136 EXPORT_SYMBOL(apm_register_callback);
137 EXPORT_SYMBOL(apm_unregister_callback);
139 extern unsigned long get_cmos_time(void);
142 * The apm_bios device is one of the misc char devices.
143 * This is its minor number.
145 #define APM_MINOR_DEV 134
147 /* Configurable options:
149 * CONFIG_APM_IGNORE_USER_SUSPEND: define to ignore USER SUSPEND requests.
150 * This is necessary on the NEC Versa M series, which generates these when
151 * resuming from SYSTEM SUSPEND. However, enabling this on other laptops
152 * will cause the laptop to generate a CRITICAL SUSPEND when an appropriate
153 * USER SUSPEND is ignored -- this may prevent the APM driver from updating
154 * the system time on a RESUME.
156 * CONFIG_APM_DO_ENABLE: enable APM features at boot time. From page 36 of
157 * the specification: "When disabled, the APM BIOS does not automatically
158 * power manage devices, enter the Standby State, enter the Suspend State,
159 * or take power saving steps in response to CPU Idle calls." This driver
160 * will make CPU Idle calls when Linux is idle (unless this feature is
161 * turned off -- see below). This should always save battery power, but
162 * more complicated APM features will be dependent on your BIOS
163 * implementation. You may need to turn this option off if your computer
164 * hangs at boot time when using APM support, or if it beeps continuously
165 * instead of suspending. Turn this off if you have a NEC UltraLite Versa
166 * 33/C or a Toshiba T400CDT. This is off by default since most machines
167 * do fine without this feature.
169 * CONFIG_APM_CPU_IDLE: enable calls to APM CPU Idle/CPU Busy inside the
170 * idle loop. On some machines, this can activate improved power savings,
171 * such as a slowed CPU clock rate, when the machine is idle. These idle
172 * call is made after the idle loop has run for some length of time (e.g.,
173 * 333 mS). On some machines, this will cause a hang at boot time or
174 * whenever the CPU becomes idle.
176 * CONFIG_APM_DISPLAY_BLANK: enable console blanking using the APM. Some
177 * laptops can use this to turn of the LCD backlight when the VC screen
178 * blanker blanks the screen. Note that this is only used by the VC screen
179 * blanker, and probably won't turn off the backlight when using X11. Some
180 * problems have been reported when using this option with gpm (if you'd
181 * like to debug this, please do so).
183 * CONFIG_APM_IGNORE_MULTIPLE_SUSPEND: The IBM TP560 bios seems to insist
184 * on returning multiple suspend/standby events whenever one occurs. We
185 * really only need one at a time, so just ignore any beyond the first.
186 * This is probably safe on most laptops.
188 * If you are debugging the APM support for your laptop, note that code for
189 * all of these options is contained in this file, so you can #define or
190 * #undef these on the next line to avoid recompiling the whole kernel.
194 /* KNOWN PROBLEM MACHINES:
196 * U: TI 4000M TravelMate: BIOS is *NOT* APM compliant
197 * [Confirmed by TI representative]
198 * ?: ACER 486DX4/75: uses dseg 0040, in violation of APM specification
199 * [Confirmed by BIOS disassembly]
200 * [This may work now ...]
201 * P: Toshiba 1950S: battery life information only gets updated after resume
202 * P: Midwest Micro Soundbook Elite DX2/66 monochrome: screen blanking
203 * broken in BIOS [Reported by Garst R. Reese <reese@isn.net>]
205 * Legend: U = unusable with APM patches
206 * P = partially usable with APM patches
210 * Define to have debug messages.
212 #undef APM_DEBUG
215 * Define to always call the APM BIOS busy routine even if the clock was
216 * not slowed by the idle routine.
218 #define ALWAYS_CALL_BUSY
221 * Define to make the APM BIOS calls zero all data segment registers (so
222 * that an incorrect BIOS implementation will cause a kernel panic if it
223 * tries to write to arbitrary memory).
225 #define APM_ZERO_SEGS
228 * Define to make all _set_limit calls use 64k limits. The APM 1.1 BIOS is
229 * supposed to provide limit information that it recognizes. Many machines
230 * do this correctly, but many others do not restrict themselves to their
231 * claimed limit. When this happens, they will cause a segmentation
232 * violation in the kernel at boot time. Most BIOS's, however, will
233 * respect a 64k limit, so we use that. If you want to be pedantic and
234 * hold your BIOS to its claims, then undefine this.
236 #define APM_RELAX_SEGMENTS
239 * Define to re-initialize the interrupt 0 timer to 100 Hz after a suspend.
240 * This patched by Chad Miller <cmiller@surfsouth.com>, orig code by David
241 * Chen <chen@ctpa04.mit.edu>
243 #undef INIT_TIMER_AFTER_SUSPEND
245 #ifdef INIT_TIMER_AFTER_SUSPEND
246 #include <linux/timex.h>
247 #include <asm/io.h>
248 #include <linux/delay.h>
249 #endif
252 * Need to poll the APM BIOS every second
254 #define APM_CHECK_TIMEOUT (HZ)
257 * If CONFIG_APM_IGNORE_SUSPEND_BOUNCE is defined then
258 * ignore suspend events for this amount of time
260 #define BOUNCE_INTERVAL (3 * HZ)
263 * Save a segment register away
265 #define savesegment(seg, where) \
266 __asm__ __volatile__("movl %%" #seg ",%0" : "=m" (where))
269 * Forward declarations
271 static void suspend(void);
272 static void standby(void);
273 static void set_time(void);
275 static void check_events(void);
276 static void do_apm_timer(unsigned long);
278 static int do_open(struct inode *, struct file *);
279 static int do_release(struct inode *, struct file *);
280 static ssize_t do_read(struct file *, char *, size_t , loff_t *);
281 static unsigned int do_poll(struct file *, poll_table *);
282 static int do_ioctl(struct inode *, struct file *, u_int, u_long);
284 static int apm_get_info(char *, char **, off_t, int, int);
286 extern int apm_register_callback(int (*)(apm_event_t));
287 extern void apm_unregister_callback(int (*)(apm_event_t));
290 * Local variables
292 static asmlinkage struct {
293 unsigned long offset;
294 unsigned short segment;
295 } apm_bios_entry;
296 static int apm_enabled = 0;
297 static int smp_hack = 0;
298 #ifdef CONFIG_APM_CPU_IDLE
299 static int clock_slowed = 0;
300 #endif
301 static int suspends_pending = 0;
302 static int standbys_pending = 0;
303 #ifdef CONFIG_APM_IGNORE_MULTIPLE_SUSPEND
304 static int waiting_for_resume = 0;
305 #endif
307 #ifdef CONFIG_APM_RTC_IS_GMT
308 # define clock_cmos_diff 0
309 # define got_clock_diff 1
310 #else
311 static long clock_cmos_diff;
312 static int got_clock_diff = 0;
313 #endif
314 static int debug = 0;
315 static int apm_disabled = 0;
317 static DECLARE_WAIT_QUEUE_HEAD(process_list);
318 static struct apm_bios_struct * user_list = NULL;
320 static struct timer_list apm_timer;
322 static char driver_version[] = "1.9"; /* no spaces */
324 #ifdef APM_DEBUG
325 static char * apm_event_name[] = {
326 "system standby",
327 "system suspend",
328 "normal resume",
329 "critical resume",
330 "low battery",
331 "power status change",
332 "update time",
333 "critical suspend",
334 "user standby",
335 "user suspend",
336 "system standby resume",
337 "capabilities change"
339 #define NR_APM_EVENT_NAME \
340 (sizeof(apm_event_name) / sizeof(apm_event_name[0]))
341 #endif
343 static struct file_operations apm_bios_fops = {
344 NULL, /* lseek */
345 do_read,
346 NULL, /* write */
347 NULL, /* readdir */
348 do_poll,
349 do_ioctl,
350 NULL, /* mmap */
351 do_open,
352 NULL, /* flush */
353 do_release,
354 NULL, /* fsync */
355 NULL /* fasync */
358 static struct miscdevice apm_device = {
359 APM_MINOR_DEV,
360 "apm",
361 &apm_bios_fops
364 typedef struct callback_list_t {
365 int (* callback)(apm_event_t);
366 struct callback_list_t * next;
367 } callback_list_t;
369 static callback_list_t * callback_list = NULL;
371 typedef struct lookup_t {
372 int key;
373 char * msg;
374 } lookup_t;
376 static const lookup_t error_table[] = {
377 /* N/A { APM_SUCCESS, "Operation succeeded" }, */
378 { APM_DISABLED, "Power management disabled" },
379 { APM_CONNECTED, "Real mode interface already connected" },
380 { APM_NOT_CONNECTED, "Interface not connected" },
381 { APM_16_CONNECTED, "16 bit interface already connected" },
382 /* N/A { APM_16_UNSUPPORTED, "16 bit interface not supported" }, */
383 { APM_32_CONNECTED, "32 bit interface already connected" },
384 { APM_32_UNSUPPORTED, "32 bit interface not supported" },
385 { APM_BAD_DEVICE, "Unrecognized device ID" },
386 { APM_BAD_PARAM, "Parameter out of range" },
387 { APM_NOT_ENGAGED, "Interface not engaged" },
388 { APM_BAD_FUNCTION, "Function not supported" },
389 { APM_RESUME_DISABLED, "Resume timer disabled" },
390 { APM_BAD_STATE, "Unable to enter requested state" },
391 /* N/A { APM_NO_EVENTS, "No events pending" }, */
392 { APM_NOT_PRESENT, "No APM present" }
394 #define ERROR_COUNT (sizeof(error_table)/sizeof(lookup_t))
397 * These are the actual BIOS calls. Depending on APM_ZERO_SEGS and
398 * CONFIG_APM_ALLOW_INTS, we are being really paranoid here! Not only
399 * are interrupts disabled, but all the segment registers (except SS)
400 * are saved and zeroed this means that if the BIOS tries to reference
401 * any data without explicitly loading the segment registers, the kernel
402 * will fault immediately rather than have some unforeseen circumstances
403 * for the rest of the kernel. And it will be very obvious! :-) Doing
404 * this depends on CS referring to the same physical memory as DS so that
405 * DS can be zeroed before the call. Unfortunately, we can't do anything
406 * about the stack segment/pointer. Also, we tell the compiler that
407 * everything could change.
409 * Also, we KNOW that for the non error case of apm_bios_call, there
410 * is no useful data returned in the low order 8 bits of eax.
412 #ifndef CONFIG_APM_ALLOW_INTS
413 # define APM_DO_CLI __cli()
414 #else
415 # define APM_DO_CLI
416 #endif
417 #ifdef APM_ZERO_SEGS
418 # define APM_DO_SAVE_SEGS \
419 savesegment(fs, saved_fs); \
420 savesegment(gs, saved_gs)
421 # define APM_DO_ZERO_SEGS \
422 "pushl %%ds\n\t" \
423 "pushl %%es\n\t" \
424 "xorl %%edx, %%edx\n\t" \
425 "mov %%dx, %%ds\n\t" \
426 "mov %%dx, %%es\n\t" \
427 "mov %%dx, %%fs\n\t" \
428 "mov %%dx, %%gs\n\t"
429 # define APM_DO_POP_SEGS \
430 "popl %%es\n\t" \
431 "popl %%ds\n\t"
432 # define APM_DO_RESTORE_SEGS \
433 loadsegment(fs, saved_fs); \
434 loadsegment(gs, saved_gs)
435 #else
436 # define APM_DO_SAVE_SEGS
437 # define APM_DO_ZERO_SEGS
438 # define APM_DO_POP_SEGS
439 # define APM_DO_RESTORE_SEGS
440 #endif
442 static u8 apm_bios_call(u32 eax_in, u32 ebx_in, u32 ecx_in,
443 u32 *eax, u32 *ebx, u32 *ecx, u32 *edx, u32 *esi)
445 unsigned int saved_fs;
446 unsigned int saved_gs;
447 unsigned long flags;
449 __save_flags(flags);
450 APM_DO_CLI;
451 APM_DO_SAVE_SEGS;
452 __asm__ __volatile__(APM_DO_ZERO_SEGS
453 "pushl %%edi\n\t"
454 "pushl %%ebp\n\t"
455 "lcall %%cs:" SYMBOL_NAME_STR(apm_bios_entry) "\n\t"
456 "setc %%al\n\t"
457 "popl %%ebp\n\t"
458 "popl %%edi\n\t"
459 APM_DO_POP_SEGS
460 : "=a" (*eax), "=b" (*ebx), "=c" (*ecx), "=d" (*edx),
461 "=S" (*esi)
462 : "a" (eax_in), "b" (ebx_in), "c" (ecx_in)
463 : "memory", "cc");
464 APM_DO_RESTORE_SEGS;
465 __restore_flags(flags);
466 return *eax & 0xff;
470 * This version only returns one value (usually an error code)
473 static u8 apm_bios_call_simple(u32 eax_in, u32 ebx_in, u32 ecx_in,
474 u32 *eax)
476 u8 error;
477 unsigned int saved_fs;
478 unsigned int saved_gs;
479 unsigned long flags;
481 __save_flags(flags);
482 APM_DO_CLI;
483 APM_DO_SAVE_SEGS;
485 int cx, dx, si;
487 __asm__ __volatile__(APM_DO_ZERO_SEGS
488 "pushl %%edi\n\t"
489 "pushl %%ebp\n\t"
490 "lcall %%cs:" SYMBOL_NAME_STR(apm_bios_entry) "\n\t"
491 "setc %%bl\n\t"
492 "popl %%ebp\n\t"
493 "popl %%edi\n\t"
494 APM_DO_POP_SEGS
495 : "=a" (*eax), "=b" (error), "=c" (cx), "=d" (dx),
496 "=S" (si)
497 : "a" (eax_in), "b" (ebx_in), "c" (ecx_in)
498 : "memory", "cc");
500 APM_DO_RESTORE_SEGS;
501 __restore_flags(flags);
502 return error;
505 static int apm_driver_version(u_short *val)
507 u32 eax;
509 if (apm_bios_call_simple(0x530e, 0, *val, &eax))
510 return (eax >> 8) & 0xff;
511 *val = eax;
512 return APM_SUCCESS;
515 static int apm_get_event(apm_event_t *event, apm_eventinfo_t *info)
517 u32 eax;
518 u32 ebx;
519 u32 ecx;
520 u32 dummy;
522 if (apm_bios_call(0x530b, 0, 0, &eax, &ebx, &ecx, &dummy, &dummy))
523 return (eax >> 8) & 0xff;
524 *event = ebx;
525 if (apm_bios_info.version < 0x0102)
526 *info = ~0; /* indicate info not valid */
527 else
528 *info = ecx;
529 return APM_SUCCESS;
532 static int set_power_state(u_short what, u_short state)
534 u32 eax;
536 if (apm_bios_call_simple(0x5307, what, state, &eax))
537 return (eax >> 8) & 0xff;
538 return APM_SUCCESS;
541 static int apm_set_power_state(u_short state)
543 return set_power_state(0x0001, state);
546 void apm_power_off(void)
549 * smp_hack == 2 means that we would have enabled APM support
550 * except there is more than one processor and so most of
551 * the APM stuff is unsafe. We will still try power down
552 * because is is useful to some people and they know what
553 * they are doing because they booted with the smp-power-off
554 * kernel option.
556 if (apm_enabled || (smp_hack == 2))
557 (void) apm_set_power_state(APM_STATE_OFF);
560 #ifdef CONFIG_APM_DISPLAY_BLANK
561 /* Called by apm_display_blank and apm_display_unblank when apm_enabled. */
562 static int apm_set_display_power_state(u_short state)
564 int error;
566 /* Blank the first display device */
567 error = set_power_state(0x0100, state);
568 if (error == APM_BAD_DEVICE)
569 /* try to blank them all instead */
570 error = set_power_state(0x01ff, state);
571 return error;
573 #endif
575 #ifdef CONFIG_APM_DO_ENABLE
576 static int __init apm_enable_power_management(void)
578 u32 eax;
580 if (apm_bios_call_simple(0x5308,
581 (apm_bios_info.version > 0x100) ? 0x0001 : 0xffff,
582 1, &eax))
583 return (eax >> 8) & 0xff;
584 apm_bios_info.flags &= ~APM_BIOS_DISABLED;
585 return APM_SUCCESS;
587 #endif
589 static int apm_get_power_status(u_short *status, u_short *bat, u_short *life)
591 u32 eax;
592 u32 ebx;
593 u32 ecx;
594 u32 edx;
595 u32 dummy;
597 if (apm_bios_call(0x530a, 1, 0, &eax, &ebx, &ecx, &edx, &dummy))
598 return (eax >> 8) & 0xff;
599 *status = ebx;
600 *bat = ecx;
601 *life = edx;
602 return APM_SUCCESS;
605 static int apm_get_battery_status(u_short which, u_short *status,
606 u_short *bat, u_short *life, u_short *nbat)
608 u32 eax;
609 u32 ebx;
610 u32 ecx;
611 u32 edx;
612 u32 esi;
614 if (apm_bios_info.version < 0x0102) {
615 /* pretend we only have one battery. */
616 if (which != 1)
617 return APM_BAD_DEVICE;
618 *nbat = 1;
619 return apm_get_power_status(status, bat, life);
622 if (apm_bios_call(0x530a, (0x8000 | (which)), 0, &eax,
623 &ebx, &ecx, &edx, &esi))
624 return (eax >> 8) & 0xff;
625 *status = ebx;
626 *bat = ecx;
627 *life = edx;
628 *nbat = esi;
629 return APM_SUCCESS;
632 static int __init apm_engage_power_management(u_short device)
634 u32 eax;
636 if (apm_bios_call_simple(0x530f, device, 1, &eax))
637 return (eax >> 8) & 0xff;
638 return APM_SUCCESS;
641 static void apm_error(char *str, int err)
643 int i;
645 for (i = 0; i < ERROR_COUNT; i++)
646 if (error_table[i].key == err) break;
647 if (i < ERROR_COUNT)
648 printk(KERN_NOTICE "apm: %s: %s\n", str, error_table[i].msg);
649 else
650 printk(KERN_NOTICE "apm: %s: unknown error code %#2.2x\n",
651 str, err);
654 /* Called from console driver -- must make sure apm_enabled. */
655 int apm_display_blank(void)
657 #ifdef CONFIG_APM_DISPLAY_BLANK
658 int error;
660 if (!apm_enabled)
661 return 0;
662 error = apm_set_display_power_state(APM_STATE_STANDBY);
663 if (error == APM_SUCCESS)
664 return 1;
665 apm_error("set display standby", error);
666 #endif
667 return 0;
670 /* Called from console driver -- must make sure apm_enabled. */
671 int apm_display_unblank(void)
673 #ifdef CONFIG_APM_DISPLAY_BLANK
674 int error;
676 if (!apm_enabled)
677 return 0;
678 error = apm_set_display_power_state(APM_STATE_READY);
679 if (error == APM_SUCCESS)
680 return 1;
681 apm_error("set display ready", error);
682 #endif
683 return 0;
686 int apm_register_callback(int (*callback)(apm_event_t))
688 callback_list_t * new;
690 new = kmalloc(sizeof(callback_list_t), GFP_KERNEL);
691 if (new == NULL)
692 return -ENOMEM;
693 new->callback = callback;
694 new->next = callback_list;
695 callback_list = new;
696 return 0;
699 void apm_unregister_callback(int (*callback)(apm_event_t))
701 callback_list_t ** ptr;
702 callback_list_t * old;
704 for (ptr = &callback_list; *ptr != NULL; ptr = &(*ptr)->next)
705 if ((*ptr)->callback == callback)
706 break;
707 old = *ptr;
708 *ptr = old->next;
709 kfree_s(old, sizeof(callback_list_t));
712 static int queue_empty(struct apm_bios_struct * as)
714 return as->event_head == as->event_tail;
717 static apm_event_t get_queued_event(struct apm_bios_struct * as)
719 as->event_tail = (as->event_tail + 1) % APM_MAX_EVENTS;
720 return as->events[as->event_tail];
723 static int queue_event(apm_event_t event, struct apm_bios_struct *sender)
725 struct apm_bios_struct * as;
727 if (user_list == NULL)
728 return 0;
729 for (as = user_list; as != NULL; as = as->next) {
730 if (as == sender)
731 continue;
732 as->event_head = (as->event_head + 1) % APM_MAX_EVENTS;
733 if (as->event_head == as->event_tail) {
734 static int notified;
736 if (notified == 0) {
737 printk(KERN_ERR "apm: an event queue overflowed\n");
738 notified = 1;
740 as->event_tail = (as->event_tail + 1) % APM_MAX_EVENTS;
742 as->events[as->event_head] = event;
743 if (!as->suser)
744 continue;
745 switch (event) {
746 case APM_SYS_SUSPEND:
747 case APM_USER_SUSPEND:
748 as->suspends_pending++;
749 suspends_pending++;
750 break;
752 case APM_SYS_STANDBY:
753 case APM_USER_STANDBY:
754 as->standbys_pending++;
755 standbys_pending++;
756 break;
759 wake_up_interruptible(&process_list);
760 return 1;
763 static void set_time(void)
765 unsigned long flags;
767 if (!got_clock_diff) /* Don't know time zone, can't set clock */
768 return;
770 save_flags(flags);
771 cli();
772 CURRENT_TIME = get_cmos_time() + clock_cmos_diff;
773 restore_flags(flags);
776 static void suspend(void)
778 unsigned long flags;
779 int err;
781 #ifndef CONFIG_APM_RTC_IS_GMT
783 * Estimate time zone so that set_time can update the clock
785 save_flags(flags);
786 clock_cmos_diff = -get_cmos_time();
787 cli();
788 clock_cmos_diff += CURRENT_TIME;
789 got_clock_diff = 1;
790 restore_flags(flags);
791 #endif
793 err = apm_set_power_state(APM_STATE_SUSPEND);
794 if (err)
795 apm_error("suspend", err);
796 #ifdef INIT_TIMER_AFTER_SUSPEND
797 save_flags(flags);
798 cli();
799 /* set the clock to 100 Hz */
800 outb_p(0x34,0x43); /* binary, mode 2, LSB/MSB, ch 0 */
801 udelay(10);
802 outb_p(LATCH & 0xff , 0x40); /* LSB */
803 udelay(10);
804 outb(LATCH >> 8 , 0x40); /* MSB */
805 udelay(10);
806 restore_flags(flags);
807 #endif
808 set_time();
811 static void standby(void)
813 int err;
815 err = apm_set_power_state(APM_STATE_STANDBY);
816 if (err)
817 apm_error("standby", err);
820 static apm_event_t get_event(void)
822 int error;
823 apm_event_t event;
824 apm_eventinfo_t info;
826 static int notified = 0;
828 /* we don't use the eventinfo */
829 error = apm_get_event(&event, &info);
830 if (error == APM_SUCCESS)
831 return event;
833 if ((error != APM_NO_EVENTS) && (notified++ == 0))
834 apm_error("get_event", error);
836 return 0;
839 static void send_event(apm_event_t event, apm_event_t undo,
840 struct apm_bios_struct *sender)
842 callback_list_t * call;
843 callback_list_t * fix;
845 for (call = callback_list; call != NULL; call = call->next) {
846 if (call->callback(event) && undo) {
847 for (fix = callback_list; fix != call; fix = fix->next)
848 fix->callback(undo);
849 if (apm_bios_info.version > 0x100)
850 apm_set_power_state(APM_STATE_REJECT);
851 return;
855 queue_event(event, sender);
858 static void check_events(void)
860 apm_event_t event;
861 #ifdef CONFIG_APM_IGNORE_SUSPEND_BOUNCE
862 static unsigned long last_resume = 0;
863 static int ignore_bounce = 0;
864 #endif
866 while ((event = get_event()) != 0) {
867 #ifdef APM_DEBUG
868 if (event <= NR_APM_EVENT_NAME)
869 printk(KERN_DEBUG "apm: received %s notify\n",
870 apm_event_name[event - 1]);
871 else
872 printk(KERN_DEBUG "apm: received unknown "
873 "event 0x%02x\n", event);
874 #endif
875 #ifdef CONFIG_APM_IGNORE_SUSPEND_BOUNCE
876 if (ignore_bounce
877 && ((jiffies - last_resume) > BOUNCE_INTERVAL))
878 ignore_bounce = 0;
879 #endif
880 switch (event) {
881 case APM_SYS_STANDBY:
882 case APM_USER_STANDBY:
883 #ifdef CONFIG_APM_IGNORE_MULTIPLE_SUSPEND
884 if (waiting_for_resume)
885 return;
886 waiting_for_resume = 1;
887 #endif
888 send_event(event, APM_STANDBY_RESUME, NULL);
889 if (standbys_pending <= 0)
890 standby();
891 break;
893 case APM_USER_SUSPEND:
894 #ifdef CONFIG_APM_IGNORE_USER_SUSPEND
895 if (apm_bios_info.version > 0x100)
896 apm_set_power_state(APM_STATE_REJECT);
897 break;
898 #endif
899 case APM_SYS_SUSPEND:
900 #ifdef CONFIG_APM_IGNORE_SUSPEND_BOUNCE
901 if (ignore_bounce)
902 break;
903 #endif
904 #ifdef CONFIG_APM_IGNORE_MULTIPLE_SUSPEND
905 if (waiting_for_resume)
906 return;
907 waiting_for_resume = 1;
908 #endif
909 send_event(event, APM_NORMAL_RESUME, NULL);
910 if (suspends_pending <= 0)
911 suspend();
912 break;
914 case APM_NORMAL_RESUME:
915 case APM_CRITICAL_RESUME:
916 case APM_STANDBY_RESUME:
917 #ifdef CONFIG_APM_IGNORE_MULTIPLE_SUSPEND
918 waiting_for_resume = 0;
919 #endif
920 #ifdef CONFIG_APM_IGNORE_SUSPEND_BOUNCE
921 last_resume = jiffies;
922 ignore_bounce = 1;
923 #endif
924 set_time();
925 send_event(event, 0, NULL);
926 break;
928 case APM_LOW_BATTERY:
929 case APM_POWER_STATUS_CHANGE:
930 case APM_CAPABILITY_CHANGE:
931 send_event(event, 0, NULL);
932 break;
934 case APM_UPDATE_TIME:
935 set_time();
936 break;
938 case APM_CRITICAL_SUSPEND:
939 suspend();
940 break;
945 static void do_apm_timer(unsigned long unused)
947 int err;
949 static int pending_count = 0;
951 if (((standbys_pending > 0) || (suspends_pending > 0))
952 && (apm_bios_info.version > 0x100)
953 && (pending_count-- <= 0)) {
954 pending_count = 4;
956 err = apm_set_power_state(APM_STATE_BUSY);
957 if (err)
958 apm_error("busy", err);
961 if (!(((standbys_pending > 0) || (suspends_pending > 0))
962 && (apm_bios_info.version == 0x100)))
963 check_events();
965 init_timer(&apm_timer);
966 apm_timer.expires = APM_CHECK_TIMEOUT + jiffies;
967 add_timer(&apm_timer);
970 /* Called from sys_idle, must make sure apm_enabled. */
971 int apm_do_idle(void)
973 #ifdef CONFIG_APM_CPU_IDLE
974 u32 dummy;
976 if (!apm_enabled)
977 return 0;
979 if (apm_bios_call_simple(0x5305, 0, 0, &dummy))
980 return 0;
982 clock_slowed = (apm_bios_info.flags & APM_IDLE_SLOWS_CLOCK) != 0;
983 return 1;
984 #else
985 return 0;
986 #endif
989 /* Called from sys_idle, must make sure apm_enabled. */
990 void apm_do_busy(void)
992 #ifdef CONFIG_APM_CPU_IDLE
993 u32 dummy;
995 if (apm_enabled
996 #ifndef ALWAYS_CALL_BUSY
997 && clock_slowed
998 #endif
1000 (void) apm_bios_call_simple(0x5306, 0, 0, &dummy);
1001 clock_slowed = 0;
1003 #endif
1006 static int check_apm_bios_struct(struct apm_bios_struct *as, const char *func)
1008 if ((as == NULL) || (as->magic != APM_BIOS_MAGIC)) {
1009 printk(KERN_ERR "apm: %s passed bad filp", func);
1010 return 1;
1012 return 0;
1015 static ssize_t do_read(struct file *fp, char *buf, size_t count, loff_t *ppos)
1017 struct apm_bios_struct * as;
1018 int i;
1019 apm_event_t event;
1020 DECLARE_WAITQUEUE(wait, current);
1022 as = fp->private_data;
1023 if (check_apm_bios_struct(as, "read"))
1024 return -EIO;
1025 if (count < sizeof(apm_event_t))
1026 return -EINVAL;
1027 if (queue_empty(as)) {
1028 if (fp->f_flags & O_NONBLOCK)
1029 return -EAGAIN;
1030 add_wait_queue(&process_list, &wait);
1031 repeat:
1032 current->state = TASK_INTERRUPTIBLE;
1033 if (queue_empty(as) && !signal_pending(current)) {
1034 schedule();
1035 goto repeat;
1037 current->state = TASK_RUNNING;
1038 remove_wait_queue(&process_list, &wait);
1040 i = count;
1041 while ((i >= sizeof(event)) && !queue_empty(as)) {
1042 event = get_queued_event(as);
1043 copy_to_user(buf, &event, sizeof(event));
1044 switch (event) {
1045 case APM_SYS_SUSPEND:
1046 case APM_USER_SUSPEND:
1047 as->suspends_read++;
1048 break;
1050 case APM_SYS_STANDBY:
1051 case APM_USER_STANDBY:
1052 as->standbys_read++;
1053 break;
1055 buf += sizeof(event);
1056 i -= sizeof(event);
1058 if (i < count)
1059 return count - i;
1060 if (signal_pending(current))
1061 return -ERESTARTSYS;
1062 return 0;
1065 static unsigned int do_poll(struct file *fp, poll_table * wait)
1067 struct apm_bios_struct * as;
1069 as = fp->private_data;
1070 if (check_apm_bios_struct(as, "select"))
1071 return 0;
1072 poll_wait(fp, &process_list, wait);
1073 if (!queue_empty(as))
1074 return POLLIN | POLLRDNORM;
1075 return 0;
1078 static int do_ioctl(struct inode * inode, struct file *filp,
1079 u_int cmd, u_long arg)
1081 struct apm_bios_struct * as;
1083 as = filp->private_data;
1084 if (check_apm_bios_struct(as, "ioctl"))
1085 return -EIO;
1086 if (!as->suser)
1087 return -EPERM;
1088 switch (cmd) {
1089 case APM_IOC_STANDBY:
1090 if (as->standbys_read > 0) {
1091 as->standbys_read--;
1092 as->standbys_pending--;
1093 standbys_pending--;
1095 else
1096 send_event(APM_USER_STANDBY, APM_STANDBY_RESUME, as);
1097 if (standbys_pending <= 0)
1098 standby();
1099 break;
1100 case APM_IOC_SUSPEND:
1101 if (as->suspends_read > 0) {
1102 as->suspends_read--;
1103 as->suspends_pending--;
1104 suspends_pending--;
1106 else
1107 send_event(APM_USER_SUSPEND, APM_NORMAL_RESUME, as);
1108 if (suspends_pending <= 0)
1109 suspend();
1110 break;
1111 default:
1112 return -EINVAL;
1114 return 0;
1117 static int do_release(struct inode * inode, struct file * filp)
1119 struct apm_bios_struct * as;
1121 as = filp->private_data;
1122 filp->private_data = NULL;
1123 if (check_apm_bios_struct(as, "release"))
1124 return 0;
1125 if (as->standbys_pending > 0) {
1126 standbys_pending -= as->standbys_pending;
1127 if (standbys_pending <= 0)
1128 standby();
1130 if (as->suspends_pending > 0) {
1131 suspends_pending -= as->suspends_pending;
1132 if (suspends_pending <= 0)
1133 suspend();
1135 if (user_list == as)
1136 user_list = as->next;
1137 else {
1138 struct apm_bios_struct * as1;
1140 for (as1 = user_list;
1141 (as1 != NULL) && (as1->next != as);
1142 as1 = as1->next)
1144 if (as1 == NULL)
1145 printk(KERN_ERR "apm: filp not in user list");
1146 else
1147 as1->next = as->next;
1149 kfree_s(as, sizeof(*as));
1150 return 0;
1153 static int do_open(struct inode * inode, struct file * filp)
1155 struct apm_bios_struct * as;
1157 as = (struct apm_bios_struct *)kmalloc(sizeof(*as), GFP_KERNEL);
1158 if (as == NULL) {
1159 printk(KERN_ERR "apm: cannot allocate struct of size %d bytes",
1160 sizeof(*as));
1161 return -ENOMEM;
1163 as->magic = APM_BIOS_MAGIC;
1164 as->event_tail = as->event_head = 0;
1165 as->suspends_pending = as->standbys_pending = 0;
1166 as->suspends_read = as->standbys_read = 0;
1168 * XXX - this is a tiny bit broken, when we consider BSD
1169 * process accounting. If the device is opened by root, we
1170 * instantly flag that we used superuser privs. Who knows,
1171 * we might close the device immediately without doing a
1172 * privileged operation -- cevans
1174 as->suser = capable(CAP_SYS_ADMIN);
1175 as->next = user_list;
1176 user_list = as;
1177 filp->private_data = as;
1178 return 0;
1181 int apm_get_info(char *buf, char **start, off_t fpos, int length, int dummy)
1183 char * p;
1184 unsigned short bx;
1185 unsigned short cx;
1186 unsigned short dx;
1187 unsigned short nbat;
1188 unsigned short error;
1189 unsigned short ac_line_status = 0xff;
1190 unsigned short battery_status = 0xff;
1191 unsigned short battery_flag = 0xff;
1192 int percentage = -1;
1193 int time_units = -1;
1194 char *units = "?";
1196 if (!apm_enabled)
1197 return 0;
1198 p = buf;
1200 if (!(error = apm_get_power_status(&bx, &cx, &dx))) {
1201 ac_line_status = (bx >> 8) & 0xff;
1202 battery_status = bx & 0xff;
1203 if ((cx & 0xff) != 0xff)
1204 percentage = cx & 0xff;
1206 if (apm_bios_info.version > 0x100) {
1207 battery_flag = (cx >> 8) & 0xff;
1208 if (dx != 0xffff) {
1209 units = (dx & 0x8000) ? "min" : "sec";
1210 time_units = dx & 0x7fff;
1214 /* Arguments, with symbols from linux/apm_bios.h. Information is
1215 from the Get Power Status (0x0a) call unless otherwise noted.
1217 0) Linux driver version (this will change if format changes)
1218 1) APM BIOS Version. Usually 1.0 or 1.1.
1219 2) APM flags from APM Installation Check (0x00):
1220 bit 0: APM_16_BIT_SUPPORT
1221 bit 1: APM_32_BIT_SUPPORT
1222 bit 2: APM_IDLE_SLOWS_CLOCK
1223 bit 3: APM_BIOS_DISABLED
1224 bit 4: APM_BIOS_DISENGAGED
1225 3) AC line status
1226 0x00: Off-line
1227 0x01: On-line
1228 0x02: On backup power (APM BIOS 1.1 only)
1229 0xff: Unknown
1230 4) Battery status
1231 0x00: High
1232 0x01: Low
1233 0x02: Critical
1234 0x03: Charging
1235 0xff: Unknown
1236 5) Battery flag
1237 bit 0: High
1238 bit 1: Low
1239 bit 2: Critical
1240 bit 3: Charging
1241 bit 7: No system battery
1242 0xff: Unknown
1243 6) Remaining battery life (percentage of charge):
1244 0-100: valid
1245 -1: Unknown
1246 7) Remaining battery life (time units):
1247 Number of remaining minutes or seconds
1248 -1: Unknown
1249 8) min = minutes; sec = seconds */
1251 p += sprintf(p, "%s %d.%d 0x%02x 0x%02x 0x%02x 0x%02x %d%% %d %s\n",
1252 driver_version,
1253 (apm_bios_info.version >> 8) & 0xff,
1254 apm_bios_info.version & 0xff,
1255 apm_bios_info.flags,
1256 ac_line_status,
1257 battery_status,
1258 battery_flag,
1259 percentage,
1260 time_units,
1261 units);
1263 return p - buf;
1266 void __init apm_setup(char *str, int *dummy)
1268 int invert;
1270 while ((str != NULL) && (*str != '\0')) {
1271 if (strncmp(str, "off", 3) == 0)
1272 apm_disabled = 1;
1273 if (strncmp(str, "on", 2) == 0)
1274 apm_disabled = 0;
1275 invert = (strncmp(str, "no-", 3) == 0);
1276 if (invert)
1277 str += 3;
1278 if (strncmp(str, "debug", 5) == 0)
1279 debug = !invert;
1280 if (strncmp(str, "smp-power-off", 13) == 0)
1281 smp_hack = !invert;
1282 str = strchr(str, ',');
1283 if (str != NULL)
1284 str += strspn(str, ", \t");
1288 void __init apm_bios_init(void)
1290 unsigned short bx;
1291 unsigned short cx;
1292 unsigned short dx;
1293 unsigned short error;
1294 char * power_stat;
1295 char * bat_stat;
1296 static struct proc_dir_entry *ent;
1298 if (apm_bios_info.version == 0) {
1299 printk(KERN_INFO "apm: BIOS not found.\n");
1300 return;
1302 printk(KERN_INFO
1303 "apm: BIOS version %d.%d Flags 0x%02x (Driver version %s)\n",
1304 ((apm_bios_info.version >> 8) & 0xff),
1305 (apm_bios_info.version & 0xff),
1306 apm_bios_info.flags,
1307 driver_version);
1308 if ((apm_bios_info.flags & APM_32_BIT_SUPPORT) == 0) {
1309 printk(KERN_INFO "apm: no 32 bit BIOS support\n");
1310 return;
1314 * Fix for the Compaq Contura 3/25c which reports BIOS version 0.1
1315 * but is reportedly a 1.0 BIOS.
1317 if (apm_bios_info.version == 0x001)
1318 apm_bios_info.version = 0x100;
1320 /* BIOS < 1.2 doesn't set cseg_16_len */
1321 if (apm_bios_info.version < 0x102)
1322 apm_bios_info.cseg_16_len = 0; /* 64k */
1324 if (debug) {
1325 printk(KERN_INFO "apm: entry %x:%lx cseg16 %x dseg %x",
1326 apm_bios_info.cseg, apm_bios_info.offset,
1327 apm_bios_info.cseg_16, apm_bios_info.dseg);
1328 if (apm_bios_info.version > 0x100)
1329 printk(" cseg len %x, dseg len %x",
1330 apm_bios_info.cseg_len,
1331 apm_bios_info.dseg_len);
1332 if (apm_bios_info.version > 0x101)
1333 printk(" cseg16 len %x", apm_bios_info.cseg_16_len);
1334 printk("\n");
1337 if (apm_disabled) {
1338 printk(KERN_NOTICE "apm: disabled on user request.\n");
1339 return;
1343 * Set up a segment that references the real mode segment 0x40
1344 * that extends up to the end of page zero (that we have reserved).
1345 * This is for buggy BIOS's that refer to (real mode) segment 0x40
1346 * even though they are called in protected mode.
1348 set_base(gdt[APM_40 >> 3],
1349 __va((unsigned long)0x40 << 4));
1350 _set_limit((char *)&gdt[APM_40 >> 3], 4095 - (0x40 << 4));
1352 apm_bios_entry.offset = apm_bios_info.offset;
1353 apm_bios_entry.segment = APM_CS;
1354 set_base(gdt[APM_CS >> 3],
1355 __va((unsigned long)apm_bios_info.cseg << 4));
1356 set_base(gdt[APM_CS_16 >> 3],
1357 __va((unsigned long)apm_bios_info.cseg_16 << 4));
1358 set_base(gdt[APM_DS >> 3],
1359 __va((unsigned long)apm_bios_info.dseg << 4));
1360 #ifndef APM_RELAX_SEGMENTS
1361 if (apm_bios_info.version == 0x100)
1362 #endif
1364 /* For ASUS motherboard, Award BIOS rev 110 (and others?) */
1365 _set_limit((char *)&gdt[APM_CS >> 3], 64 * 1024 - 1);
1366 /* For some unknown machine. */
1367 _set_limit((char *)&gdt[APM_CS_16 >> 3], 64 * 1024 - 1);
1368 /* For the DEC Hinote Ultra CT475 (and others?) */
1369 _set_limit((char *)&gdt[APM_DS >> 3], 64 * 1024 - 1);
1371 #ifndef APM_RELAX_SEGMENTS
1372 else {
1373 _set_limit((char *)&gdt[APM_CS >> 3],
1374 (apm_bios_info.cseg_len - 1) & 0xffff);
1375 _set_limit((char *)&gdt[APM_CS_16 >> 3],
1376 (apm_bios_info.cseg_16_len - 1) & 0xffff);
1377 _set_limit((char *)&gdt[APM_DS >> 3],
1378 (apm_bios_info.dseg_len - 1) & 0xffff);
1380 #endif
1381 #ifdef CONFIG_SMP
1382 if (smp_num_cpus > 1) {
1383 printk(KERN_NOTICE "apm: disabled - APM is not SMP safe.\n");
1384 if (smp_hack)
1385 smp_hack = 2;
1386 return;
1388 #endif
1389 if (apm_bios_info.version > 0x100) {
1391 * We only support BIOSs up to version 1.2
1393 if (apm_bios_info.version > 0x0102)
1394 apm_bios_info.version = 0x0102;
1395 if (apm_driver_version(&apm_bios_info.version) != APM_SUCCESS) {
1396 /* Fall back to an APM 1.0 connection. */
1397 apm_bios_info.version = 0x100;
1400 if (debug) {
1401 printk(KERN_INFO "apm: Connection version %d.%d\n",
1402 (apm_bios_info.version >> 8) & 0xff,
1403 apm_bios_info.version & 0xff );
1405 error = apm_get_power_status(&bx, &cx, &dx);
1406 if (error)
1407 printk(KERN_INFO "apm: power status not available\n");
1408 else {
1409 switch ((bx >> 8) & 0xff) {
1410 case 0: power_stat = "off line"; break;
1411 case 1: power_stat = "on line"; break;
1412 case 2: power_stat = "on backup power"; break;
1413 default: power_stat = "unknown"; break;
1415 switch (bx & 0xff) {
1416 case 0: bat_stat = "high"; break;
1417 case 1: bat_stat = "low"; break;
1418 case 2: bat_stat = "critical"; break;
1419 case 3: bat_stat = "charging"; break;
1420 default: bat_stat = "unknown"; break;
1422 printk(KERN_INFO
1423 "apm: AC %s, battery status %s, battery life ",
1424 power_stat, bat_stat);
1425 if ((cx & 0xff) == 0xff)
1426 printk("unknown\n");
1427 else
1428 printk("%d%%\n", cx & 0xff);
1429 if (apm_bios_info.version > 0x100) {
1430 printk(KERN_INFO
1431 "apm: battery flag 0x%02x, battery life ",
1432 (cx >> 8) & 0xff);
1433 if (dx == 0xffff)
1434 printk("unknown\n");
1435 else
1436 printk("%d %s\n", dx & 0x7fff,
1437 (dx & 0x8000) ?
1438 "minutes" : "seconds");
1443 #ifdef CONFIG_APM_DO_ENABLE
1444 if (apm_bios_info.flags & APM_BIOS_DISABLED) {
1446 * This call causes my NEC UltraLite Versa 33/C to hang if it
1447 * is booted with PM disabled but not in the docking station.
1448 * Unfortunate ...
1450 error = apm_enable_power_management();
1451 if (error) {
1452 apm_error("enable power management", error);
1453 return;
1456 #endif
1457 if (((apm_bios_info.flags & APM_BIOS_DISABLED) == 0)
1458 && (apm_bios_info.version > 0x0100)) {
1459 if (apm_engage_power_management(0x0001) == APM_SUCCESS)
1460 apm_bios_info.flags &= ~APM_BIOS_DISENGAGED;
1463 init_timer(&apm_timer);
1464 apm_timer.function = do_apm_timer;
1465 apm_timer.expires = APM_CHECK_TIMEOUT + jiffies;
1466 add_timer(&apm_timer);
1468 ent = create_proc_entry("apm", 0, 0);
1469 if (ent != NULL)
1470 ent->get_info = apm_get_info;
1472 misc_register(&apm_device);
1474 apm_enabled = 1;