#
# Patch managed by http://www.holgerschurig.de/patcher.html
#

--- linux-2.4.17_mvl21/arch/arm/mach-sa1100/apm.c~apm-hh-merge
+++ linux-2.4.17_mvl21/arch/arm/mach-sa1100/apm.c
@@ -86,6 +86,8 @@
 	int		magic;
 	struct apm_user *	next;
 	int		suser: 1;
+	int             writer : 1;
+	int             reader : 1;
 	int		suspend_wait: 1;
 	int		suspend_result;
 	int		suspends_pending;
@@ -105,7 +107,7 @@
 /*
  * Local variables
  */
-//static int			suspends_pending;
+static int			suspends_pending;
 //static int			standbys_pending;
 //static int			ignore_normal_resume;
 
@@ -123,8 +125,6 @@
 #else
 static int			power_off = 1;
 #endif
-static int			exit_kapmd;
-static int			kapmd_running;
 
 static DECLARE_WAIT_QUEUE_HEAD(apm_waitqueue);
 static DECLARE_WAIT_QUEUE_HEAD(apm_suspend_waitqueue);
@@ -192,6 +192,41 @@
 	return as->events[as->event_tail];
 }
 
+static void queue_event(apm_event_t event, struct apm_user *sender)
+{
+	struct apm_user *	as;
+	if (user_list == NULL)
+		return;
+	for (as = user_list; as != NULL; as = as->next) {
+		if ((as == sender) || (!as->reader))
+			continue;
+		as->event_head = (as->event_head + 1) % APM_MAX_EVENTS;
+		if (as->event_head == as->event_tail) {
+			static int notified;
+
+			if (notified++ == 0)
+				printk(KERN_ERR "apm: an event queue overflowed\n");
+			as->event_tail = (as->event_tail + 1) % APM_MAX_EVENTS;
+		}
+		as->events[as->event_head] = event;
+		if ((!as->suser) || (!as->writer))
+			continue;
+		switch (event) {
+		case APM_SYS_SUSPEND:
+		case APM_USER_SUSPEND:
+			as->suspends_pending++;
+			suspends_pending++;
+			break;
+
+		case APM_SYS_STANDBY:
+		case APM_USER_STANDBY:
+			as->standbys_pending++;
+			break;
+		}
+	}
+	wake_up_interruptible(&apm_waitqueue);
+}
+
 static int check_apm_user(struct apm_user *as, const char *func)
 {
 	if ((as == NULL) || (as->magic != APM_BIOS_MAGIC)) {
@@ -270,7 +305,6 @@
 	return 0;
 }
 
-extern int pm_do_suspend(void);
 
 static int do_ioctl(struct inode * inode, struct file *filp,
 		    u_int cmd, u_long arg)
@@ -284,7 +318,17 @@
 		return -EPERM;
 	switch (cmd) {
         case APM_IOC_SUSPEND:
-		pm_do_suspend();
+		if (as->suspends_read > 0) {
+			as->suspends_read--;
+			as->suspends_pending--;
+			suspends_pending--;
+		} else {
+			queue_event(APM_USER_SUSPEND, as);
+		}
+
+		if (suspends_pending <= 0)
+			wake_up(&apm_suspend_waitqueue);
+
 		break;
 	default:
 		return -EINVAL;
@@ -301,6 +345,20 @@
 		return 0;
 	filp->private_data = NULL;
 	lock_kernel();
+	if (user_list == as)
+		user_list = as->next;
+	else {
+		struct apm_user *	as1;
+
+		for (as1 = user_list;
+		     (as1 != NULL) && (as1->next != as);
+		     as1 = as1->next)
+			;
+		if (as1 == NULL)
+			printk(KERN_ERR "apm: filp not in user list\n");
+		else
+			as1->next = as->next;
+	}
 	unlock_kernel();
 	kfree(as);
 	return 0;
@@ -328,6 +386,8 @@
 	 * privileged operation -- cevans
 	 */
 	as->suser = capable(CAP_SYS_ADMIN);
+	as->writer = (filp->f_mode & FMODE_WRITE) == FMODE_WRITE;
+	as->reader = (filp->f_mode & FMODE_READ) == FMODE_READ;
 	as->next = user_list;
 	user_list = as;
 	filp->private_data = as;
@@ -411,33 +471,7 @@
 	return p - buf;
 }
 
-#ifndef MODULE
-static int __init apm_setup(char *str)
-{
-	int	invert;
-
-	while ((str != NULL) && (*str != '\0')) {
-		if (strncmp(str, "off", 3) == 0)
-			apm_disabled = 1;
-		if (strncmp(str, "on", 2) == 0)
-			apm_disabled = 0;
-		invert = (strncmp(str, "no-", 3) == 0);
-		if (invert)
-			str += 3;
-		if (strncmp(str, "debug", 5) == 0)
-			debug = !invert;
-		if ((strncmp(str, "power-off", 9) == 0) ||
-		    (strncmp(str, "power_off", 9) == 0))
-			power_off = !invert;
-		str = strchr(str, ',');
-		if (str != NULL)
-			str += strspn(str, ", \t");
-	}
-	return 1;
-}
 
-__setup("apm=", apm_setup);
-#endif
 
 static struct file_operations apm_bios_fops = {
 	owner:		THIS_MODULE,
@@ -449,13 +483,55 @@
 };
 
 static struct miscdevice apm_device = {
-	APM_MINOR_DEV,
-	"apm_bios",
-	&apm_bios_fops
+	minor : APM_MINOR_DEV,
+	name  : "apm_bios",
+	fops  : &apm_bios_fops
 };
 
 #define APM_INIT_ERROR_RETURN	return -1
 
+static pid_t apmd_pid;
+static DECLARE_COMPLETION(apmd_exited);
+
+static int apm(void *unused)
+{
+	unsigned short	bx;
+	unsigned short	cx;
+	unsigned short	dx;
+	int		error;
+	char *		power_stat;
+	char *		bat_stat;
+	DECLARE_WAITQUEUE(wait, current);
+	struct apm_user au, *as;
+
+	lock_kernel();
+
+	daemonize();
+
+	strcpy(current->comm, "kapmd");
+
+	as = &au;
+	as->magic = APM_BIOS_MAGIC;
+	as->event_tail = as->event_head = 0;
+	as->suspends_pending = as->standbys_pending = 0;
+	as->suspends_read = as->standbys_read = 0;
+	as->suser = 1;
+	as->writer = 1;
+	as->reader = 0;
+
+	while (!signal_pending (current)) {
+		interruptible_sleep_on(&apm_suspend_waitqueue);
+
+		pm_suggest_suspend();
+
+		queue_event(APM_NORMAL_RESUME, as);
+	}
+
+	unlock_kernel();
+
+	complete_and_exit(&apmd_exited, 0);
+}
+
 /*
  * Just start the APM thread. We do NOT want to do APM BIOS
  * calls from anything but the APM thread, if for no other reason
@@ -494,18 +570,19 @@
 
 	misc_register(&apm_device);
 
+	apmd_pid = kernel_thread(apm, NULL, 0);
+
 	return 0;
 }
 
 static void __exit apm_exit(void)
 {
 	misc_deregister(&apm_device);
-	remove_proc_entry("apm", NULL);
+	remove_proc_entry("apm", NULL);	
+	kill_proc (apmd_pid, SIGTERM, 1);
+	wait_for_completion(&apmd_exited);
 	if (power_off)
 		pm_power_off = NULL;
-	exit_kapmd = 1;
-	while (kapmd_running)
-		schedule();
 	pm_active = 0;
 }
 
@@ -514,6 +591,7 @@
 
 MODULE_AUTHOR("Jamey Hicks, pulling bits from original by Stephen Rothwell");
 MODULE_DESCRIPTION("A minimal emulation of APM");
+MODULE_LICENSE("GPL");
 MODULE_PARM(debug, "i");
 MODULE_PARM_DESC(debug, "Enable debug mode");
 MODULE_PARM(power_off, "i");
--- linux-2.4.17_mvl21/arch/arm/mach-sa1100/pm.c~apm-hh-merge
+++ linux-2.4.17_mvl21/arch/arm/mach-sa1100/pm.c
@@ -53,6 +53,10 @@
 #include <asm/arch/assabet.h>
 #endif
 
+#define __KERNEL_SYSCALLS__
+#include <linux/unistd.h>
+
+
 /*
  * ARGH!  Stupid ACPI people.  They should define this in linux/sysctl.h,
  * NOT linux/acpi.h.
@@ -64,123 +68,6 @@
 #define CTL_ACPI 9999
 #define ACPI_S1_SLP_TYP 19
 
-#ifndef CONFIG_SA1100_BEAGLE
-
-extern void sa1100_cpu_suspend(void);
-extern void sa1100_cpu_resume(void);
-
-extern unsigned long *sleep_save;	/* virtual address */
-extern unsigned long  sleep_save_p;	/* physical address */
-
-#define SAVE(x)		sleep_save[SLEEP_SAVE_##x] = x
-#define RESTORE(x)	x = sleep_save[SLEEP_SAVE_##x]
-
-int sa1110_suspend(void)
-{
-	int retval;
-
-	/* set up pointer to sleep parameters */
-	sleep_save = kmalloc (SLEEP_SAVE_SIZE*sizeof(long), GFP_ATOMIC);
-	if (!sleep_save)
-		return -ENOMEM;
-	sleep_save_p = virt_to_phys(sleep_save);
-
-	retval = pm_send_all(PM_SUSPEND, (void *)2);
-	if (retval) {
-		kfree(sleep_save);
-		return retval;
-	}
-
-	cli();
-
-	/* preserve current time */
-	RCNR = xtime.tv_sec;
-
-	/* save vital registers */
-	SAVE(OSCR);
-	SAVE(OSMR0);
-	SAVE(OSMR1);
-	SAVE(OSMR2);
-	SAVE(OSMR3);
-	SAVE(OIER);
-
-	SAVE(GPDR);
-	SAVE(GRER);
-	SAVE(GFER);
-	SAVE(GAFR);
-
-	SAVE(PPDR);
-	SAVE(PPSR);
-	SAVE(PPAR);
-	SAVE(PSDR);
-
-	SAVE(Ser1SDCR0);
-
-	SAVE(ICMR);
-
-	/* ... maybe a global variable initialized by arch code to set this? */
-	GRER = PWER;
-	GFER = 0;
-	GEDR = GEDR;
-
-	/* Clear previous reset status */
-	RCSR = RCSR_HWR | RCSR_SWR | RCSR_WDR | RCSR_SMR;
-
-	/* set resume return address */
-	PSPR = virt_to_phys(sa1100_cpu_resume);
-
-	/* go zzz */
-	sa1100_cpu_suspend();
-
-	/* ensure not to come back here if it wasn't intended */
-	PSPR = 0;
-
-	DPRINTK("*** made it back from resume\n");
-
-	/* restore registers */
-	RESTORE(GPDR);
-	RESTORE(GRER);
-	RESTORE(GFER);
-	RESTORE(GAFR);
-
-	/* clear any edge detect bit */
-	GEDR = GEDR;
-
-	RESTORE(PPDR);
-	RESTORE(PPSR);
-	RESTORE(PPAR);
-	RESTORE(PSDR);
-
-	RESTORE(Ser1SDCR0);
-
-	PSSR = PSSR_PH;
-
-	RESTORE(OSMR0);
-	RESTORE(OSMR1);
-	RESTORE(OSMR2);
-	RESTORE(OSMR3);
-	RESTORE(OSCR);
-	RESTORE(OIER);
-
-	ICLR = 0;
-	ICCR = 1;
-	RESTORE(ICMR);
-
-	/* restore current time */
-	xtime.tv_sec = RCNR;
-
-	sti();
-
-	kfree (sleep_save);
-
-#ifdef CONFIG_CPU_FREQ
-	cpufreq_restore();
-#endif
-
-	return pm_send_all(PM_RESUME, (void *)0);
-}
-
-#else //CONFIG_SA1100_BEAGLE
 
 typedef struct _tag_SLEEP_SAVED_DATA {
 	uint wakeup_addr;
@@ -363,9 +250,6 @@
 	" );
 }
 
-extern void h3600_control_egpio( enum ipaq_egpio_type x, int setp );
-extern unsigned long h3600_read_egpio( void );
-
 static int GPDR_saved;
 static int GPLR_saved;
 static int GRER_saved;
@@ -742,21 +626,37 @@
 	Ser3UTSR1 = 0xff;
 }
 
