Skip to contentSkip to conversation

about au_sleep for au1x00

PATCH Rodolfo Giometti 1 participant 1 message Oct 14, 2005 MIPS

lore
Share & tools
lore
#0 lore raw

Hello,

attached is my proposal for a new (and better? :) implementation of au_sleep() function (patch is against vanilla MIPS 2.6.12).

My patch adds two board specific functions whose are called before and after CPU sleeping. This can be useful to setup hardware for sleeping and also to ask to the board which should be the wake up reason (GPIO, TOY).

Patch also fixes data reading from files /proc/sys/pm/{sleep,freq}.

What is still obscure to me is how I can use the au_sleep() in order to ibernate the system... or better... how I can resume form ibarnation. In fact the system reboots correctly but it start from the beginning! What I have to do in order to have the system restart from ibarnation? My boot loader is u-boot 1.1.3.

Thanks in advance,

Rodolfo

Signature · 4 lines
GNU/Linux Solutions                  e-mail:    [email protected]
Linux Device Driver                             [email protected]
Embedded Systems                     home page: giometti.enneenne.com
UNIX programming                     phone:     +39 349 2432127

Index: arch/mips/au1000/common/power.c
===================================================================
RCS file: /home/develop/cvs_private/linux-mips-exadron/arch/mips/au1000/common/power.c,v retrieving revision 1.1.1.1
retrieving revision 1.4

arch/mips/au1000/common/power.c +79 -86
diff -u -r1.1.1.1 -r1.4
@@ -34,12 +34,18 @@
 #include <linux/pm.h>
 #include <linux/slab.h>
 #include <linux/sysctl.h>
+#include <linux/jiffies.h>
 
 #include <asm/string.h>
 #include <asm/uaccess.h>
 #include <asm/io.h>
 #include <asm/system.h>
+#include <asm/cacheflush.h>
 #include <asm/mach-au1x00/au1000.h>
+#include <asm/mach-au1x00/power.h>
+
+static int sleep_ticks;
+static unsigned long cpu_freq;
 
 #ifdef CONFIG_PM
 
@@ -50,7 +56,7 @@
 #  define DPRINTK(fmt, args...)
 #endif
 
-static void calibrate_delay(void);
+static void au1000_calibrate_delay(void);
 
 extern void set_au1x00_speed(unsigned int new_freq);
 extern unsigned int get_au1x00_speed(void);
@@ -89,17 +95,6 @@
 static uint	sleep_usbdev_enable;
 static uint	sleep_static_memctlr[4][3];
 
-/* Define this to cause the value you write to /proc/sys/pm/sleep to
- * set the TOY timer for the amount of time you want to sleep.
- * This is done mainly for testing, but may be useful in other cases.
- * The value is number of 32KHz ticks to sleep.
- */
-#define SLEEP_TEST_TIMEOUT 1
-#ifdef SLEEP_TEST_TIMEOUT
-static	int	sleep_ticks;
-void wakeup_counter0_set(int ticks);
-#endif
-
 static void
 save_core_regs(void)
 {
@@ -210,10 +205,23 @@
 	suspend_mode = 0;
 }
 
