imx8: power: Add PD device lookup interface to power domain uclass
[oweals/u-boot.git] / drivers / power / domain / imx8-power-domain-legacy.c
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Copyright 2017 NXP
4  */
5
6 #include <common.h>
7 #include <dm.h>
8 #include <malloc.h>
9 #include <power-domain-uclass.h>
10 #include <asm/io.h>
11 #include <asm/arch/power-domain.h>
12 #include <dm/device-internal.h>
13 #include <dm/device.h>
14 #include <asm/arch/sci/sci.h>
15
16 DECLARE_GLOBAL_DATA_PTR;
17
18 struct imx8_power_domain_priv {
19         bool state_on;
20 };
21
22 int imx8_power_domain_lookup_name(const char *name,
23                                   struct power_domain *power_domain)
24 {
25         struct udevice *dev;
26         struct power_domain_ops *ops;
27         int ret;
28
29         debug("%s(power_domain=%p name=%s)\n", __func__, power_domain, name);
30
31         ret = uclass_get_device_by_name(UCLASS_POWER_DOMAIN, name, &dev);
32         if (ret) {
33                 printf("%s fail: %s, ret = %d\n", __func__, name, ret);
34                 return ret;
35         }
36
37         ops = (struct power_domain_ops *)dev->driver->ops;
38         power_domain->dev = dev;
39         ret = ops->request(power_domain);
40         if (ret) {
41                 debug("ops->request() failed: %d\n", ret);
42                 return ret;
43         }
44
45         debug("%s ok: %s\n", __func__, dev->name);
46
47         return 0;
48 }
49
50 static int imx8_power_domain_request(struct power_domain *power_domain)
51 {
52         debug("%s(power_domain=%p)\n", __func__, power_domain);
53
54         return 0;
55 }
56
57 static int imx8_power_domain_free(struct power_domain *power_domain)
58 {
59         debug("%s(power_domain=%p)\n", __func__, power_domain);
60
61         return 0;
62 }
63
64 static int imx8_power_domain_on(struct power_domain *power_domain)
65 {
66         struct udevice *dev = power_domain->dev;
67         struct imx8_power_domain_platdata *pdata;
68         struct imx8_power_domain_priv *ppriv;
69         sc_err_t ret;
70         int err;
71
72         struct power_domain parent_domain;
73         struct udevice *parent = dev_get_parent(dev);
74
75         /* Need to power on parent node first */
76         if (device_get_uclass_id(parent) == UCLASS_POWER_DOMAIN) {
77                 parent_domain.dev = parent;
78                 err = imx8_power_domain_on(&parent_domain);
79                 if (err)
80                         return err;
81         }
82
83         pdata = (struct imx8_power_domain_platdata *)dev_get_platdata(dev);
84         ppriv = (struct imx8_power_domain_priv *)dev_get_priv(dev);
85
86         debug("%s(power_domain=%s) resource_id %d\n", __func__, dev->name,
87               pdata->resource_id);
88
89         /* Already powered on */
90         if (ppriv->state_on)
91                 return 0;
92
93         if (pdata->resource_id != SC_R_LAST) {
94                 ret = sc_pm_set_resource_power_mode(-1, pdata->resource_id,
95                                                     SC_PM_PW_MODE_ON);
96                 if (ret) {
97                         printf("Error: %s Power up failed! (error = %d)\n",
98                                dev->name, ret);
99                         return -EIO;
100                 }
101         }
102
103         ppriv->state_on = true;
104         debug("%s is powered on\n", dev->name);
105
106         return 0;
107 }
108
109 static int imx8_power_domain_off_node(struct power_domain *power_domain)
110 {
111         struct udevice *dev = power_domain->dev;
112         struct udevice *child;
113         struct imx8_power_domain_priv *ppriv;
114         struct imx8_power_domain_priv *child_ppriv;
115         struct imx8_power_domain_platdata *pdata;
116         sc_err_t ret;
117
118         ppriv = dev_get_priv(dev);
119         pdata = dev_get_platdata(dev);
120
121         debug("%s, %s, state_on %d\n", __func__, dev->name, ppriv->state_on);
122
123         /* Already powered off */
124         if (!ppriv->state_on)
125                 return 0;
126
127         /* Check if all subnodes are off */
128         for (device_find_first_child(dev, &child);
129                 child;
130                 device_find_next_child(&child)) {
131                 if (device_active(child)) {
132                         child_ppriv =
133                         (struct imx8_power_domain_priv *)dev_get_priv(child);
134                         if (child_ppriv->state_on)
135                                 return -EPERM;
136                 }
137         }
138
139         if (pdata->resource_id != SC_R_LAST) {
140                 if (!sc_rm_is_resource_owned(-1, pdata->resource_id)) {
141                         printf("%s not owned by curr partition\n", dev->name);
142                         return 0;
143                 }
144                 ret = sc_pm_set_resource_power_mode(-1, pdata->resource_id,
145                                                     SC_PM_PW_MODE_OFF);
146                 if (ret) {
147                         printf("Error: %s Power off failed! (error = %d)\n",
148                                dev->name, ret);
149                         return -EIO;
150                 }
151         }
152
153         ppriv->state_on = false;
154         debug("%s is powered off\n", dev->name);
155
156         return 0;
157 }
158
159 static int imx8_power_domain_off_parentnodes(struct power_domain *power_domain)
160 {
161         struct udevice *dev = power_domain->dev;
162         struct udevice *parent = dev_get_parent(dev);
163         struct udevice *child;
164         struct imx8_power_domain_priv *ppriv;
165         struct imx8_power_domain_priv *child_ppriv;
166         struct imx8_power_domain_platdata *pdata;
167         sc_err_t ret;
168         struct power_domain parent_pd;
169
170         if (device_get_uclass_id(parent) == UCLASS_POWER_DOMAIN) {
171                 pdata =
172                 (struct imx8_power_domain_platdata *)dev_get_platdata(parent);
173                 ppriv = (struct imx8_power_domain_priv *)dev_get_priv(parent);
174
175                 debug("%s, %s, state_on %d\n", __func__, parent->name,
176                       ppriv->state_on);
177
178                 /* Already powered off */
179                 if (!ppriv->state_on)
180                         return 0;
181
182                 /*
183                  * Check if all sibling nodes are off. If yes,
184                  * power off parent
185                  */
186                 for (device_find_first_child(parent, &child); child;
187                      device_find_next_child(&child)) {
188                         if (device_active(child)) {
189                                 child_ppriv = (struct imx8_power_domain_priv *)
190                                                 dev_get_priv(child);
191                                 /* Find a power on sibling */
192                                 if (child_ppriv->state_on) {
193                                         debug("sibling %s, state_on %d\n",
194                                               child->name,
195                                               child_ppriv->state_on);
196                                         return 0;
197                                 }
198                         }
199                 }
200
201                 /* power off parent */
202                 if (pdata->resource_id != SC_R_LAST) {
203                         ret = sc_pm_set_resource_power_mode(-1,
204                                                             pdata->resource_id,
205                                                             SC_PM_PW_MODE_OFF);
206                         if (ret) {
207                                 printf("%s Power off failed! (error = %d)\n",
208                                        parent->name, ret);
209                                 return -EIO;
210                         }
211                 }
212
213                 ppriv->state_on = false;
214                 debug("%s is powered off\n", parent->name);
215
216                 parent_pd.dev = parent;
217                 imx8_power_domain_off_parentnodes(&parent_pd);
218         }
219
220         return 0;
221 }
222
223 static int imx8_power_domain_off(struct power_domain *power_domain)
224 {
225         int ret;
226
227         debug("%s(power_domain=%p)\n", __func__, power_domain);
228
229         /* Turn off the node */
230         ret = imx8_power_domain_off_node(power_domain);
231         if (ret) {
232                 debug("Can't power off the node of dev %s, ret = %d\n",
233                       power_domain->dev->name, ret);
234                 return ret;
235         }
236
237         /* Turn off parent nodes, if sibling nodes are all off */
238         ret = imx8_power_domain_off_parentnodes(power_domain);
239         if (ret) {
240                 printf("Failed to power off parent nodes of dev %s, ret = %d\n",
241                        power_domain->dev->name, ret);
242                 return ret;
243         }
244
245         return 0;
246 }
247
248 static int imx8_power_domain_of_xlate(struct power_domain *power_domain,
249                                       struct ofnode_phandle_args *args)
250 {
251         debug("%s(power_domain=%p)\n", __func__, power_domain);
252
253         /* Do nothing to the xlate, since we don't have args used */
254
255         return 0;
256 }
257
258 static int imx8_power_domain_bind(struct udevice *dev)
259 {
260         int offset;
261         const char *name;
262         int ret = 0;
263
264         debug("%s(dev=%p)\n", __func__, dev);
265
266         offset = dev_of_offset(dev);
267         for (offset = fdt_first_subnode(gd->fdt_blob, offset); offset > 0;
268              offset = fdt_next_subnode(gd->fdt_blob, offset)) {
269                 /* Bind the subnode to this driver */
270                 name = fdt_get_name(gd->fdt_blob, offset, NULL);
271
272                 ret = device_bind_with_driver_data(dev, dev->driver, name,
273                                                    dev->driver_data,
274                                                    offset_to_ofnode(offset),
275                                                    NULL);
276
277                 if (ret == -ENODEV)
278                         printf("Driver '%s' refuses to bind\n",
279                                dev->driver->name);
280
281                 if (ret)
282                         printf("Error binding driver '%s': %d\n",
283                                dev->driver->name, ret);
284         }
285
286         return 0;
287 }
288
289 static int imx8_power_domain_probe(struct udevice *dev)
290 {
291         struct imx8_power_domain_priv *ppriv;
292
293         debug("%s(dev=%s)\n", __func__, dev->name);
294
295         ppriv = (struct imx8_power_domain_priv *)dev_get_priv(dev);
296
297         /* Set default to power off */
298         if (ppriv)
299                 ppriv->state_on = false;
300
301         return 0;
302 }
303
304 static int imx8_power_domain_ofdata_to_platdata(struct udevice *dev)
305 {
306         int reg;
307         struct imx8_power_domain_platdata *pdata = dev_get_platdata(dev);
308
309         reg = fdtdec_get_int(gd->fdt_blob, dev_of_offset(dev), "reg", -1);
310         if (reg == -1) {
311                 debug("%s: Invalid resource id %d\n", __func__, reg);
312                 return -EINVAL;
313         }
314         pdata->resource_id = (sc_rsrc_t)reg;
315
316         debug("%s resource_id %d\n", __func__, pdata->resource_id);
317
318         return 0;
319 }
320
321 static const struct udevice_id imx8_power_domain_ids[] = {
322         { .compatible = "nxp,imx8-pd" },
323         { }
324 };
325
326 struct power_domain_ops imx8_power_domain_ops = {
327         .request = imx8_power_domain_request,
328         .rfree = imx8_power_domain_free,
329         .on = imx8_power_domain_on,
330         .off = imx8_power_domain_off,
331         .of_xlate = imx8_power_domain_of_xlate,
332 };
333
334 U_BOOT_DRIVER(imx8_power_domain) = {
335         .name = "imx8_power_domain",
336         .id = UCLASS_POWER_DOMAIN,
337         .of_match = imx8_power_domain_ids,
338         .bind = imx8_power_domain_bind,
339         .probe = imx8_power_domain_probe,
340         .ofdata_to_platdata = imx8_power_domain_ofdata_to_platdata,
341         .platdata_auto_alloc_size = sizeof(struct imx8_power_domain_platdata),
342         .priv_auto_alloc_size = sizeof(struct imx8_power_domain_priv),
343         .ops = &imx8_power_domain_ops,
344 };