Patchwork [RFC] powerpc/fsl: add timer wakeup source

login
register
mail settings
Submitter Wang Dongsheng
Date Oct. 3, 2012, 10:42 a.m.
Message ID <1349260948-15828-1-git-send-email-dongsheng.wds@gmail.com>
Download mbox | patch
Permalink /patch/188751/
State Superseded
Delegated to: Kumar Gala
Headers show

Comments

Wang Dongsheng - Oct. 3, 2012, 10:42 a.m.
This is only for freescale powerpc platform. The driver provides a way
to wake up system. Proc interface(/proc/powerpc/wakeup_timer_seconds).

eg: "echo 5 > /proc/powerpc/wakeup_timer_seconds", 5 seconds
after the system will be woken up. echo another time into proc interface
to update the time.

Signed-off-by: Wang Dongsheng <dongsheng.wang@freescale.com>
Signed-off-by: Li Yang <leoli@freescale.com>
---
 arch/powerpc/platforms/Kconfig            |   23 +++
 arch/powerpc/platforms/Makefile           |    1 +
 arch/powerpc/platforms/fsl_timer_wakeup.c |  217 +++++++++++++++++++++++++++++
 3 files changed, 241 insertions(+)
 create mode 100644 arch/powerpc/platforms/fsl_timer_wakeup.c
Kumar Gala - Oct. 3, 2012, 1:35 p.m.
On Oct 3, 2012, at 5:42 AM, Wang Dongsheng wrote:

> This is only for freescale powerpc platform. The driver provides a way
> to wake up system. Proc interface(/proc/powerpc/wakeup_timer_seconds).
> 
> eg: "echo 5 > /proc/powerpc/wakeup_timer_seconds", 5 seconds
> after the system will be woken up. echo another time into proc interface
> to update the time.
> 
> Signed-off-by: Wang Dongsheng <dongsheng.wang@freescale.com>
> Signed-off-by: Li Yang <leoli@freescale.com>
> ---
> arch/powerpc/platforms/Kconfig            |   23 +++
> arch/powerpc/platforms/Makefile           |    1 +
> arch/powerpc/platforms/fsl_timer_wakeup.c |  217 +++++++++++++++++++++++++++++
> 3 files changed, 241 insertions(+)
> create mode 100644 arch/powerpc/platforms/fsl_timer_wakeup.c

Adding the Linux PM list to see if there is some existing support for this on other arch's in kernel.

I'm pretty sure /proc/ is NOT where we want this exposed.

- k

