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