retention: Add retention system
Adds a retention system which builds on top of retained_mem drivers to allow partitioning of areas and data integrity with magic header prefixes and checksum of stored data. Signed-off-by: Jamie McCrae <jamie.mccrae@nordicsemi.no>
This commit is contained in:
parent
7ad855c378
commit
64f4404481
66
dts/bindings/retention/zephyr,retention.yaml
Normal file
66
dts/bindings/retention/zephyr,retention.yaml
Normal file
|
@ -0,0 +1,66 @@
|
|||
# Copyright (c) 2023 Nordic Semiconductor ASA
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
description: |
|
||||
Retention subsystem area, which has a retained memory parent. Example
|
||||
64-byte area with 2-byte prefix and 1-byte checksum with 61 usable bytes
|
||||
for user storage:
|
||||
|
||||
sram@2003FFC0 {
|
||||
compatible = "zephyr,memory-region", "mmio-sram";
|
||||
reg = <0x2003FFC0 64>;
|
||||
zephyr,memory-region = "RetainedMem";
|
||||
status = "okay";
|
||||
|
||||
retainedmem {
|
||||
compatible = "zephyr,retained-ram";
|
||||
status = "okay";
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
|
||||
retention0: retention@0 {
|
||||
compatible = "zephyr,retention";
|
||||
status = "okay";
|
||||
reg = <0x0 0x40>;
|
||||
prefix = [04 fa];
|
||||
checksum = <1>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
compatible: "zephyr,retention"
|
||||
|
||||
include: base.yaml
|
||||
|
||||
properties:
|
||||
"#address-cells":
|
||||
const: 1
|
||||
description: |
|
||||
Address reg cell is for the offset of the area in parent node, can be
|
||||
increased if multiple retention partitions are used or parts are
|
||||
reserved.
|
||||
|
||||
"#size-cells":
|
||||
const: 1
|
||||
description: |
|
||||
Size reg cell is for the size of the area, which includes sizes of
|
||||
prefix and checksum (if enabled).
|
||||
|
||||
reg:
|
||||
required: true
|
||||
|
||||
prefix:
|
||||
description: |
|
||||
An optional magic prefix, which indicates that the data has been set
|
||||
(applies to the header of the data, reduces the available user data
|
||||
size).
|
||||
type: uint8-array
|
||||
|
||||
checksum:
|
||||
description: |
|
||||
An optional data verification checksum, which indicates that the data is
|
||||
valid (appended to the footer of the data, reduces the available user
|
||||
data size). Value is size in bytes (0 for none, 1 for 8-bit CRC, 2 for
|
||||
16-bit CRC, 4 for 32-bit CRC). Default is to not use a checksum.
|
||||
type: int
|
||||
default: 0
|
115
include/zephyr/retention/retention.h
Normal file
115
include/zephyr/retention/retention.h
Normal file
|
@ -0,0 +1,115 @@
|
|||
/*
|
||||
* Copyright (c) 2023 Nordic Semiconductor ASA
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file
|
||||
* @brief Public API for retention API
|
||||
*/
|
||||
|
||||
#ifndef ZEPHYR_INCLUDE_RETENTION_
|
||||
#define ZEPHYR_INCLUDE_RETENTION_
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <sys/types.h>
|
||||
#include <zephyr/kernel.h>
|
||||
#include <zephyr/device.h>
|
||||
#include <zephyr/types.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Retention API
|
||||
* @defgroup retention_api Retention API
|
||||
* @ingroup retention
|
||||
* @{
|
||||
*/
|
||||
|
||||
typedef ssize_t (*retention_size_api)(const struct device *dev);
|
||||
typedef int (*retention_is_valid_api)(const struct device *dev);
|
||||
typedef int (*retention_read_api)(const struct device *dev, off_t offset, uint8_t *buffer,
|
||||
size_t size);
|
||||
typedef int (*retention_write_api)(const struct device *dev, off_t offset,
|
||||
const uint8_t *buffer, size_t size);
|
||||
typedef int (*retention_clear_api)(const struct device *dev);
|
||||
|
||||
struct retention_api {
|
||||
retention_size_api size;
|
||||
retention_is_valid_api is_valid;
|
||||
retention_read_api read;
|
||||
retention_write_api write;
|
||||
retention_clear_api clear;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Returns the size of the retention area.
|
||||
*
|
||||
* @param dev Retention device to use.
|
||||
*
|
||||
* @retval Positive value indicating size in bytes on success, else negative errno
|
||||
* code.
|
||||
*/
|
||||
ssize_t retention_size(const struct device *dev);
|
||||
|
||||
/**
|
||||
* @brief Checks if the underlying data in the retention area is valid or not.
|
||||
*
|
||||
* @param dev Retention device to use.
|
||||
*
|
||||
* @retval 1 If successful and data is valid.
|
||||
* @retval 0 If data is not valid.
|
||||
* @retval -ENOTSUP If there is no header/checksum configured for the retention area.
|
||||
* @retval -errno Error code code.
|
||||
*/
|
||||
int retention_is_valid(const struct device *dev);
|
||||
|
||||
/**
|
||||
* @brief Reads data from the retention area.
|
||||
*
|
||||
* @param dev Retention device to use.
|
||||
* @param offset Offset to read data from.
|
||||
* @param buffer Buffer to store read data in.
|
||||
* @param size Size of data to read.
|
||||
*
|
||||
* @retval 0 If successful.
|
||||
* @retval -errno Error code code.
|
||||
*/
|
||||
int retention_read(const struct device *dev, off_t offset, uint8_t *buffer, size_t size);
|
||||
|
||||
/**
|
||||
* @brief Writes data to the retention area (underlying data does not need to be
|
||||
* cleared prior to writing), once function returns with a success code, the
|
||||
* data will be classed as valid if queried using retention_is_valid().
|
||||
*
|
||||
* @param dev Retention device to use.
|
||||
* @param offset Offset to write data to.
|
||||
* @param buffer Data to write.
|
||||
* @param size Size of data to be written.
|
||||
*
|
||||
* @retval 0 on success else negative errno code.
|
||||
*/
|
||||
int retention_write(const struct device *dev, off_t offset, const uint8_t *buffer, size_t size);
|
||||
|
||||
/**
|
||||
* @brief Clears all data in the retention area (sets it to 0)
|
||||
*
|
||||
* @param dev Retention device to use.
|
||||
*
|
||||
* @retval 0 on success else negative errno code.
|
||||
*/
|
||||
int retention_clear(const struct device *dev);
|
||||
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* ZEPHYR_INCLUDE_RETENTION_ */
|
|
@ -31,6 +31,7 @@ add_subdirectory_ifdef(CONFIG_INPUT input)
|
|||
add_subdirectory_ifdef(CONFIG_JWT jwt)
|
||||
add_subdirectory_ifdef(CONFIG_LORAWAN lorawan)
|
||||
add_subdirectory_ifdef(CONFIG_NET_BUF net)
|
||||
add_subdirectory_ifdef(CONFIG_RETENTION retention)
|
||||
add_subdirectory_ifdef(CONFIG_SETTINGS settings)
|
||||
add_subdirectory_ifdef(CONFIG_SHELL shell)
|
||||
add_subdirectory_ifdef(CONFIG_TIMING_FUNCTIONS timing)
|
||||
|
|
|
@ -28,6 +28,7 @@ source "subsys/net/Kconfig"
|
|||
source "subsys/pm/Kconfig"
|
||||
source "subsys/portability/Kconfig"
|
||||
source "subsys/random/Kconfig"
|
||||
source "subsys/retention/Kconfig"
|
||||
source "subsys/rtio/Kconfig"
|
||||
source "subsys/sd/Kconfig"
|
||||
source "subsys/settings/Kconfig"
|
||||
|
|
4
subsys/retention/CMakeLists.txt
Normal file
4
subsys/retention/CMakeLists.txt
Normal file
|
@ -0,0 +1,4 @@
|
|||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
zephyr_library()
|
||||
zephyr_library_sources(retention.c)
|
34
subsys/retention/Kconfig
Normal file
34
subsys/retention/Kconfig
Normal file
|
@ -0,0 +1,34 @@
|
|||
# Copyright (c) 2023, Nordic Semiconductor ASA
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
menuconfig RETENTION
|
||||
bool "Retention support"
|
||||
depends on CRC
|
||||
depends on RETAINED_MEM
|
||||
depends on DT_HAS_ZEPHYR_DATA_RETENTION_ENABLED
|
||||
help
|
||||
Enables support for the data retention system, which uses retained
|
||||
memory drivers.
|
||||
|
||||
if RETENTION
|
||||
|
||||
config RETENTION_INIT_PRIORITY
|
||||
int "Retention devices init priority"
|
||||
default 86
|
||||
help
|
||||
Data retention devices initialization priority (must be higher than
|
||||
init priorities for retained memory drivers.
|
||||
|
||||
config RETENTION_BUFFER_SIZE
|
||||
int "Retention stack buffer sizes"
|
||||
default 16
|
||||
range 1 4096
|
||||
help
|
||||
Size of buffers (stack based) used when reading and writing data
|
||||
from/to the retention device.
|
||||
|
||||
module = RETENTION
|
||||
module-str = retention
|
||||
source "subsys/logging/Kconfig.template.log_config"
|
||||
|
||||
endif # RETENTION
|
406
subsys/retention/retention.c
Normal file
406
subsys/retention/retention.c
Normal file
|
@ -0,0 +1,406 @@
|
|||
/*
|
||||
* Copyright (c) 2023, Nordic Semiconductor ASA
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#define DT_DRV_COMPAT zephyr_retention
|
||||
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
#include <zephyr/kernel.h>
|
||||
#include <zephyr/sys/util.h>
|
||||
#include <zephyr/sys/crc.h>
|
||||
#include <zephyr/device.h>
|
||||
#include <zephyr/devicetree.h>
|
||||
#include <zephyr/drivers/retained_mem.h>
|
||||
#include <zephyr/retention/retention.h>
|
||||
#include <zephyr/logging/log.h>
|
||||
|
||||
LOG_MODULE_REGISTER(retention, CONFIG_RETENTION_LOG_LEVEL);
|
||||
|
||||
#define DATA_VALID_VALUE 1
|
||||
|
||||
enum {
|
||||
CHECKSUM_NONE = 0,
|
||||
CHECKSUM_CRC8,
|
||||
CHECKSUM_CRC16,
|
||||
CHECKSUM_UNUSED,
|
||||
CHECKSUM_CRC32,
|
||||
};
|
||||
|
||||
struct retention_data {
|
||||
bool header_written;
|
||||
#ifdef CONFIG_MULTITHREADING
|
||||
struct k_mutex lock;
|
||||
#endif
|
||||
};
|
||||
|
||||
struct retention_config {
|
||||
const struct device *parent;
|
||||
size_t offset;
|
||||
size_t size;
|
||||
size_t reserved_size;
|
||||
uint8_t checksum_size;
|
||||
uint8_t prefix_len;
|
||||
uint8_t prefix[];
|
||||
};
|
||||
|
||||
static inline void retention_lock_take(const struct device *dev)
|
||||
{
|
||||
#ifdef CONFIG_MULTITHREADING
|
||||
struct retention_data *data = dev->data;
|
||||
|
||||
k_mutex_lock(&data->lock, K_FOREVER);
|
||||
#else
|
||||
ARG_UNUSED(dev);
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void retention_lock_release(const struct device *dev)
|
||||
{
|
||||
#ifdef CONFIG_MULTITHREADING
|
||||
struct retention_data *data = dev->data;
|
||||
|
||||
k_mutex_unlock(&data->lock);
|
||||
#else
|
||||
ARG_UNUSED(dev);
|
||||
#endif
|
||||
}
|
||||
|
||||
static int retention_checksum(const struct device *dev, uint32_t *output)
|
||||
{
|
||||
const struct retention_config *config = dev->config;
|
||||
int rc = -ENOSYS;
|
||||
|
||||
if (config->checksum_size == CHECKSUM_CRC8 ||
|
||||
config->checksum_size == CHECKSUM_CRC16 ||
|
||||
config->checksum_size == CHECKSUM_CRC32) {
|
||||
size_t pos = config->offset + config->prefix_len;
|
||||
size_t end = config->offset + config->size - config->checksum_size;
|
||||
uint8_t buffer[CONFIG_RETENTION_BUFFER_SIZE];
|
||||
|
||||
*output = 0;
|
||||
|
||||
while (pos < end) {
|
||||
uint8_t read_size = MIN((end - pos), sizeof(buffer));
|
||||
|
||||
rc = retained_mem_read(config->parent, pos, buffer, read_size);
|
||||
|
||||
if (rc < 0) {
|
||||
goto finish;
|
||||
}
|
||||
|
||||
if (config->checksum_size == CHECKSUM_CRC8) {
|
||||
*output = (uint32_t)crc8(buffer, read_size, 0x12,
|
||||
(uint8_t)*output, false);
|
||||
} else if (config->checksum_size == CHECKSUM_CRC16) {
|
||||
*output = (uint32_t)crc16_itu_t((uint16_t)*output,
|
||||
buffer, read_size);
|
||||
} else if (config->checksum_size == CHECKSUM_CRC32) {
|
||||
*output = crc32_ieee_update(*output, buffer, read_size);
|
||||
}
|
||||
|
||||
pos += read_size;
|
||||
}
|
||||
}
|
||||
|
||||
finish:
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int retention_init(const struct device *dev)
|
||||
{
|
||||
const struct retention_config *config = dev->config;
|
||||
#ifdef CONFIG_MULTITHREADING
|
||||
struct retention_data *data = dev->data;
|
||||
#endif
|
||||
ssize_t area_size;
|
||||
|
||||
if (!device_is_ready(config->parent)) {
|
||||
LOG_ERR("Parent device is not ready");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
/* Ensure backend has a large enough storage area for the requirements of
|
||||
* this retention area
|
||||
*/
|
||||
area_size = retained_mem_size(config->parent);
|
||||
|
||||
if (area_size < 0) {
|
||||
LOG_ERR("Parent initialisation failure: %d", area_size);
|
||||
return area_size;
|
||||
}
|
||||
|
||||
if ((config->offset + config->size) > area_size) {
|
||||
/* Backend storage is insufficient */
|
||||
LOG_ERR("Underlying area size is insufficient, requires: 0x%x, has: 0x%x",
|
||||
(config->offset + config->size), area_size);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_MULTITHREADING
|
||||
k_mutex_init(&data->lock);
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
ssize_t retention_size(const struct device *dev)
|
||||
{
|
||||
const struct retention_config *config = dev->config;
|
||||
|
||||
return (config->size - config->reserved_size);
|
||||
}
|
||||
|
||||
int retention_is_valid(const struct device *dev)
|
||||
{
|
||||
const struct retention_config *config = dev->config;
|
||||
struct retention_data *data = dev->data;
|
||||
int rc = 0;
|
||||
uint8_t buffer[CONFIG_RETENTION_BUFFER_SIZE];
|
||||
off_t pos;
|
||||
|
||||
retention_lock_take(dev);
|
||||
|
||||
/* If neither the header or checksum are enabled, return a not supported error */
|
||||
if (config->prefix_len == 0 && config->checksum_size == 0) {
|
||||
rc = -ENOTSUP;
|
||||
goto finish;
|
||||
}
|
||||
|
||||
if (config->prefix_len != 0) {
|
||||
/* Check magic header is present at the start of the section */
|
||||
pos = 0;
|
||||
|
||||
while (pos < config->prefix_len) {
|
||||
uint8_t read_size = MIN((config->prefix_len - pos), sizeof(buffer));
|
||||
|
||||
rc = retained_mem_read(config->parent, (config->offset + pos), buffer,
|
||||
read_size);
|
||||
|
||||
if (rc < 0) {
|
||||
goto finish;
|
||||
}
|
||||
|
||||
if (memcmp(&config->prefix[pos], buffer, read_size) != 0) {
|
||||
/* If the magic header does not match, do not check the rest of
|
||||
* the validity of the data, assume it is invalid
|
||||
*/
|
||||
data->header_written = false;
|
||||
rc = 0;
|
||||
goto finish;
|
||||
}
|
||||
|
||||
pos += read_size;
|
||||
}
|
||||
|
||||
/* Header already exists so no need to re-write it again */
|
||||
data->header_written = true;
|
||||
}
|
||||
|
||||
if (config->checksum_size != 0) {
|
||||
/* Check the checksum validity, for this all the data must be read out */
|
||||
uint32_t checksum = 0;
|
||||
uint32_t expected_checksum = 0;
|
||||
ssize_t data_size = config->size - config->checksum_size;
|
||||
|
||||
rc = retention_checksum(dev, &checksum);
|
||||
|
||||
if (rc < 0) {
|
||||
goto finish;
|
||||
}
|
||||
|
||||
if (config->checksum_size == CHECKSUM_CRC8) {
|
||||
uint8_t read_checksum;
|
||||
|
||||
rc = retained_mem_read(config->parent, (config->offset + data_size),
|
||||
(void *)&read_checksum, sizeof(read_checksum));
|
||||
expected_checksum = (uint32_t)read_checksum;
|
||||
} else if (config->checksum_size == CHECKSUM_CRC16) {
|
||||
uint16_t read_checksum;
|
||||
|
||||
rc = retained_mem_read(config->parent, (config->offset + data_size),
|
||||
(void *)&read_checksum, sizeof(read_checksum));
|
||||
expected_checksum = (uint32_t)read_checksum;
|
||||
} else if (config->checksum_size == CHECKSUM_CRC32) {
|
||||
rc = retained_mem_read(config->parent, (config->offset + data_size),
|
||||
(void *)&expected_checksum,
|
||||
sizeof(expected_checksum));
|
||||
}
|
||||
|
||||
if (rc < 0) {
|
||||
goto finish;
|
||||
}
|
||||
|
||||
if (checksum != expected_checksum) {
|
||||
goto finish;
|
||||
}
|
||||
}
|
||||
|
||||
/* At this point, checks have passed (if enabled), mark data as being valid */
|
||||
rc = DATA_VALID_VALUE;
|
||||
|
||||
finish:
|
||||
retention_lock_release(dev);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
int retention_read(const struct device *dev, off_t offset, uint8_t *buffer, size_t size)
|
||||
{
|
||||
const struct retention_config *config = dev->config;
|
||||
int rc;
|
||||
|
||||
if (offset < 0 || ((size_t)offset + size) > (config->size - config->reserved_size)) {
|
||||
/* Disallow reading past the virtual data size or before it */
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
retention_lock_take(dev);
|
||||
|
||||
rc = retained_mem_read(config->parent, (config->offset + config->prefix_len +
|
||||
(size_t)offset), buffer, size);
|
||||
|
||||
retention_lock_release(dev);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
int retention_write(const struct device *dev, off_t offset, const uint8_t *buffer, size_t size)
|
||||
{
|
||||
const struct retention_config *config = dev->config;
|
||||
struct retention_data *data = dev->data;
|
||||
int rc;
|
||||
|
||||
retention_lock_take(dev);
|
||||
|
||||
if (offset < 0 || ((size_t)offset + size) > (config->size - config->reserved_size)) {
|
||||
/* Disallow writing past the virtual data size or before it */
|
||||
rc = -EINVAL;
|
||||
goto finish;
|
||||
}
|
||||
|
||||
rc = retained_mem_write(config->parent, (config->offset + config->prefix_len +
|
||||
(size_t)offset), buffer, size);
|
||||
|
||||
if (rc < 0) {
|
||||
goto finish;
|
||||
}
|
||||
|
||||
/* Write optional header and footer information, these are done last to ensure data
|
||||
* validity before marking it as being valid
|
||||
*/
|
||||
if (config->prefix_len != 0 && data->header_written == false) {
|
||||
rc = retained_mem_write(config->parent, config->offset, (void *)config->prefix,
|
||||
config->prefix_len);
|
||||
|
||||
if (rc < 0) {
|
||||
goto finish;
|
||||
}
|
||||
|
||||
data->header_written = true;
|
||||
}
|
||||
|
||||
if (config->checksum_size != 0) {
|
||||
/* Generating a checksum requires reading out all the data in the region */
|
||||
uint32_t checksum = 0;
|
||||
|
||||
rc = retention_checksum(dev, &checksum);
|
||||
|
||||
if (rc < 0) {
|
||||
goto finish;
|
||||
}
|
||||
|
||||
if (config->checksum_size == CHECKSUM_CRC8) {
|
||||
uint8_t output_checksum = (uint8_t)checksum;
|
||||
|
||||
rc = retained_mem_write(config->parent,
|
||||
(config->offset + config->size - config->checksum_size),
|
||||
(void *)&output_checksum, sizeof(output_checksum));
|
||||
} else if (config->checksum_size == CHECKSUM_CRC16) {
|
||||
uint16_t output_checksum = (uint16_t)checksum;
|
||||
|
||||
rc = retained_mem_write(config->parent,
|
||||
(config->offset + config->size - config->checksum_size),
|
||||
(void *)&output_checksum, sizeof(output_checksum));
|
||||
} else if (config->checksum_size == CHECKSUM_CRC32) {
|
||||
rc = retained_mem_write(config->parent,
|
||||
(config->offset + config->size - config->checksum_size),
|
||||
(void *)&checksum, sizeof(checksum));
|
||||
}
|
||||
}
|
||||
|
||||
finish:
|
||||
retention_lock_release(dev);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
int retention_clear(const struct device *dev)
|
||||
{
|
||||
const struct retention_config *config = dev->config;
|
||||
struct retention_data *data = dev->data;
|
||||
int rc = 0;
|
||||
uint8_t buffer[CONFIG_RETENTION_BUFFER_SIZE];
|
||||
off_t pos = 0;
|
||||
|
||||
memset(buffer, 0, sizeof(buffer));
|
||||
|
||||
retention_lock_take(dev);
|
||||
|
||||
data->header_written = false;
|
||||
|
||||
while (pos < config->size) {
|
||||
rc = retained_mem_write(config->parent, (config->offset + pos), buffer,
|
||||
MIN((config->size - pos), sizeof(buffer)));
|
||||
|
||||
if (rc < 0) {
|
||||
goto finish;
|
||||
}
|
||||
|
||||
pos += MIN((config->size - pos), sizeof(buffer));
|
||||
}
|
||||
|
||||
finish:
|
||||
retention_lock_release(dev);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static const struct retention_api retention_api = {
|
||||
.size = retention_size,
|
||||
.is_valid = retention_is_valid,
|
||||
.read = retention_read,
|
||||
.write = retention_write,
|
||||
.clear = retention_clear,
|
||||
};
|
||||
|
||||
#define RETENTION_DEVICE(inst) \
|
||||
static struct retention_data \
|
||||
retention_data_##inst = { \
|
||||
.header_written = false, \
|
||||
}; \
|
||||
static const struct retention_config \
|
||||
retention_config_##inst = { \
|
||||
.parent = DEVICE_DT_GET(DT_PARENT(DT_INST(inst, DT_DRV_COMPAT))), \
|
||||
.checksum_size = DT_INST_PROP(inst, checksum), \
|
||||
.offset = DT_INST_REG_ADDR(inst), \
|
||||
.size = DT_INST_REG_SIZE(inst), \
|
||||
.reserved_size = (COND_CODE_1(DT_INST_NODE_HAS_PROP(inst, prefix), \
|
||||
(DT_INST_PROP_LEN(inst, prefix)), (0)) + \
|
||||
DT_INST_PROP(inst, checksum)), \
|
||||
.prefix_len = COND_CODE_1(DT_INST_NODE_HAS_PROP(inst, prefix), \
|
||||
(DT_INST_PROP_LEN(inst, prefix)), (0)), \
|
||||
.prefix = DT_INST_PROP_OR(inst, prefix, {0}), \
|
||||
}; \
|
||||
DEVICE_DT_INST_DEFINE(inst, \
|
||||
&retention_init, \
|
||||
NULL, \
|
||||
&retention_data_##inst, \
|
||||
&retention_config_##inst, \
|
||||
POST_KERNEL, \
|
||||
CONFIG_RETENTION_INIT_PRIORITY, \
|
||||
&retention_api);
|
||||
|
||||
DT_INST_FOREACH_STATUS_OKAY(RETENTION_DEVICE)
|
Loading…
Reference in a new issue