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