125 lines
3.9 KiB
C
125 lines
3.9 KiB
C
/*
|
|
* Copyright (c) 2022 Google Inc
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
#define DT_DRV_COMPAT st_stm32_bbram
|
|
|
|
#include <errno.h>
|
|
|
|
#include <zephyr/drivers/bbram.h>
|
|
#include <zephyr/logging/log.h>
|
|
#include <zephyr/sys/util.h>
|
|
#include <stm32_ll_pwr.h>
|
|
#include <stm32_ll_rtc.h>
|
|
LOG_MODULE_REGISTER(bbram, CONFIG_BBRAM_LOG_LEVEL);
|
|
|
|
#define STM32_BKP_REG_BYTES 4
|
|
#ifdef TAMP
|
|
/* If a SoC has a TAMP peripherals, then the backup registers are defined there,
|
|
* not in the RTC.
|
|
*/
|
|
#define STM32_BKP_REG_OFFSET (TAMP_BASE + offsetof(TAMP_TypeDef, BKP0R) - RTC_BASE)
|
|
#else
|
|
#define STM32_BKP_REG_OFFSET offsetof(RTC_TypeDef, BKP0R)
|
|
#endif
|
|
#define STM32_BKP_REG_INDEX(offset) ((offset) >> 2)
|
|
#define STM32_BKP_REG_BYTE_INDEX(offset) ((offset)&0x3UL)
|
|
#define STM32_BKP_REG(i) (((volatile uint32_t *)config->base_addr)[(i)])
|
|
|
|
/** Device config */
|
|
struct bbram_stm32_config {
|
|
const struct device *parent;
|
|
/* BBRAM base address */
|
|
uintptr_t base_addr;
|
|
/* BBRAM size in bytes. */
|
|
int size;
|
|
};
|
|
|
|
static int bbram_stm32_read(const struct device *dev, size_t offset, size_t size, uint8_t *data)
|
|
{
|
|
const struct bbram_stm32_config *config = dev->config;
|
|
uint32_t reg, begin, to_copy;
|
|
|
|
if (size < 1 || offset + size > config->size) {
|
|
return -EFAULT;
|
|
}
|
|
|
|
for (size_t read = 0; read < size; read += to_copy) {
|
|
reg = STM32_BKP_REG(STM32_BKP_REG_INDEX(offset + read));
|
|
begin = STM32_BKP_REG_BYTE_INDEX(offset + read);
|
|
to_copy = MIN(STM32_BKP_REG_BYTES - begin, size - read);
|
|
bytecpy(data + read, (uint8_t *)® + begin, to_copy);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int bbram_stm32_write(const struct device *dev, size_t offset, size_t size,
|
|
const uint8_t *data)
|
|
{
|
|
const struct bbram_stm32_config *config = dev->config;
|
|
uint32_t reg, begin, to_copy;
|
|
|
|
if (size < 1 || offset + size > config->size) {
|
|
return -EFAULT;
|
|
}
|
|
|
|
#if defined(PWR_CR_DBP) || defined(PWR_CR1_DBP) || defined(PWR_DBPCR_DBP) || defined(PWR_DBPR_DBP)
|
|
LL_PWR_EnableBkUpAccess();
|
|
#endif /* PWR_CR_DBP || PWR_CR1_DBP || PWR_DBPCR_DBP || PWR_DBPR_DBP */
|
|
|
|
for (size_t written = 0; written < size; written += to_copy) {
|
|
reg = STM32_BKP_REG(STM32_BKP_REG_INDEX(offset + written));
|
|
begin = STM32_BKP_REG_BYTE_INDEX(offset + written);
|
|
to_copy = MIN(STM32_BKP_REG_BYTES - begin, size - written);
|
|
bytecpy((uint8_t *)® + begin, data + written, to_copy);
|
|
STM32_BKP_REG(STM32_BKP_REG_INDEX(offset + written)) = reg;
|
|
}
|
|
|
|
#if defined(PWR_CR_DBP) || defined(PWR_CR1_DBP) || defined(PWR_DBPCR_DBP) || defined(PWR_DBPR_DBP)
|
|
LL_PWR_DisableBkUpAccess();
|
|
#endif /* PWR_CR_DBP || PWR_CR1_DBP || PWR_DBPCR_DBP || PWR_DBPR_DBP */
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int bbram_stm32_get_size(const struct device *dev, size_t *size)
|
|
{
|
|
const struct bbram_stm32_config *config = dev->config;
|
|
|
|
*size = config->size;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static const struct bbram_driver_api bbram_stm32_driver_api = {
|
|
.read = bbram_stm32_read,
|
|
.write = bbram_stm32_write,
|
|
.get_size = bbram_stm32_get_size,
|
|
};
|
|
|
|
static int bbram_stm32_init(const struct device *dev)
|
|
{
|
|
const struct bbram_stm32_config *config = dev->config;
|
|
|
|
if (!device_is_ready(config->parent)) {
|
|
LOG_ERR("Device %s is not ready", config->parent->name);
|
|
return -ENODEV;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
#define BBRAM_INIT(inst) \
|
|
static const struct bbram_stm32_config bbram_cfg_##inst = { \
|
|
.parent = DEVICE_DT_GET(DT_INST_PARENT(inst)), \
|
|
.base_addr = DT_REG_ADDR(DT_INST_PARENT(inst)) + STM32_BKP_REG_OFFSET, \
|
|
.size = DT_INST_PROP(inst, st_backup_regs) * STM32_BKP_REG_BYTES, \
|
|
}; \
|
|
DEVICE_DT_INST_DEFINE(inst, bbram_stm32_init, NULL, NULL, &bbram_cfg_##inst, PRE_KERNEL_1, \
|
|
CONFIG_BBRAM_INIT_PRIORITY, &bbram_stm32_driver_api);
|
|
|
|
DT_INST_FOREACH_STATUS_OKAY(BBRAM_INIT);
|