/* * Copyright (c) 2022 Google Inc * * SPDX-License-Identifier: Apache-2.0 */ #define DT_DRV_COMPAT st_stm32_bbram #include #include #include #include #include #include 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);