blob: f0da544b14d25fd54ee4a4e8b62d183f7ac57b9a [file] [log] [blame]
Suman Anna8ba01ca2018-05-11 12:03:18 -05001// SPDX-License-Identifier: GPL-2.0
Bjorn Andersson19a0f612015-03-24 10:11:05 -07002/*
3 * Copyright (c) 2013, The Linux Foundation. All rights reserved.
4 * Copyright (c) 2015, Sony Mobile Communications AB
Bjorn Andersson19a0f612015-03-24 10:11:05 -07005 */
6
7#include <linux/hwspinlock.h>
8#include <linux/io.h>
9#include <linux/kernel.h>
10#include <linux/mfd/syscon.h>
11#include <linux/module.h>
12#include <linux/of.h>
13#include <linux/of_device.h>
14#include <linux/platform_device.h>
Bjorn Andersson19a0f612015-03-24 10:11:05 -070015#include <linux/regmap.h>
16
17#include "hwspinlock_internal.h"
18
19#define QCOM_MUTEX_APPS_PROC_ID 1
20#define QCOM_MUTEX_NUM_LOCKS 32
21
22static int qcom_hwspinlock_trylock(struct hwspinlock *lock)
23{
24 struct regmap_field *field = lock->priv;
25 u32 lock_owner;
26 int ret;
27
28 ret = regmap_field_write(field, QCOM_MUTEX_APPS_PROC_ID);
29 if (ret)
30 return ret;
31
32 ret = regmap_field_read(field, &lock_owner);
33 if (ret)
34 return ret;
35
36 return lock_owner == QCOM_MUTEX_APPS_PROC_ID;
37}
38
39static void qcom_hwspinlock_unlock(struct hwspinlock *lock)
40{
41 struct regmap_field *field = lock->priv;
42 u32 lock_owner;
43 int ret;
44
45 ret = regmap_field_read(field, &lock_owner);
46 if (ret) {
47 pr_err("%s: unable to query spinlock owner\n", __func__);
48 return;
49 }
50
51 if (lock_owner != QCOM_MUTEX_APPS_PROC_ID) {
52 pr_err("%s: spinlock not owned by us (actual owner is %d)\n",
53 __func__, lock_owner);
54 }
55
56 ret = regmap_field_write(field, 0);
57 if (ret)
58 pr_err("%s: failed to unlock spinlock\n", __func__);
59}
60
61static const struct hwspinlock_ops qcom_hwspinlock_ops = {
62 .trylock = qcom_hwspinlock_trylock,
63 .unlock = qcom_hwspinlock_unlock,
64};
65
66static const struct of_device_id qcom_hwspinlock_of_match[] = {
67 { .compatible = "qcom,sfpb-mutex" },
68 { .compatible = "qcom,tcsr-mutex" },
69 { }
70};
71MODULE_DEVICE_TABLE(of, qcom_hwspinlock_of_match);
72
73static int qcom_hwspinlock_probe(struct platform_device *pdev)
74{
75 struct hwspinlock_device *bank;
76 struct device_node *syscon;
77 struct reg_field field;
78 struct regmap *regmap;
79 size_t array_size;
80 u32 stride;
81 u32 base;
82 int ret;
83 int i;
84
85 syscon = of_parse_phandle(pdev->dev.of_node, "syscon", 0);
86 if (!syscon) {
87 dev_err(&pdev->dev, "no syscon property\n");
88 return -ENODEV;
89 }
90
91 regmap = syscon_node_to_regmap(syscon);
Peter Chen4d41c8c2016-07-05 10:11:03 +080092 of_node_put(syscon);
Bjorn Andersson19a0f612015-03-24 10:11:05 -070093 if (IS_ERR(regmap))
94 return PTR_ERR(regmap);
95
96 ret = of_property_read_u32_index(pdev->dev.of_node, "syscon", 1, &base);
97 if (ret < 0) {
98 dev_err(&pdev->dev, "no offset in syscon\n");
99 return -EINVAL;
100 }
101
102 ret = of_property_read_u32_index(pdev->dev.of_node, "syscon", 2, &stride);
103 if (ret < 0) {
104 dev_err(&pdev->dev, "no stride syscon\n");
105 return -EINVAL;
106 }
107
108 array_size = QCOM_MUTEX_NUM_LOCKS * sizeof(struct hwspinlock);
109 bank = devm_kzalloc(&pdev->dev, sizeof(*bank) + array_size, GFP_KERNEL);
110 if (!bank)
111 return -ENOMEM;
112
113 platform_set_drvdata(pdev, bank);
114
115 for (i = 0; i < QCOM_MUTEX_NUM_LOCKS; i++) {
116 field.reg = base + i * stride;
117 field.lsb = 0;
Bjorn Anderssonbd5717a2015-06-26 14:47:21 -0700118 field.msb = 31;
Bjorn Andersson19a0f612015-03-24 10:11:05 -0700119
120 bank->lock[i].priv = devm_regmap_field_alloc(&pdev->dev,
121 regmap, field);
122 }
123
Baolin Wanged0611a2020-01-08 11:09:11 +0800124 return devm_hwspin_lock_register(&pdev->dev, bank, &qcom_hwspinlock_ops,
125 0, QCOM_MUTEX_NUM_LOCKS);
Bjorn Andersson19a0f612015-03-24 10:11:05 -0700126}
127
128static struct platform_driver qcom_hwspinlock_driver = {
129 .probe = qcom_hwspinlock_probe,
Bjorn Andersson19a0f612015-03-24 10:11:05 -0700130 .driver = {
131 .name = "qcom_hwspinlock",
132 .of_match_table = qcom_hwspinlock_of_match,
133 },
134};
135
136static int __init qcom_hwspinlock_init(void)
137{
138 return platform_driver_register(&qcom_hwspinlock_driver);
139}
140/* board init code might need to reserve hwspinlocks for predefined purposes */
141postcore_initcall(qcom_hwspinlock_init);
142
143static void __exit qcom_hwspinlock_exit(void)
144{
145 platform_driver_unregister(&qcom_hwspinlock_driver);
146}
147module_exit(qcom_hwspinlock_exit);
148
149MODULE_LICENSE("GPL v2");
150MODULE_DESCRIPTION("Hardware spinlock driver for Qualcomm SoCs");