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