projects/linux·blob master · refs

159 lines · 3.3 KiB

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/sysfs.h>
11#include <linux/errno.h>
12#include <linux/module.h>
13#include <linux/interrupt.h>
14#include <linux/device.h>
15
16#include <asm/mpic_timer.h>
17#include <asm/mpic.h>
18
19struct fsl_mpic_timer_wakeup {
20 struct mpic_timer *timer;
21 struct work_struct free_work;
22};
23
24static struct fsl_mpic_timer_wakeup *fsl_wakeup;
25static DEFINE_MUTEX(sysfs_lock);
26
27static void fsl_free_resource(struct work_struct *ws)
28{
29 struct fsl_mpic_timer_wakeup *wakeup =
30 container_of(ws, struct fsl_mpic_timer_wakeup, free_work);
31
32 mutex_lock(&sysfs_lock);
33
34 if (wakeup->timer) {
35 disable_irq_wake(wakeup->timer->irq);
36 mpic_free_timer(wakeup->timer);
37 }
38
39 wakeup->timer = NULL;
40 mutex_unlock(&sysfs_lock);
41}
42
43static irqreturn_t fsl_mpic_timer_irq(int irq, void *dev_id)
44{
45 struct fsl_mpic_timer_wakeup *wakeup = dev_id;
46
47 schedule_work(&wakeup->free_work);
48
49 return wakeup->timer ? IRQ_HANDLED : IRQ_NONE;
50}
51
52static ssize_t fsl_timer_wakeup_show(struct device *dev,
53 struct device_attribute *attr,
54 char *buf)
55{
56 time64_t interval = 0;
57
58 mutex_lock(&sysfs_lock);
59 if (fsl_wakeup->timer) {
60 mpic_get_remain_time(fsl_wakeup->timer, &interval);
61 interval++;
62 }
63 mutex_unlock(&sysfs_lock);
64
65 return sysfs_emit(buf, "%lld\n", interval);
66}
67
68static ssize_t fsl_timer_wakeup_store(struct device *dev,
69 struct device_attribute *attr,
70 const char *buf,
71 size_t count)
72{
73 time64_t interval;
74 int ret;
75
76 if (kstrtoll(buf, 0, &interval))
77 return -EINVAL;
78
79 guard(mutex)(&sysfs_lock);
80
81 if (fsl_wakeup->timer) {
82 disable_irq_wake(fsl_wakeup->timer->irq);
83 mpic_free_timer(fsl_wakeup->timer);
84 fsl_wakeup->timer = NULL;
85 }
86
87 if (!interval)
88 return count;
89
90 fsl_wakeup->timer = mpic_request_timer(fsl_mpic_timer_irq,
91 fsl_wakeup, interval);
92 if (!fsl_wakeup->timer)
93 return -EINVAL;
94
95 ret = enable_irq_wake(fsl_wakeup->timer->irq);
96 if (ret) {
97 mpic_free_timer(fsl_wakeup->timer);
98 fsl_wakeup->timer = NULL;
99 return ret;
100 }
101
102 mpic_start_timer(fsl_wakeup->timer);
103
104 return count;
105}
106
107static struct device_attribute mpic_attributes = __ATTR(timer_wakeup, 0644,
108 fsl_timer_wakeup_show, fsl_timer_wakeup_store);
109
110static int __init fsl_wakeup_sys_init(void)
111{
112 struct device *dev_root;
113 int ret = -EINVAL;
114
115 fsl_wakeup = kzalloc_obj(struct fsl_mpic_timer_wakeup);
116 if (!fsl_wakeup)
117 return -ENOMEM;
118
119 INIT_WORK(&fsl_wakeup->free_work, fsl_free_resource);
120
121 dev_root = bus_get_dev_root(&mpic_subsys);
122 if (dev_root) {
123 ret = device_create_file(dev_root, &mpic_attributes);
124 put_device(dev_root);
125 if (ret)
126 kfree(fsl_wakeup);
127 }
128
129 return ret;
130}
131
132static void __exit fsl_wakeup_sys_exit(void)
133{
134 struct device *dev_root;
135
136 dev_root = bus_get_dev_root(&mpic_subsys);
137 if (dev_root) {
138 device_remove_file(dev_root, &mpic_attributes);
139 put_device(dev_root);
140 }
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 <[email protected]>");