aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@woody.linux-foundation.org>2007-02-09 12:44:28 -0500
committerLinus Torvalds <torvalds@woody.linux-foundation.org>2007-02-09 12:44:28 -0500
commit2fd592e45b9c89d69e126f172d0f991e2af955e5 (patch)
treebacc34dbffb5da09b40840f409f7fad462e0acac
parent2affc857efdf7dacace234b63d289d67260c95a6 (diff)
parent0a9b0db19262dbb09f3a34195e68cafd5dc3fa10 (diff)
Merge branch 'upstream' of git://ftp.linux-mips.org/pub/scm/upstream-apm
* 'upstream' of git://ftp.linux-mips.org/pub/scm/upstream-apm: [APM] SH: Convert to use shared APM emulation. [APM] MIPS: Convert to use shared APM emulation. [APM] ARM: Convert to use shared APM emulation. [APM] Add shared version of APM emulation
-rw-r--r--arch/arm/Kconfig29
-rw-r--r--arch/arm/common/sharpsl_pm.c2
-rw-r--r--arch/arm/kernel/Makefile1
-rw-r--r--arch/arm/mach-pxa/corgi_pm.c2
-rw-r--r--arch/arm/mach-pxa/sharpsl_pm.c2
-rw-r--r--arch/arm/mach-pxa/spitz_pm.c2
-rw-r--r--arch/mips/Kconfig38
-rw-r--r--arch/mips/kernel/Makefile2
-rw-r--r--arch/mips/kernel/apm.c604
-rw-r--r--arch/sh/Kconfig7
-rw-r--r--arch/sh/boards/hp6xx/hp6xx_apm.c68
-rw-r--r--arch/sh/kernel/Makefile1
-rw-r--r--arch/sh/kernel/apm.c538
-rw-r--r--drivers/char/Makefile2
-rw-r--r--drivers/char/apm-emulation.c (renamed from arch/arm/kernel/apm.c)8
-rw-r--r--include/asm-arm/apm.h64
-rw-r--r--include/asm-sh/apm.h46
-rw-r--r--include/linux/apm-emulation.h (renamed from include/asm-mips/apm.h)8
-rw-r--r--kernel/power/Kconfig26
19 files changed, 80 insertions, 1370 deletions
diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index 6783c2e5512d..1523046e092b 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -9,6 +9,7 @@ config ARM
9 bool 9 bool
10 default y 10 default y
11 select RTC_LIB 11 select RTC_LIB
12 select SYS_SUPPORTS_APM_EMULATION
12 help 13 help
13 The ARM series is a line of low-power-consumption RISC chip designs 14 The ARM series is a line of low-power-consumption RISC chip designs
14 licensed by ARM Ltd and targeted at embedded applications and 15 licensed by ARM Ltd and targeted at embedded applications and
@@ -17,6 +18,9 @@ config ARM
17 Europe. There is an ARM Linux project with a web page at 18 Europe. There is an ARM Linux project with a web page at
18 <http://www.arm.linux.org.uk/>. 19 <http://www.arm.linux.org.uk/>.
19 20
21config SYS_SUPPORTS_APM_EMULATION
22 bool
23
20config GENERIC_TIME 24config GENERIC_TIME
21 bool 25 bool
22 default n 26 default n
@@ -856,31 +860,6 @@ menu "Power management options"
856 860
857source "kernel/power/Kconfig" 861source "kernel/power/Kconfig"
858 862
859config APM
860 tristate "Advanced Power Management Emulation"
861 ---help---
862 APM is a BIOS specification for saving power using several different
863 techniques. This is mostly useful for battery powered laptops with
864 APM compliant BIOSes. If you say Y here, the system time will be
865 reset after a RESUME operation, the /proc/apm device will provide
866 battery status information, and user-space programs will receive
867 notification of APM "events" (e.g. battery status change).
868
869 In order to use APM, you will need supporting software. For location
870 and more information, read <file:Documentation/pm.txt> and the
871 Battery Powered Linux mini-HOWTO, available from
872 <http://www.tldp.org/docs.html#howto>.
873
874 This driver does not spin down disk drives (see the hdparm(8)
875 manpage ("man 8 hdparm") for that), and it doesn't turn off
876 VESA-compliant "green" monitors.
877
878 Generally, if you don't have a battery in your machine, there isn't
879 much point in using this driver and you should say N. If you get
880 random kernel OOPSes or reboots that don't seem to be related to
881 anything, try disabling/enabling this option (or disabling/enabling
882 APM in your BIOS).
883
884endmenu 863endmenu
885 864
886source "net/Kconfig" 865source "net/Kconfig"
diff --git a/arch/arm/common/sharpsl_pm.c b/arch/arm/common/sharpsl_pm.c
index b3599743093b..a3b450f8ef17 100644
--- a/arch/arm/common/sharpsl_pm.c
+++ b/arch/arm/common/sharpsl_pm.c
@@ -27,7 +27,7 @@
27#include <asm/hardware.h> 27#include <asm/hardware.h>
28#include <asm/mach-types.h> 28#include <asm/mach-types.h>
29#include <asm/irq.h> 29#include <asm/irq.h>
30#include <asm/apm.h> 30#include <asm/apm-emulation.h>
31#include <asm/arch/pm.h> 31#include <asm/arch/pm.h>
32#include <asm/arch/pxa-regs.h> 32#include <asm/arch/pxa-regs.h>
33#include <asm/arch/sharpsl.h> 33#include <asm/arch/sharpsl.h>
diff --git a/arch/arm/kernel/Makefile b/arch/arm/kernel/Makefile
index ab06a86e85d5..1b935fb94b83 100644
--- a/arch/arm/kernel/Makefile
+++ b/arch/arm/kernel/Makefile
@@ -10,7 +10,6 @@ obj-y := compat.o entry-armv.o entry-common.o irq.o \
10 process.o ptrace.o semaphore.o setup.o signal.o sys_arm.o \ 10 process.o ptrace.o semaphore.o setup.o signal.o sys_arm.o \
11 time.o traps.o 11 time.o traps.o
12 12
13obj-$(CONFIG_APM) += apm.o
14obj-$(CONFIG_ISA_DMA_API) += dma.o 13obj-$(CONFIG_ISA_DMA_API) += dma.o
15obj-$(CONFIG_ARCH_ACORN) += ecard.o 14obj-$(CONFIG_ARCH_ACORN) += ecard.o
16obj-$(CONFIG_FIQ) += fiq.o 15obj-$(CONFIG_FIQ) += fiq.o
diff --git a/arch/arm/mach-pxa/corgi_pm.c b/arch/arm/mach-pxa/corgi_pm.c
index 4c3de4008a43..165017de8d0d 100644
--- a/arch/arm/mach-pxa/corgi_pm.c
+++ b/arch/arm/mach-pxa/corgi_pm.c
@@ -16,7 +16,7 @@
16#include <linux/delay.h> 16#include <linux/delay.h>
17#include <linux/interrupt.h> 17#include <linux/interrupt.h>
18#include <linux/platform_device.h> 18#include <linux/platform_device.h>
19#include <asm/apm.h> 19#include <asm/apm-emulation.h>
20#include <asm/irq.h> 20#include <asm/irq.h>
21#include <asm/mach-types.h> 21#include <asm/mach-types.h>
22#include <asm/hardware.h> 22#include <asm/hardware.h>
diff --git a/arch/arm/mach-pxa/sharpsl_pm.c b/arch/arm/mach-pxa/sharpsl_pm.c
index db6e8f56a75f..b1d8cfca245a 100644
--- a/arch/arm/mach-pxa/sharpsl_pm.c
+++ b/arch/arm/mach-pxa/sharpsl_pm.c
@@ -23,7 +23,7 @@
23 23
24#include <asm/hardware.h> 24#include <asm/hardware.h>
25#include <asm/mach-types.h> 25#include <asm/mach-types.h>
26#include <asm/apm.h> 26#include <asm/apm-emulation.h>
27#include <asm/arch/pm.h> 27#include <asm/arch/pm.h>
28#include <asm/arch/pxa-regs.h> 28#include <asm/arch/pxa-regs.h>
29#include <asm/arch/sharpsl.h> 29#include <asm/arch/sharpsl.h>
diff --git a/arch/arm/mach-pxa/spitz_pm.c b/arch/arm/mach-pxa/spitz_pm.c
index 40be833079c7..b97d543d9364 100644
--- a/arch/arm/mach-pxa/spitz_pm.c
+++ b/arch/arm/mach-pxa/spitz_pm.c
@@ -16,7 +16,7 @@
16#include <linux/delay.h> 16#include <linux/delay.h>
17#include <linux/interrupt.h> 17#include <linux/interrupt.h>
18#include <linux/platform_device.h> 18#include <linux/platform_device.h>
19#include <asm/apm.h> 19#include <asm/apm-emulation.h>
20#include <asm/irq.h> 20#include <asm/irq.h>
21#include <asm/mach-types.h> 21#include <asm/mach-types.h>
22#include <asm/hardware.h> 22#include <asm/hardware.h>
diff --git a/arch/mips/Kconfig b/arch/mips/Kconfig
index 44a0224c32dd..9d839a9c4b1a 100644
--- a/arch/mips/Kconfig
+++ b/arch/mips/Kconfig
@@ -934,6 +934,9 @@ config CPU_LITTLE_ENDIAN
934 934
935endchoice 935endchoice
936 936
937config SYS_SUPPORTS_APM_EMULATION
938 bool
939
937config SYS_SUPPORTS_BIG_ENDIAN 940config SYS_SUPPORTS_BIG_ENDIAN
938 bool 941 bool
939 942
@@ -1001,6 +1004,7 @@ config SOC_AU1X00
1001 bool 1004 bool
1002 select SYS_HAS_CPU_MIPS32_R1 1005 select SYS_HAS_CPU_MIPS32_R1
1003 select SYS_SUPPORTS_32BIT_KERNEL 1006 select SYS_SUPPORTS_32BIT_KERNEL
1007 select SYS_SUPPORTS_APM_EMULATION
1004 1008
1005config PNX8550 1009config PNX8550
1006 bool 1010 bool
@@ -2071,35 +2075,11 @@ config BINFMT_ELF32
2071 bool 2075 bool
2072 default y if MIPS32_O32 || MIPS32_N32 2076 default y if MIPS32_O32 || MIPS32_N32
2073 2077
2074config PM 2078endmenu
2075 bool "Power Management support (EXPERIMENTAL)" 2079
2076 depends on EXPERIMENTAL && SOC_AU1X00 2080menu "Power management options"
2077 2081
2078config APM 2082source "kernel/power/Kconfig"
2079 tristate "Advanced Power Management Emulation"
2080 depends on PM
2081 ---help---
2082 APM is a BIOS specification for saving power using several different
2083 techniques. This is mostly useful for battery powered systems with
2084 APM compliant BIOSes. If you say Y here, the system time will be
2085 reset after a RESUME operation, the /proc/apm device will provide
2086 battery status information, and user-space programs will receive
2087 notification of APM "events" (e.g. battery status change).
2088
2089 In order to use APM, you will need supporting software. For location
2090 and more information, read <file:Documentation/pm.txt> and the
2091 Battery Powered Linux mini-HOWTO, available from
2092 <http://www.tldp.org/docs.html#howto>.
2093
2094 This driver does not spin down disk drives (see the hdparm(8)
2095 manpage ("man 8 hdparm") for that), and it doesn't turn off
2096 VESA-compliant "green" monitors.
2097
2098 Generally, if you don't have a battery in your machine, there isn't
2099 much point in using this driver and you should say N. If you get
2100 random kernel OOPSes or reboots that don't seem to be related to
2101 anything, try disabling/enabling this option (or disabling/enabling
2102 APM in your BIOS).
2103 2083
2104endmenu 2084endmenu
2105 2085
diff --git a/arch/mips/kernel/Makefile b/arch/mips/kernel/Makefile
index bbbb8d7cb89b..1bf2c8448912 100644
--- a/arch/mips/kernel/Makefile
+++ b/arch/mips/kernel/Makefile
@@ -14,8 +14,6 @@ binfmt_irix-objs := irixelf.o irixinv.o irixioctl.o irixsig.o \
14obj-$(CONFIG_STACKTRACE) += stacktrace.o 14obj-$(CONFIG_STACKTRACE) += stacktrace.o
15obj-$(CONFIG_MODULES) += mips_ksyms.o module.o 15obj-$(CONFIG_MODULES) += mips_ksyms.o module.o
16 16
17obj-$(CONFIG_APM) += apm.o
18
19obj-$(CONFIG_CPU_R3000) += r2300_fpu.o r2300_switch.o 17obj-$(CONFIG_CPU_R3000) += r2300_fpu.o r2300_switch.o
20obj-$(CONFIG_CPU_TX39XX) += r2300_fpu.o r2300_switch.o 18obj-$(CONFIG_CPU_TX39XX) += r2300_fpu.o r2300_switch.o
21obj-$(CONFIG_CPU_TX49XX) += r4k_fpu.o r4k_switch.o 19obj-$(CONFIG_CPU_TX49XX) += r4k_fpu.o r4k_switch.o
diff --git a/arch/mips/kernel/apm.c b/arch/mips/kernel/apm.c
deleted file mode 100644
index ba16d07588cb..000000000000
--- a/arch/mips/kernel/apm.c
+++ /dev/null
@@ -1,604 +0,0 @@
1/*
2 * bios-less APM driver for MIPS Linux
3 * Jamey Hicks <jamey@crl.dec.com>
4 * adapted from the APM BIOS driver for Linux by Stephen Rothwell (sfr@linuxcare.com)
5 *
6 * APM 1.2 Reference:
7 * Intel Corporation, Microsoft Corporation. Advanced Power Management
8 * (APM) BIOS Interface Specification, Revision 1.2, February 1996.
9 *
10 * [This document is available from Microsoft at:
11 * http://www.microsoft.com/hwdev/busbios/amp_12.htm]
12 */
13#include <linux/module.h>
14#include <linux/poll.h>
15#include <linux/timer.h>
16#include <linux/slab.h>
17#include <linux/proc_fs.h>
18#include <linux/miscdevice.h>
19#include <linux/apm_bios.h>
20#include <linux/capability.h>
21#include <linux/sched.h>
22#include <linux/pm.h>
23#include <linux/device.h>
24#include <linux/kernel.h>
25#include <linux/list.h>
26#include <linux/init.h>
27#include <linux/completion.h>
28
29#include <asm/apm.h> /* apm_power_info */
30#include <asm/system.h>
31
32/*
33 * The apm_bios device is one of the misc char devices.
34 * This is its minor number.
35 */
36#define APM_MINOR_DEV 134
37
38/*
39 * See Documentation/Config.help for the configuration options.
40 *
41 * Various options can be changed at boot time as follows:
42 * (We allow underscores for compatibility with the modules code)
43 * apm=on/off enable/disable APM
44 */
45
46/*
47 * Maximum number of events stored
48 */
49#define APM_MAX_EVENTS 16
50
51struct apm_queue {
52 unsigned int event_head;
53 unsigned int event_tail;
54 apm_event_t events[APM_MAX_EVENTS];
55};
56
57/*
58 * The per-file APM data
59 */
60struct apm_user {
61 struct list_head list;
62
63 unsigned int suser: 1;
64 unsigned int writer: 1;
65 unsigned int reader: 1;
66
67 int suspend_result;
68 unsigned int suspend_state;
69#define SUSPEND_NONE 0 /* no suspend pending */
70#define SUSPEND_PENDING 1 /* suspend pending read */
71#define SUSPEND_READ 2 /* suspend read, pending ack */
72#define SUSPEND_ACKED 3 /* suspend acked */
73#define SUSPEND_DONE 4 /* suspend completed */
74
75 struct apm_queue queue;
76};
77
78/*
79 * Local variables
80 */
81static int suspends_pending;
82static int apm_disabled;
83static int mips_apm_active;
84
85static DECLARE_WAIT_QUEUE_HEAD(apm_waitqueue);
86static DECLARE_WAIT_QUEUE_HEAD(apm_suspend_waitqueue);
87
88/*
89 * This is a list of everyone who has opened /dev/apm_bios
90 */
91static DECLARE_RWSEM(user_list_lock);
92static LIST_HEAD(apm_user_list);
93
94/*
95 * kapmd info. kapmd provides us a process context to handle
96 * "APM" events within - specifically necessary if we're going
97 * to be suspending the system.
98 */
99static DECLARE_WAIT_QUEUE_HEAD(kapmd_wait);
100static DECLARE_COMPLETION(kapmd_exit);
101static DEFINE_SPINLOCK(kapmd_queue_lock);
102static struct apm_queue kapmd_queue;
103
104
105static const char driver_version[] = "1.13"; /* no spaces */
106
107
108
109/*
110 * Compatibility cruft until the IPAQ people move over to the new
111 * interface.
112 */
113static void __apm_get_power_status(struct apm_power_info *info)
114{
115}
116
117/*
118 * This allows machines to provide their own "apm get power status" function.
119 */
120void (*apm_get_power_status)(struct apm_power_info *) = __apm_get_power_status;
121EXPORT_SYMBOL(apm_get_power_status);
122
123
124/*
125 * APM event queue management.
126 */
127static inline int queue_empty(struct apm_queue *q)
128{
129 return q->event_head == q->event_tail;
130}
131
132static inline apm_event_t queue_get_event(struct apm_queue *q)
133{
134 q->event_tail = (q->event_tail + 1) % APM_MAX_EVENTS;
135 return q->events[q->event_tail];
136}
137
138static void queue_add_event(struct apm_queue *q, apm_event_t event)
139{
140 q->event_head = (q->event_head + 1) % APM_MAX_EVENTS;
141 if (q->event_head == q->event_tail) {
142 static int notified;
143
144 if (notified++ == 0)
145 printk(KERN_ERR "apm: an event queue overflowed\n");
146 q->event_tail = (q->event_tail + 1) % APM_MAX_EVENTS;
147 }
148 q->events[q->event_head] = event;
149}
150
151static void queue_event_one_user(struct apm_user *as, apm_event_t event)
152{
153 if (as->suser && as->writer) {
154 switch (event) {
155 case APM_SYS_SUSPEND:
156 case APM_USER_SUSPEND:
157 /*
158 * If this user already has a suspend pending,
159 * don't queue another one.
160 */
161 if (as->suspend_state != SUSPEND_NONE)
162 return;
163
164 as->suspend_state = SUSPEND_PENDING;
165 suspends_pending++;
166 break;
167 }
168 }
169 queue_add_event(&as->queue, event);
170}
171
172static void queue_event(apm_event_t event, struct apm_user *sender)
173{
174 struct apm_user *as;
175
176 down_read(&user_list_lock);
177 list_for_each_entry(as, &apm_user_list, list) {
178 if (as != sender && as->reader)
179 queue_event_one_user(as, event);
180 }
181 up_read(&user_list_lock);
182 wake_up_interruptible(&apm_waitqueue);
183}
184
185static void apm_suspend(void)
186{
187 struct apm_user *as;
188 int err = pm_suspend(PM_SUSPEND_MEM);
189
190 /*
191 * Anyone on the APM queues will think we're still suspended.
192 * Send a message so everyone knows we're now awake again.
193 */
194 queue_event(APM_NORMAL_RESUME, NULL);
195
196 /*
197 * Finally, wake up anyone who is sleeping on the suspend.
198 */
199 down_read(&user_list_lock);
200 list_for_each_entry(as, &apm_user_list, list) {
201 as->suspend_result = err;
202 as->suspend_state = SUSPEND_DONE;
203 }
204 up_read(&user_list_lock);
205
206 wake_up(&apm_suspend_waitqueue);
207}
208
209static ssize_t apm_read(struct file *fp, char __user *buf, size_t count, loff_t *ppos)
210{
211 struct apm_user *as = fp->private_data;
212 apm_event_t event;
213 int i = count, ret = 0;
214
215 if (count < sizeof(apm_event_t))
216 return -EINVAL;
217
218 if (queue_empty(&as->queue) && fp->f_flags & O_NONBLOCK)
219 return -EAGAIN;
220
221 wait_event_interruptible(apm_waitqueue, !queue_empty(&as->queue));
222
223 while ((i >= sizeof(event)) && !queue_empty(&as->queue)) {
224 event = queue_get_event(&as->queue);
225
226 ret = -EFAULT;
227 if (copy_to_user(buf, &event, sizeof(event)))
228 break;
229
230 if (event == APM_SYS_SUSPEND || event == APM_USER_SUSPEND)
231 as->suspend_state = SUSPEND_READ;
232
233 buf += sizeof(event);
234 i -= sizeof(event);
235 }
236
237 if (i < count)
238 ret = count - i;
239
240 return ret;
241}
242
243static unsigned int apm_poll(struct file *fp, poll_table * wait)
244{
245 struct apm_user *as = fp->private_data;
246
247 poll_wait(fp, &apm_waitqueue, wait);
248 return queue_empty(&as->queue) ? 0 : POLLIN | POLLRDNORM;
249}
250
251/*
252 * apm_ioctl - handle APM ioctl
253 *
254 * APM_IOC_SUSPEND
255 * This IOCTL is overloaded, and performs two functions. It is used to:
256 * - initiate a suspend
257 * - acknowledge a suspend read from /dev/apm_bios.
258 * Only when everyone who has opened /dev/apm_bios with write permission
259 * has acknowledge does the actual suspend happen.
260 */
261static int
262apm_ioctl(struct inode * inode, struct file *filp, unsigned int cmd, unsigned long arg)
263{
264 struct apm_user *as = filp->private_data;
265 unsigned long flags;
266 int err = -EINVAL;
267
268 if (!as->suser || !as->writer)
269 return -EPERM;
270
271 switch (cmd) {
272 case APM_IOC_SUSPEND:
273 as->suspend_result = -EINTR;
274
275 if (as->suspend_state == SUSPEND_READ) {
276 /*
277 * If we read a suspend command from /dev/apm_bios,
278 * then the corresponding APM_IOC_SUSPEND ioctl is
279 * interpreted as an acknowledge.
280 */
281 as->suspend_state = SUSPEND_ACKED;
282 suspends_pending--;
283 } else {
284 /*
285 * Otherwise it is a request to suspend the system.
286 * Queue an event for all readers, and expect an
287 * acknowledge from all writers who haven't already
288 * acknowledged.
289 */
290 queue_event(APM_USER_SUSPEND, as);
291 }
292
293 /*
294 * If there are no further acknowledges required, suspend
295 * the system.
296 */
297 if (suspends_pending == 0)
298 apm_suspend();
299
300 /*
301 * Wait for the suspend/resume to complete. If there are
302 * pending acknowledges, we wait here for them.
303 *
304 * Note that we need to ensure that the PM subsystem does
305 * not kick us out of the wait when it suspends the threads.
306 */
307 flags = current->flags;
308 current->flags |= PF_NOFREEZE;
309
310 /*
311 * Note: do not allow a thread which is acking the suspend
312 * to escape until the resume is complete.
313 */
314 if (as->suspend_state == SUSPEND_ACKED)
315 wait_event(apm_suspend_waitqueue,
316 as->suspend_state == SUSPEND_DONE);
317 else
318 wait_event_interruptible(apm_suspend_waitqueue,
319 as->suspend_state == SUSPEND_DONE);
320
321 current->flags = flags;
322 err = as->suspend_result;
323 as->suspend_state = SUSPEND_NONE;
324 break;
325 }
326
327 return err;
328}
329
330static int apm_release(struct inode * inode, struct file * filp)
331{
332 struct apm_user *as = filp->private_data;
333 filp->private_data = NULL;
334
335 down_write(&user_list_lock);
336 list_del(&as->list);
337 up_write(&user_list_lock);
338
339 /*
340 * We are now unhooked from the chain. As far as new
341 * events are concerned, we no longer exist. However, we
342 * need to balance suspends_pending, which means the
343 * possibility of sleeping.
344 */
345 if (as->suspend_state != SUSPEND_NONE) {
346 suspends_pending -= 1;
347 if (suspends_pending == 0)
348 apm_suspend();
349 }
350
351 kfree(as);
352 return 0;
353}
354
355static int apm_open(struct inode * inode, struct file * filp)
356{
357 struct apm_user *as;
358
359 as = kzalloc(sizeof(*as), GFP_KERNEL);
360 if (as) {
361 /*
362 * XXX - this is a tiny bit broken, when we consider BSD
363 * process accounting. If the device is opened by root, we
364 * instantly flag that we used superuser privs. Who knows,
365 * we might close the device immediately without doing a
366 * privileged operation -- cevans
367 */
368 as->suser = capable(CAP_SYS_ADMIN);
369 as->writer = (filp->f_mode & FMODE_WRITE) == FMODE_WRITE;
370 as->reader = (filp->f_mode & FMODE_READ) == FMODE_READ;
371
372 down_write(&user_list_lock);
373 list_add(&as->list, &apm_user_list);
374 up_write(&user_list_lock);
375
376 filp->private_data = as;
377 }
378
379 return as ? 0 : -ENOMEM;
380}
381
382static struct file_operations apm_bios_fops = {
383 .owner = THIS_MODULE,
384 .read = apm_read,
385 .poll = apm_poll,
386 .ioctl = apm_ioctl,
387 .open = apm_open,
388 .release = apm_release,
389};
390
391static struct miscdevice apm_device = {
392 .minor = APM_MINOR_DEV,
393 .name = "apm_bios",
394 .fops = &apm_bios_fops
395};
396
397
398#ifdef CONFIG_PROC_FS
399/*
400 * Arguments, with symbols from linux/apm_bios.h.
401 *
402 * 0) Linux driver version (this will change if format changes)
403 * 1) APM BIOS Version. Usually 1.0, 1.1 or 1.2.
404 * 2) APM flags from APM Installation Check (0x00):
405 * bit 0: APM_16_BIT_SUPPORT
406 * bit 1: APM_32_BIT_SUPPORT
407 * bit 2: APM_IDLE_SLOWS_CLOCK
408 * bit 3: APM_BIOS_DISABLED
409 * bit 4: APM_BIOS_DISENGAGED
410 * 3) AC line status
411 * 0x00: Off-line
412 * 0x01: On-line
413 * 0x02: On backup power (BIOS >= 1.1 only)
414 * 0xff: Unknown
415 * 4) Battery status
416 * 0x00: High
417 * 0x01: Low
418 * 0x02: Critical
419 * 0x03: Charging
420 * 0x04: Selected battery not present (BIOS >= 1.2 only)
421 * 0xff: Unknown
422 * 5) Battery flag
423 * bit 0: High
424 * bit 1: Low
425 * bit 2: Critical
426 * bit 3: Charging
427 * bit 7: No system battery
428 * 0xff: Unknown
429 * 6) Remaining battery life (percentage of charge):
430 * 0-100: valid
431 * -1: Unknown
432 * 7) Remaining battery life (time units):
433 * Number of remaining minutes or seconds
434 * -1: Unknown
435 * 8) min = minutes; sec = seconds
436 */
437static int apm_get_info(char *buf, char **start, off_t fpos, int length)
438{
439 struct apm_power_info info;
440 char *units;
441 int ret;
442
443 info.ac_line_status = 0xff;
444 info.battery_status = 0xff;
445 info.battery_flag = 0xff;
446 info.battery_life = -1;
447 info.time = -1;
448 info.units = -1;
449
450 if (apm_get_power_status)
451 apm_get_power_status(&info);
452
453 switch (info.units) {
454 default: units = "?"; break;
455 case 0: units = "min"; break;
456 case 1: units = "sec"; break;
457 }
458
459 ret = sprintf(buf, "%s 1.2 0x%02x 0x%02x 0x%02x 0x%02x %d%% %d %s\n",
460 driver_version, APM_32_BIT_SUPPORT,
461 info.ac_line_status, info.battery_status,
462 info.battery_flag, info.battery_life,
463 info.time, units);
464
465 return ret;
466}
467#endif
468
469static int kapmd(void *arg)
470{
471 daemonize("kapmd");
472 current->flags |= PF_NOFREEZE;
473
474 do {
475 apm_event_t event;
476
477 wait_event_interruptible(kapmd_wait,
478 !queue_empty(&kapmd_queue) || !mips_apm_active);
479
480 if (!mips_apm_active)
481 break;
482
483 spin_lock_irq(&kapmd_queue_lock);
484 event = 0;
485 if (!queue_empty(&kapmd_queue))
486 event = queue_get_event(&kapmd_queue);
487 spin_unlock_irq(&kapmd_queue_lock);
488
489 switch (event) {
490 case 0:
491 break;
492
493 case APM_LOW_BATTERY:
494 case APM_POWER_STATUS_CHANGE:
495 queue_event(event, NULL);
496 break;
497
498 case APM_USER_SUSPEND:
499 case APM_SYS_SUSPEND:
500 queue_event(event, NULL);
501 if (suspends_pending == 0)
502 apm_suspend();
503 break;
504
505 case APM_CRITICAL_SUSPEND:
506 apm_suspend();
507 break;
508 }
509 } while (1);
510
511 complete_and_exit(&kapmd_exit, 0);
512}
513
514static int __init apm_init(void)
515{
516 int ret;
517
518 if (apm_disabled) {
519 printk(KERN_NOTICE "apm: disabled on user request.\n");
520 return -ENODEV;
521 }
522
523 mips_apm_active = 1;
524
525 ret = kernel_thread(kapmd, NULL, CLONE_KERNEL);
526 if (ret < 0) {
527 mips_apm_active = 0;
528 return ret;
529 }
530
531#ifdef CONFIG_PROC_FS
532 create_proc_info_entry("apm", 0, NULL, apm_get_info);
533#endif
534
535 ret = misc_register(&apm_device);
536 if (ret != 0) {
537 remove_proc_entry("apm", NULL);
538
539 mips_apm_active = 0;
540 wake_up(&kapmd_wait);
541 wait_for_completion(&kapmd_exit);
542 }
543
544 return ret;
545}
546
547static void __exit apm_exit(void)
548{
549 misc_deregister(&apm_device);
550 remove_proc_entry("apm", NULL);
551
552 mips_apm_active = 0;
553 wake_up(&kapmd_wait);
554 wait_for_completion(&kapmd_exit);
555}
556
557module_init(apm_init);
558module_exit(apm_exit);
559
560MODULE_AUTHOR("Stephen Rothwell");
561MODULE_DESCRIPTION("Advanced Power Management");
562MODULE_LICENSE("GPL");
563
564#ifndef MODULE
565static int __init apm_setup(char *str)
566{
567 while ((str != NULL) && (*str != '\0')) {
568 if (strncmp(str, "off", 3) == 0)
569 apm_disabled = 1;
570 if (strncmp(str, "on", 2) == 0)
571 apm_disabled = 0;
572 str = strchr(str, ',');
573 if (str != NULL)
574 str += strspn(str, ", \t");
575 }
576 return 1;
577}
578
579__setup("apm=", apm_setup);
580#endif
581
582/**
583 * apm_queue_event - queue an APM event for kapmd
584 * @event: APM event
585 *
586 * Queue an APM event for kapmd to process and ultimately take the
587 * appropriate action. Only a subset of events are handled:
588 * %APM_LOW_BATTERY
589 * %APM_POWER_STATUS_CHANGE
590 * %APM_USER_SUSPEND
591 * %APM_SYS_SUSPEND
592 * %APM_CRITICAL_SUSPEND
593 */
594void apm_queue_event(apm_event_t event)
595{
596 unsigned long flags;
597
598 spin_lock_irqsave(&kapmd_queue_lock, flags);
599 queue_add_event(&kapmd_queue, event);
600 spin_unlock_irqrestore(&kapmd_queue_lock, flags);
601
602 wake_up_interruptible(&kapmd_wait);
603}
604EXPORT_SYMBOL(apm_queue_event);
diff --git a/arch/sh/Kconfig b/arch/sh/Kconfig
index 3aa3b885ab36..4f3891215b87 100644
--- a/arch/sh/Kconfig
+++ b/arch/sh/Kconfig
@@ -48,6 +48,9 @@ config GENERIC_IOMAP
48config GENERIC_TIME 48config GENERIC_TIME
49 def_bool n 49 def_bool n
50 50
51config SYS_SUPPORTS_APM_EMULATION
52 bool
53
51config ARCH_MAY_HAVE_PC_FDC 54config ARCH_MAY_HAVE_PC_FDC
52 bool 55 bool
53 56
@@ -126,6 +129,7 @@ config SH_7751_SYSTEMH
126 129
127config SH_HP6XX 130config SH_HP6XX
128 bool "HP6XX" 131 bool "HP6XX"
132 select SYS_SUPPORTS_APM_EMULATION
129 help 133 help
130 Select HP6XX if configuring for a HP jornada HP6xx. 134 Select HP6XX if configuring for a HP jornada HP6xx.
131 More information (hardware only) at 135 More information (hardware only) at
@@ -694,9 +698,6 @@ depends on EXPERIMENTAL
694 698
695source kernel/power/Kconfig 699source kernel/power/Kconfig
696 700
697config APM
698 bool "Advanced Power Management Emulation"
699 depends on PM
700endmenu 701endmenu
701 702
702source "net/Kconfig" 703source "net/Kconfig"
diff --git a/arch/sh/boards/hp6xx/hp6xx_apm.c b/arch/sh/boards/hp6xx/hp6xx_apm.c
index d146cdaa0b8b..d1c1460c8a06 100644
--- a/arch/sh/boards/hp6xx/hp6xx_apm.c
+++ b/arch/sh/boards/hp6xx/hp6xx_apm.c
@@ -7,12 +7,11 @@
7 * modify it under the terms of the GNU General Public License. 7 * modify it under the terms of the GNU General Public License.
8 */ 8 */
9#include <linux/module.h> 9#include <linux/module.h>
10#include <linux/apm_bios.h>
11#include <linux/kernel.h> 10#include <linux/kernel.h>
12#include <linux/init.h> 11#include <linux/init.h>
13#include <linux/interrupt.h> 12#include <linux/interrupt.h>
14#include <asm/io.h> 13#include <linux/apm-emulation.h>
15#include <asm/apm.h> 14#include <linux/io.h>
16#include <asm/adc.h> 15#include <asm/adc.h>
17#include <asm/hp6xx.h> 16#include <asm/hp6xx.h>
18 17
@@ -27,60 +26,41 @@
27 26
28#define MODNAME "hp6x0_apm" 27#define MODNAME "hp6x0_apm"
29 28
30static int hp6x0_apm_get_info(char *buf, char **start, off_t fpos, int length) 29static void hp6x0_apm_get_power_status(struct apm_power_info *info)
31{ 30{
31 int battery, backup, charging, percentage;
32 u8 pgdr; 32 u8 pgdr;
33 char *p;
34 int battery_status;
35 int battery_flag;
36 int ac_line_status;
37 int time_units = APM_BATTERY_LIFE_UNKNOWN;
38 33
39 int battery = adc_single(ADC_CHANNEL_BATTERY); 34 battery = adc_single(ADC_CHANNEL_BATTERY);
40 int backup = adc_single(ADC_CHANNEL_BACKUP); 35 backup = adc_single(ADC_CHANNEL_BACKUP);
41 int charging = adc_single(ADC_CHANNEL_CHARGE); 36 charging = adc_single(ADC_CHANNEL_CHARGE);
42 int percentage;
43 37
44 percentage = 100 * (battery - HP680_BATTERY_MIN) / 38 percentage = 100 * (battery - HP680_BATTERY_MIN) /
45 (HP680_BATTERY_MAX - HP680_BATTERY_MIN); 39 (HP680_BATTERY_MAX - HP680_BATTERY_MIN);
46 40
47 ac_line_status = (battery > HP680_BATTERY_AC_ON) ? 41 info->ac_line_status = (battery > HP680_BATTERY_AC_ON) ?
48 APM_AC_ONLINE : APM_AC_OFFLINE; 42 APM_AC_ONLINE : APM_AC_OFFLINE;
49 43
50 p = buf;
51
52 pgdr = ctrl_inb(SH7709_PGDR); 44 pgdr = ctrl_inb(SH7709_PGDR);
53 if (pgdr & PGDR_MAIN_BATTERY_OUT) { 45 if (pgdr & PGDR_MAIN_BATTERY_OUT) {
54 battery_status = APM_BATTERY_STATUS_NOT_PRESENT; 46 info->battery_status = APM_BATTERY_STATUS_NOT_PRESENT;
55 battery_flag = 0x80; 47 info->battery_flag = 0x80;
56 percentage = -1; 48 } else if (charging < 8) {
57 } else if (charging < 8 ) { 49 info->battery_status = APM_BATTERY_STATUS_CHARGING;
58 battery_status = APM_BATTERY_STATUS_CHARGING; 50 info->battery_flag = 0x08;
59 battery_flag = 0x08; 51 info->ac_line_status = 0xff;
60 ac_line_status = 0xff;
61 } else if (percentage <= APM_CRITICAL) { 52 } else if (percentage <= APM_CRITICAL) {
62 battery_status = APM_BATTERY_STATUS_CRITICAL; 53 info->battery_status = APM_BATTERY_STATUS_CRITICAL;
63 battery_flag = 0x04; 54 info->battery_flag = 0x04;
64 } else if (percentage <= APM_LOW) { 55 } else if (percentage <= APM_LOW) {
65 battery_status = APM_BATTERY_STATUS_LOW; 56 info->battery_status = APM_BATTERY_STATUS_LOW;
66 battery_flag = 0x02; 57 info->battery_flag = 0x02;
67 } else { 58 } else {
68 battery_status = APM_BATTERY_STATUS_HIGH; 59 info->battery_status = APM_BATTERY_STATUS_HIGH;
69 battery_flag = 0x01; 60 info->battery_flag = 0x01;
70 } 61 }
71 62
72 p += sprintf(p, "1.0 1.2 0x%02x 0x%02x 0x%02x 0x%02x %d%% %d %s\n", 63 info->units = 0;
73 APM_32_BIT_SUPPORT,
74 ac_line_status,
75 battery_status,
76 battery_flag,
77 percentage,
78 time_units,
79 "min");
80 p += sprintf(p, "bat=%d backup=%d charge=%d\n",
81 battery, backup, charging);
82
83 return p - buf;
84} 64}
85 65
86static irqreturn_t hp6x0_apm_interrupt(int irq, void *dev) 66static irqreturn_t hp6x0_apm_interrupt(int irq, void *dev)
@@ -96,14 +76,14 @@ static int __init hp6x0_apm_init(void)
96 int ret; 76 int ret;
97 77
98 ret = request_irq(HP680_BTN_IRQ, hp6x0_apm_interrupt, 78 ret = request_irq(HP680_BTN_IRQ, hp6x0_apm_interrupt,
99 IRQF_DISABLED, MODNAME, 0); 79 IRQF_DISABLED, MODNAME, NULL);
100 if (unlikely(ret < 0)) { 80 if (unlikely(ret < 0)) {
101 printk(KERN_ERR MODNAME ": IRQ %d request failed\n", 81 printk(KERN_ERR MODNAME ": IRQ %d request failed\n",
102 HP680_BTN_IRQ); 82 HP680_BTN_IRQ);
103 return ret; 83 return ret;
104 } 84 }
105 85
106 apm_get_info = hp6x0_apm_get_info; 86 apm_get_power_status = hp6x0_apm_get_power_status;
107 87
108 return ret; 88 return ret;
109} 89}
@@ -111,7 +91,7 @@ static int __init hp6x0_apm_init(void)
111static void __exit hp6x0_apm_exit(void) 91static void __exit hp6x0_apm_exit(void)
112{ 92{
113 free_irq(HP680_BTN_IRQ, 0); 93 free_irq(HP680_BTN_IRQ, 0);
114 apm_get_info = 0; 94 apm_get_info = NULL;
115} 95}
116 96
117module_init(hp6x0_apm_init); 97module_init(hp6x0_apm_init);
diff --git a/arch/sh/kernel/Makefile b/arch/sh/kernel/Makefile
index 99c7e5249f7a..2f6d2bcb1c93 100644
--- a/arch/sh/kernel/Makefile
+++ b/arch/sh/kernel/Makefile
@@ -19,6 +19,5 @@ obj-$(CONFIG_SH_CPU_FREQ) += cpufreq.o
19obj-$(CONFIG_MODULES) += module.o 19obj-$(CONFIG_MODULES) += module.o
20obj-$(CONFIG_EARLY_PRINTK) += early_printk.o 20obj-$(CONFIG_EARLY_PRINTK) += early_printk.o
21obj-$(CONFIG_KEXEC) += machine_kexec.o relocate_kernel.o 21obj-$(CONFIG_KEXEC) += machine_kexec.o relocate_kernel.o
22obj-$(CONFIG_APM) += apm.o
23obj-$(CONFIG_PM) += pm.o 22obj-$(CONFIG_PM) += pm.o
24obj-$(CONFIG_STACKTRACE) += stacktrace.o 23obj-$(CONFIG_STACKTRACE) += stacktrace.o
diff --git a/arch/sh/kernel/apm.c b/arch/sh/kernel/apm.c
deleted file mode 100644
index 4f66f91b1006..000000000000
--- a/arch/sh/kernel/apm.c
+++ /dev/null
@@ -1,538 +0,0 @@
1/*
2 * bios-less APM driver for hp680
3 *
4 * Copyright 2005 (c) Andriy Skulysh <askulysh@gmail.com>
5 *
6 * based on ARM APM driver by
7 * Jamey Hicks <jamey@crl.dec.com>
8 *
9 * adapted from the APM BIOS driver for Linux by
10 * Stephen Rothwell (sfr@linuxcare.com)
11 *
12 * APM 1.2 Reference:
13 * Intel Corporation, Microsoft Corporation. Advanced Power Management
14 * (APM) BIOS Interface Specification, Revision 1.2, February 1996.
15 *
16 * [This document is available from Microsoft at:
17 * http://www.microsoft.com/hwdev/busbios/amp_12.htm]
18 */
19#include <linux/module.h>
20#include <linux/poll.h>
21#include <linux/timer.h>
22#include <linux/slab.h>
23#include <linux/proc_fs.h>
24#include <linux/miscdevice.h>
25#include <linux/apm_bios.h>
26#include <linux/pm.h>
27#include <linux/pm_legacy.h>
28#include <asm/apm.h>
29
30#define MODNAME "apm"
31
32/*
33 * The apm_bios device is one of the misc char devices.
34 * This is its minor number.
35 */
36#define APM_MINOR_DEV 134
37
38/*
39 * Maximum number of events stored
40 */
41#define APM_MAX_EVENTS 16
42
43struct apm_queue {
44 unsigned int event_head;
45 unsigned int event_tail;
46 apm_event_t events[APM_MAX_EVENTS];
47};
48
49/*
50 * The per-file APM data
51 */
52struct apm_user {
53 struct list_head list;
54
55 unsigned int suser: 1;
56 unsigned int writer: 1;
57 unsigned int reader: 1;
58
59 int suspend_result;
60 unsigned int suspend_state;
61#define SUSPEND_NONE 0 /* no suspend pending */
62#define SUSPEND_PENDING 1 /* suspend pending read */
63#define SUSPEND_READ 2 /* suspend read, pending ack */
64#define SUSPEND_ACKED 3 /* suspend acked */
65#define SUSPEND_DONE 4 /* suspend completed */
66
67 struct apm_queue queue;
68};
69
70/*
71 * Local variables
72 */
73static int suspends_pending;
74
75static DECLARE_WAIT_QUEUE_HEAD(apm_waitqueue);
76static DECLARE_WAIT_QUEUE_HEAD(apm_suspend_waitqueue);
77
78/*
79 * This is a list of everyone who has opened /dev/apm_bios
80 */
81static DECLARE_RWSEM(user_list_lock);
82static LIST_HEAD(apm_user_list);
83
84/*
85 * kapmd info. kapmd provides us a process context to handle
86 * "APM" events within - specifically necessary if we're going
87 * to be suspending the system.
88 */
89static DECLARE_WAIT_QUEUE_HEAD(kapmd_wait);
90static DECLARE_COMPLETION(kapmd_exit);
91static DEFINE_SPINLOCK(kapmd_queue_lock);
92static struct apm_queue kapmd_queue;
93
94int apm_suspended;
95EXPORT_SYMBOL(apm_suspended);
96
97/* Platform-specific apm_read_proc(). */
98int (*apm_get_info)(char *buf, char **start, off_t fpos, int length);
99EXPORT_SYMBOL(apm_get_info);
100
101/*
102 * APM event queue management.
103 */
104static inline int queue_empty(struct apm_queue *q)
105{
106 return q->event_head == q->event_tail;
107}
108
109static inline apm_event_t queue_get_event(struct apm_queue *q)
110{
111 q->event_tail = (q->event_tail + 1) % APM_MAX_EVENTS;
112 return q->events[q->event_tail];
113}
114
115static void queue_add_event(struct apm_queue *q, apm_event_t event)
116{
117 q->event_head = (q->event_head + 1) % APM_MAX_EVENTS;
118 if (q->event_head == q->event_tail) {
119 static int notified;
120
121 if (notified++ == 0)
122 printk(KERN_ERR "apm: an event queue overflowed\n");
123
124 q->event_tail = (q->event_tail + 1) % APM_MAX_EVENTS;
125 }
126 q->events[q->event_head] = event;
127}
128
129static void queue_event_one_user(struct apm_user *as, apm_event_t event)
130{
131 if (as->suser && as->writer) {
132 switch (event) {
133 case APM_SYS_SUSPEND:
134 case APM_USER_SUSPEND:
135 /*
136 * If this user already has a suspend pending,
137 * don't queue another one.
138 */
139 if (as->suspend_state != SUSPEND_NONE)
140 return;
141
142 as->suspend_state = SUSPEND_PENDING;
143 suspends_pending++;
144 break;
145 }
146 }
147 queue_add_event(&as->queue, event);
148}
149
150static void queue_event(apm_event_t event, struct apm_user *sender)
151{
152 struct apm_user *as;
153
154 down_read(&user_list_lock);
155
156 list_for_each_entry(as, &apm_user_list, list)
157 if (as != sender && as->reader)
158 queue_event_one_user(as, event);
159
160 up_read(&user_list_lock);
161 wake_up_interruptible(&apm_waitqueue);
162}
163
164/**
165 * apm_queue_event - queue an APM event for kapmd
166 * @event: APM event
167 *
168 * Queue an APM event for kapmd to process and ultimately take the
169 * appropriate action. Only a subset of events are handled:
170 * %APM_LOW_BATTERY
171 * %APM_POWER_STATUS_CHANGE
172 * %APM_USER_SUSPEND
173 * %APM_SYS_SUSPEND
174 * %APM_CRITICAL_SUSPEND
175 */
176void apm_queue_event(apm_event_t event)
177{
178 spin_lock_irq(&kapmd_queue_lock);
179 queue_add_event(&kapmd_queue, event);
180 spin_unlock_irq(&kapmd_queue_lock);
181
182 wake_up_interruptible(&kapmd_wait);
183}
184EXPORT_SYMBOL(apm_queue_event);
185
186static void apm_suspend(void)
187{
188 struct apm_user *as;
189 int err;
190
191 apm_suspended = 1;
192 err = pm_suspend(PM_SUSPEND_MEM);
193
194 /*
195 * Anyone on the APM queues will think we're still suspended.
196 * Send a message so everyone knows we're now awake again.
197 */
198 queue_event(APM_NORMAL_RESUME, NULL);
199
200 /*
201 * Finally, wake up anyone who is sleeping on the suspend.
202 */
203 down_read(&user_list_lock);
204 list_for_each_entry(as, &apm_user_list, list) {
205 as->suspend_result = err;
206 as->suspend_state = SUSPEND_DONE;
207 }
208 up_read(&user_list_lock);
209
210 wake_up(&apm_suspend_waitqueue);
211 apm_suspended = 0;
212}
213
214static ssize_t apm_read(struct file *fp, char __user *buf,
215 size_t count, loff_t *ppos)
216{
217 struct apm_user *as = fp->private_data;
218 apm_event_t event;
219 int i = count, ret = 0;
220
221 if (count < sizeof(apm_event_t))
222 return -EINVAL;
223
224 if (queue_empty(&as->queue) && fp->f_flags & O_NONBLOCK)
225 return -EAGAIN;
226
227 wait_event_interruptible(apm_waitqueue, !queue_empty(&as->queue));
228
229 while ((i >= sizeof(event)) && !queue_empty(&as->queue)) {
230 event = queue_get_event(&as->queue);
231
232 ret = -EFAULT;
233 if (copy_to_user(buf, &event, sizeof(event)))
234 break;
235
236 if (event == APM_SYS_SUSPEND || event == APM_USER_SUSPEND)
237 as->suspend_state = SUSPEND_READ;
238
239 buf += sizeof(event);
240 i -= sizeof(event);
241 }
242
243 if (i < count)
244 ret = count - i;
245
246 return ret;
247}
248
249static unsigned int apm_poll(struct file *fp, poll_table * wait)
250{
251 struct apm_user *as = fp->private_data;
252
253 poll_wait(fp, &apm_waitqueue, wait);
254 return queue_empty(&as->queue) ? 0 : POLLIN | POLLRDNORM;
255}
256
257/*
258 * apm_ioctl - handle APM ioctl
259 *
260 * APM_IOC_SUSPEND
261 * This IOCTL is overloaded, and performs two functions. It is used to:
262 * - initiate a suspend
263 * - acknowledge a suspend read from /dev/apm_bios.
264 * Only when everyone who has opened /dev/apm_bios with write permission
265 * has acknowledge does the actual suspend happen.
266 */
267static int
268apm_ioctl(struct inode * inode, struct file *filp, u_int cmd, u_long arg)
269{
270 struct apm_user *as = filp->private_data;
271 unsigned long flags;
272 int err = -EINVAL;
273
274 if (!as->suser || !as->writer)
275 return -EPERM;
276
277 switch (cmd) {
278 case APM_IOC_SUSPEND:
279 as->suspend_result = -EINTR;
280
281 if (as->suspend_state == SUSPEND_READ) {
282 /*
283 * If we read a suspend command from /dev/apm_bios,
284 * then the corresponding APM_IOC_SUSPEND ioctl is
285 * interpreted as an acknowledge.
286 */
287 as->suspend_state = SUSPEND_ACKED;
288 suspends_pending--;
289 } else {
290 /*
291 * Otherwise it is a request to suspend the system.
292 * Queue an event for all readers, and expect an
293 * acknowledge from all writers who haven't already
294 * acknowledged.
295 */
296 queue_event(APM_USER_SUSPEND, as);
297 }
298
299 /*
300 * If there are no further acknowledges required, suspend
301 * the system.
302 */
303 if (suspends_pending == 0)
304 apm_suspend();
305
306 /*
307 * Wait for the suspend/resume to complete. If there are
308 * pending acknowledges, we wait here for them.
309 *
310 * Note that we need to ensure that the PM subsystem does
311 * not kick us out of the wait when it suspends the threads.
312 */
313 flags = current->flags;
314 current->flags |= PF_NOFREEZE;
315
316 /*
317 * Note: do not allow a thread which is acking the suspend
318 * to escape until the resume is complete.
319 */
320 if (as->suspend_state == SUSPEND_ACKED)
321 wait_event(apm_suspend_waitqueue,
322 as->suspend_state == SUSPEND_DONE);
323 else
324 wait_event_interruptible(apm_suspend_waitqueue,
325 as->suspend_state == SUSPEND_DONE);
326
327 current->flags = flags;
328 err = as->suspend_result;
329 as->suspend_state = SUSPEND_NONE;
330 break;
331 }
332
333 return err;
334}
335
336static int apm_release(struct inode * inode, struct file * filp)
337{
338 struct apm_user *as = filp->private_data;
339 filp->private_data = NULL;
340
341 down_write(&user_list_lock);
342 list_del(&as->list);
343 up_write(&user_list_lock);
344
345 /*
346 * We are now unhooked from the chain. As far as new
347 * events are concerned, we no longer exist. However, we
348 * need to balance suspends_pending, which means the
349 * possibility of sleeping.
350 */
351 if (as->suspend_state != SUSPEND_NONE) {
352 suspends_pending -= 1;
353 if (suspends_pending == 0)
354 apm_suspend();
355 }
356
357 kfree(as);
358 return 0;
359}
360
361static int apm_open(struct inode * inode, struct file * filp)
362{
363 struct apm_user *as;
364
365 as = kzalloc(sizeof(*as), GFP_KERNEL);
366 if (as) {
367 /*
368 * XXX - this is a tiny bit broken, when we consider BSD
369 * process accounting. If the device is opened by root, we
370 * instantly flag that we used superuser privs. Who knows,
371 * we might close the device immediately without doing a
372 * privileged operation -- cevans
373 */
374 as->suser = capable(CAP_SYS_ADMIN);
375 as->writer = (filp->f_mode & FMODE_WRITE) == FMODE_WRITE;
376 as->reader = (filp->f_mode & FMODE_READ) == FMODE_READ;
377
378 down_write(&user_list_lock);
379 list_add(&as->list, &apm_user_list);
380 up_write(&user_list_lock);
381
382 filp->private_data = as;
383 }
384
385 return as ? 0 : -ENOMEM;
386}
387
388static struct file_operations apm_bios_fops = {
389 .owner = THIS_MODULE,
390 .read = apm_read,
391 .poll = apm_poll,
392 .ioctl = apm_ioctl,
393 .open = apm_open,
394 .release = apm_release,
395};
396
397static struct miscdevice apm_device = {
398 .minor = APM_MINOR_DEV,
399 .name = "apm_bios",
400 .fops = &apm_bios_fops
401};
402
403
404#ifdef CONFIG_PROC_FS
405/*
406 * Arguments, with symbols from linux/apm_bios.h.
407 *
408 * 0) Linux driver version (this will change if format changes)
409 * 1) APM BIOS Version. Usually 1.0, 1.1 or 1.2.
410 * 2) APM flags from APM Installation Check (0x00):
411 * bit 0: APM_16_BIT_SUPPORT
412 * bit 1: APM_32_BIT_SUPPORT
413 * bit 2: APM_IDLE_SLOWS_CLOCK
414 * bit 3: APM_BIOS_DISABLED
415 * bit 4: APM_BIOS_DISENGAGED
416 * 3) AC line status
417 * 0x00: Off-line
418 * 0x01: On-line
419 * 0x02: On backup power (BIOS >= 1.1 only)
420 * 0xff: Unknown
421 * 4) Battery status
422 * 0x00: High
423 * 0x01: Low
424 * 0x02: Critical
425 * 0x03: Charging
426 * 0x04: Selected battery not present (BIOS >= 1.2 only)
427 * 0xff: Unknown
428 * 5) Battery flag
429 * bit 0: High
430 * bit 1: Low
431 * bit 2: Critical
432 * bit 3: Charging
433 * bit 7: No system battery
434 * 0xff: Unknown
435 * 6) Remaining battery life (percentage of charge):
436 * 0-100: valid
437 * -1: Unknown
438 * 7) Remaining battery life (time units):
439 * Number of remaining minutes or seconds
440 * -1: Unknown
441 * 8) min = minutes; sec = seconds
442 */
443static int apm_read_proc(char *buf, char **start, off_t fpos, int length)
444{
445 if (likely(apm_get_info))
446 return apm_get_info(buf, start, fpos, length);
447
448 return -EINVAL;
449}
450#endif
451
452static int kapmd(void *arg)
453{
454 daemonize("kapmd");
455 current->flags |= PF_NOFREEZE;
456
457 do {
458 apm_event_t event;
459
460 wait_event_interruptible(kapmd_wait,
461 !queue_empty(&kapmd_queue) || !pm_active);
462
463 if (!pm_active)
464 break;
465
466 spin_lock_irq(&kapmd_queue_lock);
467 event = 0;
468 if (!queue_empty(&kapmd_queue))
469 event = queue_get_event(&kapmd_queue);
470 spin_unlock_irq(&kapmd_queue_lock);
471
472 switch (event) {
473 case 0:
474 break;
475
476 case APM_LOW_BATTERY:
477 case APM_POWER_STATUS_CHANGE:
478 queue_event(event, NULL);
479 break;
480
481 case APM_USER_SUSPEND:
482 case APM_SYS_SUSPEND:
483 queue_event(event, NULL);
484 if (suspends_pending == 0)
485 apm_suspend();
486 break;
487
488 case APM_CRITICAL_SUSPEND:
489 apm_suspend();
490 break;
491 }
492 } while (1);
493
494 complete_and_exit(&kapmd_exit, 0);
495}
496
497static int __init apm_init(void)
498{
499 int ret;
500
501 pm_active = 1;
502
503 ret = kernel_thread(kapmd, NULL, CLONE_KERNEL);
504 if (unlikely(ret < 0)) {
505 pm_active = 0;
506 return ret;
507 }
508
509 create_proc_info_entry("apm", 0, NULL, apm_read_proc);
510
511 ret = misc_register(&apm_device);
512 if (unlikely(ret != 0)) {
513 remove_proc_entry("apm", NULL);
514
515 pm_active = 0;
516 wake_up(&kapmd_wait);
517 wait_for_completion(&kapmd_exit);
518 }
519
520 return ret;
521}
522
523static void __exit apm_exit(void)
524{
525 misc_deregister(&apm_device);
526 remove_proc_entry("apm", NULL);
527
528 pm_active = 0;
529 wake_up(&kapmd_wait);
530 wait_for_completion(&kapmd_exit);
531}
532
533module_init(apm_init);
534module_exit(apm_exit);
535
536MODULE_AUTHOR("Stephen Rothwell, Andriy Skulysh");
537MODULE_DESCRIPTION("Advanced Power Management");
538MODULE_LICENSE("GPL");
diff --git a/drivers/char/Makefile b/drivers/char/Makefile
index 0326ca1a848a..ae8567cc529c 100644
--- a/drivers/char/Makefile
+++ b/drivers/char/Makefile
@@ -60,6 +60,8 @@ obj-$(CONFIG_BRIQ_PANEL) += briq_panel.o
60obj-$(CONFIG_PRINTER) += lp.o 60obj-$(CONFIG_PRINTER) += lp.o
61obj-$(CONFIG_TIPAR) += tipar.o 61obj-$(CONFIG_TIPAR) += tipar.o
62 62
63obj-$(CONFIG_APM_EMULATION) += apm-emulation.o
64
63obj-$(CONFIG_DTLK) += dtlk.o 65obj-$(CONFIG_DTLK) += dtlk.o
64obj-$(CONFIG_R3964) += n_r3964.o 66obj-$(CONFIG_R3964) += n_r3964.o
65obj-$(CONFIG_APPLICOM) += applicom.o 67obj-$(CONFIG_APPLICOM) += applicom.o
diff --git a/arch/arm/kernel/apm.c b/drivers/char/apm-emulation.c
index 2c37b70b17ab..179c7a3b6e75 100644
--- a/arch/arm/kernel/apm.c
+++ b/drivers/char/apm-emulation.c
@@ -1,5 +1,5 @@
1/* 1/*
2 * bios-less APM driver for ARM Linux 2 * bios-less APM driver for ARM Linux
3 * Jamey Hicks <jamey@crl.dec.com> 3 * Jamey Hicks <jamey@crl.dec.com>
4 * adapted from the APM BIOS driver for Linux by Stephen Rothwell (sfr@linuxcare.com) 4 * adapted from the APM BIOS driver for Linux by Stephen Rothwell (sfr@linuxcare.com)
5 * 5 *
@@ -19,6 +19,7 @@
19#include <linux/capability.h> 19#include <linux/capability.h>
20#include <linux/sched.h> 20#include <linux/sched.h>
21#include <linux/pm.h> 21#include <linux/pm.h>
22#include <linux/apm-emulation.h>
22#include <linux/device.h> 23#include <linux/device.h>
23#include <linux/kernel.h> 24#include <linux/kernel.h>
24#include <linux/list.h> 25#include <linux/list.h>
@@ -27,7 +28,6 @@
27#include <linux/kthread.h> 28#include <linux/kthread.h>
28#include <linux/delay.h> 29#include <linux/delay.h>
29 30
30#include <asm/apm.h> /* apm_power_info */
31#include <asm/system.h> 31#include <asm/system.h>
32 32
33/* 33/*
@@ -225,7 +225,7 @@ static void apm_suspend(void)
225 list_for_each_entry(as, &apm_user_list, list) { 225 list_for_each_entry(as, &apm_user_list, list) {
226 if (as->suspend_state == SUSPEND_WAIT || 226 if (as->suspend_state == SUSPEND_WAIT ||
227 as->suspend_state == SUSPEND_ACKED) { 227 as->suspend_state == SUSPEND_ACKED) {
228 as->suspend_result = err; 228 as->suspend_result = err;
229 as->suspend_state = SUSPEND_DONE; 229 as->suspend_state = SUSPEND_DONE;
230 } 230 }
231 } 231 }
@@ -529,7 +529,7 @@ static int apm_get_info(char *buf, char **start, off_t fpos, int length)
529 info.battery_flag, info.battery_life, 529 info.battery_flag, info.battery_life,
530 info.time, units); 530 info.time, units);
531 531
532 return ret; 532 return ret;
533} 533}
534#endif 534#endif
535 535
diff --git a/include/asm-arm/apm.h b/include/asm-arm/apm.h
deleted file mode 100644
index d09113b37e4a..000000000000
--- a/include/asm-arm/apm.h
+++ /dev/null
@@ -1,64 +0,0 @@
1/* -*- linux-c -*-
2 *
3 * (C) 2003 zecke@handhelds.org
4 *
5 * GPL version 2
6 *
7 * based on arch/arm/kernel/apm.c
8 * factor out the information needed by architectures to provide
9 * apm status
10 *
11 *
12 */
13#ifndef ARM_ASM_SA1100_APM_H
14#define ARM_ASM_SA1100_APM_H
15
16#include <linux/apm_bios.h>
17
18/*
19 * This structure gets filled in by the machine specific 'get_power_status'
20 * implementation. Any fields which are not set default to a safe value.
21 */
22struct apm_power_info {
23 unsigned char ac_line_status;
24#define APM_AC_OFFLINE 0
25#define APM_AC_ONLINE 1
26#define APM_AC_BACKUP 2
27#define APM_AC_UNKNOWN 0xff
28
29 unsigned char battery_status;
30#define APM_BATTERY_STATUS_HIGH 0
31#define APM_BATTERY_STATUS_LOW 1
32#define APM_BATTERY_STATUS_CRITICAL 2
33#define APM_BATTERY_STATUS_CHARGING 3
34#define APM_BATTERY_STATUS_NOT_PRESENT 4
35#define APM_BATTERY_STATUS_UNKNOWN 0xff
36
37 unsigned char battery_flag;
38#define APM_BATTERY_FLAG_HIGH (1 << 0)
39#define APM_BATTERY_FLAG_LOW (1 << 1)
40#define APM_BATTERY_FLAG_CRITICAL (1 << 2)
41#define APM_BATTERY_FLAG_CHARGING (1 << 3)
42#define APM_BATTERY_FLAG_NOT_PRESENT (1 << 7)
43#define APM_BATTERY_FLAG_UNKNOWN 0xff
44
45 int battery_life;
46 int time;
47 int units;
48#define APM_UNITS_MINS 0
49#define APM_UNITS_SECS 1
50#define APM_UNITS_UNKNOWN -1
51
52};
53
54/*
55 * This allows machines to provide their own "apm get power status" function.
56 */
57extern void (*apm_get_power_status)(struct apm_power_info *);
58
59/*
60 * Queue an event (APM_SYS_SUSPEND or APM_CRITICAL_SUSPEND)
61 */
62void apm_queue_event(apm_event_t event);
63
64#endif
diff --git a/include/asm-sh/apm.h b/include/asm-sh/apm.h
deleted file mode 100644
index 8b091e93651f..000000000000
--- a/include/asm-sh/apm.h
+++ /dev/null
@@ -1,46 +0,0 @@
1/*
2 * Copyright 2006 (c) Andriy Skulysh <askulysh@gmail.com>
3 *
4 * This file is subject to the terms and conditions of the GNU General Public
5 * License. See the file "COPYING" in the main directory of this archive
6 * for more details.
7 *
8 */
9
10#ifndef __ASM_SH_APM_H
11#define __ASM_SH_APM_H
12
13#define APM_AC_OFFLINE 0
14#define APM_AC_ONLINE 1
15#define APM_AC_BACKUP 2
16#define APM_AC_UNKNOWN 0xff
17
18#define APM_BATTERY_STATUS_HIGH 0
19#define APM_BATTERY_STATUS_LOW 1
20#define APM_BATTERY_STATUS_CRITICAL 2
21#define APM_BATTERY_STATUS_CHARGING 3
22#define APM_BATTERY_STATUS_NOT_PRESENT 4
23#define APM_BATTERY_STATUS_UNKNOWN 0xff
24
25#define APM_BATTERY_LIFE_UNKNOWN 0xFFFF
26#define APM_BATTERY_LIFE_MINUTES 0x8000
27#define APM_BATTERY_LIFE_VALUE_MASK 0x7FFF
28
29#define APM_BATTERY_FLAG_HIGH (1 << 0)
30#define APM_BATTERY_FLAG_LOW (1 << 1)
31#define APM_BATTERY_FLAG_CRITICAL (1 << 2)
32#define APM_BATTERY_FLAG_CHARGING (1 << 3)
33#define APM_BATTERY_FLAG_NOT_PRESENT (1 << 7)
34#define APM_BATTERY_FLAG_UNKNOWN 0xff
35
36#define APM_UNITS_MINS 0
37#define APM_UNITS_SECS 1
38#define APM_UNITS_UNKNOWN -1
39
40
41extern int (*apm_get_info)(char *buf, char **start, off_t fpos, int length);
42extern int apm_suspended;
43
44void apm_queue_event(apm_event_t event);
45
46#endif
diff --git a/include/asm-mips/apm.h b/include/linux/apm-emulation.h
index 4b99ffc11529..e6d800358dd6 100644
--- a/include/asm-mips/apm.h
+++ b/include/linux/apm-emulation.h
@@ -7,11 +7,9 @@
7 * based on arch/arm/kernel/apm.c 7 * based on arch/arm/kernel/apm.c
8 * factor out the information needed by architectures to provide 8 * factor out the information needed by architectures to provide
9 * apm status 9 * apm status
10 *
11 *
12 */ 10 */
13#ifndef MIPS_ASM_SA1100_APM_H 11#ifndef __LINUX_APM_EMULATION_H
14#define MIPS_ASM_SA1100_APM_H 12#define __LINUX_APM_EMULATION_H
15 13
16#include <linux/apm_bios.h> 14#include <linux/apm_bios.h>
17 15
@@ -61,4 +59,4 @@ extern void (*apm_get_power_status)(struct apm_power_info *);
61 */ 59 */
62void apm_queue_event(apm_event_t event); 60void apm_queue_event(apm_event_t event);
63 61
64#endif 62#endif /* __LINUX_APM_EMULATION_H */
diff --git a/kernel/power/Kconfig b/kernel/power/Kconfig
index ed296225dcd4..95f6657fff73 100644
--- a/kernel/power/Kconfig
+++ b/kernel/power/Kconfig
@@ -131,3 +131,29 @@ config SUSPEND_SMP
131 bool 131 bool
132 depends on HOTPLUG_CPU && X86 && PM 132 depends on HOTPLUG_CPU && X86 && PM
133 default y 133 default y
134
135config APM_EMULATION
136 tristate "Advanced Power Management Emulation"
137 depends on PM && SYS_SUPPORTS_APM_EMULATION
138 help
139 APM is a BIOS specification for saving power using several different
140 techniques. This is mostly useful for battery powered laptops with
141 APM compliant BIOSes. If you say Y here, the system time will be
142 reset after a RESUME operation, the /proc/apm device will provide
143 battery status information, and user-space programs will receive
144 notification of APM "events" (e.g. battery status change).
145
146 In order to use APM, you will need supporting software. For location
147 and more information, read <file:Documentation/pm.txt> and the
148 Battery Powered Linux mini-HOWTO, available from
149 <http://www.tldp.org/docs.html#howto>.
150
151 This driver does not spin down disk drives (see the hdparm(8)
152 manpage ("man 8 hdparm") for that), and it doesn't turn off
153 VESA-compliant "green" monitors.
154
155 Generally, if you don't have a battery in your machine, there isn't
156 much point in using this driver and you should say N. If you get
157 random kernel OOPSes or reboots that don't seem to be related to
158 anything, try disabling/enabling this option (or disabling/enabling
159 APM in your BIOS).