* [PATCH] sysfs interface for Au1xxx power management
@ 2006-04-05 22:19 Rodolfo Giometti
2006-04-06 17:16 ` Jordan Crouse
0 siblings, 1 reply; 2+ messages in thread
From: Rodolfo Giometti @ 2006-04-05 22:19 UTC (permalink / raw)
To: Linux MIPS
[-- Attachment #1: Type: text/plain, Size: 538 bytes --]
Hello,
here a patch to support new sysfs interface for Au1xxx's power
management. Now we can put the system into sleeping mode by using:
hostname:~# echo mem > /sys/power/state
The patch keeps also the file "/proc/sys/pm/freq" from the old
interface.
Ciao,
Rodolfo
--
GNU/Linux Solutions e-mail: giometti@enneenne.com
Linux Device Driver giometti@gnudd.com
Embedded Systems giometti@linux.it
UNIX programming phone: +39 349 2432127
[-- Attachment #2: patch-pm-sysfs --]
[-- Type: text/plain, Size: 12436 bytes --]
--- /home/develop/embedded/mipsel/linux/linux-mips.git/arch/mips/au1000/common/power.c 2006-03-31 16:57:26.000000000 +0200
+++ arch/mips/au1000/common/power.c 2006-04-06 00:08:59.000000000 +0200
@@ -1,6 +1,11 @@
/*
* BRIEF MODULE DESCRIPTION
- * Au1000 Power Management routines.
+ * Au1xxx Power Management routines (sysfs and procfs interfaces).
+ *
+ * Copyright 2006 Rodolfo Giometti <giometti@linux.it>
+ *
+ * Based on some previous version's functions, so I report below the previous
+ * copyright message:
*
* Copyright 2001 MontaVista Software Inc.
* Author: MontaVista Software, Inc.
@@ -32,7 +37,6 @@
#include <linux/config.h>
#include <linux/init.h>
#include <linux/pm.h>
-#include <linux/pm_legacy.h>
#include <linux/slab.h>
#include <linux/sysctl.h>
#include <linux/jiffies.h>
@@ -43,18 +47,17 @@
#include <asm/system.h>
#include <asm/cacheflush.h>
#include <asm/mach-au1x00/au1000.h>
+#include <asm/mach-au1x00/power.h>
-#ifdef CONFIG_PM
+static unsigned long cpu_freq;
-#define DEBUG 1
+//#define DEBUG 1
#ifdef DEBUG
# define DPRINTK(fmt, args...) printk("%s: " fmt, __FUNCTION__ , ## args)
#else
# define DPRINTK(fmt, args...)
#endif
-static void au1000_calibrate_delay(void);
-
extern void set_au1x00_speed(unsigned int new_freq);
extern unsigned int get_au1x00_speed(void);
extern unsigned long get_au1x00_uart_baud_base(void);
@@ -64,9 +67,9 @@
extern void local_enable_irq(unsigned int irq_nr);
/* Quick acpi hack. This will have to change! */
-#define CTL_ACPI 9999
-#define ACPI_S1_SLP_TYP 19
-#define ACPI_SLEEP 21
+#define CTL_ACPI 9999
+#define ACPI_S1_SLP_TYP 19
+#define ACPI_SLEEP 21
static DEFINE_SPINLOCK(pm_lock);
@@ -83,6 +86,7 @@
static uint sleep_aux_pll_cntrl;
static uint sleep_cpu_pll_cntrl;
static uint sleep_pin_function;
+static uint sleep_gpio2_enable;
static uint sleep_uart0_inten;
static uint sleep_uart0_fifoctl;
static uint sleep_uart0_linectl;
@@ -92,16 +96,45 @@
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
+/* This is the number of bits of precision for the loops_per_jiffy. Each
+ bit takes on average 1.5/HZ seconds. This (like the original) is a little
+ better than 1% */
+#define LPS_PREC 8
+
+static void au1000_calibrate_delay(void)
+{
+ unsigned long ticks, loopbit;
+ int lps_precision = LPS_PREC;
+
+ loops_per_jiffy = (1 << 12);
+
+ while (loops_per_jiffy <<= 1) {
+ /* wait for "start of" clock tick */
+ ticks = jiffies;
+ while (ticks == jiffies)
+ /* nothing */ ;
+ /* Go .. */
+ ticks = jiffies;
+ __delay(loops_per_jiffy);
+ ticks = jiffies - ticks;
+ if (ticks)
+ break;
+ }
+
+/* Do a binary approximation to get loops_per_jiffy set to equal one clock
+ (up to lps_precision bits) */
+ loops_per_jiffy >>= 1;
+ loopbit = loops_per_jiffy;
+ while (lps_precision-- && (loopbit >>= 1)) {
+ loops_per_jiffy |= loopbit;
+ ticks = jiffies;
+ while (ticks == jiffies);
+ ticks = jiffies;
+ __delay(loops_per_jiffy);
+ if (jiffies != ticks) /* longer than 1 tick */
+ loops_per_jiffy &= ~loopbit;
+ }
+}
static void
save_core_regs(void)
@@ -147,6 +180,7 @@
sleep_cpu_pll_cntrl = au_readl(SYS_CPUPLL);
sleep_pin_function = au_readl(SYS_PINFUNC);
+ sleep_gpio2_enable = au_readl(GPIO2_ENABLE);
/* Save the static memory controller configuration.
*/
@@ -173,6 +207,7 @@
au_writel(sleep_aux_pll_cntrl, SYS_AUXPLL); au_sync();
au_writel(sleep_cpu_pll_cntrl, SYS_CPUPLL); au_sync();
au_writel(sleep_pin_function, SYS_PINFUNC); au_sync();
+ au_writel(sleep_gpio2_enable, GPIO2_ENABLE); au_sync();
/* Restore the static memory controller configuration.
*/
@@ -206,47 +241,48 @@
wakeup_counter0_adjust();
}
-unsigned long suspend_mode;
-
-void wakeup_from_suspend(void)
-{
- suspend_mode = 0;
-}
+void wakeup_counter0_set(int ticks);
+int (*board_before_sleep)(void);
+void (*board_after_sleep)(int reason);
-int au_sleep(void)
+int au_sleep(int reason, int force)
{
+ int ticks = 0;
unsigned long wakeup, flags;
- extern void save_and_sleep(void);
+ int board_reason = 0;
- spin_lock_irqsave(&pm_lock,flags);
+ /* 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);
save_core_regs();
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 */
+ /* FIXME: this setting for GPIO[6] should be into specific
+ * boards setup... */
+ /* 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(KERN_DEBUG "%s - reason %x force %x wakeup %lx ticks %x\n",
+ __FUNCTION__, reason, force, wakeup, ticks);
+
au_writel(1, SYS_WAKESRC); /* clear cause */
au_sync();
au_writel(wakeup, SYS_WAKEMSK);
@@ -254,102 +290,52 @@
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);
- return 0;
-}
-
-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
-
- 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);
-
- if (retval)
- return retval;
-
- au_sleep();
- retval = pm_send_all(PM_RESUME, (void *) 0);
- }
- return retval;
-}
-static int pm_do_suspend(ctl_table * ctl, int write, struct file *file,
- void __user *buffer, size_t * len, loff_t *ppos)
-{
- int retval = 0;
+ spin_unlock_irqrestore(&pm_lock, flags);
- if (!write) {
- *len = 0;
- } else {
- retval = pm_send_all(PM_SUSPEND, (void *) 2);
- if (retval)
- return retval;
- suspend_mode = 1;
+ /* 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);
- retval = pm_send_all(PM_RESUME, (void *) 0);
- }
- return retval;
+ return 0;
}
-
-static int pm_do_freq(ctl_table * ctl, int write, struct file *file,
+static int au1xxx_pm_do_freq(ctl_table * ctl, int write, struct file *file,
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);
@@ -424,72 +410,66 @@
return retval;
}
+/*
+ * Called after processes are frozen, but before we shut down devices.
+ */
+int au1xxx_pm_prepare(suspend_state_t state)
+{
+ DPRINTK("state = %d\n", state);
+ return 0;
+}
-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},
- {0}
-};
-
-static struct ctl_table pm_dir_table[] = {
- {CTL_ACPI, "pm", NULL, 0, 0555, pm_table},
- {0}
-};
+/*
+ * Called after down devices.
+ */
+int au1xxx_pm_enter(suspend_state_t state)
+{
+ DPRINTK("state = %d\n", state);
+ return au_sleep(1<<6 /* GPIO 6 */, 1);
+ /* return au_sleep(-3, 1); */ /* wake up after 3 seconds */
+}
/*
- * Initialize power interface
+ * Called after devices are re-setup, but before processes are thawed.
*/
-static int __init pm_init(void)
+int au1xxx_pm_finish(suspend_state_t state)
{
- register_sysctl_table(pm_dir_table, 1);
+ DPRINTK("state = %d\n", state);
return 0;
}
-__initcall(pm_init);
-
+/*
+ * Set to PM_DISK_FIRMWARE so we can quickly veto suspend-to-disk.
+ */
+static struct pm_ops au1xxx_pm_ops = {
+ .pm_disk_mode = PM_DISK_FIRMWARE,
+ .prepare = au1xxx_pm_prepare,
+ .enter = au1xxx_pm_enter,
+ .finish = au1xxx_pm_finish,
+};
/*
- * This is right out of init/main.c
+ * Set up the old "/proc/sys/pm" interface.
*/
+static struct ctl_table au1xxx_pm_table[] = {
+ { CTL_ACPI, "freq", &cpu_freq, sizeof(int), 0600, NULL, &au1xxx_pm_do_freq },
+ { 0 },
+};
-/* This is the number of bits of precision for the loops_per_jiffy. Each
- bit takes on average 1.5/HZ seconds. This (like the original) is a little
- better than 1% */
-#define LPS_PREC 8
+static struct ctl_table pm_dir_table[] = {
+ { CTL_ACPI, "pm", NULL, 0, 0555, au1xxx_pm_table },
+ { 0 },
+};
-static void au1000_calibrate_delay(void)
+static int __init au1xxx_pm_init(void)
{
- unsigned long ticks, loopbit;
- int lps_precision = LPS_PREC;
-
- loops_per_jiffy = (1 << 12);
+ /* The new interface */
+ pm_set_ops(&au1xxx_pm_ops);
- while (loops_per_jiffy <<= 1) {
- /* wait for "start of" clock tick */
- ticks = jiffies;
- while (ticks == jiffies)
- /* nothing */ ;
- /* Go .. */
- ticks = jiffies;
- __delay(loops_per_jiffy);
- ticks = jiffies - ticks;
- if (ticks)
- break;
- }
+ /* The old interface */
+ register_sysctl_table(pm_dir_table, 1);
-/* Do a binary approximation to get loops_per_jiffy set to equal one clock
- (up to lps_precision bits) */
- loops_per_jiffy >>= 1;
- loopbit = loops_per_jiffy;
- while (lps_precision-- && (loopbit >>= 1)) {
- loops_per_jiffy |= loopbit;
- ticks = jiffies;
- while (ticks == jiffies);
- ticks = jiffies;
- __delay(loops_per_jiffy);
- if (jiffies != ticks) /* longer than 1 tick */
- loops_per_jiffy &= ~loopbit;
- }
+ return 0;
}
-#endif /* CONFIG_PM */
+
+device_initcall(au1xxx_pm_init);
^ permalink raw reply [flat|nested] 2+ messages in thread
* Re: sysfs interface for Au1xxx power management
2006-04-05 22:19 [PATCH] sysfs interface for Au1xxx power management Rodolfo Giometti
@ 2006-04-06 17:16 ` Jordan Crouse
0 siblings, 0 replies; 2+ messages in thread
From: Jordan Crouse @ 2006-04-06 17:16 UTC (permalink / raw)
To: Rodolfo Giometti; +Cc: Linux MIPS
On 06/04/06 00:19 +0200, Rodolfo Giometti wrote:
> Hello,
>
> here a patch to support new sysfs interface for Au1xxx's power
> management. Now we can put the system into sleeping mode by using:
>
> hostname:~# echo mem > /sys/power/state
>
> The patch keeps also the file "/proc/sys/pm/freq" from the old
> interface.
Generally looks good, thought I just glanced it over and I didn't take
it for a test run.
> /* Quick acpi hack. This will have to change! */
> -#define CTL_ACPI 9999
> -#define ACPI_S1_SLP_TYP 19
> -#define ACPI_SLEEP 21
> +#define CTL_ACPI 9999
> +#define ACPI_S1_SLP_TYP 19
> +#define ACPI_SLEEP 21
Code review comment - you have lots of minor typo fixes and whitespace
changes. You should Keep whitespace changes to a minimum, or better yet
put then in a separate patch. They detract from the actual meat of your
effort, and makes it tough to code review.
Jordan
--
Jordan Crouse
Senior Linux Engineer
AMD - Personal Connectivity Solutions Group
<www.amd.com/embeddedprocessors>
^ permalink raw reply [flat|nested] 2+ messages in thread
end of thread, other threads:[~2006-04-06 16:29 UTC | newest]
Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2006-04-05 22:19 [PATCH] sysfs interface for Au1xxx power management Rodolfo Giometti
2006-04-06 17:16 ` Jordan Crouse
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.