> 
> diff --git a/arch/powerpc/platforms/Kconfig b/arch/powerpc/platforms/Kconfig
> index b190a6e..7b9232a 100644
> --- a/arch/powerpc/platforms/Kconfig
> +++ b/arch/powerpc/platforms/Kconfig
> @@ -99,6 +99,29 @@ config MPIC_TIMER
> 	  only tested on fsl chip, but it can potentially support
> 	  other global timers complying to Open-PIC standard.
> 
> +menuconfig FSL_WAKEUP_SOURCE
> +	bool "Freescale wakeup source"
> +	depends on FSL_SOC && SUSPEND
> +	default n
> +	help
> +	  This option enables wakeup source for wake up system
> +	  features. This is only for freescale powerpc platform.
> +
> +if FSL_WAKEUP_SOURCE
> +
> +config FSL_TIMER_WAKEUP
> +	tristate "Freescale mpic global timer wakeup event"
> +	default n
> +	help
> +	  This is only for freescale powerpc platform. The driver
> +	  provides a way to wake up system.
> +	  Proc interface(/proc/powerpc/wakeup_timer_seconds).
> +	  eg: "echo 5 > /proc/powerpc/wakeup_timer_seconds",
> +	  5 seconds after the system will be woken up. echo another
> +	  time into proc interface to update the time.
> +
> +endif
> +
> config PPC_EPAPR_HV_PIC
> 	bool
> 	default n
> diff --git a/arch/powerpc/platforms/Makefile b/arch/powerpc/platforms/Makefile
> index 879b4a4..8e9a04f 100644
> --- a/arch/powerpc/platforms/Makefile
> +++ b/arch/powerpc/platforms/Makefile
> @@ -2,6 +2,7 @@
> subdir-ccflags-$(CONFIG_PPC_WERROR) := -Werror
> 
> obj-$(CONFIG_FSL_ULI1575)	+= fsl_uli1575.o
> +obj-$(CONFIG_FSL_TIMER_WAKEUP)	+= fsl_timer_wakeup.o
> 
> obj-$(CONFIG_PPC_PMAC)		+= powermac/
> obj-$(CONFIG_PPC_CHRP)		+= chrp/
> diff --git a/arch/powerpc/platforms/fsl_timer_wakeup.c b/arch/powerpc/platforms/fsl_timer_wakeup.c
> new file mode 100644
> index 0000000..f20199f
> --- /dev/null
> +++ b/arch/powerpc/platforms/fsl_timer_wakeup.c
> @@ -0,0 +1,217 @@
> +/*
> + * Copyright 2012 Freescale Semiconductor, Inc.
> + * Wang Dongsheng <Dongsheng.Wang@freescale.com>
> + * Li Yang <leoli@freescale.com>
> + *
> + * This is only for freescale powerpc platform. The driver provides a way
> + * to wake up system. Proc interface(/proc/powerpc/wakeup_timer_seconds).
> + *
> + * eg: "echo 5 > /proc/powerpc/wakeup_timer_seconds", 5 seconds
> + * after the system will be woken up. echo another time into proc interface
> + * to update the time.
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms of the GNU General Public License version 2 as published
> + * by the Free Software Foundation.
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/init.h>
> +#include <linux/errno.h>
> +#include <linux/fs.h>
> +#include <linux/module.h>
> +#include <linux/interrupt.h>
> +#include <linux/sysfs.h>
> +#include <linux/slab.h>
> +#include <linux/mutex.h>
> +#include <linux/pm.h>
> +#include <linux/proc_fs.h>
> +#include <asm/uaccess.h>
> +#include <asm/mpic_timer.h>
> +
> +struct fsl_timer_wakeup {
> +	struct mpic_timer *timer;
> +	struct work_struct free_work;
> +	struct mutex mutex;
> +	struct proc_dir_entry *proc_timer_wakeup;
> +	struct timeval time;
> +};
> +
> +static struct fsl_timer_wakeup *priv;
> +
> +static void timer_event_wakeup_free_work(struct work_struct *ws)
> +{
> +	struct fsl_timer_wakeup *priv =
> +		container_of(ws, struct fsl_timer_wakeup, free_work);
> +
> +	mutex_lock(&priv->mutex);
> +	mpic_free_timer(priv->timer);
> +	priv->timer = NULL;
> +	mutex_unlock(&priv->mutex);
> +}
> +
> +static irqreturn_t timer_event_interrupt(int irq, void *dev_id)
> +{
> +	struct fsl_timer_wakeup *priv = dev_id;
> +
> +	schedule_work(&priv->free_work);
> +
> +	return IRQ_HANDLED;
> +}
> +
> +static ssize_t timer_wakeup_read(struct file *file, char __user *buf,
> +		size_t count, loff_t *offp)
> +{
> +	struct fsl_timer_wakeup *priv;
> +	struct inode *inode = file->f_path.dentry->d_inode;
> +	struct proc_dir_entry *dp;
> +
> +	int ret;
> +	char *kbuf;
> +
> +	dp = PDE(inode);
> +	priv = dp->data;
> +
> +	mutex_lock(&priv->mutex);
> +
> +	if (!priv->timer ||
> +			(priv->time.tv_sec >= 0 && priv->time.tv_usec >= 0)) {
> +		priv->time.tv_sec = -1;
> +		priv->time.tv_usec = -1;
> +
> +		mutex_unlock(&priv->mutex);
> +
> +		return 0;
> +	}
> +
> +	mpic_get_remain_time(priv->timer, &priv->time);
> +
> +	mutex_unlock(&priv->mutex);
> +
> +	kbuf = kzalloc(count, GFP_KERNEL);
> +	if (!kbuf)
> +		return -ENOMEM;
> +
> +	sprintf(kbuf, "%ld\n%c", priv->time.tv_sec + 1, '\0');
> +
> +	ret = strlen(kbuf);
> +
> +	copy_to_user(buf, kbuf, count);
> +
> +	kfree(kbuf);
> +
> +	return ret;
> +}
> +
> +static ssize_t timer_wakeup_write(struct file *file, const char __user *buf,
> +		size_t count, loff_t *off)
> +{
> +	struct fsl_timer_wakeup *priv;
> +	struct inode *inode = file->f_path.dentry->d_inode;
> +	struct proc_dir_entry *dp;
> +	struct timeval time;
> +	char *kbuf;
> +
> +	dp = PDE(inode);
> +	priv = dp->data;
> +
> +	kbuf = kzalloc(count + 1, GFP_KERNEL);
> +	if (!kbuf)
> +		return -ENOMEM;
> +
> +	if (copy_from_user(kbuf, buf, count)) {
> +		kfree(kbuf);
> +		return -EFAULT;
> +	}
> +
> +	kbuf[count] = '\0';
> +
> +	if (kstrtol(kbuf, 0, &time.tv_sec)) {
> +		kfree(kbuf);
> +		return -EINVAL;
> +	}
> +
> +	kfree(kbuf);
> +
> +	time.tv_usec = 0;
> +
> +	mutex_lock(&priv->mutex);
> +
> +	if (!time.tv_sec) {
> +		if (priv->timer) {
> +			mpic_free_timer(priv->timer);
> +			priv->timer = NULL;
> +		}
> +		mutex_unlock(&priv->mutex);
> +
> +		return count;
> +	}
> +
> +	if (priv->timer) {
> +		mpic_free_timer(priv->timer);
> +		priv->timer = NULL;
> +	}
> +
> +	priv->timer = mpic_request_timer(timer_event_interrupt, priv, &time);
> +	if (!priv->timer) {
> +		mutex_unlock(&priv->mutex);
> +
> +		return -EINVAL;
> +	}
> +
> +	mpic_start_timer(priv->timer);
> +
> +	mutex_unlock(&priv->mutex);
> +
> +	return count;
> +}
> +
> +static const struct file_operations timer_wakeup_fops = {
> +	.owner		= THIS_MODULE,
> +	.read		= timer_wakeup_read,
> +	.write		= timer_wakeup_write,
> +	.llseek		= no_llseek,
> +};
> +
> +static int timer_wakeup_init(void)
> +{
> +	struct proc_dir_entry *ent;
> +
> +	priv = kzalloc(sizeof(struct fsl_timer_wakeup), GFP_KERNEL);
> +	if (!priv)
> +		return -ENOMEM;
> +
> +	mutex_init(&priv->mutex);
> +	INIT_WORK(&priv->free_work, timer_event_wakeup_free_work);
> +	priv->time.tv_sec = -1;
> +	priv->time.tv_usec = -1;
> +
> +	ent = proc_create_data("powerpc/wakeup_timer_seconds",
> +			S_IRUSR | S_IWUSR, NULL, &timer_wakeup_fops, priv);
> +	if (!ent) {
> +		kfree(priv);
> +		return -ENOMEM;
> +	}
> +
> +	priv->proc_timer_wakeup = ent;
> +
> +	return 0;
> +}
> +
> +static void timer_wakeup_exit(void)
> +{
> +	if (priv->timer)
> +		mpic_free_timer(priv->timer);
> +
> +	remove_proc_entry("wakeup_timer_seconds",
> +			priv->proc_timer_wakeup->parent);
> +
> +	kfree(priv);
> +}
> +
> +module_init(timer_wakeup_init);
> +module_exit(timer_wakeup_exit);
> +
> +MODULE_DESCRIPTION("Freescale mpic global timer event wake-up driver");
> +MODULE_LICENSE("GPL v2");
> +MODULE_AUTHOR("Wang Dongsheng <dongsheng.wang@freescale.com>");
> -- 
> 1.7.9.5
Wang Dongsheng-B40534 - Oct. 8, 2012, 7:13 a.m.
> -----Original Message-----
> From: Wood Scott-B07421
> Sent: Thursday, October 04, 2012 6:20 AM
> To: Kumar Gala
> Cc: Wang Dongsheng; Wood Scott-B07421; linuxppc-dev@lists.ozlabs.org list;
> Wang Dongsheng-B40534; Li Yang-R58472; linux-pm@vger.kernel.org
> Subject: Re: [RFC PATCH] powerpc/fsl: add timer wakeup source
> 
> On 10/03/2012 08:35:58 AM, Kumar Gala wrote:
> >
> > On Oct 3, 2012, at 5:42 AM, Wang Dongsheng wrote:
> >
> > > This is only for freescale powerpc platform. The driver provides a
> > way
> > > to wake up system. Proc
> > interface(/proc/powerpc/wakeup_timer_seconds).
> > >
> > > eg: "echo 5 > /proc/powerpc/wakeup_timer_seconds", 5 seconds after
> > > the system will be woken up. echo another time into proc
> > interface
> > > to update the time.
> > >
> > > Signed-off-by: Wang Dongsheng <dongsheng.wang@freescale.com>
> > > Signed-off-by: Li Yang <leoli@freescale.com>
> > > ---
> > > arch/powerpc/platforms/Kconfig            |   23 +++
> > > arch/powerpc/platforms/Makefile           |    1 +
> > > arch/powerpc/platforms/fsl_timer_wakeup.c |  217
> > +++++++++++++++++++++++++++++
> > > 3 files changed, 241 insertions(+)
> > > create mode 100644 arch/powerpc/platforms/fsl_timer_wakeup.c
> >
> > Adding the Linux PM list to see if there is some existing support for
> > this on other arch's in kernel.
> >
> > I'm pretty sure /proc/ is NOT where we want this exposed.
> 
> Should probably go under the sysfs directory of the mpic device.  Or
> better, make a generic interface for timer-based suspend wakeup (if there
> isn't one already).  This current approach sits in an unpleasant middle
> ground between generic and device-specific.
> 	
/sys/power/wakeup_timer_seconds how about this?
I think it is a freescale generic interface, this interface control by
FSL_SOC && SUSPEND.

> > > diff --git a/arch/powerpc/platforms/Kconfig
> > b/arch/powerpc/platforms/Kconfig
> > > index b190a6e..7b9232a 100644
> > > --- a/arch/powerpc/platforms/Kconfig
> > > +++ b/arch/powerpc/platforms/Kconfig
> > > @@ -99,6 +99,29 @@ config MPIC_TIMER
> > > 	  only tested on fsl chip, but it can potentially support
> > > 	  other global timers complying to Open-PIC standard.
> > >
> > > +menuconfig FSL_WAKEUP_SOURCE
> > > +	bool "Freescale wakeup source"
> > > +	depends on FSL_SOC && SUSPEND
> > > +	default n
> > > +	help
> > > +	  This option enables wakeup source for wake up system
> > > +	  features. This is only for freescale powerpc platform.
> > > +
> > > +if FSL_WAKEUP_SOURCE
> > > +
> > > +config FSL_TIMER_WAKEUP
> > > +	tristate "Freescale mpic global timer wakeup event"
> > > +	default n
> > > +	help
> > > +	  This is only for freescale powerpc platform. The driver
> > > +	  provides a way to wake up system.
> > > +	  Proc interface(/proc/powerpc/wakeup_timer_seconds).
> > > +	  eg: "echo 5 > /proc/powerpc/wakeup_timer_seconds",
> > > +	  5 seconds after the system will be woken up. echo another
> > > +	  time into proc interface to update the time.
> > > +
> > > +endif
> 
> Use depends rather than if/else.  Why do you need FSL_WAKEUP_SOURCE?
>
It lists all wake up source. If later have wakeup source can be improved by
it to control. Buttons event wakeup source will be added after the timer.

> These names are overly broad -- this is only for FSL MPIC, not for other
> FSL chips (e.g. mpc83xx has a different global timer implementation, and
> there's FSL ARM chips, etc).
> 
Yes, thanks. Change to FSL_MPIC_TIMER_WAKEUP.

> > > +static ssize_t timer_wakeup_write(struct file *file, const char
> > __user *buf,
> > > +		size_t count, loff_t *off)
> > > +{
> > > +	struct fsl_timer_wakeup *priv;
> > > +	struct inode *inode = file->f_path.dentry->d_inode;
> > > +	struct proc_dir_entry *dp;
> > > +	struct timeval time;
> > > +	char *kbuf;
> > > +
> > > +	dp = PDE(inode);
> > > +	priv = dp->data;
> > > +
> > > +	kbuf = kzalloc(count + 1, GFP_KERNEL);
> > > +	if (!kbuf)
> > > +		return -ENOMEM;
> > > +
> > > +	if (copy_from_user(kbuf, buf, count)) {
> > > +		kfree(kbuf);
> > > +		return -EFAULT;
> > > +	}
> > > +
> > > +	kbuf[count] = '\0';
> > > +
> > > +	if (kstrtol(kbuf, 0, &time.tv_sec)) {
> > > +		kfree(kbuf);
> > > +		return -EINVAL;
> > > +	}
> > > +
> > > +	kfree(kbuf);
> > > +
> > > +	time.tv_usec = 0;
> > > +
> > > +	mutex_lock(&priv->mutex);
> > > +
> > > +	if (!time.tv_sec) {
> > > +		if (priv->timer) {
> > > +			mpic_free_timer(priv->timer);
> > > +			priv->timer = NULL;
> > > +		}
> > > +		mutex_unlock(&priv->mutex);
> > > +
> > > +		return count;
> > > +	}
> > > +
> > > +	if (priv->timer) {
> > > +		mpic_free_timer(priv->timer);
> > > +		priv->timer = NULL;
> > > +	}
> > > +
> > > +	priv->timer = mpic_request_timer(timer_event_interrupt, priv,
> > &time);
> 
> If the new time is zero, consider that a cancellation of the timer and
> don't request a new one or return -EINVAL.
> 
Thanks, I think i should add comments. Let this patch easy to read.
Here is get a new timer.
If the new time is zero, consider that has been checked.

if (!time.tv_sec) {...} this is check zero.
The "mpic_request_timer" before this code.

 - Dongsheng
Wang Dongsheng-B40534 - Oct. 9, 2012, 1:56 p.m.
> > > > I'm pretty sure /proc/ is NOT where we want this exposed.
> > >
> > > Should probably go under the sysfs directory of the mpic device.  Or
> > > better, make a generic interface for timer-based suspend wakeup (if
> > there
> > > isn't one already).  This current approach sits in an unpleasant
> middle
> > > ground between generic and device-specific.
> > >
> > /sys/power/wakeup_timer_seconds how about this?
> > I think it is a freescale generic interface, this interface control by
> > FSL_SOC && SUSPEND.

> There's no such thing as a "Freescale generic interface".  Linux APIs
> are not organized by hardware vendor.  Either make a truly generic
> interface, reuse an existing one, or do something that is attached to
> the specific driver.
Thanks, I think i can change mpic timer registration, i will use platform_driver_register.
I will merge "mpic timer" and "timer wakeup". In "mpic timer" the wakeup function
controls by SUSPEND. The sys path is "/sys/devices/soc8572.4/ffe41100.timer/wakeup_seconds".
do you have any suggestions for this? or have a better idea about the sys path?

> > > Use depends rather than if/else.  Why do you need FSL_WAKEUP_SOURCE?
> > >
> > It lists all wake up source. If later have wakeup source can be
> > improved by
> > it to control. Buttons event wakeup source will be added after the
> > timer.

> It does not list all wake up sources -- there's also ethernet, USB, etc.
fine, change to depends. thanks.

> > >
> > > If the new time is zero, consider that a cancellation of the timer
> > and
> > > don't request a new one or return -EINVAL.
> > >
> > Thanks, I think i should add comments. Let this patch easy to read.
> > Here is get a new timer.
> > If the new time is zero, consider that has been checked.
> >
> > if (!time.tv_sec) {...} this is check zero.
> > The "mpic_request_timer" before this code.

> Ah, I see.  Wouldn't it be simpler to remove that block and just test
> time.tv_sec when requesting the new timer?
Yes, wake up function only need seconds level, and it's just hold a timer.
echo zero to cancel, and echo a new time to update the timer time.
Scott Wood - Oct. 9, 2012, 6:18 p.m.
On 10/09/2012 08:56:53 AM, Wang Dongsheng-B40534 wrote:
> 
> > > > > I'm pretty sure /proc/ is NOT where we want this exposed.
> > > >
> > > > Should probably go under the sysfs directory of the mpic  
> device.  Or
> > > > better, make a generic interface for timer-based suspend wakeup  
> (if
> > > there
> > > > isn't one already).  This current approach sits in an unpleasant
> > middle
> > > > ground between generic and device-specific.
> > > >
> > > /sys/power/wakeup_timer_seconds how about this?
> > > I think it is a freescale generic interface, this interface  
> control by
> > > FSL_SOC && SUSPEND.
> 
> > There's no such thing as a "Freescale generic interface".  Linux  
> APIs
> > are not organized by hardware vendor.  Either make a truly generic
> > interface, reuse an existing one, or do something that is attached  
> to
> > the specific driver.
> Thanks, I think i can change mpic timer registration, i will use  
> platform_driver_register.
> I will merge "mpic timer" and "timer wakeup". In "mpic timer" the  
> wakeup function
> controls by SUSPEND. The sys path is  
> "/sys/devices/soc8572.4/ffe41100.timer/wakeup_seconds".
> do you have any suggestions for this? or have a better idea about the  
> sys path?

I'm not sure what you mean by the merging, but the path looks OK (I'd  
prefer a dash rather than underscore, but I'm not sure what the usual  
practice is in sysfs).

That said, this seems like something that could use a truly generic  
interface.

-Scott
Tabi Timur-B04825 - Dec. 13, 2012, 3:51 p.m.
On Wed, Oct 3, 2012 at 5:42 AM, Wang Dongsheng <dongsheng.wds@gmail.com> wrote:
>
>
> Signed-off-by: Wang Dongsheng <dongsheng.wang@freescale.com>

Wang,

Your patches must always have a From: line of your Freescale email
address.  Do not use your gmail.com address to send patches.

This needs to be fixed when this patch is applied.

Patch

diff --git a/arch/powerpc/platforms/Kconfig b/arch/powerpc/platforms/Kconfig
index b190a6e..7b9232a 100644
--- a/arch/powerpc/platforms/Kconfig
+++ b/arch/powerpc/platforms/Kconfig
@@ -99,6 +99,29 @@  config MPIC_TIMER
 	  only tested on fsl chip, but it can potentially support
 	  other global timers complying to Open-PIC standard.
 
+menuconfig FSL_WAKEUP_SOURCE
+	bool "Freescale wakeup source"
+	depends on FSL_SOC && SUSPEND
+	default n
+	help
+	  This option enables wakeup source for wake up system
+	  features. This is only for freescale powerpc platform.
+
+if FSL_WAKEUP_SOURCE
+
+config FSL_TIMER_WAKEUP
+	tristate "Freescale mpic global timer wakeup event"
+	default n
+	help
+	  This is only for freescale powerpc platform. The driver
+	  provides a way to wake up system.
+	  Proc interface(/proc/powerpc/wakeup_timer_seconds).
+	  eg: "echo 5 > /proc/powerpc/wakeup_timer_seconds",
+	  5 seconds after the system will be woken up. echo another
+	  time into proc interface to update the time.
+
+endif
+
 config PPC_EPAPR_HV_PIC
 	bool
 	default n
diff --git a/arch/powerpc/platforms/Makefile b/arch/powerpc/platforms/Makefile
index 879b4a4..8e9a04f 100644
--- a/arch/powerpc/platforms/Makefile
+++ b/arch/powerpc/platforms/Makefile
@@ -2,6 +2,7 @@ 
 subdir-ccflags-$(CONFIG_PPC_WERROR) := -Werror
 
 obj-$(CONFIG_FSL_ULI1575)	+= fsl_uli1575.o
+obj-$(CONFIG_FSL_TIMER_WAKEUP)	+= fsl_timer_wakeup.o
 
 obj-$(CONFIG_PPC_PMAC)		+= powermac/
 obj-$(CONFIG_PPC_CHRP)		+= chrp/
diff --git a/arch/powerpc/platforms/fsl_timer_wakeup.c b/arch/powerpc/platforms/fsl_timer_wakeup.c
new file mode 100644
index 0000000..f20199f
--- /dev/null
+++ b/arch/powerpc/platforms/fsl_timer_wakeup.c
@@ -0,0 +1,217 @@ 
+/*
+ * Copyright 2012 Freescale Semiconductor, Inc.
+ * Wang Dongsheng <Dongsheng.Wang@freescale.com>
+ * Li Yang <leoli@freescale.com>
+ *
+ * This is only for freescale powerpc platform. The driver provides a way
+ * to wake up system. Proc interface(/proc/powerpc/wakeup_timer_seconds).
+ *
+ * eg: "echo 5 > /proc/powerpc/wakeup_timer_seconds", 5 seconds
+ * after the system will be woken up. echo another time into proc interface
+ * to update the time.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/sysfs.h>
+#include <linux/slab.h>
+#include <linux/mutex.h>
+#include <linux/pm.h>
+#include <linux/proc_fs.h>
+#include <asm/uaccess.h>
+#include <asm/mpic_timer.h>
+
+struct fsl_timer_wakeup {
+	struct mpic_timer *timer;
+	struct work_struct free_work;
+	struct mutex mutex;
+	struct proc_dir_entry *proc_timer_wakeup;
+	struct timeval time;
+};
+
+static struct fsl_timer_wakeup *priv;
+
+static void timer_event_wakeup_free_work(struct work_struct *ws)
+{
+	struct fsl_timer_wakeup *priv =
+		container_of(ws, struct fsl_timer_wakeup, free_work);
+
+	mutex_lock(&priv->mutex);
+	mpic_free_timer(priv->timer);
+	priv->timer = NULL;
+	mutex_unlock(&priv->mutex);
+}
+
+static irqreturn_t timer_event_interrupt(int irq, void *dev_id)
+{
+	struct fsl_timer_wakeup *priv = dev_id;
+
+	schedule_work(&priv->free_work);
+
+	return IRQ_HANDLED;
+}
+
+static ssize_t timer_wakeup_read(struct file *file, char __user *buf,
+		size_t count, loff_t *offp)
+{
+	struct fsl_timer_wakeup *priv;
+	struct inode *inode = file->f_path.dentry->d_inode;
+	struct proc_dir_entry *dp;
+
+	int ret;
+	char *kbuf;
+
+	dp = PDE(inode);
+	priv = dp->data;
+
+	mutex_lock(&priv->mutex);
+
+	if (!priv->timer ||
+			(priv->time.tv_sec >= 0 && priv->time.tv_usec >= 0)) {
+		priv->time.tv_sec = -1;
+		priv->time.tv_usec = -1;
+
+		mutex_unlock(&priv->mutex);
+
+		return 0;
+	}
+
+	mpic_get_remain_time(priv->timer, &priv->time);
+
+	mutex_unlock(&priv->mutex);
+
+	kbuf = kzalloc(count, GFP_KERNEL);
+	if (!kbuf)
+		return -ENOMEM;
+
+	sprintf(kbuf, "%ld\n%c", priv->time.tv_sec + 1, '\0');
+
+	ret = strlen(kbuf);
+
+	copy_to_user(buf, kbuf, count);
+
+	kfree(kbuf);
+
+	return ret;
+}
+
+static ssize_t timer_wakeup_write(struct file *file, const char __user *buf,
+		size_t count, loff_t *off)
+{
+	struct fsl_timer_wakeup *priv;
+	struct inode *inode = file->f_path.dentry->d_inode;
+	struct proc_dir_entry *dp;
+	struct timeval time;
+	char *kbuf;
+
+	dp = PDE(inode);
+	priv = dp->data;
+
+	kbuf = kzalloc(count + 1, GFP_KERNEL);
+	if (!kbuf)
+		return -ENOMEM;
+
+	if (copy_from_user(kbuf, buf, count)) {
+		kfree(kbuf);
+		return -EFAULT;
+	}
+
+	kbuf[count] = '\0';
+
+	if (kstrtol(kbuf, 0, &time.tv_sec)) {
+		kfree(kbuf);
+		return -EINVAL;
+	}
+
+	kfree(kbuf);
+
+	time.tv_usec = 0;
+
+	mutex_lock(&priv->mutex);
+
+	if (!time.tv_sec) {
+		if (priv->timer) {
+			mpic_free_timer(priv->timer);
+			priv->timer = NULL;
+		}
+		mutex_unlock(&priv->mutex);
+
+		return count;
+	}
+
+	if (priv->timer) {
+		mpic_free_timer(priv->timer);
+		priv->timer = NULL;
+	}
+
+	priv->timer = mpic_request_timer(timer_event_interrupt, priv, &time);
+	if (!priv->timer) {
+		mutex_unlock(&priv->mutex);
+
+		return -EINVAL;
+	}
+
+	mpic_start_timer(priv->timer);
+
+	mutex_unlock(&priv->mutex);
+
+	return count;
+}
+
+static const struct file_operations timer_wakeup_fops = {
+	.owner		= THIS_MODULE,
+	.read		= timer_wakeup_read,
+	.write		= timer_wakeup_write,
+	.llseek		= no_llseek,
+};
+
+static int timer_wakeup_init(void)
+{
+	struct proc_dir_entry *ent;
+
+	priv = kzalloc(sizeof(struct fsl_timer_wakeup), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	mutex_init(&priv->mutex);
+	INIT_WORK(&priv->free_work, timer_event_wakeup_free_work);
+	priv->time.tv_sec = -1;
+	priv->time.tv_usec = -1;
+
+	ent = proc_create_data("powerpc/wakeup_timer_seconds",
+			S_IRUSR | S_IWUSR, NULL, &timer_wakeup_fops, priv);
+	if (!ent) {
+		kfree(priv);
+		return -ENOMEM;
+	}
+
+	priv->proc_timer_wakeup = ent;
+
+	return 0;
+}
+
+static void timer_wakeup_exit(void)
+{
+	if (priv->timer)
+		mpic_free_timer(priv->timer);
+
+	remove_proc_entry("wakeup_timer_seconds",
+			priv->proc_timer_wakeup->parent);
+
+	kfree(priv);
+}
+
+module_init(timer_wakeup_init);
+module_exit(timer_wakeup_exit);
+
+MODULE_DESCRIPTION("Freescale mpic global timer event wake-up driver");
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Wang Dongsheng <dongsheng.wang@freescale.com>");