-#endif //CONFIG_SA1100_BEAGLE
-
+/*
+ * If pm_suggest_suspend_hook is non-NULL, it is called by pm_suggest_suspend.
+ *
+ * If sysctl_pm_do_suspend_hook is non-NULL, it is called by sysctl_pm_do_suspend.
+ * If it returns a true value, then pm_suspend is not called. 
+ * Use this to hook in apmd, for now.
+ *
+ * -not exported just so that the code compiles
+ */
+int (*pm_suggest_suspend_hook)(int state);
+int (*pm_sysctl_suspend_hook)(int state);
+int pm_use_sbin_pm_helper = 1;
 static char pm_helper_path[128] = "/sbin/pm_helper";
+extern int exec_usermodehelper(char *path, char **argv, char **envp);
+int debug_pm = 0;
+static int pm_helper_veto = 0;
 
-static void
+static int
 run_sbin_pm_helper( pm_request_t action )
 {
 	int i;
 	char *argv[3], *envp[8];
 
 	if (!pm_helper_path[0])
-		return;
+		return 2;
 
 	if ( action != PM_SUSPEND && action != PM_RESUME )
-		return;
+		return 1;
+
+	/* Be root */
+	current->uid = current->gid = 0;
 
 	i = 0;
 	argv[i++] = pm_helper_path;
@@ -771,14 +671,15 @@
 	envp[i] = 0;
 
 	/* other stuff we want to pass to /sbin/hotplug */
-	call_usermodehelper (argv [0], argv, envp);
+	return exec_usermodehelper (argv [0], argv, envp);
 }
 
