Commit | Line | Data |
---|---|---|
8ba01cae | 1 | // SPDX-License-Identifier: GPL-2.0 |
19a0f612 BA |
2 | /* |
3 | * Copyright (c) 2013, The Linux Foundation. All rights reserved. | |
4 | * Copyright (c) 2015, Sony Mobile Communications AB | |
19a0f612 BA |
5 | */ |
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> | |
19a0f612 BA |
15 | #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 | ||
cdab30b4 CM |
22 | struct qcom_hwspinlock_of_data { |
23 | u32 offset; | |
24 | u32 stride; | |
5d4753f7 | 25 | const struct regmap_config *regmap_config; |
cdab30b4 CM |
26 | }; |
27 | ||
19a0f612 BA |
28 | static int qcom_hwspinlock_trylock(struct hwspinlock *lock) |
29 | { | |
30 | struct regmap_field *field = lock->priv; | |
31 | u32 lock_owner; | |
32 | int ret; | |
33 | ||
34 | ret = regmap_field_write(field, QCOM_MUTEX_APPS_PROC_ID); | |
35 | if (ret) | |
36 | return ret; | |
37 | ||
38 | ret = regmap_field_read(field, &lock_owner); | |
39 | if (ret) | |
40 | return ret; | |
41 | ||
42 | return lock_owner == QCOM_MUTEX_APPS_PROC_ID; | |
43 | } | |
44 | ||
45 | static void qcom_hwspinlock_unlock(struct hwspinlock *lock) | |
46 | { | |
47 | struct regmap_field *field = lock->priv; | |
48 | u32 lock_owner; | |
49 | int ret; | |
50 | ||
51 | ret = regmap_field_read(field, &lock_owner); | |
52 | if (ret) { | |
53 | pr_err("%s: unable to query spinlock owner\n", __func__); | |
54 | return; | |
55 | } | |
56 | ||
57 | if (lock_owner != QCOM_MUTEX_APPS_PROC_ID) { | |
58 | pr_err("%s: spinlock not owned by us (actual owner is %d)\n", | |
59 | __func__, lock_owner); | |
60 | } | |
61 | ||
62 | ret = regmap_field_write(field, 0); | |
63 | if (ret) | |
64 | pr_err("%s: failed to unlock spinlock\n", __func__); | |
65 | } | |
66 | ||
67 | static const struct hwspinlock_ops qcom_hwspinlock_ops = { | |
68 | .trylock = qcom_hwspinlock_trylock, | |
69 | .unlock = qcom_hwspinlock_unlock, | |
70 | }; | |
71 | ||
cdab30b4 CM |
72 | static const struct qcom_hwspinlock_of_data of_sfpb_mutex = { |
73 | .offset = 0x4, | |
74 | .stride = 0x4, | |
75 | }; | |
76 | ||
5d4753f7 KK |
77 | static const struct regmap_config tcsr_msm8226_mutex_config = { |
78 | .reg_bits = 32, | |
79 | .reg_stride = 4, | |
80 | .val_bits = 32, | |
81 | .max_register = 0x1000, | |
82 | .fast_io = true, | |
83 | }; | |
84 | ||
85 | static const struct qcom_hwspinlock_of_data of_msm8226_tcsr_mutex = { | |
86 | .offset = 0, | |
87 | .stride = 0x80, | |
88 | .regmap_config = &tcsr_msm8226_mutex_config, | |
89 | }; | |
90 | ||
91 | static const struct regmap_config tcsr_mutex_config = { | |
92 | .reg_bits = 32, | |
93 | .reg_stride = 4, | |
94 | .val_bits = 32, | |
95 | .max_register = 0x20000, | |
96 | .fast_io = true, | |
97 | }; | |
98 | ||
cdab30b4 CM |
99 | static const struct qcom_hwspinlock_of_data of_tcsr_mutex = { |
100 | .offset = 0, | |
101 | .stride = 0x1000, | |
5d4753f7 | 102 | .regmap_config = &tcsr_mutex_config, |
cdab30b4 CM |
103 | }; |
104 | ||
19a0f612 | 105 | static const struct of_device_id qcom_hwspinlock_of_match[] = { |
cdab30b4 CM |
106 | { .compatible = "qcom,sfpb-mutex", .data = &of_sfpb_mutex }, |
107 | { .compatible = "qcom,tcsr-mutex", .data = &of_tcsr_mutex }, | |
5d4753f7 KK |
108 | { .compatible = "qcom,apq8084-tcsr-mutex", .data = &of_msm8226_tcsr_mutex }, |
109 | { .compatible = "qcom,ipq6018-tcsr-mutex", .data = &of_msm8226_tcsr_mutex }, | |
110 | { .compatible = "qcom,msm8226-tcsr-mutex", .data = &of_msm8226_tcsr_mutex }, | |
111 | { .compatible = "qcom,msm8974-tcsr-mutex", .data = &of_msm8226_tcsr_mutex }, | |
112 | { .compatible = "qcom,msm8994-tcsr-mutex", .data = &of_msm8226_tcsr_mutex }, | |
19a0f612 BA |
113 | { } |
114 | }; | |
115 | MODULE_DEVICE_TABLE(of, qcom_hwspinlock_of_match); | |
116 | ||
7a1e6fb1 BA |
117 | static struct regmap *qcom_hwspinlock_probe_syscon(struct platform_device *pdev, |
118 | u32 *base, u32 *stride) | |
19a0f612 | 119 | { |
19a0f612 | 120 | struct device_node *syscon; |
19a0f612 | 121 | struct regmap *regmap; |
19a0f612 | 122 | int ret; |
19a0f612 BA |
123 | |
124 | syscon = of_parse_phandle(pdev->dev.of_node, "syscon", 0); | |
7a1e6fb1 BA |
125 | if (!syscon) |
126 | return ERR_PTR(-ENODEV); | |
19a0f612 BA |
127 | |
128 | regmap = syscon_node_to_regmap(syscon); | |
4d41c8cb | 129 | of_node_put(syscon); |
19a0f612 | 130 | if (IS_ERR(regmap)) |
7a1e6fb1 | 131 | return regmap; |
19a0f612 | 132 | |
7a1e6fb1 | 133 | ret = of_property_read_u32_index(pdev->dev.of_node, "syscon", 1, base); |
19a0f612 BA |
134 | if (ret < 0) { |
135 | dev_err(&pdev->dev, "no offset in syscon\n"); | |
7a1e6fb1 | 136 | return ERR_PTR(-EINVAL); |
19a0f612 BA |
137 | } |
138 | ||
7a1e6fb1 | 139 | ret = of_property_read_u32_index(pdev->dev.of_node, "syscon", 2, stride); |
19a0f612 BA |
140 | if (ret < 0) { |
141 | dev_err(&pdev->dev, "no stride syscon\n"); | |
7a1e6fb1 | 142 | return ERR_PTR(-EINVAL); |
19a0f612 BA |
143 | } |
144 | ||
7a1e6fb1 BA |
145 | return regmap; |
146 | } | |
147 | ||
7a1e6fb1 BA |
148 | static struct regmap *qcom_hwspinlock_probe_mmio(struct platform_device *pdev, |
149 | u32 *offset, u32 *stride) | |
150 | { | |
cdab30b4 | 151 | const struct qcom_hwspinlock_of_data *data; |
7a1e6fb1 BA |
152 | struct device *dev = &pdev->dev; |
153 | void __iomem *base; | |
154 | ||
cdab30b4 | 155 | data = of_device_get_match_data(dev); |
5d4753f7 KK |
156 | if (!data->regmap_config) |
157 | return ERR_PTR(-EINVAL); | |
cdab30b4 CM |
158 | |
159 | *offset = data->offset; | |
160 | *stride = data->stride; | |
7a1e6fb1 BA |
161 | |
162 | base = devm_platform_ioremap_resource(pdev, 0); | |
163 | if (IS_ERR(base)) | |
164 | return ERR_CAST(base); | |
165 | ||
5d4753f7 | 166 | return devm_regmap_init_mmio(dev, base, data->regmap_config); |
7a1e6fb1 BA |
167 | } |
168 | ||
169 | static int qcom_hwspinlock_probe(struct platform_device *pdev) | |
170 | { | |
171 | struct hwspinlock_device *bank; | |
172 | struct reg_field field; | |
173 | struct regmap *regmap; | |
174 | size_t array_size; | |
175 | u32 stride; | |
176 | u32 base; | |
177 | int i; | |
178 | ||
179 | regmap = qcom_hwspinlock_probe_syscon(pdev, &base, &stride); | |
180 | if (IS_ERR(regmap) && PTR_ERR(regmap) == -ENODEV) | |
181 | regmap = qcom_hwspinlock_probe_mmio(pdev, &base, &stride); | |
182 | ||
183 | if (IS_ERR(regmap)) | |
184 | return PTR_ERR(regmap); | |
185 | ||
19a0f612 BA |
186 | array_size = QCOM_MUTEX_NUM_LOCKS * sizeof(struct hwspinlock); |
187 | bank = devm_kzalloc(&pdev->dev, sizeof(*bank) + array_size, GFP_KERNEL); | |
188 | if (!bank) | |
189 | return -ENOMEM; | |
190 | ||
191 | platform_set_drvdata(pdev, bank); | |
192 | ||
193 | for (i = 0; i < QCOM_MUTEX_NUM_LOCKS; i++) { | |
194 | field.reg = base + i * stride; | |
195 | field.lsb = 0; | |
bd5717a4 | 196 | field.msb = 31; |
19a0f612 BA |
197 | |
198 | bank->lock[i].priv = devm_regmap_field_alloc(&pdev->dev, | |
199 | regmap, field); | |
200 | } | |
201 | ||
ed0611a6 BW |
202 | return devm_hwspin_lock_register(&pdev->dev, bank, &qcom_hwspinlock_ops, |
203 | 0, QCOM_MUTEX_NUM_LOCKS); | |
19a0f612 BA |
204 | } |
205 | ||
206 | static struct platform_driver qcom_hwspinlock_driver = { | |
207 | .probe = qcom_hwspinlock_probe, | |
19a0f612 BA |
208 | .driver = { |
209 | .name = "qcom_hwspinlock", | |
210 | .of_match_table = qcom_hwspinlock_of_match, | |
211 | }, | |
212 | }; | |
213 | ||
214 | static int __init qcom_hwspinlock_init(void) | |
215 | { | |
216 | return platform_driver_register(&qcom_hwspinlock_driver); | |
217 | } | |
218 | /* board init code might need to reserve hwspinlocks for predefined purposes */ | |
219 | postcore_initcall(qcom_hwspinlock_init); | |
220 | ||
221 | static void __exit qcom_hwspinlock_exit(void) | |
222 | { | |
223 | platform_driver_unregister(&qcom_hwspinlock_driver); | |
224 | } | |
225 | module_exit(qcom_hwspinlock_exit); | |
226 | ||
227 | MODULE_LICENSE("GPL v2"); | |
228 | MODULE_DESCRIPTION("Hardware spinlock driver for Qualcomm SoCs"); |