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