xref: /openbmc/linux/drivers/hwspinlock/qcom_hwspinlock.c (revision 19a0f61224d2d91860fa8291ab63cb104ee86bdd)
1*19a0f612SBjorn Andersson /*
2*19a0f612SBjorn Andersson  * Copyright (c) 2013, The Linux Foundation. All rights reserved.
3*19a0f612SBjorn Andersson  * Copyright (c) 2015, Sony Mobile Communications AB
4*19a0f612SBjorn Andersson  *
5*19a0f612SBjorn Andersson  * This software is licensed under the terms of the GNU General Public
6*19a0f612SBjorn Andersson  * License version 2, as published by the Free Software Foundation, and
7*19a0f612SBjorn Andersson  * may be copied, distributed, and modified under those terms.
8*19a0f612SBjorn Andersson  *
9*19a0f612SBjorn Andersson  * This program is distributed in the hope that it will be useful,
10*19a0f612SBjorn Andersson  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11*19a0f612SBjorn Andersson  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12*19a0f612SBjorn Andersson  * GNU General Public License for more details.
13*19a0f612SBjorn Andersson  */
14*19a0f612SBjorn Andersson 
15*19a0f612SBjorn Andersson #include <linux/hwspinlock.h>
16*19a0f612SBjorn Andersson #include <linux/io.h>
17*19a0f612SBjorn Andersson #include <linux/kernel.h>
18*19a0f612SBjorn Andersson #include <linux/mfd/syscon.h>
19*19a0f612SBjorn Andersson #include <linux/module.h>
20*19a0f612SBjorn Andersson #include <linux/of.h>
21*19a0f612SBjorn Andersson #include <linux/of_device.h>
22*19a0f612SBjorn Andersson #include <linux/platform_device.h>
23*19a0f612SBjorn Andersson #include <linux/pm_runtime.h>
24*19a0f612SBjorn Andersson #include <linux/regmap.h>
25*19a0f612SBjorn Andersson 
26*19a0f612SBjorn Andersson #include "hwspinlock_internal.h"
27*19a0f612SBjorn Andersson 
28*19a0f612SBjorn Andersson #define QCOM_MUTEX_APPS_PROC_ID	1
29*19a0f612SBjorn Andersson #define QCOM_MUTEX_NUM_LOCKS	32
30*19a0f612SBjorn Andersson 
31*19a0f612SBjorn Andersson static int qcom_hwspinlock_trylock(struct hwspinlock *lock)
32*19a0f612SBjorn Andersson {
33*19a0f612SBjorn Andersson 	struct regmap_field *field = lock->priv;
34*19a0f612SBjorn Andersson 	u32 lock_owner;
35*19a0f612SBjorn Andersson 	int ret;
36*19a0f612SBjorn Andersson 
37*19a0f612SBjorn Andersson 	ret = regmap_field_write(field, QCOM_MUTEX_APPS_PROC_ID);
38*19a0f612SBjorn Andersson 	if (ret)
39*19a0f612SBjorn Andersson 		return ret;
40*19a0f612SBjorn Andersson 
41*19a0f612SBjorn Andersson 	ret = regmap_field_read(field, &lock_owner);
42*19a0f612SBjorn Andersson 	if (ret)
43*19a0f612SBjorn Andersson 		return ret;
44*19a0f612SBjorn Andersson 
45*19a0f612SBjorn Andersson 	return lock_owner == QCOM_MUTEX_APPS_PROC_ID;
46*19a0f612SBjorn Andersson }
47*19a0f612SBjorn Andersson 
48*19a0f612SBjorn Andersson static void qcom_hwspinlock_unlock(struct hwspinlock *lock)
49*19a0f612SBjorn Andersson {
50*19a0f612SBjorn Andersson 	struct regmap_field *field = lock->priv;
51*19a0f612SBjorn Andersson 	u32 lock_owner;
52*19a0f612SBjorn Andersson 	int ret;
53*19a0f612SBjorn Andersson 
54*19a0f612SBjorn Andersson 	ret = regmap_field_read(field, &lock_owner);
55*19a0f612SBjorn Andersson 	if (ret) {
56*19a0f612SBjorn Andersson 		pr_err("%s: unable to query spinlock owner\n", __func__);
57*19a0f612SBjorn Andersson 		return;
58*19a0f612SBjorn Andersson 	}
59*19a0f612SBjorn Andersson 
60*19a0f612SBjorn Andersson 	if (lock_owner != QCOM_MUTEX_APPS_PROC_ID) {
61*19a0f612SBjorn Andersson 		pr_err("%s: spinlock not owned by us (actual owner is %d)\n",
62*19a0f612SBjorn Andersson 				__func__, lock_owner);
63*19a0f612SBjorn Andersson 	}
64*19a0f612SBjorn Andersson 
65*19a0f612SBjorn Andersson 	ret = regmap_field_write(field, 0);
66*19a0f612SBjorn Andersson 	if (ret)
67*19a0f612SBjorn Andersson 		pr_err("%s: failed to unlock spinlock\n", __func__);
68*19a0f612SBjorn Andersson }
69*19a0f612SBjorn Andersson 
70*19a0f612SBjorn Andersson static const struct hwspinlock_ops qcom_hwspinlock_ops = {
71*19a0f612SBjorn Andersson 	.trylock	= qcom_hwspinlock_trylock,
72*19a0f612SBjorn Andersson 	.unlock		= qcom_hwspinlock_unlock,
73*19a0f612SBjorn Andersson };
74*19a0f612SBjorn Andersson 
75*19a0f612SBjorn Andersson static const struct of_device_id qcom_hwspinlock_of_match[] = {
76*19a0f612SBjorn Andersson 	{ .compatible = "qcom,sfpb-mutex" },
77*19a0f612SBjorn Andersson 	{ .compatible = "qcom,tcsr-mutex" },
78*19a0f612SBjorn Andersson 	{ }
79*19a0f612SBjorn Andersson };
80*19a0f612SBjorn Andersson MODULE_DEVICE_TABLE(of, qcom_hwspinlock_of_match);
81*19a0f612SBjorn Andersson 
82*19a0f612SBjorn Andersson static int qcom_hwspinlock_probe(struct platform_device *pdev)
83*19a0f612SBjorn Andersson {
84*19a0f612SBjorn Andersson 	struct hwspinlock_device *bank;
85*19a0f612SBjorn Andersson 	struct device_node *syscon;
86*19a0f612SBjorn Andersson 	struct reg_field field;
87*19a0f612SBjorn Andersson 	struct regmap *regmap;
88*19a0f612SBjorn Andersson 	size_t array_size;
89*19a0f612SBjorn Andersson 	u32 stride;
90*19a0f612SBjorn Andersson 	u32 base;
91*19a0f612SBjorn Andersson 	int ret;
92*19a0f612SBjorn Andersson 	int i;
93*19a0f612SBjorn Andersson 
94*19a0f612SBjorn Andersson 	syscon = of_parse_phandle(pdev->dev.of_node, "syscon", 0);
95*19a0f612SBjorn Andersson 	if (!syscon) {
96*19a0f612SBjorn Andersson 		dev_err(&pdev->dev, "no syscon property\n");
97*19a0f612SBjorn Andersson 		return -ENODEV;
98*19a0f612SBjorn Andersson 	}
99*19a0f612SBjorn Andersson 
100*19a0f612SBjorn Andersson 	regmap = syscon_node_to_regmap(syscon);
101*19a0f612SBjorn Andersson 	if (IS_ERR(regmap))
102*19a0f612SBjorn Andersson 		return PTR_ERR(regmap);
103*19a0f612SBjorn Andersson 
104*19a0f612SBjorn Andersson 	ret = of_property_read_u32_index(pdev->dev.of_node, "syscon", 1, &base);
105*19a0f612SBjorn Andersson 	if (ret < 0) {
106*19a0f612SBjorn Andersson 		dev_err(&pdev->dev, "no offset in syscon\n");
107*19a0f612SBjorn Andersson 		return -EINVAL;
108*19a0f612SBjorn Andersson 	}
109*19a0f612SBjorn Andersson 
110*19a0f612SBjorn Andersson 	ret = of_property_read_u32_index(pdev->dev.of_node, "syscon", 2, &stride);
111*19a0f612SBjorn Andersson 	if (ret < 0) {
112*19a0f612SBjorn Andersson 		dev_err(&pdev->dev, "no stride syscon\n");
113*19a0f612SBjorn Andersson 		return -EINVAL;
114*19a0f612SBjorn Andersson 	}
115*19a0f612SBjorn Andersson 
116*19a0f612SBjorn Andersson 	array_size = QCOM_MUTEX_NUM_LOCKS * sizeof(struct hwspinlock);
117*19a0f612SBjorn Andersson 	bank = devm_kzalloc(&pdev->dev, sizeof(*bank) + array_size, GFP_KERNEL);
118*19a0f612SBjorn Andersson 	if (!bank)
119*19a0f612SBjorn Andersson 		return -ENOMEM;
120*19a0f612SBjorn Andersson 
121*19a0f612SBjorn Andersson 	platform_set_drvdata(pdev, bank);
122*19a0f612SBjorn Andersson 
123*19a0f612SBjorn Andersson 	for (i = 0; i < QCOM_MUTEX_NUM_LOCKS; i++) {
124*19a0f612SBjorn Andersson 		field.reg = base + i * stride;
125*19a0f612SBjorn Andersson 		field.lsb = 0;
126*19a0f612SBjorn Andersson 		field.msb = 32;
127*19a0f612SBjorn Andersson 
128*19a0f612SBjorn Andersson 		bank->lock[i].priv = devm_regmap_field_alloc(&pdev->dev,
129*19a0f612SBjorn Andersson 							     regmap, field);
130*19a0f612SBjorn Andersson 	}
131*19a0f612SBjorn Andersson 
132*19a0f612SBjorn Andersson 	pm_runtime_enable(&pdev->dev);
133*19a0f612SBjorn Andersson 
134*19a0f612SBjorn Andersson 	ret = hwspin_lock_register(bank, &pdev->dev, &qcom_hwspinlock_ops,
135*19a0f612SBjorn Andersson 				   0, QCOM_MUTEX_NUM_LOCKS);
136*19a0f612SBjorn Andersson 	if (ret)
137*19a0f612SBjorn Andersson 		pm_runtime_disable(&pdev->dev);
138*19a0f612SBjorn Andersson 
139*19a0f612SBjorn Andersson 	return ret;
140*19a0f612SBjorn Andersson }
141*19a0f612SBjorn Andersson 
142*19a0f612SBjorn Andersson static int qcom_hwspinlock_remove(struct platform_device *pdev)
143*19a0f612SBjorn Andersson {
144*19a0f612SBjorn Andersson 	struct hwspinlock_device *bank = platform_get_drvdata(pdev);
145*19a0f612SBjorn Andersson 	int ret;
146*19a0f612SBjorn Andersson 
147*19a0f612SBjorn Andersson 	ret = hwspin_lock_unregister(bank);
148*19a0f612SBjorn Andersson 	if (ret) {
149*19a0f612SBjorn Andersson 		dev_err(&pdev->dev, "%s failed: %d\n", __func__, ret);
150*19a0f612SBjorn Andersson 		return ret;
151*19a0f612SBjorn Andersson 	}
152*19a0f612SBjorn Andersson 
153*19a0f612SBjorn Andersson 	pm_runtime_disable(&pdev->dev);
154*19a0f612SBjorn Andersson 
155*19a0f612SBjorn Andersson 	return 0;
156*19a0f612SBjorn Andersson }
157*19a0f612SBjorn Andersson 
158*19a0f612SBjorn Andersson static struct platform_driver qcom_hwspinlock_driver = {
159*19a0f612SBjorn Andersson 	.probe		= qcom_hwspinlock_probe,
160*19a0f612SBjorn Andersson 	.remove		= qcom_hwspinlock_remove,
161*19a0f612SBjorn Andersson 	.driver		= {
162*19a0f612SBjorn Andersson 		.name	= "qcom_hwspinlock",
163*19a0f612SBjorn Andersson 		.of_match_table = qcom_hwspinlock_of_match,
164*19a0f612SBjorn Andersson 	},
165*19a0f612SBjorn Andersson };
166*19a0f612SBjorn Andersson 
167*19a0f612SBjorn Andersson static int __init qcom_hwspinlock_init(void)
168*19a0f612SBjorn Andersson {
169*19a0f612SBjorn Andersson 	return platform_driver_register(&qcom_hwspinlock_driver);
170*19a0f612SBjorn Andersson }
171*19a0f612SBjorn Andersson /* board init code might need to reserve hwspinlocks for predefined purposes */
172*19a0f612SBjorn Andersson postcore_initcall(qcom_hwspinlock_init);
173*19a0f612SBjorn Andersson 
174*19a0f612SBjorn Andersson static void __exit qcom_hwspinlock_exit(void)
175*19a0f612SBjorn Andersson {
176*19a0f612SBjorn Andersson 	platform_driver_unregister(&qcom_hwspinlock_driver);
177*19a0f612SBjorn Andersson }
178*19a0f612SBjorn Andersson module_exit(qcom_hwspinlock_exit);
179*19a0f612SBjorn Andersson 
180*19a0f612SBjorn Andersson MODULE_LICENSE("GPL v2");
181*19a0f612SBjorn Andersson MODULE_DESCRIPTION("Hardware spinlock driver for Qualcomm SoCs");
182