1 // SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause
2 /*
3  * Copyright (C) 2018, STMicroelectronics - All Rights Reserved
4  */
5 
6 #include <common.h>
7 #include <dm.h>
8 #include <errno.h>
9 #include <hwspinlock.h>
10 #include <dm/device-internal.h>
11 
12 static inline const struct hwspinlock_ops *
13 hwspinlock_dev_ops(struct udevice *dev)
14 {
15 	return (const struct hwspinlock_ops *)dev->driver->ops;
16 }
17 
18 static int hwspinlock_of_xlate_default(struct hwspinlock *hws,
19 				       struct ofnode_phandle_args *args)
20 {
21 	if (args->args_count > 1) {
22 		debug("Invaild args_count: %d\n", args->args_count);
23 		return -EINVAL;
24 	}
25 
26 	if (args->args_count)
27 		hws->id = args->args[0];
28 	else
29 		hws->id = 0;
30 
31 	return 0;
32 }
33 
34 int hwspinlock_get_by_index(struct udevice *dev, int index,
35 			    struct hwspinlock *hws)
36 {
37 	int ret;
38 	struct ofnode_phandle_args args;
39 	struct udevice *dev_hws;
40 	const struct hwspinlock_ops *ops;
41 
42 	assert(hws);
43 	hws->dev = NULL;
44 
45 	ret = dev_read_phandle_with_args(dev, "hwlocks", "#hwlock-cells", 1,
46 					 index, &args);
47 	if (ret) {
48 		dev_dbg(dev, "%s: dev_read_phandle_with_args: err=%d\n",
49 			__func__, ret);
50 		return ret;
51 	}
52 
53 	ret = uclass_get_device_by_ofnode(UCLASS_HWSPINLOCK,
54 					  args.node, &dev_hws);
55 	if (ret) {
56 		dev_dbg(dev,
57 			"%s: uclass_get_device_by_of_offset failed: err=%d\n",
58 			__func__, ret);
59 		return ret;
60 	}
61 
62 	hws->dev = dev_hws;
63 
64 	ops = hwspinlock_dev_ops(dev_hws);
65 
66 	if (ops->of_xlate)
67 		ret = ops->of_xlate(hws, &args);
68 	else
69 		ret = hwspinlock_of_xlate_default(hws, &args);
70 	if (ret)
71 		dev_dbg(dev, "of_xlate() failed: %d\n", ret);
72 
73 	return ret;
74 }
75 
76 int hwspinlock_lock_timeout(struct hwspinlock *hws, unsigned int timeout)
77 {
78 	const struct hwspinlock_ops *ops;
79 	ulong start;
80 	int ret;
81 
82 	assert(hws);
83 
84 	if (!hws->dev)
85 		return -EINVAL;
86 
87 	ops = hwspinlock_dev_ops(hws->dev);
88 	if (!ops->lock)
89 		return -ENOSYS;
90 
91 	start = get_timer(0);
92 	do {
93 		ret = ops->lock(hws->dev, hws->id);
94 		if (!ret)
95 			return ret;
96 
97 		if (ops->relax)
98 			ops->relax(hws->dev);
99 	} while (get_timer(start) < timeout);
100 
101 	return -ETIMEDOUT;
102 }
103 
104 int hwspinlock_unlock(struct hwspinlock *hws)
105 {
106 	const struct hwspinlock_ops *ops;
107 
108 	assert(hws);
109 
110 	if (!hws->dev)
111 		return -EINVAL;
112 
113 	ops = hwspinlock_dev_ops(hws->dev);
114 	if (!ops->unlock)
115 		return -ENOSYS;
116 
117 	return ops->unlock(hws->dev, hws->id);
118 }
119 
120 static int hwspinlock_post_bind(struct udevice *dev)
121 {
122 #if defined(CONFIG_NEEDS_MANUAL_RELOC)
123 	struct hwspinlock_ops *ops = device_get_ops(dev);
124 	static int reloc_done;
125 
126 	if (!reloc_done) {
127 		if (ops->lock)
128 			ops->lock += gd->reloc_off;
129 		if (ops->unlock)
130 			ops->unlock += gd->reloc_off;
131 		if (ops->relax)
132 			ops->relax += gd->reloc_off;
133 
134 		reloc_done++;
135 	}
136 #endif
137 	return 0;
138 }
139 
140 UCLASS_DRIVER(hwspinlock) = {
141 	.id		= UCLASS_HWSPINLOCK,
142 	.name		= "hwspinlock",
143 	.post_bind	= hwspinlock_post_bind,
144 };
145