Skip to content

Commit eaac933

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 eaac933

File tree

5 files changed

+294
-0
lines changed

5 files changed

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