Browse Source

[Feature] Support simple power domain API (#9005)

* [Feature] Power domain for device

1.Support device power on/off.
2.Support attach/detach device.
3.Support power domain driver api.

Signed-off-by: GuEe-GUI <2991707448@qq.com>

* [DM/platform] Enhanced platform bus

1.Add power domain for device.
2.Support `remove` and `shutdown` bus interface.

Signed-off-by: GuEe-GUI <2991707448@qq.com>

---------

Signed-off-by: GuEe-GUI <2991707448@qq.com>
GUI 11 months ago
parent
commit
e7cddf3a52

+ 1 - 1
components/drivers/core/SConscript

@@ -8,7 +8,7 @@ if GetDepend(['RT_USING_DEV_BUS']) or GetDepend(['RT_USING_DM']):
     src = src + ['bus.c']
     src = src + ['bus.c']
 
 
 if GetDepend(['RT_USING_DM']):
 if GetDepend(['RT_USING_DM']):
-    src = src + ['dm.c', 'driver.c', 'numa.c', 'platform.c']
+    src = src + ['dm.c', 'driver.c', 'numa.c', 'platform.c', 'power_domain.c']
 
 
     if GetDepend(['RT_USING_DFS']):
     if GetDepend(['RT_USING_DFS']):
         src += ['mnt.c'];
         src += ['mnt.c'];

+ 46 - 0
components/drivers/core/platform.c

@@ -18,6 +18,7 @@
 #include <drivers/platform.h>
 #include <drivers/platform.h>
 #include <drivers/core/bus.h>
 #include <drivers/core/bus.h>
 #include <drivers/core/dm.h>
 #include <drivers/core/dm.h>
+#include <drivers/core/power_domain.h>
 
 
 static struct rt_bus platform_bus;
 static struct rt_bus platform_bus;
 
 
@@ -118,6 +119,15 @@ static rt_err_t platform_probe(rt_device_t dev)
     struct rt_ofw_node *np = dev->ofw_node;
     struct rt_ofw_node *np = dev->ofw_node;
 #endif
 #endif
 
 
+    err = rt_dm_power_domain_attach(dev, RT_TRUE);
+
+    if (err && err != -RT_EEMPTY)
+    {
+        LOG_E("Attach power domain error = %s in device %s", pdev->name, rt_strerror(err));
+
+        return err;
+    }
+
     err = pdrv->probe(pdev);
     err = pdrv->probe(pdev);
 
 
     if (!err)
     if (!err)
@@ -135,16 +145,52 @@ static rt_err_t platform_probe(rt_device_t dev)
         {
         {
             LOG_W("System not memory in driver %s", pdrv->name);
             LOG_W("System not memory in driver %s", pdrv->name);
         }
         }
+
+        rt_dm_power_domain_detach(dev, RT_TRUE);
     }
     }
 
 
     return err;
     return err;
 }
 }
 
 
+static rt_err_t platform_remove(rt_device_t dev)
+{
+    struct rt_platform_driver *pdrv = rt_container_of(dev->drv, struct rt_platform_driver, parent);
+    struct rt_platform_device *pdev = rt_container_of(dev, struct rt_platform_device, parent);
+
+    if (pdrv && pdrv->remove)
+    {
+        pdrv->remove(pdev);
+    }
+
+    rt_dm_power_domain_detach(dev, RT_TRUE);
+    rt_platform_ofw_free(pdev);
+
+    return RT_EOK;
+}
+
+static rt_err_t platform_shutdown(rt_device_t dev)
+{
+    struct rt_platform_driver *pdrv = rt_container_of(dev->drv, struct rt_platform_driver, parent);
+    struct rt_platform_device *pdev = rt_container_of(dev, struct rt_platform_device, parent);
+
+    if (pdrv && pdrv->shutdown)
+    {
+        pdrv->shutdown(pdev);
+    }
+
+    rt_dm_power_domain_detach(dev, RT_TRUE);
+    rt_platform_ofw_free(pdev);
+
+    return RT_EOK;
+}
+
 static struct rt_bus platform_bus =
 static struct rt_bus platform_bus =
 {
 {
     .name = "platform",
     .name = "platform",
     .match = platform_match,
     .match = platform_match,
     .probe = platform_probe,
     .probe = platform_probe,
+    .remove = platform_remove,
+    .shutdown = platform_shutdown,
 };
 };
 
 
 static int platform_bus_init(void)
 static int platform_bus_init(void)

