diff --git a/components/drivers/core/SConscript b/components/drivers/core/SConscript index 216f9a03483..392487c6aa3 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', 'power_domain.c'] + src = src + ['dm.c', 'driver.c', 'numa.c', 'platform.c', 'power.c', 'power_domain.c'] if GetDepend(['RT_USING_DFS']): src += ['mnt.c']; diff --git a/components/drivers/core/power.c b/components/drivers/core/power.c new file mode 100644 index 00000000000..038e37c5072 --- /dev/null +++ b/components/drivers/core/power.c @@ -0,0 +1,311 @@ +/* + * 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" +#define DBG_LVL DBG_INFO +#include + +struct power_off_track +{ + rt_slist_t list; + + struct rt_device *dev; + rt_err_t (*callback)(struct rt_device *); +}; + +void (*rt_dm_machine_shutdown)(void) = RT_NULL; +void (*rt_dm_machine_reset)(void) = RT_NULL; + +static RT_DEFINE_SPINLOCK(_power_off_lock); +static rt_slist_t _power_off_handler_nodes[RT_DM_POWER_OFF_MODE_NR][RT_DM_POWER_OFF_PRIO_NR] = +{ + [0 ... RT_DM_POWER_OFF_MODE_NR - 1] = + { + [0 ... RT_DM_POWER_OFF_PRIO_NR - 1] = + { + RT_NULL, + } + } +}; + +static rt_used char * const _mode_name[] = +{ + [RT_DM_POWER_OFF_MODE_SHUTDOWN] = "SHUTDOWN", + [RT_DM_POWER_OFF_MODE_RESET] = "RESET", +}; + +rt_err_t rt_dm_power_off_handler(struct rt_device *dev, int mode, int priority, + rt_err_t (*callback)(struct rt_device *dev)) +{ + struct power_off_track *track; + + RT_ASSERT(mode < RT_DM_POWER_OFF_MODE_NR); + RT_ASSERT(priority < RT_DM_POWER_OFF_PRIO_NR); + + track = rt_malloc(sizeof(*track)); + + if (!track) + { + return -RT_ENOMEM; + } + + rt_slist_init(&track->list); + track->dev = dev; + track->callback = callback; + + rt_hw_spin_lock(&_power_off_lock.lock); + + rt_slist_insert(&_power_off_handler_nodes[mode][priority], &track->list); + + rt_hw_spin_unlock(&_power_off_lock.lock); + + return RT_EOK; +} + +static void dm_power_off_handler(int mode) +{ + struct power_off_track *track; + + rt_hw_spin_lock(&_power_off_lock.lock); + + for (int i = 0; i < RT_DM_POWER_OFF_PRIO_NR; ++i) + { + rt_slist_t *nodes = &_power_off_handler_nodes[mode][i]; + + rt_slist_for_each_entry(track, nodes, list) + { + rt_err_t err; + struct rt_device *dev = track->dev; + + if ((err = track->callback(dev))) + { + LOG_E("%s: %s fail error = %s", dev ? rt_dm_dev_get_name(dev) : RT_NULL, + _mode_name[mode], rt_strerror(err)); + } + } + } + + rt_hw_spin_unlock(&_power_off_lock.lock); +} + +struct reboot_mode_track +{ + rt_slist_t list; + + struct rt_device *dev; + rt_err_t (*callback)(struct rt_device *, char *cmd); +}; + +static char *_reboot_mode_cmd = "normal"; +static RT_DEFINE_SPINLOCK(_reboot_mode_lock); +static rt_slist_t _reboot_mode_handler_nodes = { RT_NULL }; + +rt_err_t rt_dm_reboot_mode_register(struct rt_device *dev, + rt_err_t (*callback)(struct rt_device *, char *cmd)) +{ + struct reboot_mode_track *track; + + track = rt_malloc(sizeof(*track)); + + if (!track) + { + return -RT_ENOMEM; + } + + rt_slist_init(&track->list); + track->dev = dev; + track->callback = callback; + + rt_hw_spin_lock(&_reboot_mode_lock.lock); + + rt_slist_insert(&_reboot_mode_handler_nodes, &track->list); + + rt_hw_spin_unlock(&_reboot_mode_lock.lock); + + return RT_EOK; +} + +rt_err_t rt_dm_reboot_mode_unregister(struct rt_device *dev) +{ + struct reboot_mode_track *track, *target_track = RT_NULL; + + rt_hw_spin_lock(&_reboot_mode_lock.lock); + + rt_slist_for_each_entry(track, &_reboot_mode_handler_nodes, list) + { + if (track->dev == dev) + { + target_track = track; + rt_slist_remove(&_reboot_mode_handler_nodes, &track->list); + break; + } + } + + rt_hw_spin_unlock(&_reboot_mode_lock.lock); + + if (target_track) + { + rt_free(target_track); + } + + return target_track ? RT_EOK : -RT_EEMPTY; +} + +static rt_err_t dm_reboot_notifiy(struct rt_device *request_dev) +{ + struct reboot_mode_track *track; + + rt_hw_spin_lock(&_reboot_mode_lock.lock); + + rt_slist_for_each_entry(track, &_reboot_mode_handler_nodes, list) + { + rt_err_t err; + struct rt_device *dev = track->dev; + + if ((err = track->callback(dev, _reboot_mode_cmd))) + { + LOG_E("%s: %s fail error = %s", dev ? rt_dm_dev_get_name(dev) : RT_NULL, + "reboot mode apply", rt_strerror(err)); + } + } + + rt_hw_spin_unlock(&_reboot_mode_lock.lock); + + return RT_EOK; +} + +static int reboot_mode_init(void) +{ + return rt_dm_power_off_handler(RT_NULL, RT_DM_POWER_OFF_MODE_RESET, + RT_DM_POWER_OFF_PRIO_HIGH, &dm_reboot_notifiy); +} +INIT_CORE_EXPORT(reboot_mode_init); + +void rt_hw_cpu_reset_mode(char *cmd) +{ + static RT_DEFINE_SPINLOCK(pe_lock); + + rt_hw_spin_lock(&pe_lock.lock); + + _reboot_mode_cmd = cmd ? : _reboot_mode_cmd; + + rt_hw_cpu_reset(); + + /* Unreachable */ + rt_hw_spin_unlock(&pe_lock.lock); +} + +static struct rt_thread power_task; +static void power_task_async(void (*fn)(void)); + +rt_inline rt_bool_t power_need_async(void) +{ + struct rt_thread *tid = rt_thread_self(); + + return tid && tid != &power_task && rt_interrupt_get_nest(); +} + +void rt_hw_cpu_shutdown(void) +{ + register rt_ubase_t level; + + if (power_need_async()) + { + power_task_async(&rt_hw_cpu_shutdown); + + return; + } + + dm_power_off_handler(RT_DM_POWER_OFF_MODE_SHUTDOWN); + + LOG_I("Shutdown"); + + /* Machine shutdown */ + if (rt_dm_machine_shutdown) + { + rt_dm_machine_shutdown(); + } + + level = rt_hw_interrupt_disable(); + while (level) + { + RT_ASSERT(0); + } +} +MSH_CMD_EXPORT_ALIAS(rt_hw_cpu_shutdown, shutdown, shutdown machine); + +void rt_hw_cpu_reset(void) +{ + register rt_ubase_t level; + + if (power_need_async()) + { + power_task_async(&rt_hw_cpu_reset); + + return; + } + + dm_power_off_handler(RT_DM_POWER_OFF_MODE_RESET); + + LOG_I("Reset"); + + /* Machine reset */ + if (rt_dm_machine_reset) + { + rt_dm_machine_reset(); + } + + level = rt_hw_interrupt_disable(); + while (level) + { + RT_ASSERT(0); + } +} + +static int reset(int args, char**argv) +{ + if (args > 1) + { + rt_hw_cpu_reset_mode(argv[1]); + } + else + { + rt_hw_cpu_reset(); + } + + return 0; +} +MSH_CMD_EXPORT(reset, reset machine); + +static void power_task_entry(void *param) +{ + void (*fn)(void) = rt_thread_self()->parameter; + + fn(); +} + +static void power_task_async(void (*fn)(void)) +{ + power_task.parameter = fn; + + rt_thread_startup(&power_task); +} + +static int power_init(void) +{ + static rt_uint8_t power_task_stack[DM_THREAD_STACK_SIZE]; + + return rt_thread_init(&power_task, "pwr", power_task_entry, RT_NULL, + &power_task_stack, sizeof(power_task_stack), + RT_THREAD_PRIORITY_MAX / 2, 32); +} +INIT_CORE_EXPORT(power_init); diff --git a/components/drivers/include/drivers/core/power.h b/components/drivers/include/drivers/core/power.h new file mode 100644 index 00000000000..2d4f3c3fb82 --- /dev/null +++ b/components/drivers/include/drivers/core/power.h @@ -0,0 +1,47 @@ +/* + * 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 + */ + +#ifndef __RT_DM_POWER_H__ +#define __RT_DM_POWER_H__ + +#include + +enum rt_dm_power_off_mode +{ + RT_DM_POWER_OFF_MODE_SHUTDOWN, + RT_DM_POWER_OFF_MODE_RESET, + + RT_DM_POWER_OFF_MODE_NR, +}; + +enum rt_dm_power_off_priority +{ + RT_DM_POWER_OFF_PRIO_PLATFORM, + RT_DM_POWER_OFF_PRIO_LOW, + RT_DM_POWER_OFF_PRIO_DEFAULT, + RT_DM_POWER_OFF_PRIO_HIGH, + RT_DM_POWER_OFF_PRIO_FIRMWARE, + + RT_DM_POWER_OFF_PRIO_NR, +}; + +rt_err_t rt_dm_power_off_handler(struct rt_device *dev, int mode, int priority, + rt_err_t (*callback)(struct rt_device *)); + +rt_err_t rt_dm_reboot_mode_register(struct rt_device *dev, + rt_err_t (*callback)(struct rt_device *, char *cmd)); +rt_err_t rt_dm_reboot_mode_unregister(struct rt_device *dev); + +void rt_hw_cpu_reset_mode(char *cmd); + +extern void (*rt_dm_machine_shutdown)(void); +extern void (*rt_dm_machine_reset)(void); + +#endif /* __RT_DM_POWER_H__ */ diff --git a/components/drivers/include/rtdevice.h b/components/drivers/include/rtdevice.h index 7007441c6bc..4a50bb7d31e 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.h" #include "drivers/core/power_domain.h" #include "drivers/platform.h"