-int au_sleep(void)
+void wakeup_counter0_set(int ticks);
+int (*board_before_sleep)(void);
+void (*board_after_sleep)(int reason);
+
+int au_sleep(int reason, int force)
 {
+	int ticks;
 	unsigned long wakeup, flags;
-	extern	void	save_and_sleep(void);
+	int board_reason = 0;
+
+	/* We need to configure the GPIO or timer interrupts that will bring
+	 * us out of sleep, so we use the specific board wake up source
+	 * (if present) */
+	if (board_before_sleep)
+		board_reason = board_before_sleep();
+	if (!force)
+		reason = board_reason;
 
 	spin_lock_irqsave(&pm_lock,flags);
 
@@ -221,29 +229,22 @@
 
 	flush_cache_all();
 
-	/** The code below is all system dependent and we should probably
-	 ** have a function call out of here to set this up.  You need
-	 ** to configure the GPIO or timer interrupts that will bring
-	 ** you out of sleep.
-	 ** For testing, the TOY counter wakeup is useful.
-	 **/
-
-#if 0
-	au_writel(au_readl(SYS_PINSTATERD) & ~(1 << 11), SYS_PINSTATERD);
-
-	/* gpio 6 can cause a wake up event */
-	wakeup = au_readl(SYS_WAKEMSK);
-	wakeup &= ~(1 << 8);	/* turn off match20 wakeup */
-	wakeup |= 1 << 6;	/* turn on gpio 6 wakeup   */
-#else
-	/* For testing, allow match20 to wake us up.
-	*/
-#ifdef SLEEP_TEST_TIMEOUT
-	wakeup_counter0_set(sleep_ticks);
-#endif
-	wakeup = 1 << 8;	/* turn on match20 wakeup   */
-	wakeup = 0;
-#endif
+	if (reason == 0)		/* machine_halt() */
+		wakeup = 0;
+	else if (reason < 0) {		/* TOY wake up */
+		ticks = -reason*HZ;
+		wakeup_counter0_set(ticks);
+		wakeup = 1<<8;		/* turn on match20 as wake up */
+	}
+	else {				/* GPIO[0-7] wake up */
+		/* We force pin GPIO[6]/SMROMCKE as gpio6 */
+		au_writel(au_readl(SYS_PINSTATERD)&~(1<<11), SYS_PINSTATERD);
+
+		wakeup = reason&0x0ff;	/* turn on selected gpio as wake up */
+	}
+	printk("%s - reason %x force %x wakeup %x ticks %x\n",
+	       __FUNCTION__, reason, force, wakeup, ticks);
+
 	au_writel(1, SYS_WAKESRC);	/* clear cause */
 	au_sync();
 	au_writel(wakeup, SYS_WAKEMSK);
@@ -251,50 +252,50 @@
 
 	save_and_sleep();
 
-	/* after a wakeup, the cpu vectors back to 0x1fc00000 so
+	/* after a wake up, the cpu vectors back to 0x1fc00000 so
 	 * it's up to the boot code to get us back here.
 	 */
 	restore_core_regs();
+
 	spin_unlock_irqrestore(&pm_lock, flags);
+
+	/* Get wake up source */
+	reason = au_readl(SYS_WAKESRC)>>18;
+	if (reason&(1<<8))		/* Wake up thanks to TOY */
+		reason = -ticks*HZ;
+
+	/* Call specific board routine */
+	if (board_after_sleep)
+		board_after_sleep(reason);
+
 	return 0;
 }
 