+ 24 - 2
components/drivers/core/platform_ofw.c

@@ -92,9 +92,7 @@ static struct rt_platform_device *alloc_ofw_platform_device(struct rt_ofw_node *
         rt_ofw_node_get(np);
         rt_ofw_node_get(np);
         rt_ofw_node_set_flag(np, RT_OFW_F_PLATFORM);
         rt_ofw_node_set_flag(np, RT_OFW_F_PLATFORM);
 
 
-#ifdef RT_USING_OFW
         pdev->parent.ofw_node = np;
         pdev->parent.ofw_node = np;
-#endif
 
 
         ofw_device_rename(&pdev->parent);
         ofw_device_rename(&pdev->parent);
     }
     }
@@ -232,3 +230,27 @@ static int platform_ofw_device_probe(void)
     return (int)err;
     return (int)err;
 }
 }
 INIT_PLATFORM_EXPORT(platform_ofw_device_probe);
 INIT_PLATFORM_EXPORT(platform_ofw_device_probe);
+
+rt_err_t rt_platform_ofw_free(struct rt_platform_device *pdev)
+{
+    rt_err_t err = RT_EOK;
+
+    if (pdev)
+    {
+        struct rt_ofw_node *np = pdev->parent.ofw_node;
+
+        if (np)
+        {
+            rt_ofw_node_clear_flag(np, RT_OFW_F_PLATFORM);
+            rt_ofw_node_put(np);
+
+            pdev->parent.ofw_node = RT_NULL;
+        }
+    }
+    else
+    {
+        err = -RT_EINVAL;
+    }
+
+    return err;
+}

+ 477 - 0
components/drivers/core/power_domain.c

