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