Merge tag 'efi-2020-07-rc6' of https://gitlab.denx.de/u-boot/custodians/u-boot-efi
[oweals/u-boot.git] / drivers / power / domain / power-domain-uclass.c
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Copyright (c) 2016, NVIDIA CORPORATION.
4  */
5
6 #include <common.h>
7 #include <dm.h>
8 #include <log.h>
9 #include <malloc.h>
10 #include <power-domain.h>
11 #include <power-domain-uclass.h>
12 #include <dm/device-internal.h>
13
14 static inline struct power_domain_ops *power_domain_dev_ops(struct udevice *dev)
15 {
16         return (struct power_domain_ops *)dev->driver->ops;
17 }
18
19 static int power_domain_of_xlate_default(struct power_domain *power_domain,
20                                          struct ofnode_phandle_args *args)
21 {
22         debug("%s(power_domain=%p)\n", __func__, power_domain);
23
24         if (args->args_count != 1) {
25                 debug("Invalid args_count: %d\n", args->args_count);
26                 return -EINVAL;
27         }
28
29         power_domain->id = args->args[0];
30
31         return 0;
32 }
33
34 int power_domain_get_by_index(struct udevice *dev,
35                               struct power_domain *power_domain, int index)
36 {
37         struct ofnode_phandle_args args;
38         int ret;
39         struct udevice *dev_power_domain;
40         struct power_domain_ops *ops;
41
42         debug("%s(dev=%p, power_domain=%p)\n", __func__, dev, power_domain);
43
44         ret = dev_read_phandle_with_args(dev, "power-domains",
45                                          "#power-domain-cells", 0, index,
46                                          &args);
47         if (ret) {
48                 debug("%s: dev_read_phandle_with_args failed: %d\n",
49                       __func__, ret);
50                 return ret;
51         }
52
53         ret = uclass_get_device_by_ofnode(UCLASS_POWER_DOMAIN, args.node,
54                                           &dev_power_domain);
55         if (ret) {
56                 debug("%s: uclass_get_device_by_ofnode failed: %d\n",
57                       __func__, ret);
58                 return ret;
59         }
60         ops = power_domain_dev_ops(dev_power_domain);
61
62         power_domain->dev = dev_power_domain;
63         if (ops->of_xlate)
64                 ret = ops->of_xlate(power_domain, &args);
65         else
66                 ret = power_domain_of_xlate_default(power_domain, &args);
67         if (ret) {
68                 debug("of_xlate() failed: %d\n", ret);
69                 return ret;
70         }
71
72         ret = ops->request(power_domain);
73         if (ret) {
74                 debug("ops->request() failed: %d\n", ret);
75                 return ret;
76         }
77
78         return 0;
79 }
80
81 int power_domain_get(struct udevice *dev, struct power_domain *power_domain)
82 {
83         return power_domain_get_by_index(dev, power_domain, 0);
84 }
85
86 int power_domain_free(struct power_domain *power_domain)
87 {
88         struct power_domain_ops *ops = power_domain_dev_ops(power_domain->dev);
89
90         debug("%s(power_domain=%p)\n", __func__, power_domain);
91
92         return ops->rfree(power_domain);
93 }
94
95 int power_domain_on(struct power_domain *power_domain)
96 {
97         struct power_domain_ops *ops = power_domain_dev_ops(power_domain->dev);
98
99         debug("%s(power_domain=%p)\n", __func__, power_domain);
100
101         return ops->on(power_domain);
102 }
103
104 int power_domain_off(struct power_domain *power_domain)
105 {
106         struct power_domain_ops *ops = power_domain_dev_ops(power_domain->dev);
107
108         debug("%s(power_domain=%p)\n", __func__, power_domain);
109
110         return ops->off(power_domain);
111 }
112
113 #if (CONFIG_IS_ENABLED(OF_CONTROL) && !CONFIG_IS_ENABLED(OF_PLATDATA))
114 static int dev_power_domain_ctrl(struct udevice *dev, bool on)
115 {
116         struct power_domain pd;
117         int i, count, ret = 0;
118
119         count = dev_count_phandle_with_args(dev, "power-domains",
120                                             "#power-domain-cells");
121         for (i = 0; i < count; i++) {
122                 ret = power_domain_get_by_index(dev, &pd, i);
123                 if (ret)
124                         return ret;
125                 if (on)
126                         ret = power_domain_on(&pd);
127                 else
128                         ret = power_domain_off(&pd);
129         }
130
131         /*
132          * For platforms with parent and child power-domain devices
133          * we may not run device_remove() on the power-domain parent
134          * because it will result in removing its children and switching
135          * off their power-domain parent. So we will get here again and
136          * again and will be stuck in an endless loop.
137          */
138         if (!on && dev_get_parent(dev) == pd.dev &&
139             device_get_uclass_id(dev) == UCLASS_POWER_DOMAIN)
140                 return ret;
141
142         /*
143          * power_domain_get() bound the device, thus
144          * we must remove it again to prevent unbinding
145          * active devices (which would result in unbind
146          * error).
147          */
148         if (count > 0 && !on)
149                 device_remove(pd.dev, DM_REMOVE_NORMAL);
150
151         return ret;
152 }
153
154 int dev_power_domain_on(struct udevice *dev)
155 {
156         return dev_power_domain_ctrl(dev, true);
157 }
158
159 int dev_power_domain_off(struct udevice *dev)
160 {
161         return dev_power_domain_ctrl(dev, false);
162 }
163 #endif
164
165 UCLASS_DRIVER(power_domain) = {
166         .id             = UCLASS_POWER_DOMAIN,
167         .name           = "power_domain",
168 };