Skip to content

Commit 7c1cbfa

Browse files
committed
drivers: syscon: Introduce BFLB Efuse driver
This introduces a driver used to access bouffalolab efuses via syscon API Signed-off-by: Camille BAUD <[email protected]>
1 parent 98b4a9f commit 7c1cbfa

File tree

5 files changed

+285
-0
lines changed

5 files changed

+285
-0
lines changed

drivers/syscon/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,4 @@
22

33
zephyr_library()
44
zephyr_library_sources_ifdef(CONFIG_SYSCON_GENERIC syscon.c)
5+
zephyr_library_sources_ifdef(CONFIG_SYSCON_BFLB_EFUSE syscon_bflb_efuse.c)

drivers/syscon/Kconfig

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,4 +38,6 @@ config SYSCON_INIT_PRIORITY
3838
initialized earlier in the startup cycle. If unsure, leave at default
3939
value
4040

41+
source "drivers/syscon/Kconfig.bflb_efuse"
42+
4143
endif # SYSCON

drivers/syscon/Kconfig.bflb_efuse

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
# Copyright (c) 2024-2025 MASSDRIVER EI (massdriver.space)
2+
# SPDX-License-Identifier: Apache-2.0
3+
4+
config SYSCON_BFLB_EFUSE
5+
bool "Bouffalolab EFUSEs driver"
6+
default y
7+
depends on DT_HAS_BFLB_EFUSE_ENABLED
8+
help
9+
E-Fuse access via SYSCON API for Bouffalolab platforms.

drivers/syscon/syscon_bflb_efuse.c

