From 98a0ccd5c5275e2e2e2d09265f2d4be4d9f6f4bb Mon Sep 17 00:00:00 2001 From: Arvin Farahmand Date: Thu, 30 Apr 2020 22:32:52 -0400 Subject: [PATCH] drivers: hwinfo: Add reset cause support Add `hwinfo_get_reset_cause` and `hwinfo_clear_reset_cause` to retrieve and to clear cause of system reset on supported platforms. Different platforms can provide different causes of reset, however there is a great deal of overlap. `enum reset_cause` can be expanded in the future to support additional reasons, as additional platforms are supported. Signed-off-by: Arvin Farahmand --- doc/reference/peripherals/hwinfo.rst | 7 +- drivers/hwinfo/Kconfig | 14 --- drivers/hwinfo/hwinfo_handlers.c | 31 ++++++ drivers/hwinfo/hwinfo_nrf.c | 88 +++++++++++++++ drivers/hwinfo/hwinfo_stm32.c | 76 +++++++++++++ drivers/hwinfo/hwinfo_weak_impl.c | 17 ++- include/drivers/hwinfo.h | 57 ++++++++++ tests/drivers/hwinfo/api/src/main.c | 148 +++++++++++++++++++++---- tests/drivers/hwinfo/api/testcase.yaml | 6 + 9 files changed, 409 insertions(+), 35 deletions(-) diff --git a/doc/reference/peripherals/hwinfo.rst b/doc/reference/peripherals/hwinfo.rst index dac0f4b0849..9fb2ab7ed57 100644 --- a/doc/reference/peripherals/hwinfo.rst +++ b/doc/reference/peripherals/hwinfo.rst @@ -7,7 +7,12 @@ Overview ******** The HW Info API provides access to hardware information such as device -identifiers. +identifiers and reset cause flags. + +Reset cause flags can be used to determine why the device was reset; for example +due to a watchdog timeout or due to power cycling. Different devices support different +subset of flags. Use `hwinfo_get_supported_reset_cause` to retrieve the flags +that are supported by that device. Configuration Options ********************* diff --git a/drivers/hwinfo/Kconfig b/drivers/hwinfo/Kconfig index bbdc04e8cd7..26709574254 100644 --- a/drivers/hwinfo/Kconfig +++ b/drivers/hwinfo/Kconfig @@ -10,9 +10,6 @@ menuconfig HWINFO if HWINFO -config HWINFO_HAS_DRIVER - bool - config HWINFO_SHELL bool "Enable HWINFO Shell" default y @@ -24,7 +21,6 @@ config HWINFO_STM32 bool "STM32 hwinfo" default y depends on SOC_FAMILY_STM32 - select HWINFO_HAS_DRIVER help Enable STM32 hwinfo driver. @@ -32,7 +28,6 @@ config HWINFO_NRF bool "NRF device ID" default y depends on SOC_FAMILY_NRF && !TRUSTED_EXECUTION_NONSECURE - select HWINFO_HAS_DRIVER help Enable Nordic NRF hwinfo driver. @@ -40,7 +35,6 @@ config HWINFO_MCUX_SIM bool "NXP kinetis device ID" default y depends on HAS_MCUX_SIM - select HWINFO_HAS_DRIVER help Enable NXP kinetis mcux hwinfo driver. @@ -48,7 +42,6 @@ config HWINFO_IMXRT bool "NXP i.mx RT device ID" default y depends on SOC_SERIES_IMX_RT - select HWINFO_HAS_DRIVER help Enable NXP i.mx RT hwinfo driver. @@ -56,7 +49,6 @@ config HWINFO_SAM bool "Atmel SAM device ID" default y depends on SOC_FAMILY_SAM && !SOC_SERIES_SAM4L - select HWINFO_HAS_DRIVER help Enable Atmel SAM hwinfo driver. @@ -64,7 +56,6 @@ config HWINFO_SAM4L bool "Atmel SAM4L device ID" default y depends on SOC_SERIES_SAM4L - select HWINFO_HAS_DRIVER help Enable Atmel SAM4L hwinfo driver. @@ -72,7 +63,6 @@ config HWINFO_SAM0 bool "Atmel SAM0 device ID" default y depends on SOC_FAMILY_SAM0 - select HWINFO_HAS_DRIVER help Enable Atmel SAM0 hwinfo driver. @@ -80,7 +70,6 @@ config HWINFO_ESP32 bool "ESP32 device ID" default y depends on SOC_ESP32 - select HWINFO_HAS_DRIVER help Enable ESP32 hwinfo driver. @@ -88,7 +77,6 @@ config HWINFO_LITEX bool "LiteX device ID" default y depends on SOC_RISCV32_LITEX_VEXRISCV - select HWINFO_HAS_DRIVER help Enable LiteX hwinfo driver @@ -96,7 +84,6 @@ config HWINFO_PSOC6 bool "Cypress PSoC-6 unique device ID" default y depends on SOC_FAMILY_PSOC6 - select HWINFO_HAS_DRIVER help Enable Cypress PSoC-6 hwinfo driver. @@ -104,7 +91,6 @@ config HWINFO_GECKO bool "GECKO hwinfo" default y depends on SOC_FAMILY_EXX32 - select HWINFO_HAS_DRIVER help Enable Silabs GECKO hwinfo driver. diff --git a/drivers/hwinfo/hwinfo_handlers.c b/drivers/hwinfo/hwinfo_handlers.c index 1a34f962d59..845782f9334 100644 --- a/drivers/hwinfo/hwinfo_handlers.c +++ b/drivers/hwinfo/hwinfo_handlers.c @@ -14,3 +14,34 @@ ssize_t z_vrfy_hwinfo_get_device_id(uint8_t *buffer, size_t length) return z_impl_hwinfo_get_device_id((uint8_t *)buffer, (size_t)length); } #include + +int z_vrfy_hwinfo_get_reset_cause(uint32_t *cause) +{ + int ret; + uint32_t cause_copy; + + ret = z_impl_hwinfo_get_reset_cause(&cause_copy); + Z_OOPS(z_user_to_copy(cause, &cause_copy, sizeof(uint32_t))); + + return ret; +} +#include + + +int z_vrfy_hwinfo_clear_reset_cause(void) +{ + return z_impl_hwinfo_clear_reset_cause(); +} +#include + +int z_vrfy_hwinfo_get_supported_reset_cause(uint32_t *supported) +{ + int ret; + uint32_t supported_copy; + + ret = z_impl_hwinfo_get_supported_reset_cause(&supported_copy); + Z_OOPS(z_user_to_copy(supported, &supported_copy, sizeof(uint32_t))); + + return ret; +} +#include diff --git a/drivers/hwinfo/hwinfo_nrf.c b/drivers/hwinfo/hwinfo_nrf.c index a9a6a04fd1f..5be49bd88a6 100644 --- a/drivers/hwinfo/hwinfo_nrf.c +++ b/drivers/hwinfo/hwinfo_nrf.c @@ -9,6 +9,9 @@ #include #include #include +#ifndef CONFIG_BOARD_QEMU_CORTEX_M0 +#include +#endif struct nrf_uid { uint32_t id[2]; @@ -29,3 +32,88 @@ ssize_t z_impl_hwinfo_get_device_id(uint8_t *buffer, size_t length) return length; } + +#ifndef CONFIG_BOARD_QEMU_CORTEX_M0 +int z_impl_hwinfo_get_reset_cause(uint32_t *cause) +{ + uint32_t flags = 0; + + uint32_t reason = nrfx_reset_reason_get(); + + if (reason & NRFX_RESET_REASON_RESETPIN_MASK) { + flags |= RESET_PIN; + } + if (reason & NRFX_RESET_REASON_DOG_MASK) { + flags |= RESET_WATCHDOG; + } + if (reason & NRFX_RESET_REASON_LOCKUP_MASK) { + flags |= RESET_CPU_LOCKUP; + } + if (reason & NRFX_RESET_REASON_OFF_MASK) { + flags |= RESET_LOW_POWER_WAKE; + } + if (reason & NRFX_RESET_REASON_DIF_MASK) { + flags |= RESET_DEBUG; + } + +#if !NRF_POWER_HAS_RESETREAS + if (reason & NRFX_RESET_REASON_CTRLAP_MASK) { + flags |= RESET_DEBUG; + } + if (reason & NRFX_RESET_REASON_DOG0_MASK) { + flags |= RESET_WATCHDOG; + } + if (reason & NRFX_RESET_REASON_DOG1_MASK) { + flags |= RESET_WATCHDOG; + } + if (reason & NRFX_RESETREAS_SREQ_MASK) { + flags |= RESET_SOFTWARE; + } + +#if NRF_RESET_HAS_NETWORK + if (reason & NRFX_RESET_REASON_LSREQ_MASK) { + flags |= RESET_SOFTWARE; + } + if (reason & NRFX_RESET_REASON_LLOCKUP_MASK) { + flags |= RESET_CPU_LOCKUP; + } + if (reason & NRFX_RESET_REASON_LDOG_MASK) { + flags |= RESET_WATCHDOG; + } + if (reason & NRFX_RESET_REASON_LCTRLAP_MASK) { + flags |= RESET_DEBUG; + } +#endif + +#else + if (reason & NRFX_RESET_REASON_SREQ_MASK) { + flags |= RESET_SOFTWARE; + } +#endif + + *cause = flags; + + return 0; +} + +int z_impl_hwinfo_clear_reset_cause(void) +{ + uint32_t reason = -1; + + nrfx_reset_reason_clear(reason); + + return 0; +} + +int z_impl_hwinfo_get_supported_reset_cause(uint32_t *supported) +{ + *supported = (RESET_PIN + | RESET_WATCHDOG + | RESET_SOFTWARE + | RESET_CPU_LOCKUP + | RESET_LOW_POWER_WAKE + | RESET_DEBUG); + + return 0; +} +#endif diff --git a/drivers/hwinfo/hwinfo_stm32.c b/drivers/hwinfo/hwinfo_stm32.c index 70faeda7957..9f672592219 100644 --- a/drivers/hwinfo/hwinfo_stm32.c +++ b/drivers/hwinfo/hwinfo_stm32.c @@ -6,6 +6,7 @@ #include #include +#include #include #include #include @@ -30,3 +31,78 @@ ssize_t z_impl_hwinfo_get_device_id(uint8_t *buffer, size_t length) return length; } + +int z_impl_hwinfo_get_reset_cause(uint32_t *cause) +{ + uint32_t flags = 0; + +#if defined(RCC_FLAG_SFTRST) + if (LL_RCC_IsActiveFlag_SFTRST()) { + flags |= RESET_SOFTWARE; + } +#endif +#if defined(RCC_FLAG_PINRST) + if (LL_RCC_IsActiveFlag_PINRST()) { + flags |= RESET_PIN; + } +#endif +#if defined(RCC_FLAG_IWDGRST) + if (LL_RCC_IsActiveFlag_IWDGRST()) { + flags |= RESET_WATCHDOG; + } +#endif +#if defined(RCC_FLAG_WWDGRST) + if (LL_RCC_IsActiveFlag_WWDGRST()) { + flags |= RESET_WATCHDOG; + } +#endif +#if defined(RCC_FLAG_FWRST) + if (LL_RCC_IsActiveFlag_FWRST()) { + flags |= RESET_SECURITY; + } +#endif +#if defined(RCC_FLAG_BORRST) + if (LL_RCC_IsActiveFlag_BORRST()) { + flags |= RESET_BROWNOUT; + } +#endif +#if defined(RCC_FLAG_PWRRST) + if (LL_RCC_IsActiveFlag_PWRRST()) { + flags |= RESET_POR; + } +#endif +#if defined(RCC_FLAG_PORRST) + if (LL_RCC_IsActiveFlag_PORRST()) { + flags |= RESET_POR; + } +#endif +#if defined(RCC_FLAG_LPWRRST) + if (LL_RCC_IsActiveFlag_LPWRRST()) { + flags |= RESET_LOW_POWER_WAKE; + } +#endif + + *cause = flags; + + return 0; +} + +int z_impl_hwinfo_clear_reset_cause(void) +{ + LL_RCC_ClearResetFlags(); + + return 0; +} + +int z_impl_hwinfo_get_supported_reset_cause(uint32_t *supported) +{ + *supported = (RESET_PIN + | RESET_WATCHDOG + | RESET_SOFTWARE + | RESET_SECURITY + | RESET_LOW_POWER_WAKE + | RESET_POR + | RESET_BROWNOUT); + + return 0; +} diff --git a/drivers/hwinfo/hwinfo_weak_impl.c b/drivers/hwinfo/hwinfo_weak_impl.c index a0f8bae3cf0..97f6a0af619 100644 --- a/drivers/hwinfo/hwinfo_weak_impl.c +++ b/drivers/hwinfo/hwinfo_weak_impl.c @@ -8,5 +8,20 @@ ssize_t __weak z_impl_hwinfo_get_device_id(uint8_t *buffer, size_t length) { - return -ENOTSUP; + return -ENOSYS; +} + +int __weak z_impl_hwinfo_get_reset_cause(uint32_t *cause) +{ + return -ENOSYS; +} + +int __weak z_impl_hwinfo_clear_reset_cause(void) +{ + return -ENOSYS; +} + +int __weak z_impl_hwinfo_get_supported_reset_cause(uint32_t *supported) +{ + return -ENOSYS; } diff --git a/include/drivers/hwinfo.h b/include/drivers/hwinfo.h index 0c659d13807..86e76d67cf1 100644 --- a/include/drivers/hwinfo.h +++ b/include/drivers/hwinfo.h @@ -30,6 +30,17 @@ extern "C" { #endif +#define RESET_PIN BIT(0) +#define RESET_SOFTWARE BIT(1) +#define RESET_BROWNOUT BIT(2) +#define RESET_POR BIT(3) +#define RESET_WATCHDOG BIT(4) +#define RESET_DEBUG BIT(5) +#define RESET_SECURITY BIT(6) +#define RESET_LOW_POWER_WAKE BIT(7) +#define RESET_CPU_LOCKUP BIT(8) +#define RESET_PARITY BIT(9) + /** * @brief Copy the device id to a buffer * @@ -54,6 +65,52 @@ __syscall ssize_t hwinfo_get_device_id(uint8_t *buffer, size_t length); ssize_t z_impl_hwinfo_get_device_id(uint8_t *buffer, size_t length); +/** + * @brief Retrieve cause of device reset. + * + * @param cause OR'd `reset_cause` flags + * + * This routine retrieves the flags that indicate why the device was reset. + * + * Multiple calls to this routine will return the same value, unless + * `hwinfo_clear_reset_cause` has been called. + * + * @retval zero if successful. + * @retval -ENOTSUP if there is no implementation for the particular device. + * @retval any negative value on driver specific errors. + */ +__syscall int hwinfo_get_reset_cause(uint32_t *cause); + +int z_impl_hwinfo_get_reset_cause(uint32_t *cause); + +/** + * @brief Clear cause of device reset. + * + * Clears reset cause flags. + * + * @retval zero if successful. + * @retval -ENOTSUP if there is no implementation for the particular device. + * @retval any negative value on driver specific errors. + */ +__syscall int hwinfo_clear_reset_cause(void); + +int z_impl_hwinfo_clear_reset_cause(void); + +/** + * @brief Get supported reset cause flags + * + * @param supported OR'd `reset_cause` flags that are supported + * + * Retrieves all `reset_cause` flags that are supported by this device. + * + * @retval zero if successful. + * @retval -ENOTSUP if there is no implementation for the particular device. + * @retval any negative value on driver specific errors. + */ +__syscall int hwinfo_get_supported_reset_cause(uint32_t *supported); + +int z_impl_hwinfo_get_supported_reset_cause(uint32_t *supported); + /** * @} */ diff --git a/tests/drivers/hwinfo/api/src/main.c b/tests/drivers/hwinfo/api/src/main.c index b9476a8b679..8b31745d040 100644 --- a/tests/drivers/hwinfo/api/src/main.c +++ b/tests/drivers/hwinfo/api/src/main.c @@ -26,7 +26,6 @@ #define BUFFER_LENGTH 17 #define BUFFER_CANARY 0xFF -#ifdef CONFIG_HWINFO_HAS_DRIVER static void test_device_id_get(void) { uint8_t buffer_1[BUFFER_LENGTH]; @@ -35,7 +34,11 @@ static void test_device_id_get(void) int i; length_read_1 = hwinfo_get_device_id(buffer_1, 1); - zassert_not_equal(length_read_1, -ENOTSUP, "Not supported by hardware"); + if (length_read_1 == -ENOSYS) { + ztest_test_skip(); + return; + } + zassert_false((length_read_1 < 0), "Unexpected negative return value: %d", length_read_1); zassert_not_equal(length_read_1, 0, "Zero bytes read"); @@ -64,33 +67,140 @@ static void test_device_id_get(void) "Two consecutively readings don't match"); } } -#else -static void test_device_id_get(void) {} -#endif /* CONFIG_HWINFO_HAS_DRIVER */ -#ifndef CONFIG_HWINFO_HAS_DRIVER -static void test_device_id_enotsup(void) +/* + * @addtogroup t_hwinfo_get_reset_cause_api + * @{ + * @defgroup t_hwinfo_get_reset_cause test_hwinfo_get_reset_cause + * @brief TestPurpose: verify get reset cause works. + * @details + * - Test Steps + * -# Set target buffer to a known value + * -# Read the reset cause + * -# Check if target buffer has been altered + * - Expected Results + * -# Target buffer contents should be changed after the call. + * @} + */ + +static void test_get_reset_cause(void) { + uint32_t cause; ssize_t ret; - uint8_t buffer[1]; - ret = hwinfo_get_device_id(buffer, 1); - /* There is no hwinfo driver for this platform, hence the return value - * should be -ENOTSUP - */ - zassert_equal(ret, -ENOTSUP, - "hwinfo_get_device_id returned % instead of %d", - ret, -ENOTSUP); + /* Set `cause` to a known value prior to call. */ + cause = 0xDEADBEEF; + + ret = hwinfo_get_reset_cause(&cause); + if (ret == -ENOSYS) { + ztest_test_skip(); + return; + } + + zassert_false((ret < 0), + "Unexpected negative return value: %d", ret); + + /* Verify that `cause` has been changed. */ + zassert_not_equal(cause, 0xDEADBEEF, "Reset cause not written."); +} + +/* + * @addtogroup t_hwinfo_clear_reset_cause_api + * @{ + * @defgroup t_hwinfo_clear_reset_cause test_hwinfo_clear_reset_cause + * @brief TestPurpose: verify clear reset cause works. This may + * not work on some platforms, depending on how reset cause register + * works on that SoC. + * @details + * - Test Steps + * -# Read the reset cause and store the result + * -# Call clear reset cause + * -# Read the reset cause again + * -# Check if the two readings match + * - Expected Results + * -# Reset cause value should change after calling clear reset cause. + * @} + */ +static void test_clear_reset_cause(void) +{ + uint32_t cause_1, cause_2; + ssize_t ret; + + ret = hwinfo_get_reset_cause(&cause_1); + if (ret == -ENOSYS) { + ztest_test_skip(); + return; + } + + zassert_false((ret < 0), + "Unexpected negative return value: %d", ret); + + ret = hwinfo_clear_reset_cause(); + if (ret == -ENOSYS) { + ztest_test_skip(); + return; + } + + zassert_false((ret < 0), + "Unexpected negative return value: %d", ret); + + ret = hwinfo_get_reset_cause(&cause_2); + if (ret == -ENOSYS) { + ztest_test_skip(); + return; + } + + zassert_false((ret < 0), + "Unexpected negative return value: %d", ret); + + /* Verify that `cause` has been changed. */ + zassert_not_equal(cause_1, cause_2, + "Reset cause did not change after clearing"); +} + +/* + * @addtogroup t_hwinfo_get_reset_cause_api + * @{ + * @defgroup t_hwinfo_get_reset_cause test_hwinfo_get_supported_reset_cause + * @brief TestPurpose: verify get supported reset cause works. + * @details + * - Test Steps + * -# Set target buffer to a known value + * -# Read the reset cause + * -# Check if target buffer has been altered + * - Expected Results + * -# Target buffer contents should be changed after the call. + * @} + */ +static void test_get_supported_reset_cause(void) +{ + uint32_t supported; + ssize_t ret; + + /* Set `supported` to a known value prior to call. */ + supported = 0xDEADBEEF; + + ret = hwinfo_get_supported_reset_cause(&supported); + if (ret == -ENOSYS) { + ztest_test_skip(); + return; + } + + zassert_false((ret < 0), + "Unexpected negative return value: %d", ret); + + /* Verify that `supported` has been changed. */ + zassert_not_equal(supported, 0xDEADBEEF, + "Supported reset cause not written."); } -#else -static void test_device_id_enotsup(void) {} -#endif /* CONFIG_HWINFO_HAS_DRIVER not defined*/ void test_main(void) { ztest_test_suite(hwinfo_device_id_api, ztest_unit_test(test_device_id_get), - ztest_unit_test(test_device_id_enotsup) + ztest_unit_test(test_get_reset_cause), + ztest_unit_test(test_clear_reset_cause), + ztest_unit_test(test_get_supported_reset_cause) ); ztest_run_test_suite(hwinfo_device_id_api); diff --git a/tests/drivers/hwinfo/api/testcase.yaml b/tests/drivers/hwinfo/api/testcase.yaml index 5eb62207e3e..10ea84f0774 100644 --- a/tests/drivers/hwinfo/api/testcase.yaml +++ b/tests/drivers/hwinfo/api/testcase.yaml @@ -1,3 +1,9 @@ tests: drivers.device_id: tags: driver + drivers.get_reset_cause: + tags: driver + drivers.clear_reset_cause: + tags: driver + drivers.get_supported_reset_cause: + tags: driver