+int pm_force_suspend(void);
+
 int pm_do_suspend(void)
 {
-	DPRINTK("suggest\n");
-	run_sbin_pm_helper(PM_SUSPEND);
-	return 0;
+	DPRINTK("suspend now\n");
+	return pm_force_suspend();
 }
 
 #ifdef CONFIG_SA1100_BEAGLE
@@ -863,9 +764,91 @@
 }
 #endif
 
+int pm_suggest_suspend(void)
+{
+	int retval;
+
+	if (pm_suggest_suspend_hook) {
+		if (pm_suggest_suspend_hook(PM_SUSPEND))
+			return 0;
+	}
+
+	if (pm_use_sbin_pm_helper) {
+		pid_t pid;
+		int res;
+		int status = 0;
+		unsigned int old_fs;
+
+		pid = kernel_thread ((int (*) (void *)) run_sbin_pm_helper, (void *) PM_SUSPEND, 0 );
+		if ( pid < 0 )
+			return pid;
+
+		if (debug_pm)
+			printk(KERN_CRIT "%s:%d got pid=%d\n", __FUNCTION__, __LINE__, pid);	
+
+		old_fs = get_fs ();
+		set_fs (get_ds ());
+		res = waitpid(pid, &status, __WCLONE);
+		set_fs (old_fs);
+
+		if ( pid != res ) {
+			if (debug_pm)
+				printk(KERN_CRIT ": waitpid returned %d (exit_code=%d); not suspending\n", res, status );
+
+			return -1;
+		}
+
+		/*if ( WIFEXITED(status) && ( WIFEXITSTATUS(status) != 0 )) {*/
+		if (( status & 0xff7f ) != 0 ) {
+			if (pm_helper_veto) {
+				if (debug_pm)
+					printk(KERN_CRIT "%s: SUSPEND WAS CANCELLED BY pm_helper (exit status %d)\n", __FUNCTION__, status >> 8);
+				return -1;
+			} else {
+				if (debug_pm)
+					printk(KERN_CRIT "%s: pm_helper returned %d, but going ahead anyway\n", __FUNCTION__, status >> 8);
+			}
+		}
+	}
+
+	if (debug_pm)
+		printk(KERN_CRIT "%s: REALLY SUSPENDING NOW\n", __FUNCTION__ );
+
+	if (pm_sysctl_suspend_hook) {
+		if (pm_sysctl_suspend_hook(PM_SUSPEND))
+			return 0;
+	}
+
+	retval = pm_do_suspend();
+	if (retval) {
+		if (debug_pm)
+			printk(KERN_CRIT "pm_suspend returned %d\n", retval);
+		return retval;
+	}
+
+	if (pm_use_sbin_pm_helper) {
+		pid_t pid;
+        	
+		if (debug_pm)
+			printk(KERN_CRIT "%s: running pm_helper for wakeup\n", __FUNCTION__);
+
+		pid = kernel_thread ((int (*) (void *)) run_sbin_pm_helper, (void *) PM_RESUME, 0 );
+		if ( pid < 0 )
+			return pid;
+        		
+		if ( pid != waitpid ( pid, NULL, __WCLONE ))
+			return -1;
+	}
+
+	return 0;
+}
+
+EXPORT_SYMBOL(pm_suggest_suspend);
+
+
 static struct ctl_table pm_table[] =
 {
-	{ACPI_S1_SLP_TYP, "suspend", NULL, 0, 0644, NULL, (proc_handler *)&pm_force_suspend},
+/*	{ACPI_S1_SLP_TYP, "suspend", NULL, 0, 0644, NULL, (proc_handler *)&pm_force_suspend}, */
 	{2, "helper", pm_helper_path, sizeof(pm_helper_path), 0644, NULL, (proc_handler *)&proc_dostring},
 #ifdef CONFIG_SA1100_BEAGLE
 	{3, "wakeup_delayed_time", &wakeup_delayed_time, sizeof(wakeup_delayed_time), 0644, NULL, &proc_dointvec },