@@ -0,0 +1,477 @@
+/*
+ * Copyright (c) 2006-2023, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date           Author       Notes
+ * 2022-09-24     GuEe-GUI     the first version
+ */
+
+#include <rtdevice.h>
+
+#define DBG_TAG "rtdm.power_domain"
+#define DBG_LVL DBG_INFO
+#include <rtdbg.h>
+
+#include <drivers/ofw.h>
+
+void rt_dm_power_domain_proxy_default_name(struct rt_dm_power_domain_proxy *proxy)
+{
+#if RT_NAME_MAX > 0
+    rt_strncpy(proxy->parent.name, RT_POWER_DOMAIN_OBJ_NAME, RT_NAME_MAX);
+#else
+    proxy->parent.name = RT_POWER_DOMAIN_OBJ_NAME;
+#endif
+}
+
+void rt_dm_power_domain_proxy_ofw_bind(struct rt_dm_power_domain_proxy *proxy,
+        struct rt_ofw_node *np)
+{
+    if (!proxy || !proxy->ofw_parse || !np)
+    {
+        return;
+    }
+
+    rt_dm_power_domain_proxy_default_name(proxy);
+    rt_ofw_data(np) = proxy;
+}
+
+static void dm_power_domain_init(struct rt_dm_power_domain *domain)
+{
+#if RT_NAME_MAX > 0
+    rt_strncpy(domain->parent.name, RT_POWER_DOMAIN_OBJ_NAME, RT_NAME_MAX);
+#else
+    domain->parent.name = RT_POWER_DOMAIN_OBJ_NAME;
+#endif
+
+    domain->parent_domain = RT_NULL;
+
+    rt_list_init(&domain->list);
+    rt_list_init(&domain->child_nodes);
+    rt_list_init(&domain->unit_nodes);
+
+    rt_ref_init(&domain->ref);
+    rt_spin_lock_init(&domain->lock);
+}
+
+static rt_bool_t dm_power_domain_is_free(struct rt_dm_power_domain *domain)
+{
+    return rt_ref_read(&domain->ref) == 1 && !rt_list_isempty(&domain->child_nodes);
+}
+
+rt_err_t rt_dm_power_domain_register(struct rt_dm_power_domain *domain)
+{
+    if (!domain)
+    {
+        return -RT_EINVAL;
+    }
+
+    dm_power_domain_init(domain);
+
+    return RT_EOK;
+}
+
+rt_err_t rt_dm_power_domain_unregister(struct rt_dm_power_domain *domain)
+{
+    rt_err_t err = RT_EOK;
+
+    if (!domain)
+    {
+        return -RT_EINVAL;
+    }
+
+    if (!dm_power_domain_is_free(domain))
+    {
+        return -RT_EBUSY;
+    }
+
+    if (domain->parent_domain)
+    {
+        err = rt_dm_power_domain_unregister_child(domain->parent_domain, domain);
+    }
+
+    return err;
+}
+
+rt_err_t rt_dm_power_domain_register_child(struct rt_dm_power_domain *domain,
+        struct rt_dm_power_domain *child_domain)
+{
+    if (!domain || !child_domain)
+    {
+        return -RT_EINVAL;
+    }
+
+    dm_power_domain_init(child_domain);
+    child_domain->parent_domain = domain;
+
+    return RT_EOK;
+}
+
+rt_err_t rt_dm_power_domain_unregister_child(struct rt_dm_power_domain *domain,
+        struct rt_dm_power_domain *child_domain)
+{
+    rt_err_t err = RT_EOK;
+
+    if (!domain || !child_domain)
+    {
+        return -RT_EINVAL;
+    }
+
+    rt_hw_spin_lock(&domain->lock.lock);
+
+    if (dm_power_domain_is_free(domain))
+    {
+        rt_list_remove(&child_domain->list);
+    }
+    else
+    {
+        err = -RT_EBUSY;
+    }
+
+    rt_hw_spin_unlock(&domain->lock.lock);
+
+    return err;
+}
+
+rt_err_t rt_dm_power_domain_power_on(struct rt_dm_power_domain *domain)
+{
+    rt_err_t err = RT_EOK;
+    struct rt_dm_power_domain *child_domain;
+
+    if (!domain)
+    {
+        return -RT_EINVAL;
+    }
+
+    rt_hw_spin_lock(&domain->lock.lock);
+
+    if (rt_ref_read(&domain->ref) == 1)
+    {
+        err = domain->power_on(domain);
+    }
+
+    if (!err)
+    {
+        struct rt_dm_power_domain *fail_domain = RT_NULL;
+
+        rt_list_for_each_entry(child_domain, &domain->child_nodes, list)
+        {
+            err = rt_dm_power_domain_power_on(child_domain);
+
+            if (err)
+            {
+                fail_domain = child_domain;
+                break;
+            }
+        }
+
+        if (fail_domain)
+        {
+            rt_list_for_each_entry(child_domain, &domain->child_nodes, list)
+            {
+                if (child_domain == fail_domain)
+                {
+                    break;
+                }
+
+                rt_dm_power_domain_power_off(child_domain);
+            }
+        }
+    }
+
+    rt_hw_spin_unlock(&domain->lock.lock);
+
+    if (!err)
+    {
+        rt_ref_get(&domain->ref);
+    }
+
+    return err;
+}
+
+static void dm_power_domain_release(struct rt_ref *r)
+{
+    struct rt_dm_power_domain *domain = rt_container_of(r, struct rt_dm_power_domain, ref);
+
+    if (domain->dev)
+    {
+        LOG_E("%s power domain is release", rt_dm_dev_get_name(domain->dev));
+    }
+
+    RT_ASSERT(0);
+}
+
+rt_err_t rt_dm_power_domain_power_off(struct rt_dm_power_domain *domain)
+{
+    rt_err_t err;
+    struct rt_dm_power_domain *child_domain;
+
+    if (!domain)
+    {
+        return -RT_EINVAL;
+    }
+
+    rt_ref_put(&domain->ref, dm_power_domain_release);
+
+    rt_hw_spin_lock(&domain->lock.lock);
+
+    if (rt_ref_read(&domain->ref) == 1)
+    {
+        err = domain->power_off(domain);
+    }
+    else
+    {
+        err = -RT_EBUSY;
+    }
+
+    if (!err)
+    {
+        struct rt_dm_power_domain *fail_domain = RT_NULL;
+
+        rt_list_for_each_entry(child_domain, &domain->child_nodes, list)
+        {
+            err = rt_dm_power_domain_power_off(child_domain);
+
+            if (err)
+            {
+                fail_domain = child_domain;
+                break;
+            }
+        }
+
+        if (fail_domain)
+        {
+            rt_list_for_each_entry(child_domain, &domain->child_nodes, list)
+            {
+                if (child_domain == fail_domain)
+                {
+                    break;
+                }
+
+                rt_dm_power_domain_power_on(child_domain);
+            }
+        }
+    }
+
+    rt_hw_spin_unlock(&domain->lock.lock);
+
+    if (err)
+    {
+        rt_ref_get(&domain->ref);
+    }
+
+    return err;
+}
+
+#ifdef RT_USING_OFW
+static struct rt_dm_power_domain *ofw_find_power_domain(struct rt_device *dev,
+        int index, struct rt_ofw_cell_args *args)
+{
+    struct rt_object *obj;
+    struct rt_dm_power_domain_proxy *proxy;
+    struct rt_dm_power_domain *domain = RT_NULL;
+    struct rt_ofw_node *np = dev->ofw_node, *power_domain_np;
+
+    if (!rt_ofw_parse_phandle_cells(np, "power-domains", "#power-domain-cells",
+            index, args))
+    {
+        power_domain_np = args->data;
+
+        if (power_domain_np && (obj = rt_ofw_data(power_domain_np)))
+        {
+            if (!rt_strcmp(obj->name, RT_POWER_DOMAIN_OBJ_NAME))
+            {
+                proxy = rt_container_of(obj, struct rt_dm_power_domain_proxy, parent);
+                domain = proxy->ofw_parse(proxy, args);
+            }
+            else if (!rt_strcmp(obj->name, RT_POWER_DOMAIN_OBJ_NAME))
+            {
+                domain = rt_container_of(obj, struct rt_dm_power_domain, parent);
+            }
+            else if ((obj = rt_ofw_parse_object(power_domain_np,
+                    RT_POWER_DOMAIN_PROXY_OBJ_NAME, "#power-domain-cells")))
+            {
+                proxy = rt_container_of(obj, struct rt_dm_power_domain_proxy, parent);
+                domain = proxy->ofw_parse(proxy, args);
+            }
+            else if ((obj = rt_ofw_parse_object(power_domain_np,
+                    RT_POWER_DOMAIN_OBJ_NAME, "#power-domain-cells")))
+            {
+                domain = rt_container_of(obj, struct rt_dm_power_domain, parent);
+            }
+
+            rt_ofw_node_put(power_domain_np);
+        }
+    }
+
+    return domain;
+}
+#else
+rt_inline struct rt_dm_power_domain *ofw_find_power_domain(struct rt_device *dev,
+        int index, struct rt_ofw_cell_args *args)
+{
+    return RT_NULL;
+}
+#endif /* RT_USING_OFW */
+
+struct rt_dm_power_domain *rt_dm_power_domain_get_by_index(struct rt_device *dev,
+        int index)
+{
+    struct rt_ofw_cell_args args;
+    struct rt_dm_power_domain *domain;
+
+    if (!dev || index < 0)
+    {
+        return RT_NULL;
+    }
+
+    if ((domain = ofw_find_power_domain(dev, index, &args)))
+    {
+        goto _end;
+    }
+
+_end:
+    return domain;
+}
+
+struct rt_dm_power_domain *rt_dm_power_domain_get_by_name(struct rt_device *dev,
+        const char *name)
+{
+    int index;
+
+    if (!dev || !name)
+    {
+        return RT_NULL;
+    }
+
+    if ((index = rt_dm_dev_prop_index_of_string(dev, "power-domain-names", name)) < 0)
+    {
+        LOG_E("%s find power domain %s not found", rt_dm_dev_get_name(dev));
+
+        return RT_NULL;
+    }
+
+    return rt_dm_power_domain_get_by_index(dev, index);
+}
+
+rt_err_t rt_dm_power_domain_put(struct rt_dm_power_domain *domain)
+{
+    if (!domain)
+    {
+        return -RT_EINVAL;
+    }
+
+    return RT_EOK;
+}
+
+rt_err_t rt_dm_power_domain_attach(struct rt_device *dev, rt_bool_t on)
+{
+    int id = -1;
+    rt_err_t err = RT_EOK;
+    struct rt_ofw_cell_args args;
+    struct rt_dm_power_domain *domain;
+    struct rt_dm_power_domain_unit *unit;
+
+    if (!dev)
+    {
+        return -RT_EINVAL;
+    }
+
+    /* We only attach the first one, get domains self if there are multiple domains */
+    if ((domain = ofw_find_power_domain(dev, 0, &args)))
+    {
+        id = args.args[0];
+    }
+
+    if (!domain)
+    {
+        return -RT_EEMPTY;
+    }
+
+    unit = rt_malloc(sizeof(*unit));
+
+    if (!unit)
+    {
+        return -RT_ENOMEM;
+    }
+
+    rt_list_init(&unit->list);
+    unit->id = id;
+    unit->domain = domain;
+
+    dev->power_domain_unit = unit;
+
+    rt_hw_spin_lock(&domain->lock.lock);
+
+    if (domain->attach_dev)
+    {
+        err = domain->attach_dev(domain, dev);
+    }
+
+    if (!err)
+    {
+        rt_list_insert_before(&domain->unit_nodes, &unit->list);
+    }
+
+    rt_hw_spin_unlock(&domain->lock.lock);
+
+    if (err)
+    {
+        dev->power_domain_unit = RT_NULL;
+        rt_free(unit);
+
+        return err;
+    }
+
+    if (on)
+    {
+        err = rt_dm_power_domain_power_on(domain);
+    }
+
+    return err;
+}
+
+rt_err_t rt_dm_power_domain_detach(struct rt_device *dev, rt_bool_t off)
+{
+    rt_err_t err = RT_EOK;
+    struct rt_dm_power_domain *domain;
+    struct rt_dm_power_domain_unit *unit;
+
+    if (!dev || !dev->power_domain_unit)
+    {
+        return -RT_EINVAL;
+    }
+
+    unit = dev->power_domain_unit;
+    domain = unit->domain;
+
+    rt_hw_spin_lock(&domain->lock.lock);
+
+    if (domain->detach_dev)
+    {
+        err = domain->detach_dev(domain, dev);
+    }
+
+    if (!err)
+    {
+        rt_list_remove(&unit->list);
+    }
+
+    rt_hw_spin_unlock(&domain->lock.lock);
+
+    if (err)
+    {
+        return err;
+    }
+
+    rt_free(unit);
+    dev->power_domain_unit = RT_NULL;
+
+    if (off)
+    {
+        err = rt_dm_power_domain_power_off(domain);
+    }
+
+    return err;
+}

