From: Rodolfo Giometti <giometti@linux.it>
To: Linux MIPS <linux-mips@linux-mips.org>
Subject: [PATCH] sysfs interface for Au1xxx power management
Date: Thu, 6 Apr 2006 00:19:33 +0200 [thread overview]
Message-ID: <20060405221933.GN7029@enneenne.com> (raw)
[-- 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);
next reply other threads:[~2006-04-05 22:08 UTC|newest]
Thread overview: 2+ messages / expand[flat|nested] mbox.gz Atom feed top
2006-04-05 22:19 Rodolfo Giometti [this message]
2006-04-06 17:16 ` sysfs interface for Au1xxx power management Jordan Crouse
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20060405221933.GN7029@enneenne.com \
--to=giometti@linux.it \
--cc=linux-mips@linux-mips.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
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.