diff options
author | Misael Lopez Cruz | 2015-08-13 11:35:11 -0500 |
---|---|---|
committer | Misael Lopez Cruz | 2016-05-27 16:56:06 -0500 |
commit | 2ad1e8fa433dbc77fdb595f415e91582e78f3b27 (patch) | |
tree | e1b1cf2b440b131dcea4576d3f157738049171d4 | |
parent | 84da4f00605a3250b5b5f30fd226e12f30673f0a (diff) | |
download | kernel-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/Kconfig | 9 | ||||
-rw-r--r-- | drivers/hwspinlock/Makefile | 1 | ||||
-rw-r--r-- | drivers/hwspinlock/hwspinlock_user.c | 231 | ||||
-rw-r--r-- | include/uapi/linux/hwspinlock_user.h | 63 |
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 | ||
9 | menu "Hardware Spinlock drivers" | 9 | menu "Hardware Spinlock drivers" |
10 | 10 | ||
11 | config 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 | |||
11 | config HWSPINLOCK_OMAP | 20 | config 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 | ||
5 | obj-$(CONFIG_HWSPINLOCK) += hwspinlock_core.o | 5 | obj-$(CONFIG_HWSPINLOCK) += hwspinlock_core.o |
6 | obj-$(CONFIG_HWSPINLOCK_USER) += hwspinlock_user.o | ||
6 | obj-$(CONFIG_HWSPINLOCK_OMAP) += omap_hwspinlock.o | 7 | obj-$(CONFIG_HWSPINLOCK_OMAP) += omap_hwspinlock.o |
7 | obj-$(CONFIG_HWSPINLOCK_QCOM) += qcom_hwspinlock.o | 8 | obj-$(CONFIG_HWSPINLOCK_QCOM) += qcom_hwspinlock.o |
8 | obj-$(CONFIG_HWSPINLOCK_SIRF) += sirf_hwspinlock.o | 9 | obj-$(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 | |||
28 | struct hwlock { | ||
29 | struct hwspinlock *hwlock; | ||
30 | struct file *owner; | ||
31 | }; | ||
32 | |||
33 | struct hwspinlock_user { | ||
34 | struct device *dev; | ||
35 | struct mutex mutex; | ||
36 | |||
37 | int num_locks; | ||
38 | struct hwlock locks[0]; | ||
39 | }; | ||
40 | |||
41 | struct hwspinlock_user *user; | ||
42 | |||
43 | static 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 | |||
109 | static 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 | |||
130 | static 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 | |||
137 | static struct miscdevice hwspinlock_user_miscdev = { | ||
138 | .minor = MISC_DYNAMIC_MINOR, | ||
139 | .name = "hwspinlock", | ||
140 | .fops = &hwspinlock_user_fops, | ||
141 | }; | ||
142 | |||
143 | static 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 | |||
194 | err: | ||
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 | |||
202 | static 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 | |||
215 | static const struct of_device_id hwspinlock_user_of_match[] = { | ||
216 | { .compatible = "hwspinlock-user", }, | ||
217 | { /* end */ }, | ||
218 | }; | ||
219 | MODULE_DEVICE_TABLE(of, hwspinlock_user_of_match); | ||
220 | |||
221 | static 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 | |||
231 | module_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 | */ | ||
43 | struct 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 | */ | ||
52 | struct 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 */ | ||