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        time64_t interval = 0;
  60
  61        mutex_lock(&sysfs_lock);
  62        if (fsl_wakeup->timer) {
  63                mpic_get_remain_time(fsl_wakeup->timer, &interval);
  64                interval++;
  65        }
  66        mutex_unlock(&sysfs_lock);
  67
  68        return sprintf(buf, "%lld\n", interval);
  69}
  70
  71static ssize_t fsl_timer_wakeup_store(struct device *dev,
  72                                struct device_attribute *attr,
  73                                const char *buf,
  74                                size_t count)
  75{
  76        time64_t interval;
  77        int ret;
  78
  79        if (kstrtoll(buf, 0, &interval))
  80                return -EINVAL;
  81
  82        mutex_lock(&sysfs_lock);
  83
  84        if (fsl_wakeup->timer) {
  85                disable_irq_wake(fsl_wakeup->timer->irq);
  86                mpic_free_timer(fsl_wakeup->timer);
  87                fsl_wakeup->timer = NULL;
  88        }
  89
  90        if (!interval) {
  91                mutex_unlock(&sysfs_lock);
  92                return count;
  93        }
  94
  95        fsl_wakeup->timer = mpic_request_timer(fsl_mpic_timer_irq,
  96                                                fsl_wakeup, interval);
  97        if (!fsl_wakeup->timer) {
  98                mutex_unlock(&sysfs_lock);
  99                return -EINVAL;
 100        }
 101
 102        ret = enable_irq_wake(fsl_wakeup->timer->irq);
 103        if (ret) {
 104                mpic_free_timer(fsl_wakeup->timer);
 105                fsl_wakeup->timer = NULL;
 106                mutex_unlock(&sysfs_lock);
 107
 108                return ret;
 109        }
 110
 111        mpic_start_timer(fsl_wakeup->timer);
 112
 113        mutex_unlock(&sysfs_lock);
 114
 115        return count;
 116}
 117
 118static struct device_attribute mpic_attributes = __ATTR(timer_wakeup, 0644,
 119                        fsl_timer_wakeup_show, fsl_timer_wakeup_store);
 120
 121static int __init fsl_wakeup_sys_init(void)
 122{
 123        int ret;
 124
 125        fsl_wakeup = kzalloc(sizeof(struct fsl_mpic_timer_wakeup), GFP_KERNEL);
 126        if (!fsl_wakeup)
 127                return -ENOMEM;
 128
 129        INIT_WORK(&fsl_wakeup->free_work, fsl_free_resource);
 130
 131        ret = device_create_file(mpic_subsys.dev_root, &mpic_attributes);
 132        if (ret)
 133                kfree(fsl_wakeup);
 134
 135        return ret;
 136}
 137
 138static void __exit fsl_wakeup_sys_exit(void)
 139{
 140        device_remove_file(mpic_subsys.dev_root, &mpic_attributes);
 141
 142        mutex_lock(&sysfs_lock);
 143
 144        if (fsl_wakeup->timer) {
 145                disable_irq_wake(fsl_wakeup->timer->irq);
 146                mpic_free_timer(fsl_wakeup->timer);
 147        }
 148
 149        kfree(fsl_wakeup);
 150
 151        mutex_unlock(&sysfs_lock);
 152}
 153
 154module_init(fsl_wakeup_sys_init);
 155module_exit(fsl_wakeup_sys_exit);
 156
 157MODULE_DESCRIPTION("Freescale MPIC global timer wakeup driver");
 158MODULE_LICENSE("GPL v2");
 159MODULE_AUTHOR("Wang Dongsheng <dongsheng.wang@freescale.com>");
 160