Skip to content

Commit 3c13e88

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 8fd2394 commit 3c13e88

File tree

5 files changed

+298
-0
lines changed

5 files changed

+298
-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_EFUSE_BFLB efuse_bflb.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"
42+
4143
endif # SYSCON

drivers/syscon/Kconfig.bflb

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 EFUSE_BFLB
5+
bool "Bouffalolab EFUSEs driver"
6+
default y
7+
depends on DT_HAS_BFLB_EFUSE_ENABLED
8+
help
9+
Enable accessing internal Efuses via EEPROM API for Bouffalolab platforms.

drivers/syscon/efuse_bflb.c

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

dts/bindings/mtd/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 MASSDRIVER EI (massdriver.space)
2+
# SPDX-License-Identifier: Apache-2.0
3+
4+
description: BouffaloLab Efuse Driver
5+
6+
compatible: "bflb,efuse"
7+
8+
include: eeprom-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)