2013-04-09 10:22:32 +08:00
/*
* MPIC timer wakeup driver
*
* Copyright 2013 Freescale Semiconductor , Inc .
*
* This program is free software ; you can redistribute it and / or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation ; either version 2 of the License , or ( at your
* option ) any later version .
*/
# include <linux/kernel.h>
# include <linux/slab.h>
# include <linux/errno.h>
# include <linux/module.h>
# include <linux/interrupt.h>
# include <linux/device.h>
# include <asm/mpic_timer.h>
# include <asm/mpic.h>
struct fsl_mpic_timer_wakeup {
struct mpic_timer * timer ;
struct work_struct free_work ;
} ;
static struct fsl_mpic_timer_wakeup * fsl_wakeup ;
static DEFINE_MUTEX ( sysfs_lock ) ;
static void fsl_free_resource ( struct work_struct * ws )
{
struct fsl_mpic_timer_wakeup * wakeup =
container_of ( ws , struct fsl_mpic_timer_wakeup , free_work ) ;
mutex_lock ( & sysfs_lock ) ;
if ( wakeup - > timer ) {
disable_irq_wake ( wakeup - > timer - > irq ) ;
mpic_free_timer ( wakeup - > timer ) ;
}
wakeup - > timer = NULL ;
mutex_unlock ( & sysfs_lock ) ;
}
static irqreturn_t fsl_mpic_timer_irq ( int irq , void * dev_id )
{
struct fsl_mpic_timer_wakeup * wakeup = dev_id ;
schedule_work ( & wakeup - > free_work ) ;
return wakeup - > timer ? IRQ_HANDLED : IRQ_NONE ;
}
static ssize_t fsl_timer_wakeup_show ( struct device * dev ,
struct device_attribute * attr ,
char * buf )
{
2018-01-16 18:01:50 +01:00
time64_t interval = 0 ;
2013-04-09 10:22:32 +08:00
mutex_lock ( & sysfs_lock ) ;
if ( fsl_wakeup - > timer ) {
mpic_get_remain_time ( fsl_wakeup - > timer , & interval ) ;
2018-01-16 18:01:50 +01:00
interval + + ;
2013-04-09 10:22:32 +08:00
}
mutex_unlock ( & sysfs_lock ) ;
2018-01-16 18:01:50 +01:00
return sprintf ( buf , " %lld \n " , interval ) ;
2013-04-09 10:22:32 +08:00
}
static ssize_t fsl_timer_wakeup_store ( struct device * dev ,
struct device_attribute * attr ,
const char * buf ,
size_t count )
{
2018-01-16 18:01:50 +01:00
time64_t interval ;
2013-04-09 10:22:32 +08:00
int ret ;
2018-01-16 18:01:50 +01:00
if ( kstrtoll ( buf , 0 , & interval ) )
2013-04-09 10:22:32 +08:00
return - EINVAL ;
mutex_lock ( & sysfs_lock ) ;
if ( fsl_wakeup - > timer ) {
disable_irq_wake ( fsl_wakeup - > timer - > irq ) ;
mpic_free_timer ( fsl_wakeup - > timer ) ;
fsl_wakeup - > timer = NULL ;
}
2018-01-16 18:01:50 +01:00
if ( ! interval ) {
2013-04-09 10:22:32 +08:00
mutex_unlock ( & sysfs_lock ) ;
return count ;
}
fsl_wakeup - > timer = mpic_request_timer ( fsl_mpic_timer_irq ,
2018-01-16 18:01:50 +01:00
fsl_wakeup , interval ) ;
2013-04-09 10:22:32 +08:00
if ( ! fsl_wakeup - > timer ) {
mutex_unlock ( & sysfs_lock ) ;
return - EINVAL ;
}
ret = enable_irq_wake ( fsl_wakeup - > timer - > irq ) ;
if ( ret ) {
mpic_free_timer ( fsl_wakeup - > timer ) ;
fsl_wakeup - > timer = NULL ;
mutex_unlock ( & sysfs_lock ) ;
return ret ;
}
mpic_start_timer ( fsl_wakeup - > timer ) ;
mutex_unlock ( & sysfs_lock ) ;
return count ;
}
static struct device_attribute mpic_attributes = __ATTR ( timer_wakeup , 0644 ,
fsl_timer_wakeup_show , fsl_timer_wakeup_store ) ;
static int __init fsl_wakeup_sys_init ( void )
{
int ret ;
fsl_wakeup = kzalloc ( sizeof ( struct fsl_mpic_timer_wakeup ) , GFP_KERNEL ) ;
if ( ! fsl_wakeup )
return - ENOMEM ;
INIT_WORK ( & fsl_wakeup - > free_work , fsl_free_resource ) ;
ret = device_create_file ( mpic_subsys . dev_root , & mpic_attributes ) ;
if ( ret )
kfree ( fsl_wakeup ) ;
return ret ;
}
static void __exit fsl_wakeup_sys_exit ( void )
{
device_remove_file ( mpic_subsys . dev_root , & mpic_attributes ) ;
mutex_lock ( & sysfs_lock ) ;
if ( fsl_wakeup - > timer ) {
disable_irq_wake ( fsl_wakeup - > timer - > irq ) ;
mpic_free_timer ( fsl_wakeup - > timer ) ;
}
kfree ( fsl_wakeup ) ;
mutex_unlock ( & sysfs_lock ) ;
}
module_init ( fsl_wakeup_sys_init ) ;
module_exit ( fsl_wakeup_sys_exit ) ;
MODULE_DESCRIPTION ( " Freescale MPIC global timer wakeup driver " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;
MODULE_AUTHOR ( " Wang Dongsheng <dongsheng.wang@freescale.com> " ) ;