On devices where the backup memory is part of the TAMP peripheral, the Backup Domain Protection prevents write to the backup registers. This fix disables the protection before writing registers and re-enables it afterwards. Signed-off-by: Aurelien Jarno <aurelien@aurel32.net>
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);
|