aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMisael Lopez Cruz2015-08-13 11:35:11 -0500
committerMisael Lopez Cruz2016-05-27 16:56:06 -0500
commit2ad1e8fa433dbc77fdb595f415e91582e78f3b27 (patch)
treee1b1cf2b440b131dcea4576d3f157738049171d4
parent84da4f00605a3250b5b5f30fd226e12f30673f0a (diff)
downloadkernel-audio-2ad1e8fa433dbc77fdb595f415e91582e78f3b27.tar.gz
kernel-audio-2ad1e8fa433dbc77fdb595f415e91582e78f3b27.tar.xz
kernel-audio-2ad1e8fa433dbc77fdb595f415e91582e78f3b27.zip
hwspinlock: user: Add a miscdev for userspace access
The hwspinlock_user driver provides an interface for user-space applications to lock and unlock hwspinlocks that were previously reserved through device-tree. Change-Id: I82bbeb9b22f8b6f2be7b8541f877b9a90049fc1e Signed-off-by: Misael Lopez Cruz <misael.lopez@ti.com>
-rw-r--r--drivers/hwspinlock/Kconfig9
-rw-r--r--drivers/hwspinlock/Makefile1
-rw-r--r--drivers/hwspinlock/hwspinlock_user.c231
-rw-r--r--include/uapi/linux/hwspinlock_user.h63
4 files changed, 304 insertions, 0 deletions
diff --git a/drivers/hwspinlock/Kconfig b/drivers/hwspinlock/Kconfig
index 73a401662853..7980d5082197 100644
--- a/drivers/hwspinlock/Kconfig
+++ b/drivers/hwspinlock/Kconfig
@@ -8,6 +8,15 @@ config HWSPINLOCK
8 8
9menu "Hardware Spinlock drivers" 9menu "Hardware Spinlock drivers"
10 10
11config HWSPINLOCK_USER
12 tristate "Hardware Spinlock User-Space Interface"
13 depends on HWSPINLOCK
14 help
15 Say y here to have an user-space interface for the hardware spinlock
16 functionality.
17
18 If unsure, say N.
19
11config HWSPINLOCK_OMAP 20config HWSPINLOCK_OMAP
12 tristate "OMAP Hardware Spinlock device" 21 tristate "OMAP Hardware Spinlock device"
13 depends on ARCH_OMAP4 || SOC_OMAP5 || SOC_DRA7XX || SOC_AM33XX || SOC_AM43XX 22 depends on ARCH_OMAP4 || SOC_OMAP5 || SOC_DRA7XX || SOC_AM33XX || SOC_AM43XX
diff --git a/drivers/hwspinlock/Makefile b/drivers/hwspinlock/Makefile
index 6b59cb5a4f3a..1407cbad4df0 100644
--- a/drivers/hwspinlock/Makefile
+++ b/drivers/hwspinlock/Makefile
@@ -3,6 +3,7 @@
3# 3#
4 4
5obj-$(CONFIG_HWSPINLOCK) += hwspinlock_core.o 5obj-$(CONFIG_HWSPINLOCK) += hwspinlock_core.o
6obj-$(CONFIG_HWSPINLOCK_USER) += hwspinlock_user.o
6obj-$(CONFIG_HWSPINLOCK_OMAP) += omap_hwspinlock.o 7obj-$(CONFIG_HWSPINLOCK_OMAP) += omap_hwspinlock.o
7obj-$(CONFIG_HWSPINLOCK_QCOM) += qcom_hwspinlock.o 8obj-$(CONFIG_HWSPINLOCK_QCOM) += qcom_hwspinlock.o
8obj-$(CONFIG_HWSPINLOCK_SIRF) += sirf_hwspinlock.o 9obj-$(CONFIG_HWSPINLOCK_SIRF) += sirf_hwspinlock.o
diff --git a/drivers/hwspinlock/hwspinlock_user.c b/drivers/hwspinlock/hwspinlock_user.c
new file mode 100644
index 000000000000..327c226b68f1
--- /dev/null
+++ b/drivers/hwspinlock/hwspinlock_user.c
@@ -0,0 +1,231 @@
1/*
2 * Userspace interface to hardware spinlocks
3 *
4 * Copyright (C) 2015 Texas Instruments Incorporated - http://www.ti.com
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * version 2 as published by the Free Software Foundation.
9 *
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * General Public License for more details.
14 */
15
16#include <linux/kernel.h>
17#include <linux/module.h>
18#include <linux/device.h>
19#include <linux/hwspinlock.h>
20#include <linux/of_device.h>
21#include <linux/platform_device.h>
22#include <linux/uaccess.h>
23#include <linux/fs.h>
24#include <uapi/linux/hwspinlock_user.h>
25
26#include <linux/miscdevice.h>
27
28struct hwlock {
29 struct hwspinlock *hwlock;
30 struct file *owner;
31};
32
33struct hwspinlock_user {
34 struct device *dev;
35 struct mutex mutex;
36
37 int num_locks;
38 struct hwlock locks[0];
39};
40
41struct hwspinlock_user *user;
42
43static long hwspinlock_user_ioctl(struct file *filp, unsigned int cmd,
44 unsigned long arg)
45{
46 struct hwspinlock *hwlock = NULL;
47 union {
48 struct hwspinlock_user_lock lock;
49 struct hwspinlock_user_unlock unlock;
50 } data;
51 int i, id, ret = 0;
52
53 if (copy_from_user(&data, (void __user *)arg, _IOC_SIZE(cmd)))
54 return -EFAULT;
55
56 mutex_unlock(&user->mutex);
57
58 switch (cmd) {
59 case HWSPINLOCK_USER_LOCK:
60 for (i = 0; i < user->num_locks; i++) {
61 id = hwspin_lock_get_id(user->locks[i].hwlock);
62 if (id == data.lock.id) {
63 hwlock = user->locks[i].hwlock;
64 break;
65 }
66 }
67
68 if (hwlock) {
69 ret = hwspin_lock_timeout_can_sleep(hwlock,
70 data.lock.timeout);
71 if (!ret)
72 user->locks[i].owner = filp;
73 } else {
74 dev_err(user->dev, "hwspinlock %d is not reserved\n",
75 data.lock.id);
76 ret = -EINVAL;
77 }
78 break;
79
80 case HWSPINLOCK_USER_UNLOCK:
81 for (i = 0; i < user->num_locks; i++) {
82 id = hwspin_lock_get_id(user->locks[i].hwlock);
83 if (id == data.unlock.id) {
84 hwlock = user->locks[i].hwlock;
85 break;
86 }
87 }
88
89 if (hwlock) {
90 hwspin_unlock_can_sleep(hwlock);
91 user->locks[i].owner = NULL;
92 } else {
93 dev_err(user->dev, "hwspinlock %d is not reserved\n",
94 data.unlock.id);
95 ret = -EINVAL;
96 }
97 break;
98
99 default:
100 ret = -ENOTTY;
101 break;
102 }
103
104 mutex_unlock(&user->mutex);
105
106 return ret;
107}
108
109static int hwspinlock_user_release(struct inode *inode, struct file *filp)
110{
111 int i;
112
113 mutex_lock(&user->mutex);
114
115 for (i = 0; i < user->num_locks; i++) {
116 if (user->locks[i].owner == filp) {
117 dev_warn(user->dev,
118 "hwspinlock %d is forcefully unlocked\n",
119 hwspin_lock_get_id(user->locks[i].hwlock));
120 hwspin_unlock_can_sleep(user->locks[i].hwlock);
121 }
122 }
123
124 mutex_unlock(&user->mutex);
125
126 return 0;
127}
128
129
130static const struct file_operations hwspinlock_user_fops = {
131 .owner = THIS_MODULE,
132 .unlocked_ioctl = hwspinlock_user_ioctl,
133 .release = hwspinlock_user_release,
134 .llseek = noop_llseek,
135};
136
137static struct miscdevice hwspinlock_user_miscdev = {
138 .minor = MISC_DYNAMIC_MINOR,
139 .name = "hwspinlock",
140 .fops = &hwspinlock_user_fops,
141};
142
143static int hwspinlock_user_probe(struct platform_device *pdev)
144{
145 struct device_node *node = pdev->dev.of_node;
146 struct hwspinlock *hwlock;
147 int num, id, i;
148 int ret;
149
150 if (!node)
151 return -ENODEV;
152
153 num = of_count_phandle_with_args(node, "hwlocks", "#hwlock-cells");
154
155 user = devm_kzalloc(&pdev->dev, sizeof(struct hwspinlock_user) +
156 num * sizeof(struct hwlock), GFP_KERNEL);
157 if (!user)
158 return -ENOMEM;
159
160 user->dev = &pdev->dev;
161 user->num_locks = num;
162 mutex_init(&user->mutex);
163
164 ret = misc_register(&hwspinlock_user_miscdev);
165 if (ret) {
166 dev_err(user->dev, "failed to register miscdev %d\n", ret);
167 return ret;
168 }
169
170 for (i = 0; i < user->num_locks; i++) {
171 id = of_hwspin_lock_get_id(node, i);
172 if (id < 0) {
173 dev_err(user->dev, "failed to get lock id %d\n", id);
174 ret = -ENODEV;
175 goto err;
176 }
177
178 hwlock = hwspin_lock_request_specific(id);
179 if (IS_ERR_OR_NULL(hwlock)) {
180 dev_err(user->dev, "failed to request lock %d\n", id);
181 ret = IS_ERR(hwlock) ? PTR_ERR(hwlock) : -EBUSY;
182 goto err;
183 }
184
185 user->locks[i].hwlock = hwlock;
186 }
187
188 dev_info(user->dev, "requested %d hwspinlocks\n", i);
189
190 platform_set_drvdata(pdev, user);
191
192 return 0;
193
194err:
195 misc_deregister(&hwspinlock_user_miscdev);
196 for (i--; i >= 0; i--)
197 hwspin_lock_free(user->locks[i].hwlock);
198
199 return ret;
200}
201
202static int hwspinlock_user_remove(struct platform_device *pdev)
203{
204 struct hwspinlock_user *user = platform_get_drvdata(pdev);
205 int i;
206
207 misc_deregister(&hwspinlock_user_miscdev);
208
209 for (i = 0; i < user->num_locks; i++)
210 hwspin_lock_free(user->locks[i].hwlock);
211
212 return 0;
213}
214
215static const struct of_device_id hwspinlock_user_of_match[] = {
216 { .compatible = "hwspinlock-user", },
217 { /* end */ },
218};
219MODULE_DEVICE_TABLE(of, hwspinlock_user_of_match);
220
221static struct platform_driver hwspinlock_user_driver = {
222 .probe = hwspinlock_user_probe,
223 .remove = hwspinlock_user_remove,
224 .driver = {
225 .name = "hwspinlock_user",
226 .owner = THIS_MODULE,
227 .of_match_table = of_match_ptr(hwspinlock_user_of_match),
228 },
229};
230
231module_platform_driver(hwspinlock_user_driver);
diff --git a/include/uapi/linux/hwspinlock_user.h b/include/uapi/linux/hwspinlock_user.h
new file mode 100644
index 000000000000..68f3dff70c7f
--- /dev/null
+++ b/include/uapi/linux/hwspinlock_user.h
@@ -0,0 +1,63 @@
1/*
2 * Userspace interface to hardware spinlocks
3 *
4 * Copyright (C) 2015 Texas Instruments Incorporated - http://www.ti.com
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 *
10 * * Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * * Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in
14 * the documentation and/or other materials provided with the
15 * distribution.
16 * * Neither the name Texas Instruments nor the names of its
17 * contributors may be used to endorse or promote products derived
18 * from this software without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 */
32
33#ifndef _UAPI_HWSPINLOCK_USER_H
34#define _UAPI_HWSPINLOCK_USER_H
35
36#include <linux/ioctl.h>
37
38/**
39 * struct hwspinlock_user_lock - Sent to lock a specific hwspinlock
40 * @id: hardware spinlock id
41 * @timeout: timeout value in msecs
42 */
43struct hwspinlock_user_lock {
44 int id;
45 unsigned int timeout;
46};
47
48/**
49 * struct hwspinlock_user_unlock - Sent to unlock a specific hwspinlock
50 * @id: hardware spinlock id
51 */
52struct hwspinlock_user_unlock {
53 int id;
54};
55
56#define HWSPINLOCK_IOC_MAGIC 'h'
57
58#define HWSPINLOCK_USER_LOCK _IOW(HWSPINLOCK_IOC_MAGIC, 0, \
59 struct hwspinlock_user_lock)
60#define HWSPINLOCK_USER_UNLOCK _IOW(HWSPINLOCK_IOC_MAGIC, 1, \
61 struct hwspinlock_user_unlock)
62
63#endif /* _UAPI_HWSPINLOCK_USER_H */