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