+ 85 - 0
components/drivers/include/drivers/core/power_domain.h

@@ -0,0 +1,85 @@
+/*
+ * Copyright (c) 2006-2022, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date           Author       Notes
+ * 2022-11-21     GuEe-GUI     first version
+ */
+
+#ifndef __RT_DM_POWER_DOMAIN_H__
+#define __RT_DM_POWER_DOMAIN_H__
+
+#include <rthw.h>
+#include <rtthread.h>
+
+#include <ref.h>
+
+#define RT_POWER_DOMAIN_OBJ_NAME        "PMD"
+#define RT_POWER_DOMAIN_PROXY_OBJ_NAME  "PMP"
+
+struct rt_ofw_node;
+struct rt_ofw_cell_args;
+
+struct rt_dm_power_domain_unit
+{
+    rt_list_t list;
+
+    int id;
+    struct rt_dm_power_domain *domain;
+};
+
+struct rt_dm_power_domain_proxy
+{
+    struct rt_object parent;
+
+    struct rt_dm_power_domain *(*ofw_parse)(struct rt_dm_power_domain_proxy *proxy,
+            struct rt_ofw_cell_args *args);
+};
+
+struct rt_dm_power_domain
+{
+    struct rt_object parent;
+
+    struct rt_device *dev;
+    struct rt_dm_power_domain *parent_domain;
+
+    rt_list_t list;
+    rt_list_t child_nodes;
+    rt_list_t unit_nodes;
+
+    struct rt_ref ref;
+    struct rt_spinlock lock;
+
+    rt_err_t (*power_on)(struct rt_dm_power_domain *domain);
+    rt_err_t (*power_off)(struct rt_dm_power_domain *domain);
+    rt_err_t (*attach_dev)(struct rt_dm_power_domain *domain, struct rt_device *dev);
+    rt_err_t (*detach_dev)(struct rt_dm_power_domain *domain, struct rt_device *dev);
+
+    void *pirv;
+};
+
+void rt_dm_power_domain_proxy_default_name(struct rt_dm_power_domain_proxy *proxy);
+void rt_dm_power_domain_proxy_ofw_bind(struct rt_dm_power_domain_proxy *proxy,
+        struct rt_ofw_node *np);
+
+rt_err_t rt_dm_power_domain_register(struct rt_dm_power_domain *domain);
+rt_err_t rt_dm_power_domain_unregister(struct rt_dm_power_domain *domain);
+
+rt_err_t rt_dm_power_domain_register_child(struct rt_dm_power_domain *domain,
+        struct rt_dm_power_domain *child_domain);
+rt_err_t rt_dm_power_domain_unregister_child(struct rt_dm_power_domain *domain,
+        struct rt_dm_power_domain *child_domain);
+
+rt_err_t rt_dm_power_domain_power_on(struct rt_dm_power_domain *domain);
+rt_err_t rt_dm_power_domain_power_off(struct rt_dm_power_domain *domain);
+
+struct rt_dm_power_domain *rt_dm_power_domain_get_by_index(struct rt_device *dev, int index);
+struct rt_dm_power_domain *rt_dm_power_domain_get_by_name(struct rt_device *dev, const char *name);
+rt_err_t rt_dm_power_domain_put(struct rt_dm_power_domain *domain);
+
+rt_err_t rt_dm_power_domain_attach(struct rt_device *dev, rt_bool_t on);
+rt_err_t rt_dm_power_domain_detach(struct rt_device *dev, rt_bool_t off);
+
+#endif /* __RT_DM_POWER_DOMAIN_H__ */

