3 # Patch managed by http://www.holgerschurig.de/patcher.html
7 +++ linux-2.4.27/arch/arm/mach-sa1100/pm-common.c
10 + * SA1100 Power Management Routines
12 + * Copyright (c) 2001 Cliff Brake <cbrake@accelent.com>
14 + * This program is free software; you can redistribute it and/or
15 + * modify it under the terms of the GNU General Public License.
19 + * 2001-02-06: Cliff Brake Initial code
21 + * 2001-02-25: Sukjae Cho <sjcho@east.isi.edu> &
22 + * Chester Kuo <chester@linux.org.tw>
23 + * Save more value for the resume function! Support
24 + * Bitsy/Assabet/Freebird board
26 + * 2001-08-29: Nicolas Pitre <nico@cam.org>
27 + * Cleaned up, pushed platform dependent stuff
28 + * in the platform specific files.
30 + * 2002-05-27: Nicolas Pitre Killed sleep.h and the kmalloced save array.
31 + * Storage is local on the stack now.
33 +#include <linux/config.h>
34 +#include <linux/module.h>
35 +#include <linux/init.h>
36 +#include <linux/pm.h>
37 +#include <linux/slab.h>
38 +#include <linux/sched.h>
39 +#include <linux/interrupt.h>
40 +#include <linux/sysctl.h>
41 +#include <linux/errno.h>
42 +#include <linux/cpufreq.h>
44 +#include <asm/hardware.h>
45 +#include <asm/memory.h>
46 +#include <asm/system.h>
47 +#include <asm/leds.h>
48 +#include <asm/uaccess.h>
51 +#ifdef CONFIG_IPAQ_HANDHELD
52 +#include <asm/arch-sa1100/h3600_asic.h>
55 +#define __KERNEL_SYSCALLS__
56 +#include <linux/unistd.h>
65 +static char pm_helper_path[128] = "/sbin/pm_helper";
66 +extern int exec_usermodehelper(char *path, char **argv, char **envp);
68 +static int pm_helper_veto = 0;
71 +run_sbin_pm_helper( pm_request_t action )
74 + char *argv[3], *envp[8];
76 + if (!pm_helper_path[0])
79 + if ( action != PM_SUSPEND && action != PM_RESUME )
83 + current->uid = current->gid = 0;
86 + argv[i++] = pm_helper_path;
87 + argv[i++] = (action == PM_RESUME ? "resume" : "suspend");
91 + /* minimal command environment */
92 + envp[i++] = "HOME=/";
93 + envp[i++] = "PATH=/sbin:/bin:/usr/sbin:/usr/bin";
96 + /* other stuff we want to pass to /sbin/pm_helper */
97 + return exec_usermodehelper (argv [0], argv, envp);
101 + * If pm_suggest_suspend_hook is non-NULL, it is called by pm_suggest_suspend.
103 +int (*pm_suggest_suspend_hook)(int state);
104 +EXPORT_SYMBOL(pm_suggest_suspend_hook);
107 + * If pm_use_sbin_pm_helper is nonzero, then run_sbin_pm_helper is called before suspend and after resume
109 +int pm_use_sbin_pm_helper = 1;
110 +EXPORT_SYMBOL(pm_use_sbin_pm_helper);
113 + * If sysctl_pm_do_suspend_hook is non-NULL, it is called by sysctl_pm_do_suspend.
114 + * If it returns a true value, then pm_suspend is not called.
115 + * Use this to hook in apmd, for now.
117 +int (*pm_sysctl_suspend_hook)(int state);
118 +EXPORT_SYMBOL(pm_sysctl_suspend_hook);
120 +int pm_suspend(void);
122 +int pm_suggest_suspend(void)
126 + if (pm_suggest_suspend_hook) {
127 + if (pm_suggest_suspend_hook(PM_SUSPEND))
131 + if (pm_use_sbin_pm_helper) {
135 + unsigned int old_fs;
137 + pid = kernel_thread ((int (*) (void *)) run_sbin_pm_helper, (void *) PM_SUSPEND, 0 );
142 + printk(KERN_CRIT "%s:%d got pid=%d\n", __FUNCTION__, __LINE__, pid);
144 + old_fs = get_fs ();
145 + set_fs (get_ds ());
146 + res = waitpid(pid, &status, __WCLONE);
149 + if ( pid != res ) {
151 + printk(KERN_CRIT ": waitpid returned %d (exit_code=%d); not suspending\n", res, status );
156 + /*if ( WIFEXITED(status) && ( WIFEXITSTATUS(status) != 0 )) {*/
157 + if (( status & 0xff7f ) != 0 ) {
158 + if (pm_helper_veto) {
160 + printk(KERN_CRIT "%s: SUSPEND WAS CANCELLED BY pm_helper (exit status %d)\n", __FUNCTION__, status >> 8);
164 + printk(KERN_CRIT "%s: pm_helper returned %d, but going ahead anyway\n", __FUNCTION__, status >> 8);
170 + printk(KERN_CRIT "%s: REALLY SUSPENDING NOW\n", __FUNCTION__ );
172 + if (pm_sysctl_suspend_hook) {
173 + if (pm_sysctl_suspend_hook(PM_SUSPEND))
177 + retval = pm_suspend();
180 + printk(KERN_CRIT "pm_suspend returned %d\n", retval);
184 + if (pm_use_sbin_pm_helper) {
188 + printk(KERN_CRIT "%s: running pm_helper for wakeup\n", __FUNCTION__);
190 + pid = kernel_thread ((int (*) (void *)) run_sbin_pm_helper, (void *) PM_RESUME, 0 );
194 + if ( pid != waitpid ( pid, NULL, __WCLONE ))
201 +EXPORT_SYMBOL(pm_suggest_suspend);
205 + * Send us to sleep.
207 +int pm_suspend(void)
211 + retval = pm_send_all(PM_SUSPEND, (void *)3);
215 +#ifdef CONFIG_IPAQ_HANDHELD
216 + retval = h3600_power_management(PM_SUSPEND);
218 + pm_send_all(PM_RESUME, (void *)0);
223 + retval = pm_do_suspend();
225 +#ifdef CONFIG_IPAQ_HANDHELD
226 + /* Allow the power management routines to override resuming */
227 + while ( h3600_power_management(PM_RESUME) )
228 + retval = pm_do_suspend();
231 + pm_send_all(PM_RESUME, (void *)0);
235 +EXPORT_SYMBOL(pm_suspend);
237 +#ifdef CONFIG_SYSCTL
239 + * ARGH! ACPI people defined CTL_ACPI in linux/acpi.h rather than
242 + * This means our interface here won't survive long - it needs a new
243 + * interface. Quick hack to get this working - use sysctl id 9999.
245 +#warning ACPI broke the kernel, this interface needs to be fixed up.
246 +#define CTL_ACPI 9999
247 +#define ACPI_S1_SLP_TYP 19
249 +static struct ctl_table pm_table[] =
251 +/* {ACPI_S1_SLP_TYP, "suspend", NULL, 0, 0600, NULL, (proc_handler *)&sysctl_pm_suspend},*/
252 + {2, "helper", pm_helper_path, sizeof(pm_helper_path), 0644, NULL, (proc_handler *)&proc_dostring},
253 + {3, "debug", &debug_pm, sizeof(debug_pm), 0644, NULL, (proc_handler *)&proc_dointvec},
254 + {4, "helper_veto", &pm_helper_veto, sizeof(pm_helper_veto), 0644, NULL, (proc_handler *)&proc_dointvec},
258 +static struct ctl_table pm_dir_table[] =
260 + {CTL_ACPI, "pm", NULL, 0, 0555, pm_table},
265 + * Initialize power interface
267 +static int __init pm_init(void)
269 + register_sysctl_table(pm_dir_table, 1);
273 +__initcall(pm_init);
277 --- linux-2.4.27/arch/arm/mach-sa1100/apm.c~simpad-apm
278 +++ linux-2.4.27/arch/arm/mach-sa1100/apm.c
281 #include <asm/system.h>
282 #include <asm/hardware.h>
284 #include <asm/arch-sa1100/pm.h>
287 #ifdef CONFIG_IPAQ_HANDHELD
288 #include <asm/arch-sa1100/h3600_hal.h>
291 struct apm_user * next;
297 int suspends_pending;
302 -//static int suspends_pending;
303 +static int suspends_pending;
304 //static int standbys_pending;
305 //static int ignore_normal_resume;
309 static int power_off = 1;
311 -static int exit_kapmd;
312 -static int kapmd_running;
314 static DECLARE_WAIT_QUEUE_HEAD(apm_waitqueue);
315 static DECLARE_WAIT_QUEUE_HEAD(apm_suspend_waitqueue);
317 return as->events[as->event_tail];
320 +static void queue_event(apm_event_t event, struct apm_user *sender)
322 + struct apm_user * as;
324 + if (user_list == NULL)
326 + for (as = user_list; as != NULL; as = as->next) {
327 + if ((as == sender) || (!as->reader))
329 + as->event_head = (as->event_head + 1) % APM_MAX_EVENTS;
330 + if (as->event_head == as->event_tail) {
331 + static int notified;
333 + if (notified++ == 0)
334 + printk(KERN_ERR "apm: an event queue overflowed\n");
335 + as->event_tail = (as->event_tail + 1) % APM_MAX_EVENTS;
337 + as->events[as->event_head] = event;
338 + if ((!as->suser) || (!as->writer))
341 + case APM_SYS_SUSPEND:
342 + case APM_USER_SUSPEND:
343 + as->suspends_pending++;
344 + suspends_pending++;
347 + case APM_SYS_STANDBY:
348 + case APM_USER_STANDBY:
349 + as->standbys_pending++;
353 + wake_up_interruptible(&apm_waitqueue);
356 static int check_apm_user(struct apm_user *as, const char *func)
358 if ((as == NULL) || (as->magic != APM_BIOS_MAGIC)) {
361 while ((i >= sizeof(event)) && !queue_empty(as)) {
362 event = get_queued_event(as);
363 - printk(" do_read: event=%d\n", event);
364 if (copy_to_user(buf, &event, sizeof(event))) {
370 case APM_IOC_SUSPEND:
372 - pm_suggest_suspend();
374 + if (as->suspends_read > 0) {
375 + as->suspends_read--;
376 + as->suspends_pending--;
377 + suspends_pending--;
379 + queue_event(APM_USER_SUSPEND, as);
382 + if (suspends_pending <= 0)
383 + wake_up(&apm_suspend_waitqueue);
390 filp->private_data = NULL;
392 + if (user_list == as)
393 + user_list = as->next;
395 + struct apm_user * as1;
397 + for (as1 = user_list;
398 + (as1 != NULL) && (as1->next != as);
402 + printk(KERN_ERR "apm: filp not in user list\n");
404 + as1->next = as->next;
410 * privileged operation -- cevans
412 as->suser = capable(CAP_SYS_ADMIN);
413 + as->writer = (filp->f_mode & FMODE_WRITE) == FMODE_WRITE;
414 + as->reader = (filp->f_mode & FMODE_READ) == FMODE_READ;
415 as->next = user_list;
417 filp->private_data = as;
423 -static int __init apm_setup(char *str)
427 - while ((str != NULL) && (*str != '\0')) {
428 - if (strncmp(str, "off", 3) == 0)
430 - if (strncmp(str, "on", 2) == 0)
432 - invert = (strncmp(str, "no-", 3) == 0);
435 - if (strncmp(str, "debug", 5) == 0)
437 - if ((strncmp(str, "power-off", 9) == 0) ||
438 - (strncmp(str, "power_off", 9) == 0))
439 - power_off = !invert;
440 - str = strchr(str, ',');
442 - str += strspn(str, ", \t");
447 -__setup("apm=", apm_setup);
450 static struct file_operations apm_bios_fops = {
455 #define APM_INIT_ERROR_RETURN return -1
457 +static pid_t apmd_pid;
458 +static DECLARE_COMPLETION(apmd_exited);
460 +static int apm(void *unused)
468 + DECLARE_WAITQUEUE(wait, current);
469 + struct apm_user au, *as;
475 + strcpy(current->comm, "kapmd");
478 + as->magic = APM_BIOS_MAGIC;
479 + as->event_tail = as->event_head = 0;
480 + as->suspends_pending = as->standbys_pending = 0;
481 + as->suspends_read = as->standbys_read = 0;
487 + interruptible_sleep_on(&apm_suspend_waitqueue);
488 + if (signal_pending (current))
491 + pm_suggest_suspend();
493 + queue_event(APM_NORMAL_RESUME, as);
498 + complete_and_exit(&apmd_exited, 0);
502 * Just start the APM thread. We do NOT want to do APM BIOS
503 * calls from anything but the APM thread, if for no other reason
506 misc_register(&apm_device);
508 + apmd_pid = kernel_thread(apm, NULL, 0);
513 @@ -499,11 +572,10 @@
515 misc_deregister(&apm_device);
516 remove_proc_entry("apm", NULL);
517 + kill_proc (apmd_pid, SIGTERM, 1);
518 + wait_for_completion(&apmd_exited);
522 - while (kapmd_running)
529 MODULE_AUTHOR("Jamey Hicks, pulling bits from original by Stephen Rothwell");
530 MODULE_DESCRIPTION("A minimal emulation of APM");
531 +MODULE_LICENSE("GPL");
532 MODULE_PARM(debug, "i");
533 MODULE_PARM_DESC(debug, "Enable debug mode");
534 MODULE_PARM(power_off, "i");
536 +++ linux-2.4.27/include/asm-arm/arch-sa1100/pm.h
540 + * Declarations for ARM Linux Power Management
542 + * Copyright 2002 Compaq Computer Corporation.
544 + * This program is free software; you can redistribute it and/or modify
545 + * it under the terms of the GNU General Public License version 2 as
546 + * published by the Free Software Foundation.
548 + * Author: Jamey Hicks.
553 +extern int (*pm_suggest_suspend_hook)(int state);
554 +extern int (*pm_sysctl_suspend_hook)(int state);
555 +extern int pm_use_sbin_pm_helper;
556 +extern int pm_suspend(void);
557 +extern int pm_suggest_suspend(void); /* triggers /sbin/pm_helper or queueing event to apmd */
558 --- linux-2.4.27/arch/arm/mach-sa1100/Makefile~simpad-apm
559 +++ linux-2.4.27/arch/arm/mach-sa1100/Makefile
561 flexanet.o freebird.o frodo.o generic.o h3600.o \
562 huw_webpanel.o irq.o sa1111.o sa1111-pcibuf.o \
563 system3.o yopy.o usb_ctl.o usb_recv.o usb_send.o simputer.o ssp.o \
565 + simpad.o pm-sa1100.o
567 # These aren't present yet, and prevents a plain -ac kernel building.
570 obj-$(CONFIG_SA1100_USB_CHAR) += usb-char.o
572 # Miscelaneous functions
573 -obj-$(CONFIG_PM) += pm.o sleep.o
574 +obj-$(CONFIG_PM) += pm-sa1100.o sleep.o
575 obj-$(CONFIG_APM) += apm.o
579 +++ linux-2.4.27/arch/arm/mach-sa1100/pm-sa1100.c
582 + * SA1100 Power Management Routines
584 + * Copyright (c) 2001 Cliff Brake <cbrake@accelent.com>
586 + * This program is free software; you can redistribute it and/or
587 + * modify it under the terms of the GNU General Public License.
591 + * 2001-02-06: Cliff Brake Initial code
593 + * 2001-02-25: Sukjae Cho <sjcho@east.isi.edu> &
594 + * Chester Kuo <chester@linux.org.tw>
595 + * Save more value for the resume function! Support
596 + * Bitsy/Assabet/Freebird board
598 + * 2001-08-29: Nicolas Pitre <nico@cam.org>
599 + * Cleaned up, pushed platform dependent stuff
600 + * in the platform specific files.
602 + * 2002-05-27: Nicolas Pitre Killed sleep.h and the kmalloced save array.
603 + * Storage is local on the stack now.
605 +#include <linux/config.h>
606 +#include <linux/module.h>
607 +#include <linux/init.h>
608 +#include <linux/pm.h>
609 +#include <linux/slab.h>
610 +#include <linux/sched.h>
611 +#include <linux/interrupt.h>
612 +#include <linux/sysctl.h>
613 +#include <linux/errno.h>
614 +#include <linux/cpufreq.h>
616 +#include <asm/hardware.h>
617 +#include <asm/memory.h>
618 +#include <asm/system.h>
619 +#include <asm/leds.h>
622 +#ifdef CONFIG_IPAQ_HANDHELD
623 +#include <asm/arch/h3600_asic.h>
626 +#define __KERNEL_SYSCALLS__
627 +#include <linux/unistd.h>
629 +extern void sa1100_cpu_suspend(void);
630 +extern void sa1100_cpu_resume(void);
631 +extern int debug_pm;
633 +#define SAVE(x) sleep_save[SLEEP_SAVE_##x] = x
634 +#define RESTORE(x) x = sleep_save[SLEEP_SAVE_##x]
637 + * List of global SA11x0 peripheral registers to preserve.
638 + * More ones like CP and general purpose register values are preserved
639 + * with the stack location in sleep.S.
641 +enum { SLEEP_SAVE_START = 0,
643 + SLEEP_SAVE_OSCR, SLEEP_SAVE_OIER,
644 + SLEEP_SAVE_OSMR0, SLEEP_SAVE_OSMR1, SLEEP_SAVE_OSMR2, SLEEP_SAVE_OSMR3,
646 + SLEEP_SAVE_GPDR, SLEEP_SAVE_GRER, SLEEP_SAVE_GFER, SLEEP_SAVE_GAFR,
647 + SLEEP_SAVE_PPDR, SLEEP_SAVE_PPSR, SLEEP_SAVE_PPAR, SLEEP_SAVE_PSDR,
650 +#ifdef CONFIG_SA1100_SIMPAD
651 + SLEEP_SAVE_MECR, /* needed by SIMpad to get PCMCIA working after resume */
653 + SLEEP_SAVE_Ser1SDCR0,
656 + SLEEP_SAVE_MSC1, SLEEP_SAVE_MSC2,
662 +int pm_do_suspend(void)
664 + unsigned long sleep_save[SLEEP_SAVE_SIZE];
668 + leds_event(led_stop);
670 + /* preserve current time */
671 + RCNR = xtime.tv_sec;
673 + /* save vital registers */
694 +#ifdef CONFIG_SA1100_SIMPAD
701 + /* ... maybe a global variable initialized by arch code to set this? */
704 + // Ugly, but I need the AC inserted event
705 + // In the future, we're going to care about DCD and USB interrupts as well
706 + if ( machine_is_h3800()) {
707 +#ifdef CONFIG_IPAQ_HANDHELD
708 + GFER = GPIO_H3800_AC_IN;
712 + if (machine_is_jornada56x()) {
720 + /* Clear previous reset status */
721 + RCSR = RCSR_HWR | RCSR_SWR | RCSR_WDR | RCSR_SMR;
723 + /* set resume return address */
724 + PSPR = virt_to_phys(sa1100_cpu_resume);
727 + sa1100_cpu_suspend();
729 + /* ensure not to come back here if it wasn't intended */
733 + printk(KERN_CRIT "*** made it back from resume\n");
735 +#ifdef CONFIG_IPAQ_HANDHELD
736 + if ( machine_is_ipaq()) {
737 + ipaq_model_ops.gedr = GEDR;
738 + ipaq_model_ops.icpr = ICPR;
742 + /* restore registers */
748 + /* clear any edge detect bit */
756 + RESTORE(Ser1SDCR0);
767 +#ifdef CONFIG_IPAQ_HANDHELD
768 +/* OSMR0 may have fired before we went to sleep, but after interrupts
769 + were shut off. Set OSMR0 to something plausible */
770 + OSMR0 = OSCR + LATCH;
775 +#ifdef CONFIG_SA1100_SIMPAD
781 + /* restore current time */
782 + xtime.tv_sec = RCNR;
784 + leds_event(led_start);
789 + printk("interrupts are enabled\n");
792 + * Restore the CPU frequency settings.
794 +#ifdef CONFIG_CPU_FREQ
800 +unsigned long sleep_phys_sp(void *sp)
802 + return virt_to_phys(sp);
805 +#include "pm-common.c"