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 <arvinf@ip-logix.com>
This commit is contained in:
Arvin Farahmand 2020-04-30 22:32:52 -04:00 committed by Carles Cufí
commit 98a0ccd5c5
9 changed files with 409 additions and 35 deletions

View file

@ -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
*********************

View file

@ -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.

View file

@ -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 <syscalls/hwinfo_get_device_id_mrsh.c>
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 <syscalls/hwinfo_get_reset_cause_mrsh.c>
int z_vrfy_hwinfo_clear_reset_cause(void)
{
return z_impl_hwinfo_clear_reset_cause();
}
#include <syscalls/hwinfo_clear_reset_cause_mrsh.c>
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 <syscalls/hwinfo_get_supported_reset_cause_mrsh.c>

View file

@ -9,6 +9,9 @@
#include <string.h>
#include <hal/nrf_ficr.h>
#include <sys/byteorder.h>
#ifndef CONFIG_BOARD_QEMU_CORTEX_M0
#include <helpers/nrfx_reset_reason.h>
#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

View file

@ -6,6 +6,7 @@
#include <soc.h>
#include <stm32_ll_utils.h>
#include <stm32_ll_rcc.h>
#include <drivers/hwinfo.h>
#include <string.h>
#include <sys/byteorder.h>
@ -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;
}

View file

@ -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;
}

View file

@ -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);
/**
* @}
*/

View file

@ -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);

View file

@ -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