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