blob: e012d5a4c935ffb8856e4754d2aae347ae1e70ae [file] [log] [blame]
Benjamin Gaignard7f84fc62018-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
Patrick Delaunayb953ec22021-04-27 11:02:19 +02006#define LOG_CATEGORY UCLASS_HWSPINLOCK
7
Benjamin Gaignard7f84fc62018-11-27 13:49:50 +01008#include <common.h>
9#include <dm.h>
10#include <errno.h>
11#include <hwspinlock.h>
Simon Glassf7ae49f2020-05-10 11:40:05 -060012#include <log.h>
Benjamin Gaignard7f84fc62018-11-27 13:49:50 +010013#include <dm/device-internal.h>
Simon Glass336d4612020-02-03 07:36:16 -070014#include <dm/device_compat.h>
15#include <linux/compat.h>
Simon Glass401d1c42020-10-30 21:38:53 -060016#include <asm/global_data.h>
Benjamin Gaignard7f84fc62018-11-27 13:49:50 +010017
18static inline const struct hwspinlock_ops *
19hwspinlock_dev_ops(struct udevice *dev)
20{
21 return (const struct hwspinlock_ops *)dev->driver->ops;
22}
23
24static int hwspinlock_of_xlate_default(struct hwspinlock *hws,
25 struct ofnode_phandle_args *args)
26{
27 if (args->args_count > 1) {
Sean Anderson46ad7ce2021-12-01 14:26:53 -050028 debug("Invalid args_count: %d\n", args->args_count);
Benjamin Gaignard7f84fc62018-11-27 13:49:50 +010029 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
40int 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
82int 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
110int 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
126static 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
146UCLASS_DRIVER(hwspinlock) = {
147 .id = UCLASS_HWSPINLOCK,
148 .name = "hwspinlock",
149 .post_bind = hwspinlock_post_bind,
150};