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