diff --git a/components/drivers/core/SConscript b/components/drivers/core/SConscript index e1f003549fb..f3f3fbd313c 100644 --- a/components/drivers/core/SConscript +++ b/components/drivers/core/SConscript @@ -8,7 +8,7 @@ if GetDepend(['RT_USING_DEV_BUS']) or GetDepend(['RT_USING_DM']): src = src + ['bus.c'] 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']): src += ['mnt.c']; diff --git a/components/drivers/core/platform.c b/components/drivers/core/platform.c index 18c044f6c4f..d8c6679a6d5 100644 --- a/components/drivers/core/platform.c +++ b/components/drivers/core/platform.c @@ -18,6 +18,7 @@ #include #include #include +#include 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; #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); 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); } + + rt_dm_power_domain_detach(dev, RT_TRUE); } 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 = { .name = "platform", .match = platform_match, .probe = platform_probe, + .remove = platform_remove, + .shutdown = platform_shutdown, }; static int platform_bus_init(void) diff --git a/components/drivers/core/platform_ofw.c b/components/drivers/core/platform_ofw.c index 408dec9dc7b..f305fde62ba 100644 --- a/components/drivers/core/platform_ofw.c +++ b/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_set_flag(np, RT_OFW_F_PLATFORM); -#ifdef RT_USING_OFW pdev->parent.ofw_node = np; -#endif ofw_device_rename(&pdev->parent); } @@ -232,3 +230,27 @@ static int platform_ofw_device_probe(void) return (int)err; } 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; +} diff --git a/components/drivers/core/power_domain.c b/components/drivers/core/power_domain.c new file mode 100644 index 00000000000..254010b4fe4 --- /dev/null +++ b/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 + +#define DBG_TAG "rtdm.power_domain" +#define DBG_LVL DBG_INFO +#include + +#include + +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; +} diff --git a/components/drivers/include/drivers/core/power_domain.h b/components/drivers/include/drivers/core/power_domain.h new file mode 100644 index 00000000000..6b1f7cde654 --- /dev/null +++ b/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 +#include + +#include + +#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__ */ diff --git a/components/drivers/include/drivers/ofw.h b/components/drivers/include/drivers/ofw.h index 809a84d4a4a..7531dfe3c52 100644 --- a/components/drivers/include/drivers/ofw.h +++ b/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, 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); const char *rt_ofw_bootargs_select(const char *key, int index); diff --git a/components/drivers/include/drivers/platform.h b/components/drivers/include/drivers/platform.h index d63980fc657..5dac6762b5b 100644 --- a/components/drivers/include/drivers/platform.h +++ b/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_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) diff --git a/components/drivers/include/rtdevice.h b/components/drivers/include/rtdevice.h index 863e9d8503b..5b080fced70 100644 --- a/components/drivers/include/rtdevice.h +++ b/components/drivers/include/rtdevice.h @@ -42,6 +42,7 @@ extern "C" { #ifdef RT_USING_DM #include "drivers/core/dm.h" #include "drivers/core/numa.h" +#include "drivers/core/power_domain.h" #include "drivers/platform.h" #ifdef RT_USING_OFW diff --git a/components/drivers/ofw/ofw.c b/components/drivers/ofw/ofw.c index 1f30bd98f9d..d09fb4e7caa 100644 --- a/components/drivers/ofw/ofw.c +++ b/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; } +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) { rt_object_t rt_obj = RT_NULL; diff --git a/include/rtdef.h b/include/rtdef.h index 72b61de7aae..3e48a137f65 100644 --- a/include/rtdef.h +++ b/include/rtdef.h @@ -1363,6 +1363,7 @@ struct rt_device #ifdef RT_USING_OFW void *ofw_node; /**< ofw node get from device tree */ #endif /* RT_USING_OFW */ + void *power_domain_unit; #endif /* RT_USING_DM */ enum rt_device_class_type type; /**< device type */