1/* 2 * MPIC timer wakeup driver 3 * 4 * Copyright 2013 Freescale Semiconductor, Inc. 5 * 6 * This program is free software; you can redistribute it and/or modify it 7 * under the terms of the GNU General Public License as published by the 8 * Free Software Foundation; either version 2 of the License, or (at your 9 * option) any later version. 10 */ 11 12#include <linux/kernel.h> 13#include <linux/slab.h> 14#include <linux/errno.h> 15#include <linux/module.h> 16#include <linux/interrupt.h> 17#include <linux/device.h> 18 19#include <asm/mpic_timer.h> 20#include <asm/mpic.h> 21 22struct fsl_mpic_timer_wakeup { 23 struct mpic_timer *timer; 24 struct work_struct free_work; 25}; 26 27static struct fsl_mpic_timer_wakeup *fsl_wakeup; 28static DEFINE_MUTEX(sysfs_lock); 29 30static void fsl_free_resource(struct work_struct *ws) 31{ 32 struct fsl_mpic_timer_wakeup *wakeup = 33 container_of(ws, struct fsl_mpic_timer_wakeup, free_work); 34 35 mutex_lock(&sysfs_lock); 36 37 if (wakeup->timer) { 38 disable_irq_wake(wakeup->timer->irq); 39 mpic_free_timer(wakeup->timer); 40 } 41 42 wakeup->timer = NULL; 43 mutex_unlock(&sysfs_lock); 44} 45 46static irqreturn_t fsl_mpic_timer_irq(int irq, void *dev_id) 47{ 48 struct fsl_mpic_timer_wakeup *wakeup = dev_id; 49 50 schedule_work(&wakeup->free_work); 51 52 return wakeup->timer ? IRQ_HANDLED : IRQ_NONE; 53} 54 55static ssize_t fsl_timer_wakeup_show(struct device *dev, 56 struct device_attribute *attr, 57 char *buf) 58{ 59 struct timeval interval; 60 int val = 0; 61 62 mutex_lock(&sysfs_lock); 63 if (fsl_wakeup->timer) { 64 mpic_get_remain_time(fsl_wakeup->timer, &interval); 65 val = interval.tv_sec + 1; 66 } 67 mutex_unlock(&sysfs_lock); 68 69 return sprintf(buf, "%d\n", val); 70} 71 72static ssize_t fsl_timer_wakeup_store(struct device *dev, 73 struct device_attribute *attr, 74 const char *buf, 75 size_t count) 76{ 77 struct timeval interval; 78 int ret; 79 80 interval.tv_usec = 0; 81 if (kstrtol(buf, 0, &interval.tv_sec)) 82 return -EINVAL; 83 84 mutex_lock(&sysfs_lock); 85 86 if (fsl_wakeup->timer) { 87 disable_irq_wake(fsl_wakeup->timer->irq); 88 mpic_free_timer(fsl_wakeup->timer); 89 fsl_wakeup->timer = NULL; 90 } 91 92 if (!interval.tv_sec) { 93 mutex_unlock(&sysfs_lock); 94 return count; 95 } 96 97 fsl_wakeup->timer = mpic_request_timer(fsl_mpic_timer_irq, 98 fsl_wakeup, &interval); 99 if (!fsl_wakeup->timer) { 100 mutex_unlock(&sysfs_lock); 101 return -EINVAL; 102 } 103 104 ret = enable_irq_wake(fsl_wakeup->timer->irq); 105 if (ret) { 106 mpic_free_timer(fsl_wakeup->timer); 107 fsl_wakeup->timer = NULL; 108 mutex_unlock(&sysfs_lock); 109 110 return ret; 111 } 112 113 mpic_start_timer(fsl_wakeup->timer); 114 115 mutex_unlock(&sysfs_lock); 116 117 return count; 118} 119 120static struct device_attribute mpic_attributes = __ATTR(timer_wakeup, 0644, 121 fsl_timer_wakeup_show, fsl_timer_wakeup_store); 122 123static int __init fsl_wakeup_sys_init(void) 124{ 125 int ret; 126 127 fsl_wakeup = kzalloc(sizeof(struct fsl_mpic_timer_wakeup), GFP_KERNEL); 128 if (!fsl_wakeup) 129 return -ENOMEM; 130 131 INIT_WORK(&fsl_wakeup->free_work, fsl_free_resource); 132 133 ret = device_create_file(mpic_subsys.dev_root, &mpic_attributes); 134 if (ret) 135 kfree(fsl_wakeup); 136 137 return ret; 138} 139 140static void __exit fsl_wakeup_sys_exit(void) 141{ 142 device_remove_file(mpic_subsys.dev_root, &mpic_attributes); 143 144 mutex_lock(&sysfs_lock); 145 146 if (fsl_wakeup->timer) { 147 disable_irq_wake(fsl_wakeup->timer->irq); 148 mpic_free_timer(fsl_wakeup->timer); 149 } 150 151 kfree(fsl_wakeup); 152 153 mutex_unlock(&sysfs_lock); 154} 155 156module_init(fsl_wakeup_sys_init); 157module_exit(fsl_wakeup_sys_exit); 158 159MODULE_DESCRIPTION("Freescale MPIC global timer wakeup driver"); 160MODULE_LICENSE("GPL v2"); 161MODULE_AUTHOR("Wang Dongsheng <dongsheng.wang@freescale.com>"); 162