This source file includes following definitions.
- zynqmp_gpd_is_active_wakeup_path
- zynqmp_gpd_power_on
- zynqmp_gpd_power_off
- zynqmp_gpd_attach_dev
- zynqmp_gpd_detach_dev
- zynqmp_gpd_probe
- zynqmp_gpd_remove
1
2
3
4
5
6
7
8
9
10
11
12 #include <linux/err.h>
13 #include <linux/list.h>
14 #include <linux/module.h>
15 #include <linux/of_platform.h>
16 #include <linux/platform_device.h>
17 #include <linux/pm_domain.h>
18 #include <linux/slab.h>
19
20 #include <linux/firmware/xlnx-zynqmp.h>
21
22 #define ZYNQMP_NUM_DOMAINS (100)
23
24 #define ZYNQMP_PM_DOMAIN_REQUESTED BIT(0)
25
26 static const struct zynqmp_eemi_ops *eemi_ops;
27
28
29
30
31
32
33
34 struct zynqmp_pm_domain {
35 struct generic_pm_domain gpd;
36 u32 node_id;
37 u8 flags;
38 };
39
40
41
42
43
44
45
46
47
48
49
50
51 static int zynqmp_gpd_is_active_wakeup_path(struct device *dev, void *not_used)
52 {
53 int may_wakeup;
54
55 may_wakeup = device_may_wakeup(dev);
56 if (may_wakeup)
57 return may_wakeup;
58
59 return device_for_each_child(dev, NULL,
60 zynqmp_gpd_is_active_wakeup_path);
61 }
62
63
64
65
66
67
68
69
70
71
72 static int zynqmp_gpd_power_on(struct generic_pm_domain *domain)
73 {
74 int ret;
75 struct zynqmp_pm_domain *pd;
76
77 if (!eemi_ops->set_requirement)
78 return -ENXIO;
79
80 pd = container_of(domain, struct zynqmp_pm_domain, gpd);
81 ret = eemi_ops->set_requirement(pd->node_id,
82 ZYNQMP_PM_CAPABILITY_ACCESS,
83 ZYNQMP_PM_MAX_QOS,
84 ZYNQMP_PM_REQUEST_ACK_BLOCKING);
85 if (ret) {
86 pr_err("%s() %s set requirement for node %d failed: %d\n",
87 __func__, domain->name, pd->node_id, ret);
88 return ret;
89 }
90
91 pr_debug("%s() Powered on %s domain\n", __func__, domain->name);
92 return 0;
93 }
94
95
96
97
98
99
100
101
102
103
104 static int zynqmp_gpd_power_off(struct generic_pm_domain *domain)
105 {
106 int ret;
107 struct pm_domain_data *pdd, *tmp;
108 struct zynqmp_pm_domain *pd;
109 u32 capabilities = 0;
110 bool may_wakeup;
111
112 if (!eemi_ops->set_requirement)
113 return -ENXIO;
114
115 pd = container_of(domain, struct zynqmp_pm_domain, gpd);
116
117
118 if (!(pd->flags & ZYNQMP_PM_DOMAIN_REQUESTED)) {
119 pr_debug("%s() %s domain is already released\n",
120 __func__, domain->name);
121 return 0;
122 }
123
124 list_for_each_entry_safe(pdd, tmp, &domain->dev_list, list_node) {
125
126 may_wakeup = zynqmp_gpd_is_active_wakeup_path(pdd->dev, NULL);
127 if (may_wakeup) {
128 dev_dbg(pdd->dev, "device is in wakeup path in %s\n",
129 domain->name);
130 capabilities = ZYNQMP_PM_CAPABILITY_WAKEUP;
131 break;
132 }
133 }
134
135 ret = eemi_ops->set_requirement(pd->node_id, capabilities, 0,
136 ZYNQMP_PM_REQUEST_ACK_NO);
137
138
139
140
141 if (ret) {
142 pr_err("%s() %s set requirement for node %d failed: %d\n",
143 __func__, domain->name, pd->node_id, ret);
144 return ret;
145 }
146
147 pr_debug("%s() Powered off %s domain\n", __func__, domain->name);
148 return 0;
149 }
150
151
152
153
154
155
156
157
158 static int zynqmp_gpd_attach_dev(struct generic_pm_domain *domain,
159 struct device *dev)
160 {
161 int ret;
162 struct zynqmp_pm_domain *pd;
163
164 if (!eemi_ops->request_node)
165 return -ENXIO;
166
167 pd = container_of(domain, struct zynqmp_pm_domain, gpd);
168
169
170 if (domain->device_count)
171 return 0;
172
173 ret = eemi_ops->request_node(pd->node_id, 0, 0,
174 ZYNQMP_PM_REQUEST_ACK_BLOCKING);
175
176 if (ret) {
177 pr_err("%s() %s request failed for node %d: %d\n",
178 __func__, domain->name, pd->node_id, ret);
179 return ret;
180 }
181
182 pd->flags |= ZYNQMP_PM_DOMAIN_REQUESTED;
183
184 pr_debug("%s() %s attached to %s domain\n", __func__,
185 dev_name(dev), domain->name);
186 return 0;
187 }
188
189
190
191
192
193
194 static void zynqmp_gpd_detach_dev(struct generic_pm_domain *domain,
195 struct device *dev)
196 {
197 int ret;
198 struct zynqmp_pm_domain *pd;
199
200 if (!eemi_ops->release_node)
201 return;
202
203 pd = container_of(domain, struct zynqmp_pm_domain, gpd);
204
205
206 if (domain->device_count)
207 return;
208
209 ret = eemi_ops->release_node(pd->node_id);
210
211 if (ret) {
212 pr_err("%s() %s release failed for node %d: %d\n",
213 __func__, domain->name, pd->node_id, ret);
214 return;
215 }
216
217 pd->flags &= ~ZYNQMP_PM_DOMAIN_REQUESTED;
218
219 pr_debug("%s() %s detached from %s domain\n", __func__,
220 dev_name(dev), domain->name);
221 }
222
223 static struct generic_pm_domain *zynqmp_gpd_xlate
224 (struct of_phandle_args *genpdspec, void *data)
225 {
226 struct genpd_onecell_data *genpd_data = data;
227 unsigned int i, idx = genpdspec->args[0];
228 struct zynqmp_pm_domain *pd;
229
230 pd = container_of(genpd_data->domains[0], struct zynqmp_pm_domain, gpd);
231
232 if (genpdspec->args_count != 1)
233 return ERR_PTR(-EINVAL);
234
235
236 for (i = 0; i < ZYNQMP_NUM_DOMAINS; i++) {
237 if (pd[i].node_id == idx)
238 goto done;
239 }
240
241
242
243
244
245 for (i = 0; i < ZYNQMP_NUM_DOMAINS; i++) {
246 if (pd[i].node_id == 0) {
247 pd[i].node_id = idx;
248 break;
249 }
250 }
251
252 done:
253 if (!genpd_data->domains[i] || i == ZYNQMP_NUM_DOMAINS)
254 return ERR_PTR(-ENOENT);
255
256 return genpd_data->domains[i];
257 }
258
259 static int zynqmp_gpd_probe(struct platform_device *pdev)
260 {
261 int i;
262 struct genpd_onecell_data *zynqmp_pd_data;
263 struct generic_pm_domain **domains;
264 struct zynqmp_pm_domain *pd;
265 struct device *dev = &pdev->dev;
266
267 eemi_ops = zynqmp_pm_get_eemi_ops();
268 if (IS_ERR(eemi_ops))
269 return PTR_ERR(eemi_ops);
270
271 pd = devm_kcalloc(dev, ZYNQMP_NUM_DOMAINS, sizeof(*pd), GFP_KERNEL);
272 if (!pd)
273 return -ENOMEM;
274
275 zynqmp_pd_data = devm_kzalloc(dev, sizeof(*zynqmp_pd_data), GFP_KERNEL);
276 if (!zynqmp_pd_data)
277 return -ENOMEM;
278
279 zynqmp_pd_data->xlate = zynqmp_gpd_xlate;
280
281 domains = devm_kcalloc(dev, ZYNQMP_NUM_DOMAINS, sizeof(*domains),
282 GFP_KERNEL);
283 if (!domains)
284 return -ENOMEM;
285
286 for (i = 0; i < ZYNQMP_NUM_DOMAINS; i++, pd++) {
287 pd->node_id = 0;
288 pd->gpd.name = kasprintf(GFP_KERNEL, "domain%d", i);
289 pd->gpd.power_off = zynqmp_gpd_power_off;
290 pd->gpd.power_on = zynqmp_gpd_power_on;
291 pd->gpd.attach_dev = zynqmp_gpd_attach_dev;
292 pd->gpd.detach_dev = zynqmp_gpd_detach_dev;
293
294 domains[i] = &pd->gpd;
295
296
297 pm_genpd_init(&pd->gpd, NULL, true);
298 }
299
300 zynqmp_pd_data->domains = domains;
301 zynqmp_pd_data->num_domains = ZYNQMP_NUM_DOMAINS;
302 of_genpd_add_provider_onecell(dev->parent->of_node, zynqmp_pd_data);
303
304 return 0;
305 }
306
307 static int zynqmp_gpd_remove(struct platform_device *pdev)
308 {
309 of_genpd_del_provider(pdev->dev.parent->of_node);
310
311 return 0;
312 }
313
314 static struct platform_driver zynqmp_power_domain_driver = {
315 .driver = {
316 .name = "zynqmp_power_controller",
317 },
318 .probe = zynqmp_gpd_probe,
319 .remove = zynqmp_gpd_remove,
320 };
321 module_platform_driver(zynqmp_power_domain_driver);
322
323 MODULE_ALIAS("platform:zynqmp_power_controller");