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