+ 2 - 0
components/drivers/include/drivers/ofw.h

@@ -430,6 +430,8 @@ rt_inline rt_bool_t rt_ofw_node_is_type(const struct rt_ofw_node *np, const char
 struct rt_ofw_stub *rt_ofw_stub_probe_range(struct rt_ofw_node *np,
 struct rt_ofw_stub *rt_ofw_stub_probe_range(struct rt_ofw_node *np,
         const struct rt_ofw_stub *stub_start, const struct rt_ofw_stub *stub_end);
         const struct rt_ofw_stub *stub_start, const struct rt_ofw_stub *stub_end);
 
 
+struct rt_object *rt_ofw_parse_object(struct rt_ofw_node *np, const char *obj_name, const char *cells_name);
+
 rt_err_t rt_ofw_console_setup(void);
 rt_err_t rt_ofw_console_setup(void);
 const char *rt_ofw_bootargs_select(const char *key, int index);
 const char *rt_ofw_bootargs_select(const char *key, int index);
 
 

+ 1 - 0
components/drivers/include/drivers/platform.h

@@ -54,6 +54,7 @@ rt_err_t rt_platform_driver_register(struct rt_platform_driver *pdrv);
 rt_err_t rt_platform_device_register(struct rt_platform_device *pdev);
 rt_err_t rt_platform_device_register(struct rt_platform_device *pdev);
 
 
 rt_err_t rt_platform_ofw_device_probe_child(struct rt_ofw_node *np);
 rt_err_t rt_platform_ofw_device_probe_child(struct rt_ofw_node *np);
+rt_err_t rt_platform_ofw_free(struct rt_platform_device *pdev);
 
 
 #define RT_PLATFORM_DRIVER_EXPORT(driver)  RT_DRIVER_EXPORT(driver, platform, BUILIN)
 #define RT_PLATFORM_DRIVER_EXPORT(driver)  RT_DRIVER_EXPORT(driver, platform, BUILIN)
 
 

+ 1 - 0
components/drivers/include/rtdevice.h

@@ -42,6 +42,7 @@ extern "C" {
 #ifdef RT_USING_DM
 #ifdef RT_USING_DM
 #include "drivers/core/dm.h"
 #include "drivers/core/dm.h"
 #include "drivers/core/numa.h"
 #include "drivers/core/numa.h"
+#include "drivers/core/power_domain.h"
 #include "drivers/platform.h"
 #include "drivers/platform.h"
 
 
 #ifdef RT_USING_OFW
 #ifdef RT_USING_OFW

+ 87 - 0
components/drivers/ofw/ofw.c

@@ -60,6 +60,93 @@ struct rt_ofw_stub *rt_ofw_stub_probe_range(struct rt_ofw_node *np,
     return (struct rt_ofw_stub *)stub;
     return (struct rt_ofw_stub *)stub;
 }
 }
 
 
+struct ofw_obj_cmp_list
+{
+    const char *cells_name;
+    const char *obj_name;
+    rt_size_t obj_size;
+};
+
+static const struct ofw_obj_cmp_list ofw_obj_cmp_list[] =
+{
+    { "#power-domain-cells", RT_POWER_DOMAIN_PROXY_OBJ_NAME, sizeof(struct rt_dm_power_domain_proxy) },
+    { "#power-domain-cells", RT_POWER_DOMAIN_OBJ_NAME, sizeof(struct rt_dm_power_domain) },
+};
+
+static struct rt_object *ofw_parse_object(struct rt_ofw_node *np, const char *cells_name)
+{
+    const struct ofw_obj_cmp_list *item;
+    struct rt_object *obj = rt_ofw_data(np), *ret_obj = RT_NULL;
+    RT_BITMAP_DECLARE(idx_mask, RT_ARRAY_SIZE(ofw_obj_cmp_list)) = {};
+
+    for (int i = 0; i < RT_ARRAY_SIZE(ofw_obj_cmp_list); ++i)
+    {
+        item = &ofw_obj_cmp_list[i];
+
+        if (!rt_ofw_prop_read_bool(np, item->cells_name))
+        {
+            rt_bitmap_set_bit(idx_mask, i);
+        }
+    }
+
+    while (!ret_obj)
+    {
+        int i = 0;
+
+        /* Is print ? */
+        if (!((rt_uint32_t)(obj->name[0] - 0x20) < 0x5f))
+        {
+            break;
+        }
+
+        rt_bitmap_for_each_clear_bit(idx_mask, i, RT_ARRAY_SIZE(ofw_obj_cmp_list))
+        {
+            item = &ofw_obj_cmp_list[i];
+
+            if (!rt_strcmp(item->cells_name, cells_name))
+            {
+                ret_obj = obj;
+                break;
+            }
+
+            if (!rt_strncmp(item->obj_name, obj->name, RT_NAME_MAX))
+            {
+                obj = (struct rt_object *)((rt_ubase_t)obj + item->obj_size);
+                break;
+            }
+        }
+
+        if (i >= RT_ARRAY_SIZE(ofw_obj_cmp_list))
+        {
+            LOG_E("Invalid rt_object = %s", obj->name);
+
+            break;
+        }
+    }
+
+    return ret_obj;
+}
+
+struct rt_object *rt_ofw_parse_object(struct rt_ofw_node *np, const char *obj_name, const char *cells_name)
+{
+    struct rt_object *obj = RT_NULL, *test_obj;
+
+    if (np && (test_obj = rt_ofw_data(np)) && cells_name)
+    {
+        /* The composite object is rare, so we try to find this object as much as possible at once. */
+        if (obj_name && rt_strcmp(test_obj->name, obj_name))
+        {
+            obj = test_obj;
+        }
+        else
+        {
+            obj = ofw_parse_object(np, cells_name);
+        }
+    }
+
+    return obj;
+}
+
 static const char *ofw_console_serial_find(char *dst_con, struct rt_ofw_node *np)
 static const char *ofw_console_serial_find(char *dst_con, struct rt_ofw_node *np)
 {
 {
     rt_object_t rt_obj = RT_NULL;
     rt_object_t rt_obj = RT_NULL;

+ 1 - 0
include/rtdef.h

@@ -1363,6 +1363,7 @@ struct rt_device
 #ifdef RT_USING_OFW
 #ifdef RT_USING_OFW
     void *ofw_node;                                     /**< ofw node get from device tree */
     void *ofw_node;                                     /**< ofw node get from device tree */
 #endif /* RT_USING_OFW */
 #endif /* RT_USING_OFW */
+    void *power_domain_unit;
 #endif /* RT_USING_DM */
 #endif /* RT_USING_DM */
 
 
     enum rt_device_class_type type;                     /**< device type */
     enum rt_device_class_type type;                     /**< device type */