linux/arch/powerpc/sysdev/fsl_mpic_timer_wakeup.c
<<
>>
Prefs
   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