1 /*
2 * Copyright (c) 2006-2023, RT-Thread Development Team
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 *
6 * Change Logs:
7 * Date Author Notes
8 * 2022-09-24 GuEe-GUI the first version
9 */
10
11 #include <rtdevice.h>
12
13 #define DBG_TAG "rtdm.power_domain"
14 #define DBG_LVL DBG_INFO
15 #include <rtdbg.h>
16
17 #include <drivers/ofw.h>
18
rt_dm_power_domain_proxy_default_name(struct rt_dm_power_domain_proxy * proxy)19 void rt_dm_power_domain_proxy_default_name(struct rt_dm_power_domain_proxy *proxy)
20 {
21 #if RT_NAME_MAX > 0
22 rt_strncpy(proxy->parent.name, RT_POWER_DOMAIN_OBJ_NAME, RT_NAME_MAX);
23 #else
24 proxy->parent.name = RT_POWER_DOMAIN_OBJ_NAME;
25 #endif
26 }
27
rt_dm_power_domain_proxy_ofw_bind(struct rt_dm_power_domain_proxy * proxy,struct rt_ofw_node * np)28 void rt_dm_power_domain_proxy_ofw_bind(struct rt_dm_power_domain_proxy *proxy,
29 struct rt_ofw_node *np)
30 {
31 if (!proxy || !proxy->ofw_parse || !np)
32 {
33 return;
34 }
35
36 rt_dm_power_domain_proxy_default_name(proxy);
37 rt_ofw_data(np) = proxy;
38 }
39
dm_power_domain_init(struct rt_dm_power_domain * domain)40 static void dm_power_domain_init(struct rt_dm_power_domain *domain)
41 {
42 #if RT_NAME_MAX > 0
43 rt_strncpy(domain->parent.name, RT_POWER_DOMAIN_OBJ_NAME, RT_NAME_MAX);
44 #else
45 domain->parent.name = RT_POWER_DOMAIN_OBJ_NAME;
46 #endif
47
48 domain->parent_domain = RT_NULL;
49
50 rt_list_init(&domain->list);
51 rt_list_init(&domain->child_nodes);
52 rt_list_init(&domain->unit_nodes);
53
54 rt_ref_init(&domain->ref);
55 rt_spin_lock_init(&domain->lock);
56 }
57
dm_power_domain_is_free(struct rt_dm_power_domain * domain)58 static rt_bool_t dm_power_domain_is_free(struct rt_dm_power_domain *domain)
59 {
60 return rt_ref_read(&domain->ref) == 1 && !rt_list_isempty(&domain->child_nodes);
61 }
62
rt_dm_power_domain_register(struct rt_dm_power_domain * domain)63 rt_err_t rt_dm_power_domain_register(struct rt_dm_power_domain *domain)
64 {
65 if (!domain)
66 {
67 return -RT_EINVAL;
68 }
69
70 dm_power_domain_init(domain);
71
72 return RT_EOK;
73 }
74
rt_dm_power_domain_unregister(struct rt_dm_power_domain * domain)75 rt_err_t rt_dm_power_domain_unregister(struct rt_dm_power_domain *domain)
76 {
77 rt_err_t err = RT_EOK;
78
79 if (!domain)
80 {
81 return -RT_EINVAL;
82 }
83
84 if (!dm_power_domain_is_free(domain))
85 {
86 return -RT_EBUSY;
87 }
88
89 if (domain->parent_domain)
90 {
91 err = rt_dm_power_domain_unregister_child(domain->parent_domain, domain);
92 }
93
94 return err;
95 }
96
rt_dm_power_domain_register_child(struct rt_dm_power_domain * domain,struct rt_dm_power_domain * child_domain)97 rt_err_t rt_dm_power_domain_register_child(struct rt_dm_power_domain *domain,
98 struct rt_dm_power_domain *child_domain)
99 {
100 if (!domain || !child_domain)
101 {
102 return -RT_EINVAL;
103 }
104
105 dm_power_domain_init(child_domain);
106 child_domain->parent_domain = domain;
107
108 return RT_EOK;
109 }
110
rt_dm_power_domain_unregister_child(struct rt_dm_power_domain * domain,struct rt_dm_power_domain * child_domain)111 rt_err_t rt_dm_power_domain_unregister_child(struct rt_dm_power_domain *domain,
112 struct rt_dm_power_domain *child_domain)
113 {
114 rt_err_t err = RT_EOK;
115
116 if (!domain || !child_domain)
117 {
118 return -RT_EINVAL;
119 }
120
121 rt_hw_spin_lock(&domain->lock.lock);
122
123 if (dm_power_domain_is_free(domain))
124 {
125 rt_list_remove(&child_domain->list);
126 }
127 else
128 {
129 err = -RT_EBUSY;
130 }
131
132 rt_hw_spin_unlock(&domain->lock.lock);
133
134 return err;
135 }
136
rt_dm_power_domain_power_on(struct rt_dm_power_domain * domain)137 rt_err_t rt_dm_power_domain_power_on(struct rt_dm_power_domain *domain)
138 {
139 rt_err_t err = RT_EOK;
140 struct rt_dm_power_domain *child_domain;
141
142 if (!domain)
143 {
144 return -RT_EINVAL;
145 }
146
147 rt_hw_spin_lock(&domain->lock.lock);
148
149 if (rt_ref_read(&domain->ref) == 1)
150 {
151 err = domain->power_on(domain);
152 }
153
154 if (!err)
155 {
156 struct rt_dm_power_domain *fail_domain = RT_NULL;
157
158 rt_list_for_each_entry(child_domain, &domain->child_nodes, list)
159 {
160 err = rt_dm_power_domain_power_on(child_domain);
161
162 if (err)
163 {
164 fail_domain = child_domain;
165 break;
166 }
167 }
168
169 if (fail_domain)
170 {
171 rt_list_for_each_entry(child_domain, &domain->child_nodes, list)
172 {
173 if (child_domain == fail_domain)
174 {
175 break;
176 }
177
178 rt_dm_power_domain_power_off(child_domain);
179 }
180 }
181 }
182
183 rt_hw_spin_unlock(&domain->lock.lock);
184
185 if (!err)
186 {
187 rt_ref_get(&domain->ref);
188 }
189
190 return err;
191 }
192
dm_power_domain_release(struct rt_ref * r)193 static void dm_power_domain_release(struct rt_ref *r)
194 {
195 struct rt_dm_power_domain *domain = rt_container_of(r, struct rt_dm_power_domain, ref);
196
197 if (domain->dev)
198 {
199 LOG_E("%s power domain is release", rt_dm_dev_get_name(domain->dev));
200 }
201
202 RT_ASSERT(0);
203 }
204
rt_dm_power_domain_power_off(struct rt_dm_power_domain * domain)205 rt_err_t rt_dm_power_domain_power_off(struct rt_dm_power_domain *domain)
206 {
207 rt_err_t err;
208 struct rt_dm_power_domain *child_domain;
209
210 if (!domain)
211 {
212 return -RT_EINVAL;
213 }
214
215 rt_ref_put(&domain->ref, dm_power_domain_release);
216
217 rt_hw_spin_lock(&domain->lock.lock);
218
219 if (rt_ref_read(&domain->ref) == 1)
220 {
221 err = domain->power_off(domain);
222 }
223 else
224 {
225 err = -RT_EBUSY;
226 }
227
228 if (!err)
229 {
230 struct rt_dm_power_domain *fail_domain = RT_NULL;
231
232 rt_list_for_each_entry(child_domain, &domain->child_nodes, list)
233 {
234 err = rt_dm_power_domain_power_off(child_domain);
235
236 if (err)
237 {
238 fail_domain = child_domain;
239 break;
240 }
241 }
242
243 if (fail_domain)
244 {
245 rt_list_for_each_entry(child_domain, &domain->child_nodes, list)
246 {
247 if (child_domain == fail_domain)
248 {
249 break;
250 }
251
252 rt_dm_power_domain_power_on(child_domain);
253 }
254 }
255 }
256
257 rt_hw_spin_unlock(&domain->lock.lock);
258
259 if (err)
260 {
261 rt_ref_get(&domain->ref);
262 }
263
264 return err;
265 }
266
267 #ifdef RT_USING_OFW
ofw_find_power_domain(struct rt_device * dev,int index,struct rt_ofw_cell_args * args)268 static struct rt_dm_power_domain *ofw_find_power_domain(struct rt_device *dev,
269 int index, struct rt_ofw_cell_args *args)
270 {
271 struct rt_object *obj;
272 struct rt_dm_power_domain_proxy *proxy;
273 struct rt_dm_power_domain *domain = RT_NULL;
274 struct rt_ofw_node *np = dev->ofw_node, *power_domain_np;
275
276 if (!rt_ofw_parse_phandle_cells(np, "power-domains", "#power-domain-cells",
277 index, args))
278 {
279 power_domain_np = args->data;
280
281 if (power_domain_np && (obj = rt_ofw_data(power_domain_np)))
282 {
283 if (!rt_strcmp(obj->name, RT_POWER_DOMAIN_OBJ_NAME))
284 {
285 proxy = rt_container_of(obj, struct rt_dm_power_domain_proxy, parent);
286 domain = proxy->ofw_parse(proxy, args);
287 }
288 else if (!rt_strcmp(obj->name, RT_POWER_DOMAIN_OBJ_NAME))
289 {
290 domain = rt_container_of(obj, struct rt_dm_power_domain, parent);
291 }
292 else if ((obj = rt_ofw_parse_object(power_domain_np,
293 RT_POWER_DOMAIN_PROXY_OBJ_NAME, "#power-domain-cells")))
294 {
295 proxy = rt_container_of(obj, struct rt_dm_power_domain_proxy, parent);
296 domain = proxy->ofw_parse(proxy, args);
297 }
298 else if ((obj = rt_ofw_parse_object(power_domain_np,
299 RT_POWER_DOMAIN_OBJ_NAME, "#power-domain-cells")))
300 {
301 domain = rt_container_of(obj, struct rt_dm_power_domain, parent);
302 }
303
304 rt_ofw_node_put(power_domain_np);
305 }
306 }
307
308 return domain;
309 }
310 #else
ofw_find_power_domain(struct rt_device * dev,int index,struct rt_ofw_cell_args * args)311 rt_inline struct rt_dm_power_domain *ofw_find_power_domain(struct rt_device *dev,
312 int index, struct rt_ofw_cell_args *args)
313 {
314 return RT_NULL;
315 }
316 #endif /* RT_USING_OFW */
317
rt_dm_power_domain_get_by_index(struct rt_device * dev,int index)318 struct rt_dm_power_domain *rt_dm_power_domain_get_by_index(struct rt_device *dev,
319 int index)
320 {
321 struct rt_ofw_cell_args args;
322 struct rt_dm_power_domain *domain;
323
324 if (!dev || index < 0)
325 {
326 return RT_NULL;
327 }
328
329 if ((domain = ofw_find_power_domain(dev, index, &args)))
330 {
331 goto _end;
332 }
333
334 _end:
335 return domain;
336 }
337
rt_dm_power_domain_get_by_name(struct rt_device * dev,const char * name)338 struct rt_dm_power_domain *rt_dm_power_domain_get_by_name(struct rt_device *dev,
339 const char *name)
340 {
341 int index;
342
343 if (!dev || !name)
344 {
345 return RT_NULL;
346 }
347
348 if ((index = rt_dm_dev_prop_index_of_string(dev, "power-domain-names", name)) < 0)
349 {
350 LOG_E("%s find power domain %s not found", rt_dm_dev_get_name(dev));
351
352 return RT_NULL;
353 }
354
355 return rt_dm_power_domain_get_by_index(dev, index);
356 }
357
rt_dm_power_domain_put(struct rt_dm_power_domain * domain)358 rt_err_t rt_dm_power_domain_put(struct rt_dm_power_domain *domain)
359 {
360 if (!domain)
361 {
362 return -RT_EINVAL;
363 }
364
365 return RT_EOK;
366 }
367
rt_dm_power_domain_attach(struct rt_device * dev,rt_bool_t on)368 rt_err_t rt_dm_power_domain_attach(struct rt_device *dev, rt_bool_t on)
369 {
370 int id = -1;
371 rt_err_t err = RT_EOK;
372 struct rt_ofw_cell_args args;
373 struct rt_dm_power_domain *domain;
374 struct rt_dm_power_domain_unit *unit;
375
376 if (!dev)
377 {
378 return -RT_EINVAL;
379 }
380
381 /* We only attach the first one, get domains self if there are multiple domains */
382 if ((domain = ofw_find_power_domain(dev, 0, &args)))
383 {
384 id = args.args[0];
385 }
386
387 if (!domain)
388 {
389 return -RT_EEMPTY;
390 }
391
392 unit = rt_malloc(sizeof(*unit));
393
394 if (!unit)
395 {
396 return -RT_ENOMEM;
397 }
398
399 rt_list_init(&unit->list);
400 unit->id = id;
401 unit->domain = domain;
402
403 dev->power_domain_unit = unit;
404
405 rt_hw_spin_lock(&domain->lock.lock);
406
407 if (domain->attach_dev)
408 {
409 err = domain->attach_dev(domain, dev);
410 }
411
412 if (!err)
413 {
414 rt_list_insert_before(&domain->unit_nodes, &unit->list);
415 }
416
417 rt_hw_spin_unlock(&domain->lock.lock);
418
419 if (err)
420 {
421 dev->power_domain_unit = RT_NULL;
422 rt_free(unit);
423
424 return err;
425 }
426
427 if (on)
428 {
429 err = rt_dm_power_domain_power_on(domain);
430 }
431
432 return err;
433 }
434
rt_dm_power_domain_detach(struct rt_device * dev,rt_bool_t off)435 rt_err_t rt_dm_power_domain_detach(struct rt_device *dev, rt_bool_t off)
436 {
437 rt_err_t err = RT_EOK;
438 struct rt_dm_power_domain *domain;
439 struct rt_dm_power_domain_unit *unit;
440
441 if (!dev || !dev->power_domain_unit)
442 {
443 return -RT_EINVAL;
444 }
445
446 unit = dev->power_domain_unit;
447 domain = unit->domain;
448
449 rt_hw_spin_lock(&domain->lock.lock);
450
451 if (domain->detach_dev)
452 {
453 err = domain->detach_dev(domain, dev);
454 }
455
456 if (!err)
457 {
458 rt_list_remove(&unit->list);
459 }
460
461 rt_hw_spin_unlock(&domain->lock.lock);
462
463 if (err)
464 {
465 return err;
466 }
467
468 rt_free(unit);
469 dev->power_domain_unit = RT_NULL;
470
471 if (off)
472 {
473 err = rt_dm_power_domain_power_off(domain);
474 }
475
476 return err;
477 }
478