aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'drivers')
-rw-r--r--drivers/hwspinlock/Kconfig24
-rw-r--r--drivers/hwspinlock/Makefile2
-rw-r--r--drivers/hwspinlock/hwspinlock_core.c79
-rw-r--r--drivers/hwspinlock/omap_hwspinlock.c18
-rw-r--r--drivers/hwspinlock/qcom_hwspinlock.c181
-rw-r--r--drivers/hwspinlock/sirf_hwspinlock.c136
6 files changed, 436 insertions, 4 deletions
diff --git a/drivers/hwspinlock/Kconfig b/drivers/hwspinlock/Kconfig
index 3612cb5b30b2..73a401662853 100644
--- a/drivers/hwspinlock/Kconfig
+++ b/drivers/hwspinlock/Kconfig
@@ -18,6 +18,30 @@ config HWSPINLOCK_OMAP
18 18
19 If unsure, say N. 19 If unsure, say N.
20 20
21config HWSPINLOCK_QCOM
22 tristate "Qualcomm Hardware Spinlock device"
23 depends on ARCH_QCOM
24 select HWSPINLOCK
25 select MFD_SYSCON
26 help
27 Say y here to support the Qualcomm Hardware Mutex functionality, which
28 provides a synchronisation mechanism for the various processors on
29 the SoC.
30
31 If unsure, say N.
32
33config HWSPINLOCK_SIRF
34 tristate "SIRF Hardware Spinlock device"
35 depends on ARCH_SIRF
36 select HWSPINLOCK
37 help
38 Say y here to support the SIRF Hardware Spinlock device, which
39 provides a synchronisation mechanism for the various processors
40 on the SoC.
41
42 It's safe to say n here if you're not interested in SIRF hardware
43 spinlock or just want a bare minimum kernel.
44
21config HSEM_U8500 45config HSEM_U8500
22 tristate "STE Hardware Semaphore functionality" 46 tristate "STE Hardware Semaphore functionality"
23 depends on ARCH_U8500 47 depends on ARCH_U8500
diff --git a/drivers/hwspinlock/Makefile b/drivers/hwspinlock/Makefile
index 93eb64b66486..6b59cb5a4f3a 100644
--- a/drivers/hwspinlock/Makefile
+++ b/drivers/hwspinlock/Makefile
@@ -4,4 +4,6 @@
4 4
5obj-$(CONFIG_HWSPINLOCK) += hwspinlock_core.o 5obj-$(CONFIG_HWSPINLOCK) += hwspinlock_core.o
6obj-$(CONFIG_HWSPINLOCK_OMAP) += omap_hwspinlock.o 6obj-$(CONFIG_HWSPINLOCK_OMAP) += omap_hwspinlock.o
7obj-$(CONFIG_HWSPINLOCK_QCOM) += qcom_hwspinlock.o
8obj-$(CONFIG_HWSPINLOCK_SIRF) += sirf_hwspinlock.o
7obj-$(CONFIG_HSEM_U8500) += u8500_hsem.o 9obj-$(CONFIG_HSEM_U8500) += u8500_hsem.o
diff --git a/drivers/hwspinlock/hwspinlock_core.c b/drivers/hwspinlock/hwspinlock_core.c
index 461a0d739d75..52f708bcf77f 100644
--- a/drivers/hwspinlock/hwspinlock_core.c
+++ b/drivers/hwspinlock/hwspinlock_core.c
@@ -27,6 +27,7 @@
27#include <linux/hwspinlock.h> 27#include <linux/hwspinlock.h>
28#include <linux/pm_runtime.h> 28#include <linux/pm_runtime.h>
29#include <linux/mutex.h> 29#include <linux/mutex.h>
30#include <linux/of.h>
30 31
31#include "hwspinlock_internal.h" 32#include "hwspinlock_internal.h"
32 33
@@ -257,6 +258,84 @@ void __hwspin_unlock(struct hwspinlock *hwlock, int mode, unsigned long *flags)
257} 258}
258EXPORT_SYMBOL_GPL(__hwspin_unlock); 259EXPORT_SYMBOL_GPL(__hwspin_unlock);
259 260
261/**
262 * of_hwspin_lock_simple_xlate - translate hwlock_spec to return a lock id
263 * @bank: the hwspinlock device bank
264 * @hwlock_spec: hwlock specifier as found in the device tree
265 *
266 * This is a simple translation function, suitable for hwspinlock platform
267 * drivers that only has a lock specifier length of 1.
268 *
269 * Returns a relative index of the lock within a specified bank on success,
270 * or -EINVAL on invalid specifier cell count.
271 */
272static inline int
273of_hwspin_lock_simple_xlate(const struct of_phandle_args *hwlock_spec)
274{
275 if (WARN_ON(hwlock_spec->args_count != 1))
276 return -EINVAL;
277
278 return hwlock_spec->args[0];
279}
280
281/**
282 * of_hwspin_lock_get_id() - get lock id for an OF phandle-based specific lock
283 * @np: device node from which to request the specific hwlock
284 * @index: index of the hwlock in the list of values
285 *
286 * This function provides a means for DT users of the hwspinlock module to
287 * get the global lock id of a specific hwspinlock using the phandle of the
288 * hwspinlock device, so that it can be requested using the normal
289 * hwspin_lock_request_specific() API.
290 *
291 * Returns the global lock id number on success, -EPROBE_DEFER if the hwspinlock
292 * device is not yet registered, -EINVAL on invalid args specifier value or an
293 * appropriate error as returned from the OF parsing of the DT client node.
294 */
295int of_hwspin_lock_get_id(struct device_node *np, int index)
296{
297 struct of_phandle_args args;
298 struct hwspinlock *hwlock;
299 struct radix_tree_iter iter;
300 void **slot;
301 int id;
302 int ret;
303
304 ret = of_parse_phandle_with_args(np, "hwlocks", "#hwlock-cells", index,
305 &args);
306 if (ret)
307 return ret;
308
309 /* Find the hwspinlock device: we need its base_id */
310 ret = -EPROBE_DEFER;
311 rcu_read_lock();
312 radix_tree_for_each_slot(slot, &hwspinlock_tree, &iter, 0) {
313 hwlock = radix_tree_deref_slot(slot);
314 if (unlikely(!hwlock))
315 continue;
316
317 if (hwlock->bank->dev->of_node == args.np) {
318 ret = 0;
319 break;
320 }
321 }
322 rcu_read_unlock();
323 if (ret < 0)
324 goto out;
325
326 id = of_hwspin_lock_simple_xlate(&args);
327 if (id < 0 || id >= hwlock->bank->num_locks) {
328 ret = -EINVAL;
329 goto out;
330 }
331 id += hwlock->bank->base_id;
332
333out:
334 of_node_put(args.np);
335 return ret ? ret : id;
336}
337EXPORT_SYMBOL_GPL(of_hwspin_lock_get_id);
338
260static int hwspin_lock_register_single(struct hwspinlock *hwlock, int id) 339static int hwspin_lock_register_single(struct hwspinlock *hwlock, int id)
261{ 340{
262 struct hwspinlock *tmp; 341 struct hwspinlock *tmp;
diff --git a/drivers/hwspinlock/omap_hwspinlock.c b/drivers/hwspinlock/omap_hwspinlock.c
index 47a275c6ece1..ad2f8cac8487 100644
--- a/drivers/hwspinlock/omap_hwspinlock.c
+++ b/drivers/hwspinlock/omap_hwspinlock.c
@@ -1,7 +1,7 @@
1/* 1/*
2 * OMAP hardware spinlock driver 2 * OMAP hardware spinlock driver
3 * 3 *
4 * Copyright (C) 2010 Texas Instruments Incorporated - http://www.ti.com 4 * Copyright (C) 2010-2015 Texas Instruments Incorporated - http://www.ti.com
5 * 5 *
6 * Contact: Simon Que <sque@ti.com> 6 * Contact: Simon Que <sque@ti.com>
7 * Hari Kanigeri <h-kanigeri2@ti.com> 7 * Hari Kanigeri <h-kanigeri2@ti.com>
@@ -27,6 +27,7 @@
27#include <linux/slab.h> 27#include <linux/slab.h>
28#include <linux/spinlock.h> 28#include <linux/spinlock.h>
29#include <linux/hwspinlock.h> 29#include <linux/hwspinlock.h>
30#include <linux/of.h>
30#include <linux/platform_device.h> 31#include <linux/platform_device.h>
31 32
32#include "hwspinlock_internal.h" 33#include "hwspinlock_internal.h"
@@ -80,14 +81,16 @@ static const struct hwspinlock_ops omap_hwspinlock_ops = {
80 81
81static int omap_hwspinlock_probe(struct platform_device *pdev) 82static int omap_hwspinlock_probe(struct platform_device *pdev)
82{ 83{
83 struct hwspinlock_pdata *pdata = pdev->dev.platform_data; 84 struct device_node *node = pdev->dev.of_node;
84 struct hwspinlock_device *bank; 85 struct hwspinlock_device *bank;
85 struct hwspinlock *hwlock; 86 struct hwspinlock *hwlock;
86 struct resource *res; 87 struct resource *res;
87 void __iomem *io_base; 88 void __iomem *io_base;
88 int num_locks, i, ret; 89 int num_locks, i, ret;
90 /* Only a single hwspinlock block device is supported */
91 int base_id = 0;
89 92
90 if (!pdata) 93 if (!node)
91 return -ENODEV; 94 return -ENODEV;
92 95
93 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 96 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
@@ -141,7 +144,7 @@ static int omap_hwspinlock_probe(struct platform_device *pdev)
141 hwlock->priv = io_base + LOCK_BASE_OFFSET + sizeof(u32) * i; 144 hwlock->priv = io_base + LOCK_BASE_OFFSET + sizeof(u32) * i;
142 145
143 ret = hwspin_lock_register(bank, &pdev->dev, &omap_hwspinlock_ops, 146 ret = hwspin_lock_register(bank, &pdev->dev, &omap_hwspinlock_ops,
144 pdata->base_id, num_locks); 147 base_id, num_locks);
145 if (ret) 148 if (ret)
146 goto reg_fail; 149 goto reg_fail;
147 150
@@ -174,11 +177,18 @@ static int omap_hwspinlock_remove(struct platform_device *pdev)
174 return 0; 177 return 0;
175} 178}
176 179
180static const struct of_device_id omap_hwspinlock_of_match[] = {
181 { .compatible = "ti,omap4-hwspinlock", },
182 { /* end */ },
183};
184MODULE_DEVICE_TABLE(of, omap_hwspinlock_of_match);
185
177static struct platform_driver omap_hwspinlock_driver = { 186static struct platform_driver omap_hwspinlock_driver = {
178 .probe = omap_hwspinlock_probe, 187 .probe = omap_hwspinlock_probe,
179 .remove = omap_hwspinlock_remove, 188 .remove = omap_hwspinlock_remove,
180 .driver = { 189 .driver = {
181 .name = "omap_hwspinlock", 190 .name = "omap_hwspinlock",
191 .of_match_table = of_match_ptr(omap_hwspinlock_of_match),
182 }, 192 },
183}; 193};
184 194
diff --git a/drivers/hwspinlock/qcom_hwspinlock.c b/drivers/hwspinlock/qcom_hwspinlock.c
new file mode 100644
index 000000000000..c752447fbac7
--- /dev/null
+++ b/drivers/hwspinlock/qcom_hwspinlock.c
@@ -0,0 +1,181 @@
1/*
2 * Copyright (c) 2013, The Linux Foundation. All rights reserved.
3 * Copyright (c) 2015, Sony Mobile Communications AB
4 *
5 * This software is licensed under the terms of the GNU General Public
6 * License version 2, as published by the Free Software Foundation, and
7 * may be copied, distributed, and modified under those terms.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 */
14
15#include <linux/hwspinlock.h>
16#include <linux/io.h>
17#include <linux/kernel.h>
18#include <linux/mfd/syscon.h>
19#include <linux/module.h>
20#include <linux/of.h>
21#include <linux/of_device.h>
22#include <linux/platform_device.h>
23#include <linux/pm_runtime.h>
24#include <linux/regmap.h>
25
26#include "hwspinlock_internal.h"
27
28#define QCOM_MUTEX_APPS_PROC_ID 1
29#define QCOM_MUTEX_NUM_LOCKS 32
30
31static int qcom_hwspinlock_trylock(struct hwspinlock *lock)
32{
33 struct regmap_field *field = lock->priv;
34 u32 lock_owner;
35 int ret;
36
37 ret = regmap_field_write(field, QCOM_MUTEX_APPS_PROC_ID);
38 if (ret)
39 return ret;
40
41 ret = regmap_field_read(field, &lock_owner);
42 if (ret)
43 return ret;
44
45 return lock_owner == QCOM_MUTEX_APPS_PROC_ID;
46}
47
48static void qcom_hwspinlock_unlock(struct hwspinlock *lock)
49{
50 struct regmap_field *field = lock->priv;
51 u32 lock_owner;
52 int ret;
53
54 ret = regmap_field_read(field, &lock_owner);
55 if (ret) {
56 pr_err("%s: unable to query spinlock owner\n", __func__);
57 return;
58 }
59
60 if (lock_owner != QCOM_MUTEX_APPS_PROC_ID) {
61 pr_err("%s: spinlock not owned by us (actual owner is %d)\n",
62 __func__, lock_owner);
63 }
64
65 ret = regmap_field_write(field, 0);
66 if (ret)
67 pr_err("%s: failed to unlock spinlock\n", __func__);
68}
69
70static const struct hwspinlock_ops qcom_hwspinlock_ops = {
71 .trylock = qcom_hwspinlock_trylock,
72 .unlock = qcom_hwspinlock_unlock,
73};
74
75static const struct of_device_id qcom_hwspinlock_of_match[] = {
76 { .compatible = "qcom,sfpb-mutex" },
77 { .compatible = "qcom,tcsr-mutex" },
78 { }
79};
80MODULE_DEVICE_TABLE(of, qcom_hwspinlock_of_match);
81
82static int qcom_hwspinlock_probe(struct platform_device *pdev)
83{
84 struct hwspinlock_device *bank;
85 struct device_node *syscon;
86 struct reg_field field;
87 struct regmap *regmap;
88 size_t array_size;
89 u32 stride;
90 u32 base;
91 int ret;
92 int i;
93
94 syscon = of_parse_phandle(pdev->dev.of_node, "syscon", 0);
95 if (!syscon) {
96 dev_err(&pdev->dev, "no syscon property\n");
97 return -ENODEV;
98 }
99
100 regmap = syscon_node_to_regmap(syscon);
101 if (IS_ERR(regmap))
102 return PTR_ERR(regmap);
103
104 ret = of_property_read_u32_index(pdev->dev.of_node, "syscon", 1, &base);
105 if (ret < 0) {
106 dev_err(&pdev->dev, "no offset in syscon\n");
107 return -EINVAL;
108 }
109
110 ret = of_property_read_u32_index(pdev->dev.of_node, "syscon", 2, &stride);
111 if (ret < 0) {
112 dev_err(&pdev->dev, "no stride syscon\n");
113 return -EINVAL;
114 }
115
116 array_size = QCOM_MUTEX_NUM_LOCKS * sizeof(struct hwspinlock);
117 bank = devm_kzalloc(&pdev->dev, sizeof(*bank) + array_size, GFP_KERNEL);
118 if (!bank)
119 return -ENOMEM;
120
121 platform_set_drvdata(pdev, bank);
122
123 for (i = 0; i < QCOM_MUTEX_NUM_LOCKS; i++) {
124 field.reg = base + i * stride;
125 field.lsb = 0;
126 field.msb = 31;
127
128 bank->lock[i].priv = devm_regmap_field_alloc(&pdev->dev,
129 regmap, field);
130 }
131
132 pm_runtime_enable(&pdev->dev);
133
134 ret = hwspin_lock_register(bank, &pdev->dev, &qcom_hwspinlock_ops,
135 0, QCOM_MUTEX_NUM_LOCKS);
136 if (ret)
137 pm_runtime_disable(&pdev->dev);
138
139 return ret;
140}
141
142static int qcom_hwspinlock_remove(struct platform_device *pdev)
143{
144 struct hwspinlock_device *bank = platform_get_drvdata(pdev);
145 int ret;
146
147 ret = hwspin_lock_unregister(bank);
148 if (ret) {
149 dev_err(&pdev->dev, "%s failed: %d\n", __func__, ret);
150 return ret;
151 }
152
153 pm_runtime_disable(&pdev->dev);
154
155 return 0;
156}
157
158static struct platform_driver qcom_hwspinlock_driver = {
159 .probe = qcom_hwspinlock_probe,
160 .remove = qcom_hwspinlock_remove,
161 .driver = {
162 .name = "qcom_hwspinlock",
163 .of_match_table = qcom_hwspinlock_of_match,
164 },
165};
166
167static int __init qcom_hwspinlock_init(void)
168{
169 return platform_driver_register(&qcom_hwspinlock_driver);
170}
171/* board init code might need to reserve hwspinlocks for predefined purposes */
172postcore_initcall(qcom_hwspinlock_init);
173
174static void __exit qcom_hwspinlock_exit(void)
175{
176 platform_driver_unregister(&qcom_hwspinlock_driver);
177}
178module_exit(qcom_hwspinlock_exit);
179
180MODULE_LICENSE("GPL v2");
181MODULE_DESCRIPTION("Hardware spinlock driver for Qualcomm SoCs");
diff --git a/drivers/hwspinlock/sirf_hwspinlock.c b/drivers/hwspinlock/sirf_hwspinlock.c
new file mode 100644
index 000000000000..16018544d431
--- /dev/null
+++ b/drivers/hwspinlock/sirf_hwspinlock.c
@@ -0,0 +1,136 @@
1/*
2 * SIRF hardware spinlock driver
3 *
4 * Copyright (c) 2015 Cambridge Silicon Radio Limited, a CSR plc group company.
5 *
6 * Licensed under GPLv2.
7 */
8
9#include <linux/kernel.h>
10#include <linux/module.h>
11#include <linux/device.h>
12#include <linux/io.h>
13#include <linux/pm_runtime.h>
14#include <linux/slab.h>
15#include <linux/spinlock.h>
16#include <linux/hwspinlock.h>
17#include <linux/platform_device.h>
18#include <linux/of.h>
19#include <linux/of_address.h>
20
21#include "hwspinlock_internal.h"
22
23struct sirf_hwspinlock {
24 void __iomem *io_base;
25 struct hwspinlock_device bank;
26};
27
28/* Number of Hardware Spinlocks*/
29#define HW_SPINLOCK_NUMBER 30
30
31/* Hardware spinlock register offsets */
32#define HW_SPINLOCK_BASE 0x404
33#define HW_SPINLOCK_OFFSET(x) (HW_SPINLOCK_BASE + 0x4 * (x))
34
35static int sirf_hwspinlock_trylock(struct hwspinlock *lock)
36{
37 void __iomem *lock_addr = lock->priv;
38
39 /* attempt to acquire the lock by reading value == 1 from it */
40 return !!readl(lock_addr);
41}
42
43static void sirf_hwspinlock_unlock(struct hwspinlock *lock)
44{
45 void __iomem *lock_addr = lock->priv;
46
47 /* release the lock by writing 0 to it */
48 writel(0, lock_addr);
49}
50
51static const struct hwspinlock_ops sirf_hwspinlock_ops = {
52 .trylock = sirf_hwspinlock_trylock,
53 .unlock = sirf_hwspinlock_unlock,
54};
55
56static int sirf_hwspinlock_probe(struct platform_device *pdev)
57{
58 struct sirf_hwspinlock *hwspin;
59 struct hwspinlock *hwlock;
60 int idx, ret;
61
62 if (!pdev->dev.of_node)
63 return -ENODEV;
64
65 hwspin = devm_kzalloc(&pdev->dev, sizeof(*hwspin) +
66 sizeof(*hwlock) * HW_SPINLOCK_NUMBER, GFP_KERNEL);
67 if (!hwspin)
68 return -ENOMEM;
69
70 /* retrieve io base */
71 hwspin->io_base = of_iomap(pdev->dev.of_node, 0);
72 if (!hwspin->io_base)
73 return -ENOMEM;
74
75 for (idx = 0; idx < HW_SPINLOCK_NUMBER; idx++) {
76 hwlock = &hwspin->bank.lock[idx];
77 hwlock->priv = hwspin->io_base + HW_SPINLOCK_OFFSET(idx);
78 }
79
80 platform_set_drvdata(pdev, hwspin);
81
82 pm_runtime_enable(&pdev->dev);
83
84 ret = hwspin_lock_register(&hwspin->bank, &pdev->dev,
85 &sirf_hwspinlock_ops, 0,
86 HW_SPINLOCK_NUMBER);
87 if (ret)
88 goto reg_failed;
89
90 return 0;
91
92reg_failed:
93 pm_runtime_disable(&pdev->dev);
94 iounmap(hwspin->io_base);
95
96 return ret;
97}
98
99static int sirf_hwspinlock_remove(struct platform_device *pdev)
100{
101 struct sirf_hwspinlock *hwspin = platform_get_drvdata(pdev);
102 int ret;
103
104 ret = hwspin_lock_unregister(&hwspin->bank);
105 if (ret) {
106 dev_err(&pdev->dev, "%s failed: %d\n", __func__, ret);
107 return ret;
108 }
109
110 pm_runtime_disable(&pdev->dev);
111
112 iounmap(hwspin->io_base);
113
114 return 0;
115}
116
117static const struct of_device_id sirf_hwpinlock_ids[] = {
118 { .compatible = "sirf,hwspinlock", },
119 {},
120};
121MODULE_DEVICE_TABLE(of, sirf_hwpinlock_ids);
122
123static struct platform_driver sirf_hwspinlock_driver = {
124 .probe = sirf_hwspinlock_probe,
125 .remove = sirf_hwspinlock_remove,
126 .driver = {
127 .name = "atlas7_hwspinlock",
128 .of_match_table = of_match_ptr(sirf_hwpinlock_ids),
129 },
130};
131
132module_platform_driver(sirf_hwspinlock_driver);
133
134MODULE_LICENSE("GPL v2");
135MODULE_DESCRIPTION("SIRF Hardware spinlock driver");
136MODULE_AUTHOR("Wei Chen <wei.chen@csr.com>");