-static int pm_do_sleep(ctl_table * ctl, int write, struct file *file,
-		       void *buffer, size_t * len)
+static int pm_do_sleep(ctl_table *ctl, int write, struct file *file,
+		       void __user *buffer, size_t *len, loff_t *ppos)
 {
 	int retval = 0;
-#ifdef SLEEP_TEST_TIMEOUT
-#define TMPBUFLEN2 16
-	char buf[TMPBUFLEN2], *p;
-#endif
+	int *valp = (int *) ctl->data;
 
-	if (!write) {
+	if (!write)
 		*len = 0;
-	} else {
-#ifdef SLEEP_TEST_TIMEOUT
-		if (*len > TMPBUFLEN2 - 1) {
-			return -EFAULT;
-		}
-		if (copy_from_user(buf, buffer, *len)) {
-			return -EFAULT;
-		}
-		buf[*len] = 0;
-		p = buf;
-		sleep_ticks = simple_strtoul(p, &p, 0);
-#endif
-		retval = pm_send_all(PM_SUSPEND, (void *) 2);
 
+	retval = proc_dointvec(ctl, write, file, buffer, len, ppos);
+
+	if (write) {
+		retval = pm_send_all(PM_SUSPEND, (void *) 2);
 		if (retval)
 			return retval;
 
-		au_sleep();
+		au_sleep(*valp > 0 ? -*valp : 0, 1);
+
 		retval = pm_send_all(PM_RESUME, (void *) 0);
 	}
 	return retval;
 }
 
 static int pm_do_suspend(ctl_table * ctl, int write, struct file *file,
-			 void *buffer, size_t * len)
+			 void __user *buffer, size_t * len, loff_t *ppos)
 {
 	int retval = 0;
 
@@ -313,40 +314,32 @@
 
 
 static int pm_do_freq(ctl_table * ctl, int write, struct file *file,
-		      void *buffer, size_t * len)
+		      void __user *buffer, size_t * len, loff_t *ppos)
 {
 	int retval = 0, i;
-	unsigned long val, pll;
-#define TMPBUFLEN 64
-#define MAX_CPU_FREQ 396
-	char buf[TMPBUFLEN], *p;
+	unsigned long *valp = (unsigned long *) ctl->data;
+	unsigned long pll;
 	unsigned long flags, intc0_mask, intc1_mask;
 	unsigned long old_baud_base, old_cpu_freq, baud_rate, old_clk,
 	    old_refresh;
 	unsigned long new_baud_base, new_cpu_freq, new_clk, new_refresh;
 
 	spin_lock_irqsave(&pm_lock, flags);
-	if (!write) {
+
+	if (!write)
 		*len = 0;
-	} else {
-		/* Parse the new frequency */
-		if (*len > TMPBUFLEN - 1) {
-			spin_unlock_irqrestore(&pm_lock, flags);
-			return -EFAULT;
-		}
-		if (copy_from_user(buf, buffer, *len)) {
-			spin_unlock_irqrestore(&pm_lock, flags);
-			return -EFAULT;
-		}
-		buf[*len] = 0;
-		p = buf;
-		val = simple_strtoul(p, &p, 0);
-		if (val > MAX_CPU_FREQ) {
+
+	retval = proc_dointvec(ctl, write, file, buffer, len, ppos);
+
+#define MAX_CPU_FREQ 396
+	if (write) {
+		/* Check the new frequency */
+		if (*valp > MAX_CPU_FREQ) {
 			spin_unlock_irqrestore(&pm_lock, flags);
 			return -EFAULT;
 		}
 
-		pll = val / 12;
+		pll = *valp / 12;
 		if ((pll > 33) || (pll < 7)) {	/* 396 MHz max, 84 MHz min */
 			/* revisit this for higher speed cpus */
 			spin_unlock_irqrestore(&pm_lock, flags);
@@ -408,14 +401,14 @@
 
 
 	/* We don't want _any_ interrupts other than
-	 * match20. Otherwise our calibrate_delay()
+	 * match20. Otherwise our au1000_calibrate_delay()
 	 * calculation will be off, potentially a lot.
 	 */
 	intc0_mask = save_local_and_disable(0);
 	intc1_mask = save_local_and_disable(1);
 	local_enable_irq(AU1000_TOY_MATCH2_INT);
 	spin_unlock_irqrestore(&pm_lock, flags);
-	calibrate_delay();
+	au1000_calibrate_delay();
 	restore_local_and_enable(0, intc0_mask);
 	restore_local_and_enable(1, intc1_mask);
 	return retval;
@@ -424,8 +417,8 @@
 
 static struct ctl_table pm_table[] = {
 	{ACPI_S1_SLP_TYP, "suspend", NULL, 0, 0600, NULL, &pm_do_suspend},
-	{ACPI_SLEEP, "sleep", NULL, 0, 0600, NULL, &pm_do_sleep},
-	{CTL_ACPI, "freq", NULL, 0, 0600, NULL, &pm_do_freq},
+	{ACPI_SLEEP, "sleep", &sleep_ticks, sizeof(int), 0600, NULL, &pm_do_sleep},
+	{CTL_ACPI, "freq", &cpu_freq, sizeof(int), 0600, NULL, &pm_do_freq},
 	{0}
 };
 
@@ -455,7 +448,7 @@
    better than 1% */
 #define LPS_PREC 8
 
-static void calibrate_delay(void)
+static void au1000_calibrate_delay(void)
 {
 	unsigned long ticks, loopbit;
 	int lps_precision = LPS_PREC;

Index: arch/mips/au1000/common/reset.c
===================================================================
RCS file: /home/develop/cvs_private/linux-mips-exadron/arch/mips/au1000/common/reset.c,v retrieving revision 1.1.1.1
retrieving revision 1.3

arch/mips/au1000/common/reset.c +2 -2
diff -u -r1.1.1.1 -r1.3
@@ -36,8 +36,8 @@
 #include <asm/reboot.h>
 #include <asm/system.h>
 #include <asm/mach-au1x00/au1000.h>
+#include <asm/mach-au1x00/power.h>
 
-extern int au_sleep(void);
 extern void (*flush_cache_all)(void);
 
 void au1000_restart(char *command)
@@ -176,7 +176,7 @@
 	au_writel((1 << 26) | (1 << 10), GPIO2_OUTPUT);
 #endif
 #ifdef CONFIG_PM
-	au_sleep();
+	au_sleep(0, 0);
 
 	/* should not get here */
 	printk(KERN_ERR "Unable to put cpu in sleep mode\n");

Index: include/asm-mips/mach-au1x00/power.h
===================================================================
RCS file: include/asm-mips/mach-au1x00/power.h

include/asm-mips/mach-au1x00/power.h new file +61 -0

61 lines not on this page · raw message