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