1 /*
2  * Copyright (c) 2025 MASSDRIVER EI (massdriver.space)
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #define DT_DRV_COMPAT bflb_efuse
8 
9 #include <zephyr/drivers/syscon.h>
10 #include <zephyr/kernel.h>
11 
12 #include <zephyr/logging/log.h>
13 LOG_MODULE_REGISTER(efuse_bflb, CONFIG_SYSCON_LOG_LEVEL);
14 
15 #include <bflb_soc.h>
16 #include <hbn_reg.h>
17 #include <ef_ctrl_reg.h>
18 #include <extra_defines.h>
19 #include <zephyr/drivers/clock_control/clock_control_bflb_common.h>
20 
21 struct efuse_bflb_data {
22 	uint8_t cache[DT_INST_PROP(0, size)];
23 	bool cached;
24 };
25 
26 struct efuse_bflb_config {
27 	uintptr_t addr;
28 	size_t size;
29 };
30 
efuse_bflb_clock_delay_32M_ms(uint32_t ms)31 static void efuse_bflb_clock_delay_32M_ms(uint32_t ms)
32 {
33 	uint32_t count = 0;
34 
35 	do {
36 		__asm__ volatile (".rept 32 ; nop ; .endr");
37 		count++;
38 	} while (count < ms);
39 }
40 
efuse_bflb_is_pds_busy(const struct device * dev)41 static uint32_t efuse_bflb_is_pds_busy(const struct device *dev)
42 {
43 	uint32_t tmp;
44 	const struct efuse_bflb_config *config = dev->config;
45 
46 	tmp = sys_read32(config->addr + EF_CTRL_EF_IF_CTRL_0_OFFSET);
47 	if (tmp & EF_CTRL_EF_IF_0_BUSY_MSK) {
48 		return 1;
49 	}
50 	return 0;
51 }
52 
53 /* /!\ only use when running on 32Mhz Oscillator Clock
54  * (system_set_root_clock(0);
55  * system_set_root_clock_dividers(0, 0);
56  * sys_write32(32 * 1000 * 1000, CORECLOCKREGISTER);)
57  * Only Use with IRQs off
58  * returns 0 when error
59  */
efuse_bflb_efuse_read(const struct device * dev)60 static void efuse_bflb_efuse_read(const struct device *dev)
61 {
62 	const struct efuse_bflb_config *config = dev->config;
63 	uint32_t tmp;
64 	uint32_t *pefuse_start = (uint32_t *)(config->addr);
65 	uint32_t timeout = 0;
66 
67 	do {
68 		efuse_bflb_clock_delay_32M_ms(1);
69 		timeout++;
70 	} while (timeout < EF_CTRL_DFT_TIMEOUT_VAL && efuse_bflb_is_pds_busy(dev) > 0);
71 
72 	/* do a 'ahb clock' setup */
73 	tmp =	EF_CTRL_EFUSE_CTRL_PROTECT
74 		| (EF_CTRL_OP_MODE_AUTO << EF_CTRL_EF_IF_0_MANUAL_EN_POS)
75 		| (EF_CTRL_PARA_DFT << EF_CTRL_EF_IF_0_CYC_MODIFY_POS)
76 #if defined(CONFIG_SOC_SERIES_BL60X) || defined(CONFIG_SOC_SERIES_BL70X)
77 		| (EF_CTRL_SAHB_CLK << EF_CTRL_EF_CLK_SAHB_DATA_SEL_POS)
78 #endif
79 		| (1 << EF_CTRL_EF_IF_AUTO_RD_EN_POS)
80 		| (0 << EF_CTRL_EF_IF_POR_DIG_POS)
81 		| (1 << EF_CTRL_EF_IF_0_INT_CLR_POS)
82 		| (0 << EF_CTRL_EF_IF_0_RW_POS)
83 		| (0 << EF_CTRL_EF_IF_0_TRIG_POS);
84 
85 	sys_write32(tmp, config->addr + EF_CTRL_EF_IF_CTRL_0_OFFSET);
86 	clock_bflb_settle();
87 
88 	/* clear PDS cache registry */
89 	for (uint32_t i = 0; i < config->size / 4; i++) {
90 		pefuse_start[i] = 0;
91 	}
92 
93 	/* Load efuse region0 */
94 	/* not ahb clock setup */
95 	tmp =	EF_CTRL_EFUSE_CTRL_PROTECT
96 		| (EF_CTRL_OP_MODE_AUTO << EF_CTRL_EF_IF_0_MANUAL_EN_POS)
97 		| (EF_CTRL_PARA_DFT << EF_CTRL_EF_IF_0_CYC_MODIFY_POS)
98 #if defined(CONFIG_SOC_SERIES_BL60X) || defined(CONFIG_SOC_SERIES_BL70X)
99 		| (EF_CTRL_EF_CLK << EF_CTRL_EF_CLK_SAHB_DATA_SEL_POS)
100 #endif
101 		| (1 << EF_CTRL_EF_IF_AUTO_RD_EN_POS)
102 		| (0 << EF_CTRL_EF_IF_POR_DIG_POS)
103 		| (1 << EF_CTRL_EF_IF_0_INT_CLR_POS)
104 		| (0 << EF_CTRL_EF_IF_0_RW_POS)
105 		| (0 << EF_CTRL_EF_IF_0_TRIG_POS);
106 	sys_write32(tmp, config->addr + EF_CTRL_EF_IF_CTRL_0_OFFSET);
107 
108 	/* trigger read */
109 	tmp =	EF_CTRL_EFUSE_CTRL_PROTECT
110 		| (EF_CTRL_OP_MODE_AUTO << EF_CTRL_EF_IF_0_MANUAL_EN_POS)
111 		| (EF_CTRL_PARA_DFT << EF_CTRL_EF_IF_0_CYC_MODIFY_POS)
112 #if defined(CONFIG_SOC_SERIES_BL60X) || defined(CONFIG_SOC_SERIES_BL70X)
113 		| (EF_CTRL_EF_CLK << EF_CTRL_EF_CLK_SAHB_DATA_SEL_POS)
114 #endif
115 		| (1 << EF_CTRL_EF_IF_AUTO_RD_EN_POS)
116 		| (0 << EF_CTRL_EF_IF_POR_DIG_POS)
117 		| (1 << EF_CTRL_EF_IF_0_INT_CLR_POS)
118 		| (0 << EF_CTRL_EF_IF_0_RW_POS)
119 		| (1 << EF_CTRL_EF_IF_0_TRIG_POS);
120 	sys_write32(tmp, config->addr + EF_CTRL_EF_IF_CTRL_0_OFFSET);
121 	efuse_bflb_clock_delay_32M_ms(5);
122 
123 	/* wait for read to complete */
124 	do {
125 		efuse_bflb_clock_delay_32M_ms(1);
126 		tmp = sys_read32(config->addr + EF_CTRL_EF_IF_CTRL_0_OFFSET);
127 	} while ((tmp & EF_CTRL_EF_IF_0_BUSY_MSK) ||
128 		!(tmp & EF_CTRL_EF_IF_0_AUTOLOAD_DONE_MSK));
129 
130 	/* do a 'ahb clock' setup */
131 	tmp =	EF_CTRL_EFUSE_CTRL_PROTECT
132 		| (EF_CTRL_OP_MODE_AUTO << EF_CTRL_EF_IF_0_MANUAL_EN_POS)
133 		| (EF_CTRL_PARA_DFT << EF_CTRL_EF_IF_0_CYC_MODIFY_POS)
134 #if defined(CONFIG_SOC_SERIES_BL60X) || defined(CONFIG_SOC_SERIES_BL70X)
135 		| (EF_CTRL_SAHB_CLK << EF_CTRL_EF_CLK_SAHB_DATA_SEL_POS)
136 #endif
137 		| (1 << EF_CTRL_EF_IF_AUTO_RD_EN_POS)
138 		| (0 << EF_CTRL_EF_IF_POR_DIG_POS)
139 		| (1 << EF_CTRL_EF_IF_0_INT_CLR_POS)
140 		| (0 << EF_CTRL_EF_IF_0_RW_POS)
141 		| (0 << EF_CTRL_EF_IF_0_TRIG_POS);
142 
143 	sys_write32(tmp, config->addr + EF_CTRL_EF_IF_CTRL_0_OFFSET);
144 }
145 
efuse_bflb_cache(const struct device * dev)146 static void efuse_bflb_cache(const struct device *dev)
147 {
148 	struct efuse_bflb_data *data = dev->data;
149 	const struct efuse_bflb_config *config = dev->config;
150 	uint32_t tmp;
151 	uint8_t old_clock_root;
152 	uint32_t key;
153 
154 	key = irq_lock();
155 
156 	old_clock_root = clock_bflb_get_root_clock();
157 
158 	clock_bflb_set_root_clock(BFLB_MAIN_CLOCK_RC32M);
159 	clock_bflb_settle();
160 
161 	efuse_bflb_efuse_read(dev);
162 	/* reads *must* be 32-bits aligned AND does not work with the method memcpy uses */
163 	for (int i = 0; i < config->size / sizeof(uint32_t); i++) {
164 		tmp = sys_read32(config->addr + i * 4);
165 		data->cache[i * sizeof(uint32_t) + 3] = (tmp & 0xFF000000U) >> 24;
166 		data->cache[i * sizeof(uint32_t) + 2] = (tmp & 0x00FF0000U) >> 16;
167 		data->cache[i * sizeof(uint32_t) + 1] = (tmp & 0x0000FF00U) >> 8;
168 		data->cache[i * sizeof(uint32_t) + 0] = (tmp & 0x000000FFU);
169 	}
170 
171 	clock_bflb_set_root_clock(old_clock_root);
172 	clock_bflb_settle();
173 	data->cached = true;
174 
175 	irq_unlock(key);
176 }
177 
efuse_bflb_read(const struct device * dev,uint16_t reg,uint32_t * val)178 static int efuse_bflb_read(const struct device *dev, uint16_t reg, uint32_t *val)
179 {
180 	struct efuse_bflb_data *data = dev->data;
181 
182 	if (!val) {
183 		return -EINVAL;
184 	}
185 
186 	if (!data->cached) {
187 		efuse_bflb_cache(dev);
188 	}
189 
190 	*val = *((uint32_t *)&data->cache[reg]);
191 	return 0;
192 }
193 
efuse_bflb_size(const struct device * dev,size_t * size)194 static int efuse_bflb_size(const struct device *dev, size_t *size)
195 {
196 	const struct efuse_bflb_config *config = dev->config;
197 
198 	*size = config->size;
199 	return 0;
200 }
201 
efuse_bflb_get_base(const struct device * dev,uintptr_t * addr)202 static int efuse_bflb_get_base(const struct device *dev, uintptr_t *addr)
203 {
204 	struct efuse_bflb_data *data = dev->data;
205 
206 	*addr = (uintptr_t)data->cache;
207 	return 0;
208 }
209 
210 static DEVICE_API(syscon, efuse_bflb_api) = {
211 	.read = efuse_bflb_read,
212 	.get_size = efuse_bflb_size,
213 	.get_base = efuse_bflb_get_base,
214 };
215 
216 static const struct efuse_bflb_config efuse_config = {
217 	.addr = DT_INST_REG_ADDR(0),
218 	.size = DT_INST_PROP(0, size),
219 };
220 
221 static struct efuse_bflb_data efuse_data = {
222 	.cached = false,
223 	.cache = {0},
224 };
225 
226 
227 DEVICE_DT_INST_DEFINE(0, NULL, NULL, &efuse_data, &efuse_config, POST_KERNEL,
228 		      CONFIG_SYSCON_INIT_PRIORITY, &efuse_bflb_api);
229