blob: a6e5f9ed0369eb9d2dfa66edc9e938bac6720dab [file] [log] [blame]
Tom Rini10e47792018-05-06 17:58:06 -04001// SPDX-License-Identifier: GPL-2.0
Stephen Warren92c67fa2016-07-13 13:45:31 -06002/*
3 * Copyright (c) 2016, NVIDIA CORPORATION.
Stephen Warren92c67fa2016-07-13 13:45:31 -06004 */
5
Patrick Delaunay81313352021-04-27 11:02:19 +02006#define LOG_CATEGORY UCLASS_POWER_DOMAIN
7
Stephen Warren92c67fa2016-07-13 13:45:31 -06008#include <dm.h>
Simon Glass0f2af882020-05-10 11:40:05 -06009#include <log.h>
Simon Glass9bc15642020-02-03 07:36:16 -070010#include <malloc.h>
Stephen Warren92c67fa2016-07-13 13:45:31 -060011#include <power-domain.h>
12#include <power-domain-uclass.h>
Lokesh Vutla589eeb82019-09-27 13:48:14 +053013#include <dm/device-internal.h>
Stephen Warren92c67fa2016-07-13 13:45:31 -060014
Miquel Raynal0654e132025-04-03 09:39:05 +020015struct power_domain_priv {
16 int on_count;
17};
18
Stephen Warren92c67fa2016-07-13 13:45:31 -060019static inline struct power_domain_ops *power_domain_dev_ops(struct udevice *dev)
20{
21 return (struct power_domain_ops *)dev->driver->ops;
22}
23
24static int power_domain_of_xlate_default(struct power_domain *power_domain,
Simon Glass085ba472017-05-18 20:09:49 -060025 struct ofnode_phandle_args *args)
Stephen Warren92c67fa2016-07-13 13:45:31 -060026{
27 debug("%s(power_domain=%p)\n", __func__, power_domain);
28
29 if (args->args_count != 1) {
30 debug("Invalid args_count: %d\n", args->args_count);
31 return -EINVAL;
32 }
33
34 power_domain->id = args->args[0];
35
36 return 0;
37}
38
Lokesh Vutlac411c672018-08-27 15:57:44 +053039int power_domain_get_by_index(struct udevice *dev,
40 struct power_domain *power_domain, int index)
Stephen Warren92c67fa2016-07-13 13:45:31 -060041{
Simon Glass085ba472017-05-18 20:09:49 -060042 struct ofnode_phandle_args args;
Stephen Warren92c67fa2016-07-13 13:45:31 -060043 int ret;
44 struct udevice *dev_power_domain;
45 struct power_domain_ops *ops;
46
47 debug("%s(dev=%p, power_domain=%p)\n", __func__, dev, power_domain);
48
Simon Glass085ba472017-05-18 20:09:49 -060049 ret = dev_read_phandle_with_args(dev, "power-domains",
Lokesh Vutlac411c672018-08-27 15:57:44 +053050 "#power-domain-cells", 0, index,
51 &args);
Stephen Warren92c67fa2016-07-13 13:45:31 -060052 if (ret) {
Simon Glass085ba472017-05-18 20:09:49 -060053 debug("%s: dev_read_phandle_with_args failed: %d\n",
Stephen Warren92c67fa2016-07-13 13:45:31 -060054 __func__, ret);
55 return ret;
56 }
57
Simon Glass085ba472017-05-18 20:09:49 -060058 ret = uclass_get_device_by_ofnode(UCLASS_POWER_DOMAIN, args.node,
59 &dev_power_domain);
Stephen Warren92c67fa2016-07-13 13:45:31 -060060 if (ret) {
Simon Glass085ba472017-05-18 20:09:49 -060061 debug("%s: uclass_get_device_by_ofnode failed: %d\n",
Stephen Warren92c67fa2016-07-13 13:45:31 -060062 __func__, ret);
63 return ret;
64 }
65 ops = power_domain_dev_ops(dev_power_domain);
66
67 power_domain->dev = dev_power_domain;
68 if (ops->of_xlate)
69 ret = ops->of_xlate(power_domain, &args);
70 else
71 ret = power_domain_of_xlate_default(power_domain, &args);
72 if (ret) {
73 debug("of_xlate() failed: %d\n", ret);
74 return ret;
75 }
76
Marek Vasut4befe912022-04-13 00:42:48 +020077 ret = ops->request ? ops->request(power_domain) : 0;
Stephen Warren92c67fa2016-07-13 13:45:31 -060078 if (ret) {
79 debug("ops->request() failed: %d\n", ret);
80 return ret;
81 }
82
83 return 0;
84}
85
Marek Vasutfb4e8e92022-04-13 00:42:52 +020086int power_domain_get_by_name(struct udevice *dev,
87 struct power_domain *power_domain, const char *name)
88{
89 int index;
90
91 index = dev_read_stringlist_search(dev, "power-domain-names", name);
92 if (index < 0) {
93 debug("fdt_stringlist_search() failed: %d\n", index);
94 return index;
95 }
96
97 return power_domain_get_by_index(dev, power_domain, index);
98}
99
Lokesh Vutlac411c672018-08-27 15:57:44 +0530100int power_domain_get(struct udevice *dev, struct power_domain *power_domain)
101{
102 return power_domain_get_by_index(dev, power_domain, 0);
103}
104
Stephen Warren92c67fa2016-07-13 13:45:31 -0600105int power_domain_free(struct power_domain *power_domain)
106{
107 struct power_domain_ops *ops = power_domain_dev_ops(power_domain->dev);
108
109 debug("%s(power_domain=%p)\n", __func__, power_domain);
110
Marek Vasut4befe912022-04-13 00:42:48 +0200111 return ops->rfree ? ops->rfree(power_domain) : 0;
Stephen Warren92c67fa2016-07-13 13:45:31 -0600112}
113
Miquel Raynal0654e132025-04-03 09:39:05 +0200114int power_domain_on_lowlevel(struct power_domain *power_domain)
Stephen Warren92c67fa2016-07-13 13:45:31 -0600115{
Miquel Raynal0654e132025-04-03 09:39:05 +0200116 struct power_domain_priv *priv = dev_get_uclass_priv(power_domain->dev);
Stephen Warren92c67fa2016-07-13 13:45:31 -0600117 struct power_domain_ops *ops = power_domain_dev_ops(power_domain->dev);
Miquel Raynal0654e132025-04-03 09:39:05 +0200118 int ret;
Stephen Warren92c67fa2016-07-13 13:45:31 -0600119
120 debug("%s(power_domain=%p)\n", __func__, power_domain);
121
Miquel Raynal0654e132025-04-03 09:39:05 +0200122 if (priv->on_count++ > 0)
123 return -EALREADY;
124
125 ret = ops->on ? ops->on(power_domain) : 0;
126 if (ret) {
127 priv->on_count--;
128 return ret;
129 }
130
131 return 0;
Stephen Warren92c67fa2016-07-13 13:45:31 -0600132}
133
Miquel Raynal0654e132025-04-03 09:39:05 +0200134int power_domain_off_lowlevel(struct power_domain *power_domain)
Stephen Warren92c67fa2016-07-13 13:45:31 -0600135{
Miquel Raynal0654e132025-04-03 09:39:05 +0200136 struct power_domain_priv *priv = dev_get_uclass_priv(power_domain->dev);
Stephen Warren92c67fa2016-07-13 13:45:31 -0600137 struct power_domain_ops *ops = power_domain_dev_ops(power_domain->dev);
Miquel Raynal0654e132025-04-03 09:39:05 +0200138 int ret;
Stephen Warren92c67fa2016-07-13 13:45:31 -0600139
140 debug("%s(power_domain=%p)\n", __func__, power_domain);
141
Miquel Raynal0654e132025-04-03 09:39:05 +0200142 if (priv->on_count <= 0) {
143 debug("Power domain %s already off.\n", power_domain->dev->name);
144 return -EALREADY;
145 }
146
147 if (priv->on_count-- > 1)
148 return -EBUSY;
149
150 ret = ops->off ? ops->off(power_domain) : 0;
151 if (ret) {
152 priv->on_count++;
153 return ret;
154 }
155
156 return 0;
Stephen Warren92c67fa2016-07-13 13:45:31 -0600157}
158
Simon Glass3580f6d2021-08-07 07:24:03 -0600159#if CONFIG_IS_ENABLED(OF_REAL)
Lokesh Vutla589eeb82019-09-27 13:48:14 +0530160static int dev_power_domain_ctrl(struct udevice *dev, bool on)
Peng Fandb513e92019-09-17 09:29:19 +0000161{
162 struct power_domain pd;
Lokesh Vutla589eeb82019-09-27 13:48:14 +0530163 int i, count, ret = 0;
Peng Fandb513e92019-09-17 09:29:19 +0000164
165 count = dev_count_phandle_with_args(dev, "power-domains",
Patrick Delaunayd776a842020-09-25 09:41:14 +0200166 "#power-domain-cells", 0);
Peng Fandb513e92019-09-17 09:29:19 +0000167 for (i = 0; i < count; i++) {
168 ret = power_domain_get_by_index(dev, &pd, i);
169 if (ret)
170 return ret;
Lokesh Vutla589eeb82019-09-27 13:48:14 +0530171 if (on)
172 ret = power_domain_on(&pd);
173 else
174 ret = power_domain_off(&pd);
Peng Fandb513e92019-09-17 09:29:19 +0000175 }
176
Lokesh Vutla589eeb82019-09-27 13:48:14 +0530177 /*
Anatolij Gustschin0e3ce202020-02-17 09:42:11 +0100178 * For platforms with parent and child power-domain devices
179 * we may not run device_remove() on the power-domain parent
180 * because it will result in removing its children and switching
181 * off their power-domain parent. So we will get here again and
182 * again and will be stuck in an endless loop.
183 */
Sean Andersone91b0032022-03-23 14:26:09 -0400184 if (count > 0 && !on && dev_get_parent(dev) == pd.dev &&
Anatolij Gustschin0e3ce202020-02-17 09:42:11 +0100185 device_get_uclass_id(dev) == UCLASS_POWER_DOMAIN)
186 return ret;
187
188 /*
Lokesh Vutla589eeb82019-09-27 13:48:14 +0530189 * power_domain_get() bound the device, thus
190 * we must remove it again to prevent unbinding
191 * active devices (which would result in unbind
192 * error).
193 */
194 if (count > 0 && !on)
195 device_remove(pd.dev, DM_REMOVE_NORMAL);
196
197 return ret;
198}
199
200int dev_power_domain_on(struct udevice *dev)
201{
202 return dev_power_domain_ctrl(dev, true);
203}
204
205int dev_power_domain_off(struct udevice *dev)
206{
207 return dev_power_domain_ctrl(dev, false);
Peng Fandb513e92019-09-17 09:29:19 +0000208}
Simon Glass3580f6d2021-08-07 07:24:03 -0600209#endif /* OF_REAL */
Peng Fandb513e92019-09-17 09:29:19 +0000210
Stephen Warren92c67fa2016-07-13 13:45:31 -0600211UCLASS_DRIVER(power_domain) = {
212 .id = UCLASS_POWER_DOMAIN,
213 .name = "power_domain",
Miquel Raynal0654e132025-04-03 09:39:05 +0200214 .per_device_auto = sizeof(struct power_domain_priv),
Stephen Warren92c67fa2016-07-13 13:45:31 -0600215};