Lines changed: 256 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,256 @@
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 <bouffalolab/bl60x/bflb_soc.h>
16+
#include <bouffalolab/bl60x/hbn_reg.h>
17+
#include <bouffalolab/bl60x/ef_ctrl_reg.h>
18+
#include <bouffalolab/bl60x/extra_defines.h>
19+
20+
struct efuse_bflb_data {
21+
uint8_t cache[DT_INST_PROP(0, size)];
22+
bool cached;
23+
};
24+
25+
struct efuse_bflb_config {
26+
uintptr_t addr;
27+
size_t size;
28+
};
29+
30+
static inline void efuse_bflb_clock_settle(void)
31+
{
32+
__asm__ volatile (".rept 15 ; nop ; .endr");
33+
}
34+
35+
/* 32 Mhz Oscillator: 0
36+
* crystal: 1
37+
* PLL and 32M: 2
38+
* PLL and crystal: 3
39+
*/
40+
static void efuse_bflb_set_root_clock(uint32_t clock)
41+
{
42+
uint32_t tmp = 0;
43+
44+
/* invalid value, fallback to internal 32M */
45+
if (clock > 3) {
46+
clock = 0;
47+
}
48+
tmp = sys_read32(HBN_BASE + HBN_GLB_OFFSET);
49+
tmp = (tmp & HBN_ROOT_CLK_SEL_UMSK) | (clock << HBN_ROOT_CLK_SEL_POS);
50+
sys_write32(tmp, HBN_BASE + HBN_GLB_OFFSET);
51+
52+
efuse_bflb_clock_settle();
53+
}
54+
55+
static void efuse_bflb_clock_delay_32M_ms(uint32_t ms)
56+
{
57+
uint32_t count = 0;
58+
59+
do {
60+
__asm__ volatile (".rept 32 ; nop ; .endr");
61+
count++;
62+
} while (count < ms);
63+
}
64+
65+
static uint32_t efuse_bflb_is_pds_busy(const struct device *dev)
66+
{
67+
uint32_t tmp = 0;
68+
const struct efuse_bflb_config *config = dev->config;
69+
70+
tmp = sys_read32(config->addr + EF_CTRL_EF_IF_CTRL_0_OFFSET);
71+
if (tmp & EF_CTRL_EF_IF_0_BUSY_MSK) {
72+
return 1;
73+
}
74+
return 0;
75+
}
76+
77+
/* /!\ only use when running on 32Mhz Oscillator Clock
78+
* (system_set_root_clock(0);
79+
* system_set_root_clock_dividers(0, 0);
80+
* sys_write32(32 * 1000 * 1000, CORECLOCKREGISTER);)
81+
* Only Use with IRQs off
82+
* returns 0 when error
83+
*/
84+
static void efuse_bflb_efuse_read(const struct device *dev)
85+
{
86+
const struct efuse_bflb_config *config = dev->config;
87+
uint32_t tmp = 0;
88+
uint32_t *pefuse_start = (uint32_t *)(config->addr);
89+
uint32_t timeout = 0;
90+
91+
do {
92+
efuse_bflb_clock_delay_32M_ms(1);
93+
timeout++;
94+
} while (timeout < EF_CTRL_DFT_TIMEOUT_VAL && efuse_bflb_is_pds_busy(dev) > 0);
95+
96+
/* do a 'ahb clock' setup */
97+
tmp = EF_CTRL_EFUSE_CTRL_PROTECT
98+
| (EF_CTRL_OP_MODE_AUTO << EF_CTRL_EF_IF_0_MANUAL_EN_POS)
99+
| (EF_CTRL_PARA_DFT << EF_CTRL_EF_IF_0_CYC_MODIFY_POS)
100+
#if defined(CONFIG_SOC_SERIES_BL60X)
101+
| (EF_CTRL_SAHB_CLK << EF_CTRL_EF_CLK_SAHB_DATA_SEL_POS)
102+
#endif
103+
| (1 << EF_CTRL_EF_IF_AUTO_RD_EN_POS)
104+
| (0 << EF_CTRL_EF_IF_POR_DIG_POS)
105+
| (1 << EF_CTRL_EF_IF_0_INT_CLR_POS)
106+
| (0 << EF_CTRL_EF_IF_0_RW_POS)
107+
| (0 << EF_CTRL_EF_IF_0_TRIG_POS);
108+
109+
sys_write32(tmp, config->addr + EF_CTRL_EF_IF_CTRL_0_OFFSET);
110+
efuse_bflb_clock_settle();
111+
112+
/* clear PDS cache registry */
113+
for (uint32_t i = 0; i < config->size / 4; i++) {
114+
pefuse_start[i] = 0;
115+
}
116+
117+
/* Load efuse region0 */
118+
/* not ahb clock setup */
119+
tmp = EF_CTRL_EFUSE_CTRL_PROTECT
120+
| (EF_CTRL_OP_MODE_AUTO << EF_CTRL_EF_IF_0_MANUAL_EN_POS)
121+
| (EF_CTRL_PARA_DFT << EF_CTRL_EF_IF_0_CYC_MODIFY_POS)
122+
#if defined(CONFIG_SOC_SERIES_BL60X)
123+
| (EF_CTRL_EF_CLK << EF_CTRL_EF_CLK_SAHB_DATA_SEL_POS)
124+
#endif
125+
| (1 << EF_CTRL_EF_IF_AUTO_RD_EN_POS)
126+
| (0 << EF_CTRL_EF_IF_POR_DIG_POS)
127+
| (1 << EF_CTRL_EF_IF_0_INT_CLR_POS)
128+
| (0 << EF_CTRL_EF_IF_0_RW_POS)
129+
| (0 << EF_CTRL_EF_IF_0_TRIG_POS);
130+
sys_write32(tmp, config->addr + EF_CTRL_EF_IF_CTRL_0_OFFSET);
131+
132+
/* trigger read */
133+
tmp = EF_CTRL_EFUSE_CTRL_PROTECT
134+
| (EF_CTRL_OP_MODE_AUTO << EF_CTRL_EF_IF_0_MANUAL_EN_POS)
135+
| (EF_CTRL_PARA_DFT << EF_CTRL_EF_IF_0_CYC_MODIFY_POS)
136+
#if defined(CONFIG_SOC_SERIES_BL60X)
137+
| (EF_CTRL_EF_CLK << EF_CTRL_EF_CLK_SAHB_DATA_SEL_POS)
138+
#endif
139+
| (1 << EF_CTRL_EF_IF_AUTO_RD_EN_POS)
140+
| (0 << EF_CTRL_EF_IF_POR_DIG_POS)
141+
| (1 << EF_CTRL_EF_IF_0_INT_CLR_POS)
142+
| (0 << EF_CTRL_EF_IF_0_RW_POS)
143+
| (1 << EF_CTRL_EF_IF_0_TRIG_POS);
144+
sys_write32(tmp, config->addr + EF_CTRL_EF_IF_CTRL_0_OFFSET);
145+
efuse_bflb_clock_delay_32M_ms(5);
146+
147+
/* wait for read to complete */
148+
do {
149+
efuse_bflb_clock_delay_32M_ms(1);
150+
tmp = sys_read32(config->addr + EF_CTRL_EF_IF_CTRL_0_OFFSET);
151+
} while ((tmp & EF_CTRL_EF_IF_0_BUSY_MSK) ||
152+
!(tmp & EF_CTRL_EF_IF_0_AUTOLOAD_DONE_MSK));
153+
154+
/* do a 'ahb clock' setup */
155+
tmp = EF_CTRL_EFUSE_CTRL_PROTECT
156+
| (EF_CTRL_OP_MODE_AUTO << EF_CTRL_EF_IF_0_MANUAL_EN_POS)
157+
| (EF_CTRL_PARA_DFT << EF_CTRL_EF_IF_0_CYC_MODIFY_POS)
158+
#if defined(CONFIG_SOC_SERIES_BL60X)
159+
| (EF_CTRL_SAHB_CLK << EF_CTRL_EF_CLK_SAHB_DATA_SEL_POS)
160+
#endif
161+
| (1 << EF_CTRL_EF_IF_AUTO_RD_EN_POS)
162+
| (0 << EF_CTRL_EF_IF_POR_DIG_POS)
163+
| (1 << EF_CTRL_EF_IF_0_INT_CLR_POS)
164+
| (0 << EF_CTRL_EF_IF_0_RW_POS)
165+
| (0 << EF_CTRL_EF_IF_0_TRIG_POS);
166+
167+
sys_write32(tmp, config->addr + EF_CTRL_EF_IF_CTRL_0_OFFSET);
168+
}
169+
170+
static void efuse_bflb_cache(const struct device *dev)
171+
{
172+
struct efuse_bflb_data *data = dev->data;
173+
const struct efuse_bflb_config *config = dev->config;
174+
uint32_t tmp = 0;
175+
uint8_t old_clock_root;
176+
177+
tmp = sys_read32(HBN_BASE + HBN_GLB_OFFSET);
178+
old_clock_root = (tmp & HBN_ROOT_CLK_SEL_MSK) >> HBN_ROOT_CLK_SEL_POS;
179+
180+
efuse_bflb_set_root_clock(0);
181+
efuse_bflb_clock_settle();
182+
183+
efuse_bflb_efuse_read(dev);
184+
/* reads *must* be 32-bits aligned AND does not work with the method memcpy uses */
185+
for (int i = 0; i < config->size / sizeof(uint32_t); i++) {
186+
tmp = sys_read32(config->addr + i * 4);
187+
data->cache[i * sizeof(uint32_t) + 3] = (tmp & 0xFF000000U) >> 24;
188+
data->cache[i * sizeof(uint32_t) + 2] = (tmp & 0x00FF0000U) >> 16;
189+
data->cache[i * sizeof(uint32_t) + 1] = (tmp & 0x0000FF00U) >> 8;
190+
data->cache[i * sizeof(uint32_t) + 0] = (tmp & 0x000000FFU);
191+
}
192+
193+
efuse_bflb_set_root_clock(old_clock_root);
194+
efuse_bflb_clock_settle();
195+
data->cached = true;
196+
}
197+
198+
static int efuse_bflb_read(const struct device *dev, uint16_t reg, uint32_t *val)
199+
{
200+
if (!dev) {
201+
return -ENODEV;
202+
}
203+
204+
struct efuse_bflb_data *data = dev->data;
205+
206+
if (!val) {
207+
return -EINVAL;
208+
}
209+
210+
if (!data->cached) {
211+
efuse_bflb_cache(dev);
212+
}
213+
214+
*val = *((uint32_t *)&data->cache[reg]);
215+
return 0;
216+
}
217+
218+
static int efuse_bflb_size(const struct device *dev, size_t *size)
219+
{
220+
const struct efuse_bflb_config *config = dev->config;
221+
222+
*size = config->size;
223+
return 0;
224+
}
225+
226+
static int efuse_bflb_get_base(const struct device *dev, uintptr_t *addr)
227+
{
228+
if (!dev) {
229+
return -ENODEV;
230+
}
231+
232+
struct efuse_bflb_data *data = dev->data;
233+
234+
*addr = (uintptr_t)data->cache;
235+
return 0;
236+
}
237+
238+
static DEVICE_API(syscon, efuse_bflb_api) = {
239+
.read = efuse_bflb_read,
240+
.get_size = efuse_bflb_size,
241+
.get_base = efuse_bflb_get_base,
242+
};
243+
244+
static const struct efuse_bflb_config efuse_config = {
245+
.addr = DT_INST_REG_ADDR(0),
246+
.size = DT_INST_PROP(0, size),
247+
};
248+
249+
static struct efuse_bflb_data efuse_data = {
250+
.cached = false,
251+
.cache = {0},
252+
};
253+
254+
255+
DEVICE_DT_INST_DEFINE(0, NULL, NULL, &efuse_data, &efuse_config, POST_KERNEL,
256+
CONFIG_SYSCON_INIT_PRIORITY, &efuse_bflb_api);

dts/bindings/syscon/bflb,efuse.yaml

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
# Copyright (c) 2024-2025 MASSDRIVER EI (massdriver.space)
2+
# SPDX-License-Identifier: Apache-2.0
3+
4+
description: BouffaloLab Efuse
5+
6+
compatible: "bflb,efuse"
7+
8+
include: base.yaml
9+
10+
properties:
11+
reg:
12+
required: true
13+
14+
size:
15+
required: true
16+
type: int
17+
description: Size of efuse storage (bytes)

0 commit comments

Comments
 (0)