0001
0002
0003
0004
0005
0006
0007
0008 #include <linux/kernel.h>
0009 #include <linux/slab.h>
0010 #include <linux/errno.h>
0011 #include <linux/module.h>
0012 #include <linux/interrupt.h>
0013 #include <linux/device.h>
0014
0015 #include <asm/mpic_timer.h>
0016 #include <asm/mpic.h>
0017
0018 struct fsl_mpic_timer_wakeup {
0019 struct mpic_timer *timer;
0020 struct work_struct free_work;
0021 };
0022
0023 static struct fsl_mpic_timer_wakeup *fsl_wakeup;
0024 static DEFINE_MUTEX(sysfs_lock);
0025
0026 static void fsl_free_resource(struct work_struct *ws)
0027 {
0028 struct fsl_mpic_timer_wakeup *wakeup =
0029 container_of(ws, struct fsl_mpic_timer_wakeup, free_work);
0030
0031 mutex_lock(&sysfs_lock);
0032
0033 if (wakeup->timer) {
0034 disable_irq_wake(wakeup->timer->irq);
0035 mpic_free_timer(wakeup->timer);
0036 }
0037
0038 wakeup->timer = NULL;
0039 mutex_unlock(&sysfs_lock);
0040 }
0041
0042 static irqreturn_t fsl_mpic_timer_irq(int irq, void *dev_id)
0043 {
0044 struct fsl_mpic_timer_wakeup *wakeup = dev_id;
0045
0046 schedule_work(&wakeup->free_work);
0047
0048 return wakeup->timer ? IRQ_HANDLED : IRQ_NONE;
0049 }
0050
0051 static ssize_t fsl_timer_wakeup_show(struct device *dev,
0052 struct device_attribute *attr,
0053 char *buf)
0054 {
0055 time64_t interval = 0;
0056
0057 mutex_lock(&sysfs_lock);
0058 if (fsl_wakeup->timer) {
0059 mpic_get_remain_time(fsl_wakeup->timer, &interval);
0060 interval++;
0061 }
0062 mutex_unlock(&sysfs_lock);
0063
0064 return sprintf(buf, "%lld\n", interval);
0065 }
0066
0067 static ssize_t fsl_timer_wakeup_store(struct device *dev,
0068 struct device_attribute *attr,
0069 const char *buf,
0070 size_t count)
0071 {
0072 time64_t interval;
0073 int ret;
0074
0075 if (kstrtoll(buf, 0, &interval))
0076 return -EINVAL;
0077
0078 mutex_lock(&sysfs_lock);
0079
0080 if (fsl_wakeup->timer) {
0081 disable_irq_wake(fsl_wakeup->timer->irq);
0082 mpic_free_timer(fsl_wakeup->timer);
0083 fsl_wakeup->timer = NULL;
0084 }
0085
0086 if (!interval) {
0087 mutex_unlock(&sysfs_lock);
0088 return count;
0089 }
0090
0091 fsl_wakeup->timer = mpic_request_timer(fsl_mpic_timer_irq,
0092 fsl_wakeup, interval);
0093 if (!fsl_wakeup->timer) {
0094 mutex_unlock(&sysfs_lock);
0095 return -EINVAL;
0096 }
0097
0098 ret = enable_irq_wake(fsl_wakeup->timer->irq);
0099 if (ret) {
0100 mpic_free_timer(fsl_wakeup->timer);
0101 fsl_wakeup->timer = NULL;
0102 mutex_unlock(&sysfs_lock);
0103
0104 return ret;
0105 }
0106
0107 mpic_start_timer(fsl_wakeup->timer);
0108
0109 mutex_unlock(&sysfs_lock);
0110
0111 return count;
0112 }
0113
0114 static struct device_attribute mpic_attributes = __ATTR(timer_wakeup, 0644,
0115 fsl_timer_wakeup_show, fsl_timer_wakeup_store);
0116
0117 static int __init fsl_wakeup_sys_init(void)
0118 {
0119 int ret;
0120
0121 fsl_wakeup = kzalloc(sizeof(struct fsl_mpic_timer_wakeup), GFP_KERNEL);
0122 if (!fsl_wakeup)
0123 return -ENOMEM;
0124
0125 INIT_WORK(&fsl_wakeup->free_work, fsl_free_resource);
0126
0127 ret = device_create_file(mpic_subsys.dev_root, &mpic_attributes);
0128 if (ret)
0129 kfree(fsl_wakeup);
0130
0131 return ret;
0132 }
0133
0134 static void __exit fsl_wakeup_sys_exit(void)
0135 {
0136 device_remove_file(mpic_subsys.dev_root, &mpic_attributes);
0137
0138 mutex_lock(&sysfs_lock);
0139
0140 if (fsl_wakeup->timer) {
0141 disable_irq_wake(fsl_wakeup->timer->irq);
0142 mpic_free_timer(fsl_wakeup->timer);
0143 }
0144
0145 kfree(fsl_wakeup);
0146
0147 mutex_unlock(&sysfs_lock);
0148 }
0149
0150 module_init(fsl_wakeup_sys_init);
0151 module_exit(fsl_wakeup_sys_exit);
0152
0153 MODULE_DESCRIPTION("Freescale MPIC global timer wakeup driver");
0154 MODULE_LICENSE("GPL v2");
0155 MODULE_AUTHOR("Wang Dongsheng <dongsheng.wang@freescale.com>");