1 /*
2  * Copyright (c) 2022 Google Inc
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #define DT_DRV_COMPAT st_stm32_bbram
8 
9 #include <errno.h>
10 
11 #include <zephyr/drivers/bbram.h>
12 #include <zephyr/logging/log.h>
13 #include <zephyr/sys/util.h>
14 #include <stm32_ll_pwr.h>
15 #include <stm32_ll_rtc.h>
16 LOG_MODULE_REGISTER(bbram, CONFIG_BBRAM_LOG_LEVEL);
17 
18 #define STM32_BKP_REG_BYTES		 4
19 #ifdef TAMP
20 /* If a SoC has a TAMP peripherals, then the backup registers are defined there,
21  * not in the RTC.
22  */
23 #define STM32_BKP_REG_OFFSET		 (TAMP_BASE + offsetof(TAMP_TypeDef, BKP0R) - RTC_BASE)
24 #else
25 #define STM32_BKP_REG_OFFSET		 offsetof(RTC_TypeDef, BKP0R)
26 #endif
27 #define STM32_BKP_REG_INDEX(offset)	 ((offset) >> 2)
28 #define STM32_BKP_REG_BYTE_INDEX(offset) ((offset)&0x3UL)
29 #define STM32_BKP_REG(i)		 (((volatile uint32_t *)config->base_addr)[(i)])
30 
31 /** Device config */
32 struct bbram_stm32_config {
33 	const struct device *parent;
34 	/* BBRAM base address */
35 	uintptr_t base_addr;
36 	/* BBRAM size in bytes. */
37 	int size;
38 };
39 
bbram_stm32_read(const struct device * dev,size_t offset,size_t size,uint8_t * data)40 static int bbram_stm32_read(const struct device *dev, size_t offset, size_t size, uint8_t *data)
41 {
42 	const struct bbram_stm32_config *config = dev->config;
43 	uint32_t reg, begin, to_copy;
44 
45 	if (size < 1 || offset + size > config->size) {
46 		return -EFAULT;
47 	}
48 
49 	for (size_t read = 0; read < size; read += to_copy) {
50 		reg = STM32_BKP_REG(STM32_BKP_REG_INDEX(offset + read));
51 		begin = STM32_BKP_REG_BYTE_INDEX(offset + read);
52 		to_copy = MIN(STM32_BKP_REG_BYTES - begin, size - read);
53 		bytecpy(data + read, (uint8_t *)&reg + begin, to_copy);
54 	}
55 
56 	return 0;
57 }
58 
bbram_stm32_write(const struct device * dev,size_t offset,size_t size,const uint8_t * data)59 static int bbram_stm32_write(const struct device *dev, size_t offset, size_t size,
60 			     const uint8_t *data)
61 {
62 	const struct bbram_stm32_config *config = dev->config;
63 	uint32_t reg, begin, to_copy;
64 
65 	if (size < 1 || offset + size > config->size) {
66 		return -EFAULT;
67 	}
68 
69 #if defined(PWR_CR_DBP) || defined(PWR_CR1_DBP) || defined(PWR_DBPCR_DBP) || defined(PWR_DBPR_DBP)
70 	LL_PWR_EnableBkUpAccess();
71 #endif /* PWR_CR_DBP || PWR_CR1_DBP || PWR_DBPCR_DBP || PWR_DBPR_DBP */
72 
73 	for (size_t written = 0; written < size; written += to_copy) {
74 		reg = STM32_BKP_REG(STM32_BKP_REG_INDEX(offset + written));
75 		begin = STM32_BKP_REG_BYTE_INDEX(offset + written);
76 		to_copy = MIN(STM32_BKP_REG_BYTES - begin, size - written);
77 		bytecpy((uint8_t *)&reg + begin, data + written, to_copy);
78 		STM32_BKP_REG(STM32_BKP_REG_INDEX(offset + written)) = reg;
79 	}
80 
81 #if defined(PWR_CR_DBP) || defined(PWR_CR1_DBP) || defined(PWR_DBPCR_DBP) || defined(PWR_DBPR_DBP)
82 	LL_PWR_DisableBkUpAccess();
83 #endif /* PWR_CR_DBP || PWR_CR1_DBP || PWR_DBPCR_DBP || PWR_DBPR_DBP */
84 
85 	return 0;
86 }
87 
bbram_stm32_get_size(const struct device * dev,size_t * size)88 static int bbram_stm32_get_size(const struct device *dev, size_t *size)
89 {
90 	const struct bbram_stm32_config *config = dev->config;
91 
92 	*size = config->size;
93 
94 	return 0;
95 }
96 
97 static const struct bbram_driver_api bbram_stm32_driver_api = {
98 	.read = bbram_stm32_read,
99 	.write = bbram_stm32_write,
100 	.get_size = bbram_stm32_get_size,
101 };
102 
bbram_stm32_init(const struct device * dev)103 static int bbram_stm32_init(const struct device *dev)
104 {
105 	const struct bbram_stm32_config *config = dev->config;
106 
107 	if (!device_is_ready(config->parent)) {
108 		LOG_ERR("Device %s is not ready", config->parent->name);
109 		return -ENODEV;
110 	}
111 
112 	return 0;
113 }
114 
115 #define BBRAM_INIT(inst)                                                                           \
116 	static const struct bbram_stm32_config bbram_cfg_##inst = {                                \
117 		.parent = DEVICE_DT_GET(DT_INST_PARENT(inst)),                                     \
118 		.base_addr = DT_REG_ADDR(DT_INST_PARENT(inst)) + STM32_BKP_REG_OFFSET,             \
119 		.size = DT_INST_PROP(inst, st_backup_regs) * STM32_BKP_REG_BYTES,                  \
120 	};                                                                                         \
121 	DEVICE_DT_INST_DEFINE(inst, bbram_stm32_init, NULL, NULL, &bbram_cfg_##inst, PRE_KERNEL_1, \
122 			      CONFIG_BBRAM_INIT_PRIORITY, &bbram_stm32_driver_api);
123 
124 DT_INST_FOREACH_STATUS_OKAY(BBRAM_INIT);
125