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